From a248084c56a8aa1f5d3a4c6fd3d0eaa16c23dab6 Mon Sep 17 00:00:00 2001 From: Martin McKeaveney Date: Mon, 16 Aug 2021 11:42:21 +0100 Subject: [PATCH 01/22] budibase kubernetes support --- docs/index.html | 9 + docs/index.yaml | 21 + hosting/kubernetes/budibase/.helmignore | 23 + hosting/kubernetes/budibase/Chart.yaml | 39 + hosting/kubernetes/budibase/README.md | 182 ++++ .../budibase/charts/couchdb/Chart.yaml | 19 + .../budibase/charts/couchdb/Chart.yaml.orig | 24 + .../budibase/charts/couchdb/README.md | 244 ++++++ .../charts/couchdb/ci/required-values.yaml | 3 + .../budibase/charts/couchdb/ci/sidecar.yaml | 9 + .../budibase/charts/couchdb/password.ini | 2 + .../charts/couchdb/templates/NOTES.txt | 20 + .../charts/couchdb/templates/_helpers.tpl | 81 ++ .../charts/couchdb/templates/configmap.yaml | 23 + .../charts/couchdb/templates/headless.yaml | 17 + .../charts/couchdb/templates/ingress.yaml | 33 + .../couchdb/templates/networkpolicy.yaml | 31 + .../charts/couchdb/templates/secrets.yaml | 19 + .../charts/couchdb/templates/service.yaml | 23 + .../couchdb/templates/serviceaccount.yaml | 15 + .../charts/couchdb/templates/statefulset.yaml | 202 +++++ .../budibase/charts/couchdb/values.yaml | 201 +++++ .../budibase/charts/ingress-nginx/.helmignore | 22 + .../charts/ingress-nginx/CHANGELOG.md | 250 ++++++ .../budibase/charts/ingress-nginx/Chart.yaml | 19 + .../budibase/charts/ingress-nginx/OWNERS | 10 + .../budibase/charts/ingress-nginx/README.md | 226 +++++ .../ci/daemonset-customconfig-values.yaml | 9 + .../ci/daemonset-customnodeport-values.yaml | 18 + .../ci/daemonset-headers-values.yaml | 10 + .../ci/daemonset-internal-lb-values.yaml | 10 + .../ci/daemonset-nodeport-values.yaml | 6 + .../ci/daemonset-podannotations-values.yaml | 13 + ...set-tcp-udp-configMapNamespace-values.yaml | 16 + .../ci/daemonset-tcp-udp-values.yaml | 12 + .../ci/daemonset-tcp-values.yaml | 10 + .../ci/deamonset-default-values.yaml | 6 + .../ci/deamonset-metrics-values.yaml | 8 + .../ci/deamonset-psp-values.yaml | 9 + .../ci/deamonset-webhook-and-psp-values.yaml | 9 + .../ci/deamonset-webhook-values.yaml | 6 + .../ci/deployment-autoscaling-values.yaml | 7 + .../ci/deployment-customconfig-values.yaml | 7 + .../ci/deployment-customnodeport-values.yaml | 16 + .../ci/deployment-default-values.yaml | 4 + .../ci/deployment-headers-values.yaml | 9 + .../ci/deployment-internal-lb-values.yaml | 9 + .../ci/deployment-metrics-values.yaml | 7 + .../ci/deployment-nodeport-values.yaml | 5 + .../ci/deployment-podannotations-values.yaml | 12 + .../ci/deployment-psp-values.yaml | 6 + ...ent-tcp-udp-configMapNamespace-values.yaml | 15 + .../ci/deployment-tcp-udp-values.yaml | 11 + .../ci/deployment-tcp-values.yaml | 7 + .../ci/deployment-webhook-and-psp-values.yaml | 8 + .../ci/deployment-webhook-values.yaml | 5 + .../charts/ingress-nginx/templates/NOTES.txt | 71 ++ .../ingress-nginx/templates/_helpers.tpl | 134 +++ .../job-patch/clusterrole.yaml | 31 + .../job-patch/clusterrolebinding.yaml | 20 + .../job-patch/job-createSecret.yaml | 61 ++ .../job-patch/job-patchWebhook.yaml | 63 ++ .../admission-webhooks/job-patch/psp.yaml | 36 + .../admission-webhooks/job-patch/role.yaml | 21 + .../job-patch/rolebinding.yaml | 21 + .../job-patch/serviceaccount.yaml | 13 + .../validating-webhook.yaml | 46 + .../ingress-nginx/templates/clusterrole.yaml | 75 ++ .../templates/clusterrolebinding.yaml | 16 + .../controller-configmap-addheaders.yaml | 11 + .../controller-configmap-proxyheaders.yaml | 16 + .../templates/controller-configmap-tcp.yaml | 14 + .../templates/controller-configmap-udp.yaml | 14 + .../templates/controller-configmap.yaml | 25 + .../templates/controller-daemonset.yaml | 244 ++++++ .../templates/controller-deployment.yaml | 245 ++++++ .../templates/controller-hpa.yaml | 45 + .../templates/controller-ingressclass.yaml | 23 + .../templates/controller-keda.yaml | 39 + .../controller-poddisruptionbudget.yaml | 16 + .../templates/controller-prometheusrules.yaml | 21 + .../templates/controller-psp.yaml | 86 ++ .../templates/controller-role.yaml | 92 ++ .../templates/controller-rolebinding.yaml | 18 + .../controller-service-internal.yaml | 51 ++ .../templates/controller-service-metrics.yaml | 44 + .../templates/controller-service-webhook.yaml | 34 + .../templates/controller-service.yaml | 85 ++ .../templates/controller-serviceaccount.yaml | 11 + .../templates/controller-servicemonitor.yaml | 45 + .../templates/default-backend-deployment.yaml | 112 +++ .../templates/default-backend-hpa.yaml | 30 + .../default-backend-poddisruptionbudget.yaml | 16 + .../templates/default-backend-psp.yaml | 33 + .../templates/default-backend-role.yaml | 19 + .../default-backend-rolebinding.yaml | 18 + .../templates/default-backend-service.yaml | 35 + .../default-backend-serviceaccount.yaml | 11 + .../templates/dh-param-secret.yaml | 10 + .../budibase/charts/ingress-nginx/values.yaml | 808 ++++++++++++++++++ .../kubernetes/budibase/templates/NOTES.txt | 22 + .../budibase/templates/_helpers.tpl | 76 ++ .../templates/app-service-deployment.yaml | 79 ++ .../templates/app-service-service.yaml | 19 + .../kubernetes/budibase/templates/hpa.yaml | 28 + .../budibase/templates/ingress.yaml | 61 ++ .../minio-data-persistentvolumeclaim.yaml | 16 + .../templates/minio-service-deployment.yaml | 72 ++ .../templates/minio-service-service.yaml | 21 + .../templates/proxy-service-deployment.yaml | 38 + .../templates/proxy-service-service.yaml | 20 + .../redis-data-persistentvolumeclaim.yaml | 16 + .../templates/redis-service-deployment.yaml | 49 ++ .../templates/redis-service-service.yaml | 21 + .../budibase/templates/secrets.yaml | 17 + .../budibase/templates/service.yaml | 15 + .../budibase/templates/serviceaccount.yaml | 12 + .../templates/tests/test-connection.yaml | 15 + .../templates/worker-service-deployment.yaml | 82 ++ .../templates/worker-service-service.yaml | 19 + .../kubernetes/budibase/values.schema.json | 178 ++++ hosting/kubernetes/budibase/values.yaml | 141 +++ hosting/kubernetes/envoy/Dockerfile | 4 + hosting/kubernetes/envoy/envoy.yaml | 125 +++ packages/server/src/db/client.js | 2 +- packages/server/src/utilities/index.js | 2 +- 126 files changed, 6064 insertions(+), 2 deletions(-) create mode 100644 docs/index.html create mode 100644 docs/index.yaml create mode 100644 hosting/kubernetes/budibase/.helmignore create mode 100644 hosting/kubernetes/budibase/Chart.yaml create mode 100644 hosting/kubernetes/budibase/README.md create mode 100755 hosting/kubernetes/budibase/charts/couchdb/Chart.yaml create mode 100755 hosting/kubernetes/budibase/charts/couchdb/Chart.yaml.orig create mode 100755 hosting/kubernetes/budibase/charts/couchdb/README.md create mode 100755 hosting/kubernetes/budibase/charts/couchdb/ci/required-values.yaml create mode 100755 hosting/kubernetes/budibase/charts/couchdb/ci/sidecar.yaml create mode 100755 hosting/kubernetes/budibase/charts/couchdb/password.ini create mode 100755 hosting/kubernetes/budibase/charts/couchdb/templates/NOTES.txt create mode 100755 hosting/kubernetes/budibase/charts/couchdb/templates/_helpers.tpl create mode 100755 hosting/kubernetes/budibase/charts/couchdb/templates/configmap.yaml create mode 100755 hosting/kubernetes/budibase/charts/couchdb/templates/headless.yaml create mode 100755 hosting/kubernetes/budibase/charts/couchdb/templates/ingress.yaml create mode 100755 hosting/kubernetes/budibase/charts/couchdb/templates/networkpolicy.yaml create mode 100755 hosting/kubernetes/budibase/charts/couchdb/templates/secrets.yaml create mode 100755 hosting/kubernetes/budibase/charts/couchdb/templates/service.yaml create mode 100755 hosting/kubernetes/budibase/charts/couchdb/templates/serviceaccount.yaml create mode 100755 hosting/kubernetes/budibase/charts/couchdb/templates/statefulset.yaml create mode 100755 hosting/kubernetes/budibase/charts/couchdb/values.yaml create mode 100644 hosting/kubernetes/budibase/charts/ingress-nginx/.helmignore create mode 100644 hosting/kubernetes/budibase/charts/ingress-nginx/CHANGELOG.md create mode 100644 hosting/kubernetes/budibase/charts/ingress-nginx/Chart.yaml create mode 100644 hosting/kubernetes/budibase/charts/ingress-nginx/OWNERS create mode 100644 hosting/kubernetes/budibase/charts/ingress-nginx/README.md create mode 100644 hosting/kubernetes/budibase/charts/ingress-nginx/ci/daemonset-customconfig-values.yaml create mode 100644 hosting/kubernetes/budibase/charts/ingress-nginx/ci/daemonset-customnodeport-values.yaml create mode 100644 hosting/kubernetes/budibase/charts/ingress-nginx/ci/daemonset-headers-values.yaml create mode 100644 hosting/kubernetes/budibase/charts/ingress-nginx/ci/daemonset-internal-lb-values.yaml create mode 100644 hosting/kubernetes/budibase/charts/ingress-nginx/ci/daemonset-nodeport-values.yaml create mode 100644 hosting/kubernetes/budibase/charts/ingress-nginx/ci/daemonset-podannotations-values.yaml create mode 100644 hosting/kubernetes/budibase/charts/ingress-nginx/ci/daemonset-tcp-udp-configMapNamespace-values.yaml create mode 100644 hosting/kubernetes/budibase/charts/ingress-nginx/ci/daemonset-tcp-udp-values.yaml create mode 100644 hosting/kubernetes/budibase/charts/ingress-nginx/ci/daemonset-tcp-values.yaml create mode 100644 hosting/kubernetes/budibase/charts/ingress-nginx/ci/deamonset-default-values.yaml create mode 100644 hosting/kubernetes/budibase/charts/ingress-nginx/ci/deamonset-metrics-values.yaml create mode 100644 hosting/kubernetes/budibase/charts/ingress-nginx/ci/deamonset-psp-values.yaml create mode 100644 hosting/kubernetes/budibase/charts/ingress-nginx/ci/deamonset-webhook-and-psp-values.yaml create mode 100644 hosting/kubernetes/budibase/charts/ingress-nginx/ci/deamonset-webhook-values.yaml create mode 100644 hosting/kubernetes/budibase/charts/ingress-nginx/ci/deployment-autoscaling-values.yaml create mode 100644 hosting/kubernetes/budibase/charts/ingress-nginx/ci/deployment-customconfig-values.yaml create mode 100644 hosting/kubernetes/budibase/charts/ingress-nginx/ci/deployment-customnodeport-values.yaml create mode 100644 hosting/kubernetes/budibase/charts/ingress-nginx/ci/deployment-default-values.yaml create mode 100644 hosting/kubernetes/budibase/charts/ingress-nginx/ci/deployment-headers-values.yaml create mode 100644 hosting/kubernetes/budibase/charts/ingress-nginx/ci/deployment-internal-lb-values.yaml create mode 100644 hosting/kubernetes/budibase/charts/ingress-nginx/ci/deployment-metrics-values.yaml create mode 100644 hosting/kubernetes/budibase/charts/ingress-nginx/ci/deployment-nodeport-values.yaml create mode 100644 hosting/kubernetes/budibase/charts/ingress-nginx/ci/deployment-podannotations-values.yaml create mode 100644 hosting/kubernetes/budibase/charts/ingress-nginx/ci/deployment-psp-values.yaml create mode 100644 hosting/kubernetes/budibase/charts/ingress-nginx/ci/deployment-tcp-udp-configMapNamespace-values.yaml create mode 100644 hosting/kubernetes/budibase/charts/ingress-nginx/ci/deployment-tcp-udp-values.yaml create mode 100644 hosting/kubernetes/budibase/charts/ingress-nginx/ci/deployment-tcp-values.yaml create mode 100644 hosting/kubernetes/budibase/charts/ingress-nginx/ci/deployment-webhook-and-psp-values.yaml create mode 100644 hosting/kubernetes/budibase/charts/ingress-nginx/ci/deployment-webhook-values.yaml create mode 100644 hosting/kubernetes/budibase/charts/ingress-nginx/templates/NOTES.txt create mode 100644 hosting/kubernetes/budibase/charts/ingress-nginx/templates/_helpers.tpl create mode 100644 hosting/kubernetes/budibase/charts/ingress-nginx/templates/admission-webhooks/job-patch/clusterrole.yaml create mode 100644 hosting/kubernetes/budibase/charts/ingress-nginx/templates/admission-webhooks/job-patch/clusterrolebinding.yaml create mode 100644 hosting/kubernetes/budibase/charts/ingress-nginx/templates/admission-webhooks/job-patch/job-createSecret.yaml create mode 100644 hosting/kubernetes/budibase/charts/ingress-nginx/templates/admission-webhooks/job-patch/job-patchWebhook.yaml create mode 100644 hosting/kubernetes/budibase/charts/ingress-nginx/templates/admission-webhooks/job-patch/psp.yaml create mode 100644 hosting/kubernetes/budibase/charts/ingress-nginx/templates/admission-webhooks/job-patch/role.yaml create mode 100644 hosting/kubernetes/budibase/charts/ingress-nginx/templates/admission-webhooks/job-patch/rolebinding.yaml create mode 100644 hosting/kubernetes/budibase/charts/ingress-nginx/templates/admission-webhooks/job-patch/serviceaccount.yaml create mode 100644 hosting/kubernetes/budibase/charts/ingress-nginx/templates/admission-webhooks/validating-webhook.yaml create mode 100644 hosting/kubernetes/budibase/charts/ingress-nginx/templates/clusterrole.yaml create mode 100644 hosting/kubernetes/budibase/charts/ingress-nginx/templates/clusterrolebinding.yaml create mode 100644 hosting/kubernetes/budibase/charts/ingress-nginx/templates/controller-configmap-addheaders.yaml create mode 100644 hosting/kubernetes/budibase/charts/ingress-nginx/templates/controller-configmap-proxyheaders.yaml create mode 100644 hosting/kubernetes/budibase/charts/ingress-nginx/templates/controller-configmap-tcp.yaml create mode 100644 hosting/kubernetes/budibase/charts/ingress-nginx/templates/controller-configmap-udp.yaml create mode 100644 hosting/kubernetes/budibase/charts/ingress-nginx/templates/controller-configmap.yaml create mode 100644 hosting/kubernetes/budibase/charts/ingress-nginx/templates/controller-daemonset.yaml create mode 100644 hosting/kubernetes/budibase/charts/ingress-nginx/templates/controller-deployment.yaml create mode 100644 hosting/kubernetes/budibase/charts/ingress-nginx/templates/controller-hpa.yaml create mode 100644 hosting/kubernetes/budibase/charts/ingress-nginx/templates/controller-ingressclass.yaml create mode 100644 hosting/kubernetes/budibase/charts/ingress-nginx/templates/controller-keda.yaml create mode 100644 hosting/kubernetes/budibase/charts/ingress-nginx/templates/controller-poddisruptionbudget.yaml create mode 100644 hosting/kubernetes/budibase/charts/ingress-nginx/templates/controller-prometheusrules.yaml create mode 100644 hosting/kubernetes/budibase/charts/ingress-nginx/templates/controller-psp.yaml create mode 100644 hosting/kubernetes/budibase/charts/ingress-nginx/templates/controller-role.yaml create mode 100644 hosting/kubernetes/budibase/charts/ingress-nginx/templates/controller-rolebinding.yaml create mode 100644 hosting/kubernetes/budibase/charts/ingress-nginx/templates/controller-service-internal.yaml create mode 100644 hosting/kubernetes/budibase/charts/ingress-nginx/templates/controller-service-metrics.yaml create mode 100644 hosting/kubernetes/budibase/charts/ingress-nginx/templates/controller-service-webhook.yaml create mode 100644 hosting/kubernetes/budibase/charts/ingress-nginx/templates/controller-service.yaml create mode 100644 hosting/kubernetes/budibase/charts/ingress-nginx/templates/controller-serviceaccount.yaml create mode 100644 hosting/kubernetes/budibase/charts/ingress-nginx/templates/controller-servicemonitor.yaml create mode 100644 hosting/kubernetes/budibase/charts/ingress-nginx/templates/default-backend-deployment.yaml create mode 100644 hosting/kubernetes/budibase/charts/ingress-nginx/templates/default-backend-hpa.yaml create mode 100644 hosting/kubernetes/budibase/charts/ingress-nginx/templates/default-backend-poddisruptionbudget.yaml create mode 100644 hosting/kubernetes/budibase/charts/ingress-nginx/templates/default-backend-psp.yaml create mode 100644 hosting/kubernetes/budibase/charts/ingress-nginx/templates/default-backend-role.yaml create mode 100644 hosting/kubernetes/budibase/charts/ingress-nginx/templates/default-backend-rolebinding.yaml create mode 100644 hosting/kubernetes/budibase/charts/ingress-nginx/templates/default-backend-service.yaml create mode 100644 hosting/kubernetes/budibase/charts/ingress-nginx/templates/default-backend-serviceaccount.yaml create mode 100644 hosting/kubernetes/budibase/charts/ingress-nginx/templates/dh-param-secret.yaml create mode 100644 hosting/kubernetes/budibase/charts/ingress-nginx/values.yaml create mode 100644 hosting/kubernetes/budibase/templates/NOTES.txt create mode 100644 hosting/kubernetes/budibase/templates/_helpers.tpl create mode 100644 hosting/kubernetes/budibase/templates/app-service-deployment.yaml create mode 100644 hosting/kubernetes/budibase/templates/app-service-service.yaml create mode 100644 hosting/kubernetes/budibase/templates/hpa.yaml create mode 100644 hosting/kubernetes/budibase/templates/ingress.yaml create mode 100644 hosting/kubernetes/budibase/templates/minio-data-persistentvolumeclaim.yaml create mode 100644 hosting/kubernetes/budibase/templates/minio-service-deployment.yaml create mode 100644 hosting/kubernetes/budibase/templates/minio-service-service.yaml create mode 100644 hosting/kubernetes/budibase/templates/proxy-service-deployment.yaml create mode 100644 hosting/kubernetes/budibase/templates/proxy-service-service.yaml create mode 100644 hosting/kubernetes/budibase/templates/redis-data-persistentvolumeclaim.yaml create mode 100644 hosting/kubernetes/budibase/templates/redis-service-deployment.yaml create mode 100644 hosting/kubernetes/budibase/templates/redis-service-service.yaml create mode 100644 hosting/kubernetes/budibase/templates/secrets.yaml create mode 100644 hosting/kubernetes/budibase/templates/service.yaml create mode 100644 hosting/kubernetes/budibase/templates/serviceaccount.yaml create mode 100644 hosting/kubernetes/budibase/templates/tests/test-connection.yaml create mode 100644 hosting/kubernetes/budibase/templates/worker-service-deployment.yaml create mode 100644 hosting/kubernetes/budibase/templates/worker-service-service.yaml create mode 100644 hosting/kubernetes/budibase/values.schema.json create mode 100644 hosting/kubernetes/budibase/values.yaml create mode 100644 hosting/kubernetes/envoy/Dockerfile create mode 100644 hosting/kubernetes/envoy/envoy.yaml diff --git a/docs/index.html b/docs/index.html new file mode 100644 index 0000000000..0fa6060f8f --- /dev/null +++ b/docs/index.html @@ -0,0 +1,9 @@ + + + Budibase Helm Chart Repo + + +

Budibase Charts Repo

+

Point Helm at this repo to see charts.

+ + \ No newline at end of file diff --git a/docs/index.yaml b/docs/index.yaml new file mode 100644 index 0000000000..aedbc97da9 --- /dev/null +++ b/docs/index.yaml @@ -0,0 +1,21 @@ +apiVersion: v1 +entries: + budibase: + - apiVersion: v2 + appVersion: 0.9.56 + created: "2021-08-11T23:12:24.184224+01:00" + description: Budibase is an open source low-code platform, helping thousands of teams build apps for their workplace in minutes. + digest: 19bde738daee9c5fe0fd3aef74c41214be4f7f1b9951b277506684493a5bc3fe + keywords: + - low-code + - database + - cluster + name: budibase + sources: + - https://github.com/Budibase/budibase + - https://budibase.com + type: application + urls: + - https://budibase.github.io/budibase/budibase-0.1.0.tgz + version: 0.1.0 +generated: "2021-08-11T23:12:24.178479+01:00" diff --git a/hosting/kubernetes/budibase/.helmignore b/hosting/kubernetes/budibase/.helmignore new file mode 100644 index 0000000000..0e8a0eb36f --- /dev/null +++ b/hosting/kubernetes/budibase/.helmignore @@ -0,0 +1,23 @@ +# Patterns to ignore when building packages. +# This supports shell glob matching, relative path matching, and +# negation (prefixed with !). Only one pattern per line. +.DS_Store +# Common VCS dirs +.git/ +.gitignore +.bzr/ +.bzrignore +.hg/ +.hgignore +.svn/ +# Common backup files +*.swp +*.bak +*.tmp +*.orig +*~ +# Various IDEs +.project +.idea/ +*.tmproj +.vscode/ diff --git a/hosting/kubernetes/budibase/Chart.yaml b/hosting/kubernetes/budibase/Chart.yaml new file mode 100644 index 0000000000..63351df4f2 --- /dev/null +++ b/hosting/kubernetes/budibase/Chart.yaml @@ -0,0 +1,39 @@ +apiVersion: v2 +name: budibase +description: Budibase is an open source low-code platform, helping thousands of teams build apps for their workplace in minutes. +keywords: +- low-code +- database +- cluster +sources: +- https://github.com/Budibase/budibase +- https://budibase.com + +# A chart can be either an 'application' or a 'library' chart. +# +# Application charts are a collection of templates that can be packaged into versioned archives +# to be deployed. +# +# Library charts provide useful utilities or functions for the chart developer. They're included as +# a dependency of application charts to inject those utilities and functions into the rendering +# pipeline. Library charts do not define any templates and therefore cannot be deployed. +type: application + +# This is the chart version. This version number should be incremented each time you make changes +# to the chart and its templates, including the app version. +# Versions are expected to follow Semantic Versioning (https://semver.org/) +version: 0.1.0 + +# This is the version number of the application being deployed. This version number should be +# incremented each time you make changes to the application. Versions are not expected to +# follow Semantic Versioning. They should reflect the version the application is using. +# It is recommended to use it with quotes. +appVersion: "0.9.56" + +dependencies: + - name: couchdb + version: 3.3.4 + repository: https://apache.github.io/couchdb-helm + - name: ingress-nginx + version: 3.35.0 + repository: https://github.com/kubernetes/ingress-nginx diff --git a/hosting/kubernetes/budibase/README.md b/hosting/kubernetes/budibase/README.md new file mode 100644 index 0000000000..9de545d36a --- /dev/null +++ b/hosting/kubernetes/budibase/README.md @@ -0,0 +1,182 @@ +# Budibase + +[Budibase](https://posthog.com/) Budibase is an open source low-code platform, helping thousands of teams build apps for their workplace in minutes. + +## TL;DR; +```console +$ cd chart +$ helm install budibase . +``` + +## Introduction + +This chart bootstraps a [Budibase](https://budibase.com/) deployment on a [Kubernetes](http://kubernetes.io) cluster using the [Helm](https://helm.sh) package manager. + + + +## Prerequisites + +- helm v3 or above +- Kubernetes 1.4+ +- PV provisioner support in the underlying infrastructure (with persistence storage enabled) + +## Installing the Chart + +To install the chart with the release name `budi-release`: + +```console +$ helm install budi-release . +``` + +The command deploys PostHog on the Kubernetes cluster in the default configuration. The [configuration](#configuration) section lists the parameters that can be configured during installation. + +> **Tip**: List all releases using `helm list` + +## Uninstalling the Chart + +To uninstall/delete the `my-release` deployment: + +```console +$ helm delete my-release +``` + +The command removes all the Kubernetes components associated with the chart and deletes the release. + +> **Warning**: Jobs are not deleted automatically. They need to be manually deleted + +```console +$ kubectl delete job/posthog-migrate +``` + +## Configuration + +The following table lists the configurable parameters of the PostHog chart and their default values. + +Dependent charts can also have values overwritten. Preface values with postgresql.* + +Parameter | Description | Default +:--------------------------------------------------- | :--------------------------------------------------------------------------------------------------------- | :--------------------------------------------------- +`image.repository` | PostHog image | `posthog/posthog` +`image.tag` | PostHog image tag | `latest` +`image.pullPolicy` | Image pull policy | `Always` +`image.imagePullSecrets` | Specify image pull secrets | `[]` +`posthogSecret` | Specify SECRET_KEY. If isn't specified it will be generated automatically. | `nil` +`disableSecureSslRedirect` | Specify DISABLE_SECURE_SSL_REDIRECT. | `1` +`useSecureCookies` | Specify SECURE_COOKIES. | `0` +`web.podAnnotations` | Web pod annotations | `{}` +`web.podLabels` | Web pod extra labels | `{}` +`web.replicacount` | Amount of web pods to run | `1` +`web.resources.limits` | Web resource limits | `{cpu: 500m, memory: 500Mi}` +`web.resources.requests` | Web resource requests | `{cpu: 300m, memory: 300Mi}` +`web.env` | Additional web environment variables | `[]` +`web.nodeSelector` | Node labels for web pod assignment | `{}` +`web.affinity` | Affinity settings for web pod assignment | `{}` +`web.schedulerName` | Name of an alternate scheduler for web pod | `nil` +`web.tolerations` | Toleration labels for web pod assignment | `[]` +`web.livenessProbe.failureThreshold` | The liveness probe failure threshold | `5` +`web.livenessProbe.initialDelaySeconds` | The liveness probe initial delay seconds | `50` +`web.livenessProbe.periodSeconds` | The liveness probe period seconds | `10` +`web.livenessProbe.successThreshold` | The liveness probe success threshold | `1` +`web.livenessProbe.timeoutSeconds` | The liveness probe timeout seconds | `2` +`web.readinessProbe.failureThreshold` | The readiness probe failure threshold | `10` +`web.readinessProbe.initialDelaySeconds` | The readiness probe initial delay seconds | `50` +`web.readinessProbe.periodSeconds` | The readiness probe period seconds | `10` +`web.readinessProbe.successThreshold` | The readiness probe success threshold | `1` +`web.readinessProbe.timeoutSeconds` | The readiness probe timeout seconds | `2` +`web.priorityClassName` | The priorityClassName on web deployment | `nil` +`web.hpa.enabled` | Boolean to create a HorizontalPodAutoscaler for web deployment | `false` +`web.hpa.cputhreshold` | CPU threshold percent for the web HorizontalPodAutoscaler | `60` +`web.hpa.minpods` | Min pods for the web HorizontalPodAutoscaler | `1` +`web.hpa.maxpods` | Max pods for the web HorizontalPodAutoscaler | `10` +`email.from_email` | Emails are sent are from | `tim@posthog.com` +`email.host` | SMTP host for sending email | `smtp` +`email.port` | SMTP port | `578` +`email.user` | SMTP user | `nil` +`email.password` | SMTP password | `nil` +`email.use_tls` | SMTP TLS for security | `false` +`email.use_ssl` | SMTP SSL for security | `false` +`email.existingSecret` | SMTP password from an existing secret | `nil` +`email.existingSecretKey` | Key to get from the `email.existingSecret` secret | `smtp-password` +`service.type` | Kubernetes service type | `LoadBalancer` +`service.name` | Kubernetes service name | `posthog` +`service.externalPort` | Kubernetes external service port | `8000` +`service.internalPort` | Kubernetes internal service port | `8000` +`service.annotations` | Service annotations | `{}` +`service.nodePort` | Kubernetes service NodePort port | Randomly chosen by Kubernetes +`service.loadBalancerSourceRanges` | Allow list for the load balancer | `nil` +`ingress.enabled` | Enable ingress controller resource | `false` +`ingress.annotations` | Ingress annotations | `{}` +`ingress.hostname` | URL to address your PostHog installation | `posthog.local` +`ingress.path` | path to address your PostHog installation | `/` +`ingress.tls` | Ingress TLS configuration | `[]` +`postgresql.enabled` | Deploy postgres server (see below) | `true` +`postgresql.postgresqlDatabase` | Postgres database name | `posthog` +`postgresql.postgresqlUsername` | Postgres username | `postgres` +`postgresql.postgresqlHost` | External postgres host | `nil` +`postgresql.postgresqlPassword` | External/Internal postgres password | `postgres` +`postgresql.postgresqlPort` | External postgres port | `5432` +`postgresql.existingSecret` | Name of existing secret to use for the PostgreSQL password | `nil` +`postgresql.existingSecretKey` | Key to get from the `postgresql.existingSecret` secret | `postgresql-password` | `nil` +`redis.enabled` | Deploy redis server (see below) | `true` +`redis.host` | External redis host | `nil` +`redis.password` | External redis password | `nil` +`redis.port` | External redis port | `6379` +`redis.existingSecret` | Name of existing secret to use for the Redis password | `nil` +`redis.existingSecretKey` | Key to get from the `redis.existingSecret` secret | `redis-password` +`metrics.enabled` | Start an exporter for posthog metrics | `false` +`metrics.nodeSelector` | Node labels for metrics pod assignment | `{}` +`metrics.tolerations` | Toleration labels for metrics pod assignment | `[]` +`metrics.affinity` | Affinity settings for metrics pod | `{}` +`metrics.schedulerName` | Name of an alternate scheduler for metrics pod | `nil` +`metrics.podLabels` | Labels for metrics pod | `nil` +`metrics.resources` | Metrics resource requests/limit | `{}` +`metrics.service.type` | Kubernetes service type for metrics service | `ClusterIP` +`metrics.service.labels` | Additional labels for metrics service | `{}` +`metrics.image.repository` | Metrics exporter image repository | `prom/statsd-exporter` +`metrics.image.tag` | Metrics exporter image tag | `v0.10.5` +`metrics.image.PullPolicy` | Metrics exporter image pull policy | `IfNotPresent` +`metrics.serviceMonitor.enabled` | if `true`, creates a Prometheus Operator ServiceMonitor (also requires `metrics.enabled` to be `true`) | `false` +`metrics.serviceMonitor.namespace` | Optional namespace which Prometheus is running in | `nil` +`metrics.serviceMonitor.interval` | How frequently to scrape metrics (use by default, falling back to Prometheus' default) | `nil` +`metrics.serviceMonitor.selector` | Default to kube-prometheus install (CoreOS recommended), but should be set according to Prometheus install | `{ prometheus: kube-prometheus }` +`hooks.affinity` | Affinity settings for hooks pods | `{}` +`hooks.migrate.resources.limits` | Hook job resource limits | `{memory: 1000Mi}` +`hooks.migrate.resources.requests` | Hook job resource requests | `{memory: 1000Mi}` +`hooks.migrate.hookAnnotation` | Hook lifecycle annotation value | `post-install,post-upgrade` +`serviceAccount.name` | name of the ServiceAccount to be used by access-controlled resources | autogenerated +`serviceAccount.create` | Configures if a ServiceAccount with this name should be created | `true` +`serviceAccount.annotations` | Configures annotation for the ServiceAccount | `{}` +`env` | Additional environment variables applied to all deployments (web, worker, beat, plugin-server) | `[]` + +Dependent charts can also have values overwritten. Preface values with "postgresql." + +Specify each parameter using the `--set key=value[,key=value]` argument to `helm install`. For example, + +```console +$ helm install \ + --set persistence.enabled=false,email.host=email \ + my-release . +``` + +Alternatively, a YAML file that specifies the values for the above parameters can be provided while installing the chart. For example, + +```console +$ helm install -f my-values.yaml my-release . +``` + +## PostgreSQL + +By default, PostgreSQL is installed as part of the chart. To use an external PostgreSQL server set `postgresql.enabled` to `false` and then set `postgresql.postgresHost` and `postgresql.postgresqlPassword`. The other options (`postgresql.postgresqlDatabase`, `postgresql.postgresqlUsername` and `postgresql.postgresqlPort`) may also want changing from their default values. + +To avoid issues when upgrading this chart, provide `postgresql.postgresqlPassword` for subsequent upgrades. This is due to an issue in the PostgreSQL chart where password will be overwritten with randomly generated passwords otherwise. See https://github.com/helm/charts/tree/master/stable/postgresql#upgrade for more detail. + +## Redis + +By default, Redis is installed as part of the chart. To use an external Redis server/cluster set `redis.enabled` to `false` and then set `redis.host`. If your redis cluster uses password define it with `redis.password`, otherwise just omit it. Check the table above for more configuration options. + +To avoid issues when upgrading this chart, provide `redis.password` for subsequent upgrades. Otherwise the redis pods will get recreated on every update, potentially incurring some downtime. + +## Ingress + +This chart provides support for Ingress resource. If you have an available Ingress Controller such as Nginx or Traefik you maybe want to set `ingress.enabled` to true and choose an `ingress.hostname` for the URL. Then, you should be able to access the installation using that address. + diff --git a/hosting/kubernetes/budibase/charts/couchdb/Chart.yaml b/hosting/kubernetes/budibase/charts/couchdb/Chart.yaml new file mode 100755 index 0000000000..74ae734a17 --- /dev/null +++ b/hosting/kubernetes/budibase/charts/couchdb/Chart.yaml @@ -0,0 +1,19 @@ +apiVersion: v1 +appVersion: 3.1.0 +description: A database featuring seamless multi-master sync, that scales from big + data to mobile, with an intuitive HTTP/JSON API and designed for reliability. +home: https://couchdb.apache.org/ +icon: http://couchdb.apache.org/CouchDB-visual-identity/logo/CouchDB-couch-symbol.svg +keywords: +- couchdb +- database +- nosql +maintainers: +- email: kocolosk@apache.org + name: kocolosk +- email: willholley@apache.org + name: willholley +name: couchdb +sources: +- https://github.com/apache/couchdb-docker +version: 3.3.4 diff --git a/hosting/kubernetes/budibase/charts/couchdb/Chart.yaml.orig b/hosting/kubernetes/budibase/charts/couchdb/Chart.yaml.orig new file mode 100755 index 0000000000..23da0fcfff --- /dev/null +++ b/hosting/kubernetes/budibase/charts/couchdb/Chart.yaml.orig @@ -0,0 +1,24 @@ +apiVersion: v1 +name: couchdb +<<<<<<< HEAD +version: 3.3.2 +======= +version: 3.3.0 +>>>>>>> Bump chart version and publish +appVersion: 2.3.1 +description: A database featuring seamless multi-master sync, that scales from + big data to mobile, with an intuitive HTTP/JSON API and designed for + reliability. +keywords: + - couchdb + - database + - nosql +home: https://couchdb.apache.org/ +sources: + - https://github.com/apache/couchdb-docker +maintainers: + - name: kocolosk + email: kocolosk@apache.org + - name: willholley + email: willholley@apache.org +icon: http://couchdb.apache.org/CouchDB-visual-identity/logo/CouchDB-couch-symbol.svg diff --git a/hosting/kubernetes/budibase/charts/couchdb/README.md b/hosting/kubernetes/budibase/charts/couchdb/README.md new file mode 100755 index 0000000000..3227123d06 --- /dev/null +++ b/hosting/kubernetes/budibase/charts/couchdb/README.md @@ -0,0 +1,244 @@ +# CouchDB + +Apache CouchDB is a database featuring seamless multi-master sync, that scales +from big data to mobile, with an intuitive HTTP/JSON API and designed for +reliability. + +This chart deploys a CouchDB cluster as a StatefulSet. It creates a ClusterIP +Service in front of the Deployment for load balancing by default, but can also +be configured to deploy other Service types or an Ingress Controller. The +default persistence mechanism is simply the ephemeral local filesystem, but +production deployments should set `persistentVolume.enabled` to `true` to attach +storage volumes to each Pod in the Deployment. + +## TL;DR + +```bash +$ helm repo add couchdb https://apache.github.io/couchdb-helm +$ helm install couchdb/couchdb \ + --set allowAdminParty=true \ + --set couchdbConfig.couchdb.uuid=$(curl https://www.uuidgenerator.net/api/version4 2>/dev/null | tr -d -) +``` + +## Prerequisites + +- Kubernetes 1.9+ with Beta APIs enabled +- Ingress requires Kubernetes 1.14+ + +## Installing the Chart + +To install the chart with the release name `my-release`: + +Add the CouchDB Helm repository: + +```bash +$ helm repo add couchdb https://apache.github.io/couchdb-helm +``` + +Afterwards install the chart replacing the UUID +`decafbaddecafbaddecafbaddecafbad` with a custom one: + +```bash +$ helm install \ + --name my-release \ + --set couchdbConfig.couchdb.uuid=decafbaddecafbaddecafbaddecafbad \ + couchdb/couchdb +``` + +This will create a Secret containing the admin credentials for the cluster. +Those credentials can be retrieved as follows: + +```bash +$ kubectl get secret my-release-couchdb -o go-template='{{ .data.adminPassword }}' | base64 --decode +``` + +If you prefer to configure the admin credentials directly you can create a +Secret containing `adminUsername`, `adminPassword` and `cookieAuthSecret` keys: + +```bash +$ kubectl create secret generic my-release-couchdb --from-literal=adminUsername=foo --from-literal=adminPassword=bar --from-literal=cookieAuthSecret=baz +``` + +If you want to set the `adminHash` directly to achieve consistent salts between +different nodes you need to addionally add the key `password.ini` to the secret: + +```bash +$ kubectl create secret generic my-release-couchdb \ + --from-literal=adminUsername=foo \ + --from-literal=cookieAuthSecret=baz \ + --from-file=./my-password.ini +``` + +With the following contents in `my-password.ini`: + +``` +[admins] +foo = +``` + +and then install the chart while overriding the `createAdminSecret` setting: + +```bash +$ helm install \ + --name my-release \ + --set createAdminSecret=false \ + --set couchdbConfig.couchdb.uuid=decafbaddecafbaddecafbaddecafbad \ + couchdb/couchdb +``` + +This Helm chart deploys CouchDB on the Kubernetes cluster in a default +configuration. The [configuration](#configuration) section lists +the parameters that can be configured during installation. + +> **Tip**: List all releases using `helm list` + +## Uninstalling the Chart + +To uninstall/delete the `my-release` Deployment: + +```bash +$ helm delete my-release +``` + +The command removes all the Kubernetes components associated with the chart and +deletes the release. + +## Upgrading an existing Release to a new major version + +A major chart version change (like v0.2.3 -> v1.0.0) indicates that there is an +incompatible breaking change needing manual actions. + +### Upgrade to 3.0.0 + +Since version 3.0.0 setting the CouchDB server instance UUID is mandatory. +Therefore you need to generate a UUID and supply it as a value during the +upgrade as follows: + +```bash +$ helm upgrade \ + --reuse-values \ + --set couchdbConfig.couchdb.uuid= \ + couchdb/couchdb +``` + +## Migrating from stable/couchdb + +This chart replaces the `stable/couchdb` chart previously hosted by Helm and continues the +version semantics. You can upgrade directly from `stable/couchdb` to this chart using: + +```bash +$ helm repo add couchdb https://apache.github.io/couchdb-helm +$ helm upgrade my-release couchdb/couchdb +``` + +## Configuration + +The following table lists the most commonly configured parameters of the +CouchDB chart and their default values: + +| Parameter | Description | Default | +|---------------------------------|-------------------------------------------------------|----------------------------------------| +| `clusterSize` | The initial number of nodes in the CouchDB cluster | 3 | +| `couchdbConfig` | Map allowing override elements of server .ini config | *See below* | +| `allowAdminParty` | If enabled, start cluster without admin account | false (requires creating a Secret) | +| `createAdminSecret` | If enabled, create an admin account and cookie secret | true | +| `schedulerName` | Name of the k8s scheduler (other than default) | `nil` | +| `erlangFlags` | Map of flags supplied to the underlying Erlang VM | name: couchdb, setcookie: monster +| `persistentVolume.enabled` | Boolean determining whether to attach a PV to each node | false +| `persistentVolume.size` | If enabled, the size of the persistent volume to attach | 10Gi +| `enableSearch` | Adds a sidecar for Lucene-powered text search | false | + +You can set the values of the `couchdbConfig` map according to the +[official configuration][4]. The following shows the map's default values and +required options to set: + +| Parameter | Description | Default | +|---------------------------------|--------------------------------------------------------------------|----------------------------------------| +| `couchdb.uuid` | UUID for this CouchDB server instance ([Required in a cluster][5]) | | +| `chttpd.bind_address` | listens on all interfaces when set to any | any | +| `chttpd.require_valid_user` | disables all the anonymous requests to the port 5984 when true | false | + +A variety of other parameters are also configurable. See the comments in the +`values.yaml` file for further details: + +| Parameter | Default | +|--------------------------------------|----------------------------------------| +| `adminUsername` | admin | +| `adminPassword` | auto-generated | +| `adminHash` | | +| `cookieAuthSecret` | auto-generated | +| `image.repository` | couchdb | +| `image.tag` | 3.1.0 | +| `image.pullPolicy` | IfNotPresent | +| `searchImage.repository` | kocolosk/couchdb-search | +| `searchImage.tag` | 0.1.0 | +| `searchImage.pullPolicy` | IfNotPresent | +| `initImage.repository` | busybox | +| `initImage.tag` | latest | +| `initImage.pullPolicy` | Always | +| `ingress.enabled` | false | +| `ingress.hosts` | chart-example.local | +| `ingress.annotations` | | +| `ingress.path` | / | +| `ingress.tls` | | +| `persistentVolume.accessModes` | ReadWriteOnce | +| `persistentVolume.storageClass` | Default for the Kube cluster | +| `podManagementPolicy` | Parallel | +| `affinity` | | +| `annotations` | | +| `tolerations` | | +| `resources` | | +| `service.annotations` | | +| `service.enabled` | true | +| `service.type` | ClusterIP | +| `service.externalPort` | 5984 | +| `dns.clusterDomainSuffix` | cluster.local | +| `networkPolicy.enabled` | true | +| `serviceAccount.enabled` | true | +| `serviceAccount.create` | true | +| `serviceAccount.imagePullSecrets` | | +| `sidecars` | {} | +| `livenessProbe.enabled` | true | +| `livenessProbe.failureThreshold` | 3 | +| `livenessProbe.initialDelaySeconds` | 0 | +| `livenessProbe.periodSeconds` | 10 | +| `livenessProbe.successThreshold` | 1 | +| `livenessProbe.timeoutSeconds` | 1 | +| `readinessProbe.enabled` | true | +| `readinessProbe.failureThreshold` | 3 | +| `readinessProbe.initialDelaySeconds` | 0 | +| `readinessProbe.periodSeconds` | 10 | +| `readinessProbe.successThreshold` | 1 | +| `readinessProbe.timeoutSeconds` | 1 | + +## Feedback, Issues, Contributing + +General feedback is welcome at our [user][1] or [developer][2] mailing lists. + +Apache CouchDB has a [CONTRIBUTING][3] file with details on how to get started +with issue reporting or contributing to the upkeep of this project. In short, +use GitHub Issues, do not report anything on Docker's website. + +## Non-Apache CouchDB Development Team Contributors + +- [@natarajaya](https://github.com/natarajaya) +- [@satchpx](https://github.com/satchpx) +- [@spanato](https://github.com/spanato) +- [@jpds](https://github.com/jpds) +- [@sebastien-prudhomme](https://github.com/sebastien-prudhomme) +- [@stepanstipl](https://github.com/sebastien-stepanstipl) +- [@amatas](https://github.com/amatas) +- [@Chimney42](https://github.com/Chimney42) +- [@mattjmcnaughton](https://github.com/mattjmcnaughton) +- [@mainephd](https://github.com/mainephd) +- [@AdamDang](https://github.com/AdamDang) +- [@mrtyler](https://github.com/mrtyler) +- [@kevinwlau](https://github.com/kevinwlau) +- [@jeyenzo](https://github.com/jeyenzo) +- [@Pinpin31.](https://github.com/Pinpin31) + +[1]: http://mail-archives.apache.org/mod_mbox/couchdb-user/ +[2]: http://mail-archives.apache.org/mod_mbox/couchdb-dev/ +[3]: https://github.com/apache/couchdb/blob/master/CONTRIBUTING.md +[4]: https://docs.couchdb.org/en/stable/config/index.html +[5]: https://docs.couchdb.org/en/latest/setup/cluster.html#preparing-couchdb-nodes-to-be-joined-into-a-cluster diff --git a/hosting/kubernetes/budibase/charts/couchdb/ci/required-values.yaml b/hosting/kubernetes/budibase/charts/couchdb/ci/required-values.yaml new file mode 100755 index 0000000000..79589d2e04 --- /dev/null +++ b/hosting/kubernetes/budibase/charts/couchdb/ci/required-values.yaml @@ -0,0 +1,3 @@ +couchdbConfig: + couchdb: + uuid: "decafbaddecafbaddecafbaddecafbad" diff --git a/hosting/kubernetes/budibase/charts/couchdb/ci/sidecar.yaml b/hosting/kubernetes/budibase/charts/couchdb/ci/sidecar.yaml new file mode 100755 index 0000000000..aa570bdf74 --- /dev/null +++ b/hosting/kubernetes/budibase/charts/couchdb/ci/sidecar.yaml @@ -0,0 +1,9 @@ +sidecars: + - name: foo + image: "busybox" + imagePullPolicy: IfNotPresent + resources: + requests: + cpu: "0.1" + memory: 10Mi + command: ['while true; do echo "foo"; sleep 5; done;'] diff --git a/hosting/kubernetes/budibase/charts/couchdb/password.ini b/hosting/kubernetes/budibase/charts/couchdb/password.ini new file mode 100755 index 0000000000..4ce8445aae --- /dev/null +++ b/hosting/kubernetes/budibase/charts/couchdb/password.ini @@ -0,0 +1,2 @@ +[admins] +{{ .Values.adminUsername }} = {{ .Values.adminHash }} diff --git a/hosting/kubernetes/budibase/charts/couchdb/templates/NOTES.txt b/hosting/kubernetes/budibase/charts/couchdb/templates/NOTES.txt new file mode 100755 index 0000000000..a3658bd37f --- /dev/null +++ b/hosting/kubernetes/budibase/charts/couchdb/templates/NOTES.txt @@ -0,0 +1,20 @@ +Apache CouchDB is starting. Check the status of the Pods using: + + kubectl get pods --namespace {{ .Release.Namespace }} -l "app={{ template "couchdb.name" . }},release={{ .Release.Name }}" + +Once all of the Pods are fully Ready, execute the following command to create +some required system databases: + + kubectl exec --namespace {{ .Release.Namespace }} {{ if not .Values.allowAdminParty }}-it {{ end }}{{ template "couchdb.fullname" . }}-0 -c couchdb -- \ + curl -s \ + http://127.0.0.1:5984/_cluster_setup \ + -X POST \ + -H "Content-Type: application/json" \ +{{- if .Values.allowAdminParty }} + -d '{"action": "finish_cluster"}' +{{- else }} + -d '{"action": "finish_cluster"}' \ + -u +{{- end }} + +Then it's time to relax. diff --git a/hosting/kubernetes/budibase/charts/couchdb/templates/_helpers.tpl b/hosting/kubernetes/budibase/charts/couchdb/templates/_helpers.tpl new file mode 100755 index 0000000000..f9d013e487 --- /dev/null +++ b/hosting/kubernetes/budibase/charts/couchdb/templates/_helpers.tpl @@ -0,0 +1,81 @@ +{{/* vim: set filetype=mustache: */}} +{{/* +Expand the name of the chart. +*/}} +{{- define "couchdb.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 "couchdb.fullname" -}} +{{- if .Values.fullnameOverride -}} +{{- printf "%s-%s" .Values.fullnameOverride .Chart.Name | trunc 63 | trimSuffix "-" -}} +{{- else -}} +{{- $name := default .Chart.Name .Values.nameOverride -}} +{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" -}} +{{- end -}} +{{- end -}} + +{{/* +In the event that we create both a headless service and a traditional one, +ensure that the latter gets a unique name. +*/}} +{{- define "couchdb.svcname" -}} +{{- if .Values.fullnameOverride -}} +{{- printf "%s-svc-%s" .Values.fullnameOverride .Chart.Name | trunc 63 | trimSuffix "-" -}} +{{- else -}} +{{- $name := default .Chart.Name .Values.nameOverride -}} +{{- printf "%s-svc-%s" .Release.Name $name | trunc 63 | trimSuffix "-" -}} +{{- end -}} +{{- end -}} + +{{/* +Create a random string if the supplied key does not exist +*/}} +{{- define "couchdb.defaultsecret" -}} +{{- if . -}} +{{- . | b64enc | quote -}} +{{- else -}} +{{- randAlphaNum 20 | b64enc | quote -}} +{{- end -}} +{{- end -}} + +{{/* +Labels used to define Pods in the CouchDB statefulset +*/}} +{{- define "couchdb.ss.selector" -}} +app: {{ template "couchdb.name" . }} +release: {{ .Release.Name }} +{{- end -}} + +{{/* +Generates a comma delimited list of nodes in the cluster +*/}} +{{- define "couchdb.seedlist" -}} +{{- $nodeCount := min 5 .Values.clusterSize | int }} + {{- range $index0 := until $nodeCount -}} + {{- $index1 := $index0 | add1 -}} + {{ $.Values.erlangFlags.name }}@{{ template "couchdb.fullname" $ }}-{{ $index0 }}.{{ template "couchdb.fullname" $ }}.{{ $.Release.Namespace }}.svc.{{ $.Values.dns.clusterDomainSuffix }}{{ if ne $index1 $nodeCount }},{{ end }} + {{- end -}} +{{- end -}} + +{{/* +If serviceAccount.name is specified, use that, else use the couchdb instance name +*/}} +{{- define "couchdb.serviceAccount" -}} +{{- if .Values.serviceAccount.name -}} +{{- .Values.serviceAccount.name }} +{{- else -}} +{{- template "couchdb.fullname" . -}} +{{- end -}} +{{- end -}} + +{{/* +Fail if couchdbConfig.couchdb.uuid is undefined +*/}} +{{- define "couchdb.uuid" -}} +{{- required "A value for couchdbConfig.couchdb.uuid must be set" (.Values.couchdbConfig.couchdb | default dict).uuid -}} +{{- end -}} \ No newline at end of file diff --git a/hosting/kubernetes/budibase/charts/couchdb/templates/configmap.yaml b/hosting/kubernetes/budibase/charts/couchdb/templates/configmap.yaml new file mode 100755 index 0000000000..a6a20e0574 --- /dev/null +++ b/hosting/kubernetes/budibase/charts/couchdb/templates/configmap.yaml @@ -0,0 +1,23 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + name: {{ template "couchdb.fullname" . }} + labels: + app: {{ template "couchdb.name" . }} + chart: "{{ .Chart.Name }}-{{ .Chart.Version }}" + heritage: {{ .Release.Service | quote }} + release: {{ .Release.Name | quote }} +data: + inifile: | + {{ $couchdbConfig := dict "couchdb" (dict "uuid" (include "couchdb.uuid" .)) -}} + {{- $couchdbConfig := merge $couchdbConfig .Values.couchdbConfig -}} + {{- range $section, $settings := $couchdbConfig -}} + {{ printf "[%s]" $section }} + {{ range $key, $value := $settings -}} + {{ printf "%s = %s" $key ($value | toString) }} + {{ end }} + {{ end }} + + seedlistinifile: | + [cluster] + seedlist = {{ template "couchdb.seedlist" . }} diff --git a/hosting/kubernetes/budibase/charts/couchdb/templates/headless.yaml b/hosting/kubernetes/budibase/charts/couchdb/templates/headless.yaml new file mode 100755 index 0000000000..0ce3ef0f35 --- /dev/null +++ b/hosting/kubernetes/budibase/charts/couchdb/templates/headless.yaml @@ -0,0 +1,17 @@ +apiVersion: v1 +kind: Service +metadata: + name: {{ template "couchdb.fullname" . }} + labels: + app: {{ template "couchdb.name" . }} + chart: {{ .Chart.Name }}-{{ .Chart.Version | replace "+" "_" }} + release: {{ .Release.Name }} + heritage: {{ .Release.Service }} +spec: + clusterIP: None + publishNotReadyAddresses: true + ports: + - name: couchdb + port: 5984 + selector: +{{ include "couchdb.ss.selector" . | indent 4 }} diff --git a/hosting/kubernetes/budibase/charts/couchdb/templates/ingress.yaml b/hosting/kubernetes/budibase/charts/couchdb/templates/ingress.yaml new file mode 100755 index 0000000000..c547847ce5 --- /dev/null +++ b/hosting/kubernetes/budibase/charts/couchdb/templates/ingress.yaml @@ -0,0 +1,33 @@ +{{- if .Values.ingress.enabled -}} +{{- $serviceName := include "couchdb.fullname" . -}} +{{- $servicePort := .Values.service.externalPort -}} +{{- $path := .Values.ingress.path | quote -}} +apiVersion: networking.k8s.io/v1beta1 +kind: Ingress +metadata: + name: {{ template "couchdb.fullname" . }} + labels: + app: {{ template "couchdb.name" . }} + chart: {{ .Chart.Name }}-{{ .Chart.Version | replace "+" "_" }} + release: {{ .Release.Name }} + heritage: {{ .Release.Service }} + annotations: + {{- range $key, $value := .Values.ingress.annotations }} + {{ $key }}: {{ $value | quote }} + {{- end }} +spec: + rules: + {{- range $host := .Values.ingress.hosts }} + - host: {{ $host }} + http: + paths: + - path: {{ $path }} + backend: + serviceName: {{ $serviceName }} + servicePort: {{ $servicePort }} + {{- end -}} + {{- if .Values.ingress.tls }} + tls: +{{ toYaml .Values.ingress.tls | indent 4 }} + {{- end -}} +{{- end -}} diff --git a/hosting/kubernetes/budibase/charts/couchdb/templates/networkpolicy.yaml b/hosting/kubernetes/budibase/charts/couchdb/templates/networkpolicy.yaml new file mode 100755 index 0000000000..2830708bef --- /dev/null +++ b/hosting/kubernetes/budibase/charts/couchdb/templates/networkpolicy.yaml @@ -0,0 +1,31 @@ + +{{- if .Values.networkPolicy.enabled }} +kind: NetworkPolicy +apiVersion: networking.k8s.io/v1 +metadata: + name: {{ template "couchdb.fullname" . }} + labels: + app: {{ template "couchdb.name" . }} + chart: {{ .Chart.Name }}-{{ .Chart.Version | replace "+" "_" }} + release: {{ .Release.Name }} + heritage: {{ .Release.Service }} +spec: + podSelector: + matchLabels: +{{ include "couchdb.ss.selector" . | indent 6 }} + ingress: + - ports: + - protocol: TCP + port: 5984 + - ports: + - protocol: TCP + port: 9100 + - protocol: TCP + port: 4369 + from: + - podSelector: + matchLabels: +{{ include "couchdb.ss.selector" . | indent 14 }} + policyTypes: + - Ingress +{{- end }} diff --git a/hosting/kubernetes/budibase/charts/couchdb/templates/secrets.yaml b/hosting/kubernetes/budibase/charts/couchdb/templates/secrets.yaml new file mode 100755 index 0000000000..92f55c6d6b --- /dev/null +++ b/hosting/kubernetes/budibase/charts/couchdb/templates/secrets.yaml @@ -0,0 +1,19 @@ +{{- if .Values.createAdminSecret -}} +apiVersion: v1 +kind: Secret +metadata: + name: {{ template "couchdb.fullname" . }} + labels: + app: {{ template "couchdb.fullname" . }} + chart: "{{ .Chart.Name }}-{{ .Chart.Version }}" + release: "{{ .Release.Name }}" + heritage: "{{ .Release.Service }}" +type: Opaque +data: + adminUsername: {{ template "couchdb.defaultsecret" .Values.adminUsername }} + adminPassword: {{ template "couchdb.defaultsecret" .Values.adminPassword }} + cookieAuthSecret: {{ template "couchdb.defaultsecret" .Values.cookieAuthSecret }} +{{- if .Values.adminHash }} + password.ini: {{ tpl (.Files.Get "password.ini") . | b64enc }} +{{- end -}} +{{- end -}} diff --git a/hosting/kubernetes/budibase/charts/couchdb/templates/service.yaml b/hosting/kubernetes/budibase/charts/couchdb/templates/service.yaml new file mode 100755 index 0000000000..6d0382477d --- /dev/null +++ b/hosting/kubernetes/budibase/charts/couchdb/templates/service.yaml @@ -0,0 +1,23 @@ +{{- if .Values.service.enabled -}} +apiVersion: v1 +kind: Service +metadata: + name: {{ template "couchdb.svcname" . }} + labels: + app: {{ template "couchdb.name" . }} + chart: {{ .Chart.Name }}-{{ .Chart.Version | replace "+" "_" }} + release: {{ .Release.Name }} + heritage: {{ .Release.Service }} +{{- if .Values.service.annotations }} + annotations: +{{ toYaml .Values.service.annotations | indent 4 }} +{{- end }} +spec: + ports: + - port: {{ .Values.service.externalPort }} + protocol: TCP + targetPort: 5984 + type: {{ .Values.service.type }} + selector: +{{ include "couchdb.ss.selector" . | indent 4 }} +{{- end -}} diff --git a/hosting/kubernetes/budibase/charts/couchdb/templates/serviceaccount.yaml b/hosting/kubernetes/budibase/charts/couchdb/templates/serviceaccount.yaml new file mode 100755 index 0000000000..bb82799a49 --- /dev/null +++ b/hosting/kubernetes/budibase/charts/couchdb/templates/serviceaccount.yaml @@ -0,0 +1,15 @@ +{{- if .Values.serviceAccount.create }} +apiVersion: v1 +kind: ServiceAccount +metadata: + name: {{ template "couchdb.serviceAccount" . }} + labels: + app: {{ template "couchdb.name" . }} + chart: {{ .Chart.Name }}-{{ .Chart.Version | replace "+" "_" }} + release: {{ .Release.Name }} + heritage: {{ .Release.Service }} +{{- if .Values.serviceAccount.imagePullSecrets }} +imagePullSecrets: +{{ toYaml .Values.serviceAccount.imagePullSecrets }} +{{- end }} +{{- end }} diff --git a/hosting/kubernetes/budibase/charts/couchdb/templates/statefulset.yaml b/hosting/kubernetes/budibase/charts/couchdb/templates/statefulset.yaml new file mode 100755 index 0000000000..6225fbe98c --- /dev/null +++ b/hosting/kubernetes/budibase/charts/couchdb/templates/statefulset.yaml @@ -0,0 +1,202 @@ +apiVersion: apps/v1 +kind: StatefulSet +metadata: + name: {{ template "couchdb.fullname" . }} + labels: + app: {{ template "couchdb.name" . }} + chart: {{ .Chart.Name }}-{{ .Chart.Version | replace "+" "_" }} + release: {{ .Release.Name }} + heritage: {{ .Release.Service }} +spec: + replicas: {{ .Values.clusterSize }} + serviceName: {{ template "couchdb.fullname" . }} + podManagementPolicy: {{ .Values.podManagementPolicy }} + selector: + matchLabels: +{{ include "couchdb.ss.selector" . | indent 6 }} + template: + metadata: + labels: +{{ include "couchdb.ss.selector" . | indent 8 }} +{{- with .Values.annotations }} + annotations: + checksum/config: {{ include (print $.Template.BasePath "/configmap.yaml") . | sha256sum }} +{{ toYaml . | indent 8 }} +{{- end }} + spec: + {{- if .Values.schedulerName }} + schedulerName: "{{ .Values.schedulerName }}" + {{- end }} + {{- if .Values.serviceAccount.enabled }} + serviceAccountName: {{ template "couchdb.serviceAccount" . }} + {{- end }} + initContainers: + - name: init-copy + image: "{{ .Values.initImage.repository }}:{{ .Values.initImage.tag }}" + imagePullPolicy: {{ .Values.initImage.pullPolicy }} + command: ['sh','-c','cp /tmp/chart.ini /default.d; cp /tmp/seedlist.ini /default.d; ls -lrt /default.d;'] + volumeMounts: + - name: config + mountPath: /tmp/ + - name: config-storage + mountPath: /default.d +{{- if .Values.adminHash }} + - name: admin-hash-copy + image: "{{ .Values.initImage.repository }}:{{ .Values.initImage.tag }}" + imagePullPolicy: {{ .Values.initImage.pullPolicy }} + command: ['sh','-c','cp /tmp/password.ini /local.d/ ;'] + volumeMounts: + - name: admin-password + mountPath: /tmp/password.ini + subPath: "password.ini" + - name: local-config-storage + mountPath: /local.d +{{- end }} + containers: + - name: couchdb + image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}" + imagePullPolicy: {{ .Values.image.pullPolicy }} + ports: + - name: couchdb + containerPort: 5984 + - name: epmd + containerPort: 4369 + - containerPort: 9100 + env: +{{- if not .Values.allowAdminParty }} + - name: COUCHDB_USER + valueFrom: + secretKeyRef: + name: {{ template "couchdb.fullname" . }} + key: adminUsername + - name: COUCHDB_PASSWORD + valueFrom: + secretKeyRef: + name: {{ template "couchdb.fullname" . }} + key: adminPassword + - name: COUCHDB_SECRET + valueFrom: + secretKeyRef: + name: {{ template "couchdb.fullname" . }} + key: cookieAuthSecret +{{- end }} + - name: ERL_FLAGS + value: "{{ range $k, $v := .Values.erlangFlags }} -{{ $k }} {{ $v }} {{ end }}" +{{- if .Values.livenessProbe.enabled }} + livenessProbe: +{{- if .Values.couchdbConfig.chttpd.require_valid_user }} + exec: + command: + - sh + - -c + - curl -G --silent --fail -u ${COUCHDB_USER}:${COUCHDB_PASSWORD} http://localhost:5984/_up +{{- else }} + httpGet: + path: /_up + port: 5984 +{{- end }} + failureThreshold: {{ .Values.livenessProbe.failureThreshold }} + initialDelaySeconds: {{ .Values.livenessProbe.initialDelaySeconds }} + periodSeconds: {{ .Values.livenessProbe.periodSeconds }} + successThreshold: {{ .Values.livenessProbe.successThreshold }} + timeoutSeconds: {{ .Values.livenessProbe.timeoutSeconds }} +{{- end }} +{{- if .Values.readinessProbe.enabled }} + readinessProbe: +{{- if .Values.couchdbConfig.chttpd.require_valid_user }} + exec: + command: + - sh + - -c + - curl -G --silent --fail -u ${COUCHDB_USER}:${COUCHDB_PASSWORD} http://localhost:5984/_up +{{- else }} + httpGet: + path: /_up + port: 5984 +{{- end }} + failureThreshold: {{ .Values.readinessProbe.failureThreshold }} + initialDelaySeconds: {{ .Values.readinessProbe.initialDelaySeconds }} + periodSeconds: {{ .Values.readinessProbe.periodSeconds }} + successThreshold: {{ .Values.readinessProbe.successThreshold }} + timeoutSeconds: {{ .Values.readinessProbe.timeoutSeconds }} +{{- end }} + resources: +{{ toYaml .Values.resources | indent 12 }} + volumeMounts: + - name: config-storage + mountPath: /opt/couchdb/etc/default.d +{{- if .Values.adminHash }} + - name: local-config-storage + mountPath: /opt/couchdb/etc/local.d +{{- end }} + - name: database-storage + mountPath: /opt/couchdb/data +{{- if .Values.enableSearch }} + - name: clouseau + image: "{{ .Values.searchImage.repository }}:{{ .Values.searchImage.tag }}" + imagePullPolicy: {{ .Values.searchImage.pullPolicy }} + volumeMounts: + - name: database-storage + mountPath: /opt/couchdb-search/data +{{- end }} +{{- if .Values.sidecars }} +{{ toYaml .Values.sidecars | indent 8}} +{{- end }} +{{- if .Values.nodeSelector }} + nodeSelector: +{{ toYaml .Values.nodeSelector | indent 8 }} +{{- end }} +{{- with .Values.tolerations }} + tolerations: +{{ toYaml . | indent 8 }} +{{- end }} +{{- with .Values.affinity }} + affinity: +{{ toYaml . | indent 8 }} +{{- end }} + volumes: + - name: config-storage + emptyDir: {} + - name: config + configMap: + name: {{ template "couchdb.fullname" . }} + items: + - key: inifile + path: chart.ini + - key: seedlistinifile + path: seedlist.ini + +{{- if .Values.adminHash }} + - name: local-config-storage + emptyDir: {} + - name: admin-password + secret: + secretName: {{ template "couchdb.fullname" . }} +{{- end -}} + +{{- if not .Values.persistentVolume.enabled }} + - name: database-storage + emptyDir: {} +{{- else }} + volumeClaimTemplates: + - metadata: + name: database-storage + labels: + app: {{ template "couchdb.name" . }} + release: {{ .Release.Name }} + spec: + accessModes: + {{- range .Values.persistentVolume.accessModes }} + - {{ . | quote }} + {{- end }} + resources: + requests: + storage: {{ .Values.persistentVolume.size | quote }} + {{- if .Values.persistentVolume.storageClass }} + {{- if (eq "-" .Values.persistentVolume.storageClass) }} + storageClassName: "" + {{- else }} + storageClassName: "{{ .Values.persistentVolume.storageClass }}" + {{- end }} + {{- end }} +{{- end }} diff --git a/hosting/kubernetes/budibase/charts/couchdb/values.yaml b/hosting/kubernetes/budibase/charts/couchdb/values.yaml new file mode 100755 index 0000000000..5a5025f816 --- /dev/null +++ b/hosting/kubernetes/budibase/charts/couchdb/values.yaml @@ -0,0 +1,201 @@ +## clusterSize is the initial size of the CouchDB cluster. +clusterSize: 3 + +## If allowAdminParty is enabled the cluster will start up without any database +## administrator account; i.e., all users will be granted administrative +## access. Otherwise, the system will look for a Secret called +## -couchdb containing `adminUsername`, `adminPassword` and +## `cookieAuthSecret` keys. See the `createAdminSecret` flag. +## ref: https://kubernetes.io/docs/concepts/configuration/secret/ +allowAdminParty: false + +## If createAdminSecret is enabled a Secret called -couchdb will +## be created containing auto-generated credentials. Users who prefer to set +## these values themselves have a couple of options: +## +## 1) The `adminUsername`, `adminPassword`, `adminHash`, and `cookieAuthSecret` +## can be defined directly in the chart's values. Note that all of a chart's +## values are currently stored in plaintext in a ConfigMap in the tiller +## namespace. +## +## 2) This flag can be disabled and a Secret with the required keys can be +## created ahead of time. +createAdminSecret: true + +# adminUsername: budibase +# adminPassword: budibase +# adminHash: -pbkdf2-this_is_not_necessarily_secure_either +# cookieAuthSecret: admin + +## When enabled, will deploy a networkpolicy that allows CouchDB pods to +## communicate with each other for clustering and ingress on port 5984 +networkPolicy: + enabled: true + +## Use an alternate scheduler, e.g. "stork". +## ref: https://kubernetes.io/docs/tasks/administer-cluster/configure-multiple-schedulers/ +## +# schedulerName: + +# Use a service account +serviceAccount: + enabled: true + create: true +# name: +# imagePullSecrets: +# - name: myimagepullsecret + +## The storage volume used by each Pod in the StatefulSet. If a +## persistentVolume is not enabled, the Pods will use `emptyDir` ephemeral +## local storage. Setting the storageClass attribute to "-" disables dynamic +## provisioning of Persistent Volumes; leaving it unset will invoke the default +## provisioner. +persistentVolume: + enabled: false + accessModes: + - ReadWriteOnce + size: 10Gi + storageClass: "" + +## The CouchDB image +image: + repository: couchdb + tag: 3.1.0 + pullPolicy: IfNotPresent + +## Experimental integration with Lucene-powered fulltext search +searchImage: + repository: kocolosk/couchdb-search + tag: 0.2.0 + pullPolicy: IfNotPresent + +## Flip this to flag to include the Search container in each Pod +enableSearch: true + +initImage: + repository: busybox + tag: latest + pullPolicy: Always + +## CouchDB is happy to spin up cluster nodes in parallel, but if you encounter +## problems you can try setting podManagementPolicy to the StatefulSet default +## `OrderedReady` +podManagementPolicy: Parallel + +## To better tolerate Node failures, we can prevent Kubernetes scheduler from +## assigning more than one Pod of CouchDB StatefulSet per Node using podAntiAffinity. +affinity: {} + # podAntiAffinity: + # requiredDuringSchedulingIgnoredDuringExecution: + # - labelSelector: + # matchExpressions: + # - key: "app" + # operator: In + # values: + # - couchdb + # topologyKey: "kubernetes.io/hostname" + +## Optional pod annotations +annotations: {} + +## Optional tolerations +tolerations: [] + +## A StatefulSet requires a headless Service to establish the stable network +## identities of the Pods, and that Service is created automatically by this +## chart without any additional configuration. The Service block below refers +## to a second Service that governs how clients connect to the CouchDB cluster. +service: + # annotations: + enabled: true + type: ClusterIP + externalPort: 5984 + +## An Ingress resource can provide name-based virtual hosting and TLS +## termination among other things for CouchDB deployments which are accessed +## from outside the Kubernetes cluster. +## ref: https://kubernetes.io/docs/concepts/services-networking/ingress/ +ingress: + enabled: false + hosts: + - chart-example.local + path: / + annotations: [] + # kubernetes.io/ingress.class: nginx + # kubernetes.io/tls-acme: "true" + tls: + # Secrets must be manually created in the namespace. + # - secretName: chart-example-tls + # hosts: + # - chart-example.local + +## Optional resource requests and limits for the CouchDB container +## ref: http://kubernetes.io/docs/user-guide/compute-resources/ +resources: + {} + # requests: + # cpu: 100m + # memory: 128Mi + # limits: + # cpu: 56 + # memory: 256Gi + +## erlangFlags is a map that is passed to the Erlang VM as flags using the +## ERL_FLAGS env. `name` and `setcookie` flags are minimally required to +## establish connectivity between cluster nodes. +## ref: http://erlang.org/doc/man/erl.html#init_flags +erlangFlags: + name: couchdb + setcookie: monster + +## couchdbConfig will override default CouchDB configuration settings. +## The contents of this map are reformatted into a .ini file laid down +## by a ConfigMap object. +## ref: http://docs.couchdb.org/en/latest/config/index.html +couchdbConfig: + couchdb: + uuid: budibase-couchdb # REQUIRED: Unique identifier for this CouchDB server instance + # cluster: + # q: 8 # Create 8 shards for each database + chttpd: + bind_address: any + # chttpd.require_valid_user disables all the anonymous requests to the port + # 5984 when is set to true. + require_valid_user: false + +# Kubernetes local cluster domain. +# This is used to generate FQDNs for peers when joining the CouchDB cluster. +dns: + clusterDomainSuffix: cluster.local + +## Configure liveness and readiness probe values +## Ref: https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-probes/#configure-probes +livenessProbe: + enabled: true + failureThreshold: 3 + initialDelaySeconds: 0 + periodSeconds: 10 + successThreshold: 1 + timeoutSeconds: 1 +readinessProbe: + enabled: true + failureThreshold: 3 + initialDelaySeconds: 0 + periodSeconds: 10 + successThreshold: 1 + timeoutSeconds: 1 + +# Configure arbitrary sidecar containers for CouchDB pods created by the +# StatefulSet +sidecars: {} + # - name: foo + # image: "busybox" + # imagePullPolicy: IfNotPresent + # resources: + # requests: + # cpu: "0.1" + # memory: 10Mi + # command: ['echo "foo";'] + # volumeMounts: + # - name: database-storage + # mountPath: /opt/couchdb/data/ diff --git a/hosting/kubernetes/budibase/charts/ingress-nginx/.helmignore b/hosting/kubernetes/budibase/charts/ingress-nginx/.helmignore new file mode 100644 index 0000000000..50af031725 --- /dev/null +++ b/hosting/kubernetes/budibase/charts/ingress-nginx/.helmignore @@ -0,0 +1,22 @@ +# Patterns to ignore when building packages. +# This supports shell glob matching, relative path matching, and +# negation (prefixed with !). Only one pattern per line. +.DS_Store +# Common VCS dirs +.git/ +.gitignore +.bzr/ +.bzrignore +.hg/ +.hgignore +.svn/ +# Common backup files +*.swp +*.bak +*.tmp +*~ +# Various IDEs +.project +.idea/ +*.tmproj +.vscode/ diff --git a/hosting/kubernetes/budibase/charts/ingress-nginx/CHANGELOG.md b/hosting/kubernetes/budibase/charts/ingress-nginx/CHANGELOG.md new file mode 100644 index 0000000000..36526e0825 --- /dev/null +++ b/hosting/kubernetes/budibase/charts/ingress-nginx/CHANGELOG.md @@ -0,0 +1,250 @@ +# Changelog + +This file documents all notable changes to [ingress-nginx](https://github.com/kubernetes/ingress-nginx) Helm Chart. The release numbering uses [semantic versioning](http://semver.org). + +### 3.34.0 + +- [7256] https://github.com/kubernetes/ingress-nginx/pull/7256 Add namespace field in the namespace scoped resource templates + +### 3.33.0 + +- [7164] https://github.com/kubernetes/ingress-nginx/pull/7164 Update nginx to v1.20.1 + +### 3.32.0 + +- [7117] https://github.com/kubernetes/ingress-nginx/pull/7117 Add annotations for HPA + +### 3.31.0 + +- [7137] https://github.com/kubernetes/ingress-nginx/pull/7137 Add support for custom probes + +### 3.30.0 + +- [#7092](https://github.com/kubernetes/ingress-nginx/pull/7092) Removes the possibility of using localhost in ExternalNames as endpoints + +### 3.29.0 + +- [X] [#6945](https://github.com/kubernetes/ingress-nginx/pull/7020) Add option to specify job label for ServiceMonitor + +### 3.28.0 + +- [ ] [#6900](https://github.com/kubernetes/ingress-nginx/pull/6900) Support existing PSPs + +### 3.27.0 + +- Update ingress-nginx v0.45.0 + +### 3.26.0 + +- [X] [#6979](https://github.com/kubernetes/ingress-nginx/pull/6979) Changed servicePort value for metrics + +### 3.25.0 + +- [X] [#6957](https://github.com/kubernetes/ingress-nginx/pull/6957) Add ability to specify automountServiceAccountToken + +### 3.24.0 + +- [X] [#6908](https://github.com/kubernetes/ingress-nginx/pull/6908) Add volumes to default-backend deployment + +### 3.23.0 + +- Update ingress-nginx v0.44.0 + +### 3.22.0 + +- [X] [#6802](https://github.com/kubernetes/ingress-nginx/pull/6802) Add value for configuring a custom Diffie-Hellman parameters file +- [X] [#6815](https://github.com/kubernetes/ingress-nginx/pull/6815) Allow use of numeric namespaces in helm chart + +### 3.21.0 + +- [X] [#6783](https://github.com/kubernetes/ingress-nginx/pull/6783) Add custom annotations to ScaledObject +- [X] [#6761](https://github.com/kubernetes/ingress-nginx/pull/6761) Adding quotes in the serviceAccount name in Helm values +- [X] [#6767](https://github.com/kubernetes/ingress-nginx/pull/6767) Remove ClusterRole when scope option is enabled +- [X] [#6785](https://github.com/kubernetes/ingress-nginx/pull/6785) Update kube-webhook-certgen image to v1.5.1 + +### 3.20.1 + +- Do not create KEDA in case of DaemonSets. +- Fix KEDA v2 definition + +### 3.20.0 + +- [X] [#6730](https://github.com/kubernetes/ingress-nginx/pull/6730) Do not create HPA for defaultBackend if not enabled. + +### 3.19.0 + +- Update ingress-nginx v0.43.0 + +### 3.18.0 + +- [X] [#6688](https://github.com/kubernetes/ingress-nginx/pull/6688) Allow volume-type emptyDir in controller podsecuritypolicy +- [X] [#6691](https://github.com/kubernetes/ingress-nginx/pull/6691) Improve parsing of helm parameters + +### 3.17.0 + +- Update ingress-nginx v0.42.0 + +### 3.16.1 + +- Fix chart-releaser action + +### 3.16.0 + +- [X] [#6646](https://github.com/kubernetes/ingress-nginx/pull/6646) Added LoadBalancerIP value for internal service + +### 3.15.1 + +- Fix chart-releaser action + +### 3.15.0 + +- [X] [#6586](https://github.com/kubernetes/ingress-nginx/pull/6586) Fix 'maxmindLicenseKey' location in values.yaml + +### 3.14.0 + +- [X] [#6469](https://github.com/kubernetes/ingress-nginx/pull/6469) Allow custom service names for controller and backend + +### 3.13.0 + +- [X] [#6544](https://github.com/kubernetes/ingress-nginx/pull/6544) Fix default backend HPA name variable + +### 3.12.0 + +- [X] [#6514](https://github.com/kubernetes/ingress-nginx/pull/6514) Remove helm2 support and update docs + +### 3.11.1 + +- [X] [#6505](https://github.com/kubernetes/ingress-nginx/pull/6505) Reorder HPA resource list to work with GitOps tooling + +### 3.11.0 + +- Support Keda Autoscaling + +### 3.10.1 + +- Fix regression introduced in 0.41.0 with external authentication + +### 3.10.0 + +- Fix routing regression introduced in 0.41.0 with PathType Exact + +### 3.9.0 + +- [X] [#6423](https://github.com/kubernetes/ingress-nginx/pull/6423) Add Default backend HPA autoscaling + +### 3.8.0 + +- [X] [#6395](https://github.com/kubernetes/ingress-nginx/pull/6395) Update jettech/kube-webhook-certgen image +- [X] [#6377](https://github.com/kubernetes/ingress-nginx/pull/6377) Added loadBalancerSourceRanges for internal lbs +- [X] [#6356](https://github.com/kubernetes/ingress-nginx/pull/6356) Add securitycontext settings on defaultbackend +- [X] [#6401](https://github.com/kubernetes/ingress-nginx/pull/6401) Fix controller service annotations +- [X] [#6403](https://github.com/kubernetes/ingress-nginx/pull/6403) Initial helm chart changelog + +### 3.7.1 + +- [X] [#6326](https://github.com/kubernetes/ingress-nginx/pull/6326) Fix liveness and readiness probe path in daemonset chart + +### 3.7.0 + +- [X] [#6316](https://github.com/kubernetes/ingress-nginx/pull/6316) Numerals in podAnnotations in quotes [#6315](https://github.com/kubernetes/ingress-nginx/issues/6315) + +### 3.6.0 + +- [X] [#6305](https://github.com/kubernetes/ingress-nginx/pull/6305) Add default linux nodeSelector + +### 3.5.1 + +- [X] [#6299](https://github.com/kubernetes/ingress-nginx/pull/6299) Fix helm chart release + +### 3.5.0 + +- [X] [#6260](https://github.com/kubernetes/ingress-nginx/pull/6260) Allow Helm Chart to customize admission webhook's annotations, timeoutSeconds, namespaceSelector, objectSelector and cert files locations + +### 3.4.0 + +- [X] [#6268](https://github.com/kubernetes/ingress-nginx/pull/6268) Update to 0.40.2 in helm chart #6288 + +### 3.3.1 + +- [X] [#6259](https://github.com/kubernetes/ingress-nginx/pull/6259) Release helm chart +- [X] [#6258](https://github.com/kubernetes/ingress-nginx/pull/6258) Fix chart markdown link +- [X] [#6253](https://github.com/kubernetes/ingress-nginx/pull/6253) Release v0.40.0 + +### 3.3.1 + +- [X] [#6233](https://github.com/kubernetes/ingress-nginx/pull/6233) Add admission controller e2e test + +### 3.3.0 + +- [X] [#6203](https://github.com/kubernetes/ingress-nginx/pull/6203) Refactor parsing of key values +- [X] [#6162](https://github.com/kubernetes/ingress-nginx/pull/6162) Add helm chart options to expose metrics service as NodePort +- [X] [#6180](https://github.com/kubernetes/ingress-nginx/pull/6180) Fix helm chart admissionReviewVersions regression +- [X] [#6169](https://github.com/kubernetes/ingress-nginx/pull/6169) Fix Typo in example prometheus rules + +### 3.0.0 + +- [X] [#6167](https://github.com/kubernetes/ingress-nginx/pull/6167) Update chart requirements + +### 2.16.0 + +- [X] [#6154](https://github.com/kubernetes/ingress-nginx/pull/6154) add `topologySpreadConstraint` to controller + +### 2.15.0 + +- [X] [#6087](https://github.com/kubernetes/ingress-nginx/pull/6087) Adding parameter for externalTrafficPolicy in internal controller service spec + +### 2.14.0 + +- [X] [#6104](https://github.com/kubernetes/ingress-nginx/pull/6104) Misc fixes for nginx-ingress chart for better keel and prometheus-operator integration + +### 2.13.0 + +- [X] [#6093](https://github.com/kubernetes/ingress-nginx/pull/6093) Release v0.35.0 + +### 2.13.0 + +- [X] [#6093](https://github.com/kubernetes/ingress-nginx/pull/6093) Release v0.35.0 +- [X] [#6080](https://github.com/kubernetes/ingress-nginx/pull/6080) Switch images to k8s.gcr.io after Vanity Domain Flip + +### 2.12.1 + +- [X] [#6075](https://github.com/kubernetes/ingress-nginx/pull/6075) Sync helm chart affinity examples + +### 2.12.0 + +- [X] [#6039](https://github.com/kubernetes/ingress-nginx/pull/6039) Add configurable serviceMonitor metricRelabelling and targetLabels +- [X] [#6044](https://github.com/kubernetes/ingress-nginx/pull/6044) Fix YAML linting + +### 2.11.3 + +- [X] [#6038](https://github.com/kubernetes/ingress-nginx/pull/6038) Bump chart version PATCH + +### 2.11.2 + +- [X] [#5951](https://github.com/kubernetes/ingress-nginx/pull/5951) Bump chart patch version + +### 2.11.1 + +- [X] [#5900](https://github.com/kubernetes/ingress-nginx/pull/5900) Release helm chart for v0.34.1 + +### 2.11.0 + +- [X] [#5879](https://github.com/kubernetes/ingress-nginx/pull/5879) Update helm chart for v0.34.0 +- [X] [#5671](https://github.com/kubernetes/ingress-nginx/pull/5671) Make liveness probe more fault tolerant than readiness probe + +### 2.10.0 + +- [X] [#5843](https://github.com/kubernetes/ingress-nginx/pull/5843) Update jettech/kube-webhook-certgen image + +### 2.9.1 + +- [X] [#5823](https://github.com/kubernetes/ingress-nginx/pull/5823) Add quoting to sysctls because numeric values need to be presented as strings (#5823) + +### 2.9.0 + +- [X] [#5795](https://github.com/kubernetes/ingress-nginx/pull/5795) Use fully qualified images to avoid cri-o issues + + +### TODO + +Keep building the changelog using *git log charts* checking the tag diff --git a/hosting/kubernetes/budibase/charts/ingress-nginx/Chart.yaml b/hosting/kubernetes/budibase/charts/ingress-nginx/Chart.yaml new file mode 100644 index 0000000000..70e0df6197 --- /dev/null +++ b/hosting/kubernetes/budibase/charts/ingress-nginx/Chart.yaml @@ -0,0 +1,19 @@ +annotations: + artifacthub.io/changes: | + - Add namespace field in the namespace scoped resource templates +apiVersion: v2 +appVersion: 0.48.1 +description: Ingress controller for Kubernetes using NGINX as a reverse proxy and load balancer +home: https://github.com/kubernetes/ingress-nginx +icon: https://upload.wikimedia.org/wikipedia/commons/thumb/c/c5/Nginx_logo.svg/500px-Nginx_logo.svg.png +keywords: +- ingress +- nginx +kubeVersion: '>=1.16.0-0' +maintainers: +- name: ChiefAlexander +name: ingress-nginx +sources: +- https://github.com/kubernetes/ingress-nginx +type: application +version: 3.35.0 diff --git a/hosting/kubernetes/budibase/charts/ingress-nginx/OWNERS b/hosting/kubernetes/budibase/charts/ingress-nginx/OWNERS new file mode 100644 index 0000000000..392bc92f55 --- /dev/null +++ b/hosting/kubernetes/budibase/charts/ingress-nginx/OWNERS @@ -0,0 +1,10 @@ +# See the OWNERS docs: https://github.com/kubernetes/community/blob/master/contributors/guide/owners.md + +approvers: +- ingress-nginx-helm-maintainers + +reviewers: +- ingress-nginx-helm-reviewers + +labels: +- area/helm \ No newline at end of file diff --git a/hosting/kubernetes/budibase/charts/ingress-nginx/README.md b/hosting/kubernetes/budibase/charts/ingress-nginx/README.md new file mode 100644 index 0000000000..d5a2c3ca84 --- /dev/null +++ b/hosting/kubernetes/budibase/charts/ingress-nginx/README.md @@ -0,0 +1,226 @@ +# ingress-nginx + +[ingress-nginx](https://github.com/kubernetes/ingress-nginx) Ingress controller for Kubernetes using NGINX as a reverse proxy and load balancer + +To use, add the `kubernetes.io/ingress.class: nginx` annotation to your Ingress resources. + +This chart bootstraps an ingress-nginx deployment on a [Kubernetes](http://kubernetes.io) cluster using the [Helm](https://helm.sh) package manager. + +## Prerequisites + +- Kubernetes v1.16+ + +## Get Repo Info + +```console +helm repo add ingress-nginx https://kubernetes.github.io/ingress-nginx +helm repo update +``` + +## Install Chart + +**Important:** only helm3 is supported + +```console +helm install [RELEASE_NAME] ingress-nginx/ingress-nginx +``` + +The command deploys ingress-nginx on the Kubernetes cluster in the default configuration. + +_See [configuration](#configuration) below._ + +_See [helm install](https://helm.sh/docs/helm/helm_install/) for command documentation._ + +## Uninstall Chart + +```console +helm uninstall [RELEASE_NAME] +``` + +This removes all the Kubernetes components associated with the chart and deletes the release. + +_See [helm uninstall](https://helm.sh/docs/helm/helm_uninstall/) for command documentation._ + +## Upgrading Chart + +```console +helm upgrade [RELEASE_NAME] [CHART] --install +``` + +_See [helm upgrade](https://helm.sh/docs/helm/helm_upgrade/) for command documentation._ + +### Upgrading With Zero Downtime in Production + +By default the ingress-nginx controller has service interruptions whenever it's pods are restarted or redeployed. In order to fix that, see the excellent blog post by Lindsay Landry from Codecademy: [Kubernetes: Nginx and Zero Downtime in Production](https://medium.com/codecademy-engineering/kubernetes-nginx-and-zero-downtime-in-production-2c910c6a5ed8). + +### Migrating from stable/nginx-ingress + +There are two main ways to migrate a release from `stable/nginx-ingress` to `ingress-nginx/ingress-nginx` chart: + +1. For Nginx Ingress controllers used for non-critical services, the easiest method is to [uninstall](#uninstall-chart) the old release and [install](#install-chart) the new one +1. For critical services in production that require zero-downtime, you will want to: + 1. [Install](#install-chart) a second Ingress controller + 1. Redirect your DNS traffic from the old controller to the new controller + 1. Log traffic from both controllers during this changeover + 1. [Uninstall](#uninstall-chart) the old controller once traffic has fully drained from it + 1. For details on all of these steps see [Upgrading With Zero Downtime in Production](#upgrading-with-zero-downtime-in-production) + +Note that there are some different and upgraded configurations between the two charts, described by Rimas Mocevicius from JFrog in the "Upgrading to ingress-nginx Helm chart" section of [Migrating from Helm chart nginx-ingress to ingress-nginx](https://rimusz.net/migrating-to-ingress-nginx). As the `ingress-nginx/ingress-nginx` chart continues to update, you will want to check current differences by running [helm configuration](#configuration) commands on both charts. + +## Configuration + +See [Customizing the Chart Before Installing](https://helm.sh/docs/intro/using_helm/#customizing-the-chart-before-installing). To see all configurable options with detailed comments, visit the chart's [values.yaml](./values.yaml), or run these configuration commands: + +```console +helm show values ingress-nginx/ingress-nginx +``` + +### PodDisruptionBudget + +Note that the PodDisruptionBudget resource will only be defined if the replicaCount is greater than one, +else it would make it impossible to evacuate a node. See [gh issue #7127](https://github.com/helm/charts/issues/7127) for more info. + +### Prometheus Metrics + +The Nginx ingress controller can export Prometheus metrics, by setting `controller.metrics.enabled` to `true`. + +You can add Prometheus annotations to the metrics service using `controller.metrics.service.annotations`. Alternatively, if you use the Prometheus Operator, you can enable ServiceMonitor creation using `controller.metrics.serviceMonitor.enabled`. + +### ingress-nginx nginx\_status page/stats server + +Previous versions of this chart had a `controller.stats.*` configuration block, which is now obsolete due to the following changes in nginx ingress controller: + +- In [0.16.1](https://github.com/kubernetes/ingress-nginx/blob/master/Changelog.md#0161), the vts (virtual host traffic status) dashboard was removed +- In [0.23.0](https://github.com/kubernetes/ingress-nginx/blob/master/Changelog.md#0230), the status page at port 18080 is now a unix socket webserver only available at localhost. + You can use `curl --unix-socket /tmp/nginx-status-server.sock http://localhost/nginx_status` inside the controller container to access it locally, or use the snippet from [nginx-ingress changelog](https://github.com/kubernetes/ingress-nginx/blob/master/Changelog.md#0230) to re-enable the http server + +### ExternalDNS Service Configuration + +Add an [ExternalDNS](https://github.com/kubernetes-incubator/external-dns) annotation to the LoadBalancer service: + +```yaml +controller: + service: + annotations: + external-dns.alpha.kubernetes.io/hostname: kubernetes-example.com. +``` + +### AWS L7 ELB with SSL Termination + +Annotate the controller as shown in the [nginx-ingress l7 patch](https://github.com/kubernetes/ingress-nginx/blob/master/deploy/aws/l7/service-l7.yaml): + +```yaml +controller: + service: + targetPorts: + http: http + https: http + annotations: + service.beta.kubernetes.io/aws-load-balancer-ssl-cert: arn:aws:acm:XX-XXXX-X:XXXXXXXXX:certificate/XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXX + service.beta.kubernetes.io/aws-load-balancer-backend-protocol: "http" + service.beta.kubernetes.io/aws-load-balancer-ssl-ports: "https" + service.beta.kubernetes.io/aws-load-balancer-connection-idle-timeout: '3600' +``` + +### AWS route53-mapper + +To configure the LoadBalancer service with the [route53-mapper addon](https://github.com/kubernetes/kops/tree/master/addons/route53-mapper), add the `domainName` annotation and `dns` label: + +```yaml +controller: + service: + labels: + dns: "route53" + annotations: + domainName: "kubernetes-example.com" +``` + +### Additional Internal Load Balancer + +This setup is useful when you need both external and internal load balancers but don't want to have multiple ingress controllers and multiple ingress objects per application. + +By default, the ingress object will point to the external load balancer address, but if correctly configured, you can make use of the internal one if the URL you are looking up resolves to the internal load balancer's URL. + +You'll need to set both the following values: + +`controller.service.internal.enabled` +`controller.service.internal.annotations` + +If one of them is missing the internal load balancer will not be deployed. Example you may have `controller.service.internal.enabled=true` but no annotations set, in this case no action will be taken. + +`controller.service.internal.annotations` varies with the cloud service you're using. + +Example for AWS: + +```yaml +controller: + service: + internal: + enabled: true + annotations: + # Create internal ELB + service.beta.kubernetes.io/aws-load-balancer-internal: "true" + # Any other annotation can be declared here. +``` + +Example for GCE: + +```yaml +controller: + service: + internal: + enabled: true + annotations: + # Create internal LB. More informations: https://cloud.google.com/kubernetes-engine/docs/how-to/internal-load-balancing + # For GKE versions 1.17 and later + networking.gke.io/load-balancer-type: "Internal" + # For earlier versions + # cloud.google.com/load-balancer-type: "Internal" + + # Any other annotation can be declared here. +``` + +Example for Azure: + +```yaml +controller: + service: + annotations: + # Create internal LB + service.beta.kubernetes.io/azure-load-balancer-internal: "true" + # Any other annotation can be declared here. +``` + +Example for Oracle Cloud Infrastructure: + +```yaml +controller: + service: + annotations: + # Create internal LB + service.beta.kubernetes.io/oci-load-balancer-internal: "true" + # Any other annotation can be declared here. +``` + +An use case for this scenario is having a split-view DNS setup where the public zone CNAME records point to the external balancer URL while the private zone CNAME records point to the internal balancer URL. This way, you only need one ingress kubernetes object. + +Optionally you can set `controller.service.loadBalancerIP` if you need a static IP for the resulting `LoadBalancer`. + +### Ingress Admission Webhooks + +With nginx-ingress-controller version 0.25+, the nginx ingress controller pod exposes an endpoint that will integrate with the `validatingwebhookconfiguration` Kubernetes feature to prevent bad ingress from being added to the cluster. +**This feature is enabled by default since 0.31.0.** + +With nginx-ingress-controller in 0.25.* work only with kubernetes 1.14+, 0.26 fix [this issue](https://github.com/kubernetes/ingress-nginx/pull/4521) + +### Helm Error When Upgrading: spec.clusterIP: Invalid value: "" + +If you are upgrading this chart from a version between 0.31.0 and 1.2.2 then you may get an error like this: + +```console +Error: UPGRADE FAILED: Service "?????-controller" is invalid: spec.clusterIP: Invalid value: "": field is immutable +``` + +Detail of how and why are in [this issue](https://github.com/helm/charts/pull/13646) but to resolve this you can set `xxxx.service.omitClusterIP` to `true` where `xxxx` is the service referenced in the error. + +As of version `1.26.0` of this chart, by simply not providing any clusterIP value, `invalid: spec.clusterIP: Invalid value: "": field is immutable` will no longer occur since `clusterIP: ""` will not be rendered. diff --git a/hosting/kubernetes/budibase/charts/ingress-nginx/ci/daemonset-customconfig-values.yaml b/hosting/kubernetes/budibase/charts/ingress-nginx/ci/daemonset-customconfig-values.yaml new file mode 100644 index 0000000000..e12b53421b --- /dev/null +++ b/hosting/kubernetes/budibase/charts/ingress-nginx/ci/daemonset-customconfig-values.yaml @@ -0,0 +1,9 @@ +controller: + kind: DaemonSet + admissionWebhooks: + enabled: false + service: + type: ClusterIP + + config: + use-proxy-protocol: "true" diff --git a/hosting/kubernetes/budibase/charts/ingress-nginx/ci/daemonset-customnodeport-values.yaml b/hosting/kubernetes/budibase/charts/ingress-nginx/ci/daemonset-customnodeport-values.yaml new file mode 100644 index 0000000000..cfc545f69f --- /dev/null +++ b/hosting/kubernetes/budibase/charts/ingress-nginx/ci/daemonset-customnodeport-values.yaml @@ -0,0 +1,18 @@ +controller: + kind: DaemonSet + admissionWebhooks: + enabled: false + + service: + type: NodePort + nodePorts: + tcp: + 9000: 30090 + udp: + 9001: 30091 + +tcp: + 9000: "default/test:8080" + +udp: + 9001: "default/test:8080" diff --git a/hosting/kubernetes/budibase/charts/ingress-nginx/ci/daemonset-headers-values.yaml b/hosting/kubernetes/budibase/charts/ingress-nginx/ci/daemonset-headers-values.yaml new file mode 100644 index 0000000000..ff82cd9c70 --- /dev/null +++ b/hosting/kubernetes/budibase/charts/ingress-nginx/ci/daemonset-headers-values.yaml @@ -0,0 +1,10 @@ +controller: + kind: DaemonSet + admissionWebhooks: + enabled: false + addHeaders: + X-Frame-Options: deny + proxySetHeaders: + X-Forwarded-Proto: https + service: + type: ClusterIP diff --git a/hosting/kubernetes/budibase/charts/ingress-nginx/ci/daemonset-internal-lb-values.yaml b/hosting/kubernetes/budibase/charts/ingress-nginx/ci/daemonset-internal-lb-values.yaml new file mode 100644 index 0000000000..d8948d634b --- /dev/null +++ b/hosting/kubernetes/budibase/charts/ingress-nginx/ci/daemonset-internal-lb-values.yaml @@ -0,0 +1,10 @@ +controller: + kind: DaemonSet + admissionWebhooks: + enabled: false + service: + type: ClusterIP + internal: + enabled: true + annotations: + service.beta.kubernetes.io/aws-load-balancer-internal: "true" diff --git a/hosting/kubernetes/budibase/charts/ingress-nginx/ci/daemonset-nodeport-values.yaml b/hosting/kubernetes/budibase/charts/ingress-nginx/ci/daemonset-nodeport-values.yaml new file mode 100644 index 0000000000..6d6605f0e1 --- /dev/null +++ b/hosting/kubernetes/budibase/charts/ingress-nginx/ci/daemonset-nodeport-values.yaml @@ -0,0 +1,6 @@ +controller: + kind: DaemonSet + admissionWebhooks: + enabled: false + service: + type: NodePort diff --git a/hosting/kubernetes/budibase/charts/ingress-nginx/ci/daemonset-podannotations-values.yaml b/hosting/kubernetes/budibase/charts/ingress-nginx/ci/daemonset-podannotations-values.yaml new file mode 100644 index 0000000000..04ac58dbd8 --- /dev/null +++ b/hosting/kubernetes/budibase/charts/ingress-nginx/ci/daemonset-podannotations-values.yaml @@ -0,0 +1,13 @@ +controller: + kind: DaemonSet + admissionWebhooks: + enabled: false + metrics: + enabled: true + service: + type: ClusterIP + podAnnotations: + prometheus.io/path: /metrics + prometheus.io/port: "10254" + prometheus.io/scheme: http + prometheus.io/scrape: "true" diff --git a/hosting/kubernetes/budibase/charts/ingress-nginx/ci/daemonset-tcp-udp-configMapNamespace-values.yaml b/hosting/kubernetes/budibase/charts/ingress-nginx/ci/daemonset-tcp-udp-configMapNamespace-values.yaml new file mode 100644 index 0000000000..afb5487c57 --- /dev/null +++ b/hosting/kubernetes/budibase/charts/ingress-nginx/ci/daemonset-tcp-udp-configMapNamespace-values.yaml @@ -0,0 +1,16 @@ +controller: + kind: DaemonSet + admissionWebhooks: + enabled: false + service: + type: ClusterIP + tcp: + configMapNamespace: default + udp: + configMapNamespace: default + +tcp: + 9000: "default/test:8080" + +udp: + 9001: "default/test:8080" diff --git a/hosting/kubernetes/budibase/charts/ingress-nginx/ci/daemonset-tcp-udp-values.yaml b/hosting/kubernetes/budibase/charts/ingress-nginx/ci/daemonset-tcp-udp-values.yaml new file mode 100644 index 0000000000..7b4d7cbe7d --- /dev/null +++ b/hosting/kubernetes/budibase/charts/ingress-nginx/ci/daemonset-tcp-udp-values.yaml @@ -0,0 +1,12 @@ +controller: + kind: DaemonSet + admissionWebhooks: + enabled: false + service: + type: ClusterIP + +tcp: + 9000: "default/test:8080" + +udp: + 9001: "default/test:8080" diff --git a/hosting/kubernetes/budibase/charts/ingress-nginx/ci/daemonset-tcp-values.yaml b/hosting/kubernetes/budibase/charts/ingress-nginx/ci/daemonset-tcp-values.yaml new file mode 100644 index 0000000000..a359a6a401 --- /dev/null +++ b/hosting/kubernetes/budibase/charts/ingress-nginx/ci/daemonset-tcp-values.yaml @@ -0,0 +1,10 @@ +controller: + kind: DaemonSet + admissionWebhooks: + enabled: false + service: + type: ClusterIP + +tcp: + 9000: "default/test:8080" + 9001: "default/test:8080" diff --git a/hosting/kubernetes/budibase/charts/ingress-nginx/ci/deamonset-default-values.yaml b/hosting/kubernetes/budibase/charts/ingress-nginx/ci/deamonset-default-values.yaml new file mode 100644 index 0000000000..e63a7f5db3 --- /dev/null +++ b/hosting/kubernetes/budibase/charts/ingress-nginx/ci/deamonset-default-values.yaml @@ -0,0 +1,6 @@ +controller: + kind: DaemonSet + admissionWebhooks: + enabled: false + service: + type: ClusterIP diff --git a/hosting/kubernetes/budibase/charts/ingress-nginx/ci/deamonset-metrics-values.yaml b/hosting/kubernetes/budibase/charts/ingress-nginx/ci/deamonset-metrics-values.yaml new file mode 100644 index 0000000000..1e5190afc0 --- /dev/null +++ b/hosting/kubernetes/budibase/charts/ingress-nginx/ci/deamonset-metrics-values.yaml @@ -0,0 +1,8 @@ +controller: + kind: DaemonSet + admissionWebhooks: + enabled: false + metrics: + enabled: true + service: + type: ClusterIP diff --git a/hosting/kubernetes/budibase/charts/ingress-nginx/ci/deamonset-psp-values.yaml b/hosting/kubernetes/budibase/charts/ingress-nginx/ci/deamonset-psp-values.yaml new file mode 100644 index 0000000000..017b60a9c6 --- /dev/null +++ b/hosting/kubernetes/budibase/charts/ingress-nginx/ci/deamonset-psp-values.yaml @@ -0,0 +1,9 @@ +controller: + kind: DaemonSet + admissionWebhooks: + enabled: false + service: + type: ClusterIP + +podSecurityPolicy: + enabled: true diff --git a/hosting/kubernetes/budibase/charts/ingress-nginx/ci/deamonset-webhook-and-psp-values.yaml b/hosting/kubernetes/budibase/charts/ingress-nginx/ci/deamonset-webhook-and-psp-values.yaml new file mode 100644 index 0000000000..88aafc66fd --- /dev/null +++ b/hosting/kubernetes/budibase/charts/ingress-nginx/ci/deamonset-webhook-and-psp-values.yaml @@ -0,0 +1,9 @@ +controller: + kind: DaemonSet + admissionWebhooks: + enabled: true + service: + type: ClusterIP + +podSecurityPolicy: + enabled: true diff --git a/hosting/kubernetes/budibase/charts/ingress-nginx/ci/deamonset-webhook-values.yaml b/hosting/kubernetes/budibase/charts/ingress-nginx/ci/deamonset-webhook-values.yaml new file mode 100644 index 0000000000..6e3b371da6 --- /dev/null +++ b/hosting/kubernetes/budibase/charts/ingress-nginx/ci/deamonset-webhook-values.yaml @@ -0,0 +1,6 @@ +controller: + kind: DaemonSet + admissionWebhooks: + enabled: true + service: + type: ClusterIP diff --git a/hosting/kubernetes/budibase/charts/ingress-nginx/ci/deployment-autoscaling-values.yaml b/hosting/kubernetes/budibase/charts/ingress-nginx/ci/deployment-autoscaling-values.yaml new file mode 100644 index 0000000000..5314cecb38 --- /dev/null +++ b/hosting/kubernetes/budibase/charts/ingress-nginx/ci/deployment-autoscaling-values.yaml @@ -0,0 +1,7 @@ +controller: + autoscaling: + enabled: true + admissionWebhooks: + enabled: false + service: + type: ClusterIP diff --git a/hosting/kubernetes/budibase/charts/ingress-nginx/ci/deployment-customconfig-values.yaml b/hosting/kubernetes/budibase/charts/ingress-nginx/ci/deployment-customconfig-values.yaml new file mode 100644 index 0000000000..f232531acb --- /dev/null +++ b/hosting/kubernetes/budibase/charts/ingress-nginx/ci/deployment-customconfig-values.yaml @@ -0,0 +1,7 @@ +controller: + config: + use-proxy-protocol: "true" + admissionWebhooks: + enabled: false + service: + type: ClusterIP diff --git a/hosting/kubernetes/budibase/charts/ingress-nginx/ci/deployment-customnodeport-values.yaml b/hosting/kubernetes/budibase/charts/ingress-nginx/ci/deployment-customnodeport-values.yaml new file mode 100644 index 0000000000..9eda282b13 --- /dev/null +++ b/hosting/kubernetes/budibase/charts/ingress-nginx/ci/deployment-customnodeport-values.yaml @@ -0,0 +1,16 @@ +controller: + admissionWebhooks: + enabled: false + service: + type: NodePort + nodePorts: + tcp: + 9000: 30090 + udp: + 9001: 30091 + +tcp: + 9000: "default/test:8080" + +udp: + 9001: "default/test:8080" diff --git a/hosting/kubernetes/budibase/charts/ingress-nginx/ci/deployment-default-values.yaml b/hosting/kubernetes/budibase/charts/ingress-nginx/ci/deployment-default-values.yaml new file mode 100644 index 0000000000..93a393c975 --- /dev/null +++ b/hosting/kubernetes/budibase/charts/ingress-nginx/ci/deployment-default-values.yaml @@ -0,0 +1,4 @@ +# Left blank to test default values +controller: + service: + type: ClusterIP diff --git a/hosting/kubernetes/budibase/charts/ingress-nginx/ci/deployment-headers-values.yaml b/hosting/kubernetes/budibase/charts/ingress-nginx/ci/deployment-headers-values.yaml new file mode 100644 index 0000000000..665fd48d35 --- /dev/null +++ b/hosting/kubernetes/budibase/charts/ingress-nginx/ci/deployment-headers-values.yaml @@ -0,0 +1,9 @@ +controller: + admissionWebhooks: + enabled: false + addHeaders: + X-Frame-Options: deny + proxySetHeaders: + X-Forwarded-Proto: https + service: + type: ClusterIP diff --git a/hosting/kubernetes/budibase/charts/ingress-nginx/ci/deployment-internal-lb-values.yaml b/hosting/kubernetes/budibase/charts/ingress-nginx/ci/deployment-internal-lb-values.yaml new file mode 100644 index 0000000000..c7f22d636e --- /dev/null +++ b/hosting/kubernetes/budibase/charts/ingress-nginx/ci/deployment-internal-lb-values.yaml @@ -0,0 +1,9 @@ +controller: + admissionWebhooks: + enabled: false + service: + type: ClusterIP + internal: + enabled: true + annotations: + service.beta.kubernetes.io/aws-load-balancer-internal: "true" diff --git a/hosting/kubernetes/budibase/charts/ingress-nginx/ci/deployment-metrics-values.yaml b/hosting/kubernetes/budibase/charts/ingress-nginx/ci/deployment-metrics-values.yaml new file mode 100644 index 0000000000..887ed0f620 --- /dev/null +++ b/hosting/kubernetes/budibase/charts/ingress-nginx/ci/deployment-metrics-values.yaml @@ -0,0 +1,7 @@ +controller: + admissionWebhooks: + enabled: false + metrics: + enabled: true + service: + type: ClusterIP diff --git a/hosting/kubernetes/budibase/charts/ingress-nginx/ci/deployment-nodeport-values.yaml b/hosting/kubernetes/budibase/charts/ingress-nginx/ci/deployment-nodeport-values.yaml new file mode 100644 index 0000000000..84f1f7582e --- /dev/null +++ b/hosting/kubernetes/budibase/charts/ingress-nginx/ci/deployment-nodeport-values.yaml @@ -0,0 +1,5 @@ +controller: + admissionWebhooks: + enabled: false + service: + type: NodePort diff --git a/hosting/kubernetes/budibase/charts/ingress-nginx/ci/deployment-podannotations-values.yaml b/hosting/kubernetes/budibase/charts/ingress-nginx/ci/deployment-podannotations-values.yaml new file mode 100644 index 0000000000..b65a0910b3 --- /dev/null +++ b/hosting/kubernetes/budibase/charts/ingress-nginx/ci/deployment-podannotations-values.yaml @@ -0,0 +1,12 @@ +controller: + admissionWebhooks: + enabled: false + metrics: + enabled: true + service: + type: ClusterIP + podAnnotations: + prometheus.io/path: /metrics + prometheus.io/port: "10254" + prometheus.io/scheme: http + prometheus.io/scrape: "true" diff --git a/hosting/kubernetes/budibase/charts/ingress-nginx/ci/deployment-psp-values.yaml b/hosting/kubernetes/budibase/charts/ingress-nginx/ci/deployment-psp-values.yaml new file mode 100644 index 0000000000..e339c69c32 --- /dev/null +++ b/hosting/kubernetes/budibase/charts/ingress-nginx/ci/deployment-psp-values.yaml @@ -0,0 +1,6 @@ +controller: + service: + type: ClusterIP + +podSecurityPolicy: + enabled: true diff --git a/hosting/kubernetes/budibase/charts/ingress-nginx/ci/deployment-tcp-udp-configMapNamespace-values.yaml b/hosting/kubernetes/budibase/charts/ingress-nginx/ci/deployment-tcp-udp-configMapNamespace-values.yaml new file mode 100644 index 0000000000..141e06b687 --- /dev/null +++ b/hosting/kubernetes/budibase/charts/ingress-nginx/ci/deployment-tcp-udp-configMapNamespace-values.yaml @@ -0,0 +1,15 @@ +controller: + admissionWebhooks: + enabled: false + service: + type: ClusterIP + tcp: + configMapNamespace: default + udp: + configMapNamespace: default + +tcp: + 9000: "default/test:8080" + +udp: + 9001: "default/test:8080" diff --git a/hosting/kubernetes/budibase/charts/ingress-nginx/ci/deployment-tcp-udp-values.yaml b/hosting/kubernetes/budibase/charts/ingress-nginx/ci/deployment-tcp-udp-values.yaml new file mode 100644 index 0000000000..bc29abeba7 --- /dev/null +++ b/hosting/kubernetes/budibase/charts/ingress-nginx/ci/deployment-tcp-udp-values.yaml @@ -0,0 +1,11 @@ +controller: + admissionWebhooks: + enabled: false + service: + type: ClusterIP + +tcp: + 9000: "default/test:8080" + +udp: + 9001: "default/test:8080" diff --git a/hosting/kubernetes/budibase/charts/ingress-nginx/ci/deployment-tcp-values.yaml b/hosting/kubernetes/budibase/charts/ingress-nginx/ci/deployment-tcp-values.yaml new file mode 100644 index 0000000000..b7f54c09fa --- /dev/null +++ b/hosting/kubernetes/budibase/charts/ingress-nginx/ci/deployment-tcp-values.yaml @@ -0,0 +1,7 @@ +controller: + service: + type: ClusterIP + +tcp: + 9000: "default/test:8080" + 9001: "default/test:8080" diff --git a/hosting/kubernetes/budibase/charts/ingress-nginx/ci/deployment-webhook-and-psp-values.yaml b/hosting/kubernetes/budibase/charts/ingress-nginx/ci/deployment-webhook-and-psp-values.yaml new file mode 100644 index 0000000000..a829c36144 --- /dev/null +++ b/hosting/kubernetes/budibase/charts/ingress-nginx/ci/deployment-webhook-and-psp-values.yaml @@ -0,0 +1,8 @@ +controller: + admissionWebhooks: + enabled: true + service: + type: ClusterIP + +podSecurityPolicy: + enabled: true diff --git a/hosting/kubernetes/budibase/charts/ingress-nginx/ci/deployment-webhook-values.yaml b/hosting/kubernetes/budibase/charts/ingress-nginx/ci/deployment-webhook-values.yaml new file mode 100644 index 0000000000..4f18a70b9f --- /dev/null +++ b/hosting/kubernetes/budibase/charts/ingress-nginx/ci/deployment-webhook-values.yaml @@ -0,0 +1,5 @@ +controller: + admissionWebhooks: + enabled: true + service: + type: ClusterIP diff --git a/hosting/kubernetes/budibase/charts/ingress-nginx/templates/NOTES.txt b/hosting/kubernetes/budibase/charts/ingress-nginx/templates/NOTES.txt new file mode 100644 index 0000000000..60fb2c1f62 --- /dev/null +++ b/hosting/kubernetes/budibase/charts/ingress-nginx/templates/NOTES.txt @@ -0,0 +1,71 @@ +The ingress-nginx controller has been installed. + +{{- if contains "NodePort" .Values.controller.service.type }} +Get the application URL by running these commands: + +{{- if (not (empty .Values.controller.service.nodePorts.http)) }} + export HTTP_NODE_PORT={{ .Values.controller.service.nodePorts.http }} +{{- else }} + export HTTP_NODE_PORT=$(kubectl --namespace {{ .Release.Namespace }} get services -o jsonpath="{.spec.ports[0].nodePort}" {{ include "ingress-nginx.controller.fullname" . }}) +{{- end }} +{{- if (not (empty .Values.controller.service.nodePorts.https)) }} + export HTTPS_NODE_PORT={{ .Values.controller.service.nodePorts.https }} +{{- else }} + export HTTPS_NODE_PORT=$(kubectl --namespace {{ .Release.Namespace }} get services -o jsonpath="{.spec.ports[1].nodePort}" {{ include "ingress-nginx.controller.fullname" . }}) +{{- end }} + export NODE_IP=$(kubectl --namespace {{ .Release.Namespace }} get nodes -o jsonpath="{.items[0].status.addresses[1].address}") + + echo "Visit http://$NODE_IP:$HTTP_NODE_PORT to access your application via HTTP." + echo "Visit https://$NODE_IP:$HTTPS_NODE_PORT to access your application via HTTPS." +{{- else if contains "LoadBalancer" .Values.controller.service.type }} +It may take a few minutes for the LoadBalancer IP to be available. +You can watch the status by running 'kubectl --namespace {{ .Release.Namespace }} get services -o wide -w {{ include "ingress-nginx.controller.fullname" . }}' +{{- else if contains "ClusterIP" .Values.controller.service.type }} +Get the application URL by running these commands: + export POD_NAME=$(kubectl --namespace {{ .Release.Namespace }} get pods -o jsonpath="{.items[0].metadata.name}" -l "app={{ template "ingress-nginx.name" . }},component={{ .Values.controller.name }},release={{ .Release.Name }}") + kubectl --namespace {{ .Release.Namespace }} port-forward $POD_NAME 8080:80 + echo "Visit http://127.0.0.1:8080 to access your application." +{{- end }} + +An example Ingress that makes use of the controller: + + apiVersion: networking.k8s.io/v1beta1 + kind: Ingress + metadata: + annotations: + kubernetes.io/ingress.class: {{ .Values.controller.ingressClass }} + name: example + namespace: foo + spec: + rules: + - host: www.example.com + http: + paths: + - backend: + serviceName: exampleService + servicePort: 80 + path: / + # This section is only required if TLS is to be enabled for the Ingress + tls: + - hosts: + - www.example.com + secretName: example-tls + +If TLS is enabled for the Ingress, a Secret containing the certificate and key must also be provided: + + apiVersion: v1 + kind: Secret + metadata: + name: example-tls + namespace: foo + data: + tls.crt: + tls.key: + type: kubernetes.io/tls + +{{- if .Values.controller.headers }} +################################################################################# +###### WARNING: `controller.headers` has been deprecated! ##### +###### It has been renamed to `controller.proxySetHeaders`. ##### +################################################################################# +{{- end }} diff --git a/hosting/kubernetes/budibase/charts/ingress-nginx/templates/_helpers.tpl b/hosting/kubernetes/budibase/charts/ingress-nginx/templates/_helpers.tpl new file mode 100644 index 0000000000..8b1fd09513 --- /dev/null +++ b/hosting/kubernetes/budibase/charts/ingress-nginx/templates/_helpers.tpl @@ -0,0 +1,134 @@ +{{/* vim: set filetype=mustache: */}} +{{/* +Expand the name of the chart. +*/}} +{{- define "ingress-nginx.name" -}} +{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" -}} +{{- end -}} + +{{/* +Create chart name and version as used by the chart label. +*/}} +{{- define "ingress-nginx.chart" -}} +{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" -}} +{{- end -}} + +{{/* +Create a default fully qualified app name. +We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). +*/}} +{{- define "ingress-nginx.fullname" -}} +{{- if .Values.fullnameOverride -}} +{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" -}} +{{- else -}} +{{- $name := default .Chart.Name .Values.nameOverride -}} +{{- if contains $name .Release.Name -}} +{{- .Release.Name | trunc 63 | trimSuffix "-" -}} +{{- else -}} +{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" -}} +{{- end -}} +{{- end -}} +{{- end -}} + +{{/* +Create a default fully qualified controller name. +We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). +*/}} +{{- define "ingress-nginx.controller.fullname" -}} +{{- printf "%s-%s" (include "ingress-nginx.fullname" .) .Values.controller.name | trunc 63 | trimSuffix "-" -}} +{{- end -}} + +{{/* +Construct the path for the publish-service. + +By convention this will simply use the / to match the name of the +service generated. + +Users can provide an override for an explicit service they want bound via `.Values.controller.publishService.pathOverride` + +*/}} +{{- define "ingress-nginx.controller.publishServicePath" -}} +{{- $defServiceName := printf "%s/%s" "$(POD_NAMESPACE)" (include "ingress-nginx.controller.fullname" .) -}} +{{- $servicePath := default $defServiceName .Values.controller.publishService.pathOverride }} +{{- print $servicePath | trimSuffix "-" -}} +{{- end -}} + +{{/* +Create a default fully qualified default backend name. +We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). +*/}} +{{- define "ingress-nginx.defaultBackend.fullname" -}} +{{- printf "%s-%s" (include "ingress-nginx.fullname" .) .Values.defaultBackend.name | trunc 63 | trimSuffix "-" -}} +{{- end -}} + +{{/* +Common labels +*/}} +{{- define "ingress-nginx.labels" -}} +helm.sh/chart: {{ include "ingress-nginx.chart" . }} +{{ include "ingress-nginx.selectorLabels" . }} +{{- if .Chart.AppVersion }} +app.kubernetes.io/version: {{ .Chart.AppVersion | quote }} +{{- end }} +app.kubernetes.io/managed-by: {{ .Release.Service }} +{{- end -}} + +{{/* +Selector labels +*/}} +{{- define "ingress-nginx.selectorLabels" -}} +app.kubernetes.io/name: {{ include "ingress-nginx.name" . }} +app.kubernetes.io/instance: {{ .Release.Name }} +{{- end -}} + +{{/* +Create the name of the controller service account to use +*/}} +{{- define "ingress-nginx.serviceAccountName" -}} +{{- if .Values.serviceAccount.create -}} + {{ default (include "ingress-nginx.fullname" .) .Values.serviceAccount.name }} +{{- else -}} + {{ default "default" .Values.serviceAccount.name }} +{{- end -}} +{{- end -}} + +{{/* +Create the name of the backend service account to use - only used when podsecuritypolicy is also enabled +*/}} +{{- define "ingress-nginx.defaultBackend.serviceAccountName" -}} +{{- if .Values.defaultBackend.serviceAccount.create -}} + {{ default (printf "%s-backend" (include "ingress-nginx.fullname" .)) .Values.defaultBackend.serviceAccount.name }} +{{- else -}} + {{ default "default-backend" .Values.defaultBackend.serviceAccount.name }} +{{- end -}} +{{- end -}} + +{{/* +Return the appropriate apiGroup for PodSecurityPolicy. +*/}} +{{- define "podSecurityPolicy.apiGroup" -}} +{{- if semverCompare ">=1.14-0" .Capabilities.KubeVersion.GitVersion -}} +{{- print "policy" -}} +{{- else -}} +{{- print "extensions" -}} +{{- end -}} +{{- end -}} + +{{/* +Check the ingress controller version tag is at most three versions behind the last release +*/}} +{{- define "isControllerTagValid" -}} +{{- if not (semverCompare ">=0.27.0-0" .Values.controller.image.tag) -}} +{{- fail "Controller container image tag should be 0.27.0 or higher" -}} +{{- end -}} +{{- end -}} + +{{/* +IngressClass parameters. +*/}} +{{- define "ingressClass.parameters" -}} + {{- if .Values.controller.ingressClassResource.parameters -}} + parameters: +{{ toYaml .Values.controller.ingressClassResource.parameters | indent 4}} + {{ end }} +{{- end -}} diff --git a/hosting/kubernetes/budibase/charts/ingress-nginx/templates/admission-webhooks/job-patch/clusterrole.yaml b/hosting/kubernetes/budibase/charts/ingress-nginx/templates/admission-webhooks/job-patch/clusterrole.yaml new file mode 100644 index 0000000000..fd762f9354 --- /dev/null +++ b/hosting/kubernetes/budibase/charts/ingress-nginx/templates/admission-webhooks/job-patch/clusterrole.yaml @@ -0,0 +1,31 @@ +{{- if and .Values.controller.admissionWebhooks.enabled .Values.controller.admissionWebhooks.patch.enabled -}} +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: {{ include "ingress-nginx.fullname" . }}-admission + annotations: + "helm.sh/hook": pre-install,pre-upgrade,post-install,post-upgrade + "helm.sh/hook-delete-policy": before-hook-creation,hook-succeeded + labels: + {{- include "ingress-nginx.labels" . | nindent 4 }} + app.kubernetes.io/component: admission-webhook +rules: + - apiGroups: + - admissionregistration.k8s.io + resources: + - validatingwebhookconfigurations + verbs: + - get + - update +{{- if .Values.podSecurityPolicy.enabled }} + - apiGroups: ['extensions'] + resources: ['podsecuritypolicies'] + verbs: ['use'] + resourceNames: + {{- with .Values.controller.admissionWebhooks.existingPsp }} + - {{ . }} + {{- else }} + - {{ include "ingress-nginx.fullname" . }}-admission + {{- end }} +{{- end }} +{{- end }} diff --git a/hosting/kubernetes/budibase/charts/ingress-nginx/templates/admission-webhooks/job-patch/clusterrolebinding.yaml b/hosting/kubernetes/budibase/charts/ingress-nginx/templates/admission-webhooks/job-patch/clusterrolebinding.yaml new file mode 100644 index 0000000000..4990fb1c34 --- /dev/null +++ b/hosting/kubernetes/budibase/charts/ingress-nginx/templates/admission-webhooks/job-patch/clusterrolebinding.yaml @@ -0,0 +1,20 @@ +{{- if and .Values.controller.admissionWebhooks.enabled .Values.controller.admissionWebhooks.patch.enabled -}} +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + name: {{ include "ingress-nginx.fullname" . }}-admission + annotations: + "helm.sh/hook": pre-install,pre-upgrade,post-install,post-upgrade + "helm.sh/hook-delete-policy": before-hook-creation,hook-succeeded + labels: + {{- include "ingress-nginx.labels" . | nindent 4 }} + app.kubernetes.io/component: admission-webhook +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: {{ include "ingress-nginx.fullname" . }}-admission +subjects: + - kind: ServiceAccount + name: {{ include "ingress-nginx.fullname" . }}-admission + namespace: {{ .Release.Namespace | quote }} +{{- end }} diff --git a/hosting/kubernetes/budibase/charts/ingress-nginx/templates/admission-webhooks/job-patch/job-createSecret.yaml b/hosting/kubernetes/budibase/charts/ingress-nginx/templates/admission-webhooks/job-patch/job-createSecret.yaml new file mode 100644 index 0000000000..3656be4876 --- /dev/null +++ b/hosting/kubernetes/budibase/charts/ingress-nginx/templates/admission-webhooks/job-patch/job-createSecret.yaml @@ -0,0 +1,61 @@ +{{- if and .Values.controller.admissionWebhooks.enabled .Values.controller.admissionWebhooks.patch.enabled -}} +apiVersion: batch/v1 +kind: Job +metadata: + name: {{ include "ingress-nginx.fullname" . }}-admission-create + namespace: {{ .Release.Namespace }} + annotations: + "helm.sh/hook": pre-install,pre-upgrade + "helm.sh/hook-delete-policy": before-hook-creation,hook-succeeded + labels: + {{- include "ingress-nginx.labels" . | nindent 4 }} + app.kubernetes.io/component: admission-webhook +spec: +{{- if .Capabilities.APIVersions.Has "batch/v1alpha1" }} + # Alpha feature since k8s 1.12 + ttlSecondsAfterFinished: 0 +{{- end }} + template: + metadata: + name: {{ include "ingress-nginx.fullname" . }}-admission-create + {{- if .Values.controller.admissionWebhooks.patch.podAnnotations }} + annotations: {{ toYaml .Values.controller.admissionWebhooks.patch.podAnnotations | nindent 8 }} + {{- end }} + labels: + {{- include "ingress-nginx.labels" . | nindent 8 }} + app.kubernetes.io/component: admission-webhook + spec: + {{- if .Values.controller.admissionWebhooks.patch.priorityClassName }} + priorityClassName: {{ .Values.controller.admissionWebhooks.patch.priorityClassName }} + {{- end }} + {{- if .Values.imagePullSecrets }} + imagePullSecrets: {{ toYaml .Values.imagePullSecrets | nindent 8 }} + {{- end }} + containers: + - name: create + {{- with .Values.controller.admissionWebhooks.patch.image }} + image: "{{- if .repository -}}{{ .repository }}{{ else }}{{ .registry }}/{{ .image }}{{- end -}}:{{ .tag }}{{- if (.digest) -}} @{{.digest}} {{- end -}}" + {{- end }} + imagePullPolicy: {{ .Values.controller.admissionWebhooks.patch.image.pullPolicy }} + args: + - create + - --host={{ include "ingress-nginx.controller.fullname" . }}-admission,{{ include "ingress-nginx.controller.fullname" . }}-admission.$(POD_NAMESPACE).svc + - --namespace=$(POD_NAMESPACE) + - --secret-name={{ include "ingress-nginx.fullname" . }}-admission + env: + - name: POD_NAMESPACE + valueFrom: + fieldRef: + fieldPath: metadata.namespace + restartPolicy: OnFailure + serviceAccountName: {{ include "ingress-nginx.fullname" . }}-admission + {{- if .Values.controller.admissionWebhooks.patch.nodeSelector }} + nodeSelector: {{ toYaml .Values.controller.admissionWebhooks.patch.nodeSelector | nindent 8 }} + {{- end }} + {{- if .Values.controller.admissionWebhooks.patch.tolerations }} + tolerations: {{ toYaml .Values.controller.admissionWebhooks.patch.tolerations | nindent 8 }} + {{- end }} + securityContext: + runAsNonRoot: true + runAsUser: {{ .Values.controller.admissionWebhooks.patch.runAsUser }} +{{- end }} diff --git a/hosting/kubernetes/budibase/charts/ingress-nginx/templates/admission-webhooks/job-patch/job-patchWebhook.yaml b/hosting/kubernetes/budibase/charts/ingress-nginx/templates/admission-webhooks/job-patch/job-patchWebhook.yaml new file mode 100644 index 0000000000..9e9bd0138d --- /dev/null +++ b/hosting/kubernetes/budibase/charts/ingress-nginx/templates/admission-webhooks/job-patch/job-patchWebhook.yaml @@ -0,0 +1,63 @@ +{{- if and .Values.controller.admissionWebhooks.enabled .Values.controller.admissionWebhooks.patch.enabled -}} +apiVersion: batch/v1 +kind: Job +metadata: + name: {{ include "ingress-nginx.fullname" . }}-admission-patch + namespace: {{ .Release.Namespace }} + annotations: + "helm.sh/hook": post-install,post-upgrade + "helm.sh/hook-delete-policy": before-hook-creation,hook-succeeded + labels: + {{- include "ingress-nginx.labels" . | nindent 4 }} + app.kubernetes.io/component: admission-webhook +spec: +{{- if .Capabilities.APIVersions.Has "batch/v1alpha1" }} + # Alpha feature since k8s 1.12 + ttlSecondsAfterFinished: 0 +{{- end }} + template: + metadata: + name: {{ include "ingress-nginx.fullname" . }}-admission-patch + {{- if .Values.controller.admissionWebhooks.patch.podAnnotations }} + annotations: {{ toYaml .Values.controller.admissionWebhooks.patch.podAnnotations | nindent 8 }} + {{- end }} + labels: + {{- include "ingress-nginx.labels" . | nindent 8 }} + app.kubernetes.io/component: admission-webhook + spec: + {{- if .Values.controller.admissionWebhooks.patch.priorityClassName }} + priorityClassName: {{ .Values.controller.admissionWebhooks.patch.priorityClassName }} + {{- end }} + {{- if .Values.imagePullSecrets }} + imagePullSecrets: {{ toYaml .Values.imagePullSecrets | nindent 8 }} + {{- end }} + containers: + - name: patch + {{- with .Values.controller.admissionWebhooks.patch.image }} + image: "{{- if .repository -}}{{ .repository }}{{ else }}{{ .registry }}/{{ .image }}{{- end -}}:{{ .tag }}{{- if (.digest) -}} @{{.digest}} {{- end -}}" + {{- end }} + imagePullPolicy: {{ .Values.controller.admissionWebhooks.patch.image.pullPolicy }} + args: + - patch + - --webhook-name={{ include "ingress-nginx.fullname" . }}-admission + - --namespace=$(POD_NAMESPACE) + - --patch-mutating=false + - --secret-name={{ include "ingress-nginx.fullname" . }}-admission + - --patch-failure-policy={{ .Values.controller.admissionWebhooks.failurePolicy }} + env: + - name: POD_NAMESPACE + valueFrom: + fieldRef: + fieldPath: metadata.namespace + restartPolicy: OnFailure + serviceAccountName: {{ include "ingress-nginx.fullname" . }}-admission + {{- if .Values.controller.admissionWebhooks.patch.nodeSelector }} + nodeSelector: {{ toYaml .Values.controller.admissionWebhooks.patch.nodeSelector | nindent 8 }} + {{- end }} + {{- if .Values.controller.admissionWebhooks.patch.tolerations }} + tolerations: {{ toYaml .Values.controller.admissionWebhooks.patch.tolerations | nindent 8 }} + {{- end }} + securityContext: + runAsNonRoot: true + runAsUser: {{ .Values.controller.admissionWebhooks.patch.runAsUser }} +{{- end }} diff --git a/hosting/kubernetes/budibase/charts/ingress-nginx/templates/admission-webhooks/job-patch/psp.yaml b/hosting/kubernetes/budibase/charts/ingress-nginx/templates/admission-webhooks/job-patch/psp.yaml new file mode 100644 index 0000000000..d2c7de6858 --- /dev/null +++ b/hosting/kubernetes/budibase/charts/ingress-nginx/templates/admission-webhooks/job-patch/psp.yaml @@ -0,0 +1,36 @@ +{{- if and .Values.controller.admissionWebhooks.enabled .Values.controller.admissionWebhooks.patch.enabled .Values.podSecurityPolicy.enabled (empty .Values.controller.admissionWebhooks.existingPsp) -}} +apiVersion: policy/v1beta1 +kind: PodSecurityPolicy +metadata: + name: {{ include "ingress-nginx.fullname" . }}-admission + annotations: + "helm.sh/hook": pre-install,pre-upgrade,post-install,post-upgrade + "helm.sh/hook-delete-policy": before-hook-creation,hook-succeeded + labels: + {{- include "ingress-nginx.labels" . | nindent 4 }} + app.kubernetes.io/component: admission-webhook +spec: + allowPrivilegeEscalation: false + fsGroup: + ranges: + - max: 65535 + min: 1 + rule: MustRunAs + requiredDropCapabilities: + - ALL + runAsUser: + rule: MustRunAsNonRoot + seLinux: + rule: RunAsAny + supplementalGroups: + ranges: + - max: 65535 + min: 1 + rule: MustRunAs + volumes: + - configMap + - emptyDir + - projected + - secret + - downwardAPI +{{- end }} diff --git a/hosting/kubernetes/budibase/charts/ingress-nginx/templates/admission-webhooks/job-patch/role.yaml b/hosting/kubernetes/budibase/charts/ingress-nginx/templates/admission-webhooks/job-patch/role.yaml new file mode 100644 index 0000000000..9b083ee6e3 --- /dev/null +++ b/hosting/kubernetes/budibase/charts/ingress-nginx/templates/admission-webhooks/job-patch/role.yaml @@ -0,0 +1,21 @@ +{{- if and .Values.controller.admissionWebhooks.enabled .Values.controller.admissionWebhooks.patch.enabled -}} +apiVersion: rbac.authorization.k8s.io/v1 +kind: Role +metadata: + name: {{ include "ingress-nginx.fullname" . }}-admission + namespace: {{ .Release.Namespace }} + annotations: + "helm.sh/hook": pre-install,pre-upgrade,post-install,post-upgrade + "helm.sh/hook-delete-policy": before-hook-creation,hook-succeeded + labels: + {{- include "ingress-nginx.labels" . | nindent 4 }} + app.kubernetes.io/component: admission-webhook +rules: + - apiGroups: + - "" + resources: + - secrets + verbs: + - get + - create +{{- end }} diff --git a/hosting/kubernetes/budibase/charts/ingress-nginx/templates/admission-webhooks/job-patch/rolebinding.yaml b/hosting/kubernetes/budibase/charts/ingress-nginx/templates/admission-webhooks/job-patch/rolebinding.yaml new file mode 100644 index 0000000000..edda07f5d9 --- /dev/null +++ b/hosting/kubernetes/budibase/charts/ingress-nginx/templates/admission-webhooks/job-patch/rolebinding.yaml @@ -0,0 +1,21 @@ +{{- if and .Values.controller.admissionWebhooks.enabled .Values.controller.admissionWebhooks.patch.enabled -}} +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + name: {{ include "ingress-nginx.fullname" . }}-admission + namespace: {{ .Release.Namespace }} + annotations: + "helm.sh/hook": pre-install,pre-upgrade,post-install,post-upgrade + "helm.sh/hook-delete-policy": before-hook-creation,hook-succeeded + labels: + {{- include "ingress-nginx.labels" . | nindent 4 }} + app.kubernetes.io/component: admission-webhook +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: Role + name: {{ include "ingress-nginx.fullname" . }}-admission +subjects: + - kind: ServiceAccount + name: {{ include "ingress-nginx.fullname" . }}-admission + namespace: {{ .Release.Namespace | quote }} +{{- end }} diff --git a/hosting/kubernetes/budibase/charts/ingress-nginx/templates/admission-webhooks/job-patch/serviceaccount.yaml b/hosting/kubernetes/budibase/charts/ingress-nginx/templates/admission-webhooks/job-patch/serviceaccount.yaml new file mode 100644 index 0000000000..1ff0f7f0e5 --- /dev/null +++ b/hosting/kubernetes/budibase/charts/ingress-nginx/templates/admission-webhooks/job-patch/serviceaccount.yaml @@ -0,0 +1,13 @@ +{{- if and .Values.controller.admissionWebhooks.enabled .Values.controller.admissionWebhooks.patch.enabled -}} +apiVersion: v1 +kind: ServiceAccount +metadata: + name: {{ include "ingress-nginx.fullname" . }}-admission + namespace: {{ .Release.Namespace }} + annotations: + "helm.sh/hook": pre-install,pre-upgrade,post-install,post-upgrade + "helm.sh/hook-delete-policy": before-hook-creation,hook-succeeded + labels: + {{- include "ingress-nginx.labels" . | nindent 4 }} + app.kubernetes.io/component: admission-webhook +{{- end }} diff --git a/hosting/kubernetes/budibase/charts/ingress-nginx/templates/admission-webhooks/validating-webhook.yaml b/hosting/kubernetes/budibase/charts/ingress-nginx/templates/admission-webhooks/validating-webhook.yaml new file mode 100644 index 0000000000..2f3dd77848 --- /dev/null +++ b/hosting/kubernetes/budibase/charts/ingress-nginx/templates/admission-webhooks/validating-webhook.yaml @@ -0,0 +1,46 @@ +{{- if .Values.controller.admissionWebhooks.enabled -}} +# before changing this value, check the required kubernetes version +# https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#prerequisites +apiVersion: admissionregistration.k8s.io/v1 +kind: ValidatingWebhookConfiguration +metadata: + {{- if .Values.controller.admissionWebhooks.annotations }} + annotations: {{ toYaml .Values.controller.admissionWebhooks.annotations | nindent 4 }} + {{- end }} + labels: + {{- include "ingress-nginx.labels" . | nindent 4 }} + app.kubernetes.io/component: admission-webhook + name: {{ include "ingress-nginx.fullname" . }}-admission +webhooks: + - name: validate.nginx.ingress.kubernetes.io + matchPolicy: Equivalent + rules: + - apiGroups: + - networking.k8s.io + apiVersions: + - v1beta1 + operations: + - CREATE + - UPDATE + resources: + - ingresses + failurePolicy: {{ .Values.controller.admissionWebhooks.failurePolicy | default "Fail" }} + sideEffects: None + admissionReviewVersions: + - v1 + - v1beta1 + clientConfig: + service: + namespace: {{ .Release.Namespace | quote }} + name: {{ include "ingress-nginx.controller.fullname" . }}-admission + path: /networking/v1beta1/ingresses + {{- if .Values.controller.admissionWebhooks.timeoutSeconds }} + timeoutSeconds: {{ .Values.controller.admissionWebhooks.timeoutSeconds }} + {{- end }} + {{- if .Values.controller.admissionWebhooks.namespaceSelector }} + namespaceSelector: {{ toYaml .Values.controller.admissionWebhooks.namespaceSelector | nindent 6 }} + {{- end }} + {{- if .Values.controller.admissionWebhooks.objectSelector }} + objectSelector: {{ toYaml .Values.controller.admissionWebhooks.objectSelector | nindent 6 }} + {{- end }} +{{- end }} diff --git a/hosting/kubernetes/budibase/charts/ingress-nginx/templates/clusterrole.yaml b/hosting/kubernetes/budibase/charts/ingress-nginx/templates/clusterrole.yaml new file mode 100644 index 0000000000..8ec5f49fa4 --- /dev/null +++ b/hosting/kubernetes/budibase/charts/ingress-nginx/templates/clusterrole.yaml @@ -0,0 +1,75 @@ +{{- if and .Values.rbac.create (not .Values.rbac.scope) -}} +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + labels: + {{- include "ingress-nginx.labels" . | nindent 4 }} + name: {{ include "ingress-nginx.fullname" . }} +rules: + - apiGroups: + - "" + resources: + - configmaps + - endpoints + - nodes + - pods + - secrets + verbs: + - list + - watch +{{- if and .Values.controller.scope.enabled .Values.controller.scope.namespace }} + - apiGroups: + - "" + resources: + - namespaces + resourceNames: + - "{{ .Values.controller.scope.namespace }}" + verbs: + - get +{{- end }} + - apiGroups: + - "" + resources: + - nodes + verbs: + - get + - apiGroups: + - "" + resources: + - services + verbs: + - get + - list + - watch + - apiGroups: + - extensions + - "networking.k8s.io" # k8s 1.14+ + resources: + - ingresses + verbs: + - get + - list + - watch + - apiGroups: + - "" + resources: + - events + verbs: + - create + - patch + - apiGroups: + - extensions + - "networking.k8s.io" # k8s 1.14+ + resources: + - ingresses/status + verbs: + - update + - apiGroups: + - "networking.k8s.io" # k8s 1.14+ + resources: + - ingressclasses + verbs: + - get + - list + - watch +{{- end }} diff --git a/hosting/kubernetes/budibase/charts/ingress-nginx/templates/clusterrolebinding.yaml b/hosting/kubernetes/budibase/charts/ingress-nginx/templates/clusterrolebinding.yaml new file mode 100644 index 0000000000..81be52b87d --- /dev/null +++ b/hosting/kubernetes/budibase/charts/ingress-nginx/templates/clusterrolebinding.yaml @@ -0,0 +1,16 @@ +{{- if and .Values.rbac.create (not .Values.rbac.scope) -}} +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + labels: + {{- include "ingress-nginx.labels" . | nindent 4 }} + name: {{ include "ingress-nginx.fullname" . }} +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: {{ include "ingress-nginx.fullname" . }} +subjects: + - kind: ServiceAccount + name: {{ template "ingress-nginx.serviceAccountName" . }} + namespace: {{ .Release.Namespace | quote }} +{{- end }} diff --git a/hosting/kubernetes/budibase/charts/ingress-nginx/templates/controller-configmap-addheaders.yaml b/hosting/kubernetes/budibase/charts/ingress-nginx/templates/controller-configmap-addheaders.yaml new file mode 100644 index 0000000000..e0b7a0f21a --- /dev/null +++ b/hosting/kubernetes/budibase/charts/ingress-nginx/templates/controller-configmap-addheaders.yaml @@ -0,0 +1,11 @@ +{{- if .Values.controller.addHeaders -}} +apiVersion: v1 +kind: ConfigMap +metadata: + labels: + {{- include "ingress-nginx.labels" . | nindent 4 }} + app.kubernetes.io/component: controller + name: {{ include "ingress-nginx.fullname" . }}-custom-add-headers + namespace: {{ .Release.Namespace }} +data: {{ toYaml .Values.controller.addHeaders | nindent 2 }} +{{- end }} diff --git a/hosting/kubernetes/budibase/charts/ingress-nginx/templates/controller-configmap-proxyheaders.yaml b/hosting/kubernetes/budibase/charts/ingress-nginx/templates/controller-configmap-proxyheaders.yaml new file mode 100644 index 0000000000..91f22f03d9 --- /dev/null +++ b/hosting/kubernetes/budibase/charts/ingress-nginx/templates/controller-configmap-proxyheaders.yaml @@ -0,0 +1,16 @@ +{{- if or .Values.controller.proxySetHeaders .Values.controller.headers -}} +apiVersion: v1 +kind: ConfigMap +metadata: + labels: + {{- include "ingress-nginx.labels" . | nindent 4 }} + app.kubernetes.io/component: controller + name: {{ include "ingress-nginx.fullname" . }}-custom-proxy-headers + namespace: {{ .Release.Namespace }} +data: +{{- if .Values.controller.proxySetHeaders }} +{{ toYaml .Values.controller.proxySetHeaders | indent 2 }} +{{ else if and .Values.controller.headers (not .Values.controller.proxySetHeaders) }} +{{ toYaml .Values.controller.headers | indent 2 }} +{{- end }} +{{- end }} diff --git a/hosting/kubernetes/budibase/charts/ingress-nginx/templates/controller-configmap-tcp.yaml b/hosting/kubernetes/budibase/charts/ingress-nginx/templates/controller-configmap-tcp.yaml new file mode 100644 index 0000000000..aaf336fb30 --- /dev/null +++ b/hosting/kubernetes/budibase/charts/ingress-nginx/templates/controller-configmap-tcp.yaml @@ -0,0 +1,14 @@ +{{- if .Values.tcp -}} +apiVersion: v1 +kind: ConfigMap +metadata: + labels: + {{- include "ingress-nginx.labels" . | nindent 4 }} + app.kubernetes.io/component: controller +{{- if .Values.controller.tcp.annotations }} + annotations: {{ toYaml .Values.controller.tcp.annotations | nindent 4 }} +{{- end }} + name: {{ include "ingress-nginx.fullname" . }}-tcp + namespace: {{ .Release.Namespace }} +data: {{ tpl (toYaml .Values.tcp) . | nindent 2 }} +{{- end }} diff --git a/hosting/kubernetes/budibase/charts/ingress-nginx/templates/controller-configmap-udp.yaml b/hosting/kubernetes/budibase/charts/ingress-nginx/templates/controller-configmap-udp.yaml new file mode 100644 index 0000000000..7f46791ecb --- /dev/null +++ b/hosting/kubernetes/budibase/charts/ingress-nginx/templates/controller-configmap-udp.yaml @@ -0,0 +1,14 @@ +{{- if .Values.udp -}} +apiVersion: v1 +kind: ConfigMap +metadata: + labels: + {{- include "ingress-nginx.labels" . | nindent 4 }} + app.kubernetes.io/component: controller +{{- if .Values.controller.udp.annotations }} + annotations: {{ toYaml .Values.controller.udp.annotations | nindent 4 }} +{{- end }} + name: {{ include "ingress-nginx.fullname" . }}-udp + namespace: {{ .Release.Namespace }} +data: {{ tpl (toYaml .Values.udp) . | nindent 2 }} +{{- end }} diff --git a/hosting/kubernetes/budibase/charts/ingress-nginx/templates/controller-configmap.yaml b/hosting/kubernetes/budibase/charts/ingress-nginx/templates/controller-configmap.yaml new file mode 100644 index 0000000000..630545140e --- /dev/null +++ b/hosting/kubernetes/budibase/charts/ingress-nginx/templates/controller-configmap.yaml @@ -0,0 +1,25 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + labels: + {{- include "ingress-nginx.labels" . | nindent 4 }} + app.kubernetes.io/component: controller +{{- if .Values.controller.configAnnotations }} + annotations: {{ toYaml .Values.controller.configAnnotations | nindent 4 }} +{{- end }} + name: {{ include "ingress-nginx.controller.fullname" . }} + namespace: {{ .Release.Namespace }} +data: +{{- if .Values.controller.addHeaders }} + add-headers: {{ .Release.Namespace }}/{{ include "ingress-nginx.fullname" . }}-custom-add-headers +{{- end }} +{{- if or .Values.controller.proxySetHeaders .Values.controller.headers }} + proxy-set-headers: {{ .Release.Namespace }}/{{ include "ingress-nginx.fullname" . }}-custom-proxy-headers +{{- end }} +{{- if .Values.dhParam }} + ssl-dh-param: {{ printf "%s/%s" .Release.Namespace (include "ingress-nginx.controller.fullname" .) }} +{{- end }} +{{- range $key, $value := .Values.controller.config }} + {{ $key | nindent 2 }}: {{ $value | quote }} +{{- end }} + diff --git a/hosting/kubernetes/budibase/charts/ingress-nginx/templates/controller-daemonset.yaml b/hosting/kubernetes/budibase/charts/ingress-nginx/templates/controller-daemonset.yaml new file mode 100644 index 0000000000..2f6def5897 --- /dev/null +++ b/hosting/kubernetes/budibase/charts/ingress-nginx/templates/controller-daemonset.yaml @@ -0,0 +1,244 @@ +{{- if or (eq .Values.controller.kind "DaemonSet") (eq .Values.controller.kind "Both") -}} +{{- include "isControllerTagValid" . -}} +apiVersion: apps/v1 +kind: DaemonSet +metadata: + labels: + {{- include "ingress-nginx.labels" . | nindent 4 }} + app.kubernetes.io/component: controller + {{- with .Values.controller.labels }} + {{- toYaml . | nindent 4 }} + {{- end }} + name: {{ include "ingress-nginx.controller.fullname" . }} + namespace: {{ .Release.Namespace }} + {{- if .Values.controller.annotations }} + annotations: {{ toYaml .Values.controller.annotations | nindent 4 }} + {{- end }} +spec: + selector: + matchLabels: + {{- include "ingress-nginx.selectorLabels" . | nindent 6 }} + app.kubernetes.io/component: controller + revisionHistoryLimit: {{ .Values.revisionHistoryLimit }} + {{- if .Values.controller.updateStrategy }} + updateStrategy: {{ toYaml .Values.controller.updateStrategy | nindent 4 }} + {{- end }} + minReadySeconds: {{ .Values.controller.minReadySeconds }} + template: + metadata: + {{- if .Values.controller.podAnnotations }} + annotations: + {{- range $key, $value := .Values.controller.podAnnotations }} + {{ $key }}: {{ $value | quote }} + {{- end }} + {{- end }} + labels: + {{- include "ingress-nginx.selectorLabels" . | nindent 8 }} + app.kubernetes.io/component: controller + {{- if .Values.controller.podLabels }} + {{- toYaml .Values.controller.podLabels | nindent 8 }} + {{- end }} + spec: + {{- if .Values.controller.dnsConfig }} + dnsConfig: {{ toYaml .Values.controller.dnsConfig | nindent 8 }} + {{- end }} + dnsPolicy: {{ .Values.controller.dnsPolicy }} + {{- if .Values.imagePullSecrets }} + imagePullSecrets: {{ toYaml .Values.imagePullSecrets | nindent 8 }} + {{- end }} + {{- if .Values.controller.priorityClassName }} + priorityClassName: {{ .Values.controller.priorityClassName }} + {{- end }} + {{- if or .Values.controller.podSecurityContext .Values.controller.sysctls }} + securityContext: + {{- end }} + {{- if .Values.controller.podSecurityContext }} + {{- toYaml .Values.controller.podSecurityContext | nindent 8 }} + {{- end }} + {{- if .Values.controller.sysctls }} + sysctls: + {{- range $sysctl, $value := .Values.controller.sysctls }} + - name: {{ $sysctl | quote }} + value: {{ $value | quote }} + {{- end }} + {{- end }} + containers: + - name: {{ .Values.controller.containerName }} + {{- with .Values.controller.image }} + image: "{{- if .repository -}}{{ .repository }}{{ else }}{{ .registry }}/{{ .image }}{{- end -}}:{{ .tag }}{{- if (.digest) -}} @{{.digest}} {{- end -}}" + {{- end }} + imagePullPolicy: {{ .Values.controller.image.pullPolicy }} + {{- if .Values.controller.lifecycle }} + lifecycle: {{ toYaml .Values.controller.lifecycle | nindent 12 }} + {{- end }} + args: + - /nginx-ingress-controller + {{- if .Values.defaultBackend.enabled }} + - --default-backend-service={{ .Release.Namespace }}/{{ include "ingress-nginx.defaultBackend.fullname" . }} + {{- end }} + {{- if .Values.controller.publishService.enabled }} + - --publish-service={{ template "ingress-nginx.controller.publishServicePath" . }} + {{- end }} + - --election-id={{ .Values.controller.electionID }} + - --ingress-class={{ .Values.controller.ingressClass }} + - --configmap={{ .Release.Namespace }}/{{ include "ingress-nginx.controller.fullname" . }} + {{- if .Values.tcp }} + - --tcp-services-configmap={{ .Release.Namespace }}/{{ include "ingress-nginx.fullname" . }}-tcp + {{- end }} + {{- if .Values.udp }} + - --udp-services-configmap={{ .Release.Namespace }}/{{ include "ingress-nginx.fullname" . }}-udp + {{- end }} + {{- if .Values.controller.scope.enabled }} + - --watch-namespace={{ default .Release.Namespace .Values.controller.scope.namespace }} + {{- end }} + {{- if and .Values.controller.reportNodeInternalIp .Values.controller.hostNetwork }} + - --report-node-internal-ip-address={{ .Values.controller.reportNodeInternalIp }} + {{- end }} + {{- if .Values.controller.admissionWebhooks.enabled }} + - --validating-webhook=:{{ .Values.controller.admissionWebhooks.port }} + - --validating-webhook-certificate={{ .Values.controller.admissionWebhooks.certificate }} + - --validating-webhook-key={{ .Values.controller.admissionWebhooks.key }} + {{- end }} + {{- if .Values.controller.maxmindMirror }} + - --maxmind-mirror={{ .Values.controller.maxmindMirror }} + {{- end}} + {{- if .Values.controller.maxmindLicenseKey }} + - --maxmind-license-key={{ .Values.controller.maxmindLicenseKey }} + {{- end }} + {{- if not (eq .Values.controller.healthCheckPath "/healthz") }} + - --health-check-path={{ .Values.controller.healthCheckPath }} + {{- end }} + {{- range $key, $value := .Values.controller.extraArgs }} + {{- /* Accept keys without values or with false as value */}} + {{- if eq ($value | quote | len) 2 }} + - --{{ $key }} + {{- else }} + - --{{ $key }}={{ $value }} + {{- end }} + {{- end }} + securityContext: + capabilities: + drop: + - ALL + add: + - NET_BIND_SERVICE + runAsUser: {{ .Values.controller.image.runAsUser }} + allowPrivilegeEscalation: {{ .Values.controller.image.allowPrivilegeEscalation }} + env: + - name: POD_NAME + valueFrom: + fieldRef: + fieldPath: metadata.name + - name: POD_NAMESPACE + valueFrom: + fieldRef: + fieldPath: metadata.namespace + {{- if .Values.controller.enableMimalloc }} + - name: LD_PRELOAD + value: /usr/local/lib/libmimalloc.so + {{- end }} + {{- if .Values.controller.extraEnvs }} + {{- toYaml .Values.controller.extraEnvs | nindent 12 }} + {{- end }} + {{- if .Values.controller.startupProbe }} + startupProbe: {{ toYaml .Values.controller.startupProbe | nindent 12 }} + {{- end }} + livenessProbe: {{ toYaml .Values.controller.livenessProbe | nindent 12 }} + readinessProbe: {{ toYaml .Values.controller.readinessProbe | nindent 12 }} + ports: + {{- range $key, $value := .Values.controller.containerPort }} + - name: {{ $key }} + containerPort: {{ $value }} + protocol: TCP + {{- if $.Values.controller.hostPort.enabled }} + hostPort: {{ index $.Values.controller.hostPort.ports $key | default $value }} + {{- end }} + {{- end }} + {{- if .Values.controller.metrics.enabled }} + - name: metrics + containerPort: {{ .Values.controller.metrics.port }} + protocol: TCP + {{- end }} + {{- if .Values.controller.admissionWebhooks.enabled }} + - name: webhook + containerPort: {{ .Values.controller.admissionWebhooks.port }} + protocol: TCP + {{- end }} + {{- range $key, $value := .Values.tcp }} + - name: {{ $key }}-tcp + containerPort: {{ $key }} + protocol: TCP + {{- if $.Values.controller.hostPort.enabled }} + hostPort: {{ $key }} + {{- end }} + {{- end }} + {{- range $key, $value := .Values.udp }} + - name: {{ $key }}-udp + containerPort: {{ $key }} + protocol: UDP + {{- if $.Values.controller.hostPort.enabled }} + hostPort: {{ $key }} + {{- end }} + {{- end }} + {{- if (or .Values.controller.customTemplate.configMapName .Values.controller.extraVolumeMounts .Values.controller.admissionWebhooks.enabled) }} + volumeMounts: + {{- if .Values.controller.customTemplate.configMapName }} + - mountPath: /etc/nginx/template + name: nginx-template-volume + readOnly: true + {{- end }} + {{- if .Values.controller.admissionWebhooks.enabled }} + - name: webhook-cert + mountPath: /usr/local/certificates/ + readOnly: true + {{- end }} + {{- if .Values.controller.extraVolumeMounts }} + {{- toYaml .Values.controller.extraVolumeMounts | nindent 12 }} + {{- end }} + {{- end }} + {{- if .Values.controller.resources }} + resources: {{ toYaml .Values.controller.resources | nindent 12 }} + {{- end }} + {{- if .Values.controller.extraContainers }} + {{ toYaml .Values.controller.extraContainers | nindent 8 }} + {{- end }} + {{- if .Values.controller.extraInitContainers }} + initContainers: {{ toYaml .Values.controller.extraInitContainers | nindent 8 }} + {{- end }} + {{- if .Values.controller.hostNetwork }} + hostNetwork: {{ .Values.controller.hostNetwork }} + {{- end }} + {{- if .Values.controller.nodeSelector }} + nodeSelector: {{ toYaml .Values.controller.nodeSelector | nindent 8 }} + {{- end }} + {{- if .Values.controller.tolerations }} + tolerations: {{ toYaml .Values.controller.tolerations | nindent 8 }} + {{- end }} + {{- if .Values.controller.affinity }} + affinity: {{ toYaml .Values.controller.affinity | nindent 8 }} + {{- end }} + {{- if .Values.controller.topologySpreadConstraints }} + topologySpreadConstraints: {{ toYaml .Values.controller.topologySpreadConstraints | nindent 8 }} + {{- end }} + serviceAccountName: {{ template "ingress-nginx.serviceAccountName" . }} + terminationGracePeriodSeconds: {{ .Values.controller.terminationGracePeriodSeconds }} + {{- if (or .Values.controller.customTemplate.configMapName .Values.controller.extraVolumeMounts .Values.controller.admissionWebhooks.enabled .Values.controller.extraVolumes) }} + volumes: + {{- if .Values.controller.customTemplate.configMapName }} + - name: nginx-template-volume + configMap: + name: {{ .Values.controller.customTemplate.configMapName }} + items: + - key: {{ .Values.controller.customTemplate.configMapKey }} + path: nginx.tmpl + {{- end }} + {{- if .Values.controller.admissionWebhooks.enabled }} + - name: webhook-cert + secret: + secretName: {{ include "ingress-nginx.fullname" . }}-admission + {{- end }} + {{- if .Values.controller.extraVolumes }} + {{ toYaml .Values.controller.extraVolumes | nindent 8 }} + {{- end }} + {{- end }} +{{- end }} diff --git a/hosting/kubernetes/budibase/charts/ingress-nginx/templates/controller-deployment.yaml b/hosting/kubernetes/budibase/charts/ingress-nginx/templates/controller-deployment.yaml new file mode 100644 index 0000000000..7e2d223a99 --- /dev/null +++ b/hosting/kubernetes/budibase/charts/ingress-nginx/templates/controller-deployment.yaml @@ -0,0 +1,245 @@ +{{- if or (eq .Values.controller.kind "Deployment") (eq .Values.controller.kind "Both") -}} +{{- include "isControllerTagValid" . -}} +apiVersion: apps/v1 +kind: Deployment +metadata: + labels: + {{- include "ingress-nginx.labels" . | nindent 4 }} + app.kubernetes.io/component: controller + {{- with .Values.controller.labels }} + {{- toYaml . | nindent 4 }} + {{- end }} + name: {{ include "ingress-nginx.controller.fullname" . }} + namespace: {{ .Release.Namespace }} + {{- if .Values.controller.annotations }} + annotations: {{ toYaml .Values.controller.annotations | nindent 4 }} + {{- end }} +spec: + selector: + matchLabels: + {{- include "ingress-nginx.selectorLabels" . | nindent 6 }} + app.kubernetes.io/component: controller + {{- if not .Values.controller.autoscaling.enabled }} + replicas: {{ .Values.controller.replicaCount }} + {{- end }} + revisionHistoryLimit: {{ .Values.revisionHistoryLimit }} + {{- if .Values.controller.updateStrategy }} + strategy: + {{ toYaml .Values.controller.updateStrategy | nindent 4 }} + {{- end }} + minReadySeconds: {{ .Values.controller.minReadySeconds }} + template: + metadata: + {{- if .Values.controller.podAnnotations }} + annotations: + {{- range $key, $value := .Values.controller.podAnnotations }} + {{ $key }}: {{ $value | quote }} + {{- end }} + {{- end }} + labels: + {{- include "ingress-nginx.selectorLabels" . | nindent 8 }} + app.kubernetes.io/component: controller + {{- if .Values.controller.podLabels }} + {{- toYaml .Values.controller.podLabels | nindent 8 }} + {{- end }} + spec: + {{- if .Values.controller.dnsConfig }} + dnsConfig: {{ toYaml .Values.controller.dnsConfig | nindent 8 }} + {{- end }} + dnsPolicy: {{ .Values.controller.dnsPolicy }} + {{- if .Values.imagePullSecrets }} + imagePullSecrets: {{ toYaml .Values.imagePullSecrets | nindent 8 }} + {{- end }} + {{- if .Values.controller.priorityClassName }} + priorityClassName: {{ .Values.controller.priorityClassName }} + {{- end }} + {{- if or .Values.controller.podSecurityContext .Values.controller.sysctls }} + securityContext: + {{- end }} + {{- if .Values.controller.podSecurityContext }} + {{- toYaml .Values.controller.podSecurityContext | nindent 8 }} + {{- end }} + {{- if .Values.controller.sysctls }} + sysctls: + {{- range $sysctl, $value := .Values.controller.sysctls }} + - name: {{ $sysctl | quote }} + value: {{ $value | quote }} + {{- end }} + {{- end }} + containers: + - name: {{ .Values.controller.containerName }} + {{- with .Values.controller.image }} + image: "{{- if .repository -}}{{ .repository }}{{ else }}{{ .registry }}/{{ .image }}{{- end -}}:{{ .tag }}{{- if (.digest) -}} @{{.digest}} {{- end -}}" + {{- end }} + imagePullPolicy: {{ .Values.controller.image.pullPolicy }} + {{- if .Values.controller.lifecycle }} + lifecycle: {{ toYaml .Values.controller.lifecycle | nindent 12 }} + {{- end }} + args: + - /nginx-ingress-controller + {{- if .Values.defaultBackend.enabled }} + - --default-backend-service=$(POD_NAMESPACE)/{{ include "ingress-nginx.defaultBackend.fullname" . }} + {{- end }} + {{- if .Values.controller.publishService.enabled }} + - --publish-service={{ template "ingress-nginx.controller.publishServicePath" . }} + {{- end }} + - --election-id={{ .Values.controller.electionID }} + - --ingress-class={{ .Values.controller.ingressClass }} + - --configmap=$(POD_NAMESPACE)/{{ include "ingress-nginx.controller.fullname" . }} + {{- if .Values.tcp }} + - --tcp-services-configmap=$(POD_NAMESPACE)/{{ include "ingress-nginx.fullname" . }}-tcp + {{- end }} + {{- if .Values.udp }} + - --udp-services-configmap=$(POD_NAMESPACE)/{{ include "ingress-nginx.fullname" . }}-udp + {{- end }} + {{- if .Values.controller.scope.enabled }} + - --watch-namespace={{ default "$(POD_NAMESPACE)" .Values.controller.scope.namespace }} + {{- end }} + {{- if and .Values.controller.reportNodeInternalIp .Values.controller.hostNetwork }} + - --report-node-internal-ip-address={{ .Values.controller.reportNodeInternalIp }} + {{- end }} + {{- if .Values.controller.admissionWebhooks.enabled }} + - --validating-webhook=:{{ .Values.controller.admissionWebhooks.port }} + - --validating-webhook-certificate={{ .Values.controller.admissionWebhooks.certificate }} + - --validating-webhook-key={{ .Values.controller.admissionWebhooks.key }} + {{- end }} + {{- if .Values.controller.maxmindLicenseKey }} + - --maxmind-license-key={{ .Values.controller.maxmindLicenseKey }} + {{- end }} + {{- if not (eq .Values.controller.healthCheckPath "/healthz") }} + - --health-check-path={{ .Values.controller.healthCheckPath }} + {{- end }} + {{- range $key, $value := .Values.controller.extraArgs }} + {{- /* Accept keys without values or with false as value */}} + {{- if eq ($value | quote | len) 2 }} + - --{{ $key }} + {{- else }} + - --{{ $key }}={{ $value }} + {{- end }} + {{- end }} + securityContext: + capabilities: + drop: + - ALL + add: + - NET_BIND_SERVICE + runAsUser: {{ .Values.controller.image.runAsUser }} + allowPrivilegeEscalation: {{ .Values.controller.image.allowPrivilegeEscalation }} + env: + - name: POD_NAME + valueFrom: + fieldRef: + fieldPath: metadata.name + - name: POD_NAMESPACE + valueFrom: + fieldRef: + fieldPath: metadata.namespace + {{- if .Values.controller.enableMimalloc }} + - name: LD_PRELOAD + value: /usr/local/lib/libmimalloc.so + {{- end }} + {{- if .Values.controller.extraEnvs }} + {{- toYaml .Values.controller.extraEnvs | nindent 12 }} + {{- end }} + {{- if .Values.controller.startupProbe }} + startupProbe: {{ toYaml .Values.controller.startupProbe | nindent 12 }} + {{- end }} + livenessProbe: {{ toYaml .Values.controller.livenessProbe | nindent 12 }} + readinessProbe: {{ toYaml .Values.controller.readinessProbe | nindent 12 }} + ports: + {{- range $key, $value := .Values.controller.containerPort }} + - name: {{ $key }} + containerPort: {{ $value }} + protocol: TCP + {{- if $.Values.controller.hostPort.enabled }} + hostPort: {{ index $.Values.controller.hostPort.ports $key | default $value }} + {{- end }} + {{- end }} + {{- if .Values.controller.metrics.enabled }} + - name: metrics + containerPort: {{ .Values.controller.metrics.port }} + protocol: TCP + {{- end }} + {{- if .Values.controller.admissionWebhooks.enabled }} + - name: webhook + containerPort: {{ .Values.controller.admissionWebhooks.port }} + protocol: TCP + {{- end }} + {{- range $key, $value := .Values.tcp }} + - name: {{ $key }}-tcp + containerPort: {{ $key }} + protocol: TCP + {{- if $.Values.controller.hostPort.enabled }} + hostPort: {{ $key }} + {{- end }} + {{- end }} + {{- range $key, $value := .Values.udp }} + - name: {{ $key }}-udp + containerPort: {{ $key }} + protocol: UDP + {{- if $.Values.controller.hostPort.enabled }} + hostPort: {{ $key }} + {{- end }} + {{- end }} + {{- if (or .Values.controller.customTemplate.configMapName .Values.controller.extraVolumeMounts .Values.controller.admissionWebhooks.enabled) }} + volumeMounts: + {{- if .Values.controller.customTemplate.configMapName }} + - mountPath: /etc/nginx/template + name: nginx-template-volume + readOnly: true + {{- end }} + {{- if .Values.controller.admissionWebhooks.enabled }} + - name: webhook-cert + mountPath: /usr/local/certificates/ + readOnly: true + {{- end }} + {{- if .Values.controller.extraVolumeMounts }} + {{- toYaml .Values.controller.extraVolumeMounts | nindent 12 }} + {{- end }} + {{- end }} + {{- if .Values.controller.resources }} + resources: {{ toYaml .Values.controller.resources | nindent 12 }} + {{- end }} + {{- if .Values.controller.extraContainers }} + {{ toYaml .Values.controller.extraContainers | nindent 8 }} + {{- end }} + {{- if .Values.controller.extraInitContainers }} + initContainers: {{ toYaml .Values.controller.extraInitContainers | nindent 8 }} + {{- end }} + {{- if .Values.controller.hostNetwork }} + hostNetwork: {{ .Values.controller.hostNetwork }} + {{- end }} + {{- if .Values.controller.nodeSelector }} + nodeSelector: {{ toYaml .Values.controller.nodeSelector | nindent 8 }} + {{- end }} + {{- if .Values.controller.tolerations }} + tolerations: {{ toYaml .Values.controller.tolerations | nindent 8 }} + {{- end }} + {{- if .Values.controller.affinity }} + affinity: {{ toYaml .Values.controller.affinity | nindent 8 }} + {{- end }} + {{- if .Values.controller.topologySpreadConstraints }} + topologySpreadConstraints: {{ toYaml .Values.controller.topologySpreadConstraints | nindent 8 }} + {{- end }} + serviceAccountName: {{ template "ingress-nginx.serviceAccountName" . }} + terminationGracePeriodSeconds: {{ .Values.controller.terminationGracePeriodSeconds }} + {{- if (or .Values.controller.customTemplate.configMapName .Values.controller.extraVolumeMounts .Values.controller.admissionWebhooks.enabled .Values.controller.extraVolumes) }} + volumes: + {{- if .Values.controller.customTemplate.configMapName }} + - name: nginx-template-volume + configMap: + name: {{ .Values.controller.customTemplate.configMapName }} + items: + - key: {{ .Values.controller.customTemplate.configMapKey }} + path: nginx.tmpl + {{- end }} + {{- if .Values.controller.admissionWebhooks.enabled }} + - name: webhook-cert + secret: + secretName: {{ include "ingress-nginx.fullname" . }}-admission + {{- end }} + {{- if .Values.controller.extraVolumes }} + {{ toYaml .Values.controller.extraVolumes | nindent 8 }} + {{- end }} + {{- end }} +{{- end }} diff --git a/hosting/kubernetes/budibase/charts/ingress-nginx/templates/controller-hpa.yaml b/hosting/kubernetes/budibase/charts/ingress-nginx/templates/controller-hpa.yaml new file mode 100644 index 0000000000..fb14bdf6a7 --- /dev/null +++ b/hosting/kubernetes/budibase/charts/ingress-nginx/templates/controller-hpa.yaml @@ -0,0 +1,45 @@ +{{- if and .Values.controller.autoscaling.enabled (or (eq .Values.controller.kind "Deployment") (eq .Values.controller.kind "Both")) -}} +{{- if not .Values.controller.keda.enabled }} + +apiVersion: autoscaling/v2beta2 +kind: HorizontalPodAutoscaler +metadata: + annotations: + {{- with .Values.controller.autoscaling.annotations }} + {{- toYaml . | trimSuffix "\n" | nindent 4 }} + {{- end }} + labels: + {{- include "ingress-nginx.labels" . | nindent 4 }} + app.kubernetes.io/component: controller + name: {{ include "ingress-nginx.controller.fullname" . }} + namespace: {{ .Release.Namespace }} +spec: + scaleTargetRef: + apiVersion: apps/v1 + kind: Deployment + name: {{ include "ingress-nginx.controller.fullname" . }} + minReplicas: {{ .Values.controller.autoscaling.minReplicas }} + maxReplicas: {{ .Values.controller.autoscaling.maxReplicas }} + metrics: + {{- with .Values.controller.autoscaling.targetMemoryUtilizationPercentage }} + - type: Resource + resource: + name: memory + target: + type: Utilization + averageUtilization: {{ . }} + {{- end }} + {{- with .Values.controller.autoscaling.targetCPUUtilizationPercentage }} + - type: Resource + resource: + name: cpu + target: + type: Utilization + averageUtilization: {{ . }} + {{- end }} + {{- with .Values.controller.autoscalingTemplate }} +{{- toYaml . | nindent 2 }} + {{- end }} +{{- end }} +{{- end }} + diff --git a/hosting/kubernetes/budibase/charts/ingress-nginx/templates/controller-ingressclass.yaml b/hosting/kubernetes/budibase/charts/ingress-nginx/templates/controller-ingressclass.yaml new file mode 100644 index 0000000000..f94b9590de --- /dev/null +++ b/hosting/kubernetes/budibase/charts/ingress-nginx/templates/controller-ingressclass.yaml @@ -0,0 +1,23 @@ +{{- if and (semverCompare ">=1.18-0" .Capabilities.KubeVersion.GitVersion) (.Values.controller.ingressClassResource.enabled) -}} +{{- if and (semverCompare "=1.18-0" .Capabilities.KubeVersion.GitVersion) }} +apiVersion: networking.k8s.io/v1beta1 +{{- else }} +apiVersion: networking.k8s.io/v1 +{{- end }} +kind: IngressClass +metadata: + labels: + {{- include "ingress-nginx.labels" . | nindent 4 }} + app.kubernetes.io/component: controller + {{- with .Values.controller.labels }} + {{- toYaml . | nindent 4 }} + {{- end }} + name: {{ .Values.controller.ingressClass }} +{{- if .Values.controller.ingressClassResource.default }} + annotations: + ingressclass.kubernetes.io/is-default-class: "true" +{{- end }} +spec: + controller: k8s.io/ingress-nginx + {{ template "ingressClass.parameters" . }} +{{- end }} diff --git a/hosting/kubernetes/budibase/charts/ingress-nginx/templates/controller-keda.yaml b/hosting/kubernetes/budibase/charts/ingress-nginx/templates/controller-keda.yaml new file mode 100644 index 0000000000..c7eebf5c86 --- /dev/null +++ b/hosting/kubernetes/budibase/charts/ingress-nginx/templates/controller-keda.yaml @@ -0,0 +1,39 @@ +{{- if and .Values.controller.keda.enabled (or (eq .Values.controller.kind "Deployment") (eq .Values.controller.kind "Both")) -}} +# https://keda.sh/docs/ + +apiVersion: {{ .Values.controller.keda.apiVersion }} +kind: ScaledObject +metadata: + labels: + {{- include "ingress-nginx.labels" . | nindent 4 }} + app.kubernetes.io/component: controller + name: {{ include "ingress-nginx.controller.fullname" . }} + {{- if .Values.controller.keda.scaledObject.annotations }} + annotations: {{ toYaml .Values.controller.keda.scaledObject.annotations | nindent 4 }} + {{- end }} +spec: + scaleTargetRef: +{{- if eq .Values.controller.keda.apiVersion "keda.k8s.io/v1alpha1" }} + deploymentName: {{ include "ingress-nginx.controller.fullname" . }} +{{- else if eq .Values.controller.keda.apiVersion "keda.sh/v1alpha1" }} + name: {{ include "ingress-nginx.controller.fullname" . }} +{{- end }} + pollingInterval: {{ .Values.controller.keda.pollingInterval }} + cooldownPeriod: {{ .Values.controller.keda.cooldownPeriod }} + minReplicaCount: {{ .Values.controller.keda.minReplicas }} + maxReplicaCount: {{ .Values.controller.keda.maxReplicas }} + triggers: +{{- with .Values.controller.keda.triggers }} +{{ toYaml . | indent 2 }} +{{ end }} + advanced: + restoreToOriginalReplicaCount: {{ .Values.controller.keda.restoreToOriginalReplicaCount }} +{{- if .Values.controller.keda.behavior }} + horizontalPodAutoscalerConfig: + behavior: +{{ with .Values.controller.keda.behavior -}} +{{ toYaml . | indent 8 }} +{{ end }} + +{{- end }} +{{- end }} diff --git a/hosting/kubernetes/budibase/charts/ingress-nginx/templates/controller-poddisruptionbudget.yaml b/hosting/kubernetes/budibase/charts/ingress-nginx/templates/controller-poddisruptionbudget.yaml new file mode 100644 index 0000000000..a5a425f74b --- /dev/null +++ b/hosting/kubernetes/budibase/charts/ingress-nginx/templates/controller-poddisruptionbudget.yaml @@ -0,0 +1,16 @@ +{{- if or (and .Values.controller.autoscaling.enabled (gt (.Values.controller.autoscaling.minReplicas | int) 1)) (and (not .Values.controller.autoscaling.enabled) (gt (.Values.controller.replicaCount | int) 1)) }} +apiVersion: policy/v1beta1 +kind: PodDisruptionBudget +metadata: + labels: + {{- include "ingress-nginx.labels" . | nindent 4 }} + app.kubernetes.io/component: controller + name: {{ include "ingress-nginx.controller.fullname" . }} + namespace: {{ .Release.Namespace }} +spec: + selector: + matchLabels: + {{- include "ingress-nginx.selectorLabels" . | nindent 6 }} + app.kubernetes.io/component: controller + minAvailable: {{ .Values.controller.minAvailable }} +{{- end }} diff --git a/hosting/kubernetes/budibase/charts/ingress-nginx/templates/controller-prometheusrules.yaml b/hosting/kubernetes/budibase/charts/ingress-nginx/templates/controller-prometheusrules.yaml new file mode 100644 index 0000000000..ca5427523d --- /dev/null +++ b/hosting/kubernetes/budibase/charts/ingress-nginx/templates/controller-prometheusrules.yaml @@ -0,0 +1,21 @@ +{{- if and .Values.controller.metrics.enabled .Values.controller.metrics.prometheusRule.enabled -}} +apiVersion: monitoring.coreos.com/v1 +kind: PrometheusRule +metadata: + name: {{ include "ingress-nginx.controller.fullname" . }} +{{- if .Values.controller.metrics.prometheusRule.namespace }} + namespace: {{ .Values.controller.metrics.prometheusRule.namespace | quote }} +{{- end }} + labels: + {{- include "ingress-nginx.labels" . | nindent 4 }} + app.kubernetes.io/component: controller + {{- if .Values.controller.metrics.prometheusRule.additionalLabels }} + {{- toYaml .Values.controller.metrics.prometheusRule.additionalLabels | nindent 4 }} + {{- end }} +spec: +{{- if .Values.controller.metrics.prometheusRule.rules }} + groups: + - name: {{ template "ingress-nginx.name" . }} + rules: {{- toYaml .Values.controller.metrics.prometheusRule.rules | nindent 4 }} +{{- end }} +{{- end }} diff --git a/hosting/kubernetes/budibase/charts/ingress-nginx/templates/controller-psp.yaml b/hosting/kubernetes/budibase/charts/ingress-nginx/templates/controller-psp.yaml new file mode 100644 index 0000000000..bdb8563105 --- /dev/null +++ b/hosting/kubernetes/budibase/charts/ingress-nginx/templates/controller-psp.yaml @@ -0,0 +1,86 @@ +{{- if and .Values.podSecurityPolicy.enabled (empty .Values.controller.existingPsp) -}} +apiVersion: policy/v1beta1 +kind: PodSecurityPolicy +metadata: + name: {{ include "ingress-nginx.fullname" . }} + labels: + {{- include "ingress-nginx.labels" . | nindent 4 }} + app.kubernetes.io/component: controller +spec: + allowedCapabilities: + - NET_BIND_SERVICE +{{- if .Values.controller.sysctls }} + allowedUnsafeSysctls: + {{- range $sysctl, $value := .Values.controller.sysctls }} + - {{ $sysctl }} + {{- end }} +{{- end }} + privileged: false + allowPrivilegeEscalation: true + # Allow core volume types. + volumes: + - 'configMap' + - 'emptyDir' + #- 'projected' + - 'secret' + #- 'downwardAPI' +{{- if .Values.controller.hostNetwork }} + hostNetwork: {{ .Values.controller.hostNetwork }} +{{- end }} +{{- if or .Values.controller.hostNetwork .Values.controller.hostPort.enabled }} + hostPorts: +{{- if .Values.controller.hostNetwork }} +{{- range $key, $value := .Values.controller.containerPort }} + # {{ $key }} + - min: {{ $value }} + max: {{ $value }} +{{- end }} +{{- else if .Values.controller.hostPort.enabled }} +{{- range $key, $value := .Values.controller.hostPort.ports }} + # {{ $key }} + - min: {{ $value }} + max: {{ $value }} +{{- end }} +{{- end }} +{{- if .Values.controller.metrics.enabled }} + # metrics + - min: {{ .Values.controller.metrics.port }} + max: {{ .Values.controller.metrics.port }} +{{- end }} +{{- if .Values.controller.admissionWebhooks.enabled }} + # admission webhooks + - min: {{ .Values.controller.admissionWebhooks.port }} + max: {{ .Values.controller.admissionWebhooks.port }} +{{- end }} +{{- range $key, $value := .Values.tcp }} + # {{ $key }}-tcp + - min: {{ $key }} + max: {{ $key }} +{{- end }} +{{- range $key, $value := .Values.udp }} + # {{ $key }}-udp + - min: {{ $key }} + max: {{ $key }} +{{- end }} +{{- end }} + hostIPC: false + hostPID: false + runAsUser: + # Require the container to run without root privileges. + rule: 'MustRunAsNonRoot' + supplementalGroups: + rule: 'MustRunAs' + ranges: + # Forbid adding the root group. + - min: 1 + max: 65535 + fsGroup: + rule: 'MustRunAs' + ranges: + # Forbid adding the root group. + - min: 1 + max: 65535 + readOnlyRootFilesystem: false + seLinux: + rule: 'RunAsAny' +{{- end }} diff --git a/hosting/kubernetes/budibase/charts/ingress-nginx/templates/controller-role.yaml b/hosting/kubernetes/budibase/charts/ingress-nginx/templates/controller-role.yaml new file mode 100644 index 0000000000..1a5ccd29bf --- /dev/null +++ b/hosting/kubernetes/budibase/charts/ingress-nginx/templates/controller-role.yaml @@ -0,0 +1,92 @@ +{{- if .Values.rbac.create -}} +apiVersion: rbac.authorization.k8s.io/v1 +kind: Role +metadata: + labels: + {{- include "ingress-nginx.labels" . | nindent 4 }} + app.kubernetes.io/component: controller + name: {{ include "ingress-nginx.fullname" . }} + namespace: {{ .Release.Namespace }} +rules: + - apiGroups: + - "" + resources: + - namespaces + verbs: + - get + - apiGroups: + - "" + resources: + - configmaps + - pods + - secrets + - endpoints + verbs: + - get + - list + - watch + - apiGroups: + - "" + resources: + - services + verbs: + - get + - list + - watch + - apiGroups: + - extensions + - "networking.k8s.io" # k8s 1.14+ + resources: + - ingresses + verbs: + - get + - list + - watch + - apiGroups: + - extensions + - "networking.k8s.io" # k8s 1.14+ + resources: + - ingresses/status + verbs: + - update + - apiGroups: + - "networking.k8s.io" # k8s 1.14+ + resources: + - ingressclasses + verbs: + - get + - list + - watch + - apiGroups: + - "" + resources: + - configmaps + resourceNames: + - {{ .Values.controller.electionID }}-{{ .Values.controller.ingressClass }} + verbs: + - get + - update + - apiGroups: + - "" + resources: + - configmaps + verbs: + - create + - apiGroups: + - "" + resources: + - events + verbs: + - create + - patch +{{- if .Values.podSecurityPolicy.enabled }} + - apiGroups: [{{ template "podSecurityPolicy.apiGroup" . }}] + resources: ['podsecuritypolicies'] + verbs: ['use'] + {{- with .Values.controller.existingPsp }} + resourceNames: [{{ . }}] + {{- else }} + resourceNames: [{{ include "ingress-nginx.fullname" . }}] + {{- end }} +{{- end }} +{{- end }} diff --git a/hosting/kubernetes/budibase/charts/ingress-nginx/templates/controller-rolebinding.yaml b/hosting/kubernetes/budibase/charts/ingress-nginx/templates/controller-rolebinding.yaml new file mode 100644 index 0000000000..5ec3bc7749 --- /dev/null +++ b/hosting/kubernetes/budibase/charts/ingress-nginx/templates/controller-rolebinding.yaml @@ -0,0 +1,18 @@ +{{- if .Values.rbac.create -}} +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + labels: + {{- include "ingress-nginx.labels" . | nindent 4 }} + app.kubernetes.io/component: controller + name: {{ include "ingress-nginx.fullname" . }} + namespace: {{ .Release.Namespace }} +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: Role + name: {{ include "ingress-nginx.fullname" . }} +subjects: + - kind: ServiceAccount + name: {{ template "ingress-nginx.serviceAccountName" . }} + namespace: {{ .Release.Namespace | quote }} +{{- end }} diff --git a/hosting/kubernetes/budibase/charts/ingress-nginx/templates/controller-service-internal.yaml b/hosting/kubernetes/budibase/charts/ingress-nginx/templates/controller-service-internal.yaml new file mode 100644 index 0000000000..0bb9661274 --- /dev/null +++ b/hosting/kubernetes/budibase/charts/ingress-nginx/templates/controller-service-internal.yaml @@ -0,0 +1,51 @@ +{{- if and .Values.controller.service.enabled .Values.controller.service.internal.enabled .Values.controller.service.internal.annotations}} +apiVersion: v1 +kind: Service +metadata: + annotations: + {{- range $key, $value := .Values.controller.service.internal.annotations }} + {{ $key }}: {{ $value | quote }} + {{- end }} + labels: + {{- include "ingress-nginx.labels" . | nindent 4 }} + app.kubernetes.io/component: controller + {{- if .Values.controller.service.labels }} + {{- toYaml .Values.controller.service.labels | nindent 4 }} + {{- end }} + name: {{ include "ingress-nginx.controller.fullname" . }}-internal + namespace: {{ .Release.Namespace }} +spec: + type: "{{ .Values.controller.service.type }}" +{{- if .Values.controller.service.internal.loadBalancerIP }} + loadBalancerIP: {{ .Values.controller.service.internal.loadBalancerIP }} +{{- end }} +{{- if .Values.controller.service.internal.loadBalancerSourceRanges }} + loadBalancerSourceRanges: {{ toYaml .Values.controller.service.internal.loadBalancerSourceRanges | nindent 4 }} +{{- end }} +{{- if .Values.controller.service.internal.externalTrafficPolicy }} + externalTrafficPolicy: {{ .Values.controller.service.internal.externalTrafficPolicy }} +{{- end }} + ports: + {{- $setNodePorts := (or (eq .Values.controller.service.type "NodePort") (eq .Values.controller.service.type "LoadBalancer")) }} + {{- if .Values.controller.service.enableHttp }} + - name: http + port: {{ .Values.controller.service.ports.http }} + protocol: TCP + targetPort: {{ .Values.controller.service.targetPorts.http }} + {{- if (and $setNodePorts (not (empty .Values.controller.service.nodePorts.http))) }} + nodePort: {{ .Values.controller.service.nodePorts.http }} + {{- end }} + {{- end }} + {{- if .Values.controller.service.enableHttps }} + - name: https + port: {{ .Values.controller.service.ports.https }} + protocol: TCP + targetPort: {{ .Values.controller.service.targetPorts.https }} + {{- if (and $setNodePorts (not (empty .Values.controller.service.nodePorts.https))) }} + nodePort: {{ .Values.controller.service.nodePorts.https }} + {{- end }} + {{- end }} + selector: + {{- include "ingress-nginx.selectorLabels" . | nindent 4 }} + app.kubernetes.io/component: controller +{{- end }} diff --git a/hosting/kubernetes/budibase/charts/ingress-nginx/templates/controller-service-metrics.yaml b/hosting/kubernetes/budibase/charts/ingress-nginx/templates/controller-service-metrics.yaml new file mode 100644 index 0000000000..1b690192c8 --- /dev/null +++ b/hosting/kubernetes/budibase/charts/ingress-nginx/templates/controller-service-metrics.yaml @@ -0,0 +1,44 @@ +{{- if .Values.controller.metrics.enabled -}} +apiVersion: v1 +kind: Service +metadata: +{{- if .Values.controller.metrics.service.annotations }} + annotations: {{ toYaml .Values.controller.metrics.service.annotations | nindent 4 }} +{{- end }} + labels: + {{- include "ingress-nginx.labels" . | nindent 4 }} + app.kubernetes.io/component: controller + {{- if .Values.controller.metrics.service.labels }} + {{- toYaml .Values.controller.metrics.service.labels | nindent 4 }} + {{- end }} + name: {{ include "ingress-nginx.controller.fullname" . }}-metrics + namespace: {{ .Release.Namespace }} +spec: + type: {{ .Values.controller.metrics.service.type }} +{{- if .Values.controller.metrics.service.clusterIP }} + clusterIP: {{ .Values.controller.metrics.service.clusterIP }} +{{- end }} +{{- if .Values.controller.metrics.service.externalIPs }} + externalIPs: {{ toYaml .Values.controller.metrics.service.externalIPs | nindent 4 }} +{{- end }} +{{- if .Values.controller.metrics.service.loadBalancerIP }} + loadBalancerIP: {{ .Values.controller.metrics.service.loadBalancerIP }} +{{- end }} +{{- if .Values.controller.metrics.service.loadBalancerSourceRanges }} + loadBalancerSourceRanges: {{ toYaml .Values.controller.metrics.service.loadBalancerSourceRanges | nindent 4 }} +{{- end }} +{{- if .Values.controller.metrics.service.externalTrafficPolicy }} + externalTrafficPolicy: {{ .Values.controller.metrics.service.externalTrafficPolicy }} +{{- end }} + ports: + - name: metrics + port: {{ .Values.controller.metrics.service.servicePort }} + targetPort: metrics + {{- $setNodePorts := (or (eq .Values.controller.metrics.service.type "NodePort") (eq .Values.controller.metrics.service.type "LoadBalancer")) }} + {{- if (and $setNodePorts (not (empty .Values.controller.metrics.service.nodePort))) }} + nodePort: {{ .Values.controller.metrics.service.nodePort }} + {{- end }} + selector: + {{- include "ingress-nginx.selectorLabels" . | nindent 4 }} + app.kubernetes.io/component: controller +{{- end }} diff --git a/hosting/kubernetes/budibase/charts/ingress-nginx/templates/controller-service-webhook.yaml b/hosting/kubernetes/budibase/charts/ingress-nginx/templates/controller-service-webhook.yaml new file mode 100644 index 0000000000..228cb59d88 --- /dev/null +++ b/hosting/kubernetes/budibase/charts/ingress-nginx/templates/controller-service-webhook.yaml @@ -0,0 +1,34 @@ +{{- if .Values.controller.admissionWebhooks.enabled -}} +apiVersion: v1 +kind: Service +metadata: +{{- if .Values.controller.admissionWebhooks.service.annotations }} + annotations: {{ toYaml .Values.controller.admissionWebhooks.service.annotations | nindent 4 }} +{{- end }} + labels: + {{- include "ingress-nginx.labels" . | nindent 4 }} + app.kubernetes.io/component: controller + name: {{ include "ingress-nginx.controller.fullname" . }}-admission + namespace: {{ .Release.Namespace }} +spec: + type: {{ .Values.controller.admissionWebhooks.service.type }} +{{- if .Values.controller.admissionWebhooks.service.clusterIP }} + clusterIP: {{ .Values.controller.admissionWebhooks.service.clusterIP }} +{{- end }} +{{- if .Values.controller.admissionWebhooks.service.externalIPs }} + externalIPs: {{ toYaml .Values.controller.admissionWebhooks.service.externalIPs | nindent 4 }} +{{- end }} +{{- if .Values.controller.admissionWebhooks.service.loadBalancerIP }} + loadBalancerIP: {{ .Values.controller.admissionWebhooks.service.loadBalancerIP }} +{{- end }} +{{- if .Values.controller.admissionWebhooks.service.loadBalancerSourceRanges }} + loadBalancerSourceRanges: {{ toYaml .Values.controller.admissionWebhooks.service.loadBalancerSourceRanges | nindent 4 }} +{{- end }} + ports: + - name: https-webhook + port: 443 + targetPort: webhook + selector: + {{- include "ingress-nginx.selectorLabels" . | nindent 4 }} + app.kubernetes.io/component: controller +{{- end }} diff --git a/hosting/kubernetes/budibase/charts/ingress-nginx/templates/controller-service.yaml b/hosting/kubernetes/budibase/charts/ingress-nginx/templates/controller-service.yaml new file mode 100644 index 0000000000..908291cfff --- /dev/null +++ b/hosting/kubernetes/budibase/charts/ingress-nginx/templates/controller-service.yaml @@ -0,0 +1,85 @@ +{{- if .Values.controller.service.enabled -}} +apiVersion: v1 +kind: Service +metadata: + annotations: + {{- range $key, $value := .Values.controller.service.annotations }} + {{ $key }}: {{ $value | quote }} + {{- end }} + labels: + {{- include "ingress-nginx.labels" . | nindent 4 }} + app.kubernetes.io/component: controller + {{- if .Values.controller.service.labels }} + {{- toYaml .Values.controller.service.labels | nindent 4 }} + {{- end }} + name: {{ include "ingress-nginx.controller.fullname" . }} + namespace: {{ .Release.Namespace }} +spec: + type: {{ .Values.controller.service.type }} +{{- if .Values.controller.service.clusterIP }} + clusterIP: {{ .Values.controller.service.clusterIP }} +{{- end }} +{{- if .Values.controller.service.externalIPs }} + externalIPs: {{ toYaml .Values.controller.service.externalIPs | nindent 4 }} +{{- end }} +{{- if .Values.controller.service.loadBalancerIP }} + loadBalancerIP: {{ .Values.controller.service.loadBalancerIP }} +{{- end }} +{{- if .Values.controller.service.loadBalancerSourceRanges }} + loadBalancerSourceRanges: {{ toYaml .Values.controller.service.loadBalancerSourceRanges | nindent 4 }} +{{- end }} +{{- if .Values.controller.service.externalTrafficPolicy }} + externalTrafficPolicy: {{ .Values.controller.service.externalTrafficPolicy }} +{{- end }} +{{- if .Values.controller.service.sessionAffinity }} + sessionAffinity: {{ .Values.controller.service.sessionAffinity }} +{{- end }} +{{- if .Values.controller.service.healthCheckNodePort }} + healthCheckNodePort: {{ .Values.controller.service.healthCheckNodePort }} +{{- end }} + ports: + {{- $setNodePorts := (or (eq .Values.controller.service.type "NodePort") (eq .Values.controller.service.type "LoadBalancer")) }} + {{- if .Values.controller.service.enableHttp }} + - name: http + port: {{ .Values.controller.service.ports.http }} + protocol: TCP + targetPort: {{ .Values.controller.service.targetPorts.http }} + {{- if (and $setNodePorts (not (empty .Values.controller.service.nodePorts.http))) }} + nodePort: {{ .Values.controller.service.nodePorts.http }} + {{- end }} + {{- end }} + {{- if .Values.controller.service.enableHttps }} + - name: https + port: {{ .Values.controller.service.ports.https }} + protocol: TCP + targetPort: {{ .Values.controller.service.targetPorts.https }} + {{- if (and $setNodePorts (not (empty .Values.controller.service.nodePorts.https))) }} + nodePort: {{ .Values.controller.service.nodePorts.https }} + {{- end }} + {{- end }} + {{- range $key, $value := .Values.tcp }} + - name: {{ $key }}-tcp + port: {{ $key }} + protocol: TCP + targetPort: {{ $key }}-tcp + {{- if $.Values.controller.service.nodePorts.tcp }} + {{- if index $.Values.controller.service.nodePorts.tcp $key }} + nodePort: {{ index $.Values.controller.service.nodePorts.tcp $key }} + {{- end }} + {{- end }} + {{- end }} + {{- range $key, $value := .Values.udp }} + - name: {{ $key }}-udp + port: {{ $key }} + protocol: UDP + targetPort: {{ $key }}-udp + {{- if $.Values.controller.service.nodePorts.udp }} + {{- if index $.Values.controller.service.nodePorts.udp $key }} + nodePort: {{ index $.Values.controller.service.nodePorts.udp $key }} + {{- end }} + {{- end }} + {{- end }} + selector: + {{- include "ingress-nginx.selectorLabels" . | nindent 4 }} + app.kubernetes.io/component: controller +{{- end }} diff --git a/hosting/kubernetes/budibase/charts/ingress-nginx/templates/controller-serviceaccount.yaml b/hosting/kubernetes/budibase/charts/ingress-nginx/templates/controller-serviceaccount.yaml new file mode 100644 index 0000000000..50a718d32d --- /dev/null +++ b/hosting/kubernetes/budibase/charts/ingress-nginx/templates/controller-serviceaccount.yaml @@ -0,0 +1,11 @@ +{{- if or .Values.serviceAccount.create -}} +apiVersion: v1 +kind: ServiceAccount +metadata: + labels: + {{- include "ingress-nginx.labels" . | nindent 4 }} + app.kubernetes.io/component: controller + name: {{ template "ingress-nginx.serviceAccountName" . }} + namespace: {{ .Release.Namespace }} +automountServiceAccountToken: {{ .Values.serviceAccount.automountServiceAccountToken }} +{{- end }} diff --git a/hosting/kubernetes/budibase/charts/ingress-nginx/templates/controller-servicemonitor.yaml b/hosting/kubernetes/budibase/charts/ingress-nginx/templates/controller-servicemonitor.yaml new file mode 100644 index 0000000000..066488a040 --- /dev/null +++ b/hosting/kubernetes/budibase/charts/ingress-nginx/templates/controller-servicemonitor.yaml @@ -0,0 +1,45 @@ +{{- if and .Values.controller.metrics.enabled .Values.controller.metrics.serviceMonitor.enabled -}} +apiVersion: monitoring.coreos.com/v1 +kind: ServiceMonitor +metadata: + name: {{ include "ingress-nginx.controller.fullname" . }} +{{- if .Values.controller.metrics.serviceMonitor.namespace }} + namespace: {{ .Values.controller.metrics.serviceMonitor.namespace | quote }} +{{- end }} + labels: + {{- include "ingress-nginx.labels" . | nindent 4 }} + app.kubernetes.io/component: controller + {{- if .Values.controller.metrics.serviceMonitor.additionalLabels }} + {{- toYaml .Values.controller.metrics.serviceMonitor.additionalLabels | nindent 4 }} + {{- end }} +spec: + endpoints: + - port: metrics + interval: {{ .Values.controller.metrics.serviceMonitor.scrapeInterval }} + {{- if .Values.controller.metrics.serviceMonitor.honorLabels }} + honorLabels: true + {{- end }} + {{- if .Values.controller.metrics.serviceMonitor.metricRelabelings }} + metricRelabelings: {{ toYaml .Values.controller.metrics.serviceMonitor.metricRelabelings | nindent 8 }} + {{- end }} +{{- if .Values.controller.metrics.serviceMonitor.jobLabel }} + jobLabel: {{ .Values.controller.metrics.serviceMonitor.jobLabel | quote }} +{{- end }} +{{- if .Values.controller.metrics.serviceMonitor.namespaceSelector }} + namespaceSelector: {{ toYaml .Values.controller.metrics.serviceMonitor.namespaceSelector | nindent 4 }} +{{ else }} + namespaceSelector: + matchNames: + - {{ .Release.Namespace }} +{{- end }} +{{- if .Values.controller.metrics.serviceMonitor.targetLabels }} + targetLabels: + {{- range .Values.controller.metrics.serviceMonitor.targetLabels }} + - {{ . }} + {{- end }} +{{- end }} + selector: + matchLabels: + {{- include "ingress-nginx.selectorLabels" . | nindent 6 }} + app.kubernetes.io/component: controller +{{- end }} diff --git a/hosting/kubernetes/budibase/charts/ingress-nginx/templates/default-backend-deployment.yaml b/hosting/kubernetes/budibase/charts/ingress-nginx/templates/default-backend-deployment.yaml new file mode 100644 index 0000000000..99345269ba --- /dev/null +++ b/hosting/kubernetes/budibase/charts/ingress-nginx/templates/default-backend-deployment.yaml @@ -0,0 +1,112 @@ +{{- if .Values.defaultBackend.enabled -}} +apiVersion: apps/v1 +kind: Deployment +metadata: + labels: + {{- include "ingress-nginx.labels" . | nindent 4 }} + app.kubernetes.io/component: default-backend + name: {{ include "ingress-nginx.defaultBackend.fullname" . }} + namespace: {{ .Release.Namespace }} +spec: + selector: + matchLabels: + {{- include "ingress-nginx.selectorLabels" . | nindent 6 }} + app.kubernetes.io/component: default-backend +{{- if not .Values.defaultBackend.autoscaling.enabled }} + replicas: {{ .Values.defaultBackend.replicaCount }} +{{- end }} + revisionHistoryLimit: {{ .Values.revisionHistoryLimit }} + template: + metadata: + {{- if .Values.defaultBackend.podAnnotations }} + annotations: {{ toYaml .Values.defaultBackend.podAnnotations | nindent 8 }} + {{- end }} + labels: + {{- include "ingress-nginx.selectorLabels" . | nindent 8 }} + app.kubernetes.io/component: default-backend + {{- if .Values.defaultBackend.podLabels }} + {{- toYaml .Values.defaultBackend.podLabels | nindent 8 }} + {{- end }} + spec: + {{- if .Values.imagePullSecrets }} + imagePullSecrets: {{ toYaml .Values.imagePullSecrets | nindent 8 }} + {{- end }} + {{- if .Values.defaultBackend.priorityClassName }} + priorityClassName: {{ .Values.defaultBackend.priorityClassName }} + {{- end }} + {{- if .Values.defaultBackend.podSecurityContext }} + securityContext: {{ toYaml .Values.defaultBackend.podSecurityContext | nindent 8 }} + {{- end }} + containers: + - name: {{ template "ingress-nginx.name" . }}-default-backend + {{- with .Values.defaultBackend.image }} + image: "{{- if .repository -}}{{ .repository }}{{ else }}{{ .registry }}/{{ .image }}{{- end -}}:{{ .tag }}{{- if (.digest) -}} @{{.digest}} {{- end -}}" + {{- end }} + imagePullPolicy: {{ .Values.defaultBackend.image.pullPolicy }} + {{- if .Values.defaultBackend.extraArgs }} + args: + {{- range $key, $value := .Values.defaultBackend.extraArgs }} + {{- /* Accept keys without values or with false as value */}} + {{- if eq ($value | quote | len) 2 }} + - --{{ $key }} + {{- else }} + - --{{ $key }}={{ $value }} + {{- end }} + {{- end }} + {{- end }} + securityContext: + capabilities: + drop: + - ALL + runAsUser: {{ .Values.defaultBackend.image.runAsUser }} + runAsNonRoot: {{ .Values.defaultBackend.image.runAsNonRoot }} + allowPrivilegeEscalation: {{ .Values.defaultBackend.image.allowPrivilegeEscalation }} + readOnlyRootFilesystem: {{ .Values.defaultBackend.image.readOnlyRootFilesystem}} + {{- if .Values.defaultBackend.extraEnvs }} + env: {{ toYaml .Values.defaultBackend.extraEnvs | nindent 12 }} + {{- end }} + livenessProbe: + httpGet: + path: /healthz + port: {{ .Values.defaultBackend.port }} + scheme: HTTP + initialDelaySeconds: {{ .Values.defaultBackend.livenessProbe.initialDelaySeconds }} + periodSeconds: {{ .Values.defaultBackend.livenessProbe.periodSeconds }} + timeoutSeconds: {{ .Values.defaultBackend.livenessProbe.timeoutSeconds }} + successThreshold: {{ .Values.defaultBackend.livenessProbe.successThreshold }} + failureThreshold: {{ .Values.defaultBackend.livenessProbe.failureThreshold }} + readinessProbe: + httpGet: + path: /healthz + port: {{ .Values.defaultBackend.port }} + scheme: HTTP + initialDelaySeconds: {{ .Values.defaultBackend.readinessProbe.initialDelaySeconds }} + periodSeconds: {{ .Values.defaultBackend.readinessProbe.periodSeconds }} + timeoutSeconds: {{ .Values.defaultBackend.readinessProbe.timeoutSeconds }} + successThreshold: {{ .Values.defaultBackend.readinessProbe.successThreshold }} + failureThreshold: {{ .Values.defaultBackend.readinessProbe.failureThreshold }} + ports: + - name: http + containerPort: {{ .Values.defaultBackend.port }} + protocol: TCP + {{- if .Values.defaultBackend.extraVolumeMounts }} + volumeMounts: {{- toYaml .Values.defaultBackend.extraVolumeMounts | nindent 12 }} + {{- end }} + {{- if .Values.defaultBackend.resources }} + resources: {{ toYaml .Values.defaultBackend.resources | nindent 12 }} + {{- end }} + {{- if .Values.defaultBackend.nodeSelector }} + nodeSelector: {{ toYaml .Values.defaultBackend.nodeSelector | nindent 8 }} + {{- end }} + serviceAccountName: {{ template "ingress-nginx.defaultBackend.serviceAccountName" . }} + {{- if .Values.defaultBackend.tolerations }} + tolerations: {{ toYaml .Values.defaultBackend.tolerations | nindent 8 }} + {{- end }} + {{- if .Values.defaultBackend.affinity }} + affinity: {{ toYaml .Values.defaultBackend.affinity | nindent 8 }} + {{- end }} + terminationGracePeriodSeconds: 60 + {{- if .Values.defaultBackend.extraVolumes }} + volumes: {{ toYaml .Values.defaultBackend.extraVolumes | nindent 8 }} + {{- end }} +{{- end }} diff --git a/hosting/kubernetes/budibase/charts/ingress-nginx/templates/default-backend-hpa.yaml b/hosting/kubernetes/budibase/charts/ingress-nginx/templates/default-backend-hpa.yaml new file mode 100644 index 0000000000..e31fda3f9a --- /dev/null +++ b/hosting/kubernetes/budibase/charts/ingress-nginx/templates/default-backend-hpa.yaml @@ -0,0 +1,30 @@ +{{- if and .Values.defaultBackend.enabled .Values.defaultBackend.autoscaling.enabled }} +apiVersion: autoscaling/v2beta1 +kind: HorizontalPodAutoscaler +metadata: + labels: + {{- include "ingress-nginx.labels" . | nindent 4 }} + app.kubernetes.io/component: default-backend + name: {{ template "ingress-nginx.defaultBackend.fullname" . }} + namespace: {{ .Release.Namespace }} +spec: + scaleTargetRef: + apiVersion: apps/v1 + kind: Deployment + name: {{ template "ingress-nginx.defaultBackend.fullname" . }} + minReplicas: {{ .Values.defaultBackend.autoscaling.minReplicas }} + maxReplicas: {{ .Values.defaultBackend.autoscaling.maxReplicas }} + metrics: +{{- with .Values.defaultBackend.autoscaling.targetCPUUtilizationPercentage }} + - type: Resource + resource: + name: cpu + targetAverageUtilization: {{ . }} +{{- end }} +{{- with .Values.defaultBackend.autoscaling.targetMemoryUtilizationPercentage }} + - type: Resource + resource: + name: memory + targetAverageUtilization: {{ . }} +{{- end }} +{{- end }} diff --git a/hosting/kubernetes/budibase/charts/ingress-nginx/templates/default-backend-poddisruptionbudget.yaml b/hosting/kubernetes/budibase/charts/ingress-nginx/templates/default-backend-poddisruptionbudget.yaml new file mode 100644 index 0000000000..153f005e25 --- /dev/null +++ b/hosting/kubernetes/budibase/charts/ingress-nginx/templates/default-backend-poddisruptionbudget.yaml @@ -0,0 +1,16 @@ +{{- if or (gt (.Values.defaultBackend.replicaCount | int) 1) (gt (.Values.defaultBackend.autoscaling.minReplicas | int) 1) }} +apiVersion: policy/v1beta1 +kind: PodDisruptionBudget +metadata: + labels: + {{- include "ingress-nginx.labels" . | nindent 4 }} + app.kubernetes.io/component: default-backend + name: {{ include "ingress-nginx.defaultBackend.fullname" . }} + namespace: {{ .Release.Namespace }} +spec: + selector: + matchLabels: + {{- include "ingress-nginx.selectorLabels" . | nindent 6 }} + app.kubernetes.io/component: default-backend + minAvailable: {{ .Values.defaultBackend.minAvailable }} +{{- end }} diff --git a/hosting/kubernetes/budibase/charts/ingress-nginx/templates/default-backend-psp.yaml b/hosting/kubernetes/budibase/charts/ingress-nginx/templates/default-backend-psp.yaml new file mode 100644 index 0000000000..716dbf16fe --- /dev/null +++ b/hosting/kubernetes/budibase/charts/ingress-nginx/templates/default-backend-psp.yaml @@ -0,0 +1,33 @@ +{{- if and .Values.podSecurityPolicy.enabled .Values.defaultBackend.enabled (empty .Values.defaultBackend.existingPsp) -}} +apiVersion: policy/v1beta1 +kind: PodSecurityPolicy +metadata: + name: {{ include "ingress-nginx.fullname" . }}-backend + labels: + {{- include "ingress-nginx.labels" . | nindent 4 }} + app.kubernetes.io/component: default-backend +spec: + allowPrivilegeEscalation: false + fsGroup: + ranges: + - max: 65535 + min: 1 + rule: MustRunAs + requiredDropCapabilities: + - ALL + runAsUser: + rule: MustRunAsNonRoot + seLinux: + rule: RunAsAny + supplementalGroups: + ranges: + - max: 65535 + min: 1 + rule: MustRunAs + volumes: + - configMap + - emptyDir + - projected + - secret + - downwardAPI +{{- end }} diff --git a/hosting/kubernetes/budibase/charts/ingress-nginx/templates/default-backend-role.yaml b/hosting/kubernetes/budibase/charts/ingress-nginx/templates/default-backend-role.yaml new file mode 100644 index 0000000000..5d29a2d526 --- /dev/null +++ b/hosting/kubernetes/budibase/charts/ingress-nginx/templates/default-backend-role.yaml @@ -0,0 +1,19 @@ +{{- if and .Values.rbac.create .Values.podSecurityPolicy.enabled .Values.defaultBackend.enabled -}} +apiVersion: rbac.authorization.k8s.io/v1 +kind: Role +metadata: + labels: + {{- include "ingress-nginx.labels" . | nindent 4 }} + app.kubernetes.io/component: default-backend + name: {{ include "ingress-nginx.fullname" . }}-backend + namespace: {{ .Release.Namespace }} +rules: + - apiGroups: [{{ template "podSecurityPolicy.apiGroup" . }}] + resources: ['podsecuritypolicies'] + verbs: ['use'] + {{- with .Values.defaultBackend.existingPsp }} + resourceNames: [{{ . }}] + {{- else }} + resourceNames: [{{ include "ingress-nginx.fullname" . }}-backend] + {{- end }} +{{- end }} diff --git a/hosting/kubernetes/budibase/charts/ingress-nginx/templates/default-backend-rolebinding.yaml b/hosting/kubernetes/budibase/charts/ingress-nginx/templates/default-backend-rolebinding.yaml new file mode 100644 index 0000000000..4a9cb92845 --- /dev/null +++ b/hosting/kubernetes/budibase/charts/ingress-nginx/templates/default-backend-rolebinding.yaml @@ -0,0 +1,18 @@ +{{- if and .Values.rbac.create .Values.podSecurityPolicy.enabled .Values.defaultBackend.enabled -}} +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + labels: + {{- include "ingress-nginx.labels" . | nindent 4 }} + app.kubernetes.io/component: default-backend + name: {{ include "ingress-nginx.fullname" . }}-backend + namespace: {{ .Release.Namespace }} +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: Role + name: {{ include "ingress-nginx.fullname" . }}-backend +subjects: + - kind: ServiceAccount + name: {{ template "ingress-nginx.defaultBackend.serviceAccountName" . }} + namespace: {{ .Release.Namespace | quote }} +{{- end }} diff --git a/hosting/kubernetes/budibase/charts/ingress-nginx/templates/default-backend-service.yaml b/hosting/kubernetes/budibase/charts/ingress-nginx/templates/default-backend-service.yaml new file mode 100644 index 0000000000..7624ab36c4 --- /dev/null +++ b/hosting/kubernetes/budibase/charts/ingress-nginx/templates/default-backend-service.yaml @@ -0,0 +1,35 @@ +{{- if .Values.defaultBackend.enabled -}} +apiVersion: v1 +kind: Service +metadata: +{{- if .Values.defaultBackend.service.annotations }} + annotations: {{ toYaml .Values.defaultBackend.service.annotations | nindent 4 }} +{{- end }} + labels: + {{- include "ingress-nginx.labels" . | nindent 4 }} + app.kubernetes.io/component: default-backend + name: {{ include "ingress-nginx.defaultBackend.fullname" . }} + namespace: {{ .Release.Namespace }} +spec: + type: {{ .Values.defaultBackend.service.type }} +{{- if .Values.defaultBackend.service.clusterIP }} + clusterIP: {{ .Values.defaultBackend.service.clusterIP }} +{{- end }} +{{- if .Values.defaultBackend.service.externalIPs }} + externalIPs: {{ toYaml .Values.defaultBackend.service.externalIPs | nindent 4 }} +{{- end }} +{{- if .Values.defaultBackend.service.loadBalancerIP }} + loadBalancerIP: {{ .Values.defaultBackend.service.loadBalancerIP }} +{{- end }} +{{- if .Values.defaultBackend.service.loadBalancerSourceRanges }} + loadBalancerSourceRanges: {{ toYaml .Values.defaultBackend.service.loadBalancerSourceRanges | nindent 4 }} +{{- end }} + ports: + - name: http + port: {{ .Values.defaultBackend.service.servicePort }} + protocol: TCP + targetPort: http + selector: + {{- include "ingress-nginx.selectorLabels" . | nindent 4 }} + app.kubernetes.io/component: default-backend +{{- end }} diff --git a/hosting/kubernetes/budibase/charts/ingress-nginx/templates/default-backend-serviceaccount.yaml b/hosting/kubernetes/budibase/charts/ingress-nginx/templates/default-backend-serviceaccount.yaml new file mode 100644 index 0000000000..0c00e93690 --- /dev/null +++ b/hosting/kubernetes/budibase/charts/ingress-nginx/templates/default-backend-serviceaccount.yaml @@ -0,0 +1,11 @@ +{{- if and .Values.defaultBackend.enabled .Values.defaultBackend.serviceAccount.create -}} +apiVersion: v1 +kind: ServiceAccount +metadata: + labels: + {{- include "ingress-nginx.labels" . | nindent 4 }} + app.kubernetes.io/component: default-backend + name: {{ template "ingress-nginx.defaultBackend.serviceAccountName" . }} + namespace: {{ .Release.Namespace }} +automountServiceAccountToken: {{ .Values.defaultBackend.serviceAccount.automountServiceAccountToken }} +{{- end }} diff --git a/hosting/kubernetes/budibase/charts/ingress-nginx/templates/dh-param-secret.yaml b/hosting/kubernetes/budibase/charts/ingress-nginx/templates/dh-param-secret.yaml new file mode 100644 index 0000000000..12e7a4f633 --- /dev/null +++ b/hosting/kubernetes/budibase/charts/ingress-nginx/templates/dh-param-secret.yaml @@ -0,0 +1,10 @@ +{{- with .Values.dhParam -}} +apiVersion: v1 +kind: Secret +metadata: + name: {{ include "ingress-nginx.controller.fullname" $ }} + labels: + {{- include "ingress-nginx.labels" $ | nindent 4 }} +data: + dhparam.pem: {{ . }} +{{- end }} diff --git a/hosting/kubernetes/budibase/charts/ingress-nginx/values.yaml b/hosting/kubernetes/budibase/charts/ingress-nginx/values.yaml new file mode 100644 index 0000000000..f5496eb616 --- /dev/null +++ b/hosting/kubernetes/budibase/charts/ingress-nginx/values.yaml @@ -0,0 +1,808 @@ +## nginx configuration +## Ref: https://github.com/kubernetes/ingress-nginx/blob/master/docs/user-guide/nginx-configuration/index.md +## + +## Overrides for generated resource names +# See templates/_helpers.tpl +# nameOverride: +# fullnameOverride: + +controller: + name: controller + image: + registry: k8s.gcr.io + image: ingress-nginx/controller + # for backwards compatibility consider setting the full image url via the repository value below + # use *either* current default registry/image or repository format or installing chart by providing the values.yaml will fail + # repository: + tag: "v0.48.1" + digest: sha256:e9fb216ace49dfa4a5983b183067e97496e7a8b307d2093f4278cd550c303899 + pullPolicy: IfNotPresent + # www-data -> uid 101 + runAsUser: 101 + allowPrivilegeEscalation: true + + # Use an existing PSP instead of creating one + existingPsp: "" + + # Configures the controller container name + containerName: controller + + # Configures the ports the nginx-controller listens on + containerPort: + http: 80 + https: 443 + + # Will add custom configuration options to Nginx https://kubernetes.github.io/ingress-nginx/user-guide/nginx-configuration/configmap/ + config: {} + + ## Annotations to be added to the controller config configuration configmap + ## + configAnnotations: {} + + # Will add custom headers before sending traffic to backends according to https://github.com/kubernetes/ingress-nginx/tree/master/docs/examples/customization/custom-headers + proxySetHeaders: {} + + # Will add custom headers before sending response traffic to the client according to: https://kubernetes.github.io/ingress-nginx/user-guide/nginx-configuration/configmap/#add-headers + addHeaders: {} + + # Optionally customize the pod dnsConfig. + dnsConfig: {} + + # Optionally change this to ClusterFirstWithHostNet in case you have 'hostNetwork: true'. + # By default, while using host network, name resolution uses the host's DNS. If you wish nginx-controller + # to keep resolving names inside the k8s network, use ClusterFirstWithHostNet. + dnsPolicy: ClusterFirst + + # Bare-metal considerations via the host network https://kubernetes.github.io/ingress-nginx/deploy/baremetal/#via-the-host-network + # Ingress status was blank because there is no Service exposing the NGINX Ingress controller in a configuration using the host network, the default --publish-service flag used in standard cloud setups does not apply + reportNodeInternalIp: false + + # Required for use with CNI based kubernetes installations (such as ones set up by kubeadm), + # since CNI and hostport don't mix yet. Can be deprecated once https://github.com/kubernetes/kubernetes/issues/23920 + # is merged + hostNetwork: false + + ## Use host ports 80 and 443 + ## Disabled by default + ## + hostPort: + enabled: false + ports: + http: 80 + https: 443 + + ## Election ID to use for status update + ## + electionID: ingress-controller-leader + + ## Name of the ingress class to route through this controller + ## + ingressClass: nginx + + # This section refers to the creation of the IngressClass resource + # IngressClass resources are supported since k8s >= 1.18 + ingressClassResource: + enabled: false + default: false + + # Parameters is a link to a custom resource containing additional + # configuration for the controller. This is optional if the controller + # does not require extra parameters. + parameters: {} + + # labels to add to the pod container metadata + podLabels: {} + # key: value + + ## Security Context policies for controller pods + ## + podSecurityContext: {} + + ## See https://kubernetes.io/docs/tasks/administer-cluster/sysctl-cluster/ for + ## notes on enabling and using sysctls + ### + sysctls: {} + # sysctls: + # "net.core.somaxconn": "8192" + + ## Allows customization of the source of the IP address or FQDN to report + ## in the ingress status field. By default, it reads the information provided + ## by the service. If disable, the status field reports the IP address of the + ## node or nodes where an ingress controller pod is running. + publishService: + enabled: true + ## Allows overriding of the publish service to bind to + ## Must be / + ## + pathOverride: "" + + ## Limit the scope of the controller + ## + scope: + enabled: false + namespace: "" # defaults to .Release.Namespace + + ## Allows customization of the configmap / nginx-configmap namespace + ## + configMapNamespace: "" # defaults to .Release.Namespace + + ## Allows customization of the tcp-services-configmap + ## + tcp: + configMapNamespace: "" # defaults to .Release.Namespace + ## Annotations to be added to the tcp config configmap + annotations: {} + + ## Allows customization of the udp-services-configmap + ## + udp: + configMapNamespace: "" # defaults to .Release.Namespace + ## Annotations to be added to the udp config configmap + annotations: {} + + # Maxmind license key to download GeoLite2 Databases + # https://blog.maxmind.com/2019/12/18/significant-changes-to-accessing-and-using-geolite2-databases + maxmindLicenseKey: "" + + ## Additional command line arguments to pass to nginx-ingress-controller + ## E.g. to specify the default SSL certificate you can use + ## extraArgs: + ## default-ssl-certificate: "/" + extraArgs: {} + + ## Additional environment variables to set + extraEnvs: [] + # extraEnvs: + # - name: FOO + # valueFrom: + # secretKeyRef: + # key: FOO + # name: secret-resource + + ## DaemonSet or Deployment + ## + kind: Deployment + + ## Annotations to be added to the controller Deployment or DaemonSet + ## + annotations: {} + # keel.sh/pollSchedule: "@every 60m" + + ## Labels to be added to the controller Deployment or DaemonSet + ## + labels: {} + # keel.sh/policy: patch + # keel.sh/trigger: poll + + + # The update strategy to apply to the Deployment or DaemonSet + ## + updateStrategy: {} + # rollingUpdate: + # maxUnavailable: 1 + # type: RollingUpdate + + # minReadySeconds to avoid killing pods before we are ready + ## + minReadySeconds: 0 + + + ## Node tolerations for server scheduling to nodes with taints + ## Ref: https://kubernetes.io/docs/concepts/configuration/assign-pod-node/ + ## + tolerations: [] + # - key: "key" + # operator: "Equal|Exists" + # value: "value" + # effect: "NoSchedule|PreferNoSchedule|NoExecute(1.6 only)" + + ## Affinity and anti-affinity + ## Ref: https://kubernetes.io/docs/concepts/configuration/assign-pod-node/#affinity-and-anti-affinity + ## + affinity: {} + # # An example of preferred pod anti-affinity, weight is in the range 1-100 + # podAntiAffinity: + # preferredDuringSchedulingIgnoredDuringExecution: + # - weight: 100 + # podAffinityTerm: + # labelSelector: + # matchExpressions: + # - key: app.kubernetes.io/name + # operator: In + # values: + # - ingress-nginx + # - key: app.kubernetes.io/instance + # operator: In + # values: + # - ingress-nginx + # - key: app.kubernetes.io/component + # operator: In + # values: + # - controller + # topologyKey: kubernetes.io/hostname + + # # An example of required pod anti-affinity + # podAntiAffinity: + # requiredDuringSchedulingIgnoredDuringExecution: + # - labelSelector: + # matchExpressions: + # - key: app.kubernetes.io/name + # operator: In + # values: + # - ingress-nginx + # - key: app.kubernetes.io/instance + # operator: In + # values: + # - ingress-nginx + # - key: app.kubernetes.io/component + # operator: In + # values: + # - controller + # topologyKey: "kubernetes.io/hostname" + + ## Topology spread constraints rely on node labels to identify the topology domain(s) that each Node is in. + ## Ref: https://kubernetes.io/docs/concepts/workloads/pods/pod-topology-spread-constraints/ + ## + topologySpreadConstraints: [] + # - maxSkew: 1 + # topologyKey: failure-domain.beta.kubernetes.io/zone + # whenUnsatisfiable: DoNotSchedule + # labelSelector: + # matchLabels: + # app.kubernetes.io/instance: ingress-nginx-internal + + ## terminationGracePeriodSeconds + ## wait up to five minutes for the drain of connections + ## + terminationGracePeriodSeconds: 300 + + ## Node labels for controller pod assignment + ## Ref: https://kubernetes.io/docs/user-guide/node-selection/ + ## + nodeSelector: + kubernetes.io/os: linux + + ## Liveness and readiness probe values + ## Ref: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle/#container-probes + ## + # startupProbe: + # httpGet: + # # should match container.healthCheckPath + # path: "/healthz" + # port: 10254 + # scheme: HTTP + # initialDelaySeconds: 5 + # periodSeconds: 5 + # timeoutSeconds: 2 + # successThreshold: 1 + # failureThreshold: 5 + livenessProbe: + httpGet: + # should match container.healthCheckPath + path: "/healthz" + port: 10254 + scheme: HTTP + initialDelaySeconds: 10 + periodSeconds: 10 + timeoutSeconds: 1 + successThreshold: 1 + failureThreshold: 5 + readinessProbe: + httpGet: + # should match container.healthCheckPath + path: "/healthz" + port: 10254 + scheme: HTTP + initialDelaySeconds: 10 + periodSeconds: 10 + timeoutSeconds: 1 + successThreshold: 1 + failureThreshold: 3 + + + # Path of the health check endpoint. All requests received on the port defined by + # the healthz-port parameter are forwarded internally to this path. + healthCheckPath: "/healthz" + + ## Annotations to be added to controller pods + ## + podAnnotations: {} + + replicaCount: 1 + + minAvailable: 1 + + # Define requests resources to avoid probe issues due to CPU utilization in busy nodes + # ref: https://github.com/kubernetes/ingress-nginx/issues/4735#issuecomment-551204903 + # Ideally, there should be no limits. + # https://engineering.indeedblog.com/blog/2019/12/cpu-throttling-regression-fix/ + resources: + # limits: + # cpu: 100m + # memory: 90Mi + requests: + cpu: 100m + memory: 90Mi + + # Mutually exclusive with keda autoscaling + autoscaling: + enabled: false + minReplicas: 1 + maxReplicas: 11 + targetCPUUtilizationPercentage: 50 + targetMemoryUtilizationPercentage: 50 + + autoscalingTemplate: [] + # Custom or additional autoscaling metrics + # ref: https://kubernetes.io/docs/tasks/run-application/horizontal-pod-autoscale/#support-for-custom-metrics + # - type: Pods + # pods: + # metric: + # name: nginx_ingress_controller_nginx_process_requests_total + # target: + # type: AverageValue + # averageValue: 10000m + + # Mutually exclusive with hpa autoscaling + keda: + apiVersion: "keda.sh/v1alpha1" + # apiVersion changes with keda 1.x vs 2.x + # 2.x = keda.sh/v1alpha1 + # 1.x = keda.k8s.io/v1alpha1 + enabled: false + minReplicas: 1 + maxReplicas: 11 + pollingInterval: 30 + cooldownPeriod: 300 + restoreToOriginalReplicaCount: false + scaledObject: + annotations: {} + # Custom annotations for ScaledObject resource + # annotations: + # key: value + triggers: [] + # - type: prometheus + # metadata: + # serverAddress: http://:9090 + # metricName: http_requests_total + # threshold: '100' + # query: sum(rate(http_requests_total{deployment="my-deployment"}[2m])) + + behavior: {} + # scaleDown: + # stabilizationWindowSeconds: 300 + # policies: + # - type: Pods + # value: 1 + # periodSeconds: 180 + # scaleUp: + # stabilizationWindowSeconds: 300 + # policies: + # - type: Pods + # value: 2 + # periodSeconds: 60 + + ## Enable mimalloc as a drop-in replacement for malloc. + ## ref: https://github.com/microsoft/mimalloc + ## + enableMimalloc: true + + ## Override NGINX template + customTemplate: + configMapName: "" + configMapKey: "" + + service: + enabled: true + + annotations: {} + labels: {} + # clusterIP: "" + + ## List of IP addresses at which the controller services are available + ## Ref: https://kubernetes.io/docs/user-guide/services/#external-ips + ## + externalIPs: [] + + # loadBalancerIP: "" + loadBalancerSourceRanges: [] + + enableHttp: true + enableHttps: true + + ## Set external traffic policy to: "Local" to preserve source IP on + ## providers supporting it + ## Ref: https://kubernetes.io/docs/tutorials/services/source-ip/#source-ip-for-services-with-typeloadbalancer + # externalTrafficPolicy: "" + + # Must be either "None" or "ClientIP" if set. Kubernetes will default to "None". + # Ref: https://kubernetes.io/docs/concepts/services-networking/service/#virtual-ips-and-service-proxies + # sessionAffinity: "" + + # specifies the health check node port (numeric port number) for the service. If healthCheckNodePort isn’t specified, + # the service controller allocates a port from your cluster’s NodePort range. + # Ref: https://kubernetes.io/docs/tasks/access-application-cluster/create-external-load-balancer/#preserving-the-client-source-ip + # healthCheckNodePort: 0 + + ports: + http: 80 + https: 443 + + targetPorts: + http: http + https: https + + type: LoadBalancer + + # type: NodePort + # nodePorts: + # http: 32080 + # https: 32443 + # tcp: + # 8080: 32808 + nodePorts: + http: "" + https: "" + tcp: {} + udp: {} + + ## Enables an additional internal load balancer (besides the external one). + ## Annotations are mandatory for the load balancer to come up. Varies with the cloud service. + internal: + enabled: false + annotations: {} + + # loadBalancerIP: "" + + ## Restrict access For LoadBalancer service. Defaults to 0.0.0.0/0. + loadBalancerSourceRanges: [] + + ## Set external traffic policy to: "Local" to preserve source IP on + ## providers supporting it + ## Ref: https://kubernetes.io/docs/tutorials/services/source-ip/#source-ip-for-services-with-typeloadbalancer + # externalTrafficPolicy: "" + + extraContainers: [] + ## Additional containers to be added to the controller pod. + ## See https://github.com/lemonldap-ng-controller/lemonldap-ng-controller as example. + # - name: my-sidecar + # image: nginx:latest + # - name: lemonldap-ng-controller + # image: lemonldapng/lemonldap-ng-controller:0.2.0 + # args: + # - /lemonldap-ng-controller + # - --alsologtostderr + # - --configmap=$(POD_NAMESPACE)/lemonldap-ng-configuration + # env: + # - name: POD_NAME + # valueFrom: + # fieldRef: + # fieldPath: metadata.name + # - name: POD_NAMESPACE + # valueFrom: + # fieldRef: + # fieldPath: metadata.namespace + # volumeMounts: + # - name: copy-portal-skins + # mountPath: /srv/var/lib/lemonldap-ng/portal/skins + + extraVolumeMounts: [] + ## Additional volumeMounts to the controller main container. + # - name: copy-portal-skins + # mountPath: /var/lib/lemonldap-ng/portal/skins + + extraVolumes: [] + ## Additional volumes to the controller pod. + # - name: copy-portal-skins + # emptyDir: {} + + extraInitContainers: [] + ## Containers, which are run before the app containers are started. + # - name: init-myservice + # image: busybox + # command: ['sh', '-c', 'until nslookup myservice; do echo waiting for myservice; sleep 2; done;'] + + admissionWebhooks: + annotations: {} + enabled: true + failurePolicy: Fail + # timeoutSeconds: 10 + port: 8443 + certificate: "/usr/local/certificates/cert" + key: "/usr/local/certificates/key" + namespaceSelector: {} + objectSelector: {} + + # Use an existing PSP instead of creating one + existingPsp: "" + + service: + annotations: {} + # clusterIP: "" + externalIPs: [] + # loadBalancerIP: "" + loadBalancerSourceRanges: [] + servicePort: 443 + type: ClusterIP + + patch: + enabled: true + image: + registry: docker.io + image: jettech/kube-webhook-certgen + # for backwards compatibility consider setting the full image url via the repository value below + # use *either* current default registry/image or repository format or installing chart by providing the values.yaml will fail + # repository: + tag: v1.5.1 + pullPolicy: IfNotPresent + ## Provide a priority class name to the webhook patching job + ## + priorityClassName: "" + podAnnotations: {} + nodeSelector: {} + tolerations: [] + runAsUser: 2000 + + metrics: + port: 10254 + # if this port is changed, change healthz-port: in extraArgs: accordingly + enabled: false + + service: + annotations: {} + # prometheus.io/scrape: "true" + # prometheus.io/port: "10254" + + # clusterIP: "" + + ## List of IP addresses at which the stats-exporter service is available + ## Ref: https://kubernetes.io/docs/user-guide/services/#external-ips + ## + externalIPs: [] + + # loadBalancerIP: "" + loadBalancerSourceRanges: [] + servicePort: 10254 + type: ClusterIP + # externalTrafficPolicy: "" + # nodePort: "" + + serviceMonitor: + enabled: false + additionalLabels: {} + # The label to use to retrieve the job name from. + # jobLabel: "app.kubernetes.io/name" + namespace: "" + namespaceSelector: {} + # Default: scrape .Release.Namespace only + # To scrape all, use the following: + # namespaceSelector: + # any: true + scrapeInterval: 30s + # honorLabels: true + targetLabels: [] + metricRelabelings: [] + + prometheusRule: + enabled: false + additionalLabels: {} + # namespace: "" + rules: [] + # # These are just examples rules, please adapt them to your needs + # - alert: NGINXConfigFailed + # expr: count(nginx_ingress_controller_config_last_reload_successful == 0) > 0 + # for: 1s + # labels: + # severity: critical + # annotations: + # description: bad ingress config - nginx config test failed + # summary: uninstall the latest ingress changes to allow config reloads to resume + # - alert: NGINXCertificateExpiry + # expr: (avg(nginx_ingress_controller_ssl_expire_time_seconds) by (host) - time()) < 604800 + # for: 1s + # labels: + # severity: critical + # annotations: + # description: ssl certificate(s) will expire in less then a week + # summary: renew expiring certificates to avoid downtime + # - alert: NGINXTooMany500s + # expr: 100 * ( sum( nginx_ingress_controller_requests{status=~"5.+"} ) / sum(nginx_ingress_controller_requests) ) > 5 + # for: 1m + # labels: + # severity: warning + # annotations: + # description: Too many 5XXs + # summary: More than 5% of all requests returned 5XX, this requires your attention + # - alert: NGINXTooMany400s + # expr: 100 * ( sum( nginx_ingress_controller_requests{status=~"4.+"} ) / sum(nginx_ingress_controller_requests) ) > 5 + # for: 1m + # labels: + # severity: warning + # annotations: + # description: Too many 4XXs + # summary: More than 5% of all requests returned 4XX, this requires your attention + + ## Improve connection draining when ingress controller pod is deleted using a lifecycle hook: + ## With this new hook, we increased the default terminationGracePeriodSeconds from 30 seconds + ## to 300, allowing the draining of connections up to five minutes. + ## If the active connections end before that, the pod will terminate gracefully at that time. + ## To effectively take advantage of this feature, the Configmap feature + ## worker-shutdown-timeout new value is 240s instead of 10s. + ## + lifecycle: + preStop: + exec: + command: + - /wait-shutdown + + priorityClassName: "" + +## Rollback limit +## +revisionHistoryLimit: 10 + +## Default 404 backend +## +defaultBackend: + ## + enabled: false + + name: defaultbackend + image: + registry: k8s.gcr.io + image: defaultbackend-amd64 + # for backwards compatibility consider setting the full image url via the repository value below + # use *either* current default registry/image or repository format or installing chart by providing the values.yaml will fail + # repository: + tag: "1.5" + pullPolicy: IfNotPresent + # nobody user -> uid 65534 + runAsUser: 65534 + runAsNonRoot: true + readOnlyRootFilesystem: true + allowPrivilegeEscalation: false + + # Use an existing PSP instead of creating one + existingPsp: "" + + extraArgs: {} + + serviceAccount: + create: true + name: "" + automountServiceAccountToken: true + ## Additional environment variables to set for defaultBackend pods + extraEnvs: [] + + port: 8080 + + ## Readiness and liveness probes for default backend + ## Ref: https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-probes/ + ## + livenessProbe: + failureThreshold: 3 + initialDelaySeconds: 30 + periodSeconds: 10 + successThreshold: 1 + timeoutSeconds: 5 + readinessProbe: + failureThreshold: 6 + initialDelaySeconds: 0 + periodSeconds: 5 + successThreshold: 1 + timeoutSeconds: 5 + + ## Node tolerations for server scheduling to nodes with taints + ## Ref: https://kubernetes.io/docs/concepts/configuration/assign-pod-node/ + ## + tolerations: [] + # - key: "key" + # operator: "Equal|Exists" + # value: "value" + # effect: "NoSchedule|PreferNoSchedule|NoExecute(1.6 only)" + + affinity: {} + + ## Security Context policies for controller pods + ## See https://kubernetes.io/docs/tasks/administer-cluster/sysctl-cluster/ for + ## notes on enabling and using sysctls + ## + podSecurityContext: {} + + # labels to add to the pod container metadata + podLabels: {} + # key: value + + ## Node labels for default backend pod assignment + ## Ref: https://kubernetes.io/docs/user-guide/node-selection/ + ## + nodeSelector: {} + + ## Annotations to be added to default backend pods + ## + podAnnotations: {} + + replicaCount: 1 + + minAvailable: 1 + + resources: {} + # limits: + # cpu: 10m + # memory: 20Mi + # requests: + # cpu: 10m + # memory: 20Mi + + extraVolumeMounts: [] + ## Additional volumeMounts to the default backend container. + # - name: copy-portal-skins + # mountPath: /var/lib/lemonldap-ng/portal/skins + + extraVolumes: [] + ## Additional volumes to the default backend pod. + # - name: copy-portal-skins + # emptyDir: {} + + autoscaling: + annotations: {} + enabled: false + minReplicas: 1 + maxReplicas: 2 + targetCPUUtilizationPercentage: 50 + targetMemoryUtilizationPercentage: 50 + + service: + annotations: {} + + # clusterIP: "" + + ## List of IP addresses at which the default backend service is available + ## Ref: https://kubernetes.io/docs/user-guide/services/#external-ips + ## + externalIPs: [] + + # loadBalancerIP: "" + loadBalancerSourceRanges: [] + servicePort: 80 + type: ClusterIP + + priorityClassName: "" + +## Enable RBAC as per https://github.com/kubernetes/ingress/tree/master/examples/rbac/nginx and https://github.com/kubernetes/ingress/issues/266 +rbac: + create: true + scope: false + +# If true, create & use Pod Security Policy resources +# https://kubernetes.io/docs/concepts/policy/pod-security-policy/ +podSecurityPolicy: + enabled: false + +serviceAccount: + create: true + name: "" + automountServiceAccountToken: true + +## Optional array of imagePullSecrets containing private registry credentials +## Ref: https://kubernetes.io/docs/tasks/configure-pod-container/pull-image-private-registry/ +imagePullSecrets: [] +# - name: secretName + +# TCP service key:value pairs +# Ref: https://github.com/kubernetes/contrib/tree/master/ingress/controllers/nginx/examples/tcp +## +tcp: {} +# 8080: "default/example-tcp-svc:9000" + +# UDP service key:value pairs +# Ref: https://github.com/kubernetes/contrib/tree/master/ingress/controllers/nginx/examples/udp +## +udp: {} +# 53: "kube-system/kube-dns:53" + +# A base64ed Diffie-Hellman parameter +# This can be generated with: openssl dhparam 4096 2> /dev/null | base64 +# Ref: https://github.com/krmichel/ingress-nginx/blob/master/docs/examples/customization/ssl-dh-param +dhParam: diff --git a/hosting/kubernetes/budibase/templates/NOTES.txt b/hosting/kubernetes/budibase/templates/NOTES.txt new file mode 100644 index 0000000000..2ace0aa38d --- /dev/null +++ b/hosting/kubernetes/budibase/templates/NOTES.txt @@ -0,0 +1,22 @@ +1. Get the application URL by running these commands: +{{- if .Values.ingress.enabled }} +{{- range $host := .Values.ingress.hosts }} + {{- range .paths }} + http{{ if $.Values.ingress.tls }}s{{ end }}://{{ $host.host }}{{ .path }} + {{- end }} +{{- end }} +{{- else if contains "NodePort" .Values.service.type }} + export NODE_PORT=$(kubectl get --namespace {{ .Release.Namespace }} -o jsonpath="{.spec.ports[0].nodePort}" services {{ include "budibase.fullname" . }}) + export NODE_IP=$(kubectl get nodes --namespace {{ .Release.Namespace }} -o jsonpath="{.items[0].status.addresses[0].address}") + echo http://$NODE_IP:$NODE_PORT +{{- else if contains "LoadBalancer" .Values.service.type }} + NOTE: It may take a few minutes for the LoadBalancer IP to be available. + You can watch the status of by running 'kubectl get --namespace {{ .Release.Namespace }} svc -w {{ include "budibase.fullname" . }}' + export SERVICE_IP=$(kubectl get svc --namespace {{ .Release.Namespace }} {{ include "budibase.fullname" . }} --template "{{"{{ range (index .status.loadBalancer.ingress 0) }}{{.}}{{ end }}"}}") + echo http://$SERVICE_IP:{{ .Values.service.port }} +{{- else if contains "ClusterIP" .Values.service.type }} + export POD_NAME=$(kubectl get pods --namespace {{ .Release.Namespace }} -l "app.kubernetes.io/name={{ include "budibase.name" . }},app.kubernetes.io/instance={{ .Release.Name }}" -o jsonpath="{.items[0].metadata.name}") + export CONTAINER_PORT=$(kubectl get pod --namespace {{ .Release.Namespace }} $POD_NAME -o jsonpath="{.spec.containers[0].ports[0].containerPort}") + echo "Visit http://127.0.0.1:8080 to use your application" + kubectl --namespace {{ .Release.Namespace }} port-forward $POD_NAME 8080:$CONTAINER_PORT +{{- end }} diff --git a/hosting/kubernetes/budibase/templates/_helpers.tpl b/hosting/kubernetes/budibase/templates/_helpers.tpl new file mode 100644 index 0000000000..67485ac2a0 --- /dev/null +++ b/hosting/kubernetes/budibase/templates/_helpers.tpl @@ -0,0 +1,76 @@ +{{/* +Expand the name of the chart. +*/}} +{{- define "budibase.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 "budibase.fullname" -}} +{{- if .Values.fullnameOverride -}} +{{- printf "%s-%s" .Values.fullnameOverride .Chart.Name | trunc 63 | trimSuffix "-" -}} +{{- else -}} +{{- $name := default .Chart.Name .Values.nameOverride -}} +{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" -}} +{{- end -}} +{{- end -}} + +{{/* +CouchDB secret identifier +*/}} +{{- define "couchdb.fullname" -}} +{{- $name := "couchdb" -}} +{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" -}} +{{- end -}} + +{{/* +Create chart name and version as used by the chart label. +*/}} +{{- define "budibase.chart" -}} +{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }} +{{- end }} + +{{/* +Common labels +*/}} +{{- define "budibase.labels" -}} +helm.sh/chart: {{ include "budibase.chart" . }} +{{ include "budibase.selectorLabels" . }} +{{- if .Chart.AppVersion }} +app.kubernetes.io/version: {{ .Chart.AppVersion | quote }} +{{- end }} +app.kubernetes.io/managed-by: {{ .Release.Service }} +{{- end }} + +{{/* +Selector labels +*/}} +{{- define "budibase.selectorLabels" -}} +app.kubernetes.io/name: {{ include "budibase.name" . }} +app.kubernetes.io/instance: {{ .Release.Name }} +{{- end }} + +{{/* +Create the name of the service account to use +*/}} +{{- define "budibase.serviceAccountName" -}} +{{- if .Values.serviceAccount.create }} +{{- default (include "budibase.fullname" .) .Values.serviceAccount.name }} +{{- else }} +{{- default "default" .Values.serviceAccount.name }} +{{- end }} +{{- end }} + +{{/* +Create a random string if the supplied key does not exist +*/}} +{{- define "budibase.defaultsecret" -}} +{{- if . -}} +{{- . | b64enc | quote -}} +{{- else -}} +{{- randAlphaNum 20 | b64enc | quote -}} +{{- end -}} +{{- end -}} \ No newline at end of file diff --git a/hosting/kubernetes/budibase/templates/app-service-deployment.yaml b/hosting/kubernetes/budibase/templates/app-service-deployment.yaml new file mode 100644 index 0000000000..9f23089fd8 --- /dev/null +++ b/hosting/kubernetes/budibase/templates/app-service-deployment.yaml @@ -0,0 +1,79 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + annotations: + kompose.cmd: kompose convert + kompose.version: 1.21.0 (992df58d8) + creationTimestamp: null + labels: + io.kompose.service: app-service + name: app-service +spec: + replicas: {{ .Values.services.apps.replicaCount }} + selector: + matchLabels: + io.kompose.service: app-service + strategy: + type: Recreate + template: + metadata: + annotations: + kompose.cmd: kompose convert + kompose.version: 1.21.0 (992df58d8) + creationTimestamp: null + labels: + io.kompose.service: app-service + spec: + containers: + - env: + - name: BUDIBASE_ENVIRONMENT + value: {{ .Values.globals.budibaseEnv }} + - name: COUCH_DB_URL + value: {{ .Values.services.couchdb.url | quote }} + - name: ENABLE_ANALYTICS + value: {{ .Values.globals.enableAnalytics | quote }} + - name: INTERNAL_API_KEY + valueFrom: + secretKeyRef: + name: {{ template "budibase.fullname" . }} + key: internalApiKey + - name: JWT_SECRET + valueFrom: + secretKeyRef: + name: {{ template "budibase.fullname" . }} + key: jwtSecret + - name: LOG_LEVEL + value: {{ .Values.services.apps.logLevel | default "info" | quote }} + - name: MINIO_ACCESS_KEY + valueFrom: + secretKeyRef: + name: {{ template "budibase.fullname" . }} + key: objectStoreAccess + - name: MINIO_SECRET_KEY + valueFrom: + secretKeyRef: + name: {{ template "budibase.fullname" . }} + key: objectStoreSecret + - name: MINIO_URL + value: {{ .Values.services.object_store.url }} + - name: PORT + value: {{ .Values.services.apps.port | quote }} + - name: REDIS_PASSWORD + value: {{ .Values.services.redis.password }} + - name: REDIS_URL + value: "{{ .Values.services.redis.host }}:{{ .Values.services.redis.port }}" + - name: SELF_HOSTED + value: {{ .Values.globals.selfHosted | quote }} + - name: SENTRY_DSN + value: {{ .Values.globals.sentryDSN }} + - name: WORKER_URL + value: "{{ .Values.services.worker.host }}:{{ .Values.services.worker.port }}" + image: budibase/apps + imagePullPolicy: Always + name: bbapps + ports: + - containerPort: {{ .Values.services.apps.port }} + resources: {} + restartPolicy: Always + serviceAccountName: "" +status: {} diff --git a/hosting/kubernetes/budibase/templates/app-service-service.yaml b/hosting/kubernetes/budibase/templates/app-service-service.yaml new file mode 100644 index 0000000000..5247b4de09 --- /dev/null +++ b/hosting/kubernetes/budibase/templates/app-service-service.yaml @@ -0,0 +1,19 @@ +apiVersion: v1 +kind: Service +metadata: + annotations: + kompose.cmd: kompose convert + kompose.version: 1.21.0 (992df58d8) + creationTimestamp: null + labels: + io.kompose.service: app-service + name: app-service +spec: + ports: + - name: {{ .Values.services.apps.port | quote }} + port: {{ .Values.services.apps.port }} + targetPort: {{ .Values.services.apps.port }} + selector: + io.kompose.service: app-service +status: + loadBalancer: {} diff --git a/hosting/kubernetes/budibase/templates/hpa.yaml b/hosting/kubernetes/budibase/templates/hpa.yaml new file mode 100644 index 0000000000..2f901b4664 --- /dev/null +++ b/hosting/kubernetes/budibase/templates/hpa.yaml @@ -0,0 +1,28 @@ +{{- if .Values.autoscaling.enabled }} +apiVersion: autoscaling/v2beta1 +kind: HorizontalPodAutoscaler +metadata: + name: {{ include "budibase.fullname" . }} + labels: + {{- include "budibase.labels" . | nindent 4 }} +spec: + scaleTargetRef: + apiVersion: apps/v1 + kind: Deployment + name: {{ include "budibase.fullname" . }} + minReplicas: {{ .Values.autoscaling.minReplicas }} + maxReplicas: {{ .Values.autoscaling.maxReplicas }} + metrics: + {{- if .Values.autoscaling.targetCPUUtilizationPercentage }} + - type: Resource + resource: + name: cpu + targetAverageUtilization: {{ .Values.autoscaling.targetCPUUtilizationPercentage }} + {{- end }} + {{- if .Values.autoscaling.targetMemoryUtilizationPercentage }} + - type: Resource + resource: + name: memory + targetAverageUtilization: {{ .Values.autoscaling.targetMemoryUtilizationPercentage }} + {{- end }} +{{- end }} diff --git a/hosting/kubernetes/budibase/templates/ingress.yaml b/hosting/kubernetes/budibase/templates/ingress.yaml new file mode 100644 index 0000000000..4de295e18a --- /dev/null +++ b/hosting/kubernetes/budibase/templates/ingress.yaml @@ -0,0 +1,61 @@ +{{- if .Values.ingress.enabled -}} +{{- $fullName := include "budibase.fullname" . -}} +{{- $svcPort := .Values.service.port -}} +{{- if and .Values.ingress.className (not (semverCompare ">=1.18-0" .Capabilities.KubeVersion.GitVersion)) }} + {{- if not (hasKey .Values.ingress.annotations "kubernetes.io/ingress.class") }} + {{- $_ := set .Values.ingress.annotations "kubernetes.io/ingress.class" .Values.ingress.className}} + {{- end }} +{{- end }} +{{- if semverCompare ">=1.19-0" .Capabilities.KubeVersion.GitVersion -}} +apiVersion: networking.k8s.io/v1 +{{- else if semverCompare ">=1.14-0" .Capabilities.KubeVersion.GitVersion -}} +apiVersion: networking.k8s.io/v1beta1 +{{- else -}} +apiVersion: extensions/v1beta1 +{{- end }} +kind: Ingress +metadata: + name: {{ $fullName }} + labels: + {{- include "budibase.labels" . | nindent 4 }} + {{- with .Values.ingress.annotations }} + annotations: + {{- toYaml . | nindent 4 }} + {{- end }} +spec: + {{- if and .Values.ingress.className (semverCompare ">=1.18-0" .Capabilities.KubeVersion.GitVersion) }} + ingressClassName: {{ .Values.ingress.className }} + {{- end }} + {{- if .Values.ingress.tls }} + tls: + {{- range .Values.ingress.tls }} + - hosts: + {{- range .hosts }} + - {{ . | quote }} + {{- end }} + secretName: {{ .secretName }} + {{- end }} + {{- end }} + rules: + {{- range .Values.ingress.hosts }} + - host: {{ .host | quote }} + http: + paths: + {{- range .paths }} + - path: {{ .path }} + {{- if and .pathType (semverCompare ">=1.18-0" $.Capabilities.KubeVersion.GitVersion) }} + pathType: {{ .pathType }} + {{- end }} + backend: + {{- if semverCompare ">=1.19-0" $.Capabilities.KubeVersion.GitVersion }} + service: + name: {{ .backend.service.name }} + port: + number: {{ .backend.service.port.number }} + {{- else }} + serviceName: {{ .backend.service.name }} + servicePort: {{ .backend.service.port.number }} + {{- end }} + {{- end }} + {{- end }} +{{- end }} diff --git a/hosting/kubernetes/budibase/templates/minio-data-persistentvolumeclaim.yaml b/hosting/kubernetes/budibase/templates/minio-data-persistentvolumeclaim.yaml new file mode 100644 index 0000000000..a016824d03 --- /dev/null +++ b/hosting/kubernetes/budibase/templates/minio-data-persistentvolumeclaim.yaml @@ -0,0 +1,16 @@ +{{- if .Values.services.object_store.minio }} +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + creationTimestamp: null + labels: + io.kompose.service: minio-data + name: minio-data +spec: + accessModes: + - ReadWriteOnce + resources: + requests: + storage: {{ .Values.services.object_store.storage }} +status: {} +{{- end }} \ No newline at end of file diff --git a/hosting/kubernetes/budibase/templates/minio-service-deployment.yaml b/hosting/kubernetes/budibase/templates/minio-service-deployment.yaml new file mode 100644 index 0000000000..405044c92d --- /dev/null +++ b/hosting/kubernetes/budibase/templates/minio-service-deployment.yaml @@ -0,0 +1,72 @@ +{{- if .Values.services.object_store.minio }} +apiVersion: apps/v1 +kind: Deployment +metadata: + annotations: + kompose.cmd: kompose convert + kompose.version: 1.21.0 (992df58d8) + creationTimestamp: null + labels: + io.kompose.service: minio-service + name: minio-service +spec: + replicas: 1 + selector: + matchLabels: + io.kompose.service: minio-service + strategy: + type: Recreate + template: + metadata: + annotations: + kompose.cmd: kompose convert + kompose.version: 1.21.0 (992df58d8) + creationTimestamp: null + labels: + io.kompose.service: minio-service + spec: + containers: + - args: + - server + - /data + env: + - name: MINIO_BROWSER + value: {{ .Values.services.object_store.browser | quote }} + - name: MINIO_ACCESS_KEY + valueFrom: + secretKeyRef: + name: {{ template "budibase.fullname" . }} + key: objectStoreAccess + # value: {{ .Values.services.object_store.accessKey }} + - name: MINIO_SECRET_KEY + valueFrom: + secretKeyRef: + name: {{ template "budibase.fullname" . }} + key: objectStoreSecret + # value: {{ .Values.services.object_store.secretKey }} + image: minio/minio + imagePullPolicy: "" + livenessProbe: + exec: + command: + - curl + - -f + - http://localhost:9000/minio/health/live + failureThreshold: 3 + periodSeconds: 30 + timeoutSeconds: 20 + name: minio-service + ports: + - containerPort: {{ .Values.services.object_store.port }} + resources: {} + volumeMounts: + - mountPath: /data + name: minio-data + restartPolicy: Always + serviceAccountName: "" + volumes: + - name: minio-data + persistentVolumeClaim: + claimName: minio-data +status: {} +{{- end }} \ No newline at end of file diff --git a/hosting/kubernetes/budibase/templates/minio-service-service.yaml b/hosting/kubernetes/budibase/templates/minio-service-service.yaml new file mode 100644 index 0000000000..61040e4f44 --- /dev/null +++ b/hosting/kubernetes/budibase/templates/minio-service-service.yaml @@ -0,0 +1,21 @@ +{{- if .Values.services.object_store.minio }} +apiVersion: v1 +kind: Service +metadata: + annotations: + kompose.cmd: kompose convert + kompose.version: 1.21.0 (992df58d8) + creationTimestamp: null + labels: + io.kompose.service: minio-service + name: minio-service +spec: + ports: + - name: {{ .Values.services.object_store.port | quote }} + port: {{ .Values.services.object_store.port }} + targetPort: {{ .Values.services.object_store.port }} + selector: + io.kompose.service: minio-service +status: + loadBalancer: {} +{{- end }} \ No newline at end of file diff --git a/hosting/kubernetes/budibase/templates/proxy-service-deployment.yaml b/hosting/kubernetes/budibase/templates/proxy-service-deployment.yaml new file mode 100644 index 0000000000..0f802da843 --- /dev/null +++ b/hosting/kubernetes/budibase/templates/proxy-service-deployment.yaml @@ -0,0 +1,38 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + annotations: + kompose.cmd: kompose convert + kompose.version: 1.21.0 (992df58d8) + creationTimestamp: null + labels: + app.kubernetes.io/name: budibase-proxy + name: proxy-service +spec: + replicas: {{ .Values.services.proxy.replicaCount }} + selector: + matchLabels: + app.kubernetes.io/name: budibase-proxy + strategy: + type: Recreate + template: + metadata: + annotations: + kompose.cmd: kompose convert + kompose.version: 1.21.0 (992df58d8) + creationTimestamp: null + labels: + app.kubernetes.io/name: budibase-proxy + spec: + containers: + - image: budibase/proxy + imagePullPolicy: "" + name: proxy-service + ports: + - containerPort: {{ .Values.services.proxy.port }} + resources: {} + volumeMounts: + restartPolicy: Always + serviceAccountName: "" + volumes: +status: {} diff --git a/hosting/kubernetes/budibase/templates/proxy-service-service.yaml b/hosting/kubernetes/budibase/templates/proxy-service-service.yaml new file mode 100644 index 0000000000..8f14d97862 --- /dev/null +++ b/hosting/kubernetes/budibase/templates/proxy-service-service.yaml @@ -0,0 +1,20 @@ +apiVersion: v1 +kind: Service +metadata: + annotations: + kompose.cmd: kompose convert + kompose.version: 1.21.0 (992df58d8) + creationTimestamp: null + labels: + app.kubernetes.io/name: budibase-proxy + name: proxy-service +spec: + type: NodePort + ports: + - port: {{ .Values.services.proxy.port }} + targetPort: {{ .Values.services.proxy.port }} + protocol: TCP + selector: + app.kubernetes.io/name: budibase-proxy +status: + loadBalancer: {} diff --git a/hosting/kubernetes/budibase/templates/redis-data-persistentvolumeclaim.yaml b/hosting/kubernetes/budibase/templates/redis-data-persistentvolumeclaim.yaml new file mode 100644 index 0000000000..2cb5ee8eab --- /dev/null +++ b/hosting/kubernetes/budibase/templates/redis-data-persistentvolumeclaim.yaml @@ -0,0 +1,16 @@ +{{- if .Values.services.redis.enabled }} +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + creationTimestamp: null + labels: + io.kompose.service: redis-data + name: redis-data +spec: + accessModes: + - ReadWriteOnce + resources: + requests: + storage: {{ .Values.services.redis.storage }} +status: {} +{{- end }} \ No newline at end of file diff --git a/hosting/kubernetes/budibase/templates/redis-service-deployment.yaml b/hosting/kubernetes/budibase/templates/redis-service-deployment.yaml new file mode 100644 index 0000000000..9235b0b11d --- /dev/null +++ b/hosting/kubernetes/budibase/templates/redis-service-deployment.yaml @@ -0,0 +1,49 @@ +{{- if .Values.services.redis.enabled }} +apiVersion: apps/v1 +kind: Deployment +metadata: + annotations: + kompose.cmd: kompose convert + kompose.version: 1.21.0 (992df58d8) + creationTimestamp: null + labels: + io.kompose.service: redis-service + name: redis-service +spec: + replicas: {{ .Values.services.redis.replicaCount }} + selector: + matchLabels: + io.kompose.service: redis-service + strategy: + type: Recreate + template: + metadata: + annotations: + kompose.cmd: kompose convert + kompose.version: 1.21.0 (992df58d8) + creationTimestamp: null + labels: + io.kompose.service: redis-service + spec: + containers: + - args: + - redis-server + - --requirepass + - {{ .Values.services.redis.password }} + image: redis + imagePullPolicy: "" + name: redis-service + ports: + - containerPort: {{ .Values.services.redis.port }} + resources: {} + volumeMounts: + - mountPath: /data + name: redis-data + restartPolicy: Always + serviceAccountName: "" + volumes: + - name: redis-data + persistentVolumeClaim: + claimName: redis-data +status: {} +{{- end }} \ No newline at end of file diff --git a/hosting/kubernetes/budibase/templates/redis-service-service.yaml b/hosting/kubernetes/budibase/templates/redis-service-service.yaml new file mode 100644 index 0000000000..55ca40ed88 --- /dev/null +++ b/hosting/kubernetes/budibase/templates/redis-service-service.yaml @@ -0,0 +1,21 @@ +{{- if .Values.services.redis.enabled }} +apiVersion: v1 +kind: Service +metadata: + annotations: + kompose.cmd: kompose convert + kompose.version: 1.21.0 (992df58d8) + creationTimestamp: null + labels: + io.kompose.service: redis-service + name: redis-service +spec: + ports: + - name: {{ .Values.services.redis.port | quote }} + port: {{ .Values.services.redis.port }} + targetPort: {{ .Values.services.redis.port }} + selector: + io.kompose.service: redis-service +status: + loadBalancer: {} +{{- end }} \ No newline at end of file diff --git a/hosting/kubernetes/budibase/templates/secrets.yaml b/hosting/kubernetes/budibase/templates/secrets.yaml new file mode 100644 index 0000000000..09ee77511e --- /dev/null +++ b/hosting/kubernetes/budibase/templates/secrets.yaml @@ -0,0 +1,17 @@ +{{- if .Values.globals.createSecrets -}} +apiVersion: v1 +kind: Secret +metadata: + name: {{ template "budibase.fullname" . }} + labels: + app: {{ template "budibase.fullname" . }} + chart: "{{ .Chart.Name }}-{{ .Chart.Version }}" + release: "{{ .Release.Name }}" + heritage: "{{ .Release.Service }}" +type: Opaque +data: + internalApiKey: {{ template "budibase.defaultsecret" .Values.globals.internalApiKey }} + jwtSecret: {{ template "budibase.defaultsecret" .Values.globals.jwtSecret }} + objectStoreAccess: {{ template "budibase.defaultsecret" .Values.services.object_store.accessKey }} + objectStoreSecret: {{ template "budibase.defaultsecret" .Values.services.object_store.secretKey }} +{{- end -}} diff --git a/hosting/kubernetes/budibase/templates/service.yaml b/hosting/kubernetes/budibase/templates/service.yaml new file mode 100644 index 0000000000..be4d932b59 --- /dev/null +++ b/hosting/kubernetes/budibase/templates/service.yaml @@ -0,0 +1,15 @@ +apiVersion: v1 +kind: Service +metadata: + name: {{ include "budibase.fullname" . }} + labels: + {{- include "budibase.labels" . | nindent 4 }} +spec: + type: {{ .Values.service.type }} + ports: + - port: {{ .Values.service.port }} + targetPort: http + protocol: TCP + name: http + selector: + {{- include "budibase.selectorLabels" . | nindent 4 }} \ No newline at end of file diff --git a/hosting/kubernetes/budibase/templates/serviceaccount.yaml b/hosting/kubernetes/budibase/templates/serviceaccount.yaml new file mode 100644 index 0000000000..1aa1020088 --- /dev/null +++ b/hosting/kubernetes/budibase/templates/serviceaccount.yaml @@ -0,0 +1,12 @@ +{{- if .Values.serviceAccount.create -}} +apiVersion: v1 +kind: ServiceAccount +metadata: + name: {{ include "budibase.serviceAccountName" . }} + labels: + {{- include "budibase.labels" . | nindent 4 }} + {{- with .Values.serviceAccount.annotations }} + annotations: + {{- toYaml . | nindent 4 }} + {{- end }} +{{- end }} diff --git a/hosting/kubernetes/budibase/templates/tests/test-connection.yaml b/hosting/kubernetes/budibase/templates/tests/test-connection.yaml new file mode 100644 index 0000000000..ecd1f361de --- /dev/null +++ b/hosting/kubernetes/budibase/templates/tests/test-connection.yaml @@ -0,0 +1,15 @@ +apiVersion: v1 +kind: Pod +metadata: + name: "{{ include "budibase.fullname" . }}-test-connection" + labels: + {{- include "budibase.labels" . | nindent 4 }} + annotations: + "helm.sh/hook": test +spec: + containers: + - name: wget + image: busybox + command: ['wget'] + args: ['{{ include "budibase.fullname" . }}:{{ .Values.service.port }}'] + restartPolicy: Never diff --git a/hosting/kubernetes/budibase/templates/worker-service-deployment.yaml b/hosting/kubernetes/budibase/templates/worker-service-deployment.yaml new file mode 100644 index 0000000000..79cc078f90 --- /dev/null +++ b/hosting/kubernetes/budibase/templates/worker-service-deployment.yaml @@ -0,0 +1,82 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + annotations: + kompose.cmd: kompose convert + kompose.version: 1.21.0 (992df58d8) + creationTimestamp: null + labels: + io.kompose.service: worker-service + name: worker-service +spec: + replicas: {{ .Values.services.worker.replicaCount }} + + selector: + matchLabels: + io.kompose.service: worker-service + strategy: + type: Recreate + template: + metadata: + annotations: + kompose.cmd: kompose convert + kompose.version: 1.21.0 (992df58d8) + creationTimestamp: null + labels: + io.kompose.service: worker-service + spec: + containers: + - env: + - name: CLUSTER_PORT + value: {{ .Values.services.worker.port | quote }} + - name: COUCH_DB_USERNAME + valueFrom: + secretKeyRef: + name: {{ template "couchdb.fullname" . }} + key: adminUsername + - name: COUCH_DB_PASSWORD + valueFrom: + secretKeyRef: + name: {{ template "couchdb.fullname" . }} + key: adminPassword + - name: COUCH_DB_URL + value: {{ .Values.services.couchdb.url | quote }} + - name: INTERNAL_API_KEY + valueFrom: + secretKeyRef: + name: {{ template "budibase.fullname" . }} + key: internalApiKey + - name: JWT_SECRET + valueFrom: + secretKeyRef: + name: {{ template "budibase.fullname" . }} + key: jwtSecret + - name: MINIO_ACCESS_KEY + valueFrom: + secretKeyRef: + name: {{ template "budibase.fullname" . }} + key: objectStoreAccess + - name: MINIO_SECRET_KEY + valueFrom: + secretKeyRef: + name: {{ template "budibase.fullname" . }} + key: objectStoreSecret + - name: MINIO_URL + value: {{ .Values.services.object_store.url }} + - name: PORT + value: {{ .Values.services.worker.port | quote }} + - name: REDIS_PASSWORD + value: {{ .Values.services.redis.password | quote }} + - name: REDIS_URL + value: "{{ .Values.services.redis.host }}:{{ .Values.services.redis.port }}" + - name: SELF_HOSTED + value: {{ .Values.globals.selfHosted | quote }} + image: budibase/worker + imagePullPolicy: Always + name: bbworker + ports: + - containerPort: {{ .Values.services.worker.port }} + resources: {} + restartPolicy: Always + serviceAccountName: "" +status: {} diff --git a/hosting/kubernetes/budibase/templates/worker-service-service.yaml b/hosting/kubernetes/budibase/templates/worker-service-service.yaml new file mode 100644 index 0000000000..a79ba1e04b --- /dev/null +++ b/hosting/kubernetes/budibase/templates/worker-service-service.yaml @@ -0,0 +1,19 @@ +apiVersion: v1 +kind: Service +metadata: + annotations: + kompose.cmd: kompose convert + kompose.version: 1.21.0 (992df58d8) + creationTimestamp: null + labels: + io.kompose.service: worker-service + name: worker-service +spec: + ports: + - name: {{ .Values.services.worker.port | quote }} + port: {{ .Values.services.worker.port }} + targetPort: {{ .Values.services.worker.port }} + selector: + io.kompose.service: worker-service +status: + loadBalancer: {} diff --git a/hosting/kubernetes/budibase/values.schema.json b/hosting/kubernetes/budibase/values.schema.json new file mode 100644 index 0000000000..8021a46036 --- /dev/null +++ b/hosting/kubernetes/budibase/values.schema.json @@ -0,0 +1,178 @@ +{ + "$schema": "http://json-schema.org/schema#", + "type": "object", + "properties": { + "architecture": { + "type": "string", + "title": "MySQL architecture", + "form": true, + "description": "Allowed values: `standalone` or `replication`", + "enum": ["standalone", "replication"] + }, + "auth": { + "type": "object", + "title": "Authentication configuration", + "form": true, + "required": ["database", "username", "password"], + "properties": { + "rootPassword": { + "type": "string", + "title": "MySQL root password", + "description": "Defaults to a random 10-character alphanumeric string if not set" + }, + "database": { + "type": "string", + "title": "MySQL custom database name" + }, + "username": { + "type": "string", + "title": "MySQL custom username" + }, + "password": { + "type": "string", + "title": "MySQL custom password" + }, + "replicationUser": { + "type": "string", + "title": "MySQL replication username" + }, + "replicationPassword": { + "type": "string", + "title": "MySQL replication password" + } + } + }, + "primary": { + "type": "object", + "title": "Primary database configuration", + "form": true, + "properties": { + "podSecurityContext": { + "type": "object", + "title": "MySQL primary Pod security context", + "properties": { + "enabled": { + "type": "boolean", + "default": false + }, + "fsGroup": { + "type": "integer", + "default": 1001, + "hidden": { + "value": false, + "path": "primary/podSecurityContext/enabled" + } + } + } + }, + "containerSecurityContext": { + "type": "object", + "title": "MySQL primary container security context", + "properties": { + "enabled": { + "type": "boolean", + "default": false + }, + "runAsUser": { + "type": "integer", + "default": 1001, + "hidden": { + "value": false, + "path": "primary/containerSecurityContext/enabled" + } + } + } + }, + "persistence": { + "type": "object", + "title": "Enable persistence using Persistent Volume Claims", + "properties": { + "enabled": { + "type": "boolean", + "default": true, + "title": "If true, use a Persistent Volume Claim, If false, use emptyDir" + }, + "size": { + "type": "string", + "title": "Persistent Volume Size", + "form": true, + "render": "slider", + "sliderMin": 1, + "sliderUnit": "Gi", + "hidden": { + "value": false, + "path": "primary/persistence/enabled" + } + } + } + } + } + }, + "secondary": { + "type": "object", + "title": "Secondary database configuration", + "form": true, + "properties": { + "podSecurityContext": { + "type": "object", + "title": "MySQL secondary Pod security context", + "properties": { + "enabled": { + "type": "boolean", + "default": false + }, + "fsGroup": { + "type": "integer", + "default": 1001, + "hidden": { + "value": false, + "path": "secondary/podSecurityContext/enabled" + } + } + } + }, + "containerSecurityContext": { + "type": "object", + "title": "MySQL secondary container security context", + "properties": { + "enabled": { + "type": "boolean", + "default": false + }, + "runAsUser": { + "type": "integer", + "default": 1001, + "hidden": { + "value": false, + "path": "secondary/containerSecurityContext/enabled" + } + } + } + }, + "persistence": { + "type": "object", + "title": "Enable persistence using Persistent Volume Claims", + "properties": { + "enabled": { + "type": "boolean", + "default": true, + "title": "If true, use a Persistent Volume Claim, If false, use emptyDir" + }, + "size": { + "type": "string", + "title": "Persistent Volume Size", + "form": true, + "render": "slider", + "sliderMin": 1, + "sliderUnit": "Gi", + "hidden": { + "value": false, + "path": "secondary/persistence/enabled" + } + } + } + } + } + } + } +} \ No newline at end of file diff --git a/hosting/kubernetes/budibase/values.yaml b/hosting/kubernetes/budibase/values.yaml new file mode 100644 index 0000000000..fd3ab24e62 --- /dev/null +++ b/hosting/kubernetes/budibase/values.yaml @@ -0,0 +1,141 @@ +# Default values for budibase. +# This is a YAML-formatted file. +# Declare variables to be passed into your templates. + +replicaCount: 1 + +image: + pullPolicy: IfNotPresent + # Overrides the image tag whose default is the chart appVersion. + tag: "" + +imagePullSecrets: [] +nameOverride: "" +# fullnameOverride: "" + +serviceAccount: + # Specifies whether a service account should be created + create: true + # Annotations to add to the service account + annotations: {} + # The name of the service account to use. + # If not set and create is true, a name is generated using the fullname template + name: "" + +podAnnotations: {} + +podSecurityContext: {} + # fsGroup: 2000 + +securityContext: {} + # capabilities: + # drop: + # - ALL + # readOnlyRootFilesystem: true + # runAsNonRoot: true + # runAsUser: 1000 + +service: + type: ClusterIP + port: 10000 + +ingress: + enabled: true + # certificateArn: "" + className: "" + annotations: + kubernetes.io/ingress.class: nginx + hosts: + - host: budibase.cluster + paths: + - path: / + pathType: Prefix + backend: + service: + name: proxy-service + port: + number: 10000 + +resources: {} + # We usually recommend not to specify default resources and to leave this as a conscious + # choice for the user. This also increases chances charts run on environments with little + # resources, such as Minikube. If you do want to specify resources, uncomment the following + # lines, adjust them as necessary, and remove the curly braces after 'resources:'. + # limits: + # cpu: 100m + # memory: 128Mi + # requests: + # cpu: 100m + # memory: 128Mi + +autoscaling: + enabled: false + minReplicas: 1 + maxReplicas: 100 + targetCPUUtilizationPercentage: 80 + # targetMemoryUtilizationPercentage: 80 + +nodeSelector: {} + +tolerations: [] + +affinity: {} + +globals: + budibaseEnv: PRODUCTION + enableAnalytics: false + posthogToken: "" + sentryDSN: "" + logLevel: info + selfHosted: 1 + # creates an internal API key, JWT secrets and redis password for you + createSecrets: true + # if createSecrets is set to false, you can hard-code your secrets here + internalApiKey: "" + jwtSecret: "" + + +services: + proxy: + port: 10000 + replicaCount: 1 + storage: 100Mi + + apps: + port: 4002 + replicaCount: 1 + storage: 100Mi + logLevel: info + + worker: + port: 4001 + replicaCount: 1 + storage: 100Mi + + couchdb: + replicaCount: 3 + host: budibase-prod-svc-couchdb + url: budibase-prod-svc-couchdb # only change if pointing to existing couch server + user: "" # only change if pointing to existing couch server + password: "" # only change if pointing to existing couch server + port: 5678 + storage: 100Mi + + redis: + # disable if using external redis + enabled: true + port: 6379 + replicaCount: 1 + password: "budibase" # recommended to override if using built-in redis + storage: 100Mi + + object_store: + minio: false + browser: true + accessKey: "" # AWS_ACCESS_KEY or existing minio access key + secretKey: "" # AWS_SECRET_ACCESS_KEY or existing minio secret + url: minio-service.budibase.svc.cluster.local:9000 + port: 9000 + replicaCount: 1 + storage: 100Mi + diff --git a/hosting/kubernetes/envoy/Dockerfile b/hosting/kubernetes/envoy/Dockerfile new file mode 100644 index 0000000000..96334fa723 --- /dev/null +++ b/hosting/kubernetes/envoy/Dockerfile @@ -0,0 +1,4 @@ +FROM envoyproxy/envoy:v1.16-latest +COPY envoy.yaml /etc/envoy/envoy.yaml +RUN chmod go+r /etc/envoy/envoy.yaml + diff --git a/hosting/kubernetes/envoy/envoy.yaml b/hosting/kubernetes/envoy/envoy.yaml new file mode 100644 index 0000000000..0e7859d887 --- /dev/null +++ b/hosting/kubernetes/envoy/envoy.yaml @@ -0,0 +1,125 @@ +static_resources: + listeners: + - name: main_listener + address: + socket_address: { address: 0.0.0.0, port_value: 10000 } + filter_chains: + - filters: + - name: envoy.filters.network.http_connection_manager + typed_config: + "@type": type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager + stat_prefix: ingress + codec_type: auto + route_config: + name: local_route + virtual_hosts: + - name: local_services + domains: ["*"] + routes: + - match: { prefix: "/app/" } + route: + cluster: app-service + prefix_rewrite: "/" + + - match: { prefix: "/builder/" } + route: + cluster: app-service + + - match: { prefix: "/builder" } + route: + cluster: app-service + + - match: { prefix: "/app_" } + route: + cluster: app-service + + # special case for worker admin API + - match: { prefix: "/api/admin/" } + route: + cluster: worker-service + + - match: { path: "/" } + route: + cluster: app-service + + # special case for when API requests are made, can just forward, not to minio + - match: { prefix: "/api/" } + route: + cluster: app-service + + - match: { prefix: "/worker/" } + route: + cluster: worker-service + prefix_rewrite: "/" + + - match: { prefix: "/db/" } + route: + cluster: couchdb-service + prefix_rewrite: "/" + + # minio is on the default route because this works + # best, minio + AWS SDK doesn't handle path proxy + - match: { prefix: "/" } + route: + cluster: minio-service + + http_filters: + - name: envoy.filters.http.router + + clusters: + - name: app-service + connect_timeout: 0.25s + type: strict_dns + lb_policy: round_robin + load_assignment: + cluster_name: app-service + endpoints: + - lb_endpoints: + - endpoint: + address: + socket_address: + address: app-service.budibase.svc.cluster.local + port_value: 4002 + + - name: minio-service + connect_timeout: 0.25s + type: strict_dns + lb_policy: round_robin + load_assignment: + cluster_name: minio-service + endpoints: + - lb_endpoints: + - endpoint: + address: + socket_address: + address: minio-service.budibase.svc.cluster.local + port_value: 9000 + + - name: worker-service + connect_timeout: 0.25s + type: strict_dns + lb_policy: round_robin + load_assignment: + cluster_name: worker-service + endpoints: + - lb_endpoints: + - endpoint: + address: + socket_address: + address: worker-service.budibase.svc.cluster.local + port_value: 4001 + + - name: couchdb-service + connect_timeout: 0.25s + type: strict_dns + lb_policy: round_robin + load_assignment: + cluster_name: couchdb-service + endpoints: + - lb_endpoints: + - endpoint: + address: + socket_address: + address: couchdb-service.budibase.svc.cluster.local + port_value: 5984 + diff --git a/packages/server/src/db/client.js b/packages/server/src/db/client.js index d50d72b18d..f79efb3e9e 100644 --- a/packages/server/src/db/client.js +++ b/packages/server/src/db/client.js @@ -11,7 +11,7 @@ PouchDB.plugin(find) PouchDB.adapter("writableStream", replicationStream.adapters.writableStream) let POUCH_DB_DEFAULTS = { - prefix: COUCH_DB_URL, + prefix: COUCH_DB_URL } if (env.isTest()) { diff --git a/packages/server/src/utilities/index.js b/packages/server/src/utilities/index.js index 8da98e99e4..dec17ab284 100644 --- a/packages/server/src/utilities/index.js +++ b/packages/server/src/utilities/index.js @@ -2,7 +2,7 @@ const env = require("../environment") const { OBJ_STORE_DIRECTORY } = require("../constants") const { sanitizeKey } = require("@budibase/auth/src/objectStore") -const BB_CDN = "https://cdn.app.budi.live/assets" +const BB_CDN = "https://cdn.budi.live" exports.wait = ms => new Promise(resolve => setTimeout(resolve, ms)) From 0d6b0c887b082c9f1f9113087f413c385b6530ef Mon Sep 17 00:00:00 2001 From: Martin McKeaveney Date: Mon, 16 Aug 2021 15:15:07 +0100 Subject: [PATCH 02/22] named templates for local k8s DNS resolution --- .../budibase/templates/_helpers.tpl | 7 ++++++ .../templates/app-service-deployment.yaml | 11 +++++---- .../minio-data-persistentvolumeclaim.yaml | 4 ++-- .../templates/minio-service-deployment.yaml | 8 +++---- .../templates/minio-service-service.yaml | 8 +++---- .../budibase/templates/secrets.yaml | 4 ++-- .../templates/worker-service-deployment.yaml | 6 ++--- hosting/kubernetes/budibase/values.yaml | 23 ++++++++++--------- packages/server/src/db/client.js | 6 ++++- 9 files changed, 45 insertions(+), 32 deletions(-) diff --git a/hosting/kubernetes/budibase/templates/_helpers.tpl b/hosting/kubernetes/budibase/templates/_helpers.tpl index 67485ac2a0..3b0853e19f 100644 --- a/hosting/kubernetes/budibase/templates/_helpers.tpl +++ b/hosting/kubernetes/budibase/templates/_helpers.tpl @@ -26,6 +26,13 @@ CouchDB secret identifier {{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" -}} {{- end -}} +{{/* +Internal DNS +*/}} +{{- define "budibase.serviceDns" -}} +{{- printf "%s.%s.%s" .Release.Namespace "svc" .Values.services.dns -}} +{{- end -}} + {{/* Create chart name and version as used by the chart label. */}} diff --git a/hosting/kubernetes/budibase/templates/app-service-deployment.yaml b/hosting/kubernetes/budibase/templates/app-service-deployment.yaml index 9f23089fd8..16b7bdddec 100644 --- a/hosting/kubernetes/budibase/templates/app-service-deployment.yaml +++ b/hosting/kubernetes/budibase/templates/app-service-deployment.yaml @@ -29,7 +29,8 @@ spec: - name: BUDIBASE_ENVIRONMENT value: {{ .Values.globals.budibaseEnv }} - name: COUCH_DB_URL - value: {{ .Values.services.couchdb.url | quote }} + # Or inject value directly + value: couchdb-service.{{ include "budibase.serviceDns" . }}:{{ .Values.services.couchdb.port }} - name: ENABLE_ANALYTICS value: {{ .Values.globals.enableAnalytics | quote }} - name: INTERNAL_API_KEY @@ -55,19 +56,21 @@ spec: name: {{ template "budibase.fullname" . }} key: objectStoreSecret - name: MINIO_URL - value: {{ .Values.services.object_store.url }} + # Or inject value directly + value: minio-service.{{ include "budibase.serviceDns" . }}{{ .Values.services.objectStore.port }} - name: PORT value: {{ .Values.services.apps.port | quote }} - name: REDIS_PASSWORD value: {{ .Values.services.redis.password }} - name: REDIS_URL - value: "{{ .Values.services.redis.host }}:{{ .Values.services.redis.port }}" + # Or inject value directly + value: redis-service.{{ include "budibase.serviceDns" . }}:{{ .Values.services.redis.port }} - name: SELF_HOSTED value: {{ .Values.globals.selfHosted | quote }} - name: SENTRY_DSN value: {{ .Values.globals.sentryDSN }} - name: WORKER_URL - value: "{{ .Values.services.worker.host }}:{{ .Values.services.worker.port }}" + value: worker-service.{{ include "budibase.serviceDns" . }}:{{ .Values.services.worker.port }} image: budibase/apps imagePullPolicy: Always name: bbapps diff --git a/hosting/kubernetes/budibase/templates/minio-data-persistentvolumeclaim.yaml b/hosting/kubernetes/budibase/templates/minio-data-persistentvolumeclaim.yaml index a016824d03..d122ad0a3e 100644 --- a/hosting/kubernetes/budibase/templates/minio-data-persistentvolumeclaim.yaml +++ b/hosting/kubernetes/budibase/templates/minio-data-persistentvolumeclaim.yaml @@ -1,4 +1,4 @@ -{{- if .Values.services.object_store.minio }} +{{- if .Values.services.objectStore.minio }} apiVersion: v1 kind: PersistentVolumeClaim metadata: @@ -11,6 +11,6 @@ spec: - ReadWriteOnce resources: requests: - storage: {{ .Values.services.object_store.storage }} + storage: {{ .Values.services.objectStore.storage }} status: {} {{- end }} \ No newline at end of file diff --git a/hosting/kubernetes/budibase/templates/minio-service-deployment.yaml b/hosting/kubernetes/budibase/templates/minio-service-deployment.yaml index 405044c92d..a23d0c1d89 100644 --- a/hosting/kubernetes/budibase/templates/minio-service-deployment.yaml +++ b/hosting/kubernetes/budibase/templates/minio-service-deployment.yaml @@ -1,4 +1,4 @@ -{{- if .Values.services.object_store.minio }} +{{- if .Values.services.objectStore.minio }} apiVersion: apps/v1 kind: Deployment metadata: @@ -31,19 +31,17 @@ spec: - /data env: - name: MINIO_BROWSER - value: {{ .Values.services.object_store.browser | quote }} + value: {{ .Values.services.objectStore.browser | quote }} - name: MINIO_ACCESS_KEY valueFrom: secretKeyRef: name: {{ template "budibase.fullname" . }} key: objectStoreAccess - # value: {{ .Values.services.object_store.accessKey }} - name: MINIO_SECRET_KEY valueFrom: secretKeyRef: name: {{ template "budibase.fullname" . }} key: objectStoreSecret - # value: {{ .Values.services.object_store.secretKey }} image: minio/minio imagePullPolicy: "" livenessProbe: @@ -57,7 +55,7 @@ spec: timeoutSeconds: 20 name: minio-service ports: - - containerPort: {{ .Values.services.object_store.port }} + - containerPort: {{ .Values.services.objectStore.port }} resources: {} volumeMounts: - mountPath: /data diff --git a/hosting/kubernetes/budibase/templates/minio-service-service.yaml b/hosting/kubernetes/budibase/templates/minio-service-service.yaml index 61040e4f44..cfdb22002b 100644 --- a/hosting/kubernetes/budibase/templates/minio-service-service.yaml +++ b/hosting/kubernetes/budibase/templates/minio-service-service.yaml @@ -1,4 +1,4 @@ -{{- if .Values.services.object_store.minio }} +{{- if .Values.services.objectStore.minio }} apiVersion: v1 kind: Service metadata: @@ -11,9 +11,9 @@ metadata: name: minio-service spec: ports: - - name: {{ .Values.services.object_store.port | quote }} - port: {{ .Values.services.object_store.port }} - targetPort: {{ .Values.services.object_store.port }} + - name: {{ .Values.services.objectStore.port | quote }} + port: {{ .Values.services.objectStore.port }} + targetPort: {{ .Values.services.objectStore.port }} selector: io.kompose.service: minio-service status: diff --git a/hosting/kubernetes/budibase/templates/secrets.yaml b/hosting/kubernetes/budibase/templates/secrets.yaml index 09ee77511e..1c0a914ed3 100644 --- a/hosting/kubernetes/budibase/templates/secrets.yaml +++ b/hosting/kubernetes/budibase/templates/secrets.yaml @@ -12,6 +12,6 @@ type: Opaque data: internalApiKey: {{ template "budibase.defaultsecret" .Values.globals.internalApiKey }} jwtSecret: {{ template "budibase.defaultsecret" .Values.globals.jwtSecret }} - objectStoreAccess: {{ template "budibase.defaultsecret" .Values.services.object_store.accessKey }} - objectStoreSecret: {{ template "budibase.defaultsecret" .Values.services.object_store.secretKey }} + objectStoreAccess: {{ template "budibase.defaultsecret" .Values.services.objectStore.accessKey }} + objectStoreSecret: {{ template "budibase.defaultsecret" .Values.services.objectStore.secretKey }} {{- end -}} diff --git a/hosting/kubernetes/budibase/templates/worker-service-deployment.yaml b/hosting/kubernetes/budibase/templates/worker-service-deployment.yaml index 79cc078f90..1596b74bec 100644 --- a/hosting/kubernetes/budibase/templates/worker-service-deployment.yaml +++ b/hosting/kubernetes/budibase/templates/worker-service-deployment.yaml @@ -40,7 +40,7 @@ spec: name: {{ template "couchdb.fullname" . }} key: adminPassword - name: COUCH_DB_URL - value: {{ .Values.services.couchdb.url | quote }} + value: http://couchdb-service.{{ include "budibase.serviceDns" . }}:{{ .Values.services.couchdb.port }} - name: INTERNAL_API_KEY valueFrom: secretKeyRef: @@ -62,13 +62,13 @@ spec: name: {{ template "budibase.fullname" . }} key: objectStoreSecret - name: MINIO_URL - value: {{ .Values.services.object_store.url }} + value: minio-service.{{ include "budibase.serviceDns" . }}{{ .Values.services.objectStore.port }} - name: PORT value: {{ .Values.services.worker.port | quote }} - name: REDIS_PASSWORD value: {{ .Values.services.redis.password | quote }} - name: REDIS_URL - value: "{{ .Values.services.redis.host }}:{{ .Values.services.redis.port }}" + value: redis-service.{{ include "budibase.serviceDns" . }}{{ .Values.services.redis.port }} - name: SELF_HOSTED value: {{ .Values.globals.selfHosted | quote }} image: budibase/worker diff --git a/hosting/kubernetes/budibase/values.yaml b/hosting/kubernetes/budibase/values.yaml index fd3ab24e62..a71ad09b59 100644 --- a/hosting/kubernetes/budibase/values.yaml +++ b/hosting/kubernetes/budibase/values.yaml @@ -88,14 +88,16 @@ globals: sentryDSN: "" logLevel: info selfHosted: 1 - # creates an internal API key, JWT secrets and redis password for you - createSecrets: true + createSecrets: true # creates an internal API key, JWT secrets and redis password for you + # if createSecrets is set to false, you can hard-code your secrets here internalApiKey: "" jwtSecret: "" services: + dns: cluster.local + proxy: port: 10000 replicaCount: 1 @@ -114,28 +116,27 @@ services: couchdb: replicaCount: 3 - host: budibase-prod-svc-couchdb - url: budibase-prod-svc-couchdb # only change if pointing to existing couch server + url: "" # only change if pointing to existing couch server user: "" # only change if pointing to existing couch server password: "" # only change if pointing to existing couch server port: 5678 storage: 100Mi redis: - # disable if using external redis - enabled: true + enabled: true # disable if using external redis port: 6379 replicaCount: 1 + host: "" # only change if pointing to existing redis cluster and enabled: false password: "budibase" # recommended to override if using built-in redis storage: 100Mi - object_store: - minio: false + objectStore: + minio: true browser: true - accessKey: "" # AWS_ACCESS_KEY or existing minio access key - secretKey: "" # AWS_SECRET_ACCESS_KEY or existing minio secret - url: minio-service.budibase.svc.cluster.local:9000 port: 9000 replicaCount: 1 + accessKey: "" # AWS_ACCESS_KEY if using S3 or existing minio access key + secretKey: "" # AWS_SECRET_ACCESS_KEY if using S3 or existing minio secret + url: "" # only change if pointing to existing minio cluster and minio: false storage: 100Mi diff --git a/packages/server/src/db/client.js b/packages/server/src/db/client.js index f79efb3e9e..7523a53c81 100644 --- a/packages/server/src/db/client.js +++ b/packages/server/src/db/client.js @@ -11,7 +11,11 @@ PouchDB.plugin(find) PouchDB.adapter("writableStream", replicationStream.adapters.writableStream) let POUCH_DB_DEFAULTS = { - prefix: COUCH_DB_URL + prefix: COUCH_DB_URL, + auth: { + username: env.COUCH_DB_USER, + password: env.COUCH_DB_PASSWORD, + } } if (env.isTest()) { From 1b78d8349fcae4f8a22d331b0c6aa9805db00d82 Mon Sep 17 00:00:00 2001 From: Martin McKeaveney Date: Mon, 16 Aug 2021 19:00:08 +0100 Subject: [PATCH 03/22] totally generic templates complete, pending test in different clouds --- .../templates/app-service-deployment.yaml | 34 +++++++++++++++---- .../templates/worker-service-deployment.yaml | 23 +++++++++++-- hosting/kubernetes/budibase/values.yaml | 2 +- 3 files changed, 48 insertions(+), 11 deletions(-) diff --git a/hosting/kubernetes/budibase/templates/app-service-deployment.yaml b/hosting/kubernetes/budibase/templates/app-service-deployment.yaml index 16b7bdddec..24728c175f 100644 --- a/hosting/kubernetes/budibase/templates/app-service-deployment.yaml +++ b/hosting/kubernetes/budibase/templates/app-service-deployment.yaml @@ -29,8 +29,22 @@ spec: - name: BUDIBASE_ENVIRONMENT value: {{ .Values.globals.budibaseEnv }} - name: COUCH_DB_URL - # Or inject value directly - value: couchdb-service.{{ include "budibase.serviceDns" . }}:{{ .Values.services.couchdb.port }} + {{ if .Values.services.couchdb.url }} + value: {{ .Values.services.couchdb.url }} + {{ else }} + # value: http://{{ .Release.Name }}-svc-couchdb.{{ include "budibase.serviceDns" . }}:{{ .Values.services.couchdb.port }} + value: http://{{ .Release.Name }}-svc-couchdb:{{ .Values.services.couchdb.port }} + {{ end }} + - name: COUCH_DB_USER + valueFrom: + secretKeyRef: + name: {{ template "couchdb.fullname" . }} + key: adminUsername + - name: COUCH_DB_PASSWORD + valueFrom: + secretKeyRef: + name: {{ template "couchdb.fullname" . }} + key: adminPassword - name: ENABLE_ANALYTICS value: {{ .Values.globals.enableAnalytics | quote }} - name: INTERNAL_API_KEY @@ -56,21 +70,27 @@ spec: name: {{ template "budibase.fullname" . }} key: objectStoreSecret - name: MINIO_URL - # Or inject value directly - value: minio-service.{{ include "budibase.serviceDns" . }}{{ .Values.services.objectStore.port }} + {{ if .Values.services.objectStore.url }} + value: {{ .Values.services.objectStore.url }} + {{ else }} + value: http://minio-service:{{ .Values.services.objectStore.port }} + {{ end }} - name: PORT value: {{ .Values.services.apps.port | quote }} - name: REDIS_PASSWORD value: {{ .Values.services.redis.password }} - name: REDIS_URL - # Or inject value directly - value: redis-service.{{ include "budibase.serviceDns" . }}:{{ .Values.services.redis.port }} + {{ if .Values.services.redis.url }} + value: {{ .Values.services.redis.url }} + {{ else }} + value: redis-service:{{ .Values.services.redis.port }} + {{ end }} - name: SELF_HOSTED value: {{ .Values.globals.selfHosted | quote }} - name: SENTRY_DSN value: {{ .Values.globals.sentryDSN }} - name: WORKER_URL - value: worker-service.{{ include "budibase.serviceDns" . }}:{{ .Values.services.worker.port }} + value: worker-service:{{ .Values.services.worker.port }} image: budibase/apps imagePullPolicy: Always name: bbapps diff --git a/hosting/kubernetes/budibase/templates/worker-service-deployment.yaml b/hosting/kubernetes/budibase/templates/worker-service-deployment.yaml index 1596b74bec..af6212751f 100644 --- a/hosting/kubernetes/budibase/templates/worker-service-deployment.yaml +++ b/hosting/kubernetes/budibase/templates/worker-service-deployment.yaml @@ -34,13 +34,22 @@ spec: secretKeyRef: name: {{ template "couchdb.fullname" . }} key: adminUsername + - name: COUCH_DB_USER + valueFrom: + secretKeyRef: + name: {{ template "couchdb.fullname" . }} + key: adminUsername - name: COUCH_DB_PASSWORD valueFrom: secretKeyRef: name: {{ template "couchdb.fullname" . }} key: adminPassword - name: COUCH_DB_URL - value: http://couchdb-service.{{ include "budibase.serviceDns" . }}:{{ .Values.services.couchdb.port }} + {{ if .Values.services.couchdb.url }} + value: {{ .Values.services.couchdb.url }} + {{ else }} + value: http://{{ .Release.Name }}-svc-couchdb:{{ .Values.services.couchdb.port }} + {{ end }} - name: INTERNAL_API_KEY valueFrom: secretKeyRef: @@ -62,13 +71,21 @@ spec: name: {{ template "budibase.fullname" . }} key: objectStoreSecret - name: MINIO_URL - value: minio-service.{{ include "budibase.serviceDns" . }}{{ .Values.services.objectStore.port }} + {{ if .Values.services.objectStore.url }} + value: {{ .Values.services.objectStore.url }} + {{ else }} + value: http://minio-service:{{ .Values.services.objectStore.port }} + {{ end }} - name: PORT value: {{ .Values.services.worker.port | quote }} - name: REDIS_PASSWORD value: {{ .Values.services.redis.password | quote }} - name: REDIS_URL - value: redis-service.{{ include "budibase.serviceDns" . }}{{ .Values.services.redis.port }} + {{ if .Values.services.redis.url }} + value: {{ .Values.services.redis.url }} + {{ else }} + value: redis-service:{{ .Values.services.redis.port }} + {{ end }} - name: SELF_HOSTED value: {{ .Values.globals.selfHosted | quote }} image: budibase/worker diff --git a/hosting/kubernetes/budibase/values.yaml b/hosting/kubernetes/budibase/values.yaml index a71ad09b59..b3b81bebf4 100644 --- a/hosting/kubernetes/budibase/values.yaml +++ b/hosting/kubernetes/budibase/values.yaml @@ -119,7 +119,7 @@ services: url: "" # only change if pointing to existing couch server user: "" # only change if pointing to existing couch server password: "" # only change if pointing to existing couch server - port: 5678 + port: 5984 storage: 100Mi redis: From ce72624f0f43a1f145a6e17bc748f95bd8e3811d Mon Sep 17 00:00:00 2001 From: Martin McKeaveney Date: Mon, 16 Aug 2021 19:00:52 +0100 Subject: [PATCH 04/22] tidy up --- .../kubernetes/budibase/templates/app-service-deployment.yaml | 1 - 1 file changed, 1 deletion(-) diff --git a/hosting/kubernetes/budibase/templates/app-service-deployment.yaml b/hosting/kubernetes/budibase/templates/app-service-deployment.yaml index 24728c175f..daee03620f 100644 --- a/hosting/kubernetes/budibase/templates/app-service-deployment.yaml +++ b/hosting/kubernetes/budibase/templates/app-service-deployment.yaml @@ -32,7 +32,6 @@ spec: {{ if .Values.services.couchdb.url }} value: {{ .Values.services.couchdb.url }} {{ else }} - # value: http://{{ .Release.Name }}-svc-couchdb.{{ include "budibase.serviceDns" . }}:{{ .Values.services.couchdb.port }} value: http://{{ .Release.Name }}-svc-couchdb:{{ .Values.services.couchdb.port }} {{ end }} - name: COUCH_DB_USER From 1af740fbf74fdad8fee577eb8ccaa270882e2fd4 Mon Sep 17 00:00:00 2001 From: Martin McKeaveney Date: Mon, 16 Aug 2021 21:47:08 +0100 Subject: [PATCH 05/22] Helm chart CI automation --- .github/workflows/release.yml | 7 +++++++ hosting/kubernetes/budibase/values.yaml | 3 --- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 4e76626d2c..b9c04c2cc7 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -50,3 +50,10 @@ jobs: env: DOCKER_USER: ${{ secrets.DOCKER_USERNAME }} DOCKER_PASSWORD: ${{ secrets.DOCKER_API_KEY }} + + - name: Run chart-releaser + uses: helm/chart-releaser-action@v1.1.0 + with: + charts_dir: docs + env: + CR_TOKEN: "${{ secrets.GITHUB_TOKEN }}" diff --git a/hosting/kubernetes/budibase/values.yaml b/hosting/kubernetes/budibase/values.yaml index b3b81bebf4..1f60f58f1d 100644 --- a/hosting/kubernetes/budibase/values.yaml +++ b/hosting/kubernetes/budibase/values.yaml @@ -101,18 +101,15 @@ services: proxy: port: 10000 replicaCount: 1 - storage: 100Mi apps: port: 4002 replicaCount: 1 - storage: 100Mi logLevel: info worker: port: 4001 replicaCount: 1 - storage: 100Mi couchdb: replicaCount: 3 From 8168e06b5b1f083fec3f06f31b420598d858eb97 Mon Sep 17 00:00:00 2001 From: Martin McKeaveney Date: Tue, 17 Aug 2021 11:01:06 +0100 Subject: [PATCH 06/22] remove schema defaults --- .../budibase/charts/couchdb/Chart.yaml.orig | 24 --- .../kubernetes/budibase/values.schema.json | 178 ------------------ 2 files changed, 202 deletions(-) delete mode 100755 hosting/kubernetes/budibase/charts/couchdb/Chart.yaml.orig delete mode 100644 hosting/kubernetes/budibase/values.schema.json diff --git a/hosting/kubernetes/budibase/charts/couchdb/Chart.yaml.orig b/hosting/kubernetes/budibase/charts/couchdb/Chart.yaml.orig deleted file mode 100755 index 23da0fcfff..0000000000 --- a/hosting/kubernetes/budibase/charts/couchdb/Chart.yaml.orig +++ /dev/null @@ -1,24 +0,0 @@ -apiVersion: v1 -name: couchdb -<<<<<<< HEAD -version: 3.3.2 -======= -version: 3.3.0 ->>>>>>> Bump chart version and publish -appVersion: 2.3.1 -description: A database featuring seamless multi-master sync, that scales from - big data to mobile, with an intuitive HTTP/JSON API and designed for - reliability. -keywords: - - couchdb - - database - - nosql -home: https://couchdb.apache.org/ -sources: - - https://github.com/apache/couchdb-docker -maintainers: - - name: kocolosk - email: kocolosk@apache.org - - name: willholley - email: willholley@apache.org -icon: http://couchdb.apache.org/CouchDB-visual-identity/logo/CouchDB-couch-symbol.svg diff --git a/hosting/kubernetes/budibase/values.schema.json b/hosting/kubernetes/budibase/values.schema.json deleted file mode 100644 index 8021a46036..0000000000 --- a/hosting/kubernetes/budibase/values.schema.json +++ /dev/null @@ -1,178 +0,0 @@ -{ - "$schema": "http://json-schema.org/schema#", - "type": "object", - "properties": { - "architecture": { - "type": "string", - "title": "MySQL architecture", - "form": true, - "description": "Allowed values: `standalone` or `replication`", - "enum": ["standalone", "replication"] - }, - "auth": { - "type": "object", - "title": "Authentication configuration", - "form": true, - "required": ["database", "username", "password"], - "properties": { - "rootPassword": { - "type": "string", - "title": "MySQL root password", - "description": "Defaults to a random 10-character alphanumeric string if not set" - }, - "database": { - "type": "string", - "title": "MySQL custom database name" - }, - "username": { - "type": "string", - "title": "MySQL custom username" - }, - "password": { - "type": "string", - "title": "MySQL custom password" - }, - "replicationUser": { - "type": "string", - "title": "MySQL replication username" - }, - "replicationPassword": { - "type": "string", - "title": "MySQL replication password" - } - } - }, - "primary": { - "type": "object", - "title": "Primary database configuration", - "form": true, - "properties": { - "podSecurityContext": { - "type": "object", - "title": "MySQL primary Pod security context", - "properties": { - "enabled": { - "type": "boolean", - "default": false - }, - "fsGroup": { - "type": "integer", - "default": 1001, - "hidden": { - "value": false, - "path": "primary/podSecurityContext/enabled" - } - } - } - }, - "containerSecurityContext": { - "type": "object", - "title": "MySQL primary container security context", - "properties": { - "enabled": { - "type": "boolean", - "default": false - }, - "runAsUser": { - "type": "integer", - "default": 1001, - "hidden": { - "value": false, - "path": "primary/containerSecurityContext/enabled" - } - } - } - }, - "persistence": { - "type": "object", - "title": "Enable persistence using Persistent Volume Claims", - "properties": { - "enabled": { - "type": "boolean", - "default": true, - "title": "If true, use a Persistent Volume Claim, If false, use emptyDir" - }, - "size": { - "type": "string", - "title": "Persistent Volume Size", - "form": true, - "render": "slider", - "sliderMin": 1, - "sliderUnit": "Gi", - "hidden": { - "value": false, - "path": "primary/persistence/enabled" - } - } - } - } - } - }, - "secondary": { - "type": "object", - "title": "Secondary database configuration", - "form": true, - "properties": { - "podSecurityContext": { - "type": "object", - "title": "MySQL secondary Pod security context", - "properties": { - "enabled": { - "type": "boolean", - "default": false - }, - "fsGroup": { - "type": "integer", - "default": 1001, - "hidden": { - "value": false, - "path": "secondary/podSecurityContext/enabled" - } - } - } - }, - "containerSecurityContext": { - "type": "object", - "title": "MySQL secondary container security context", - "properties": { - "enabled": { - "type": "boolean", - "default": false - }, - "runAsUser": { - "type": "integer", - "default": 1001, - "hidden": { - "value": false, - "path": "secondary/containerSecurityContext/enabled" - } - } - } - }, - "persistence": { - "type": "object", - "title": "Enable persistence using Persistent Volume Claims", - "properties": { - "enabled": { - "type": "boolean", - "default": true, - "title": "If true, use a Persistent Volume Claim, If false, use emptyDir" - }, - "size": { - "type": "string", - "title": "Persistent Volume Size", - "form": true, - "render": "slider", - "sliderMin": 1, - "sliderUnit": "Gi", - "hidden": { - "value": false, - "path": "secondary/persistence/enabled" - } - } - } - } - } - } - } -} \ No newline at end of file From a4f43dbac16ba5f2cc3985de6cd63e3c537b73cf Mon Sep 17 00:00:00 2001 From: Martin McKeaveney Date: Tue, 17 Aug 2021 14:42:12 +0100 Subject: [PATCH 07/22] pushing first budibase helm release --- .../kubernetes/budibase/templates/app-service-deployment.yaml | 2 +- .../budibase/templates/worker-service-deployment.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/hosting/kubernetes/budibase/templates/app-service-deployment.yaml b/hosting/kubernetes/budibase/templates/app-service-deployment.yaml index daee03620f..5a4a95ab2a 100644 --- a/hosting/kubernetes/budibase/templates/app-service-deployment.yaml +++ b/hosting/kubernetes/budibase/templates/app-service-deployment.yaml @@ -90,7 +90,7 @@ spec: value: {{ .Values.globals.sentryDSN }} - name: WORKER_URL value: worker-service:{{ .Values.services.worker.port }} - image: budibase/apps + image: budibase/apps:develop imagePullPolicy: Always name: bbapps ports: diff --git a/hosting/kubernetes/budibase/templates/worker-service-deployment.yaml b/hosting/kubernetes/budibase/templates/worker-service-deployment.yaml index af6212751f..946bf17b7e 100644 --- a/hosting/kubernetes/budibase/templates/worker-service-deployment.yaml +++ b/hosting/kubernetes/budibase/templates/worker-service-deployment.yaml @@ -88,7 +88,7 @@ spec: {{ end }} - name: SELF_HOSTED value: {{ .Values.globals.selfHosted | quote }} - image: budibase/worker + image: budibase/worker:develop imagePullPolicy: Always name: bbworker ports: From fae1d0995b1e19a655e3789945489d7c1532da57 Mon Sep 17 00:00:00 2001 From: Martin McKeaveney Date: Tue, 17 Aug 2021 14:50:57 +0100 Subject: [PATCH 08/22] unignoring tarball --- .gitignore | 4 ++-- docs/budibase-0.1.0.tgz | Bin 0 -> 43709 bytes 2 files changed, 2 insertions(+), 2 deletions(-) create mode 100644 docs/budibase-0.1.0.tgz diff --git a/.gitignore b/.gitignore index b2a2021cc9..3a5fc5dc7b 100644 --- a/.gitignore +++ b/.gitignore @@ -55,7 +55,7 @@ typings/ .node_repl_history # Output of 'npm pack' -*.tgz +# *.tgz # Yarn Integrity file .yarn-integrity @@ -91,4 +91,4 @@ hosting/.generated-envoy.dev.yaml # Sublime text *.sublime-project -*.sublime-workspace \ No newline at end of file +*.sublime-workspace diff --git a/docs/budibase-0.1.0.tgz b/docs/budibase-0.1.0.tgz new file mode 100644 index 0000000000000000000000000000000000000000..f2b31d3087edab16a80330817133c3b8b1b4e543 GIT binary patch literal 43709 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

!#TF)Ug%%y+5s_+5K9>)x ztiJ^fi+z%hC=p&6DuPt%LV<#T3e)|M;5;ZW4Z-U{5_L9|-ExAsAPEPYzFeIcNhnQa zSCalkD0bjDZoJ9l$bT}lbq#?>@2?%FU=B^wKh`1p{F<^;oO6xd80pWg1 z2PBYm=N*kON3WSrCe)`HSBvn!eoWcOFz>x$hJsZH=*!&nw1014`@L&I~IB zPpk$A!JmMw?)yc5t{4-A8SVi3GyIyyqXQ4UXKd(p^e9Uf)VRNatDp^roDuXgQ)m15c z^58&#U_BtpXPJQKuoB9w{0x+%5x5(t$^y+|a_B%DPAbq1%fbLf_!|DAk$NH%f^P(w zU_VpbE@lDoz%+C@8Y`u)h(XN>_t`QFI7N@@bjIE{x)v*h#QZuqr%*&)Sw z#SH1J2I=D%?$A?Vx7F9_hY|y{BN)1}9-8a#CVYir&LYjomFGDB!!qbr>PA+d6@PxY zvejFArO(w3R1=fx%0s6iT*nGJA>s5;?w3UFj4Q#*8vCuj0#(=#a|XyOFM^aOxjdUo zA<-@J70~NnCYJEu|MQ6pEHaJ~JR%MGZ#7}5exIjg0#9Ej#(@ABPOdkPae#0M3~28? zxvJpOAY}gaA-Wk;U!Euwvm3+)5}^b^K}O7q4_O$p8#y=g+>pll3Eb@A=u6%BEJl}2 zaPVI5hQKyn=C2^VDA;eddaVN`WK9G|k0&%084eM=8Z*3FE!hDIFdq*XP6Kp&lK?-BCkCi57Yl2(Ieali!iHgycP{n)+;AN&T1x>fGDdZF0uK zRScxLMO1?&F@~i%w(yuH2@#5YU(UE0sFixi#g-tY#MOu>L6Cjts3Eb*JRp>Ur^eJO z8&xkX(WO;&36L(0{cIqqLBsfYxIxShDrPMSko~y85Glp2z)j!A6jXeNNI6!-Jsnod z9?ZjGGRBoyI&xQ4un!AZef=-z;1%rd^p^|t>KMIv^@C#67ZEI=ONtZ_7P6wKEC07>_`27|J(idzvX}W|2CyBYtbR~u^Jfg=TGt?eM|3zuXT}FjE+hJ( zFy%w^5F)Zf@~MoIVHT=4g;M4L_W;mIiq{`;V7(=;`{{=`B>@_flxShXUW1>Wz1aF1 zy!ydIuZ<3yMkYqFA@;C;-iWc0qKLK_r_9;^P#f~@HWHh_W zr@v?EibCufySUOpmLqTHD8Egy9}@Ho(EltRrkIN~^TpSiI`iotH}O-YQUVc`lLwN@ zeG=o8GD-Kxc%lMH@Fb+doltTEwtOWl+<>VWBD!RQkoxF%$u^$7Ieq;CrNn1xz~}Jc zoNGvic{8S3jZ0EGk!!T9pL`ANemoTg?FLU3&jy_ucqlMvEh7}GtHlM+ao#3MKnkZh zvzI)#j(LOu&-5d0U4S|yck5u(G>YIPhdGbVsxYFGe!7I?9xRT{G_6cf2 z=p*mRlVuA;)!KMZ)N5%t4KO+D1hl+X_iu-?UJtHcF989-RJwsvYxk(#(q6J~iSZ&$ zS&Dv zp4=*`Ca}WH`X+1_M>L8uxT{r!=s8>$Ku8I812ec8PhkOQymVJ=GdFBRs{>&kr0fWm zA`rgS0+#Um_V)HR$7~i+@yq~knWZ(AgUVNMn_I+sK`GHUK6xxMSYOX^PQf5+w&dk#iiR4IS!p#)%8=p0@c=iI`t|IsW6y z+V~%{fFvvxYfylt?>~CID*hk0yF2{fMtKBd+uY@U*iPAs{C}?-nfu*4eeL!A z*2^0Bzk%eh$};?Kb-Pvm-|cmF_`i+v2jYJ&{(tQ`?#leK2L2~3un6=6>i^w#RsQex zj(7OKmGV&duYJ!eUo~%EA{>-sf2vwVa4^Z}6D-Dk)G(PKYAQe~*u7S>ebn8n`Of_@ zDJ7+?eoiqsHT=g34Qt@P@Dn!+5?7gWU*jZCZMlRNEVutv?f<>wo&A41WlQpZZtR;q zJb7AH$1?gYsmF4(Pu*QFrnC`*(f2&(_|F^UMZ>4Mn|G)MmcV%u_1OK)4 zGlUW)(6Gx&eh+2Q|IN*VuGe5Jdi1Am#aKK`%&3}6}lcV_PYk6P_r{2y2m^Izm>8X{_ni~{wif1{NFSH$Wr`o&-nlL zdOQ2yR?5TM|GxH&c4eKi4*qW%0Av~dcbhZz|K{-y|F=@M&Hs1NKEAE87XEMe0AMNq zpRxb9d!60;|E-iYZ2LPm-oId32mdz>0JPNpH}n4YxYgeI|8AxHA@;vrw4Xms*#!R| z{Qj>!d;j0s$^Tm^e*pgPF!oPW*2VunME;+N|JClbcJ}|Rly&6)T@;_cURekKH+=xS z6#r-P{~R@U`u}#ydi;O)+V`(kHo^Y~zyF`T|LgX4_`j9%@c93=XS6Hplq&xFEc3^~ zpz%!pxn`W7d1VRyx0;=b{@*(4b$9Xqw^DFI|D|#V_tB)~;3Uca?07A&=>&xPDFwgY zJ~~AK7I*+@+#!cHjYo(R9EF5)6lI~HZiFQvKzthehbr?o_i;!}c5XU?K9FE2VgnkI zLzOKX$B4!vqk>KddinnSdE#Z@{iEY<QS3osmnVl1a( zz7@4^Xc&$qk)GDoFTOhJ2LlafS?UuQwkDgk>dCB!8!-Qxq|T(+{f^h&5%a%#nMeOO z3iLLoET;dhZl_&&|J~}fcJbf0Q}*{w{`U*|J27eJX-oyhA>#5kNF`5@CWNNC=UDCf zsN*>M`{-6kOZ>%Ki-j$U{2oPi2T#lID(|?jA<-+;1vw{fs<5{;HyLZ z!#U<$GKnk6!(>NSK4aIEoMvLI2Dw7lWXe5sK?p!}<);J-0t;wr3`0EfWb2d+3r<&d zjt`hGkvH~90$3am=_pIV$ELxdpN3P}nml>0+PIm4?X9b<$(#j|>nM905Zzl~uMy4! zb4Mg5srr%TF&|$Yf~+ySNwPnLkdI?Iw}1?(6h#7>5?_Qu>(v-xqGcH(40@-I0O4)(j3i$^{&&(~U z8Bb8Dx9PrOb9L(J`M2f#DTmXOLr%Hg&)C{ikiTSyDfumf6Mf_+gX>_}cEyTH-M@@UY>x7w!ggiWKp4eD+_3aI0ZUcBTkMAC1TSF$M}o9)d9iu^1&R73k>cN!QT>&NGe4VLoso-bzG4V1CUTh9r{I)j;M%g18{KCn0h3ad-nxQ8BPY z^uF9yfX!F{3`utM->2%&*;$zbr2pJkqN}ryXoN@PJPSj$YZ7~rs|DL=3SW~f3>683 z#gRxYr;kSjO<0&kL~?-u4W?>0&RJj(Cz76uR4KAzGeZ@b{`kLBRKdarGmVTeX#rhvu`zjqVf-Gj(xPZmv4{3 z9N0bw{wq67Ny0c4ES(lQ3PJ*pjIN20+-r>)=xq3giSv|j604nmahs5oMkE$EgcoKb z#o!bwzRG+O6E|TuM3S&!7KT7OIl-wvcGUmQ>R>SnSFQ``CK|S;*Io|Wk0DKvq*Zc@ zf#eE?XzYhspmyd0`ZWBIr1FfK^XjOrP*n^akTO^;xPy#O2kh2>7h*xUsK9p`-ry+* z$nwpQEHg=_z*-X;OS#KPW3k+GU^zII3_d(WgG?YgL{pX_5(8E%R+FS`5R!<)7s(<- zI+d&%PJY6I*Eq%_A~)$=lT=p4Mf;Rqy-fqTU-F<$ubi3=`siE(rOp>a10n>72qB0P z@bmx;F%7en@I!P%0Inn@6Nx-O=OnS9W?+i|xsY=@0*s4T3d}l2EC&3SIB3?+20kIF z8ZqN~%BQiQr^BJ#u&L)@{jWayeCHsvU#-!HA5qS1pMw?ULV-r((b*_w`OhzIiJyV( zMYobj8R7v6FGxsy!P4S`ED;v|_{D7^*^Y6qF22gf*JRp9dpJq<6Ka{Ww%EWT~>=d8lap zyN@nELdDb4hUtao2qP)HfT`+2^Dn^=;Q|jr%Ey{+OH4FUf$U71gDBxf6Ozn9Ns1sY zn7$}4q@+x+2n#Bu<*DRMk}`k*A?3f4x`Ts&>S>jvb`L12>3a||{~8TQ$Zik_$k3Z$ zz>0kq2gQseNFz2OY0S}>-5@`tBo}e4J6|ETjH@^=)pIna{fXkz4f?_Z(dF zLC}Gnh|!rAf>NUFQ5refga!l{y(@*k08MBrG8`g#K#TzO{?!G{lcW)i6|Kh+ll&1x zE-|L@h=b&1rc+#3Np*tyW00;DXH&Wym?2pvIMtNdA`tmvtfOq{h2yS1G&CNWYqW-= z|53|tU~d;(=r#X}Wp};gnxX%X2PU?V~bWdN9vd92xHLYAcG6yV#G2 z712$E8kBvl7H|}0Tu7}U!f^%%*&J^zURf%KYUm;*r@c{TNP9-E2)V9Rnz*59b<;25 ze13c+IukB99FUMkRHzeBI@E^CmNv3}A2Uu;ca+h9G<+5%nINtii#MG7pAuY6&V3Pur56i!o) z5-bm~!QdBA75(cq!b;2Ink9)bXl*avz54XytJ9w@5Q!%qx&r-IDY%>nr7vCSh7w7n zf)K$z=X#*hdI}1uX$YNAF_moihLE@{M0nK|R}%s|d%*lggk$-WHx^O2FZtmo0Kl>4 z-OnxC1!u_t?xTpsayUonV^Be9#!48LlrNR2ZGsqCXQH64Mq*L$N=hq_3{VXq@|45E z%VkN)kfjk8>fi&(^=M4dkcI?>m0n;XdHaPK>!3$ zGCPg&s*a~m*{2B(Y4Ay+VWAh{FoZLXV-`;%mhl4Z_32Mcths&QHt0s`_>{|yg(_*L z$y#N7*_Yo*r zB&Fa#q<(72-*^V>+v=hv85WHQ%S2Jt$-(^=fN|^%#OZ*F6iZEs2E@l{!J*3H3)q?r zn+3U%IQzCV>*x-6u3i{x$&fLPu1euXds)YQBfSUWVhKk1ih(f6UO{kg>hLKl-RYEA!23sa$ef!BG=3RK?YH274&k$tOv|F)fS3SI!V zkq9p3Xgu=JvoZ0njaDqOlK!hi&A#IxP%V5Bq7e~_ez|V08KBRfk*Dv9y!>?+y1PSe z2v&&iKYvE%tf4(47J-2PdFbx$FjbAeuXcp)?)Dt#ZR`_syOM(@rM6)v1zE5v9U=l& z6s7GASs1b#$)YuJl002eT!*vBx&+aegN6+8(qv_qt4;LtXGDi!VfOx|E{3XCq<42N z6|x?Q19W#+yCQNv#j3bX;{!7c7t@g3k^qO9)FXg1hCoO1v z;zVSLuIm09oxir97?(Q5}%Xx8|D4VCU{1}o82gh@CJy+k3S82IhdWn{mf=vUzJa7-0J-P2Md;^An0Z`7BMv+ z^VKkQl9a|`i1z-AyZ^=a=GV8Eccqn-1lauX9Za|X{py=F54;k?R^6_%ud{o6rYw)@ zW(Uk-H^$cl4VW0)HvasM8hjl@igQCdi^-uwVxFbiUM~9#G2AMRh~NmLOc_qpahbb` zeB$405BbFZ*7q?7@xk{p$JZ3c0gDh9;5b1QS>c(KIg~F73K-Fs^tY6Y`TL(_~6&{eVupDVsvoHH=HDWv_yb*v;f_okx)@B%s*-K76;#|D3avVkh_p_A<2GzU2J*u zEdZO4K(^eo4kio8GpWPN(}^M)qoe#l+U|&75RHWr4E2_b2>K4}qfOZ~ivBqyE? zQxNCNVH*pTjv$o^l26iFgS)%KT$j)bw}7AyQQ3D#Ob&2^9M1va9mQf$2peNH9%i-QXf!X9Hjuq!*U`baDW9a?&MamPB=)psOT%+mADj`FXR#Iqdm!} zEJ;NXS^bL!7OX#D(VD7frn{kFPtXdB+pA}ja&2(u@r8?9{UvgeizHXF)74=0v zojvqe{jCuGvGM+@5CZSupvZh(?j%~B3ZJ-y!V>E{)?mUk(3%`EA&{m`hoON;w_Fr)2 ztHg%NK3r;ss5~G{CG>s$2`gj|IAvcO+rGWBoc`s~Q1mMroM-!t z7L5`t#&&H368vu4UTo4;xL$hKC*biiX$^?LhN5XF)c-_^R+DJQk`^mcU3Jl|re;7T z3QM`}?hXdcrMr@>ZQaEgnwn-InT24?xTqT%oJ8|WLGT$>!$E@_7`Wxk(+9~zVr3JO z4V4S6YzQYPZ>Y)u``086`lTQ92q$mM5hahJ#&M4>vc;)S#UR^^CzA zSDrEUKR60&>y>%Jd^^u#{9jK0OAq8W;sVR{|0?}&9kp6J`oE1*wZN5LC+2M6?oQLu zH}>bH6!<@p+!|sBQP7GfI{InOD?PDwWWJsn+j>a=WX(>m3_s*q+TTArw+K{8>fE5^ z(^j*&a^-HP_f-ElOj%SKrMe)dY`icnP5BgQdOuBycZ-JKTU_XCW7@Y_meYTAnf6D@ z|LyLK{kMIz%m28QQX&7dK_IGwC3sgk{3MS5MjZ9QEq$ zZGwNxNNy&sggL04MdeyLcbT)BYEiu$8e#3;OjFg{Y)pi8I;^%v#eH3Q(3hBxktW4@ ze2>Odjh2MyvG*el3HN>?0`1u~_73dZ_ysp|UmHvQx@8smue9|)j{difYyAIq^8Z#! z{RMo<{__wf;@nZ{&oAK5S+N;QNy+LmtIF54E35Q17L<~SIWgvjg?6>vSWzdq3!ILK zIJY$f#pG&wl^;}Z_b^@Wi265HR;K?t($6MrVA=iOaW(#5yVYy${6Dr*7CT^R&)OwS zK-XTG7gYSf{;Dj&EMhgpm25E^_G&JNSk#ThA~v*itbqT8YwCtF;9~r59rfDP{BNz+ z(Jub;R*H?+676`Sz;5f+@qe%(R}MgZ%u7O%?WC+_qVk2&3Tsmv>&hCVE|^wD{(1Y1s-Bseu)uh^g;$QPD(yphlC}!}-;9`cp^WJN)y&o_q z=U9xoTptBapbPgZg zG3Mhx^@iFeMbv-*Dqhe)t8GtpHB4ufEB@+M*<)6{0kVOrShjW6jtRiJ3pTktvYuO& zSRpH%$SlH^ozUj(?vkC|WLFpNXyq1iF~FrMo|i8)tF1{ARX4BF)VicJO)E7@cb*85 zc+xkBwfG@_Ekw`Wet7os`468yT)cQ!fec({ek|RTnxf3{Ka=S@GMxRoo)}gw5CpPx z{jd=5^V5rqU*5iZ{;dIjUWcny;4fZ0d-vk~Hw1e1US$@W%M;1txYf-;VX<-MIz-=1 zY+ounn%vzTg2!stIw-|=6a5c0^j?*7lp;IL${w;*fu+2FVv+=7edRyMGYSn62wL!1u_EuB7+F>PF*4(a2 z^{SoBM%S9#Pqk{bt7=HDxgC`&)|geSGhGfbR*x0cFl0Nl_^N18*|PiMMWwrYAx5RI z%`v0W(LGS3($hlRC_1#l#?9I>^N*J2R=eDOHQi~c7G#HlBf?)(D_TP;Uo_}k867Q- zZMVF4*{C`bJ92!Tisy$c;{<1mrGG0p{(K$4uC$&eV7Hv74=g(Zo2-})es?vw2(!_EFi|wv6!O$YaNo5TFtdg%^20J=;y^)c1ZNqEMBw5IvuRuvef0_ z>sPj+nN6+LN$22}$+V%vTh_HInFaJiiDyEfNlV#5O!RYngGDkgJ7DI@jo-)S&Wm*wMXLS=~xdg&F<1nw6wZo>~F_OA{`6T!nP03|%?tv#sn*I@MN>$!`#CXri^d|YBzWOA6qGTejvx0 z4F!wuk?z-duynspDc!FF@z_-!6_xCRL)86JsC9(pkLC1VLdS`N>UrjpJb6ouhAE4M ziE8*f&m#iyc|wK*G{7N_eYpmMsR^{B5+(VNt&KBhK=M2TCgvC9nN#^n@*FMGgmA=C zxe6vd%CkHXJ7j)wOdy|}wD<%?#2@3B@(AKH(I^R3*ugw0nGRwvr3@9l7Ql0olm(dz z>tW|d;bWGC0R(=$n#YQ-{ni2RH76SWYr<{KcV)9pWuN4dEsk}|E{{H-(<6K=` z4KN=&-=Q%HBgkuoa1i9Nq)hmFo#$BRE2b18w18Ujy+p=oUDK!8}+mGi1t=)ac} zu|CfayY&5ckHIiv;JLZEf!}rXf0lYN5e=NshR&GWMeTn!0y1gDSr|e%Jmdz*J&-eo z1vpO$Sa~=VggcIF$Cqq*PyeRy;s+wI#DYBarsG-}baW!4Qp;BNZz_eST8upTfW#2T zdC&4y%7b;<2_HcQQSmLWqN%HYxavF3X%Lh`)V(zOrSp(I)Lz2+oDKy^Zy>A0EV#W|f@TRazzc?!45yrPaunLl34`1dyKkwJYOFKIev1Nc#JI36SrHjMY{!fPFu} zX|+NX%I^pG_tFyH;8^JNxGH0Ck-oNAG)b!cvD|sNP9~2e$D!bP-X15Q!y!rK2Nlp! z_8t?(w{Q?Ztke)Pl)*sPWQwlrEA1b-P1>uS|4S*fDrhu?dRXzG?+ z1=O{N-=lx%bhTs5$Ny9v>tM-o9RVKGkeF0O z2G8qKKkFb{_aI%>KEH?DH(McYz%j~_nqej^o|Ae_r8+hVB5N|@U6y|P_;|nc_W?5T z%R{mOj}p5 z7jh`Y<3Gf79v9Bcr$#_RqLUHWEZ35lX180nDXN=gCV3izltgS|rB1TfmPHAR;Y4H3 znNK0TOfCheEy8i&sA0G*8z|}RLo!NnpwjZGAiVP5+JvF<5XR&NMfg{iA}tX)&Z+)E z4WNIPf5jt$9*6Xrph?qfdmZHd6HQuP(`z0e8V8VIndhOIQ;0{3V~56aCRorxNYEf9 z_!_WP^(QG8B=Ur8bx0);-~hgXA0X|>VI1dzN{QOQ1V83!OiPh@%jvN|{6LT%EDod` zIF;O&K$0+0qM&)6D6HDZJ%XUdQ$&SQP*mU)efq?hIGF}>4zq@%VCwwC5Kzc{pbXlMCrQbKvW$mQ zi0Pv890942Dk3Cb-`Qk1s%WK@T-52Kzy0VDr50+=g7F2t#)(p6AuqUz)J{SYnRJDkM1f4Bj)`pd zljc8@(z*>n} z@n=2|MjMDL11lOLUNY|wd=Oyjg|W1`ozvD{n#X>#j!!WgQDG4OvAcU zRtJRTwpD*HA-&{K=kClS0+UpU zdF4nqW1?nnY(xm7^MB=rA&E2QIMj~8OGsGWYfv%h@A4{s;u>Lb*(G& zn&th1PJ=*lXOpT=YTK3M@>XJbtDQRRD{Qf2n3&PwG{e^OtI`C>MC$u2RjLB)vUACX zL+aCN+USp$-H%F8E*J|Rvm4D)aPo*(xB>`%c~o+isLN!fVE&;TV?ifhiG6I=iA}~q zrm8(uAOkK-zKR}SzB4-kijWC*e0lWoV0Mk>E+#;vJj?7PtXCz7Lck#|buptNw2n&6ZYF_nm?h_@wLLEi9? z7nUjsnujD;6#==>5 zyq4Eo)?cZ|!ZWKLMmt^XvL5Rl%Blk0HS<}>_;tl!YiW=3=KcjW*O+y^SK6%5x%#Z@ zo=DBg(mE~gN-&mQRL#X1l|Fp6S=YU_nuYN+yunkxYLBIu!E<(IWwLDb2q9qS&Ki_m zm)zng35m*^y|hQ`z3EK9>ro0jEb%-<{DK-A^eDo4b6yw5MSek7d5`Z14t`0gAdpOT z%^qz>ms~+GtjgR|{hSSmts$3soPFh7von%Wh9Rll;A)S?Q*n{Dwca`|IyIa1Xt`)U zP>;E@)`RiFng~Vr#FfN%r7yd#K$6&Ft76uz)w-uw+4xNk>^b-{?vq8QX0RzW= z)j7iPRE%MW7(HkHHAx@I)fjLp^nLOh7Q2->K359@E|>2Kj*10lDfr7>{&$Q8PVujJ zia$O!S9JPr&Sz1h_ITrpnxvj0Mzv+qzj+?^JzL6|c#J#y26( zYOCg7^}doRiGQy@0Q#G%cuwPl#+{Z|Th)9$aGcB5$G&+RE0Np9sXwL@!aaSb%+gUK zV!@|qz;1KuAsaN5=XS#;Dqa*gmtCuifcg20 zWf+#kjr@u|9@0?*^3Qr>5rxj>(UO+RPSM~*WJ$vayt3(jl9B|cG#(juFc)rFUBTP| zaerkrCV@+1!CdT`gu`Fkb^b3==Dq*$X~WzJxt3*nqcN7n|7&BT>P_^1^BPE<9h^t*JT<0ciOGW`>$TFxy%2tm10aF65q{J z+aY6s{U%U=zOdLUezf2D+ZQBIW9HfD?2}PH$@*xo>9vXo0uhN=I_;xY^R;?LZH0VO z?$T0|{=3vPh(Bg%Z^+o*-w_X`vT`JUkICO3ecVx^^~yZ_w_V=vJH|5nZyq04@xR^c x?(lybC4a_Xy=JcFrBTVXYeNxyZSZ$xS9WDrc4foz{|5j7|Nq}cWibGp1OSTN)TaOd literal 0 HcmV?d00001 From 744f8efe8a4a9e91d05548983e934354ab27f683 Mon Sep 17 00:00:00 2001 From: Martin McKeaveney Date: Tue, 17 Aug 2021 17:10:18 +0100 Subject: [PATCH 09/22] updating domain values --- hosting/kubernetes/budibase/values.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hosting/kubernetes/budibase/values.yaml b/hosting/kubernetes/budibase/values.yaml index 1f60f58f1d..0c4a466c9f 100644 --- a/hosting/kubernetes/budibase/values.yaml +++ b/hosting/kubernetes/budibase/values.yaml @@ -46,7 +46,7 @@ ingress: annotations: kubernetes.io/ingress.class: nginx hosts: - - host: budibase.cluster + - host: example.cluster # change to be your domain paths: - path: / pathType: Prefix From 8a90d47e6b93d6a05b8da814c807cd59af6fa1c6 Mon Sep 17 00:00:00 2001 From: Martin McKeaveney Date: Tue, 17 Aug 2021 17:40:25 +0100 Subject: [PATCH 10/22] adding persistent volumes to couch --- hosting/kubernetes/budibase/charts/couchdb/values.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hosting/kubernetes/budibase/charts/couchdb/values.yaml b/hosting/kubernetes/budibase/charts/couchdb/values.yaml index 5a5025f816..f4cbcc9dd1 100755 --- a/hosting/kubernetes/budibase/charts/couchdb/values.yaml +++ b/hosting/kubernetes/budibase/charts/couchdb/values.yaml @@ -51,7 +51,7 @@ serviceAccount: ## provisioning of Persistent Volumes; leaving it unset will invoke the default ## provisioner. persistentVolume: - enabled: false + enabled: true accessModes: - ReadWriteOnce size: 10Gi From 9ccdbd8556716f1d4c710f0dbd4f6f6076a3e39a Mon Sep 17 00:00:00 2001 From: Martin McKeaveney Date: Tue, 17 Aug 2021 17:41:16 +0100 Subject: [PATCH 11/22] new version of BB helm chart --- docs/budibase-0.1.0.tgz | Bin 43709 -> 43415 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/docs/budibase-0.1.0.tgz b/docs/budibase-0.1.0.tgz index f2b31d3087edab16a80330817133c3b8b1b4e543..242aa79d909be283f88f922dae7013557e214dfb 100644 GIT binary patch literal 43415 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 y0v$?*;p_Jg;QJ?Mw}t<-aPrJ|Dc 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

!#TF)Ug%%y+5s_+5K9>)x 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%0Av~dcbhZz|K{-y|F=@M&Hs1NKEAE87XEMe0AMNq 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 From f992be2b2f005f27e013f8cf9cb49c4a099399af Mon Sep 17 00:00:00 2001 From: Martin McKeaveney Date: Tue, 17 Aug 2021 18:03:23 +0100 Subject: [PATCH 12/22] remove custom domain by default --- .../budibase/templates/worker-service-deployment.yaml | 5 ----- hosting/kubernetes/budibase/values.yaml | 2 +- 2 files changed, 1 insertion(+), 6 deletions(-) diff --git a/hosting/kubernetes/budibase/templates/worker-service-deployment.yaml b/hosting/kubernetes/budibase/templates/worker-service-deployment.yaml index 946bf17b7e..e69d6f6b7a 100644 --- a/hosting/kubernetes/budibase/templates/worker-service-deployment.yaml +++ b/hosting/kubernetes/budibase/templates/worker-service-deployment.yaml @@ -29,11 +29,6 @@ spec: - env: - name: CLUSTER_PORT value: {{ .Values.services.worker.port | quote }} - - name: COUCH_DB_USERNAME - valueFrom: - secretKeyRef: - name: {{ template "couchdb.fullname" . }} - key: adminUsername - name: COUCH_DB_USER valueFrom: secretKeyRef: diff --git a/hosting/kubernetes/budibase/values.yaml b/hosting/kubernetes/budibase/values.yaml index 0c4a466c9f..7b2ac7cb08 100644 --- a/hosting/kubernetes/budibase/values.yaml +++ b/hosting/kubernetes/budibase/values.yaml @@ -46,7 +46,7 @@ ingress: annotations: kubernetes.io/ingress.class: nginx hosts: - - host: example.cluster # change to be your domain + - host: # change if using custom domain paths: - path: / pathType: Prefix From 2e30626e896d2aa21bd15d43a00ad0345015ea64 Mon Sep 17 00:00:00 2001 From: Martin McKeaveney Date: Tue, 17 Aug 2021 21:27:30 +0100 Subject: [PATCH 13/22] merge with dev --- packages/server/src/db/client.js | 4 ---- 1 file changed, 4 deletions(-) diff --git a/packages/server/src/db/client.js b/packages/server/src/db/client.js index d06f4651ed..dde857c462 100644 --- a/packages/server/src/db/client.js +++ b/packages/server/src/db/client.js @@ -12,10 +12,6 @@ PouchDB.adapter("writableStream", replicationStream.adapters.writableStream) let POUCH_DB_DEFAULTS = { prefix: COUCH_DB_URL, - auth: { - username: env.COUCH_DB_USER, - password: env.COUCH_DB_PASSWORD, - } } if (env.COUCH_DB_USERNAME && env.COUCH_DB_PASSWORD) { From a81079cafa0d8a141b7602607ecfec4a2464b932 Mon Sep 17 00:00:00 2001 From: Martin McKeaveney Date: Tue, 17 Aug 2021 22:08:00 +0100 Subject: [PATCH 14/22] remove direct config for ingress nginx controller --- .../budibase/charts/couchdb/values.yaml | 2 +- .../budibase/charts/ingress-nginx-3.35.0.tgz | Bin 0 -> 25050 bytes .../budibase/charts/ingress-nginx/.helmignore | 22 - .../charts/ingress-nginx/CHANGELOG.md | 250 ------ .../budibase/charts/ingress-nginx/Chart.yaml | 19 - .../budibase/charts/ingress-nginx/OWNERS | 10 - .../budibase/charts/ingress-nginx/README.md | 226 ----- .../ci/daemonset-customconfig-values.yaml | 9 - .../ci/daemonset-customnodeport-values.yaml | 18 - .../ci/daemonset-headers-values.yaml | 10 - .../ci/daemonset-internal-lb-values.yaml | 10 - .../ci/daemonset-nodeport-values.yaml | 6 - .../ci/daemonset-podannotations-values.yaml | 13 - ...set-tcp-udp-configMapNamespace-values.yaml | 16 - .../ci/daemonset-tcp-udp-values.yaml | 12 - .../ci/daemonset-tcp-values.yaml | 10 - .../ci/deamonset-default-values.yaml | 6 - .../ci/deamonset-metrics-values.yaml | 8 - .../ci/deamonset-psp-values.yaml | 9 - .../ci/deamonset-webhook-and-psp-values.yaml | 9 - .../ci/deamonset-webhook-values.yaml | 6 - .../ci/deployment-autoscaling-values.yaml | 7 - .../ci/deployment-customconfig-values.yaml | 7 - .../ci/deployment-customnodeport-values.yaml | 16 - .../ci/deployment-default-values.yaml | 4 - .../ci/deployment-headers-values.yaml | 9 - .../ci/deployment-internal-lb-values.yaml | 9 - .../ci/deployment-metrics-values.yaml | 7 - .../ci/deployment-nodeport-values.yaml | 5 - .../ci/deployment-podannotations-values.yaml | 12 - .../ci/deployment-psp-values.yaml | 6 - ...ent-tcp-udp-configMapNamespace-values.yaml | 15 - .../ci/deployment-tcp-udp-values.yaml | 11 - .../ci/deployment-tcp-values.yaml | 7 - .../ci/deployment-webhook-and-psp-values.yaml | 8 - .../ci/deployment-webhook-values.yaml | 5 - .../charts/ingress-nginx/templates/NOTES.txt | 71 -- .../ingress-nginx/templates/_helpers.tpl | 134 --- .../job-patch/clusterrole.yaml | 31 - .../job-patch/clusterrolebinding.yaml | 20 - .../job-patch/job-createSecret.yaml | 61 -- .../job-patch/job-patchWebhook.yaml | 63 -- .../admission-webhooks/job-patch/psp.yaml | 36 - .../admission-webhooks/job-patch/role.yaml | 21 - .../job-patch/rolebinding.yaml | 21 - .../job-patch/serviceaccount.yaml | 13 - .../validating-webhook.yaml | 46 - .../ingress-nginx/templates/clusterrole.yaml | 75 -- .../templates/clusterrolebinding.yaml | 16 - .../controller-configmap-addheaders.yaml | 11 - .../controller-configmap-proxyheaders.yaml | 16 - .../templates/controller-configmap-tcp.yaml | 14 - .../templates/controller-configmap-udp.yaml | 14 - .../templates/controller-configmap.yaml | 25 - .../templates/controller-daemonset.yaml | 244 ------ .../templates/controller-deployment.yaml | 245 ------ .../templates/controller-hpa.yaml | 45 - .../templates/controller-ingressclass.yaml | 23 - .../templates/controller-keda.yaml | 39 - .../controller-poddisruptionbudget.yaml | 16 - .../templates/controller-prometheusrules.yaml | 21 - .../templates/controller-psp.yaml | 86 -- .../templates/controller-role.yaml | 92 -- .../templates/controller-rolebinding.yaml | 18 - .../controller-service-internal.yaml | 51 -- .../templates/controller-service-metrics.yaml | 44 - .../templates/controller-service-webhook.yaml | 34 - .../templates/controller-service.yaml | 85 -- .../templates/controller-serviceaccount.yaml | 11 - .../templates/controller-servicemonitor.yaml | 45 - .../templates/default-backend-deployment.yaml | 112 --- .../templates/default-backend-hpa.yaml | 30 - .../default-backend-poddisruptionbudget.yaml | 16 - .../templates/default-backend-psp.yaml | 33 - .../templates/default-backend-role.yaml | 19 - .../default-backend-rolebinding.yaml | 18 - .../templates/default-backend-service.yaml | 35 - .../default-backend-serviceaccount.yaml | 11 - .../templates/dh-param-secret.yaml | 10 - .../budibase/charts/ingress-nginx/values.yaml | 808 ------------------ hosting/kubernetes/budibase/values.yaml | 2 +- 81 files changed, 2 insertions(+), 3678 deletions(-) create mode 100644 hosting/kubernetes/budibase/charts/ingress-nginx-3.35.0.tgz delete mode 100644 hosting/kubernetes/budibase/charts/ingress-nginx/.helmignore delete mode 100644 hosting/kubernetes/budibase/charts/ingress-nginx/CHANGELOG.md delete mode 100644 hosting/kubernetes/budibase/charts/ingress-nginx/Chart.yaml delete mode 100644 hosting/kubernetes/budibase/charts/ingress-nginx/OWNERS delete mode 100644 hosting/kubernetes/budibase/charts/ingress-nginx/README.md delete mode 100644 hosting/kubernetes/budibase/charts/ingress-nginx/ci/daemonset-customconfig-values.yaml delete mode 100644 hosting/kubernetes/budibase/charts/ingress-nginx/ci/daemonset-customnodeport-values.yaml delete mode 100644 hosting/kubernetes/budibase/charts/ingress-nginx/ci/daemonset-headers-values.yaml delete mode 100644 hosting/kubernetes/budibase/charts/ingress-nginx/ci/daemonset-internal-lb-values.yaml delete mode 100644 hosting/kubernetes/budibase/charts/ingress-nginx/ci/daemonset-nodeport-values.yaml delete mode 100644 hosting/kubernetes/budibase/charts/ingress-nginx/ci/daemonset-podannotations-values.yaml delete mode 100644 hosting/kubernetes/budibase/charts/ingress-nginx/ci/daemonset-tcp-udp-configMapNamespace-values.yaml delete mode 100644 hosting/kubernetes/budibase/charts/ingress-nginx/ci/daemonset-tcp-udp-values.yaml delete mode 100644 hosting/kubernetes/budibase/charts/ingress-nginx/ci/daemonset-tcp-values.yaml delete mode 100644 hosting/kubernetes/budibase/charts/ingress-nginx/ci/deamonset-default-values.yaml delete mode 100644 hosting/kubernetes/budibase/charts/ingress-nginx/ci/deamonset-metrics-values.yaml delete mode 100644 hosting/kubernetes/budibase/charts/ingress-nginx/ci/deamonset-psp-values.yaml delete mode 100644 hosting/kubernetes/budibase/charts/ingress-nginx/ci/deamonset-webhook-and-psp-values.yaml delete mode 100644 hosting/kubernetes/budibase/charts/ingress-nginx/ci/deamonset-webhook-values.yaml delete mode 100644 hosting/kubernetes/budibase/charts/ingress-nginx/ci/deployment-autoscaling-values.yaml delete mode 100644 hosting/kubernetes/budibase/charts/ingress-nginx/ci/deployment-customconfig-values.yaml delete mode 100644 hosting/kubernetes/budibase/charts/ingress-nginx/ci/deployment-customnodeport-values.yaml delete mode 100644 hosting/kubernetes/budibase/charts/ingress-nginx/ci/deployment-default-values.yaml delete mode 100644 hosting/kubernetes/budibase/charts/ingress-nginx/ci/deployment-headers-values.yaml delete mode 100644 hosting/kubernetes/budibase/charts/ingress-nginx/ci/deployment-internal-lb-values.yaml delete mode 100644 hosting/kubernetes/budibase/charts/ingress-nginx/ci/deployment-metrics-values.yaml delete mode 100644 hosting/kubernetes/budibase/charts/ingress-nginx/ci/deployment-nodeport-values.yaml delete mode 100644 hosting/kubernetes/budibase/charts/ingress-nginx/ci/deployment-podannotations-values.yaml delete mode 100644 hosting/kubernetes/budibase/charts/ingress-nginx/ci/deployment-psp-values.yaml delete mode 100644 hosting/kubernetes/budibase/charts/ingress-nginx/ci/deployment-tcp-udp-configMapNamespace-values.yaml delete mode 100644 hosting/kubernetes/budibase/charts/ingress-nginx/ci/deployment-tcp-udp-values.yaml delete mode 100644 hosting/kubernetes/budibase/charts/ingress-nginx/ci/deployment-tcp-values.yaml delete mode 100644 hosting/kubernetes/budibase/charts/ingress-nginx/ci/deployment-webhook-and-psp-values.yaml delete mode 100644 hosting/kubernetes/budibase/charts/ingress-nginx/ci/deployment-webhook-values.yaml delete mode 100644 hosting/kubernetes/budibase/charts/ingress-nginx/templates/NOTES.txt delete mode 100644 hosting/kubernetes/budibase/charts/ingress-nginx/templates/_helpers.tpl delete mode 100644 hosting/kubernetes/budibase/charts/ingress-nginx/templates/admission-webhooks/job-patch/clusterrole.yaml delete mode 100644 hosting/kubernetes/budibase/charts/ingress-nginx/templates/admission-webhooks/job-patch/clusterrolebinding.yaml delete mode 100644 hosting/kubernetes/budibase/charts/ingress-nginx/templates/admission-webhooks/job-patch/job-createSecret.yaml delete mode 100644 hosting/kubernetes/budibase/charts/ingress-nginx/templates/admission-webhooks/job-patch/job-patchWebhook.yaml delete mode 100644 hosting/kubernetes/budibase/charts/ingress-nginx/templates/admission-webhooks/job-patch/psp.yaml delete mode 100644 hosting/kubernetes/budibase/charts/ingress-nginx/templates/admission-webhooks/job-patch/role.yaml delete mode 100644 hosting/kubernetes/budibase/charts/ingress-nginx/templates/admission-webhooks/job-patch/rolebinding.yaml delete mode 100644 hosting/kubernetes/budibase/charts/ingress-nginx/templates/admission-webhooks/job-patch/serviceaccount.yaml delete mode 100644 hosting/kubernetes/budibase/charts/ingress-nginx/templates/admission-webhooks/validating-webhook.yaml delete mode 100644 hosting/kubernetes/budibase/charts/ingress-nginx/templates/clusterrole.yaml delete mode 100644 hosting/kubernetes/budibase/charts/ingress-nginx/templates/clusterrolebinding.yaml delete mode 100644 hosting/kubernetes/budibase/charts/ingress-nginx/templates/controller-configmap-addheaders.yaml delete mode 100644 hosting/kubernetes/budibase/charts/ingress-nginx/templates/controller-configmap-proxyheaders.yaml delete mode 100644 hosting/kubernetes/budibase/charts/ingress-nginx/templates/controller-configmap-tcp.yaml delete mode 100644 hosting/kubernetes/budibase/charts/ingress-nginx/templates/controller-configmap-udp.yaml delete mode 100644 hosting/kubernetes/budibase/charts/ingress-nginx/templates/controller-configmap.yaml delete mode 100644 hosting/kubernetes/budibase/charts/ingress-nginx/templates/controller-daemonset.yaml delete mode 100644 hosting/kubernetes/budibase/charts/ingress-nginx/templates/controller-deployment.yaml delete mode 100644 hosting/kubernetes/budibase/charts/ingress-nginx/templates/controller-hpa.yaml delete mode 100644 hosting/kubernetes/budibase/charts/ingress-nginx/templates/controller-ingressclass.yaml delete mode 100644 hosting/kubernetes/budibase/charts/ingress-nginx/templates/controller-keda.yaml delete mode 100644 hosting/kubernetes/budibase/charts/ingress-nginx/templates/controller-poddisruptionbudget.yaml delete mode 100644 hosting/kubernetes/budibase/charts/ingress-nginx/templates/controller-prometheusrules.yaml delete mode 100644 hosting/kubernetes/budibase/charts/ingress-nginx/templates/controller-psp.yaml delete mode 100644 hosting/kubernetes/budibase/charts/ingress-nginx/templates/controller-role.yaml delete mode 100644 hosting/kubernetes/budibase/charts/ingress-nginx/templates/controller-rolebinding.yaml delete mode 100644 hosting/kubernetes/budibase/charts/ingress-nginx/templates/controller-service-internal.yaml delete mode 100644 hosting/kubernetes/budibase/charts/ingress-nginx/templates/controller-service-metrics.yaml delete mode 100644 hosting/kubernetes/budibase/charts/ingress-nginx/templates/controller-service-webhook.yaml delete mode 100644 hosting/kubernetes/budibase/charts/ingress-nginx/templates/controller-service.yaml delete mode 100644 hosting/kubernetes/budibase/charts/ingress-nginx/templates/controller-serviceaccount.yaml delete mode 100644 hosting/kubernetes/budibase/charts/ingress-nginx/templates/controller-servicemonitor.yaml delete mode 100644 hosting/kubernetes/budibase/charts/ingress-nginx/templates/default-backend-deployment.yaml delete mode 100644 hosting/kubernetes/budibase/charts/ingress-nginx/templates/default-backend-hpa.yaml delete mode 100644 hosting/kubernetes/budibase/charts/ingress-nginx/templates/default-backend-poddisruptionbudget.yaml delete mode 100644 hosting/kubernetes/budibase/charts/ingress-nginx/templates/default-backend-psp.yaml delete mode 100644 hosting/kubernetes/budibase/charts/ingress-nginx/templates/default-backend-role.yaml delete mode 100644 hosting/kubernetes/budibase/charts/ingress-nginx/templates/default-backend-rolebinding.yaml delete mode 100644 hosting/kubernetes/budibase/charts/ingress-nginx/templates/default-backend-service.yaml delete mode 100644 hosting/kubernetes/budibase/charts/ingress-nginx/templates/default-backend-serviceaccount.yaml delete mode 100644 hosting/kubernetes/budibase/charts/ingress-nginx/templates/dh-param-secret.yaml delete mode 100644 hosting/kubernetes/budibase/charts/ingress-nginx/values.yaml diff --git a/hosting/kubernetes/budibase/charts/couchdb/values.yaml b/hosting/kubernetes/budibase/charts/couchdb/values.yaml index f4cbcc9dd1..5a5025f816 100755 --- a/hosting/kubernetes/budibase/charts/couchdb/values.yaml +++ b/hosting/kubernetes/budibase/charts/couchdb/values.yaml @@ -51,7 +51,7 @@ serviceAccount: ## provisioning of Persistent Volumes; leaving it unset will invoke the default ## provisioner. persistentVolume: - enabled: true + enabled: false accessModes: - ReadWriteOnce size: 10Gi diff --git a/hosting/kubernetes/budibase/charts/ingress-nginx-3.35.0.tgz b/hosting/kubernetes/budibase/charts/ingress-nginx-3.35.0.tgz new file mode 100644 index 0000000000000000000000000000000000000000..ee5214c49785e6a639ea9fb34d27b37d915c04c8 GIT binary patch literal 25050 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


YxVZ#WWb-4%f~e&bVB!O)nr^(lHINBx~p5cj~O! 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 B#CHGy literal 0 HcmV?d00001 diff --git a/hosting/kubernetes/budibase/charts/ingress-nginx/.helmignore b/hosting/kubernetes/budibase/charts/ingress-nginx/.helmignore deleted file mode 100644 index 50af031725..0000000000 --- a/hosting/kubernetes/budibase/charts/ingress-nginx/.helmignore +++ /dev/null @@ -1,22 +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 -.vscode/ diff --git a/hosting/kubernetes/budibase/charts/ingress-nginx/CHANGELOG.md b/hosting/kubernetes/budibase/charts/ingress-nginx/CHANGELOG.md deleted file mode 100644 index 36526e0825..0000000000 --- a/hosting/kubernetes/budibase/charts/ingress-nginx/CHANGELOG.md +++ /dev/null @@ -1,250 +0,0 @@ -# Changelog - -This file documents all notable changes to [ingress-nginx](https://github.com/kubernetes/ingress-nginx) Helm Chart. The release numbering uses [semantic versioning](http://semver.org). - -### 3.34.0 - -- [7256] https://github.com/kubernetes/ingress-nginx/pull/7256 Add namespace field in the namespace scoped resource templates - -### 3.33.0 - -- [7164] https://github.com/kubernetes/ingress-nginx/pull/7164 Update nginx to v1.20.1 - -### 3.32.0 - -- [7117] https://github.com/kubernetes/ingress-nginx/pull/7117 Add annotations for HPA - -### 3.31.0 - -- [7137] https://github.com/kubernetes/ingress-nginx/pull/7137 Add support for custom probes - -### 3.30.0 - -- [#7092](https://github.com/kubernetes/ingress-nginx/pull/7092) Removes the possibility of using localhost in ExternalNames as endpoints - -### 3.29.0 - -- [X] [#6945](https://github.com/kubernetes/ingress-nginx/pull/7020) Add option to specify job label for ServiceMonitor - -### 3.28.0 - -- [ ] [#6900](https://github.com/kubernetes/ingress-nginx/pull/6900) Support existing PSPs - -### 3.27.0 - -- Update ingress-nginx v0.45.0 - -### 3.26.0 - -- [X] [#6979](https://github.com/kubernetes/ingress-nginx/pull/6979) Changed servicePort value for metrics - -### 3.25.0 - -- [X] [#6957](https://github.com/kubernetes/ingress-nginx/pull/6957) Add ability to specify automountServiceAccountToken - -### 3.24.0 - -- [X] [#6908](https://github.com/kubernetes/ingress-nginx/pull/6908) Add volumes to default-backend deployment - -### 3.23.0 - -- Update ingress-nginx v0.44.0 - -### 3.22.0 - -- [X] [#6802](https://github.com/kubernetes/ingress-nginx/pull/6802) Add value for configuring a custom Diffie-Hellman parameters file -- [X] [#6815](https://github.com/kubernetes/ingress-nginx/pull/6815) Allow use of numeric namespaces in helm chart - -### 3.21.0 - -- [X] [#6783](https://github.com/kubernetes/ingress-nginx/pull/6783) Add custom annotations to ScaledObject -- [X] [#6761](https://github.com/kubernetes/ingress-nginx/pull/6761) Adding quotes in the serviceAccount name in Helm values -- [X] [#6767](https://github.com/kubernetes/ingress-nginx/pull/6767) Remove ClusterRole when scope option is enabled -- [X] [#6785](https://github.com/kubernetes/ingress-nginx/pull/6785) Update kube-webhook-certgen image to v1.5.1 - -### 3.20.1 - -- Do not create KEDA in case of DaemonSets. -- Fix KEDA v2 definition - -### 3.20.0 - -- [X] [#6730](https://github.com/kubernetes/ingress-nginx/pull/6730) Do not create HPA for defaultBackend if not enabled. - -### 3.19.0 - -- Update ingress-nginx v0.43.0 - -### 3.18.0 - -- [X] [#6688](https://github.com/kubernetes/ingress-nginx/pull/6688) Allow volume-type emptyDir in controller podsecuritypolicy -- [X] [#6691](https://github.com/kubernetes/ingress-nginx/pull/6691) Improve parsing of helm parameters - -### 3.17.0 - -- Update ingress-nginx v0.42.0 - -### 3.16.1 - -- Fix chart-releaser action - -### 3.16.0 - -- [X] [#6646](https://github.com/kubernetes/ingress-nginx/pull/6646) Added LoadBalancerIP value for internal service - -### 3.15.1 - -- Fix chart-releaser action - -### 3.15.0 - -- [X] [#6586](https://github.com/kubernetes/ingress-nginx/pull/6586) Fix 'maxmindLicenseKey' location in values.yaml - -### 3.14.0 - -- [X] [#6469](https://github.com/kubernetes/ingress-nginx/pull/6469) Allow custom service names for controller and backend - -### 3.13.0 - -- [X] [#6544](https://github.com/kubernetes/ingress-nginx/pull/6544) Fix default backend HPA name variable - -### 3.12.0 - -- [X] [#6514](https://github.com/kubernetes/ingress-nginx/pull/6514) Remove helm2 support and update docs - -### 3.11.1 - -- [X] [#6505](https://github.com/kubernetes/ingress-nginx/pull/6505) Reorder HPA resource list to work with GitOps tooling - -### 3.11.0 - -- Support Keda Autoscaling - -### 3.10.1 - -- Fix regression introduced in 0.41.0 with external authentication - -### 3.10.0 - -- Fix routing regression introduced in 0.41.0 with PathType Exact - -### 3.9.0 - -- [X] [#6423](https://github.com/kubernetes/ingress-nginx/pull/6423) Add Default backend HPA autoscaling - -### 3.8.0 - -- [X] [#6395](https://github.com/kubernetes/ingress-nginx/pull/6395) Update jettech/kube-webhook-certgen image -- [X] [#6377](https://github.com/kubernetes/ingress-nginx/pull/6377) Added loadBalancerSourceRanges for internal lbs -- [X] [#6356](https://github.com/kubernetes/ingress-nginx/pull/6356) Add securitycontext settings on defaultbackend -- [X] [#6401](https://github.com/kubernetes/ingress-nginx/pull/6401) Fix controller service annotations -- [X] [#6403](https://github.com/kubernetes/ingress-nginx/pull/6403) Initial helm chart changelog - -### 3.7.1 - -- [X] [#6326](https://github.com/kubernetes/ingress-nginx/pull/6326) Fix liveness and readiness probe path in daemonset chart - -### 3.7.0 - -- [X] [#6316](https://github.com/kubernetes/ingress-nginx/pull/6316) Numerals in podAnnotations in quotes [#6315](https://github.com/kubernetes/ingress-nginx/issues/6315) - -### 3.6.0 - -- [X] [#6305](https://github.com/kubernetes/ingress-nginx/pull/6305) Add default linux nodeSelector - -### 3.5.1 - -- [X] [#6299](https://github.com/kubernetes/ingress-nginx/pull/6299) Fix helm chart release - -### 3.5.0 - -- [X] [#6260](https://github.com/kubernetes/ingress-nginx/pull/6260) Allow Helm Chart to customize admission webhook's annotations, timeoutSeconds, namespaceSelector, objectSelector and cert files locations - -### 3.4.0 - -- [X] [#6268](https://github.com/kubernetes/ingress-nginx/pull/6268) Update to 0.40.2 in helm chart #6288 - -### 3.3.1 - -- [X] [#6259](https://github.com/kubernetes/ingress-nginx/pull/6259) Release helm chart -- [X] [#6258](https://github.com/kubernetes/ingress-nginx/pull/6258) Fix chart markdown link -- [X] [#6253](https://github.com/kubernetes/ingress-nginx/pull/6253) Release v0.40.0 - -### 3.3.1 - -- [X] [#6233](https://github.com/kubernetes/ingress-nginx/pull/6233) Add admission controller e2e test - -### 3.3.0 - -- [X] [#6203](https://github.com/kubernetes/ingress-nginx/pull/6203) Refactor parsing of key values -- [X] [#6162](https://github.com/kubernetes/ingress-nginx/pull/6162) Add helm chart options to expose metrics service as NodePort -- [X] [#6180](https://github.com/kubernetes/ingress-nginx/pull/6180) Fix helm chart admissionReviewVersions regression -- [X] [#6169](https://github.com/kubernetes/ingress-nginx/pull/6169) Fix Typo in example prometheus rules - -### 3.0.0 - -- [X] [#6167](https://github.com/kubernetes/ingress-nginx/pull/6167) Update chart requirements - -### 2.16.0 - -- [X] [#6154](https://github.com/kubernetes/ingress-nginx/pull/6154) add `topologySpreadConstraint` to controller - -### 2.15.0 - -- [X] [#6087](https://github.com/kubernetes/ingress-nginx/pull/6087) Adding parameter for externalTrafficPolicy in internal controller service spec - -### 2.14.0 - -- [X] [#6104](https://github.com/kubernetes/ingress-nginx/pull/6104) Misc fixes for nginx-ingress chart for better keel and prometheus-operator integration - -### 2.13.0 - -- [X] [#6093](https://github.com/kubernetes/ingress-nginx/pull/6093) Release v0.35.0 - -### 2.13.0 - -- [X] [#6093](https://github.com/kubernetes/ingress-nginx/pull/6093) Release v0.35.0 -- [X] [#6080](https://github.com/kubernetes/ingress-nginx/pull/6080) Switch images to k8s.gcr.io after Vanity Domain Flip - -### 2.12.1 - -- [X] [#6075](https://github.com/kubernetes/ingress-nginx/pull/6075) Sync helm chart affinity examples - -### 2.12.0 - -- [X] [#6039](https://github.com/kubernetes/ingress-nginx/pull/6039) Add configurable serviceMonitor metricRelabelling and targetLabels -- [X] [#6044](https://github.com/kubernetes/ingress-nginx/pull/6044) Fix YAML linting - -### 2.11.3 - -- [X] [#6038](https://github.com/kubernetes/ingress-nginx/pull/6038) Bump chart version PATCH - -### 2.11.2 - -- [X] [#5951](https://github.com/kubernetes/ingress-nginx/pull/5951) Bump chart patch version - -### 2.11.1 - -- [X] [#5900](https://github.com/kubernetes/ingress-nginx/pull/5900) Release helm chart for v0.34.1 - -### 2.11.0 - -- [X] [#5879](https://github.com/kubernetes/ingress-nginx/pull/5879) Update helm chart for v0.34.0 -- [X] [#5671](https://github.com/kubernetes/ingress-nginx/pull/5671) Make liveness probe more fault tolerant than readiness probe - -### 2.10.0 - -- [X] [#5843](https://github.com/kubernetes/ingress-nginx/pull/5843) Update jettech/kube-webhook-certgen image - -### 2.9.1 - -- [X] [#5823](https://github.com/kubernetes/ingress-nginx/pull/5823) Add quoting to sysctls because numeric values need to be presented as strings (#5823) - -### 2.9.0 - -- [X] [#5795](https://github.com/kubernetes/ingress-nginx/pull/5795) Use fully qualified images to avoid cri-o issues - - -### TODO - -Keep building the changelog using *git log charts* checking the tag diff --git a/hosting/kubernetes/budibase/charts/ingress-nginx/Chart.yaml b/hosting/kubernetes/budibase/charts/ingress-nginx/Chart.yaml deleted file mode 100644 index 70e0df6197..0000000000 --- a/hosting/kubernetes/budibase/charts/ingress-nginx/Chart.yaml +++ /dev/null @@ -1,19 +0,0 @@ -annotations: - artifacthub.io/changes: | - - Add namespace field in the namespace scoped resource templates -apiVersion: v2 -appVersion: 0.48.1 -description: Ingress controller for Kubernetes using NGINX as a reverse proxy and load balancer -home: https://github.com/kubernetes/ingress-nginx -icon: https://upload.wikimedia.org/wikipedia/commons/thumb/c/c5/Nginx_logo.svg/500px-Nginx_logo.svg.png -keywords: -- ingress -- nginx -kubeVersion: '>=1.16.0-0' -maintainers: -- name: ChiefAlexander -name: ingress-nginx -sources: -- https://github.com/kubernetes/ingress-nginx -type: application -version: 3.35.0 diff --git a/hosting/kubernetes/budibase/charts/ingress-nginx/OWNERS b/hosting/kubernetes/budibase/charts/ingress-nginx/OWNERS deleted file mode 100644 index 392bc92f55..0000000000 --- a/hosting/kubernetes/budibase/charts/ingress-nginx/OWNERS +++ /dev/null @@ -1,10 +0,0 @@ -# See the OWNERS docs: https://github.com/kubernetes/community/blob/master/contributors/guide/owners.md - -approvers: -- ingress-nginx-helm-maintainers - -reviewers: -- ingress-nginx-helm-reviewers - -labels: -- area/helm \ No newline at end of file diff --git a/hosting/kubernetes/budibase/charts/ingress-nginx/README.md b/hosting/kubernetes/budibase/charts/ingress-nginx/README.md deleted file mode 100644 index d5a2c3ca84..0000000000 --- a/hosting/kubernetes/budibase/charts/ingress-nginx/README.md +++ /dev/null @@ -1,226 +0,0 @@ -# ingress-nginx - -[ingress-nginx](https://github.com/kubernetes/ingress-nginx) Ingress controller for Kubernetes using NGINX as a reverse proxy and load balancer - -To use, add the `kubernetes.io/ingress.class: nginx` annotation to your Ingress resources. - -This chart bootstraps an ingress-nginx deployment on a [Kubernetes](http://kubernetes.io) cluster using the [Helm](https://helm.sh) package manager. - -## Prerequisites - -- Kubernetes v1.16+ - -## Get Repo Info - -```console -helm repo add ingress-nginx https://kubernetes.github.io/ingress-nginx -helm repo update -``` - -## Install Chart - -**Important:** only helm3 is supported - -```console -helm install [RELEASE_NAME] ingress-nginx/ingress-nginx -``` - -The command deploys ingress-nginx on the Kubernetes cluster in the default configuration. - -_See [configuration](#configuration) below._ - -_See [helm install](https://helm.sh/docs/helm/helm_install/) for command documentation._ - -## Uninstall Chart - -```console -helm uninstall [RELEASE_NAME] -``` - -This removes all the Kubernetes components associated with the chart and deletes the release. - -_See [helm uninstall](https://helm.sh/docs/helm/helm_uninstall/) for command documentation._ - -## Upgrading Chart - -```console -helm upgrade [RELEASE_NAME] [CHART] --install -``` - -_See [helm upgrade](https://helm.sh/docs/helm/helm_upgrade/) for command documentation._ - -### Upgrading With Zero Downtime in Production - -By default the ingress-nginx controller has service interruptions whenever it's pods are restarted or redeployed. In order to fix that, see the excellent blog post by Lindsay Landry from Codecademy: [Kubernetes: Nginx and Zero Downtime in Production](https://medium.com/codecademy-engineering/kubernetes-nginx-and-zero-downtime-in-production-2c910c6a5ed8). - -### Migrating from stable/nginx-ingress - -There are two main ways to migrate a release from `stable/nginx-ingress` to `ingress-nginx/ingress-nginx` chart: - -1. For Nginx Ingress controllers used for non-critical services, the easiest method is to [uninstall](#uninstall-chart) the old release and [install](#install-chart) the new one -1. For critical services in production that require zero-downtime, you will want to: - 1. [Install](#install-chart) a second Ingress controller - 1. Redirect your DNS traffic from the old controller to the new controller - 1. Log traffic from both controllers during this changeover - 1. [Uninstall](#uninstall-chart) the old controller once traffic has fully drained from it - 1. For details on all of these steps see [Upgrading With Zero Downtime in Production](#upgrading-with-zero-downtime-in-production) - -Note that there are some different and upgraded configurations between the two charts, described by Rimas Mocevicius from JFrog in the "Upgrading to ingress-nginx Helm chart" section of [Migrating from Helm chart nginx-ingress to ingress-nginx](https://rimusz.net/migrating-to-ingress-nginx). As the `ingress-nginx/ingress-nginx` chart continues to update, you will want to check current differences by running [helm configuration](#configuration) commands on both charts. - -## Configuration - -See [Customizing the Chart Before Installing](https://helm.sh/docs/intro/using_helm/#customizing-the-chart-before-installing). To see all configurable options with detailed comments, visit the chart's [values.yaml](./values.yaml), or run these configuration commands: - -```console -helm show values ingress-nginx/ingress-nginx -``` - -### PodDisruptionBudget - -Note that the PodDisruptionBudget resource will only be defined if the replicaCount is greater than one, -else it would make it impossible to evacuate a node. See [gh issue #7127](https://github.com/helm/charts/issues/7127) for more info. - -### Prometheus Metrics - -The Nginx ingress controller can export Prometheus metrics, by setting `controller.metrics.enabled` to `true`. - -You can add Prometheus annotations to the metrics service using `controller.metrics.service.annotations`. Alternatively, if you use the Prometheus Operator, you can enable ServiceMonitor creation using `controller.metrics.serviceMonitor.enabled`. - -### ingress-nginx nginx\_status page/stats server - -Previous versions of this chart had a `controller.stats.*` configuration block, which is now obsolete due to the following changes in nginx ingress controller: - -- In [0.16.1](https://github.com/kubernetes/ingress-nginx/blob/master/Changelog.md#0161), the vts (virtual host traffic status) dashboard was removed -- In [0.23.0](https://github.com/kubernetes/ingress-nginx/blob/master/Changelog.md#0230), the status page at port 18080 is now a unix socket webserver only available at localhost. - You can use `curl --unix-socket /tmp/nginx-status-server.sock http://localhost/nginx_status` inside the controller container to access it locally, or use the snippet from [nginx-ingress changelog](https://github.com/kubernetes/ingress-nginx/blob/master/Changelog.md#0230) to re-enable the http server - -### ExternalDNS Service Configuration - -Add an [ExternalDNS](https://github.com/kubernetes-incubator/external-dns) annotation to the LoadBalancer service: - -```yaml -controller: - service: - annotations: - external-dns.alpha.kubernetes.io/hostname: kubernetes-example.com. -``` - -### AWS L7 ELB with SSL Termination - -Annotate the controller as shown in the [nginx-ingress l7 patch](https://github.com/kubernetes/ingress-nginx/blob/master/deploy/aws/l7/service-l7.yaml): - -```yaml -controller: - service: - targetPorts: - http: http - https: http - annotations: - service.beta.kubernetes.io/aws-load-balancer-ssl-cert: arn:aws:acm:XX-XXXX-X:XXXXXXXXX:certificate/XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXX - service.beta.kubernetes.io/aws-load-balancer-backend-protocol: "http" - service.beta.kubernetes.io/aws-load-balancer-ssl-ports: "https" - service.beta.kubernetes.io/aws-load-balancer-connection-idle-timeout: '3600' -``` - -### AWS route53-mapper - -To configure the LoadBalancer service with the [route53-mapper addon](https://github.com/kubernetes/kops/tree/master/addons/route53-mapper), add the `domainName` annotation and `dns` label: - -```yaml -controller: - service: - labels: - dns: "route53" - annotations: - domainName: "kubernetes-example.com" -``` - -### Additional Internal Load Balancer - -This setup is useful when you need both external and internal load balancers but don't want to have multiple ingress controllers and multiple ingress objects per application. - -By default, the ingress object will point to the external load balancer address, but if correctly configured, you can make use of the internal one if the URL you are looking up resolves to the internal load balancer's URL. - -You'll need to set both the following values: - -`controller.service.internal.enabled` -`controller.service.internal.annotations` - -If one of them is missing the internal load balancer will not be deployed. Example you may have `controller.service.internal.enabled=true` but no annotations set, in this case no action will be taken. - -`controller.service.internal.annotations` varies with the cloud service you're using. - -Example for AWS: - -```yaml -controller: - service: - internal: - enabled: true - annotations: - # Create internal ELB - service.beta.kubernetes.io/aws-load-balancer-internal: "true" - # Any other annotation can be declared here. -``` - -Example for GCE: - -```yaml -controller: - service: - internal: - enabled: true - annotations: - # Create internal LB. More informations: https://cloud.google.com/kubernetes-engine/docs/how-to/internal-load-balancing - # For GKE versions 1.17 and later - networking.gke.io/load-balancer-type: "Internal" - # For earlier versions - # cloud.google.com/load-balancer-type: "Internal" - - # Any other annotation can be declared here. -``` - -Example for Azure: - -```yaml -controller: - service: - annotations: - # Create internal LB - service.beta.kubernetes.io/azure-load-balancer-internal: "true" - # Any other annotation can be declared here. -``` - -Example for Oracle Cloud Infrastructure: - -```yaml -controller: - service: - annotations: - # Create internal LB - service.beta.kubernetes.io/oci-load-balancer-internal: "true" - # Any other annotation can be declared here. -``` - -An use case for this scenario is having a split-view DNS setup where the public zone CNAME records point to the external balancer URL while the private zone CNAME records point to the internal balancer URL. This way, you only need one ingress kubernetes object. - -Optionally you can set `controller.service.loadBalancerIP` if you need a static IP for the resulting `LoadBalancer`. - -### Ingress Admission Webhooks - -With nginx-ingress-controller version 0.25+, the nginx ingress controller pod exposes an endpoint that will integrate with the `validatingwebhookconfiguration` Kubernetes feature to prevent bad ingress from being added to the cluster. -**This feature is enabled by default since 0.31.0.** - -With nginx-ingress-controller in 0.25.* work only with kubernetes 1.14+, 0.26 fix [this issue](https://github.com/kubernetes/ingress-nginx/pull/4521) - -### Helm Error When Upgrading: spec.clusterIP: Invalid value: "" - -If you are upgrading this chart from a version between 0.31.0 and 1.2.2 then you may get an error like this: - -```console -Error: UPGRADE FAILED: Service "?????-controller" is invalid: spec.clusterIP: Invalid value: "": field is immutable -``` - -Detail of how and why are in [this issue](https://github.com/helm/charts/pull/13646) but to resolve this you can set `xxxx.service.omitClusterIP` to `true` where `xxxx` is the service referenced in the error. - -As of version `1.26.0` of this chart, by simply not providing any clusterIP value, `invalid: spec.clusterIP: Invalid value: "": field is immutable` will no longer occur since `clusterIP: ""` will not be rendered. diff --git a/hosting/kubernetes/budibase/charts/ingress-nginx/ci/daemonset-customconfig-values.yaml b/hosting/kubernetes/budibase/charts/ingress-nginx/ci/daemonset-customconfig-values.yaml deleted file mode 100644 index e12b53421b..0000000000 --- a/hosting/kubernetes/budibase/charts/ingress-nginx/ci/daemonset-customconfig-values.yaml +++ /dev/null @@ -1,9 +0,0 @@ -controller: - kind: DaemonSet - admissionWebhooks: - enabled: false - service: - type: ClusterIP - - config: - use-proxy-protocol: "true" diff --git a/hosting/kubernetes/budibase/charts/ingress-nginx/ci/daemonset-customnodeport-values.yaml b/hosting/kubernetes/budibase/charts/ingress-nginx/ci/daemonset-customnodeport-values.yaml deleted file mode 100644 index cfc545f69f..0000000000 --- a/hosting/kubernetes/budibase/charts/ingress-nginx/ci/daemonset-customnodeport-values.yaml +++ /dev/null @@ -1,18 +0,0 @@ -controller: - kind: DaemonSet - admissionWebhooks: - enabled: false - - service: - type: NodePort - nodePorts: - tcp: - 9000: 30090 - udp: - 9001: 30091 - -tcp: - 9000: "default/test:8080" - -udp: - 9001: "default/test:8080" diff --git a/hosting/kubernetes/budibase/charts/ingress-nginx/ci/daemonset-headers-values.yaml b/hosting/kubernetes/budibase/charts/ingress-nginx/ci/daemonset-headers-values.yaml deleted file mode 100644 index ff82cd9c70..0000000000 --- a/hosting/kubernetes/budibase/charts/ingress-nginx/ci/daemonset-headers-values.yaml +++ /dev/null @@ -1,10 +0,0 @@ -controller: - kind: DaemonSet - admissionWebhooks: - enabled: false - addHeaders: - X-Frame-Options: deny - proxySetHeaders: - X-Forwarded-Proto: https - service: - type: ClusterIP diff --git a/hosting/kubernetes/budibase/charts/ingress-nginx/ci/daemonset-internal-lb-values.yaml b/hosting/kubernetes/budibase/charts/ingress-nginx/ci/daemonset-internal-lb-values.yaml deleted file mode 100644 index d8948d634b..0000000000 --- a/hosting/kubernetes/budibase/charts/ingress-nginx/ci/daemonset-internal-lb-values.yaml +++ /dev/null @@ -1,10 +0,0 @@ -controller: - kind: DaemonSet - admissionWebhooks: - enabled: false - service: - type: ClusterIP - internal: - enabled: true - annotations: - service.beta.kubernetes.io/aws-load-balancer-internal: "true" diff --git a/hosting/kubernetes/budibase/charts/ingress-nginx/ci/daemonset-nodeport-values.yaml b/hosting/kubernetes/budibase/charts/ingress-nginx/ci/daemonset-nodeport-values.yaml deleted file mode 100644 index 6d6605f0e1..0000000000 --- a/hosting/kubernetes/budibase/charts/ingress-nginx/ci/daemonset-nodeport-values.yaml +++ /dev/null @@ -1,6 +0,0 @@ -controller: - kind: DaemonSet - admissionWebhooks: - enabled: false - service: - type: NodePort diff --git a/hosting/kubernetes/budibase/charts/ingress-nginx/ci/daemonset-podannotations-values.yaml b/hosting/kubernetes/budibase/charts/ingress-nginx/ci/daemonset-podannotations-values.yaml deleted file mode 100644 index 04ac58dbd8..0000000000 --- a/hosting/kubernetes/budibase/charts/ingress-nginx/ci/daemonset-podannotations-values.yaml +++ /dev/null @@ -1,13 +0,0 @@ -controller: - kind: DaemonSet - admissionWebhooks: - enabled: false - metrics: - enabled: true - service: - type: ClusterIP - podAnnotations: - prometheus.io/path: /metrics - prometheus.io/port: "10254" - prometheus.io/scheme: http - prometheus.io/scrape: "true" diff --git a/hosting/kubernetes/budibase/charts/ingress-nginx/ci/daemonset-tcp-udp-configMapNamespace-values.yaml b/hosting/kubernetes/budibase/charts/ingress-nginx/ci/daemonset-tcp-udp-configMapNamespace-values.yaml deleted file mode 100644 index afb5487c57..0000000000 --- a/hosting/kubernetes/budibase/charts/ingress-nginx/ci/daemonset-tcp-udp-configMapNamespace-values.yaml +++ /dev/null @@ -1,16 +0,0 @@ -controller: - kind: DaemonSet - admissionWebhooks: - enabled: false - service: - type: ClusterIP - tcp: - configMapNamespace: default - udp: - configMapNamespace: default - -tcp: - 9000: "default/test:8080" - -udp: - 9001: "default/test:8080" diff --git a/hosting/kubernetes/budibase/charts/ingress-nginx/ci/daemonset-tcp-udp-values.yaml b/hosting/kubernetes/budibase/charts/ingress-nginx/ci/daemonset-tcp-udp-values.yaml deleted file mode 100644 index 7b4d7cbe7d..0000000000 --- a/hosting/kubernetes/budibase/charts/ingress-nginx/ci/daemonset-tcp-udp-values.yaml +++ /dev/null @@ -1,12 +0,0 @@ -controller: - kind: DaemonSet - admissionWebhooks: - enabled: false - service: - type: ClusterIP - -tcp: - 9000: "default/test:8080" - -udp: - 9001: "default/test:8080" diff --git a/hosting/kubernetes/budibase/charts/ingress-nginx/ci/daemonset-tcp-values.yaml b/hosting/kubernetes/budibase/charts/ingress-nginx/ci/daemonset-tcp-values.yaml deleted file mode 100644 index a359a6a401..0000000000 --- a/hosting/kubernetes/budibase/charts/ingress-nginx/ci/daemonset-tcp-values.yaml +++ /dev/null @@ -1,10 +0,0 @@ -controller: - kind: DaemonSet - admissionWebhooks: - enabled: false - service: - type: ClusterIP - -tcp: - 9000: "default/test:8080" - 9001: "default/test:8080" diff --git a/hosting/kubernetes/budibase/charts/ingress-nginx/ci/deamonset-default-values.yaml b/hosting/kubernetes/budibase/charts/ingress-nginx/ci/deamonset-default-values.yaml deleted file mode 100644 index e63a7f5db3..0000000000 --- a/hosting/kubernetes/budibase/charts/ingress-nginx/ci/deamonset-default-values.yaml +++ /dev/null @@ -1,6 +0,0 @@ -controller: - kind: DaemonSet - admissionWebhooks: - enabled: false - service: - type: ClusterIP diff --git a/hosting/kubernetes/budibase/charts/ingress-nginx/ci/deamonset-metrics-values.yaml b/hosting/kubernetes/budibase/charts/ingress-nginx/ci/deamonset-metrics-values.yaml deleted file mode 100644 index 1e5190afc0..0000000000 --- a/hosting/kubernetes/budibase/charts/ingress-nginx/ci/deamonset-metrics-values.yaml +++ /dev/null @@ -1,8 +0,0 @@ -controller: - kind: DaemonSet - admissionWebhooks: - enabled: false - metrics: - enabled: true - service: - type: ClusterIP diff --git a/hosting/kubernetes/budibase/charts/ingress-nginx/ci/deamonset-psp-values.yaml b/hosting/kubernetes/budibase/charts/ingress-nginx/ci/deamonset-psp-values.yaml deleted file mode 100644 index 017b60a9c6..0000000000 --- a/hosting/kubernetes/budibase/charts/ingress-nginx/ci/deamonset-psp-values.yaml +++ /dev/null @@ -1,9 +0,0 @@ -controller: - kind: DaemonSet - admissionWebhooks: - enabled: false - service: - type: ClusterIP - -podSecurityPolicy: - enabled: true diff --git a/hosting/kubernetes/budibase/charts/ingress-nginx/ci/deamonset-webhook-and-psp-values.yaml b/hosting/kubernetes/budibase/charts/ingress-nginx/ci/deamonset-webhook-and-psp-values.yaml deleted file mode 100644 index 88aafc66fd..0000000000 --- a/hosting/kubernetes/budibase/charts/ingress-nginx/ci/deamonset-webhook-and-psp-values.yaml +++ /dev/null @@ -1,9 +0,0 @@ -controller: - kind: DaemonSet - admissionWebhooks: - enabled: true - service: - type: ClusterIP - -podSecurityPolicy: - enabled: true diff --git a/hosting/kubernetes/budibase/charts/ingress-nginx/ci/deamonset-webhook-values.yaml b/hosting/kubernetes/budibase/charts/ingress-nginx/ci/deamonset-webhook-values.yaml deleted file mode 100644 index 6e3b371da6..0000000000 --- a/hosting/kubernetes/budibase/charts/ingress-nginx/ci/deamonset-webhook-values.yaml +++ /dev/null @@ -1,6 +0,0 @@ -controller: - kind: DaemonSet - admissionWebhooks: - enabled: true - service: - type: ClusterIP diff --git a/hosting/kubernetes/budibase/charts/ingress-nginx/ci/deployment-autoscaling-values.yaml b/hosting/kubernetes/budibase/charts/ingress-nginx/ci/deployment-autoscaling-values.yaml deleted file mode 100644 index 5314cecb38..0000000000 --- a/hosting/kubernetes/budibase/charts/ingress-nginx/ci/deployment-autoscaling-values.yaml +++ /dev/null @@ -1,7 +0,0 @@ -controller: - autoscaling: - enabled: true - admissionWebhooks: - enabled: false - service: - type: ClusterIP diff --git a/hosting/kubernetes/budibase/charts/ingress-nginx/ci/deployment-customconfig-values.yaml b/hosting/kubernetes/budibase/charts/ingress-nginx/ci/deployment-customconfig-values.yaml deleted file mode 100644 index f232531acb..0000000000 --- a/hosting/kubernetes/budibase/charts/ingress-nginx/ci/deployment-customconfig-values.yaml +++ /dev/null @@ -1,7 +0,0 @@ -controller: - config: - use-proxy-protocol: "true" - admissionWebhooks: - enabled: false - service: - type: ClusterIP diff --git a/hosting/kubernetes/budibase/charts/ingress-nginx/ci/deployment-customnodeport-values.yaml b/hosting/kubernetes/budibase/charts/ingress-nginx/ci/deployment-customnodeport-values.yaml deleted file mode 100644 index 9eda282b13..0000000000 --- a/hosting/kubernetes/budibase/charts/ingress-nginx/ci/deployment-customnodeport-values.yaml +++ /dev/null @@ -1,16 +0,0 @@ -controller: - admissionWebhooks: - enabled: false - service: - type: NodePort - nodePorts: - tcp: - 9000: 30090 - udp: - 9001: 30091 - -tcp: - 9000: "default/test:8080" - -udp: - 9001: "default/test:8080" diff --git a/hosting/kubernetes/budibase/charts/ingress-nginx/ci/deployment-default-values.yaml b/hosting/kubernetes/budibase/charts/ingress-nginx/ci/deployment-default-values.yaml deleted file mode 100644 index 93a393c975..0000000000 --- a/hosting/kubernetes/budibase/charts/ingress-nginx/ci/deployment-default-values.yaml +++ /dev/null @@ -1,4 +0,0 @@ -# Left blank to test default values -controller: - service: - type: ClusterIP diff --git a/hosting/kubernetes/budibase/charts/ingress-nginx/ci/deployment-headers-values.yaml b/hosting/kubernetes/budibase/charts/ingress-nginx/ci/deployment-headers-values.yaml deleted file mode 100644 index 665fd48d35..0000000000 --- a/hosting/kubernetes/budibase/charts/ingress-nginx/ci/deployment-headers-values.yaml +++ /dev/null @@ -1,9 +0,0 @@ -controller: - admissionWebhooks: - enabled: false - addHeaders: - X-Frame-Options: deny - proxySetHeaders: - X-Forwarded-Proto: https - service: - type: ClusterIP diff --git a/hosting/kubernetes/budibase/charts/ingress-nginx/ci/deployment-internal-lb-values.yaml b/hosting/kubernetes/budibase/charts/ingress-nginx/ci/deployment-internal-lb-values.yaml deleted file mode 100644 index c7f22d636e..0000000000 --- a/hosting/kubernetes/budibase/charts/ingress-nginx/ci/deployment-internal-lb-values.yaml +++ /dev/null @@ -1,9 +0,0 @@ -controller: - admissionWebhooks: - enabled: false - service: - type: ClusterIP - internal: - enabled: true - annotations: - service.beta.kubernetes.io/aws-load-balancer-internal: "true" diff --git a/hosting/kubernetes/budibase/charts/ingress-nginx/ci/deployment-metrics-values.yaml b/hosting/kubernetes/budibase/charts/ingress-nginx/ci/deployment-metrics-values.yaml deleted file mode 100644 index 887ed0f620..0000000000 --- a/hosting/kubernetes/budibase/charts/ingress-nginx/ci/deployment-metrics-values.yaml +++ /dev/null @@ -1,7 +0,0 @@ -controller: - admissionWebhooks: - enabled: false - metrics: - enabled: true - service: - type: ClusterIP diff --git a/hosting/kubernetes/budibase/charts/ingress-nginx/ci/deployment-nodeport-values.yaml b/hosting/kubernetes/budibase/charts/ingress-nginx/ci/deployment-nodeport-values.yaml deleted file mode 100644 index 84f1f7582e..0000000000 --- a/hosting/kubernetes/budibase/charts/ingress-nginx/ci/deployment-nodeport-values.yaml +++ /dev/null @@ -1,5 +0,0 @@ -controller: - admissionWebhooks: - enabled: false - service: - type: NodePort diff --git a/hosting/kubernetes/budibase/charts/ingress-nginx/ci/deployment-podannotations-values.yaml b/hosting/kubernetes/budibase/charts/ingress-nginx/ci/deployment-podannotations-values.yaml deleted file mode 100644 index b65a0910b3..0000000000 --- a/hosting/kubernetes/budibase/charts/ingress-nginx/ci/deployment-podannotations-values.yaml +++ /dev/null @@ -1,12 +0,0 @@ -controller: - admissionWebhooks: - enabled: false - metrics: - enabled: true - service: - type: ClusterIP - podAnnotations: - prometheus.io/path: /metrics - prometheus.io/port: "10254" - prometheus.io/scheme: http - prometheus.io/scrape: "true" diff --git a/hosting/kubernetes/budibase/charts/ingress-nginx/ci/deployment-psp-values.yaml b/hosting/kubernetes/budibase/charts/ingress-nginx/ci/deployment-psp-values.yaml deleted file mode 100644 index e339c69c32..0000000000 --- a/hosting/kubernetes/budibase/charts/ingress-nginx/ci/deployment-psp-values.yaml +++ /dev/null @@ -1,6 +0,0 @@ -controller: - service: - type: ClusterIP - -podSecurityPolicy: - enabled: true diff --git a/hosting/kubernetes/budibase/charts/ingress-nginx/ci/deployment-tcp-udp-configMapNamespace-values.yaml b/hosting/kubernetes/budibase/charts/ingress-nginx/ci/deployment-tcp-udp-configMapNamespace-values.yaml deleted file mode 100644 index 141e06b687..0000000000 --- a/hosting/kubernetes/budibase/charts/ingress-nginx/ci/deployment-tcp-udp-configMapNamespace-values.yaml +++ /dev/null @@ -1,15 +0,0 @@ -controller: - admissionWebhooks: - enabled: false - service: - type: ClusterIP - tcp: - configMapNamespace: default - udp: - configMapNamespace: default - -tcp: - 9000: "default/test:8080" - -udp: - 9001: "default/test:8080" diff --git a/hosting/kubernetes/budibase/charts/ingress-nginx/ci/deployment-tcp-udp-values.yaml b/hosting/kubernetes/budibase/charts/ingress-nginx/ci/deployment-tcp-udp-values.yaml deleted file mode 100644 index bc29abeba7..0000000000 --- a/hosting/kubernetes/budibase/charts/ingress-nginx/ci/deployment-tcp-udp-values.yaml +++ /dev/null @@ -1,11 +0,0 @@ -controller: - admissionWebhooks: - enabled: false - service: - type: ClusterIP - -tcp: - 9000: "default/test:8080" - -udp: - 9001: "default/test:8080" diff --git a/hosting/kubernetes/budibase/charts/ingress-nginx/ci/deployment-tcp-values.yaml b/hosting/kubernetes/budibase/charts/ingress-nginx/ci/deployment-tcp-values.yaml deleted file mode 100644 index b7f54c09fa..0000000000 --- a/hosting/kubernetes/budibase/charts/ingress-nginx/ci/deployment-tcp-values.yaml +++ /dev/null @@ -1,7 +0,0 @@ -controller: - service: - type: ClusterIP - -tcp: - 9000: "default/test:8080" - 9001: "default/test:8080" diff --git a/hosting/kubernetes/budibase/charts/ingress-nginx/ci/deployment-webhook-and-psp-values.yaml b/hosting/kubernetes/budibase/charts/ingress-nginx/ci/deployment-webhook-and-psp-values.yaml deleted file mode 100644 index a829c36144..0000000000 --- a/hosting/kubernetes/budibase/charts/ingress-nginx/ci/deployment-webhook-and-psp-values.yaml +++ /dev/null @@ -1,8 +0,0 @@ -controller: - admissionWebhooks: - enabled: true - service: - type: ClusterIP - -podSecurityPolicy: - enabled: true diff --git a/hosting/kubernetes/budibase/charts/ingress-nginx/ci/deployment-webhook-values.yaml b/hosting/kubernetes/budibase/charts/ingress-nginx/ci/deployment-webhook-values.yaml deleted file mode 100644 index 4f18a70b9f..0000000000 --- a/hosting/kubernetes/budibase/charts/ingress-nginx/ci/deployment-webhook-values.yaml +++ /dev/null @@ -1,5 +0,0 @@ -controller: - admissionWebhooks: - enabled: true - service: - type: ClusterIP diff --git a/hosting/kubernetes/budibase/charts/ingress-nginx/templates/NOTES.txt b/hosting/kubernetes/budibase/charts/ingress-nginx/templates/NOTES.txt deleted file mode 100644 index 60fb2c1f62..0000000000 --- a/hosting/kubernetes/budibase/charts/ingress-nginx/templates/NOTES.txt +++ /dev/null @@ -1,71 +0,0 @@ -The ingress-nginx controller has been installed. - -{{- if contains "NodePort" .Values.controller.service.type }} -Get the application URL by running these commands: - -{{- if (not (empty .Values.controller.service.nodePorts.http)) }} - export HTTP_NODE_PORT={{ .Values.controller.service.nodePorts.http }} -{{- else }} - export HTTP_NODE_PORT=$(kubectl --namespace {{ .Release.Namespace }} get services -o jsonpath="{.spec.ports[0].nodePort}" {{ include "ingress-nginx.controller.fullname" . }}) -{{- end }} -{{- if (not (empty .Values.controller.service.nodePorts.https)) }} - export HTTPS_NODE_PORT={{ .Values.controller.service.nodePorts.https }} -{{- else }} - export HTTPS_NODE_PORT=$(kubectl --namespace {{ .Release.Namespace }} get services -o jsonpath="{.spec.ports[1].nodePort}" {{ include "ingress-nginx.controller.fullname" . }}) -{{- end }} - export NODE_IP=$(kubectl --namespace {{ .Release.Namespace }} get nodes -o jsonpath="{.items[0].status.addresses[1].address}") - - echo "Visit http://$NODE_IP:$HTTP_NODE_PORT to access your application via HTTP." - echo "Visit https://$NODE_IP:$HTTPS_NODE_PORT to access your application via HTTPS." -{{- else if contains "LoadBalancer" .Values.controller.service.type }} -It may take a few minutes for the LoadBalancer IP to be available. -You can watch the status by running 'kubectl --namespace {{ .Release.Namespace }} get services -o wide -w {{ include "ingress-nginx.controller.fullname" . }}' -{{- else if contains "ClusterIP" .Values.controller.service.type }} -Get the application URL by running these commands: - export POD_NAME=$(kubectl --namespace {{ .Release.Namespace }} get pods -o jsonpath="{.items[0].metadata.name}" -l "app={{ template "ingress-nginx.name" . }},component={{ .Values.controller.name }},release={{ .Release.Name }}") - kubectl --namespace {{ .Release.Namespace }} port-forward $POD_NAME 8080:80 - echo "Visit http://127.0.0.1:8080 to access your application." -{{- end }} - -An example Ingress that makes use of the controller: - - apiVersion: networking.k8s.io/v1beta1 - kind: Ingress - metadata: - annotations: - kubernetes.io/ingress.class: {{ .Values.controller.ingressClass }} - name: example - namespace: foo - spec: - rules: - - host: www.example.com - http: - paths: - - backend: - serviceName: exampleService - servicePort: 80 - path: / - # This section is only required if TLS is to be enabled for the Ingress - tls: - - hosts: - - www.example.com - secretName: example-tls - -If TLS is enabled for the Ingress, a Secret containing the certificate and key must also be provided: - - apiVersion: v1 - kind: Secret - metadata: - name: example-tls - namespace: foo - data: - tls.crt: - tls.key: - type: kubernetes.io/tls - -{{- if .Values.controller.headers }} -################################################################################# -###### WARNING: `controller.headers` has been deprecated! ##### -###### It has been renamed to `controller.proxySetHeaders`. ##### -################################################################################# -{{- end }} diff --git a/hosting/kubernetes/budibase/charts/ingress-nginx/templates/_helpers.tpl b/hosting/kubernetes/budibase/charts/ingress-nginx/templates/_helpers.tpl deleted file mode 100644 index 8b1fd09513..0000000000 --- a/hosting/kubernetes/budibase/charts/ingress-nginx/templates/_helpers.tpl +++ /dev/null @@ -1,134 +0,0 @@ -{{/* vim: set filetype=mustache: */}} -{{/* -Expand the name of the chart. -*/}} -{{- define "ingress-nginx.name" -}} -{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" -}} -{{- end -}} - -{{/* -Create chart name and version as used by the chart label. -*/}} -{{- define "ingress-nginx.chart" -}} -{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" -}} -{{- end -}} - -{{/* -Create a default fully qualified app name. -We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). -*/}} -{{- define "ingress-nginx.fullname" -}} -{{- if .Values.fullnameOverride -}} -{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" -}} -{{- else -}} -{{- $name := default .Chart.Name .Values.nameOverride -}} -{{- if contains $name .Release.Name -}} -{{- .Release.Name | trunc 63 | trimSuffix "-" -}} -{{- else -}} -{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" -}} -{{- end -}} -{{- end -}} -{{- end -}} - -{{/* -Create a default fully qualified controller name. -We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). -*/}} -{{- define "ingress-nginx.controller.fullname" -}} -{{- printf "%s-%s" (include "ingress-nginx.fullname" .) .Values.controller.name | trunc 63 | trimSuffix "-" -}} -{{- end -}} - -{{/* -Construct the path for the publish-service. - -By convention this will simply use the / to match the name of the -service generated. - -Users can provide an override for an explicit service they want bound via `.Values.controller.publishService.pathOverride` - -*/}} -{{- define "ingress-nginx.controller.publishServicePath" -}} -{{- $defServiceName := printf "%s/%s" "$(POD_NAMESPACE)" (include "ingress-nginx.controller.fullname" .) -}} -{{- $servicePath := default $defServiceName .Values.controller.publishService.pathOverride }} -{{- print $servicePath | trimSuffix "-" -}} -{{- end -}} - -{{/* -Create a default fully qualified default backend name. -We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). -*/}} -{{- define "ingress-nginx.defaultBackend.fullname" -}} -{{- printf "%s-%s" (include "ingress-nginx.fullname" .) .Values.defaultBackend.name | trunc 63 | trimSuffix "-" -}} -{{- end -}} - -{{/* -Common labels -*/}} -{{- define "ingress-nginx.labels" -}} -helm.sh/chart: {{ include "ingress-nginx.chart" . }} -{{ include "ingress-nginx.selectorLabels" . }} -{{- if .Chart.AppVersion }} -app.kubernetes.io/version: {{ .Chart.AppVersion | quote }} -{{- end }} -app.kubernetes.io/managed-by: {{ .Release.Service }} -{{- end -}} - -{{/* -Selector labels -*/}} -{{- define "ingress-nginx.selectorLabels" -}} -app.kubernetes.io/name: {{ include "ingress-nginx.name" . }} -app.kubernetes.io/instance: {{ .Release.Name }} -{{- end -}} - -{{/* -Create the name of the controller service account to use -*/}} -{{- define "ingress-nginx.serviceAccountName" -}} -{{- if .Values.serviceAccount.create -}} - {{ default (include "ingress-nginx.fullname" .) .Values.serviceAccount.name }} -{{- else -}} - {{ default "default" .Values.serviceAccount.name }} -{{- end -}} -{{- end -}} - -{{/* -Create the name of the backend service account to use - only used when podsecuritypolicy is also enabled -*/}} -{{- define "ingress-nginx.defaultBackend.serviceAccountName" -}} -{{- if .Values.defaultBackend.serviceAccount.create -}} - {{ default (printf "%s-backend" (include "ingress-nginx.fullname" .)) .Values.defaultBackend.serviceAccount.name }} -{{- else -}} - {{ default "default-backend" .Values.defaultBackend.serviceAccount.name }} -{{- end -}} -{{- end -}} - -{{/* -Return the appropriate apiGroup for PodSecurityPolicy. -*/}} -{{- define "podSecurityPolicy.apiGroup" -}} -{{- if semverCompare ">=1.14-0" .Capabilities.KubeVersion.GitVersion -}} -{{- print "policy" -}} -{{- else -}} -{{- print "extensions" -}} -{{- end -}} -{{- end -}} - -{{/* -Check the ingress controller version tag is at most three versions behind the last release -*/}} -{{- define "isControllerTagValid" -}} -{{- if not (semverCompare ">=0.27.0-0" .Values.controller.image.tag) -}} -{{- fail "Controller container image tag should be 0.27.0 or higher" -}} -{{- end -}} -{{- end -}} - -{{/* -IngressClass parameters. -*/}} -{{- define "ingressClass.parameters" -}} - {{- if .Values.controller.ingressClassResource.parameters -}} - parameters: -{{ toYaml .Values.controller.ingressClassResource.parameters | indent 4}} - {{ end }} -{{- end -}} diff --git a/hosting/kubernetes/budibase/charts/ingress-nginx/templates/admission-webhooks/job-patch/clusterrole.yaml b/hosting/kubernetes/budibase/charts/ingress-nginx/templates/admission-webhooks/job-patch/clusterrole.yaml deleted file mode 100644 index fd762f9354..0000000000 --- a/hosting/kubernetes/budibase/charts/ingress-nginx/templates/admission-webhooks/job-patch/clusterrole.yaml +++ /dev/null @@ -1,31 +0,0 @@ -{{- if and .Values.controller.admissionWebhooks.enabled .Values.controller.admissionWebhooks.patch.enabled -}} -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRole -metadata: - name: {{ include "ingress-nginx.fullname" . }}-admission - annotations: - "helm.sh/hook": pre-install,pre-upgrade,post-install,post-upgrade - "helm.sh/hook-delete-policy": before-hook-creation,hook-succeeded - labels: - {{- include "ingress-nginx.labels" . | nindent 4 }} - app.kubernetes.io/component: admission-webhook -rules: - - apiGroups: - - admissionregistration.k8s.io - resources: - - validatingwebhookconfigurations - verbs: - - get - - update -{{- if .Values.podSecurityPolicy.enabled }} - - apiGroups: ['extensions'] - resources: ['podsecuritypolicies'] - verbs: ['use'] - resourceNames: - {{- with .Values.controller.admissionWebhooks.existingPsp }} - - {{ . }} - {{- else }} - - {{ include "ingress-nginx.fullname" . }}-admission - {{- end }} -{{- end }} -{{- end }} diff --git a/hosting/kubernetes/budibase/charts/ingress-nginx/templates/admission-webhooks/job-patch/clusterrolebinding.yaml b/hosting/kubernetes/budibase/charts/ingress-nginx/templates/admission-webhooks/job-patch/clusterrolebinding.yaml deleted file mode 100644 index 4990fb1c34..0000000000 --- a/hosting/kubernetes/budibase/charts/ingress-nginx/templates/admission-webhooks/job-patch/clusterrolebinding.yaml +++ /dev/null @@ -1,20 +0,0 @@ -{{- if and .Values.controller.admissionWebhooks.enabled .Values.controller.admissionWebhooks.patch.enabled -}} -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRoleBinding -metadata: - name: {{ include "ingress-nginx.fullname" . }}-admission - annotations: - "helm.sh/hook": pre-install,pre-upgrade,post-install,post-upgrade - "helm.sh/hook-delete-policy": before-hook-creation,hook-succeeded - labels: - {{- include "ingress-nginx.labels" . | nindent 4 }} - app.kubernetes.io/component: admission-webhook -roleRef: - apiGroup: rbac.authorization.k8s.io - kind: ClusterRole - name: {{ include "ingress-nginx.fullname" . }}-admission -subjects: - - kind: ServiceAccount - name: {{ include "ingress-nginx.fullname" . }}-admission - namespace: {{ .Release.Namespace | quote }} -{{- end }} diff --git a/hosting/kubernetes/budibase/charts/ingress-nginx/templates/admission-webhooks/job-patch/job-createSecret.yaml b/hosting/kubernetes/budibase/charts/ingress-nginx/templates/admission-webhooks/job-patch/job-createSecret.yaml deleted file mode 100644 index 3656be4876..0000000000 --- a/hosting/kubernetes/budibase/charts/ingress-nginx/templates/admission-webhooks/job-patch/job-createSecret.yaml +++ /dev/null @@ -1,61 +0,0 @@ -{{- if and .Values.controller.admissionWebhooks.enabled .Values.controller.admissionWebhooks.patch.enabled -}} -apiVersion: batch/v1 -kind: Job -metadata: - name: {{ include "ingress-nginx.fullname" . }}-admission-create - namespace: {{ .Release.Namespace }} - annotations: - "helm.sh/hook": pre-install,pre-upgrade - "helm.sh/hook-delete-policy": before-hook-creation,hook-succeeded - labels: - {{- include "ingress-nginx.labels" . | nindent 4 }} - app.kubernetes.io/component: admission-webhook -spec: -{{- if .Capabilities.APIVersions.Has "batch/v1alpha1" }} - # Alpha feature since k8s 1.12 - ttlSecondsAfterFinished: 0 -{{- end }} - template: - metadata: - name: {{ include "ingress-nginx.fullname" . }}-admission-create - {{- if .Values.controller.admissionWebhooks.patch.podAnnotations }} - annotations: {{ toYaml .Values.controller.admissionWebhooks.patch.podAnnotations | nindent 8 }} - {{- end }} - labels: - {{- include "ingress-nginx.labels" . | nindent 8 }} - app.kubernetes.io/component: admission-webhook - spec: - {{- if .Values.controller.admissionWebhooks.patch.priorityClassName }} - priorityClassName: {{ .Values.controller.admissionWebhooks.patch.priorityClassName }} - {{- end }} - {{- if .Values.imagePullSecrets }} - imagePullSecrets: {{ toYaml .Values.imagePullSecrets | nindent 8 }} - {{- end }} - containers: - - name: create - {{- with .Values.controller.admissionWebhooks.patch.image }} - image: "{{- if .repository -}}{{ .repository }}{{ else }}{{ .registry }}/{{ .image }}{{- end -}}:{{ .tag }}{{- if (.digest) -}} @{{.digest}} {{- end -}}" - {{- end }} - imagePullPolicy: {{ .Values.controller.admissionWebhooks.patch.image.pullPolicy }} - args: - - create - - --host={{ include "ingress-nginx.controller.fullname" . }}-admission,{{ include "ingress-nginx.controller.fullname" . }}-admission.$(POD_NAMESPACE).svc - - --namespace=$(POD_NAMESPACE) - - --secret-name={{ include "ingress-nginx.fullname" . }}-admission - env: - - name: POD_NAMESPACE - valueFrom: - fieldRef: - fieldPath: metadata.namespace - restartPolicy: OnFailure - serviceAccountName: {{ include "ingress-nginx.fullname" . }}-admission - {{- if .Values.controller.admissionWebhooks.patch.nodeSelector }} - nodeSelector: {{ toYaml .Values.controller.admissionWebhooks.patch.nodeSelector | nindent 8 }} - {{- end }} - {{- if .Values.controller.admissionWebhooks.patch.tolerations }} - tolerations: {{ toYaml .Values.controller.admissionWebhooks.patch.tolerations | nindent 8 }} - {{- end }} - securityContext: - runAsNonRoot: true - runAsUser: {{ .Values.controller.admissionWebhooks.patch.runAsUser }} -{{- end }} diff --git a/hosting/kubernetes/budibase/charts/ingress-nginx/templates/admission-webhooks/job-patch/job-patchWebhook.yaml b/hosting/kubernetes/budibase/charts/ingress-nginx/templates/admission-webhooks/job-patch/job-patchWebhook.yaml deleted file mode 100644 index 9e9bd0138d..0000000000 --- a/hosting/kubernetes/budibase/charts/ingress-nginx/templates/admission-webhooks/job-patch/job-patchWebhook.yaml +++ /dev/null @@ -1,63 +0,0 @@ -{{- if and .Values.controller.admissionWebhooks.enabled .Values.controller.admissionWebhooks.patch.enabled -}} -apiVersion: batch/v1 -kind: Job -metadata: - name: {{ include "ingress-nginx.fullname" . }}-admission-patch - namespace: {{ .Release.Namespace }} - annotations: - "helm.sh/hook": post-install,post-upgrade - "helm.sh/hook-delete-policy": before-hook-creation,hook-succeeded - labels: - {{- include "ingress-nginx.labels" . | nindent 4 }} - app.kubernetes.io/component: admission-webhook -spec: -{{- if .Capabilities.APIVersions.Has "batch/v1alpha1" }} - # Alpha feature since k8s 1.12 - ttlSecondsAfterFinished: 0 -{{- end }} - template: - metadata: - name: {{ include "ingress-nginx.fullname" . }}-admission-patch - {{- if .Values.controller.admissionWebhooks.patch.podAnnotations }} - annotations: {{ toYaml .Values.controller.admissionWebhooks.patch.podAnnotations | nindent 8 }} - {{- end }} - labels: - {{- include "ingress-nginx.labels" . | nindent 8 }} - app.kubernetes.io/component: admission-webhook - spec: - {{- if .Values.controller.admissionWebhooks.patch.priorityClassName }} - priorityClassName: {{ .Values.controller.admissionWebhooks.patch.priorityClassName }} - {{- end }} - {{- if .Values.imagePullSecrets }} - imagePullSecrets: {{ toYaml .Values.imagePullSecrets | nindent 8 }} - {{- end }} - containers: - - name: patch - {{- with .Values.controller.admissionWebhooks.patch.image }} - image: "{{- if .repository -}}{{ .repository }}{{ else }}{{ .registry }}/{{ .image }}{{- end -}}:{{ .tag }}{{- if (.digest) -}} @{{.digest}} {{- end -}}" - {{- end }} - imagePullPolicy: {{ .Values.controller.admissionWebhooks.patch.image.pullPolicy }} - args: - - patch - - --webhook-name={{ include "ingress-nginx.fullname" . }}-admission - - --namespace=$(POD_NAMESPACE) - - --patch-mutating=false - - --secret-name={{ include "ingress-nginx.fullname" . }}-admission - - --patch-failure-policy={{ .Values.controller.admissionWebhooks.failurePolicy }} - env: - - name: POD_NAMESPACE - valueFrom: - fieldRef: - fieldPath: metadata.namespace - restartPolicy: OnFailure - serviceAccountName: {{ include "ingress-nginx.fullname" . }}-admission - {{- if .Values.controller.admissionWebhooks.patch.nodeSelector }} - nodeSelector: {{ toYaml .Values.controller.admissionWebhooks.patch.nodeSelector | nindent 8 }} - {{- end }} - {{- if .Values.controller.admissionWebhooks.patch.tolerations }} - tolerations: {{ toYaml .Values.controller.admissionWebhooks.patch.tolerations | nindent 8 }} - {{- end }} - securityContext: - runAsNonRoot: true - runAsUser: {{ .Values.controller.admissionWebhooks.patch.runAsUser }} -{{- end }} diff --git a/hosting/kubernetes/budibase/charts/ingress-nginx/templates/admission-webhooks/job-patch/psp.yaml b/hosting/kubernetes/budibase/charts/ingress-nginx/templates/admission-webhooks/job-patch/psp.yaml deleted file mode 100644 index d2c7de6858..0000000000 --- a/hosting/kubernetes/budibase/charts/ingress-nginx/templates/admission-webhooks/job-patch/psp.yaml +++ /dev/null @@ -1,36 +0,0 @@ -{{- if and .Values.controller.admissionWebhooks.enabled .Values.controller.admissionWebhooks.patch.enabled .Values.podSecurityPolicy.enabled (empty .Values.controller.admissionWebhooks.existingPsp) -}} -apiVersion: policy/v1beta1 -kind: PodSecurityPolicy -metadata: - name: {{ include "ingress-nginx.fullname" . }}-admission - annotations: - "helm.sh/hook": pre-install,pre-upgrade,post-install,post-upgrade - "helm.sh/hook-delete-policy": before-hook-creation,hook-succeeded - labels: - {{- include "ingress-nginx.labels" . | nindent 4 }} - app.kubernetes.io/component: admission-webhook -spec: - allowPrivilegeEscalation: false - fsGroup: - ranges: - - max: 65535 - min: 1 - rule: MustRunAs - requiredDropCapabilities: - - ALL - runAsUser: - rule: MustRunAsNonRoot - seLinux: - rule: RunAsAny - supplementalGroups: - ranges: - - max: 65535 - min: 1 - rule: MustRunAs - volumes: - - configMap - - emptyDir - - projected - - secret - - downwardAPI -{{- end }} diff --git a/hosting/kubernetes/budibase/charts/ingress-nginx/templates/admission-webhooks/job-patch/role.yaml b/hosting/kubernetes/budibase/charts/ingress-nginx/templates/admission-webhooks/job-patch/role.yaml deleted file mode 100644 index 9b083ee6e3..0000000000 --- a/hosting/kubernetes/budibase/charts/ingress-nginx/templates/admission-webhooks/job-patch/role.yaml +++ /dev/null @@ -1,21 +0,0 @@ -{{- if and .Values.controller.admissionWebhooks.enabled .Values.controller.admissionWebhooks.patch.enabled -}} -apiVersion: rbac.authorization.k8s.io/v1 -kind: Role -metadata: - name: {{ include "ingress-nginx.fullname" . }}-admission - namespace: {{ .Release.Namespace }} - annotations: - "helm.sh/hook": pre-install,pre-upgrade,post-install,post-upgrade - "helm.sh/hook-delete-policy": before-hook-creation,hook-succeeded - labels: - {{- include "ingress-nginx.labels" . | nindent 4 }} - app.kubernetes.io/component: admission-webhook -rules: - - apiGroups: - - "" - resources: - - secrets - verbs: - - get - - create -{{- end }} diff --git a/hosting/kubernetes/budibase/charts/ingress-nginx/templates/admission-webhooks/job-patch/rolebinding.yaml b/hosting/kubernetes/budibase/charts/ingress-nginx/templates/admission-webhooks/job-patch/rolebinding.yaml deleted file mode 100644 index edda07f5d9..0000000000 --- a/hosting/kubernetes/budibase/charts/ingress-nginx/templates/admission-webhooks/job-patch/rolebinding.yaml +++ /dev/null @@ -1,21 +0,0 @@ -{{- if and .Values.controller.admissionWebhooks.enabled .Values.controller.admissionWebhooks.patch.enabled -}} -apiVersion: rbac.authorization.k8s.io/v1 -kind: RoleBinding -metadata: - name: {{ include "ingress-nginx.fullname" . }}-admission - namespace: {{ .Release.Namespace }} - annotations: - "helm.sh/hook": pre-install,pre-upgrade,post-install,post-upgrade - "helm.sh/hook-delete-policy": before-hook-creation,hook-succeeded - labels: - {{- include "ingress-nginx.labels" . | nindent 4 }} - app.kubernetes.io/component: admission-webhook -roleRef: - apiGroup: rbac.authorization.k8s.io - kind: Role - name: {{ include "ingress-nginx.fullname" . }}-admission -subjects: - - kind: ServiceAccount - name: {{ include "ingress-nginx.fullname" . }}-admission - namespace: {{ .Release.Namespace | quote }} -{{- end }} diff --git a/hosting/kubernetes/budibase/charts/ingress-nginx/templates/admission-webhooks/job-patch/serviceaccount.yaml b/hosting/kubernetes/budibase/charts/ingress-nginx/templates/admission-webhooks/job-patch/serviceaccount.yaml deleted file mode 100644 index 1ff0f7f0e5..0000000000 --- a/hosting/kubernetes/budibase/charts/ingress-nginx/templates/admission-webhooks/job-patch/serviceaccount.yaml +++ /dev/null @@ -1,13 +0,0 @@ -{{- if and .Values.controller.admissionWebhooks.enabled .Values.controller.admissionWebhooks.patch.enabled -}} -apiVersion: v1 -kind: ServiceAccount -metadata: - name: {{ include "ingress-nginx.fullname" . }}-admission - namespace: {{ .Release.Namespace }} - annotations: - "helm.sh/hook": pre-install,pre-upgrade,post-install,post-upgrade - "helm.sh/hook-delete-policy": before-hook-creation,hook-succeeded - labels: - {{- include "ingress-nginx.labels" . | nindent 4 }} - app.kubernetes.io/component: admission-webhook -{{- end }} diff --git a/hosting/kubernetes/budibase/charts/ingress-nginx/templates/admission-webhooks/validating-webhook.yaml b/hosting/kubernetes/budibase/charts/ingress-nginx/templates/admission-webhooks/validating-webhook.yaml deleted file mode 100644 index 2f3dd77848..0000000000 --- a/hosting/kubernetes/budibase/charts/ingress-nginx/templates/admission-webhooks/validating-webhook.yaml +++ /dev/null @@ -1,46 +0,0 @@ -{{- if .Values.controller.admissionWebhooks.enabled -}} -# before changing this value, check the required kubernetes version -# https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#prerequisites -apiVersion: admissionregistration.k8s.io/v1 -kind: ValidatingWebhookConfiguration -metadata: - {{- if .Values.controller.admissionWebhooks.annotations }} - annotations: {{ toYaml .Values.controller.admissionWebhooks.annotations | nindent 4 }} - {{- end }} - labels: - {{- include "ingress-nginx.labels" . | nindent 4 }} - app.kubernetes.io/component: admission-webhook - name: {{ include "ingress-nginx.fullname" . }}-admission -webhooks: - - name: validate.nginx.ingress.kubernetes.io - matchPolicy: Equivalent - rules: - - apiGroups: - - networking.k8s.io - apiVersions: - - v1beta1 - operations: - - CREATE - - UPDATE - resources: - - ingresses - failurePolicy: {{ .Values.controller.admissionWebhooks.failurePolicy | default "Fail" }} - sideEffects: None - admissionReviewVersions: - - v1 - - v1beta1 - clientConfig: - service: - namespace: {{ .Release.Namespace | quote }} - name: {{ include "ingress-nginx.controller.fullname" . }}-admission - path: /networking/v1beta1/ingresses - {{- if .Values.controller.admissionWebhooks.timeoutSeconds }} - timeoutSeconds: {{ .Values.controller.admissionWebhooks.timeoutSeconds }} - {{- end }} - {{- if .Values.controller.admissionWebhooks.namespaceSelector }} - namespaceSelector: {{ toYaml .Values.controller.admissionWebhooks.namespaceSelector | nindent 6 }} - {{- end }} - {{- if .Values.controller.admissionWebhooks.objectSelector }} - objectSelector: {{ toYaml .Values.controller.admissionWebhooks.objectSelector | nindent 6 }} - {{- end }} -{{- end }} diff --git a/hosting/kubernetes/budibase/charts/ingress-nginx/templates/clusterrole.yaml b/hosting/kubernetes/budibase/charts/ingress-nginx/templates/clusterrole.yaml deleted file mode 100644 index 8ec5f49fa4..0000000000 --- a/hosting/kubernetes/budibase/charts/ingress-nginx/templates/clusterrole.yaml +++ /dev/null @@ -1,75 +0,0 @@ -{{- if and .Values.rbac.create (not .Values.rbac.scope) -}} -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRole -metadata: - labels: - {{- include "ingress-nginx.labels" . | nindent 4 }} - name: {{ include "ingress-nginx.fullname" . }} -rules: - - apiGroups: - - "" - resources: - - configmaps - - endpoints - - nodes - - pods - - secrets - verbs: - - list - - watch -{{- if and .Values.controller.scope.enabled .Values.controller.scope.namespace }} - - apiGroups: - - "" - resources: - - namespaces - resourceNames: - - "{{ .Values.controller.scope.namespace }}" - verbs: - - get -{{- end }} - - apiGroups: - - "" - resources: - - nodes - verbs: - - get - - apiGroups: - - "" - resources: - - services - verbs: - - get - - list - - watch - - apiGroups: - - extensions - - "networking.k8s.io" # k8s 1.14+ - resources: - - ingresses - verbs: - - get - - list - - watch - - apiGroups: - - "" - resources: - - events - verbs: - - create - - patch - - apiGroups: - - extensions - - "networking.k8s.io" # k8s 1.14+ - resources: - - ingresses/status - verbs: - - update - - apiGroups: - - "networking.k8s.io" # k8s 1.14+ - resources: - - ingressclasses - verbs: - - get - - list - - watch -{{- end }} diff --git a/hosting/kubernetes/budibase/charts/ingress-nginx/templates/clusterrolebinding.yaml b/hosting/kubernetes/budibase/charts/ingress-nginx/templates/clusterrolebinding.yaml deleted file mode 100644 index 81be52b87d..0000000000 --- a/hosting/kubernetes/budibase/charts/ingress-nginx/templates/clusterrolebinding.yaml +++ /dev/null @@ -1,16 +0,0 @@ -{{- if and .Values.rbac.create (not .Values.rbac.scope) -}} -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRoleBinding -metadata: - labels: - {{- include "ingress-nginx.labels" . | nindent 4 }} - name: {{ include "ingress-nginx.fullname" . }} -roleRef: - apiGroup: rbac.authorization.k8s.io - kind: ClusterRole - name: {{ include "ingress-nginx.fullname" . }} -subjects: - - kind: ServiceAccount - name: {{ template "ingress-nginx.serviceAccountName" . }} - namespace: {{ .Release.Namespace | quote }} -{{- end }} diff --git a/hosting/kubernetes/budibase/charts/ingress-nginx/templates/controller-configmap-addheaders.yaml b/hosting/kubernetes/budibase/charts/ingress-nginx/templates/controller-configmap-addheaders.yaml deleted file mode 100644 index e0b7a0f21a..0000000000 --- a/hosting/kubernetes/budibase/charts/ingress-nginx/templates/controller-configmap-addheaders.yaml +++ /dev/null @@ -1,11 +0,0 @@ -{{- if .Values.controller.addHeaders -}} -apiVersion: v1 -kind: ConfigMap -metadata: - labels: - {{- include "ingress-nginx.labels" . | nindent 4 }} - app.kubernetes.io/component: controller - name: {{ include "ingress-nginx.fullname" . }}-custom-add-headers - namespace: {{ .Release.Namespace }} -data: {{ toYaml .Values.controller.addHeaders | nindent 2 }} -{{- end }} diff --git a/hosting/kubernetes/budibase/charts/ingress-nginx/templates/controller-configmap-proxyheaders.yaml b/hosting/kubernetes/budibase/charts/ingress-nginx/templates/controller-configmap-proxyheaders.yaml deleted file mode 100644 index 91f22f03d9..0000000000 --- a/hosting/kubernetes/budibase/charts/ingress-nginx/templates/controller-configmap-proxyheaders.yaml +++ /dev/null @@ -1,16 +0,0 @@ -{{- if or .Values.controller.proxySetHeaders .Values.controller.headers -}} -apiVersion: v1 -kind: ConfigMap -metadata: - labels: - {{- include "ingress-nginx.labels" . | nindent 4 }} - app.kubernetes.io/component: controller - name: {{ include "ingress-nginx.fullname" . }}-custom-proxy-headers - namespace: {{ .Release.Namespace }} -data: -{{- if .Values.controller.proxySetHeaders }} -{{ toYaml .Values.controller.proxySetHeaders | indent 2 }} -{{ else if and .Values.controller.headers (not .Values.controller.proxySetHeaders) }} -{{ toYaml .Values.controller.headers | indent 2 }} -{{- end }} -{{- end }} diff --git a/hosting/kubernetes/budibase/charts/ingress-nginx/templates/controller-configmap-tcp.yaml b/hosting/kubernetes/budibase/charts/ingress-nginx/templates/controller-configmap-tcp.yaml deleted file mode 100644 index aaf336fb30..0000000000 --- a/hosting/kubernetes/budibase/charts/ingress-nginx/templates/controller-configmap-tcp.yaml +++ /dev/null @@ -1,14 +0,0 @@ -{{- if .Values.tcp -}} -apiVersion: v1 -kind: ConfigMap -metadata: - labels: - {{- include "ingress-nginx.labels" . | nindent 4 }} - app.kubernetes.io/component: controller -{{- if .Values.controller.tcp.annotations }} - annotations: {{ toYaml .Values.controller.tcp.annotations | nindent 4 }} -{{- end }} - name: {{ include "ingress-nginx.fullname" . }}-tcp - namespace: {{ .Release.Namespace }} -data: {{ tpl (toYaml .Values.tcp) . | nindent 2 }} -{{- end }} diff --git a/hosting/kubernetes/budibase/charts/ingress-nginx/templates/controller-configmap-udp.yaml b/hosting/kubernetes/budibase/charts/ingress-nginx/templates/controller-configmap-udp.yaml deleted file mode 100644 index 7f46791ecb..0000000000 --- a/hosting/kubernetes/budibase/charts/ingress-nginx/templates/controller-configmap-udp.yaml +++ /dev/null @@ -1,14 +0,0 @@ -{{- if .Values.udp -}} -apiVersion: v1 -kind: ConfigMap -metadata: - labels: - {{- include "ingress-nginx.labels" . | nindent 4 }} - app.kubernetes.io/component: controller -{{- if .Values.controller.udp.annotations }} - annotations: {{ toYaml .Values.controller.udp.annotations | nindent 4 }} -{{- end }} - name: {{ include "ingress-nginx.fullname" . }}-udp - namespace: {{ .Release.Namespace }} -data: {{ tpl (toYaml .Values.udp) . | nindent 2 }} -{{- end }} diff --git a/hosting/kubernetes/budibase/charts/ingress-nginx/templates/controller-configmap.yaml b/hosting/kubernetes/budibase/charts/ingress-nginx/templates/controller-configmap.yaml deleted file mode 100644 index 630545140e..0000000000 --- a/hosting/kubernetes/budibase/charts/ingress-nginx/templates/controller-configmap.yaml +++ /dev/null @@ -1,25 +0,0 @@ -apiVersion: v1 -kind: ConfigMap -metadata: - labels: - {{- include "ingress-nginx.labels" . | nindent 4 }} - app.kubernetes.io/component: controller -{{- if .Values.controller.configAnnotations }} - annotations: {{ toYaml .Values.controller.configAnnotations | nindent 4 }} -{{- end }} - name: {{ include "ingress-nginx.controller.fullname" . }} - namespace: {{ .Release.Namespace }} -data: -{{- if .Values.controller.addHeaders }} - add-headers: {{ .Release.Namespace }}/{{ include "ingress-nginx.fullname" . }}-custom-add-headers -{{- end }} -{{- if or .Values.controller.proxySetHeaders .Values.controller.headers }} - proxy-set-headers: {{ .Release.Namespace }}/{{ include "ingress-nginx.fullname" . }}-custom-proxy-headers -{{- end }} -{{- if .Values.dhParam }} - ssl-dh-param: {{ printf "%s/%s" .Release.Namespace (include "ingress-nginx.controller.fullname" .) }} -{{- end }} -{{- range $key, $value := .Values.controller.config }} - {{ $key | nindent 2 }}: {{ $value | quote }} -{{- end }} - diff --git a/hosting/kubernetes/budibase/charts/ingress-nginx/templates/controller-daemonset.yaml b/hosting/kubernetes/budibase/charts/ingress-nginx/templates/controller-daemonset.yaml deleted file mode 100644 index 2f6def5897..0000000000 --- a/hosting/kubernetes/budibase/charts/ingress-nginx/templates/controller-daemonset.yaml +++ /dev/null @@ -1,244 +0,0 @@ -{{- if or (eq .Values.controller.kind "DaemonSet") (eq .Values.controller.kind "Both") -}} -{{- include "isControllerTagValid" . -}} -apiVersion: apps/v1 -kind: DaemonSet -metadata: - labels: - {{- include "ingress-nginx.labels" . | nindent 4 }} - app.kubernetes.io/component: controller - {{- with .Values.controller.labels }} - {{- toYaml . | nindent 4 }} - {{- end }} - name: {{ include "ingress-nginx.controller.fullname" . }} - namespace: {{ .Release.Namespace }} - {{- if .Values.controller.annotations }} - annotations: {{ toYaml .Values.controller.annotations | nindent 4 }} - {{- end }} -spec: - selector: - matchLabels: - {{- include "ingress-nginx.selectorLabels" . | nindent 6 }} - app.kubernetes.io/component: controller - revisionHistoryLimit: {{ .Values.revisionHistoryLimit }} - {{- if .Values.controller.updateStrategy }} - updateStrategy: {{ toYaml .Values.controller.updateStrategy | nindent 4 }} - {{- end }} - minReadySeconds: {{ .Values.controller.minReadySeconds }} - template: - metadata: - {{- if .Values.controller.podAnnotations }} - annotations: - {{- range $key, $value := .Values.controller.podAnnotations }} - {{ $key }}: {{ $value | quote }} - {{- end }} - {{- end }} - labels: - {{- include "ingress-nginx.selectorLabels" . | nindent 8 }} - app.kubernetes.io/component: controller - {{- if .Values.controller.podLabels }} - {{- toYaml .Values.controller.podLabels | nindent 8 }} - {{- end }} - spec: - {{- if .Values.controller.dnsConfig }} - dnsConfig: {{ toYaml .Values.controller.dnsConfig | nindent 8 }} - {{- end }} - dnsPolicy: {{ .Values.controller.dnsPolicy }} - {{- if .Values.imagePullSecrets }} - imagePullSecrets: {{ toYaml .Values.imagePullSecrets | nindent 8 }} - {{- end }} - {{- if .Values.controller.priorityClassName }} - priorityClassName: {{ .Values.controller.priorityClassName }} - {{- end }} - {{- if or .Values.controller.podSecurityContext .Values.controller.sysctls }} - securityContext: - {{- end }} - {{- if .Values.controller.podSecurityContext }} - {{- toYaml .Values.controller.podSecurityContext | nindent 8 }} - {{- end }} - {{- if .Values.controller.sysctls }} - sysctls: - {{- range $sysctl, $value := .Values.controller.sysctls }} - - name: {{ $sysctl | quote }} - value: {{ $value | quote }} - {{- end }} - {{- end }} - containers: - - name: {{ .Values.controller.containerName }} - {{- with .Values.controller.image }} - image: "{{- if .repository -}}{{ .repository }}{{ else }}{{ .registry }}/{{ .image }}{{- end -}}:{{ .tag }}{{- if (.digest) -}} @{{.digest}} {{- end -}}" - {{- end }} - imagePullPolicy: {{ .Values.controller.image.pullPolicy }} - {{- if .Values.controller.lifecycle }} - lifecycle: {{ toYaml .Values.controller.lifecycle | nindent 12 }} - {{- end }} - args: - - /nginx-ingress-controller - {{- if .Values.defaultBackend.enabled }} - - --default-backend-service={{ .Release.Namespace }}/{{ include "ingress-nginx.defaultBackend.fullname" . }} - {{- end }} - {{- if .Values.controller.publishService.enabled }} - - --publish-service={{ template "ingress-nginx.controller.publishServicePath" . }} - {{- end }} - - --election-id={{ .Values.controller.electionID }} - - --ingress-class={{ .Values.controller.ingressClass }} - - --configmap={{ .Release.Namespace }}/{{ include "ingress-nginx.controller.fullname" . }} - {{- if .Values.tcp }} - - --tcp-services-configmap={{ .Release.Namespace }}/{{ include "ingress-nginx.fullname" . }}-tcp - {{- end }} - {{- if .Values.udp }} - - --udp-services-configmap={{ .Release.Namespace }}/{{ include "ingress-nginx.fullname" . }}-udp - {{- end }} - {{- if .Values.controller.scope.enabled }} - - --watch-namespace={{ default .Release.Namespace .Values.controller.scope.namespace }} - {{- end }} - {{- if and .Values.controller.reportNodeInternalIp .Values.controller.hostNetwork }} - - --report-node-internal-ip-address={{ .Values.controller.reportNodeInternalIp }} - {{- end }} - {{- if .Values.controller.admissionWebhooks.enabled }} - - --validating-webhook=:{{ .Values.controller.admissionWebhooks.port }} - - --validating-webhook-certificate={{ .Values.controller.admissionWebhooks.certificate }} - - --validating-webhook-key={{ .Values.controller.admissionWebhooks.key }} - {{- end }} - {{- if .Values.controller.maxmindMirror }} - - --maxmind-mirror={{ .Values.controller.maxmindMirror }} - {{- end}} - {{- if .Values.controller.maxmindLicenseKey }} - - --maxmind-license-key={{ .Values.controller.maxmindLicenseKey }} - {{- end }} - {{- if not (eq .Values.controller.healthCheckPath "/healthz") }} - - --health-check-path={{ .Values.controller.healthCheckPath }} - {{- end }} - {{- range $key, $value := .Values.controller.extraArgs }} - {{- /* Accept keys without values or with false as value */}} - {{- if eq ($value | quote | len) 2 }} - - --{{ $key }} - {{- else }} - - --{{ $key }}={{ $value }} - {{- end }} - {{- end }} - securityContext: - capabilities: - drop: - - ALL - add: - - NET_BIND_SERVICE - runAsUser: {{ .Values.controller.image.runAsUser }} - allowPrivilegeEscalation: {{ .Values.controller.image.allowPrivilegeEscalation }} - env: - - name: POD_NAME - valueFrom: - fieldRef: - fieldPath: metadata.name - - name: POD_NAMESPACE - valueFrom: - fieldRef: - fieldPath: metadata.namespace - {{- if .Values.controller.enableMimalloc }} - - name: LD_PRELOAD - value: /usr/local/lib/libmimalloc.so - {{- end }} - {{- if .Values.controller.extraEnvs }} - {{- toYaml .Values.controller.extraEnvs | nindent 12 }} - {{- end }} - {{- if .Values.controller.startupProbe }} - startupProbe: {{ toYaml .Values.controller.startupProbe | nindent 12 }} - {{- end }} - livenessProbe: {{ toYaml .Values.controller.livenessProbe | nindent 12 }} - readinessProbe: {{ toYaml .Values.controller.readinessProbe | nindent 12 }} - ports: - {{- range $key, $value := .Values.controller.containerPort }} - - name: {{ $key }} - containerPort: {{ $value }} - protocol: TCP - {{- if $.Values.controller.hostPort.enabled }} - hostPort: {{ index $.Values.controller.hostPort.ports $key | default $value }} - {{- end }} - {{- end }} - {{- if .Values.controller.metrics.enabled }} - - name: metrics - containerPort: {{ .Values.controller.metrics.port }} - protocol: TCP - {{- end }} - {{- if .Values.controller.admissionWebhooks.enabled }} - - name: webhook - containerPort: {{ .Values.controller.admissionWebhooks.port }} - protocol: TCP - {{- end }} - {{- range $key, $value := .Values.tcp }} - - name: {{ $key }}-tcp - containerPort: {{ $key }} - protocol: TCP - {{- if $.Values.controller.hostPort.enabled }} - hostPort: {{ $key }} - {{- end }} - {{- end }} - {{- range $key, $value := .Values.udp }} - - name: {{ $key }}-udp - containerPort: {{ $key }} - protocol: UDP - {{- if $.Values.controller.hostPort.enabled }} - hostPort: {{ $key }} - {{- end }} - {{- end }} - {{- if (or .Values.controller.customTemplate.configMapName .Values.controller.extraVolumeMounts .Values.controller.admissionWebhooks.enabled) }} - volumeMounts: - {{- if .Values.controller.customTemplate.configMapName }} - - mountPath: /etc/nginx/template - name: nginx-template-volume - readOnly: true - {{- end }} - {{- if .Values.controller.admissionWebhooks.enabled }} - - name: webhook-cert - mountPath: /usr/local/certificates/ - readOnly: true - {{- end }} - {{- if .Values.controller.extraVolumeMounts }} - {{- toYaml .Values.controller.extraVolumeMounts | nindent 12 }} - {{- end }} - {{- end }} - {{- if .Values.controller.resources }} - resources: {{ toYaml .Values.controller.resources | nindent 12 }} - {{- end }} - {{- if .Values.controller.extraContainers }} - {{ toYaml .Values.controller.extraContainers | nindent 8 }} - {{- end }} - {{- if .Values.controller.extraInitContainers }} - initContainers: {{ toYaml .Values.controller.extraInitContainers | nindent 8 }} - {{- end }} - {{- if .Values.controller.hostNetwork }} - hostNetwork: {{ .Values.controller.hostNetwork }} - {{- end }} - {{- if .Values.controller.nodeSelector }} - nodeSelector: {{ toYaml .Values.controller.nodeSelector | nindent 8 }} - {{- end }} - {{- if .Values.controller.tolerations }} - tolerations: {{ toYaml .Values.controller.tolerations | nindent 8 }} - {{- end }} - {{- if .Values.controller.affinity }} - affinity: {{ toYaml .Values.controller.affinity | nindent 8 }} - {{- end }} - {{- if .Values.controller.topologySpreadConstraints }} - topologySpreadConstraints: {{ toYaml .Values.controller.topologySpreadConstraints | nindent 8 }} - {{- end }} - serviceAccountName: {{ template "ingress-nginx.serviceAccountName" . }} - terminationGracePeriodSeconds: {{ .Values.controller.terminationGracePeriodSeconds }} - {{- if (or .Values.controller.customTemplate.configMapName .Values.controller.extraVolumeMounts .Values.controller.admissionWebhooks.enabled .Values.controller.extraVolumes) }} - volumes: - {{- if .Values.controller.customTemplate.configMapName }} - - name: nginx-template-volume - configMap: - name: {{ .Values.controller.customTemplate.configMapName }} - items: - - key: {{ .Values.controller.customTemplate.configMapKey }} - path: nginx.tmpl - {{- end }} - {{- if .Values.controller.admissionWebhooks.enabled }} - - name: webhook-cert - secret: - secretName: {{ include "ingress-nginx.fullname" . }}-admission - {{- end }} - {{- if .Values.controller.extraVolumes }} - {{ toYaml .Values.controller.extraVolumes | nindent 8 }} - {{- end }} - {{- end }} -{{- end }} diff --git a/hosting/kubernetes/budibase/charts/ingress-nginx/templates/controller-deployment.yaml b/hosting/kubernetes/budibase/charts/ingress-nginx/templates/controller-deployment.yaml deleted file mode 100644 index 7e2d223a99..0000000000 --- a/hosting/kubernetes/budibase/charts/ingress-nginx/templates/controller-deployment.yaml +++ /dev/null @@ -1,245 +0,0 @@ -{{- if or (eq .Values.controller.kind "Deployment") (eq .Values.controller.kind "Both") -}} -{{- include "isControllerTagValid" . -}} -apiVersion: apps/v1 -kind: Deployment -metadata: - labels: - {{- include "ingress-nginx.labels" . | nindent 4 }} - app.kubernetes.io/component: controller - {{- with .Values.controller.labels }} - {{- toYaml . | nindent 4 }} - {{- end }} - name: {{ include "ingress-nginx.controller.fullname" . }} - namespace: {{ .Release.Namespace }} - {{- if .Values.controller.annotations }} - annotations: {{ toYaml .Values.controller.annotations | nindent 4 }} - {{- end }} -spec: - selector: - matchLabels: - {{- include "ingress-nginx.selectorLabels" . | nindent 6 }} - app.kubernetes.io/component: controller - {{- if not .Values.controller.autoscaling.enabled }} - replicas: {{ .Values.controller.replicaCount }} - {{- end }} - revisionHistoryLimit: {{ .Values.revisionHistoryLimit }} - {{- if .Values.controller.updateStrategy }} - strategy: - {{ toYaml .Values.controller.updateStrategy | nindent 4 }} - {{- end }} - minReadySeconds: {{ .Values.controller.minReadySeconds }} - template: - metadata: - {{- if .Values.controller.podAnnotations }} - annotations: - {{- range $key, $value := .Values.controller.podAnnotations }} - {{ $key }}: {{ $value | quote }} - {{- end }} - {{- end }} - labels: - {{- include "ingress-nginx.selectorLabels" . | nindent 8 }} - app.kubernetes.io/component: controller - {{- if .Values.controller.podLabels }} - {{- toYaml .Values.controller.podLabels | nindent 8 }} - {{- end }} - spec: - {{- if .Values.controller.dnsConfig }} - dnsConfig: {{ toYaml .Values.controller.dnsConfig | nindent 8 }} - {{- end }} - dnsPolicy: {{ .Values.controller.dnsPolicy }} - {{- if .Values.imagePullSecrets }} - imagePullSecrets: {{ toYaml .Values.imagePullSecrets | nindent 8 }} - {{- end }} - {{- if .Values.controller.priorityClassName }} - priorityClassName: {{ .Values.controller.priorityClassName }} - {{- end }} - {{- if or .Values.controller.podSecurityContext .Values.controller.sysctls }} - securityContext: - {{- end }} - {{- if .Values.controller.podSecurityContext }} - {{- toYaml .Values.controller.podSecurityContext | nindent 8 }} - {{- end }} - {{- if .Values.controller.sysctls }} - sysctls: - {{- range $sysctl, $value := .Values.controller.sysctls }} - - name: {{ $sysctl | quote }} - value: {{ $value | quote }} - {{- end }} - {{- end }} - containers: - - name: {{ .Values.controller.containerName }} - {{- with .Values.controller.image }} - image: "{{- if .repository -}}{{ .repository }}{{ else }}{{ .registry }}/{{ .image }}{{- end -}}:{{ .tag }}{{- if (.digest) -}} @{{.digest}} {{- end -}}" - {{- end }} - imagePullPolicy: {{ .Values.controller.image.pullPolicy }} - {{- if .Values.controller.lifecycle }} - lifecycle: {{ toYaml .Values.controller.lifecycle | nindent 12 }} - {{- end }} - args: - - /nginx-ingress-controller - {{- if .Values.defaultBackend.enabled }} - - --default-backend-service=$(POD_NAMESPACE)/{{ include "ingress-nginx.defaultBackend.fullname" . }} - {{- end }} - {{- if .Values.controller.publishService.enabled }} - - --publish-service={{ template "ingress-nginx.controller.publishServicePath" . }} - {{- end }} - - --election-id={{ .Values.controller.electionID }} - - --ingress-class={{ .Values.controller.ingressClass }} - - --configmap=$(POD_NAMESPACE)/{{ include "ingress-nginx.controller.fullname" . }} - {{- if .Values.tcp }} - - --tcp-services-configmap=$(POD_NAMESPACE)/{{ include "ingress-nginx.fullname" . }}-tcp - {{- end }} - {{- if .Values.udp }} - - --udp-services-configmap=$(POD_NAMESPACE)/{{ include "ingress-nginx.fullname" . }}-udp - {{- end }} - {{- if .Values.controller.scope.enabled }} - - --watch-namespace={{ default "$(POD_NAMESPACE)" .Values.controller.scope.namespace }} - {{- end }} - {{- if and .Values.controller.reportNodeInternalIp .Values.controller.hostNetwork }} - - --report-node-internal-ip-address={{ .Values.controller.reportNodeInternalIp }} - {{- end }} - {{- if .Values.controller.admissionWebhooks.enabled }} - - --validating-webhook=:{{ .Values.controller.admissionWebhooks.port }} - - --validating-webhook-certificate={{ .Values.controller.admissionWebhooks.certificate }} - - --validating-webhook-key={{ .Values.controller.admissionWebhooks.key }} - {{- end }} - {{- if .Values.controller.maxmindLicenseKey }} - - --maxmind-license-key={{ .Values.controller.maxmindLicenseKey }} - {{- end }} - {{- if not (eq .Values.controller.healthCheckPath "/healthz") }} - - --health-check-path={{ .Values.controller.healthCheckPath }} - {{- end }} - {{- range $key, $value := .Values.controller.extraArgs }} - {{- /* Accept keys without values or with false as value */}} - {{- if eq ($value | quote | len) 2 }} - - --{{ $key }} - {{- else }} - - --{{ $key }}={{ $value }} - {{- end }} - {{- end }} - securityContext: - capabilities: - drop: - - ALL - add: - - NET_BIND_SERVICE - runAsUser: {{ .Values.controller.image.runAsUser }} - allowPrivilegeEscalation: {{ .Values.controller.image.allowPrivilegeEscalation }} - env: - - name: POD_NAME - valueFrom: - fieldRef: - fieldPath: metadata.name - - name: POD_NAMESPACE - valueFrom: - fieldRef: - fieldPath: metadata.namespace - {{- if .Values.controller.enableMimalloc }} - - name: LD_PRELOAD - value: /usr/local/lib/libmimalloc.so - {{- end }} - {{- if .Values.controller.extraEnvs }} - {{- toYaml .Values.controller.extraEnvs | nindent 12 }} - {{- end }} - {{- if .Values.controller.startupProbe }} - startupProbe: {{ toYaml .Values.controller.startupProbe | nindent 12 }} - {{- end }} - livenessProbe: {{ toYaml .Values.controller.livenessProbe | nindent 12 }} - readinessProbe: {{ toYaml .Values.controller.readinessProbe | nindent 12 }} - ports: - {{- range $key, $value := .Values.controller.containerPort }} - - name: {{ $key }} - containerPort: {{ $value }} - protocol: TCP - {{- if $.Values.controller.hostPort.enabled }} - hostPort: {{ index $.Values.controller.hostPort.ports $key | default $value }} - {{- end }} - {{- end }} - {{- if .Values.controller.metrics.enabled }} - - name: metrics - containerPort: {{ .Values.controller.metrics.port }} - protocol: TCP - {{- end }} - {{- if .Values.controller.admissionWebhooks.enabled }} - - name: webhook - containerPort: {{ .Values.controller.admissionWebhooks.port }} - protocol: TCP - {{- end }} - {{- range $key, $value := .Values.tcp }} - - name: {{ $key }}-tcp - containerPort: {{ $key }} - protocol: TCP - {{- if $.Values.controller.hostPort.enabled }} - hostPort: {{ $key }} - {{- end }} - {{- end }} - {{- range $key, $value := .Values.udp }} - - name: {{ $key }}-udp - containerPort: {{ $key }} - protocol: UDP - {{- if $.Values.controller.hostPort.enabled }} - hostPort: {{ $key }} - {{- end }} - {{- end }} - {{- if (or .Values.controller.customTemplate.configMapName .Values.controller.extraVolumeMounts .Values.controller.admissionWebhooks.enabled) }} - volumeMounts: - {{- if .Values.controller.customTemplate.configMapName }} - - mountPath: /etc/nginx/template - name: nginx-template-volume - readOnly: true - {{- end }} - {{- if .Values.controller.admissionWebhooks.enabled }} - - name: webhook-cert - mountPath: /usr/local/certificates/ - readOnly: true - {{- end }} - {{- if .Values.controller.extraVolumeMounts }} - {{- toYaml .Values.controller.extraVolumeMounts | nindent 12 }} - {{- end }} - {{- end }} - {{- if .Values.controller.resources }} - resources: {{ toYaml .Values.controller.resources | nindent 12 }} - {{- end }} - {{- if .Values.controller.extraContainers }} - {{ toYaml .Values.controller.extraContainers | nindent 8 }} - {{- end }} - {{- if .Values.controller.extraInitContainers }} - initContainers: {{ toYaml .Values.controller.extraInitContainers | nindent 8 }} - {{- end }} - {{- if .Values.controller.hostNetwork }} - hostNetwork: {{ .Values.controller.hostNetwork }} - {{- end }} - {{- if .Values.controller.nodeSelector }} - nodeSelector: {{ toYaml .Values.controller.nodeSelector | nindent 8 }} - {{- end }} - {{- if .Values.controller.tolerations }} - tolerations: {{ toYaml .Values.controller.tolerations | nindent 8 }} - {{- end }} - {{- if .Values.controller.affinity }} - affinity: {{ toYaml .Values.controller.affinity | nindent 8 }} - {{- end }} - {{- if .Values.controller.topologySpreadConstraints }} - topologySpreadConstraints: {{ toYaml .Values.controller.topologySpreadConstraints | nindent 8 }} - {{- end }} - serviceAccountName: {{ template "ingress-nginx.serviceAccountName" . }} - terminationGracePeriodSeconds: {{ .Values.controller.terminationGracePeriodSeconds }} - {{- if (or .Values.controller.customTemplate.configMapName .Values.controller.extraVolumeMounts .Values.controller.admissionWebhooks.enabled .Values.controller.extraVolumes) }} - volumes: - {{- if .Values.controller.customTemplate.configMapName }} - - name: nginx-template-volume - configMap: - name: {{ .Values.controller.customTemplate.configMapName }} - items: - - key: {{ .Values.controller.customTemplate.configMapKey }} - path: nginx.tmpl - {{- end }} - {{- if .Values.controller.admissionWebhooks.enabled }} - - name: webhook-cert - secret: - secretName: {{ include "ingress-nginx.fullname" . }}-admission - {{- end }} - {{- if .Values.controller.extraVolumes }} - {{ toYaml .Values.controller.extraVolumes | nindent 8 }} - {{- end }} - {{- end }} -{{- end }} diff --git a/hosting/kubernetes/budibase/charts/ingress-nginx/templates/controller-hpa.yaml b/hosting/kubernetes/budibase/charts/ingress-nginx/templates/controller-hpa.yaml deleted file mode 100644 index fb14bdf6a7..0000000000 --- a/hosting/kubernetes/budibase/charts/ingress-nginx/templates/controller-hpa.yaml +++ /dev/null @@ -1,45 +0,0 @@ -{{- if and .Values.controller.autoscaling.enabled (or (eq .Values.controller.kind "Deployment") (eq .Values.controller.kind "Both")) -}} -{{- if not .Values.controller.keda.enabled }} - -apiVersion: autoscaling/v2beta2 -kind: HorizontalPodAutoscaler -metadata: - annotations: - {{- with .Values.controller.autoscaling.annotations }} - {{- toYaml . | trimSuffix "\n" | nindent 4 }} - {{- end }} - labels: - {{- include "ingress-nginx.labels" . | nindent 4 }} - app.kubernetes.io/component: controller - name: {{ include "ingress-nginx.controller.fullname" . }} - namespace: {{ .Release.Namespace }} -spec: - scaleTargetRef: - apiVersion: apps/v1 - kind: Deployment - name: {{ include "ingress-nginx.controller.fullname" . }} - minReplicas: {{ .Values.controller.autoscaling.minReplicas }} - maxReplicas: {{ .Values.controller.autoscaling.maxReplicas }} - metrics: - {{- with .Values.controller.autoscaling.targetMemoryUtilizationPercentage }} - - type: Resource - resource: - name: memory - target: - type: Utilization - averageUtilization: {{ . }} - {{- end }} - {{- with .Values.controller.autoscaling.targetCPUUtilizationPercentage }} - - type: Resource - resource: - name: cpu - target: - type: Utilization - averageUtilization: {{ . }} - {{- end }} - {{- with .Values.controller.autoscalingTemplate }} -{{- toYaml . | nindent 2 }} - {{- end }} -{{- end }} -{{- end }} - diff --git a/hosting/kubernetes/budibase/charts/ingress-nginx/templates/controller-ingressclass.yaml b/hosting/kubernetes/budibase/charts/ingress-nginx/templates/controller-ingressclass.yaml deleted file mode 100644 index f94b9590de..0000000000 --- a/hosting/kubernetes/budibase/charts/ingress-nginx/templates/controller-ingressclass.yaml +++ /dev/null @@ -1,23 +0,0 @@ -{{- if and (semverCompare ">=1.18-0" .Capabilities.KubeVersion.GitVersion) (.Values.controller.ingressClassResource.enabled) -}} -{{- if and (semverCompare "=1.18-0" .Capabilities.KubeVersion.GitVersion) }} -apiVersion: networking.k8s.io/v1beta1 -{{- else }} -apiVersion: networking.k8s.io/v1 -{{- end }} -kind: IngressClass -metadata: - labels: - {{- include "ingress-nginx.labels" . | nindent 4 }} - app.kubernetes.io/component: controller - {{- with .Values.controller.labels }} - {{- toYaml . | nindent 4 }} - {{- end }} - name: {{ .Values.controller.ingressClass }} -{{- if .Values.controller.ingressClassResource.default }} - annotations: - ingressclass.kubernetes.io/is-default-class: "true" -{{- end }} -spec: - controller: k8s.io/ingress-nginx - {{ template "ingressClass.parameters" . }} -{{- end }} diff --git a/hosting/kubernetes/budibase/charts/ingress-nginx/templates/controller-keda.yaml b/hosting/kubernetes/budibase/charts/ingress-nginx/templates/controller-keda.yaml deleted file mode 100644 index c7eebf5c86..0000000000 --- a/hosting/kubernetes/budibase/charts/ingress-nginx/templates/controller-keda.yaml +++ /dev/null @@ -1,39 +0,0 @@ -{{- if and .Values.controller.keda.enabled (or (eq .Values.controller.kind "Deployment") (eq .Values.controller.kind "Both")) -}} -# https://keda.sh/docs/ - -apiVersion: {{ .Values.controller.keda.apiVersion }} -kind: ScaledObject -metadata: - labels: - {{- include "ingress-nginx.labels" . | nindent 4 }} - app.kubernetes.io/component: controller - name: {{ include "ingress-nginx.controller.fullname" . }} - {{- if .Values.controller.keda.scaledObject.annotations }} - annotations: {{ toYaml .Values.controller.keda.scaledObject.annotations | nindent 4 }} - {{- end }} -spec: - scaleTargetRef: -{{- if eq .Values.controller.keda.apiVersion "keda.k8s.io/v1alpha1" }} - deploymentName: {{ include "ingress-nginx.controller.fullname" . }} -{{- else if eq .Values.controller.keda.apiVersion "keda.sh/v1alpha1" }} - name: {{ include "ingress-nginx.controller.fullname" . }} -{{- end }} - pollingInterval: {{ .Values.controller.keda.pollingInterval }} - cooldownPeriod: {{ .Values.controller.keda.cooldownPeriod }} - minReplicaCount: {{ .Values.controller.keda.minReplicas }} - maxReplicaCount: {{ .Values.controller.keda.maxReplicas }} - triggers: -{{- with .Values.controller.keda.triggers }} -{{ toYaml . | indent 2 }} -{{ end }} - advanced: - restoreToOriginalReplicaCount: {{ .Values.controller.keda.restoreToOriginalReplicaCount }} -{{- if .Values.controller.keda.behavior }} - horizontalPodAutoscalerConfig: - behavior: -{{ with .Values.controller.keda.behavior -}} -{{ toYaml . | indent 8 }} -{{ end }} - -{{- end }} -{{- end }} diff --git a/hosting/kubernetes/budibase/charts/ingress-nginx/templates/controller-poddisruptionbudget.yaml b/hosting/kubernetes/budibase/charts/ingress-nginx/templates/controller-poddisruptionbudget.yaml deleted file mode 100644 index a5a425f74b..0000000000 --- a/hosting/kubernetes/budibase/charts/ingress-nginx/templates/controller-poddisruptionbudget.yaml +++ /dev/null @@ -1,16 +0,0 @@ -{{- if or (and .Values.controller.autoscaling.enabled (gt (.Values.controller.autoscaling.minReplicas | int) 1)) (and (not .Values.controller.autoscaling.enabled) (gt (.Values.controller.replicaCount | int) 1)) }} -apiVersion: policy/v1beta1 -kind: PodDisruptionBudget -metadata: - labels: - {{- include "ingress-nginx.labels" . | nindent 4 }} - app.kubernetes.io/component: controller - name: {{ include "ingress-nginx.controller.fullname" . }} - namespace: {{ .Release.Namespace }} -spec: - selector: - matchLabels: - {{- include "ingress-nginx.selectorLabels" . | nindent 6 }} - app.kubernetes.io/component: controller - minAvailable: {{ .Values.controller.minAvailable }} -{{- end }} diff --git a/hosting/kubernetes/budibase/charts/ingress-nginx/templates/controller-prometheusrules.yaml b/hosting/kubernetes/budibase/charts/ingress-nginx/templates/controller-prometheusrules.yaml deleted file mode 100644 index ca5427523d..0000000000 --- a/hosting/kubernetes/budibase/charts/ingress-nginx/templates/controller-prometheusrules.yaml +++ /dev/null @@ -1,21 +0,0 @@ -{{- if and .Values.controller.metrics.enabled .Values.controller.metrics.prometheusRule.enabled -}} -apiVersion: monitoring.coreos.com/v1 -kind: PrometheusRule -metadata: - name: {{ include "ingress-nginx.controller.fullname" . }} -{{- if .Values.controller.metrics.prometheusRule.namespace }} - namespace: {{ .Values.controller.metrics.prometheusRule.namespace | quote }} -{{- end }} - labels: - {{- include "ingress-nginx.labels" . | nindent 4 }} - app.kubernetes.io/component: controller - {{- if .Values.controller.metrics.prometheusRule.additionalLabels }} - {{- toYaml .Values.controller.metrics.prometheusRule.additionalLabels | nindent 4 }} - {{- end }} -spec: -{{- if .Values.controller.metrics.prometheusRule.rules }} - groups: - - name: {{ template "ingress-nginx.name" . }} - rules: {{- toYaml .Values.controller.metrics.prometheusRule.rules | nindent 4 }} -{{- end }} -{{- end }} diff --git a/hosting/kubernetes/budibase/charts/ingress-nginx/templates/controller-psp.yaml b/hosting/kubernetes/budibase/charts/ingress-nginx/templates/controller-psp.yaml deleted file mode 100644 index bdb8563105..0000000000 --- a/hosting/kubernetes/budibase/charts/ingress-nginx/templates/controller-psp.yaml +++ /dev/null @@ -1,86 +0,0 @@ -{{- if and .Values.podSecurityPolicy.enabled (empty .Values.controller.existingPsp) -}} -apiVersion: policy/v1beta1 -kind: PodSecurityPolicy -metadata: - name: {{ include "ingress-nginx.fullname" . }} - labels: - {{- include "ingress-nginx.labels" . | nindent 4 }} - app.kubernetes.io/component: controller -spec: - allowedCapabilities: - - NET_BIND_SERVICE -{{- if .Values.controller.sysctls }} - allowedUnsafeSysctls: - {{- range $sysctl, $value := .Values.controller.sysctls }} - - {{ $sysctl }} - {{- end }} -{{- end }} - privileged: false - allowPrivilegeEscalation: true - # Allow core volume types. - volumes: - - 'configMap' - - 'emptyDir' - #- 'projected' - - 'secret' - #- 'downwardAPI' -{{- if .Values.controller.hostNetwork }} - hostNetwork: {{ .Values.controller.hostNetwork }} -{{- end }} -{{- if or .Values.controller.hostNetwork .Values.controller.hostPort.enabled }} - hostPorts: -{{- if .Values.controller.hostNetwork }} -{{- range $key, $value := .Values.controller.containerPort }} - # {{ $key }} - - min: {{ $value }} - max: {{ $value }} -{{- end }} -{{- else if .Values.controller.hostPort.enabled }} -{{- range $key, $value := .Values.controller.hostPort.ports }} - # {{ $key }} - - min: {{ $value }} - max: {{ $value }} -{{- end }} -{{- end }} -{{- if .Values.controller.metrics.enabled }} - # metrics - - min: {{ .Values.controller.metrics.port }} - max: {{ .Values.controller.metrics.port }} -{{- end }} -{{- if .Values.controller.admissionWebhooks.enabled }} - # admission webhooks - - min: {{ .Values.controller.admissionWebhooks.port }} - max: {{ .Values.controller.admissionWebhooks.port }} -{{- end }} -{{- range $key, $value := .Values.tcp }} - # {{ $key }}-tcp - - min: {{ $key }} - max: {{ $key }} -{{- end }} -{{- range $key, $value := .Values.udp }} - # {{ $key }}-udp - - min: {{ $key }} - max: {{ $key }} -{{- end }} -{{- end }} - hostIPC: false - hostPID: false - runAsUser: - # Require the container to run without root privileges. - rule: 'MustRunAsNonRoot' - supplementalGroups: - rule: 'MustRunAs' - ranges: - # Forbid adding the root group. - - min: 1 - max: 65535 - fsGroup: - rule: 'MustRunAs' - ranges: - # Forbid adding the root group. - - min: 1 - max: 65535 - readOnlyRootFilesystem: false - seLinux: - rule: 'RunAsAny' -{{- end }} diff --git a/hosting/kubernetes/budibase/charts/ingress-nginx/templates/controller-role.yaml b/hosting/kubernetes/budibase/charts/ingress-nginx/templates/controller-role.yaml deleted file mode 100644 index 1a5ccd29bf..0000000000 --- a/hosting/kubernetes/budibase/charts/ingress-nginx/templates/controller-role.yaml +++ /dev/null @@ -1,92 +0,0 @@ -{{- if .Values.rbac.create -}} -apiVersion: rbac.authorization.k8s.io/v1 -kind: Role -metadata: - labels: - {{- include "ingress-nginx.labels" . | nindent 4 }} - app.kubernetes.io/component: controller - name: {{ include "ingress-nginx.fullname" . }} - namespace: {{ .Release.Namespace }} -rules: - - apiGroups: - - "" - resources: - - namespaces - verbs: - - get - - apiGroups: - - "" - resources: - - configmaps - - pods - - secrets - - endpoints - verbs: - - get - - list - - watch - - apiGroups: - - "" - resources: - - services - verbs: - - get - - list - - watch - - apiGroups: - - extensions - - "networking.k8s.io" # k8s 1.14+ - resources: - - ingresses - verbs: - - get - - list - - watch - - apiGroups: - - extensions - - "networking.k8s.io" # k8s 1.14+ - resources: - - ingresses/status - verbs: - - update - - apiGroups: - - "networking.k8s.io" # k8s 1.14+ - resources: - - ingressclasses - verbs: - - get - - list - - watch - - apiGroups: - - "" - resources: - - configmaps - resourceNames: - - {{ .Values.controller.electionID }}-{{ .Values.controller.ingressClass }} - verbs: - - get - - update - - apiGroups: - - "" - resources: - - configmaps - verbs: - - create - - apiGroups: - - "" - resources: - - events - verbs: - - create - - patch -{{- if .Values.podSecurityPolicy.enabled }} - - apiGroups: [{{ template "podSecurityPolicy.apiGroup" . }}] - resources: ['podsecuritypolicies'] - verbs: ['use'] - {{- with .Values.controller.existingPsp }} - resourceNames: [{{ . }}] - {{- else }} - resourceNames: [{{ include "ingress-nginx.fullname" . }}] - {{- end }} -{{- end }} -{{- end }} diff --git a/hosting/kubernetes/budibase/charts/ingress-nginx/templates/controller-rolebinding.yaml b/hosting/kubernetes/budibase/charts/ingress-nginx/templates/controller-rolebinding.yaml deleted file mode 100644 index 5ec3bc7749..0000000000 --- a/hosting/kubernetes/budibase/charts/ingress-nginx/templates/controller-rolebinding.yaml +++ /dev/null @@ -1,18 +0,0 @@ -{{- if .Values.rbac.create -}} -apiVersion: rbac.authorization.k8s.io/v1 -kind: RoleBinding -metadata: - labels: - {{- include "ingress-nginx.labels" . | nindent 4 }} - app.kubernetes.io/component: controller - name: {{ include "ingress-nginx.fullname" . }} - namespace: {{ .Release.Namespace }} -roleRef: - apiGroup: rbac.authorization.k8s.io - kind: Role - name: {{ include "ingress-nginx.fullname" . }} -subjects: - - kind: ServiceAccount - name: {{ template "ingress-nginx.serviceAccountName" . }} - namespace: {{ .Release.Namespace | quote }} -{{- end }} diff --git a/hosting/kubernetes/budibase/charts/ingress-nginx/templates/controller-service-internal.yaml b/hosting/kubernetes/budibase/charts/ingress-nginx/templates/controller-service-internal.yaml deleted file mode 100644 index 0bb9661274..0000000000 --- a/hosting/kubernetes/budibase/charts/ingress-nginx/templates/controller-service-internal.yaml +++ /dev/null @@ -1,51 +0,0 @@ -{{- if and .Values.controller.service.enabled .Values.controller.service.internal.enabled .Values.controller.service.internal.annotations}} -apiVersion: v1 -kind: Service -metadata: - annotations: - {{- range $key, $value := .Values.controller.service.internal.annotations }} - {{ $key }}: {{ $value | quote }} - {{- end }} - labels: - {{- include "ingress-nginx.labels" . | nindent 4 }} - app.kubernetes.io/component: controller - {{- if .Values.controller.service.labels }} - {{- toYaml .Values.controller.service.labels | nindent 4 }} - {{- end }} - name: {{ include "ingress-nginx.controller.fullname" . }}-internal - namespace: {{ .Release.Namespace }} -spec: - type: "{{ .Values.controller.service.type }}" -{{- if .Values.controller.service.internal.loadBalancerIP }} - loadBalancerIP: {{ .Values.controller.service.internal.loadBalancerIP }} -{{- end }} -{{- if .Values.controller.service.internal.loadBalancerSourceRanges }} - loadBalancerSourceRanges: {{ toYaml .Values.controller.service.internal.loadBalancerSourceRanges | nindent 4 }} -{{- end }} -{{- if .Values.controller.service.internal.externalTrafficPolicy }} - externalTrafficPolicy: {{ .Values.controller.service.internal.externalTrafficPolicy }} -{{- end }} - ports: - {{- $setNodePorts := (or (eq .Values.controller.service.type "NodePort") (eq .Values.controller.service.type "LoadBalancer")) }} - {{- if .Values.controller.service.enableHttp }} - - name: http - port: {{ .Values.controller.service.ports.http }} - protocol: TCP - targetPort: {{ .Values.controller.service.targetPorts.http }} - {{- if (and $setNodePorts (not (empty .Values.controller.service.nodePorts.http))) }} - nodePort: {{ .Values.controller.service.nodePorts.http }} - {{- end }} - {{- end }} - {{- if .Values.controller.service.enableHttps }} - - name: https - port: {{ .Values.controller.service.ports.https }} - protocol: TCP - targetPort: {{ .Values.controller.service.targetPorts.https }} - {{- if (and $setNodePorts (not (empty .Values.controller.service.nodePorts.https))) }} - nodePort: {{ .Values.controller.service.nodePorts.https }} - {{- end }} - {{- end }} - selector: - {{- include "ingress-nginx.selectorLabels" . | nindent 4 }} - app.kubernetes.io/component: controller -{{- end }} diff --git a/hosting/kubernetes/budibase/charts/ingress-nginx/templates/controller-service-metrics.yaml b/hosting/kubernetes/budibase/charts/ingress-nginx/templates/controller-service-metrics.yaml deleted file mode 100644 index 1b690192c8..0000000000 --- a/hosting/kubernetes/budibase/charts/ingress-nginx/templates/controller-service-metrics.yaml +++ /dev/null @@ -1,44 +0,0 @@ -{{- if .Values.controller.metrics.enabled -}} -apiVersion: v1 -kind: Service -metadata: -{{- if .Values.controller.metrics.service.annotations }} - annotations: {{ toYaml .Values.controller.metrics.service.annotations | nindent 4 }} -{{- end }} - labels: - {{- include "ingress-nginx.labels" . | nindent 4 }} - app.kubernetes.io/component: controller - {{- if .Values.controller.metrics.service.labels }} - {{- toYaml .Values.controller.metrics.service.labels | nindent 4 }} - {{- end }} - name: {{ include "ingress-nginx.controller.fullname" . }}-metrics - namespace: {{ .Release.Namespace }} -spec: - type: {{ .Values.controller.metrics.service.type }} -{{- if .Values.controller.metrics.service.clusterIP }} - clusterIP: {{ .Values.controller.metrics.service.clusterIP }} -{{- end }} -{{- if .Values.controller.metrics.service.externalIPs }} - externalIPs: {{ toYaml .Values.controller.metrics.service.externalIPs | nindent 4 }} -{{- end }} -{{- if .Values.controller.metrics.service.loadBalancerIP }} - loadBalancerIP: {{ .Values.controller.metrics.service.loadBalancerIP }} -{{- end }} -{{- if .Values.controller.metrics.service.loadBalancerSourceRanges }} - loadBalancerSourceRanges: {{ toYaml .Values.controller.metrics.service.loadBalancerSourceRanges | nindent 4 }} -{{- end }} -{{- if .Values.controller.metrics.service.externalTrafficPolicy }} - externalTrafficPolicy: {{ .Values.controller.metrics.service.externalTrafficPolicy }} -{{- end }} - ports: - - name: metrics - port: {{ .Values.controller.metrics.service.servicePort }} - targetPort: metrics - {{- $setNodePorts := (or (eq .Values.controller.metrics.service.type "NodePort") (eq .Values.controller.metrics.service.type "LoadBalancer")) }} - {{- if (and $setNodePorts (not (empty .Values.controller.metrics.service.nodePort))) }} - nodePort: {{ .Values.controller.metrics.service.nodePort }} - {{- end }} - selector: - {{- include "ingress-nginx.selectorLabels" . | nindent 4 }} - app.kubernetes.io/component: controller -{{- end }} diff --git a/hosting/kubernetes/budibase/charts/ingress-nginx/templates/controller-service-webhook.yaml b/hosting/kubernetes/budibase/charts/ingress-nginx/templates/controller-service-webhook.yaml deleted file mode 100644 index 228cb59d88..0000000000 --- a/hosting/kubernetes/budibase/charts/ingress-nginx/templates/controller-service-webhook.yaml +++ /dev/null @@ -1,34 +0,0 @@ -{{- if .Values.controller.admissionWebhooks.enabled -}} -apiVersion: v1 -kind: Service -metadata: -{{- if .Values.controller.admissionWebhooks.service.annotations }} - annotations: {{ toYaml .Values.controller.admissionWebhooks.service.annotations | nindent 4 }} -{{- end }} - labels: - {{- include "ingress-nginx.labels" . | nindent 4 }} - app.kubernetes.io/component: controller - name: {{ include "ingress-nginx.controller.fullname" . }}-admission - namespace: {{ .Release.Namespace }} -spec: - type: {{ .Values.controller.admissionWebhooks.service.type }} -{{- if .Values.controller.admissionWebhooks.service.clusterIP }} - clusterIP: {{ .Values.controller.admissionWebhooks.service.clusterIP }} -{{- end }} -{{- if .Values.controller.admissionWebhooks.service.externalIPs }} - externalIPs: {{ toYaml .Values.controller.admissionWebhooks.service.externalIPs | nindent 4 }} -{{- end }} -{{- if .Values.controller.admissionWebhooks.service.loadBalancerIP }} - loadBalancerIP: {{ .Values.controller.admissionWebhooks.service.loadBalancerIP }} -{{- end }} -{{- if .Values.controller.admissionWebhooks.service.loadBalancerSourceRanges }} - loadBalancerSourceRanges: {{ toYaml .Values.controller.admissionWebhooks.service.loadBalancerSourceRanges | nindent 4 }} -{{- end }} - ports: - - name: https-webhook - port: 443 - targetPort: webhook - selector: - {{- include "ingress-nginx.selectorLabels" . | nindent 4 }} - app.kubernetes.io/component: controller -{{- end }} diff --git a/hosting/kubernetes/budibase/charts/ingress-nginx/templates/controller-service.yaml b/hosting/kubernetes/budibase/charts/ingress-nginx/templates/controller-service.yaml deleted file mode 100644 index 908291cfff..0000000000 --- a/hosting/kubernetes/budibase/charts/ingress-nginx/templates/controller-service.yaml +++ /dev/null @@ -1,85 +0,0 @@ -{{- if .Values.controller.service.enabled -}} -apiVersion: v1 -kind: Service -metadata: - annotations: - {{- range $key, $value := .Values.controller.service.annotations }} - {{ $key }}: {{ $value | quote }} - {{- end }} - labels: - {{- include "ingress-nginx.labels" . | nindent 4 }} - app.kubernetes.io/component: controller - {{- if .Values.controller.service.labels }} - {{- toYaml .Values.controller.service.labels | nindent 4 }} - {{- end }} - name: {{ include "ingress-nginx.controller.fullname" . }} - namespace: {{ .Release.Namespace }} -spec: - type: {{ .Values.controller.service.type }} -{{- if .Values.controller.service.clusterIP }} - clusterIP: {{ .Values.controller.service.clusterIP }} -{{- end }} -{{- if .Values.controller.service.externalIPs }} - externalIPs: {{ toYaml .Values.controller.service.externalIPs | nindent 4 }} -{{- end }} -{{- if .Values.controller.service.loadBalancerIP }} - loadBalancerIP: {{ .Values.controller.service.loadBalancerIP }} -{{- end }} -{{- if .Values.controller.service.loadBalancerSourceRanges }} - loadBalancerSourceRanges: {{ toYaml .Values.controller.service.loadBalancerSourceRanges | nindent 4 }} -{{- end }} -{{- if .Values.controller.service.externalTrafficPolicy }} - externalTrafficPolicy: {{ .Values.controller.service.externalTrafficPolicy }} -{{- end }} -{{- if .Values.controller.service.sessionAffinity }} - sessionAffinity: {{ .Values.controller.service.sessionAffinity }} -{{- end }} -{{- if .Values.controller.service.healthCheckNodePort }} - healthCheckNodePort: {{ .Values.controller.service.healthCheckNodePort }} -{{- end }} - ports: - {{- $setNodePorts := (or (eq .Values.controller.service.type "NodePort") (eq .Values.controller.service.type "LoadBalancer")) }} - {{- if .Values.controller.service.enableHttp }} - - name: http - port: {{ .Values.controller.service.ports.http }} - protocol: TCP - targetPort: {{ .Values.controller.service.targetPorts.http }} - {{- if (and $setNodePorts (not (empty .Values.controller.service.nodePorts.http))) }} - nodePort: {{ .Values.controller.service.nodePorts.http }} - {{- end }} - {{- end }} - {{- if .Values.controller.service.enableHttps }} - - name: https - port: {{ .Values.controller.service.ports.https }} - protocol: TCP - targetPort: {{ .Values.controller.service.targetPorts.https }} - {{- if (and $setNodePorts (not (empty .Values.controller.service.nodePorts.https))) }} - nodePort: {{ .Values.controller.service.nodePorts.https }} - {{- end }} - {{- end }} - {{- range $key, $value := .Values.tcp }} - - name: {{ $key }}-tcp - port: {{ $key }} - protocol: TCP - targetPort: {{ $key }}-tcp - {{- if $.Values.controller.service.nodePorts.tcp }} - {{- if index $.Values.controller.service.nodePorts.tcp $key }} - nodePort: {{ index $.Values.controller.service.nodePorts.tcp $key }} - {{- end }} - {{- end }} - {{- end }} - {{- range $key, $value := .Values.udp }} - - name: {{ $key }}-udp - port: {{ $key }} - protocol: UDP - targetPort: {{ $key }}-udp - {{- if $.Values.controller.service.nodePorts.udp }} - {{- if index $.Values.controller.service.nodePorts.udp $key }} - nodePort: {{ index $.Values.controller.service.nodePorts.udp $key }} - {{- end }} - {{- end }} - {{- end }} - selector: - {{- include "ingress-nginx.selectorLabels" . | nindent 4 }} - app.kubernetes.io/component: controller -{{- end }} diff --git a/hosting/kubernetes/budibase/charts/ingress-nginx/templates/controller-serviceaccount.yaml b/hosting/kubernetes/budibase/charts/ingress-nginx/templates/controller-serviceaccount.yaml deleted file mode 100644 index 50a718d32d..0000000000 --- a/hosting/kubernetes/budibase/charts/ingress-nginx/templates/controller-serviceaccount.yaml +++ /dev/null @@ -1,11 +0,0 @@ -{{- if or .Values.serviceAccount.create -}} -apiVersion: v1 -kind: ServiceAccount -metadata: - labels: - {{- include "ingress-nginx.labels" . | nindent 4 }} - app.kubernetes.io/component: controller - name: {{ template "ingress-nginx.serviceAccountName" . }} - namespace: {{ .Release.Namespace }} -automountServiceAccountToken: {{ .Values.serviceAccount.automountServiceAccountToken }} -{{- end }} diff --git a/hosting/kubernetes/budibase/charts/ingress-nginx/templates/controller-servicemonitor.yaml b/hosting/kubernetes/budibase/charts/ingress-nginx/templates/controller-servicemonitor.yaml deleted file mode 100644 index 066488a040..0000000000 --- a/hosting/kubernetes/budibase/charts/ingress-nginx/templates/controller-servicemonitor.yaml +++ /dev/null @@ -1,45 +0,0 @@ -{{- if and .Values.controller.metrics.enabled .Values.controller.metrics.serviceMonitor.enabled -}} -apiVersion: monitoring.coreos.com/v1 -kind: ServiceMonitor -metadata: - name: {{ include "ingress-nginx.controller.fullname" . }} -{{- if .Values.controller.metrics.serviceMonitor.namespace }} - namespace: {{ .Values.controller.metrics.serviceMonitor.namespace | quote }} -{{- end }} - labels: - {{- include "ingress-nginx.labels" . | nindent 4 }} - app.kubernetes.io/component: controller - {{- if .Values.controller.metrics.serviceMonitor.additionalLabels }} - {{- toYaml .Values.controller.metrics.serviceMonitor.additionalLabels | nindent 4 }} - {{- end }} -spec: - endpoints: - - port: metrics - interval: {{ .Values.controller.metrics.serviceMonitor.scrapeInterval }} - {{- if .Values.controller.metrics.serviceMonitor.honorLabels }} - honorLabels: true - {{- end }} - {{- if .Values.controller.metrics.serviceMonitor.metricRelabelings }} - metricRelabelings: {{ toYaml .Values.controller.metrics.serviceMonitor.metricRelabelings | nindent 8 }} - {{- end }} -{{- if .Values.controller.metrics.serviceMonitor.jobLabel }} - jobLabel: {{ .Values.controller.metrics.serviceMonitor.jobLabel | quote }} -{{- end }} -{{- if .Values.controller.metrics.serviceMonitor.namespaceSelector }} - namespaceSelector: {{ toYaml .Values.controller.metrics.serviceMonitor.namespaceSelector | nindent 4 }} -{{ else }} - namespaceSelector: - matchNames: - - {{ .Release.Namespace }} -{{- end }} -{{- if .Values.controller.metrics.serviceMonitor.targetLabels }} - targetLabels: - {{- range .Values.controller.metrics.serviceMonitor.targetLabels }} - - {{ . }} - {{- end }} -{{- end }} - selector: - matchLabels: - {{- include "ingress-nginx.selectorLabels" . | nindent 6 }} - app.kubernetes.io/component: controller -{{- end }} diff --git a/hosting/kubernetes/budibase/charts/ingress-nginx/templates/default-backend-deployment.yaml b/hosting/kubernetes/budibase/charts/ingress-nginx/templates/default-backend-deployment.yaml deleted file mode 100644 index 99345269ba..0000000000 --- a/hosting/kubernetes/budibase/charts/ingress-nginx/templates/default-backend-deployment.yaml +++ /dev/null @@ -1,112 +0,0 @@ -{{- if .Values.defaultBackend.enabled -}} -apiVersion: apps/v1 -kind: Deployment -metadata: - labels: - {{- include "ingress-nginx.labels" . | nindent 4 }} - app.kubernetes.io/component: default-backend - name: {{ include "ingress-nginx.defaultBackend.fullname" . }} - namespace: {{ .Release.Namespace }} -spec: - selector: - matchLabels: - {{- include "ingress-nginx.selectorLabels" . | nindent 6 }} - app.kubernetes.io/component: default-backend -{{- if not .Values.defaultBackend.autoscaling.enabled }} - replicas: {{ .Values.defaultBackend.replicaCount }} -{{- end }} - revisionHistoryLimit: {{ .Values.revisionHistoryLimit }} - template: - metadata: - {{- if .Values.defaultBackend.podAnnotations }} - annotations: {{ toYaml .Values.defaultBackend.podAnnotations | nindent 8 }} - {{- end }} - labels: - {{- include "ingress-nginx.selectorLabels" . | nindent 8 }} - app.kubernetes.io/component: default-backend - {{- if .Values.defaultBackend.podLabels }} - {{- toYaml .Values.defaultBackend.podLabels | nindent 8 }} - {{- end }} - spec: - {{- if .Values.imagePullSecrets }} - imagePullSecrets: {{ toYaml .Values.imagePullSecrets | nindent 8 }} - {{- end }} - {{- if .Values.defaultBackend.priorityClassName }} - priorityClassName: {{ .Values.defaultBackend.priorityClassName }} - {{- end }} - {{- if .Values.defaultBackend.podSecurityContext }} - securityContext: {{ toYaml .Values.defaultBackend.podSecurityContext | nindent 8 }} - {{- end }} - containers: - - name: {{ template "ingress-nginx.name" . }}-default-backend - {{- with .Values.defaultBackend.image }} - image: "{{- if .repository -}}{{ .repository }}{{ else }}{{ .registry }}/{{ .image }}{{- end -}}:{{ .tag }}{{- if (.digest) -}} @{{.digest}} {{- end -}}" - {{- end }} - imagePullPolicy: {{ .Values.defaultBackend.image.pullPolicy }} - {{- if .Values.defaultBackend.extraArgs }} - args: - {{- range $key, $value := .Values.defaultBackend.extraArgs }} - {{- /* Accept keys without values or with false as value */}} - {{- if eq ($value | quote | len) 2 }} - - --{{ $key }} - {{- else }} - - --{{ $key }}={{ $value }} - {{- end }} - {{- end }} - {{- end }} - securityContext: - capabilities: - drop: - - ALL - runAsUser: {{ .Values.defaultBackend.image.runAsUser }} - runAsNonRoot: {{ .Values.defaultBackend.image.runAsNonRoot }} - allowPrivilegeEscalation: {{ .Values.defaultBackend.image.allowPrivilegeEscalation }} - readOnlyRootFilesystem: {{ .Values.defaultBackend.image.readOnlyRootFilesystem}} - {{- if .Values.defaultBackend.extraEnvs }} - env: {{ toYaml .Values.defaultBackend.extraEnvs | nindent 12 }} - {{- end }} - livenessProbe: - httpGet: - path: /healthz - port: {{ .Values.defaultBackend.port }} - scheme: HTTP - initialDelaySeconds: {{ .Values.defaultBackend.livenessProbe.initialDelaySeconds }} - periodSeconds: {{ .Values.defaultBackend.livenessProbe.periodSeconds }} - timeoutSeconds: {{ .Values.defaultBackend.livenessProbe.timeoutSeconds }} - successThreshold: {{ .Values.defaultBackend.livenessProbe.successThreshold }} - failureThreshold: {{ .Values.defaultBackend.livenessProbe.failureThreshold }} - readinessProbe: - httpGet: - path: /healthz - port: {{ .Values.defaultBackend.port }} - scheme: HTTP - initialDelaySeconds: {{ .Values.defaultBackend.readinessProbe.initialDelaySeconds }} - periodSeconds: {{ .Values.defaultBackend.readinessProbe.periodSeconds }} - timeoutSeconds: {{ .Values.defaultBackend.readinessProbe.timeoutSeconds }} - successThreshold: {{ .Values.defaultBackend.readinessProbe.successThreshold }} - failureThreshold: {{ .Values.defaultBackend.readinessProbe.failureThreshold }} - ports: - - name: http - containerPort: {{ .Values.defaultBackend.port }} - protocol: TCP - {{- if .Values.defaultBackend.extraVolumeMounts }} - volumeMounts: {{- toYaml .Values.defaultBackend.extraVolumeMounts | nindent 12 }} - {{- end }} - {{- if .Values.defaultBackend.resources }} - resources: {{ toYaml .Values.defaultBackend.resources | nindent 12 }} - {{- end }} - {{- if .Values.defaultBackend.nodeSelector }} - nodeSelector: {{ toYaml .Values.defaultBackend.nodeSelector | nindent 8 }} - {{- end }} - serviceAccountName: {{ template "ingress-nginx.defaultBackend.serviceAccountName" . }} - {{- if .Values.defaultBackend.tolerations }} - tolerations: {{ toYaml .Values.defaultBackend.tolerations | nindent 8 }} - {{- end }} - {{- if .Values.defaultBackend.affinity }} - affinity: {{ toYaml .Values.defaultBackend.affinity | nindent 8 }} - {{- end }} - terminationGracePeriodSeconds: 60 - {{- if .Values.defaultBackend.extraVolumes }} - volumes: {{ toYaml .Values.defaultBackend.extraVolumes | nindent 8 }} - {{- end }} -{{- end }} diff --git a/hosting/kubernetes/budibase/charts/ingress-nginx/templates/default-backend-hpa.yaml b/hosting/kubernetes/budibase/charts/ingress-nginx/templates/default-backend-hpa.yaml deleted file mode 100644 index e31fda3f9a..0000000000 --- a/hosting/kubernetes/budibase/charts/ingress-nginx/templates/default-backend-hpa.yaml +++ /dev/null @@ -1,30 +0,0 @@ -{{- if and .Values.defaultBackend.enabled .Values.defaultBackend.autoscaling.enabled }} -apiVersion: autoscaling/v2beta1 -kind: HorizontalPodAutoscaler -metadata: - labels: - {{- include "ingress-nginx.labels" . | nindent 4 }} - app.kubernetes.io/component: default-backend - name: {{ template "ingress-nginx.defaultBackend.fullname" . }} - namespace: {{ .Release.Namespace }} -spec: - scaleTargetRef: - apiVersion: apps/v1 - kind: Deployment - name: {{ template "ingress-nginx.defaultBackend.fullname" . }} - minReplicas: {{ .Values.defaultBackend.autoscaling.minReplicas }} - maxReplicas: {{ .Values.defaultBackend.autoscaling.maxReplicas }} - metrics: -{{- with .Values.defaultBackend.autoscaling.targetCPUUtilizationPercentage }} - - type: Resource - resource: - name: cpu - targetAverageUtilization: {{ . }} -{{- end }} -{{- with .Values.defaultBackend.autoscaling.targetMemoryUtilizationPercentage }} - - type: Resource - resource: - name: memory - targetAverageUtilization: {{ . }} -{{- end }} -{{- end }} diff --git a/hosting/kubernetes/budibase/charts/ingress-nginx/templates/default-backend-poddisruptionbudget.yaml b/hosting/kubernetes/budibase/charts/ingress-nginx/templates/default-backend-poddisruptionbudget.yaml deleted file mode 100644 index 153f005e25..0000000000 --- a/hosting/kubernetes/budibase/charts/ingress-nginx/templates/default-backend-poddisruptionbudget.yaml +++ /dev/null @@ -1,16 +0,0 @@ -{{- if or (gt (.Values.defaultBackend.replicaCount | int) 1) (gt (.Values.defaultBackend.autoscaling.minReplicas | int) 1) }} -apiVersion: policy/v1beta1 -kind: PodDisruptionBudget -metadata: - labels: - {{- include "ingress-nginx.labels" . | nindent 4 }} - app.kubernetes.io/component: default-backend - name: {{ include "ingress-nginx.defaultBackend.fullname" . }} - namespace: {{ .Release.Namespace }} -spec: - selector: - matchLabels: - {{- include "ingress-nginx.selectorLabels" . | nindent 6 }} - app.kubernetes.io/component: default-backend - minAvailable: {{ .Values.defaultBackend.minAvailable }} -{{- end }} diff --git a/hosting/kubernetes/budibase/charts/ingress-nginx/templates/default-backend-psp.yaml b/hosting/kubernetes/budibase/charts/ingress-nginx/templates/default-backend-psp.yaml deleted file mode 100644 index 716dbf16fe..0000000000 --- a/hosting/kubernetes/budibase/charts/ingress-nginx/templates/default-backend-psp.yaml +++ /dev/null @@ -1,33 +0,0 @@ -{{- if and .Values.podSecurityPolicy.enabled .Values.defaultBackend.enabled (empty .Values.defaultBackend.existingPsp) -}} -apiVersion: policy/v1beta1 -kind: PodSecurityPolicy -metadata: - name: {{ include "ingress-nginx.fullname" . }}-backend - labels: - {{- include "ingress-nginx.labels" . | nindent 4 }} - app.kubernetes.io/component: default-backend -spec: - allowPrivilegeEscalation: false - fsGroup: - ranges: - - max: 65535 - min: 1 - rule: MustRunAs - requiredDropCapabilities: - - ALL - runAsUser: - rule: MustRunAsNonRoot - seLinux: - rule: RunAsAny - supplementalGroups: - ranges: - - max: 65535 - min: 1 - rule: MustRunAs - volumes: - - configMap - - emptyDir - - projected - - secret - - downwardAPI -{{- end }} diff --git a/hosting/kubernetes/budibase/charts/ingress-nginx/templates/default-backend-role.yaml b/hosting/kubernetes/budibase/charts/ingress-nginx/templates/default-backend-role.yaml deleted file mode 100644 index 5d29a2d526..0000000000 --- a/hosting/kubernetes/budibase/charts/ingress-nginx/templates/default-backend-role.yaml +++ /dev/null @@ -1,19 +0,0 @@ -{{- if and .Values.rbac.create .Values.podSecurityPolicy.enabled .Values.defaultBackend.enabled -}} -apiVersion: rbac.authorization.k8s.io/v1 -kind: Role -metadata: - labels: - {{- include "ingress-nginx.labels" . | nindent 4 }} - app.kubernetes.io/component: default-backend - name: {{ include "ingress-nginx.fullname" . }}-backend - namespace: {{ .Release.Namespace }} -rules: - - apiGroups: [{{ template "podSecurityPolicy.apiGroup" . }}] - resources: ['podsecuritypolicies'] - verbs: ['use'] - {{- with .Values.defaultBackend.existingPsp }} - resourceNames: [{{ . }}] - {{- else }} - resourceNames: [{{ include "ingress-nginx.fullname" . }}-backend] - {{- end }} -{{- end }} diff --git a/hosting/kubernetes/budibase/charts/ingress-nginx/templates/default-backend-rolebinding.yaml b/hosting/kubernetes/budibase/charts/ingress-nginx/templates/default-backend-rolebinding.yaml deleted file mode 100644 index 4a9cb92845..0000000000 --- a/hosting/kubernetes/budibase/charts/ingress-nginx/templates/default-backend-rolebinding.yaml +++ /dev/null @@ -1,18 +0,0 @@ -{{- if and .Values.rbac.create .Values.podSecurityPolicy.enabled .Values.defaultBackend.enabled -}} -apiVersion: rbac.authorization.k8s.io/v1 -kind: RoleBinding -metadata: - labels: - {{- include "ingress-nginx.labels" . | nindent 4 }} - app.kubernetes.io/component: default-backend - name: {{ include "ingress-nginx.fullname" . }}-backend - namespace: {{ .Release.Namespace }} -roleRef: - apiGroup: rbac.authorization.k8s.io - kind: Role - name: {{ include "ingress-nginx.fullname" . }}-backend -subjects: - - kind: ServiceAccount - name: {{ template "ingress-nginx.defaultBackend.serviceAccountName" . }} - namespace: {{ .Release.Namespace | quote }} -{{- end }} diff --git a/hosting/kubernetes/budibase/charts/ingress-nginx/templates/default-backend-service.yaml b/hosting/kubernetes/budibase/charts/ingress-nginx/templates/default-backend-service.yaml deleted file mode 100644 index 7624ab36c4..0000000000 --- a/hosting/kubernetes/budibase/charts/ingress-nginx/templates/default-backend-service.yaml +++ /dev/null @@ -1,35 +0,0 @@ -{{- if .Values.defaultBackend.enabled -}} -apiVersion: v1 -kind: Service -metadata: -{{- if .Values.defaultBackend.service.annotations }} - annotations: {{ toYaml .Values.defaultBackend.service.annotations | nindent 4 }} -{{- end }} - labels: - {{- include "ingress-nginx.labels" . | nindent 4 }} - app.kubernetes.io/component: default-backend - name: {{ include "ingress-nginx.defaultBackend.fullname" . }} - namespace: {{ .Release.Namespace }} -spec: - type: {{ .Values.defaultBackend.service.type }} -{{- if .Values.defaultBackend.service.clusterIP }} - clusterIP: {{ .Values.defaultBackend.service.clusterIP }} -{{- end }} -{{- if .Values.defaultBackend.service.externalIPs }} - externalIPs: {{ toYaml .Values.defaultBackend.service.externalIPs | nindent 4 }} -{{- end }} -{{- if .Values.defaultBackend.service.loadBalancerIP }} - loadBalancerIP: {{ .Values.defaultBackend.service.loadBalancerIP }} -{{- end }} -{{- if .Values.defaultBackend.service.loadBalancerSourceRanges }} - loadBalancerSourceRanges: {{ toYaml .Values.defaultBackend.service.loadBalancerSourceRanges | nindent 4 }} -{{- end }} - ports: - - name: http - port: {{ .Values.defaultBackend.service.servicePort }} - protocol: TCP - targetPort: http - selector: - {{- include "ingress-nginx.selectorLabels" . | nindent 4 }} - app.kubernetes.io/component: default-backend -{{- end }} diff --git a/hosting/kubernetes/budibase/charts/ingress-nginx/templates/default-backend-serviceaccount.yaml b/hosting/kubernetes/budibase/charts/ingress-nginx/templates/default-backend-serviceaccount.yaml deleted file mode 100644 index 0c00e93690..0000000000 --- a/hosting/kubernetes/budibase/charts/ingress-nginx/templates/default-backend-serviceaccount.yaml +++ /dev/null @@ -1,11 +0,0 @@ -{{- if and .Values.defaultBackend.enabled .Values.defaultBackend.serviceAccount.create -}} -apiVersion: v1 -kind: ServiceAccount -metadata: - labels: - {{- include "ingress-nginx.labels" . | nindent 4 }} - app.kubernetes.io/component: default-backend - name: {{ template "ingress-nginx.defaultBackend.serviceAccountName" . }} - namespace: {{ .Release.Namespace }} -automountServiceAccountToken: {{ .Values.defaultBackend.serviceAccount.automountServiceAccountToken }} -{{- end }} diff --git a/hosting/kubernetes/budibase/charts/ingress-nginx/templates/dh-param-secret.yaml b/hosting/kubernetes/budibase/charts/ingress-nginx/templates/dh-param-secret.yaml deleted file mode 100644 index 12e7a4f633..0000000000 --- a/hosting/kubernetes/budibase/charts/ingress-nginx/templates/dh-param-secret.yaml +++ /dev/null @@ -1,10 +0,0 @@ -{{- with .Values.dhParam -}} -apiVersion: v1 -kind: Secret -metadata: - name: {{ include "ingress-nginx.controller.fullname" $ }} - labels: - {{- include "ingress-nginx.labels" $ | nindent 4 }} -data: - dhparam.pem: {{ . }} -{{- end }} diff --git a/hosting/kubernetes/budibase/charts/ingress-nginx/values.yaml b/hosting/kubernetes/budibase/charts/ingress-nginx/values.yaml deleted file mode 100644 index f5496eb616..0000000000 --- a/hosting/kubernetes/budibase/charts/ingress-nginx/values.yaml +++ /dev/null @@ -1,808 +0,0 @@ -## nginx configuration -## Ref: https://github.com/kubernetes/ingress-nginx/blob/master/docs/user-guide/nginx-configuration/index.md -## - -## Overrides for generated resource names -# See templates/_helpers.tpl -# nameOverride: -# fullnameOverride: - -controller: - name: controller - image: - registry: k8s.gcr.io - image: ingress-nginx/controller - # for backwards compatibility consider setting the full image url via the repository value below - # use *either* current default registry/image or repository format or installing chart by providing the values.yaml will fail - # repository: - tag: "v0.48.1" - digest: sha256:e9fb216ace49dfa4a5983b183067e97496e7a8b307d2093f4278cd550c303899 - pullPolicy: IfNotPresent - # www-data -> uid 101 - runAsUser: 101 - allowPrivilegeEscalation: true - - # Use an existing PSP instead of creating one - existingPsp: "" - - # Configures the controller container name - containerName: controller - - # Configures the ports the nginx-controller listens on - containerPort: - http: 80 - https: 443 - - # Will add custom configuration options to Nginx https://kubernetes.github.io/ingress-nginx/user-guide/nginx-configuration/configmap/ - config: {} - - ## Annotations to be added to the controller config configuration configmap - ## - configAnnotations: {} - - # Will add custom headers before sending traffic to backends according to https://github.com/kubernetes/ingress-nginx/tree/master/docs/examples/customization/custom-headers - proxySetHeaders: {} - - # Will add custom headers before sending response traffic to the client according to: https://kubernetes.github.io/ingress-nginx/user-guide/nginx-configuration/configmap/#add-headers - addHeaders: {} - - # Optionally customize the pod dnsConfig. - dnsConfig: {} - - # Optionally change this to ClusterFirstWithHostNet in case you have 'hostNetwork: true'. - # By default, while using host network, name resolution uses the host's DNS. If you wish nginx-controller - # to keep resolving names inside the k8s network, use ClusterFirstWithHostNet. - dnsPolicy: ClusterFirst - - # Bare-metal considerations via the host network https://kubernetes.github.io/ingress-nginx/deploy/baremetal/#via-the-host-network - # Ingress status was blank because there is no Service exposing the NGINX Ingress controller in a configuration using the host network, the default --publish-service flag used in standard cloud setups does not apply - reportNodeInternalIp: false - - # Required for use with CNI based kubernetes installations (such as ones set up by kubeadm), - # since CNI and hostport don't mix yet. Can be deprecated once https://github.com/kubernetes/kubernetes/issues/23920 - # is merged - hostNetwork: false - - ## Use host ports 80 and 443 - ## Disabled by default - ## - hostPort: - enabled: false - ports: - http: 80 - https: 443 - - ## Election ID to use for status update - ## - electionID: ingress-controller-leader - - ## Name of the ingress class to route through this controller - ## - ingressClass: nginx - - # This section refers to the creation of the IngressClass resource - # IngressClass resources are supported since k8s >= 1.18 - ingressClassResource: - enabled: false - default: false - - # Parameters is a link to a custom resource containing additional - # configuration for the controller. This is optional if the controller - # does not require extra parameters. - parameters: {} - - # labels to add to the pod container metadata - podLabels: {} - # key: value - - ## Security Context policies for controller pods - ## - podSecurityContext: {} - - ## See https://kubernetes.io/docs/tasks/administer-cluster/sysctl-cluster/ for - ## notes on enabling and using sysctls - ### - sysctls: {} - # sysctls: - # "net.core.somaxconn": "8192" - - ## Allows customization of the source of the IP address or FQDN to report - ## in the ingress status field. By default, it reads the information provided - ## by the service. If disable, the status field reports the IP address of the - ## node or nodes where an ingress controller pod is running. - publishService: - enabled: true - ## Allows overriding of the publish service to bind to - ## Must be / - ## - pathOverride: "" - - ## Limit the scope of the controller - ## - scope: - enabled: false - namespace: "" # defaults to .Release.Namespace - - ## Allows customization of the configmap / nginx-configmap namespace - ## - configMapNamespace: "" # defaults to .Release.Namespace - - ## Allows customization of the tcp-services-configmap - ## - tcp: - configMapNamespace: "" # defaults to .Release.Namespace - ## Annotations to be added to the tcp config configmap - annotations: {} - - ## Allows customization of the udp-services-configmap - ## - udp: - configMapNamespace: "" # defaults to .Release.Namespace - ## Annotations to be added to the udp config configmap - annotations: {} - - # Maxmind license key to download GeoLite2 Databases - # https://blog.maxmind.com/2019/12/18/significant-changes-to-accessing-and-using-geolite2-databases - maxmindLicenseKey: "" - - ## Additional command line arguments to pass to nginx-ingress-controller - ## E.g. to specify the default SSL certificate you can use - ## extraArgs: - ## default-ssl-certificate: "/" - extraArgs: {} - - ## Additional environment variables to set - extraEnvs: [] - # extraEnvs: - # - name: FOO - # valueFrom: - # secretKeyRef: - # key: FOO - # name: secret-resource - - ## DaemonSet or Deployment - ## - kind: Deployment - - ## Annotations to be added to the controller Deployment or DaemonSet - ## - annotations: {} - # keel.sh/pollSchedule: "@every 60m" - - ## Labels to be added to the controller Deployment or DaemonSet - ## - labels: {} - # keel.sh/policy: patch - # keel.sh/trigger: poll - - - # The update strategy to apply to the Deployment or DaemonSet - ## - updateStrategy: {} - # rollingUpdate: - # maxUnavailable: 1 - # type: RollingUpdate - - # minReadySeconds to avoid killing pods before we are ready - ## - minReadySeconds: 0 - - - ## Node tolerations for server scheduling to nodes with taints - ## Ref: https://kubernetes.io/docs/concepts/configuration/assign-pod-node/ - ## - tolerations: [] - # - key: "key" - # operator: "Equal|Exists" - # value: "value" - # effect: "NoSchedule|PreferNoSchedule|NoExecute(1.6 only)" - - ## Affinity and anti-affinity - ## Ref: https://kubernetes.io/docs/concepts/configuration/assign-pod-node/#affinity-and-anti-affinity - ## - affinity: {} - # # An example of preferred pod anti-affinity, weight is in the range 1-100 - # podAntiAffinity: - # preferredDuringSchedulingIgnoredDuringExecution: - # - weight: 100 - # podAffinityTerm: - # labelSelector: - # matchExpressions: - # - key: app.kubernetes.io/name - # operator: In - # values: - # - ingress-nginx - # - key: app.kubernetes.io/instance - # operator: In - # values: - # - ingress-nginx - # - key: app.kubernetes.io/component - # operator: In - # values: - # - controller - # topologyKey: kubernetes.io/hostname - - # # An example of required pod anti-affinity - # podAntiAffinity: - # requiredDuringSchedulingIgnoredDuringExecution: - # - labelSelector: - # matchExpressions: - # - key: app.kubernetes.io/name - # operator: In - # values: - # - ingress-nginx - # - key: app.kubernetes.io/instance - # operator: In - # values: - # - ingress-nginx - # - key: app.kubernetes.io/component - # operator: In - # values: - # - controller - # topologyKey: "kubernetes.io/hostname" - - ## Topology spread constraints rely on node labels to identify the topology domain(s) that each Node is in. - ## Ref: https://kubernetes.io/docs/concepts/workloads/pods/pod-topology-spread-constraints/ - ## - topologySpreadConstraints: [] - # - maxSkew: 1 - # topologyKey: failure-domain.beta.kubernetes.io/zone - # whenUnsatisfiable: DoNotSchedule - # labelSelector: - # matchLabels: - # app.kubernetes.io/instance: ingress-nginx-internal - - ## terminationGracePeriodSeconds - ## wait up to five minutes for the drain of connections - ## - terminationGracePeriodSeconds: 300 - - ## Node labels for controller pod assignment - ## Ref: https://kubernetes.io/docs/user-guide/node-selection/ - ## - nodeSelector: - kubernetes.io/os: linux - - ## Liveness and readiness probe values - ## Ref: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle/#container-probes - ## - # startupProbe: - # httpGet: - # # should match container.healthCheckPath - # path: "/healthz" - # port: 10254 - # scheme: HTTP - # initialDelaySeconds: 5 - # periodSeconds: 5 - # timeoutSeconds: 2 - # successThreshold: 1 - # failureThreshold: 5 - livenessProbe: - httpGet: - # should match container.healthCheckPath - path: "/healthz" - port: 10254 - scheme: HTTP - initialDelaySeconds: 10 - periodSeconds: 10 - timeoutSeconds: 1 - successThreshold: 1 - failureThreshold: 5 - readinessProbe: - httpGet: - # should match container.healthCheckPath - path: "/healthz" - port: 10254 - scheme: HTTP - initialDelaySeconds: 10 - periodSeconds: 10 - timeoutSeconds: 1 - successThreshold: 1 - failureThreshold: 3 - - - # Path of the health check endpoint. All requests received on the port defined by - # the healthz-port parameter are forwarded internally to this path. - healthCheckPath: "/healthz" - - ## Annotations to be added to controller pods - ## - podAnnotations: {} - - replicaCount: 1 - - minAvailable: 1 - - # Define requests resources to avoid probe issues due to CPU utilization in busy nodes - # ref: https://github.com/kubernetes/ingress-nginx/issues/4735#issuecomment-551204903 - # Ideally, there should be no limits. - # https://engineering.indeedblog.com/blog/2019/12/cpu-throttling-regression-fix/ - resources: - # limits: - # cpu: 100m - # memory: 90Mi - requests: - cpu: 100m - memory: 90Mi - - # Mutually exclusive with keda autoscaling - autoscaling: - enabled: false - minReplicas: 1 - maxReplicas: 11 - targetCPUUtilizationPercentage: 50 - targetMemoryUtilizationPercentage: 50 - - autoscalingTemplate: [] - # Custom or additional autoscaling metrics - # ref: https://kubernetes.io/docs/tasks/run-application/horizontal-pod-autoscale/#support-for-custom-metrics - # - type: Pods - # pods: - # metric: - # name: nginx_ingress_controller_nginx_process_requests_total - # target: - # type: AverageValue - # averageValue: 10000m - - # Mutually exclusive with hpa autoscaling - keda: - apiVersion: "keda.sh/v1alpha1" - # apiVersion changes with keda 1.x vs 2.x - # 2.x = keda.sh/v1alpha1 - # 1.x = keda.k8s.io/v1alpha1 - enabled: false - minReplicas: 1 - maxReplicas: 11 - pollingInterval: 30 - cooldownPeriod: 300 - restoreToOriginalReplicaCount: false - scaledObject: - annotations: {} - # Custom annotations for ScaledObject resource - # annotations: - # key: value - triggers: [] - # - type: prometheus - # metadata: - # serverAddress: http://:9090 - # metricName: http_requests_total - # threshold: '100' - # query: sum(rate(http_requests_total{deployment="my-deployment"}[2m])) - - behavior: {} - # scaleDown: - # stabilizationWindowSeconds: 300 - # policies: - # - type: Pods - # value: 1 - # periodSeconds: 180 - # scaleUp: - # stabilizationWindowSeconds: 300 - # policies: - # - type: Pods - # value: 2 - # periodSeconds: 60 - - ## Enable mimalloc as a drop-in replacement for malloc. - ## ref: https://github.com/microsoft/mimalloc - ## - enableMimalloc: true - - ## Override NGINX template - customTemplate: - configMapName: "" - configMapKey: "" - - service: - enabled: true - - annotations: {} - labels: {} - # clusterIP: "" - - ## List of IP addresses at which the controller services are available - ## Ref: https://kubernetes.io/docs/user-guide/services/#external-ips - ## - externalIPs: [] - - # loadBalancerIP: "" - loadBalancerSourceRanges: [] - - enableHttp: true - enableHttps: true - - ## Set external traffic policy to: "Local" to preserve source IP on - ## providers supporting it - ## Ref: https://kubernetes.io/docs/tutorials/services/source-ip/#source-ip-for-services-with-typeloadbalancer - # externalTrafficPolicy: "" - - # Must be either "None" or "ClientIP" if set. Kubernetes will default to "None". - # Ref: https://kubernetes.io/docs/concepts/services-networking/service/#virtual-ips-and-service-proxies - # sessionAffinity: "" - - # specifies the health check node port (numeric port number) for the service. If healthCheckNodePort isn’t specified, - # the service controller allocates a port from your cluster’s NodePort range. - # Ref: https://kubernetes.io/docs/tasks/access-application-cluster/create-external-load-balancer/#preserving-the-client-source-ip - # healthCheckNodePort: 0 - - ports: - http: 80 - https: 443 - - targetPorts: - http: http - https: https - - type: LoadBalancer - - # type: NodePort - # nodePorts: - # http: 32080 - # https: 32443 - # tcp: - # 8080: 32808 - nodePorts: - http: "" - https: "" - tcp: {} - udp: {} - - ## Enables an additional internal load balancer (besides the external one). - ## Annotations are mandatory for the load balancer to come up. Varies with the cloud service. - internal: - enabled: false - annotations: {} - - # loadBalancerIP: "" - - ## Restrict access For LoadBalancer service. Defaults to 0.0.0.0/0. - loadBalancerSourceRanges: [] - - ## Set external traffic policy to: "Local" to preserve source IP on - ## providers supporting it - ## Ref: https://kubernetes.io/docs/tutorials/services/source-ip/#source-ip-for-services-with-typeloadbalancer - # externalTrafficPolicy: "" - - extraContainers: [] - ## Additional containers to be added to the controller pod. - ## See https://github.com/lemonldap-ng-controller/lemonldap-ng-controller as example. - # - name: my-sidecar - # image: nginx:latest - # - name: lemonldap-ng-controller - # image: lemonldapng/lemonldap-ng-controller:0.2.0 - # args: - # - /lemonldap-ng-controller - # - --alsologtostderr - # - --configmap=$(POD_NAMESPACE)/lemonldap-ng-configuration - # env: - # - name: POD_NAME - # valueFrom: - # fieldRef: - # fieldPath: metadata.name - # - name: POD_NAMESPACE - # valueFrom: - # fieldRef: - # fieldPath: metadata.namespace - # volumeMounts: - # - name: copy-portal-skins - # mountPath: /srv/var/lib/lemonldap-ng/portal/skins - - extraVolumeMounts: [] - ## Additional volumeMounts to the controller main container. - # - name: copy-portal-skins - # mountPath: /var/lib/lemonldap-ng/portal/skins - - extraVolumes: [] - ## Additional volumes to the controller pod. - # - name: copy-portal-skins - # emptyDir: {} - - extraInitContainers: [] - ## Containers, which are run before the app containers are started. - # - name: init-myservice - # image: busybox - # command: ['sh', '-c', 'until nslookup myservice; do echo waiting for myservice; sleep 2; done;'] - - admissionWebhooks: - annotations: {} - enabled: true - failurePolicy: Fail - # timeoutSeconds: 10 - port: 8443 - certificate: "/usr/local/certificates/cert" - key: "/usr/local/certificates/key" - namespaceSelector: {} - objectSelector: {} - - # Use an existing PSP instead of creating one - existingPsp: "" - - service: - annotations: {} - # clusterIP: "" - externalIPs: [] - # loadBalancerIP: "" - loadBalancerSourceRanges: [] - servicePort: 443 - type: ClusterIP - - patch: - enabled: true - image: - registry: docker.io - image: jettech/kube-webhook-certgen - # for backwards compatibility consider setting the full image url via the repository value below - # use *either* current default registry/image or repository format or installing chart by providing the values.yaml will fail - # repository: - tag: v1.5.1 - pullPolicy: IfNotPresent - ## Provide a priority class name to the webhook patching job - ## - priorityClassName: "" - podAnnotations: {} - nodeSelector: {} - tolerations: [] - runAsUser: 2000 - - metrics: - port: 10254 - # if this port is changed, change healthz-port: in extraArgs: accordingly - enabled: false - - service: - annotations: {} - # prometheus.io/scrape: "true" - # prometheus.io/port: "10254" - - # clusterIP: "" - - ## List of IP addresses at which the stats-exporter service is available - ## Ref: https://kubernetes.io/docs/user-guide/services/#external-ips - ## - externalIPs: [] - - # loadBalancerIP: "" - loadBalancerSourceRanges: [] - servicePort: 10254 - type: ClusterIP - # externalTrafficPolicy: "" - # nodePort: "" - - serviceMonitor: - enabled: false - additionalLabels: {} - # The label to use to retrieve the job name from. - # jobLabel: "app.kubernetes.io/name" - namespace: "" - namespaceSelector: {} - # Default: scrape .Release.Namespace only - # To scrape all, use the following: - # namespaceSelector: - # any: true - scrapeInterval: 30s - # honorLabels: true - targetLabels: [] - metricRelabelings: [] - - prometheusRule: - enabled: false - additionalLabels: {} - # namespace: "" - rules: [] - # # These are just examples rules, please adapt them to your needs - # - alert: NGINXConfigFailed - # expr: count(nginx_ingress_controller_config_last_reload_successful == 0) > 0 - # for: 1s - # labels: - # severity: critical - # annotations: - # description: bad ingress config - nginx config test failed - # summary: uninstall the latest ingress changes to allow config reloads to resume - # - alert: NGINXCertificateExpiry - # expr: (avg(nginx_ingress_controller_ssl_expire_time_seconds) by (host) - time()) < 604800 - # for: 1s - # labels: - # severity: critical - # annotations: - # description: ssl certificate(s) will expire in less then a week - # summary: renew expiring certificates to avoid downtime - # - alert: NGINXTooMany500s - # expr: 100 * ( sum( nginx_ingress_controller_requests{status=~"5.+"} ) / sum(nginx_ingress_controller_requests) ) > 5 - # for: 1m - # labels: - # severity: warning - # annotations: - # description: Too many 5XXs - # summary: More than 5% of all requests returned 5XX, this requires your attention - # - alert: NGINXTooMany400s - # expr: 100 * ( sum( nginx_ingress_controller_requests{status=~"4.+"} ) / sum(nginx_ingress_controller_requests) ) > 5 - # for: 1m - # labels: - # severity: warning - # annotations: - # description: Too many 4XXs - # summary: More than 5% of all requests returned 4XX, this requires your attention - - ## Improve connection draining when ingress controller pod is deleted using a lifecycle hook: - ## With this new hook, we increased the default terminationGracePeriodSeconds from 30 seconds - ## to 300, allowing the draining of connections up to five minutes. - ## If the active connections end before that, the pod will terminate gracefully at that time. - ## To effectively take advantage of this feature, the Configmap feature - ## worker-shutdown-timeout new value is 240s instead of 10s. - ## - lifecycle: - preStop: - exec: - command: - - /wait-shutdown - - priorityClassName: "" - -## Rollback limit -## -revisionHistoryLimit: 10 - -## Default 404 backend -## -defaultBackend: - ## - enabled: false - - name: defaultbackend - image: - registry: k8s.gcr.io - image: defaultbackend-amd64 - # for backwards compatibility consider setting the full image url via the repository value below - # use *either* current default registry/image or repository format or installing chart by providing the values.yaml will fail - # repository: - tag: "1.5" - pullPolicy: IfNotPresent - # nobody user -> uid 65534 - runAsUser: 65534 - runAsNonRoot: true - readOnlyRootFilesystem: true - allowPrivilegeEscalation: false - - # Use an existing PSP instead of creating one - existingPsp: "" - - extraArgs: {} - - serviceAccount: - create: true - name: "" - automountServiceAccountToken: true - ## Additional environment variables to set for defaultBackend pods - extraEnvs: [] - - port: 8080 - - ## Readiness and liveness probes for default backend - ## Ref: https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-probes/ - ## - livenessProbe: - failureThreshold: 3 - initialDelaySeconds: 30 - periodSeconds: 10 - successThreshold: 1 - timeoutSeconds: 5 - readinessProbe: - failureThreshold: 6 - initialDelaySeconds: 0 - periodSeconds: 5 - successThreshold: 1 - timeoutSeconds: 5 - - ## Node tolerations for server scheduling to nodes with taints - ## Ref: https://kubernetes.io/docs/concepts/configuration/assign-pod-node/ - ## - tolerations: [] - # - key: "key" - # operator: "Equal|Exists" - # value: "value" - # effect: "NoSchedule|PreferNoSchedule|NoExecute(1.6 only)" - - affinity: {} - - ## Security Context policies for controller pods - ## See https://kubernetes.io/docs/tasks/administer-cluster/sysctl-cluster/ for - ## notes on enabling and using sysctls - ## - podSecurityContext: {} - - # labels to add to the pod container metadata - podLabels: {} - # key: value - - ## Node labels for default backend pod assignment - ## Ref: https://kubernetes.io/docs/user-guide/node-selection/ - ## - nodeSelector: {} - - ## Annotations to be added to default backend pods - ## - podAnnotations: {} - - replicaCount: 1 - - minAvailable: 1 - - resources: {} - # limits: - # cpu: 10m - # memory: 20Mi - # requests: - # cpu: 10m - # memory: 20Mi - - extraVolumeMounts: [] - ## Additional volumeMounts to the default backend container. - # - name: copy-portal-skins - # mountPath: /var/lib/lemonldap-ng/portal/skins - - extraVolumes: [] - ## Additional volumes to the default backend pod. - # - name: copy-portal-skins - # emptyDir: {} - - autoscaling: - annotations: {} - enabled: false - minReplicas: 1 - maxReplicas: 2 - targetCPUUtilizationPercentage: 50 - targetMemoryUtilizationPercentage: 50 - - service: - annotations: {} - - # clusterIP: "" - - ## List of IP addresses at which the default backend service is available - ## Ref: https://kubernetes.io/docs/user-guide/services/#external-ips - ## - externalIPs: [] - - # loadBalancerIP: "" - loadBalancerSourceRanges: [] - servicePort: 80 - type: ClusterIP - - priorityClassName: "" - -## Enable RBAC as per https://github.com/kubernetes/ingress/tree/master/examples/rbac/nginx and https://github.com/kubernetes/ingress/issues/266 -rbac: - create: true - scope: false - -# If true, create & use Pod Security Policy resources -# https://kubernetes.io/docs/concepts/policy/pod-security-policy/ -podSecurityPolicy: - enabled: false - -serviceAccount: - create: true - name: "" - automountServiceAccountToken: true - -## Optional array of imagePullSecrets containing private registry credentials -## Ref: https://kubernetes.io/docs/tasks/configure-pod-container/pull-image-private-registry/ -imagePullSecrets: [] -# - name: secretName - -# TCP service key:value pairs -# Ref: https://github.com/kubernetes/contrib/tree/master/ingress/controllers/nginx/examples/tcp -## -tcp: {} -# 8080: "default/example-tcp-svc:9000" - -# UDP service key:value pairs -# Ref: https://github.com/kubernetes/contrib/tree/master/ingress/controllers/nginx/examples/udp -## -udp: {} -# 53: "kube-system/kube-dns:53" - -# A base64ed Diffie-Hellman parameter -# This can be generated with: openssl dhparam 4096 2> /dev/null | base64 -# Ref: https://github.com/krmichel/ingress-nginx/blob/master/docs/examples/customization/ssl-dh-param -dhParam: diff --git a/hosting/kubernetes/budibase/values.yaml b/hosting/kubernetes/budibase/values.yaml index 7b2ac7cb08..8b63fe5f90 100644 --- a/hosting/kubernetes/budibase/values.yaml +++ b/hosting/kubernetes/budibase/values.yaml @@ -123,7 +123,7 @@ services: enabled: true # disable if using external redis port: 6379 replicaCount: 1 - host: "" # only change if pointing to existing redis cluster and enabled: false + url: "" # only change if pointing to existing redis cluster and enabled: false password: "budibase" # recommended to override if using built-in redis storage: 100Mi From d355219d7ff90c43b2b965e46f15971e0bf2cab5 Mon Sep 17 00:00:00 2001 From: Martin McKeaveney Date: Tue, 17 Aug 2021 22:13:32 +0100 Subject: [PATCH 15/22] revert to master images --- .../kubernetes/budibase/templates/app-service-deployment.yaml | 2 +- .../budibase/templates/worker-service-deployment.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/hosting/kubernetes/budibase/templates/app-service-deployment.yaml b/hosting/kubernetes/budibase/templates/app-service-deployment.yaml index 5a4a95ab2a..daee03620f 100644 --- a/hosting/kubernetes/budibase/templates/app-service-deployment.yaml +++ b/hosting/kubernetes/budibase/templates/app-service-deployment.yaml @@ -90,7 +90,7 @@ spec: value: {{ .Values.globals.sentryDSN }} - name: WORKER_URL value: worker-service:{{ .Values.services.worker.port }} - image: budibase/apps:develop + image: budibase/apps imagePullPolicy: Always name: bbapps ports: diff --git a/hosting/kubernetes/budibase/templates/worker-service-deployment.yaml b/hosting/kubernetes/budibase/templates/worker-service-deployment.yaml index e69d6f6b7a..1af289f4aa 100644 --- a/hosting/kubernetes/budibase/templates/worker-service-deployment.yaml +++ b/hosting/kubernetes/budibase/templates/worker-service-deployment.yaml @@ -83,7 +83,7 @@ spec: {{ end }} - name: SELF_HOSTED value: {{ .Values.globals.selfHosted | quote }} - image: budibase/worker:develop + image: budibase/worker imagePullPolicy: Always name: bbworker ports: From 26421581eb8a640b3554dcbe420bd71b0b1ef204 Mon Sep 17 00:00:00 2001 From: Martin McKeaveney Date: Wed, 18 Aug 2021 10:47:39 +0100 Subject: [PATCH 16/22] allow couchDB to be turned off --- hosting/kubernetes/budibase/Chart.yaml | 1 + hosting/kubernetes/budibase/values.yaml | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/hosting/kubernetes/budibase/Chart.yaml b/hosting/kubernetes/budibase/Chart.yaml index 63351df4f2..c1683cf78d 100644 --- a/hosting/kubernetes/budibase/Chart.yaml +++ b/hosting/kubernetes/budibase/Chart.yaml @@ -34,6 +34,7 @@ dependencies: - name: couchdb version: 3.3.4 repository: https://apache.github.io/couchdb-helm + condition: services.couchdb.enabled - name: ingress-nginx version: 3.35.0 repository: https://github.com/kubernetes/ingress-nginx diff --git a/hosting/kubernetes/budibase/values.yaml b/hosting/kubernetes/budibase/values.yaml index 8b63fe5f90..e5ce7f141f 100644 --- a/hosting/kubernetes/budibase/values.yaml +++ b/hosting/kubernetes/budibase/values.yaml @@ -41,7 +41,7 @@ service: ingress: enabled: true - # certificateArn: "" + certificateArn: "" className: "" annotations: kubernetes.io/ingress.class: nginx @@ -112,6 +112,7 @@ services: replicaCount: 1 couchdb: + enabled: true replicaCount: 3 url: "" # only change if pointing to existing couch server user: "" # only change if pointing to existing couch server From c1957208a29f963f23382bd7bf5e08558af0da16 Mon Sep 17 00:00:00 2001 From: Martin McKeaveney Date: Wed, 18 Aug 2021 11:42:04 +0100 Subject: [PATCH 17/22] add secrets identifier --- hosting/kubernetes/budibase/templates/secrets.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hosting/kubernetes/budibase/templates/secrets.yaml b/hosting/kubernetes/budibase/templates/secrets.yaml index 1c0a914ed3..011ba27f22 100644 --- a/hosting/kubernetes/budibase/templates/secrets.yaml +++ b/hosting/kubernetes/budibase/templates/secrets.yaml @@ -2,7 +2,7 @@ apiVersion: v1 kind: Secret metadata: - name: {{ template "budibase.fullname" . }} + name: {{ template "budibase.fullname" . }}-secret labels: app: {{ template "budibase.fullname" . }} chart: "{{ .Chart.Name }}-{{ .Chart.Version }}" From 23e82b96362a324443a2a0bf1ad552bdc9c9aae8 Mon Sep 17 00:00:00 2001 From: Martin McKeaveney Date: Wed, 18 Aug 2021 11:43:07 +0100 Subject: [PATCH 18/22] remove secret identifier --- hosting/kubernetes/budibase/templates/secrets.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hosting/kubernetes/budibase/templates/secrets.yaml b/hosting/kubernetes/budibase/templates/secrets.yaml index 011ba27f22..1c0a914ed3 100644 --- a/hosting/kubernetes/budibase/templates/secrets.yaml +++ b/hosting/kubernetes/budibase/templates/secrets.yaml @@ -2,7 +2,7 @@ apiVersion: v1 kind: Secret metadata: - name: {{ template "budibase.fullname" . }}-secret + name: {{ template "budibase.fullname" . }} labels: app: {{ template "budibase.fullname" . }} chart: "{{ .Chart.Name }}-{{ .Chart.Version }}" From a0bba3466402a35bffd59ef4b5f7aeb1544ace66 Mon Sep 17 00:00:00 2001 From: Martin McKeaveney Date: Wed, 18 Aug 2021 14:55:22 +0100 Subject: [PATCH 19/22] testing chart releaser on CI pipeline --- .github/workflows/budibase_ci.yml | 7 +++++++ .github/workflows/release.yml | 6 ------ 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/.github/workflows/budibase_ci.yml b/.github/workflows/budibase_ci.yml index eb9eeebb37..18fe4e576e 100644 --- a/.github/workflows/budibase_ci.yml +++ b/.github/workflows/budibase_ci.yml @@ -42,3 +42,10 @@ jobs: name: codecov-umbrella verbose: true - run: yarn test:e2e:ci + + - name: Run chart-releaser + uses: helm/chart-releaser-action@v1.1.0 + with: + charts_dir: docs + env: + CR_TOKEN: "${{ secrets.GITHUB_TOKEN }}" \ No newline at end of file diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index b9c04c2cc7..f24e9482c0 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -51,9 +51,3 @@ jobs: DOCKER_USER: ${{ secrets.DOCKER_USERNAME }} DOCKER_PASSWORD: ${{ secrets.DOCKER_API_KEY }} - - name: Run chart-releaser - uses: helm/chart-releaser-action@v1.1.0 - with: - charts_dir: docs - env: - CR_TOKEN: "${{ secrets.GITHUB_TOKEN }}" From 5843fa0286c82425f31967f8678b3951a2c09086 Mon Sep 17 00:00:00 2001 From: Martin McKeaveney Date: Wed, 18 Aug 2021 18:09:40 +0100 Subject: [PATCH 20/22] test K8S CI --- .github/workflows/budibase_ci.yml | 6 ++++ docs/budibase-0.1.0.tgz | Bin 43415 -> 43708 bytes docs/budibase-0.1.1.tgz | Bin 0 -> 43710 bytes docs/index.yaml | 39 +++++++++++++++++++++++-- hosting/kubernetes/budibase/Chart.yaml | 2 +- scripts/release_helm_chart.sh | 3 ++ 6 files changed, 46 insertions(+), 4 deletions(-) create mode 100644 docs/budibase-0.1.1.tgz create mode 100755 scripts/release_helm_chart.sh diff --git a/.github/workflows/budibase_ci.yml b/.github/workflows/budibase_ci.yml index 18fe4e576e..999ff498c9 100644 --- a/.github/workflows/budibase_ci.yml +++ b/.github/workflows/budibase_ci.yml @@ -43,6 +43,12 @@ jobs: verbose: true - run: yarn test:e2e:ci + + - uses: azure/setup-helm@v1 + id: install + + - run: ./scripts/release_helm + - name: Run chart-releaser uses: helm/chart-releaser-action@v1.1.0 with: diff --git a/docs/budibase-0.1.0.tgz b/docs/budibase-0.1.0.tgz index 242aa79d909be283f88f922dae7013557e214dfb..7873874ab002c1d4bd6622aaa79c2b01e7d2b229 100644 GIT binary patch literal 43708 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 literal 43415 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 y0v$?*;p_Jg;QJ?Mw}t<-aPrJ|Dc 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%0D7b!WrRcif062$?TF4 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 literal 0 HcmV?d00001 diff --git a/docs/index.yaml b/docs/index.yaml index aedbc97da9..668033bc4c 100644 --- a/docs/index.yaml +++ b/docs/index.yaml @@ -3,9 +3,42 @@ entries: budibase: - apiVersion: v2 appVersion: 0.9.56 - created: "2021-08-11T23:12:24.184224+01:00" + created: "2021-08-18T18:06:38.067593+01:00" + dependencies: + - condition: services.couchdb.enabled + name: couchdb + repository: https://apache.github.io/couchdb-helm + version: 3.3.4 + - name: ingress-nginx + repository: https://github.com/kubernetes/ingress-nginx + version: 3.35.0 description: Budibase is an open source low-code platform, helping thousands of teams build apps for their workplace in minutes. - digest: 19bde738daee9c5fe0fd3aef74c41214be4f7f1b9951b277506684493a5bc3fe + digest: 4a9a4030fb9f37f6ae7370aff2fa253fee00b1e09e7bcf7ba9502740ebb8556e + keywords: + - low-code + - database + - cluster + name: budibase + sources: + - https://github.com/Budibase/budibase + - https://budibase.com + type: application + urls: + - https://budibase.github.io/budibase/budibase-0.1.1.tgz + version: 0.1.1 + - apiVersion: v2 + appVersion: 0.9.56 + created: "2021-08-18T18:06:38.062135+01:00" + dependencies: + - condition: services.couchdb.enabled + name: couchdb + repository: https://apache.github.io/couchdb-helm + version: 3.3.4 + - name: ingress-nginx + repository: https://github.com/kubernetes/ingress-nginx + version: 3.35.0 + description: Budibase is an open source low-code platform, helping thousands of teams build apps for their workplace in minutes. + digest: 08031b0803cce0eff64472e569d454d9176119c8207aa9873a9c95ee66cc7d3f keywords: - low-code - database @@ -18,4 +51,4 @@ entries: urls: - https://budibase.github.io/budibase/budibase-0.1.0.tgz version: 0.1.0 -generated: "2021-08-11T23:12:24.178479+01:00" +generated: "2021-08-18T18:06:38.057522+01:00" diff --git a/hosting/kubernetes/budibase/Chart.yaml b/hosting/kubernetes/budibase/Chart.yaml index c1683cf78d..bcba5f5059 100644 --- a/hosting/kubernetes/budibase/Chart.yaml +++ b/hosting/kubernetes/budibase/Chart.yaml @@ -22,7 +22,7 @@ type: application # This is the chart version. This version number should be incremented each time you make changes # to the chart and its templates, including the app version. # Versions are expected to follow Semantic Versioning (https://semver.org/) -version: 0.1.0 +version: 0.1.1 # This is the version number of the application being deployed. This version number should be # incremented each time you make changes to the application. Versions are not expected to diff --git a/scripts/release_helm_chart.sh b/scripts/release_helm_chart.sh new file mode 100755 index 0000000000..b1594cca91 --- /dev/null +++ b/scripts/release_helm_chart.sh @@ -0,0 +1,3 @@ +cd docs +helm package ../hosting/kubernetes/budibase +helm repo index . --url https://budibase.github.io/budibase \ No newline at end of file From d811eb4e0daa66c12f5646474912886040076c66 Mon Sep 17 00:00:00 2001 From: Martin McKeaveney Date: Wed, 18 Aug 2021 18:42:22 +0100 Subject: [PATCH 21/22] use NPM script to run helm release --- .github/workflows/budibase_ci.yml | 2 +- docs/budibase-0.1.1.tgz | Bin 43710 -> 43391 bytes docs/index.yaml | 8 ++++---- package.json | 1 + 4 files changed, 6 insertions(+), 5 deletions(-) diff --git a/.github/workflows/budibase_ci.yml b/.github/workflows/budibase_ci.yml index 999ff498c9..8343e62838 100644 --- a/.github/workflows/budibase_ci.yml +++ b/.github/workflows/budibase_ci.yml @@ -47,7 +47,7 @@ jobs: - uses: azure/setup-helm@v1 id: install - - run: ./scripts/release_helm + - run: yarn release:helm - name: Run chart-releaser uses: helm/chart-releaser-action@v1.1.0 diff --git a/docs/budibase-0.1.1.tgz b/docs/budibase-0.1.1.tgz index d415a4db9764ddf10c83d3a59a683baa74ebed94..b38527c4a40a10a6f1d5afa8627fc5237a69da21 100644 GIT binary patch literal 43391 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%0D7b!WrRcif062$?TF4 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 diff --git a/docs/index.yaml b/docs/index.yaml index 668033bc4c..4e064f3dd0 100644 --- a/docs/index.yaml +++ b/docs/index.yaml @@ -3,7 +3,7 @@ entries: budibase: - apiVersion: v2 appVersion: 0.9.56 - created: "2021-08-18T18:06:38.067593+01:00" + created: "2021-08-18T18:41:52.640176+01:00" dependencies: - condition: services.couchdb.enabled name: couchdb @@ -13,7 +13,7 @@ entries: repository: https://github.com/kubernetes/ingress-nginx version: 3.35.0 description: Budibase is an open source low-code platform, helping thousands of teams build apps for their workplace in minutes. - digest: 4a9a4030fb9f37f6ae7370aff2fa253fee00b1e09e7bcf7ba9502740ebb8556e + digest: 8dc4f2ed4d98cad5adf25936aefea680042d3e4e17832f846b961fd8708ad192 keywords: - low-code - database @@ -28,7 +28,7 @@ entries: version: 0.1.1 - apiVersion: v2 appVersion: 0.9.56 - created: "2021-08-18T18:06:38.062135+01:00" + created: "2021-08-18T18:41:52.635603+01:00" dependencies: - condition: services.couchdb.enabled name: couchdb @@ -51,4 +51,4 @@ entries: urls: - https://budibase.github.io/budibase/budibase-0.1.0.tgz version: 0.1.0 -generated: "2021-08-18T18:06:38.057522+01:00" +generated: "2021-08-18T18:41:52.629415+01:00" diff --git a/package.json b/package.json index 5532cf87a9..552b8d1b0d 100644 --- a/package.json +++ b/package.json @@ -44,6 +44,7 @@ "test:e2e:ci": "lerna run cy:ci", "build:docker": "lerna run build:docker && cd hosting/scripts/linux/ && ./release-to-docker-hub.sh && cd -", "build:docker:develop": "node scripts/pinVersions && lerna run build:docker && cd hosting/scripts/linux/ && ./release-to-docker-hub.sh develop && cd -", + "release:helm": "./scripts/release_helm_chart.sh", "multi:enable": "lerna run multi:enable", "multi:disable": "lerna run multi:disable" } From 2d5cd7db23be482cd290d205d102b58e2adccdb9 Mon Sep 17 00:00:00 2001 From: Martin McKeaveney Date: Thu, 19 Aug 2021 07:53:17 +0100 Subject: [PATCH 22/22] move all the helm CI to the release pipeline --- .github/workflows/budibase_ci.yml | 13 ------------- .github/workflows/release.yml | 11 +++++++++++ 2 files changed, 11 insertions(+), 13 deletions(-) diff --git a/.github/workflows/budibase_ci.yml b/.github/workflows/budibase_ci.yml index 8343e62838..eb9eeebb37 100644 --- a/.github/workflows/budibase_ci.yml +++ b/.github/workflows/budibase_ci.yml @@ -42,16 +42,3 @@ jobs: name: codecov-umbrella verbose: true - run: yarn test:e2e:ci - - - - uses: azure/setup-helm@v1 - id: install - - - run: yarn release:helm - - - name: Run chart-releaser - uses: helm/chart-releaser-action@v1.1.0 - with: - charts_dir: docs - env: - CR_TOKEN: "${{ secrets.GITHUB_TOKEN }}" \ No newline at end of file diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index f24e9482c0..d016c10520 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -51,3 +51,14 @@ jobs: DOCKER_USER: ${{ secrets.DOCKER_USERNAME }} DOCKER_PASSWORD: ${{ secrets.DOCKER_API_KEY }} + - uses: azure/setup-helm@v1 + id: install + + - run: yarn release:helm + + - name: Run chart-releaser + uses: helm/chart-releaser-action@v1.1.0 + with: + charts_dir: docs + env: + CR_TOKEN: "${{ secrets.GITHUB_TOKEN }}" \ No newline at end of file