From a80655df046c2bfd41928f4c01b1cf2f01c51d16 Mon Sep 17 00:00:00 2001 From: Peter Clement Date: Mon, 16 Aug 2021 09:44:15 +0100 Subject: [PATCH 001/110] Bug fix for unable to navigate from sql screen --- .../[selectedDatasource]/index.svelte | 18 ------------------ 1 file changed, 18 deletions(-) diff --git a/packages/builder/src/pages/builder/app/[application]/data/datasource/[selectedDatasource]/index.svelte b/packages/builder/src/pages/builder/app/[application]/data/datasource/[selectedDatasource]/index.svelte index b0377d2f2..0cd667a82 100644 --- a/packages/builder/src/pages/builder/app/[application]/data/datasource/[selectedDatasource]/index.svelte +++ b/packages/builder/src/pages/builder/app/[application]/data/datasource/[selectedDatasource]/index.svelte @@ -9,7 +9,6 @@ import ICONS from "components/backend/DatasourceNavigator/icons" import { capitalise } from "helpers" - let unsaved = false let relationshipModal let displayColumnModal let selectedFromRelationship, selectedToRelationship @@ -71,7 +70,6 @@ // Create datasource await datasources.save(datasource) notifications.success(`Datasource ${name} updated successfully.`) - unsaved = false } catch (err) { notifications.error(`Error saving datasource: ${err}`) } @@ -81,7 +79,6 @@ try { await datasources.updateSchema(datasource) notifications.success(`Datasource ${name} tables updated successfully.`) - unsaved = false await tables.fetch() } catch (err) { notifications.error(`Error updating datasource schema: ${err}`) @@ -98,10 +95,6 @@ $goto(`../../table/${table._id}`) } - function setUnsaved() { - unsaved = true - } - function openRelationshipModal(fromRelationship, toRelationship) { selectedFromRelationship = fromRelationship || {} selectedToRelationship = toRelationship || {} @@ -111,16 +104,6 @@ function openDisplayColumnModal() { displayColumnModal.show() } - - $beforeUrlChange(() => { - if (unsaved) { - notifications.error( - "Unsaved changes. Please save your datasource configuration before leaving." - ) - return false - } - return true - }) @@ -164,7 +147,6 @@ {#if datasource.plus} From a248084c56a8aa1f5d3a4c6fd3d0eaa16c23dab6 Mon Sep 17 00:00:00 2001 From: Martin McKeaveney Date: Mon, 16 Aug 2021 11:42:21 +0100 Subject: [PATCH 002/110] 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 000000000..0fa6060f8 --- /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 000000000..aedbc97da --- /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 000000000..0e8a0eb36 --- /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 000000000..63351df4f --- /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 000000000..9de545d36 --- /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 000000000..74ae734a1 --- /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 000000000..23da0fcff --- /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 000000000..3227123d0 --- /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 000000000..79589d2e0 --- /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 000000000..aa570bdf7 --- /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 000000000..4ce8445aa --- /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 000000000..a3658bd37 --- /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 000000000..f9d013e48 --- /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 000000000..a6a20e057 --- /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 000000000..0ce3ef0f3 --- /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 000000000..c547847ce --- /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 000000000..2830708be --- /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 000000000..92f55c6d6 --- /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 000000000..6d0382477 --- /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 000000000..bb82799a4 --- /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 000000000..6225fbe98 --- /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 000000000..5a5025f81 --- /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 000000000..50af03172 --- /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 000000000..36526e082 --- /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 000000000..70e0df619 --- /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 000000000..392bc92f5 --- /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 000000000..d5a2c3ca8 --- /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 000000000..e12b53421 --- /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 000000000..cfc545f69 --- /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 000000000..ff82cd9c7 --- /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 000000000..d8948d634 --- /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 000000000..6d6605f0e --- /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 000000000..04ac58dbd --- /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 000000000..afb5487c5 --- /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 000000000..7b4d7cbe7 --- /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 000000000..a359a6a40 --- /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 000000000..e63a7f5db --- /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 000000000..1e5190afc --- /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 000000000..017b60a9c --- /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 000000000..88aafc66f --- /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 000000000..6e3b371da --- /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 000000000..5314cecb3 --- /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 000000000..f232531ac --- /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 000000000..9eda282b1 --- /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 000000000..93a393c97 --- /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 000000000..665fd48d3 --- /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 000000000..c7f22d636 --- /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 000000000..887ed0f62 --- /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 000000000..84f1f7582 --- /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 000000000..b65a0910b --- /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 000000000..e339c69c3 --- /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 000000000..141e06b68 --- /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 000000000..bc29abeba --- /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 000000000..b7f54c09f --- /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 000000000..a829c3614 --- /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 000000000..4f18a70b9 --- /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 000000000..60fb2c1f6 --- /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 000000000..8b1fd0951 --- /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 000000000..fd762f935 --- /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 000000000..4990fb1c3 --- /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 000000000..3656be487 --- /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 000000000..9e9bd0138 --- /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 000000000..d2c7de685 --- /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 000000000..9b083ee6e --- /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 000000000..edda07f5d --- /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 000000000..1ff0f7f0e --- /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 000000000..2f3dd7784 --- /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 000000000..8ec5f49fa --- /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 000000000..81be52b87 --- /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 000000000..e0b7a0f21 --- /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 000000000..91f22f03d --- /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 000000000..aaf336fb3 --- /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 000000000..7f46791ec --- /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 000000000..630545140 --- /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 000000000..2f6def589 --- /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 000000000..7e2d223a9 --- /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 000000000..fb14bdf6a --- /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 000000000..f94b9590d --- /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 000000000..c7eebf5c8 --- /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 000000000..a5a425f74 --- /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 000000000..ca5427523 --- /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 000000000..bdb856310 --- /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 000000000..1a5ccd29b --- /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 000000000..5ec3bc774 --- /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 000000000..0bb966127 --- /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 000000000..1b690192c --- /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 000000000..228cb59d8 --- /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 000000000..908291cff --- /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 000000000..50a718d32 --- /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 000000000..066488a04 --- /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 000000000..99345269b --- /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 000000000..e31fda3f9 --- /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 000000000..153f005e2 --- /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 000000000..716dbf16f --- /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 000000000..5d29a2d52 --- /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 000000000..4a9cb9284 --- /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 000000000..7624ab36c --- /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 000000000..0c00e9369 --- /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 000000000..12e7a4f63 --- /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 000000000..f5496eb61 --- /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 000000000..2ace0aa38 --- /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 000000000..67485ac2a --- /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 000000000..9f23089fd --- /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 000000000..5247b4de0 --- /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 000000000..2f901b466 --- /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 000000000..4de295e18 --- /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 000000000..a016824d0 --- /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 000000000..405044c92 --- /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 000000000..61040e4f4 --- /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 000000000..0f802da84 --- /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 000000000..8f14d9786 --- /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 000000000..2cb5ee8ea --- /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 000000000..9235b0b11 --- /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 000000000..55ca40ed8 --- /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 000000000..09ee77511 --- /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 000000000..be4d932b5 --- /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 000000000..1aa102008 --- /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 000000000..ecd1f361d --- /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 000000000..79cc078f9 --- /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 000000000..a79ba1e04 --- /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 000000000..8021a4603 --- /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 000000000..fd3ab24e6 --- /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 000000000..96334fa72 --- /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 000000000..0e7859d88 --- /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 d50d72b18..f79efb3e9 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 8da98e99e..dec17ab28 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 003/110] 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 67485ac2a..3b0853e19 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 9f23089fd..16b7bddde 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 a016824d0..d122ad0a3 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 405044c92..a23d0c1d8 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 61040e4f4..cfdb22002 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 09ee77511..1c0a914ed 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 79cc078f9..1596b74be 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 fd3ab24e6..a71ad09b5 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 f79efb3e9..7523a53c8 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 769c37a11d6f53ed2026ee60c5c2a5cac369901a Mon Sep 17 00:00:00 2001 From: Martin McKeaveney Date: Mon, 16 Aug 2021 15:58:04 +0100 Subject: [PATCH 004/110] couchdb direct auth --- packages/auth/src/db/utils.js | 8 +++++++- packages/server/scripts/dev/manage.js | 2 +- packages/server/src/db/client.js | 4 ++++ packages/server/src/environment.js | 2 ++ packages/worker/scripts/dev/manage.js | 2 +- packages/worker/src/db/index.js | 4 ++++ packages/worker/src/environment.js | 3 +-- 7 files changed, 20 insertions(+), 5 deletions(-) diff --git a/packages/auth/src/db/utils.js b/packages/auth/src/db/utils.js index 1fc78f418..e3a86f8ae 100644 --- a/packages/auth/src/db/utils.js +++ b/packages/auth/src/db/utils.js @@ -168,7 +168,13 @@ exports.getAllDbs = async () => { if (env.isTest()) { return getCouch().allDbs() } - const response = await fetch(`${env.COUCH_DB_URL}/_all_dbs`) + const response = await fetch(`${env.COUCH_DB_URL}/_all_dbs`, { + method: "POST", + body: JSON.stringify({ + name: env.COUCH_DB_USERNAME, + password: env.COUCH_DB_PASSWORD, + }), + }) if (response.status === 200) { return response.json() } else { diff --git a/packages/server/scripts/dev/manage.js b/packages/server/scripts/dev/manage.js index ffd8c6b9e..7e9d7f6a0 100644 --- a/packages/server/scripts/dev/manage.js +++ b/packages/server/scripts/dev/manage.js @@ -36,7 +36,7 @@ async function init() { const envFileJson = { PORT: 4001, MINIO_URL: "http://localhost:10000/", - COUCH_DB_URL: "http://budibase:budibase@localhost:10000/db/", + COUCH_DB_URL: "http://localhost:10000/db/", REDIS_URL: "localhost:6379", WORKER_URL: "http://localhost:4002", INTERNAL_API_KEY: "budibase", diff --git a/packages/server/src/db/client.js b/packages/server/src/db/client.js index d50d72b18..887b287c8 100644 --- a/packages/server/src/db/client.js +++ b/packages/server/src/db/client.js @@ -12,6 +12,10 @@ PouchDB.adapter("writableStream", replicationStream.adapters.writableStream) let POUCH_DB_DEFAULTS = { prefix: COUCH_DB_URL, + auth: { + username: env.COUCH_DB_USERNAME, + password: env.COUCH_DB_PASSWORD, + } } if (env.isTest()) { diff --git a/packages/server/src/environment.js b/packages/server/src/environment.js index 52c680f65..45c767385 100644 --- a/packages/server/src/environment.js +++ b/packages/server/src/environment.js @@ -23,6 +23,8 @@ module.exports = { // important PORT: process.env.PORT, JWT_SECRET: process.env.JWT_SECRET, + COUCH_DB_USERNAME: process.env.COUCH_DB_USER, + COUCH_DB_PASSWORD: process.env.COUCH_DB_PASSWORD, COUCH_DB_URL: process.env.COUCH_DB_URL, MINIO_URL: process.env.MINIO_URL, WORKER_URL: process.env.WORKER_URL, diff --git a/packages/worker/scripts/dev/manage.js b/packages/worker/scripts/dev/manage.js index b9d28b627..d7eb76fc7 100644 --- a/packages/worker/scripts/dev/manage.js +++ b/packages/worker/scripts/dev/manage.js @@ -17,7 +17,7 @@ async function init() { REDIS_URL: "localhost:6379", REDIS_PASSWORD: "budibase", MINIO_URL: "http://localhost:10000/", - COUCH_DB_URL: "http://budibase:budibase@localhost:10000/db/", + COUCH_DB_URL: "http://localhost:10000/db/", } let envFile = "" Object.keys(envFileJson).forEach(key => { diff --git a/packages/worker/src/db/index.js b/packages/worker/src/db/index.js index d6d035cff..d09ddf065 100644 --- a/packages/worker/src/db/index.js +++ b/packages/worker/src/db/index.js @@ -7,6 +7,10 @@ const COUCH_DB_URL = env.COUCH_DB_URL || "http://localhost:10000/db/" let POUCH_DB_DEFAULTS = { prefix: COUCH_DB_URL, + auth: { + username: env.COUCH_DB_USERNAME, + password: env.COUCH_DB_PASSWORD, + } } if (env.isTest()) { diff --git a/packages/worker/src/environment.js b/packages/worker/src/environment.js index 384230b9b..e8666e6b8 100644 --- a/packages/worker/src/environment.js +++ b/packages/worker/src/environment.js @@ -23,7 +23,6 @@ module.exports = { MINIO_ACCESS_KEY: process.env.MINIO_ACCESS_KEY, MINIO_SECRET_KEY: process.env.MINIO_SECRET_KEY, MINIO_URL: process.env.MINIO_URL, - COUCH_DB_URL: process.env.COUCH_DB_URL, LOG_LEVEL: process.env.LOG_LEVEL, JWT_SECRET: process.env.JWT_SECRET, SALT_ROUNDS: process.env.SALT_ROUNDS, @@ -31,7 +30,7 @@ module.exports = { REDIS_PASSWORD: process.env.REDIS_PASSWORD, INTERNAL_API_KEY: process.env.INTERNAL_API_KEY, /* TODO: to remove - once deployment removed */ - COUCH_DB_USERNAME: process.env.COUCH_DB_USERNAME, + COUCH_DB_USERNAME: process.env.COUCH_DB_USER, COUCH_DB_PASSWORD: process.env.COUCH_DB_PASSWORD, _set(key, value) { process.env[key] = value From beb16a733e70ceb5394fb2a7759d3d4acb39dce5 Mon Sep 17 00:00:00 2001 From: Martin McKeaveney Date: Mon, 16 Aug 2021 16:38:00 +0100 Subject: [PATCH 005/110] utility helper for determining correct couchDB URL --- packages/auth/src/db/utils.js | 21 ++++++++++++------- packages/auth/src/environment.js | 2 ++ .../src/api/controllers/row/internalSearch.js | 3 ++- 3 files changed, 17 insertions(+), 9 deletions(-) diff --git a/packages/auth/src/db/utils.js b/packages/auth/src/db/utils.js index e3a86f8ae..7c44664a6 100644 --- a/packages/auth/src/db/utils.js +++ b/packages/auth/src/db/utils.js @@ -159,6 +159,17 @@ exports.getDeployedAppID = appId => { return appId } +exports.getCouchUrl = () => { + // username and password already exist in URL + if (env.COUCH_DB_URL.includes("@")) { + return env.COUCH_DB_URL + } + + const [protocol, ...rest] = env.COUCH_DB_URL.split("://") + + return `${protocol}://${env.COUCH_DB_USERNAME}:${env.COUCH_DB_PASSWORD}@${rest}` +} + /** * if in production this will use the CouchDB _all_dbs call to retrieve a list of databases. If testing * when using Pouch it will use the pouchdb-all-dbs package. @@ -168,13 +179,7 @@ exports.getAllDbs = async () => { if (env.isTest()) { return getCouch().allDbs() } - const response = await fetch(`${env.COUCH_DB_URL}/_all_dbs`, { - method: "POST", - body: JSON.stringify({ - name: env.COUCH_DB_USERNAME, - password: env.COUCH_DB_PASSWORD, - }), - }) + const response = await fetch(`${exports.getCouchUrl()}/_all_dbs`) if (response.status === 200) { return response.json() } else { @@ -309,4 +314,4 @@ exports.Replication = Replication exports.getScopedConfig = getScopedConfig exports.generateConfigID = generateConfigID exports.getConfigParams = getConfigParams -exports.getScopedFullConfig = getScopedFullConfig +exports.getScopedFullConfig = getScopedFullConfig \ No newline at end of file diff --git a/packages/auth/src/environment.js b/packages/auth/src/environment.js index 355843d02..e64a1c75f 100644 --- a/packages/auth/src/environment.js +++ b/packages/auth/src/environment.js @@ -9,6 +9,8 @@ function isTest() { module.exports = { JWT_SECRET: process.env.JWT_SECRET, COUCH_DB_URL: process.env.COUCH_DB_URL, + COUCH_DB_USERNAME: process.env.COUCH_DB_USER, + COUCH_DB_PASSWORD: process.env.COUCH_DB_PASSWORD, SALT_ROUNDS: process.env.SALT_ROUNDS, REDIS_URL: process.env.REDIS_URL, REDIS_PASSWORD: process.env.REDIS_PASSWORD, diff --git a/packages/server/src/api/controllers/row/internalSearch.js b/packages/server/src/api/controllers/row/internalSearch.js index c13091d6e..20147a785 100644 --- a/packages/server/src/api/controllers/row/internalSearch.js +++ b/packages/server/src/api/controllers/row/internalSearch.js @@ -1,6 +1,7 @@ const { SearchIndexes } = require("../../../db/utils") const env = require("../../../environment") const fetch = require("node-fetch") +const { getCouchUrl } = require("@budibase/auth/db") /** * Class to build lucene query URLs. @@ -233,7 +234,7 @@ class QueryBuilder { } async run() { - const url = `${env.COUCH_DB_URL}/${this.appId}/_design/database/_search/${SearchIndexes.ROWS}` + const url = `${getCouchUrl()}/${this.appId}/_design/database/_search/${SearchIndexes.ROWS}` const body = this.buildSearchBody() return await runQuery(url, body) } From a06325cace97b2889f2bc2bc869ade8ff69a39f4 Mon Sep 17 00:00:00 2001 From: Martin McKeaveney Date: Mon, 16 Aug 2021 16:39:10 +0100 Subject: [PATCH 006/110] readd couchDB url to env --- packages/worker/src/environment.js | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/worker/src/environment.js b/packages/worker/src/environment.js index e8666e6b8..1fc68ccc7 100644 --- a/packages/worker/src/environment.js +++ b/packages/worker/src/environment.js @@ -23,6 +23,7 @@ module.exports = { MINIO_ACCESS_KEY: process.env.MINIO_ACCESS_KEY, MINIO_SECRET_KEY: process.env.MINIO_SECRET_KEY, MINIO_URL: process.env.MINIO_URL, + COUCH_DB_URL: process.env.COUCH_DB_URL, LOG_LEVEL: process.env.LOG_LEVEL, JWT_SECRET: process.env.JWT_SECRET, SALT_ROUNDS: process.env.SALT_ROUNDS, From cedc78e4902d7624fa7ae210288cbfd5dfb56f85 Mon Sep 17 00:00:00 2001 From: Martin McKeaveney Date: Mon, 16 Aug 2021 16:44:46 +0100 Subject: [PATCH 007/110] match up env var in worker --- packages/worker/src/environment.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/worker/src/environment.js b/packages/worker/src/environment.js index 1fc68ccc7..384230b9b 100644 --- a/packages/worker/src/environment.js +++ b/packages/worker/src/environment.js @@ -31,7 +31,7 @@ module.exports = { REDIS_PASSWORD: process.env.REDIS_PASSWORD, INTERNAL_API_KEY: process.env.INTERNAL_API_KEY, /* TODO: to remove - once deployment removed */ - COUCH_DB_USERNAME: process.env.COUCH_DB_USER, + COUCH_DB_USERNAME: process.env.COUCH_DB_USERNAME, COUCH_DB_PASSWORD: process.env.COUCH_DB_PASSWORD, _set(key, value) { process.env[key] = value From 0bfbedac80288de6ec820e596a7c870fdfcdfd23 Mon Sep 17 00:00:00 2001 From: Martin McKeaveney Date: Mon, 16 Aug 2021 16:47:26 +0100 Subject: [PATCH 008/110] lint --- packages/server/src/api/controllers/row/internalSearch.js | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/server/src/api/controllers/row/internalSearch.js b/packages/server/src/api/controllers/row/internalSearch.js index 20147a785..c5bd01b38 100644 --- a/packages/server/src/api/controllers/row/internalSearch.js +++ b/packages/server/src/api/controllers/row/internalSearch.js @@ -1,5 +1,4 @@ const { SearchIndexes } = require("../../../db/utils") -const env = require("../../../environment") const fetch = require("node-fetch") const { getCouchUrl } = require("@budibase/auth/db") From 2661d1c0b4edb5020299353b451070064fbe58e1 Mon Sep 17 00:00:00 2001 From: Martin McKeaveney Date: Mon, 16 Aug 2021 17:17:48 +0100 Subject: [PATCH 009/110] lint :sparkles: --- packages/auth/src/db/utils.js | 2 +- packages/server/src/api/controllers/row/internalSearch.js | 4 +++- packages/server/src/db/client.js | 2 +- packages/worker/src/db/index.js | 2 +- 4 files changed, 6 insertions(+), 4 deletions(-) diff --git a/packages/auth/src/db/utils.js b/packages/auth/src/db/utils.js index 7c44664a6..2ac23d02a 100644 --- a/packages/auth/src/db/utils.js +++ b/packages/auth/src/db/utils.js @@ -314,4 +314,4 @@ exports.Replication = Replication exports.getScopedConfig = getScopedConfig exports.generateConfigID = generateConfigID exports.getConfigParams = getConfigParams -exports.getScopedFullConfig = getScopedFullConfig \ No newline at end of file +exports.getScopedFullConfig = getScopedFullConfig diff --git a/packages/server/src/api/controllers/row/internalSearch.js b/packages/server/src/api/controllers/row/internalSearch.js index c5bd01b38..cc35da40e 100644 --- a/packages/server/src/api/controllers/row/internalSearch.js +++ b/packages/server/src/api/controllers/row/internalSearch.js @@ -233,7 +233,9 @@ class QueryBuilder { } async run() { - const url = `${getCouchUrl()}/${this.appId}/_design/database/_search/${SearchIndexes.ROWS}` + const url = `${getCouchUrl()}/${this.appId}/_design/database/_search/${ + SearchIndexes.ROWS + }` const body = this.buildSearchBody() return await runQuery(url, body) } diff --git a/packages/server/src/db/client.js b/packages/server/src/db/client.js index 887b287c8..9a244fee1 100644 --- a/packages/server/src/db/client.js +++ b/packages/server/src/db/client.js @@ -15,7 +15,7 @@ let POUCH_DB_DEFAULTS = { auth: { username: env.COUCH_DB_USERNAME, password: env.COUCH_DB_PASSWORD, - } + }, } if (env.isTest()) { diff --git a/packages/worker/src/db/index.js b/packages/worker/src/db/index.js index d09ddf065..c0676634f 100644 --- a/packages/worker/src/db/index.js +++ b/packages/worker/src/db/index.js @@ -10,7 +10,7 @@ let POUCH_DB_DEFAULTS = { auth: { username: env.COUCH_DB_USERNAME, password: env.COUCH_DB_PASSWORD, - } + }, } if (env.isTest()) { From 0cc16ee5c299d038c0a8272a34285321fae2491a Mon Sep 17 00:00:00 2001 From: Budibase Release Bot <> Date: Mon, 16 Aug 2021 16:35:51 +0000 Subject: [PATCH 010/110] v0.9.106 --- lerna.json | 2 +- packages/auth/package.json | 2 +- packages/bbui/package.json | 2 +- packages/builder/package.json | 8 ++++---- packages/cli/package.json | 2 +- packages/client/package.json | 8 ++++---- packages/server/package.json | 10 +++++----- packages/standard-components/package.json | 4 ++-- packages/string-templates/package.json | 2 +- packages/worker/package.json | 6 +++--- 10 files changed, 23 insertions(+), 23 deletions(-) diff --git a/lerna.json b/lerna.json index 825ab2f1f..30b42fc1e 100644 --- a/lerna.json +++ b/lerna.json @@ -1,5 +1,5 @@ { - "version": "0.9.105", + "version": "0.9.106", "npmClient": "yarn", "packages": [ "packages/*" diff --git a/packages/auth/package.json b/packages/auth/package.json index ea638f859..db542205f 100644 --- a/packages/auth/package.json +++ b/packages/auth/package.json @@ -1,6 +1,6 @@ { "name": "@budibase/auth", - "version": "0.9.105", + "version": "0.9.106", "description": "Authentication middlewares for budibase builder and apps", "main": "src/index.js", "author": "Budibase", diff --git a/packages/bbui/package.json b/packages/bbui/package.json index c8f93c9df..b1951d987 100644 --- a/packages/bbui/package.json +++ b/packages/bbui/package.json @@ -1,7 +1,7 @@ { "name": "@budibase/bbui", "description": "A UI solution used in the different Budibase projects.", - "version": "0.9.105", + "version": "0.9.106", "license": "AGPL-3.0", "svelte": "src/index.js", "module": "dist/bbui.es.js", diff --git a/packages/builder/package.json b/packages/builder/package.json index a7ed98b63..9595ad603 100644 --- a/packages/builder/package.json +++ b/packages/builder/package.json @@ -1,6 +1,6 @@ { "name": "@budibase/builder", - "version": "0.9.105", + "version": "0.9.106", "license": "AGPL-3.0", "private": true, "scripts": { @@ -65,10 +65,10 @@ } }, "dependencies": { - "@budibase/bbui": "^0.9.105", - "@budibase/client": "^0.9.105", + "@budibase/bbui": "^0.9.106", + "@budibase/client": "^0.9.106", "@budibase/colorpicker": "1.1.2", - "@budibase/string-templates": "^0.9.105", + "@budibase/string-templates": "^0.9.106", "@sentry/browser": "5.19.1", "@spectrum-css/page": "^3.0.1", "@spectrum-css/vars": "^3.0.1", diff --git a/packages/cli/package.json b/packages/cli/package.json index 0dc653a29..aa5b558bd 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -1,6 +1,6 @@ { "name": "@budibase/cli", - "version": "0.9.105", + "version": "0.9.106", "description": "Budibase CLI, for developers, self hosting and migrations.", "main": "src/index.js", "bin": { diff --git a/packages/client/package.json b/packages/client/package.json index 6fe0e6b3b..4fe16bc42 100644 --- a/packages/client/package.json +++ b/packages/client/package.json @@ -1,6 +1,6 @@ { "name": "@budibase/client", - "version": "0.9.105", + "version": "0.9.106", "license": "MPL-2.0", "module": "dist/budibase-client.js", "main": "dist/budibase-client.js", @@ -18,9 +18,9 @@ "dev:builder": "rollup -cw" }, "dependencies": { - "@budibase/bbui": "^0.9.105", - "@budibase/standard-components": "^0.9.105", - "@budibase/string-templates": "^0.9.105", + "@budibase/bbui": "^0.9.106", + "@budibase/standard-components": "^0.9.106", + "@budibase/string-templates": "^0.9.106", "regexparam": "^1.3.0", "shortid": "^2.2.15", "svelte-spa-router": "^3.0.5" diff --git a/packages/server/package.json b/packages/server/package.json index 7ca543743..ad9a14682 100644 --- a/packages/server/package.json +++ b/packages/server/package.json @@ -1,7 +1,7 @@ { "name": "@budibase/server", "email": "hi@budibase.com", - "version": "0.9.105", + "version": "0.9.106", "description": "Budibase Web Server", "main": "src/index.js", "repository": { @@ -60,9 +60,9 @@ "author": "Budibase", "license": "AGPL-3.0-or-later", "dependencies": { - "@budibase/auth": "^0.9.105", - "@budibase/client": "^0.9.105", - "@budibase/string-templates": "^0.9.105", + "@budibase/auth": "^0.9.106", + "@budibase/client": "^0.9.106", + "@budibase/string-templates": "^0.9.106", "@elastic/elasticsearch": "7.10.0", "@koa/router": "8.0.0", "@sendgrid/mail": "7.1.1", @@ -115,7 +115,7 @@ "devDependencies": { "@babel/core": "^7.14.3", "@babel/preset-env": "^7.14.4", - "@budibase/standard-components": "^0.9.105", + "@budibase/standard-components": "^0.9.106", "@jest/test-sequencer": "^24.8.0", "@types/bull": "^3.15.1", "@types/jest": "^26.0.23", diff --git a/packages/standard-components/package.json b/packages/standard-components/package.json index a9bdbf642..2b017c90a 100644 --- a/packages/standard-components/package.json +++ b/packages/standard-components/package.json @@ -29,11 +29,11 @@ "keywords": [ "svelte" ], - "version": "0.9.105", + "version": "0.9.106", "license": "MIT", "gitHead": "d1836a898cab3f8ab80ee6d8f42be1a9eed7dcdc", "dependencies": { - "@budibase/bbui": "^0.9.105", + "@budibase/bbui": "^0.9.106", "@spectrum-css/card": "^3.0.3", "@spectrum-css/link": "^3.1.3", "@spectrum-css/page": "^3.0.1", diff --git a/packages/string-templates/package.json b/packages/string-templates/package.json index d980f2b36..f124d5764 100644 --- a/packages/string-templates/package.json +++ b/packages/string-templates/package.json @@ -1,6 +1,6 @@ { "name": "@budibase/string-templates", - "version": "0.9.105", + "version": "0.9.106", "description": "Handlebars wrapper for Budibase templating.", "main": "src/index.cjs", "module": "dist/bundle.mjs", diff --git a/packages/worker/package.json b/packages/worker/package.json index 9e33e6ac6..8b504aa44 100644 --- a/packages/worker/package.json +++ b/packages/worker/package.json @@ -1,7 +1,7 @@ { "name": "@budibase/worker", "email": "hi@budibase.com", - "version": "0.9.105", + "version": "0.9.106", "description": "Budibase background service", "main": "src/index.js", "repository": { @@ -21,8 +21,8 @@ "author": "Budibase", "license": "AGPL-3.0-or-later", "dependencies": { - "@budibase/auth": "^0.9.105", - "@budibase/string-templates": "^0.9.105", + "@budibase/auth": "^0.9.106", + "@budibase/string-templates": "^0.9.106", "@koa/router": "^8.0.0", "@techpass/passport-openidconnect": "^0.3.0", "aws-sdk": "^2.811.0", From 1b78d8349fcae4f8a22d331b0c6aa9805db00d82 Mon Sep 17 00:00:00 2001 From: Martin McKeaveney Date: Mon, 16 Aug 2021 19:00:08 +0100 Subject: [PATCH 011/110] 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 16b7bddde..24728c175 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 1596b74be..af6212751 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 a71ad09b5..b3b81bebf 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 012/110] 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 24728c175..daee03620 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 013/110] 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 4e76626d2..b9c04c2cc 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 b3b81bebf..1f60f58f1 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 8e1e016c66fe9192dbf5416260b5013dfd46a003 Mon Sep 17 00:00:00 2001 From: Martin McKeaveney Date: Tue, 17 Aug 2021 07:16:43 +0100 Subject: [PATCH 014/110] Revert "Feature/couchdb auth" --- packages/auth/src/db/utils.js | 13 +------------ packages/auth/src/environment.js | 2 -- packages/server/scripts/dev/manage.js | 2 +- .../src/api/controllers/row/internalSearch.js | 6 ++---- packages/server/src/db/client.js | 4 ---- packages/server/src/environment.js | 2 -- packages/worker/scripts/dev/manage.js | 2 +- packages/worker/src/db/index.js | 4 ---- 8 files changed, 5 insertions(+), 30 deletions(-) diff --git a/packages/auth/src/db/utils.js b/packages/auth/src/db/utils.js index 2ac23d02a..1fc78f418 100644 --- a/packages/auth/src/db/utils.js +++ b/packages/auth/src/db/utils.js @@ -159,17 +159,6 @@ exports.getDeployedAppID = appId => { return appId } -exports.getCouchUrl = () => { - // username and password already exist in URL - if (env.COUCH_DB_URL.includes("@")) { - return env.COUCH_DB_URL - } - - const [protocol, ...rest] = env.COUCH_DB_URL.split("://") - - return `${protocol}://${env.COUCH_DB_USERNAME}:${env.COUCH_DB_PASSWORD}@${rest}` -} - /** * if in production this will use the CouchDB _all_dbs call to retrieve a list of databases. If testing * when using Pouch it will use the pouchdb-all-dbs package. @@ -179,7 +168,7 @@ exports.getAllDbs = async () => { if (env.isTest()) { return getCouch().allDbs() } - const response = await fetch(`${exports.getCouchUrl()}/_all_dbs`) + const response = await fetch(`${env.COUCH_DB_URL}/_all_dbs`) if (response.status === 200) { return response.json() } else { diff --git a/packages/auth/src/environment.js b/packages/auth/src/environment.js index e64a1c75f..355843d02 100644 --- a/packages/auth/src/environment.js +++ b/packages/auth/src/environment.js @@ -9,8 +9,6 @@ function isTest() { module.exports = { JWT_SECRET: process.env.JWT_SECRET, COUCH_DB_URL: process.env.COUCH_DB_URL, - COUCH_DB_USERNAME: process.env.COUCH_DB_USER, - COUCH_DB_PASSWORD: process.env.COUCH_DB_PASSWORD, SALT_ROUNDS: process.env.SALT_ROUNDS, REDIS_URL: process.env.REDIS_URL, REDIS_PASSWORD: process.env.REDIS_PASSWORD, diff --git a/packages/server/scripts/dev/manage.js b/packages/server/scripts/dev/manage.js index 7e9d7f6a0..ffd8c6b9e 100644 --- a/packages/server/scripts/dev/manage.js +++ b/packages/server/scripts/dev/manage.js @@ -36,7 +36,7 @@ async function init() { const envFileJson = { PORT: 4001, MINIO_URL: "http://localhost:10000/", - COUCH_DB_URL: "http://localhost:10000/db/", + COUCH_DB_URL: "http://budibase:budibase@localhost:10000/db/", REDIS_URL: "localhost:6379", WORKER_URL: "http://localhost:4002", INTERNAL_API_KEY: "budibase", diff --git a/packages/server/src/api/controllers/row/internalSearch.js b/packages/server/src/api/controllers/row/internalSearch.js index cc35da40e..c13091d6e 100644 --- a/packages/server/src/api/controllers/row/internalSearch.js +++ b/packages/server/src/api/controllers/row/internalSearch.js @@ -1,6 +1,6 @@ const { SearchIndexes } = require("../../../db/utils") +const env = require("../../../environment") const fetch = require("node-fetch") -const { getCouchUrl } = require("@budibase/auth/db") /** * Class to build lucene query URLs. @@ -233,9 +233,7 @@ class QueryBuilder { } async run() { - const url = `${getCouchUrl()}/${this.appId}/_design/database/_search/${ - SearchIndexes.ROWS - }` + const url = `${env.COUCH_DB_URL}/${this.appId}/_design/database/_search/${SearchIndexes.ROWS}` const body = this.buildSearchBody() return await runQuery(url, body) } diff --git a/packages/server/src/db/client.js b/packages/server/src/db/client.js index 9a244fee1..d50d72b18 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_USERNAME, - password: env.COUCH_DB_PASSWORD, - }, } if (env.isTest()) { diff --git a/packages/server/src/environment.js b/packages/server/src/environment.js index 45c767385..52c680f65 100644 --- a/packages/server/src/environment.js +++ b/packages/server/src/environment.js @@ -23,8 +23,6 @@ module.exports = { // important PORT: process.env.PORT, JWT_SECRET: process.env.JWT_SECRET, - COUCH_DB_USERNAME: process.env.COUCH_DB_USER, - COUCH_DB_PASSWORD: process.env.COUCH_DB_PASSWORD, COUCH_DB_URL: process.env.COUCH_DB_URL, MINIO_URL: process.env.MINIO_URL, WORKER_URL: process.env.WORKER_URL, diff --git a/packages/worker/scripts/dev/manage.js b/packages/worker/scripts/dev/manage.js index d7eb76fc7..b9d28b627 100644 --- a/packages/worker/scripts/dev/manage.js +++ b/packages/worker/scripts/dev/manage.js @@ -17,7 +17,7 @@ async function init() { REDIS_URL: "localhost:6379", REDIS_PASSWORD: "budibase", MINIO_URL: "http://localhost:10000/", - COUCH_DB_URL: "http://localhost:10000/db/", + COUCH_DB_URL: "http://budibase:budibase@localhost:10000/db/", } let envFile = "" Object.keys(envFileJson).forEach(key => { diff --git a/packages/worker/src/db/index.js b/packages/worker/src/db/index.js index c0676634f..d6d035cff 100644 --- a/packages/worker/src/db/index.js +++ b/packages/worker/src/db/index.js @@ -7,10 +7,6 @@ const COUCH_DB_URL = env.COUCH_DB_URL || "http://localhost:10000/db/" let POUCH_DB_DEFAULTS = { prefix: COUCH_DB_URL, - auth: { - username: env.COUCH_DB_USERNAME, - password: env.COUCH_DB_PASSWORD, - }, } if (env.isTest()) { From a2efbb13380d08a2ffd2c08378cd16ca774c0e2a Mon Sep 17 00:00:00 2001 From: Budibase Release Bot <> Date: Tue, 17 Aug 2021 06:25:14 +0000 Subject: [PATCH 015/110] v0.9.107 --- lerna.json | 2 +- packages/auth/package.json | 2 +- packages/bbui/package.json | 2 +- packages/builder/package.json | 8 ++++---- packages/cli/package.json | 2 +- packages/client/package.json | 8 ++++---- packages/server/package.json | 10 +++++----- packages/standard-components/package.json | 4 ++-- packages/string-templates/package.json | 2 +- packages/worker/package.json | 6 +++--- 10 files changed, 23 insertions(+), 23 deletions(-) diff --git a/lerna.json b/lerna.json index 30b42fc1e..1b2dfb6bb 100644 --- a/lerna.json +++ b/lerna.json @@ -1,5 +1,5 @@ { - "version": "0.9.106", + "version": "0.9.107", "npmClient": "yarn", "packages": [ "packages/*" diff --git a/packages/auth/package.json b/packages/auth/package.json index db542205f..8e8395b21 100644 --- a/packages/auth/package.json +++ b/packages/auth/package.json @@ -1,6 +1,6 @@ { "name": "@budibase/auth", - "version": "0.9.106", + "version": "0.9.107", "description": "Authentication middlewares for budibase builder and apps", "main": "src/index.js", "author": "Budibase", diff --git a/packages/bbui/package.json b/packages/bbui/package.json index b1951d987..7ea6d15a5 100644 --- a/packages/bbui/package.json +++ b/packages/bbui/package.json @@ -1,7 +1,7 @@ { "name": "@budibase/bbui", "description": "A UI solution used in the different Budibase projects.", - "version": "0.9.106", + "version": "0.9.107", "license": "AGPL-3.0", "svelte": "src/index.js", "module": "dist/bbui.es.js", diff --git a/packages/builder/package.json b/packages/builder/package.json index 9595ad603..2aa61c79b 100644 --- a/packages/builder/package.json +++ b/packages/builder/package.json @@ -1,6 +1,6 @@ { "name": "@budibase/builder", - "version": "0.9.106", + "version": "0.9.107", "license": "AGPL-3.0", "private": true, "scripts": { @@ -65,10 +65,10 @@ } }, "dependencies": { - "@budibase/bbui": "^0.9.106", - "@budibase/client": "^0.9.106", + "@budibase/bbui": "^0.9.107", + "@budibase/client": "^0.9.107", "@budibase/colorpicker": "1.1.2", - "@budibase/string-templates": "^0.9.106", + "@budibase/string-templates": "^0.9.107", "@sentry/browser": "5.19.1", "@spectrum-css/page": "^3.0.1", "@spectrum-css/vars": "^3.0.1", diff --git a/packages/cli/package.json b/packages/cli/package.json index aa5b558bd..c328df69c 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -1,6 +1,6 @@ { "name": "@budibase/cli", - "version": "0.9.106", + "version": "0.9.107", "description": "Budibase CLI, for developers, self hosting and migrations.", "main": "src/index.js", "bin": { diff --git a/packages/client/package.json b/packages/client/package.json index 4fe16bc42..897217186 100644 --- a/packages/client/package.json +++ b/packages/client/package.json @@ -1,6 +1,6 @@ { "name": "@budibase/client", - "version": "0.9.106", + "version": "0.9.107", "license": "MPL-2.0", "module": "dist/budibase-client.js", "main": "dist/budibase-client.js", @@ -18,9 +18,9 @@ "dev:builder": "rollup -cw" }, "dependencies": { - "@budibase/bbui": "^0.9.106", - "@budibase/standard-components": "^0.9.106", - "@budibase/string-templates": "^0.9.106", + "@budibase/bbui": "^0.9.107", + "@budibase/standard-components": "^0.9.107", + "@budibase/string-templates": "^0.9.107", "regexparam": "^1.3.0", "shortid": "^2.2.15", "svelte-spa-router": "^3.0.5" diff --git a/packages/server/package.json b/packages/server/package.json index ad9a14682..5daca5d40 100644 --- a/packages/server/package.json +++ b/packages/server/package.json @@ -1,7 +1,7 @@ { "name": "@budibase/server", "email": "hi@budibase.com", - "version": "0.9.106", + "version": "0.9.107", "description": "Budibase Web Server", "main": "src/index.js", "repository": { @@ -60,9 +60,9 @@ "author": "Budibase", "license": "AGPL-3.0-or-later", "dependencies": { - "@budibase/auth": "^0.9.106", - "@budibase/client": "^0.9.106", - "@budibase/string-templates": "^0.9.106", + "@budibase/auth": "^0.9.107", + "@budibase/client": "^0.9.107", + "@budibase/string-templates": "^0.9.107", "@elastic/elasticsearch": "7.10.0", "@koa/router": "8.0.0", "@sendgrid/mail": "7.1.1", @@ -115,7 +115,7 @@ "devDependencies": { "@babel/core": "^7.14.3", "@babel/preset-env": "^7.14.4", - "@budibase/standard-components": "^0.9.106", + "@budibase/standard-components": "^0.9.107", "@jest/test-sequencer": "^24.8.0", "@types/bull": "^3.15.1", "@types/jest": "^26.0.23", diff --git a/packages/standard-components/package.json b/packages/standard-components/package.json index 2b017c90a..96b2deff0 100644 --- a/packages/standard-components/package.json +++ b/packages/standard-components/package.json @@ -29,11 +29,11 @@ "keywords": [ "svelte" ], - "version": "0.9.106", + "version": "0.9.107", "license": "MIT", "gitHead": "d1836a898cab3f8ab80ee6d8f42be1a9eed7dcdc", "dependencies": { - "@budibase/bbui": "^0.9.106", + "@budibase/bbui": "^0.9.107", "@spectrum-css/card": "^3.0.3", "@spectrum-css/link": "^3.1.3", "@spectrum-css/page": "^3.0.1", diff --git a/packages/string-templates/package.json b/packages/string-templates/package.json index f124d5764..b64b19bb9 100644 --- a/packages/string-templates/package.json +++ b/packages/string-templates/package.json @@ -1,6 +1,6 @@ { "name": "@budibase/string-templates", - "version": "0.9.106", + "version": "0.9.107", "description": "Handlebars wrapper for Budibase templating.", "main": "src/index.cjs", "module": "dist/bundle.mjs", diff --git a/packages/worker/package.json b/packages/worker/package.json index 8b504aa44..9d6b98aa6 100644 --- a/packages/worker/package.json +++ b/packages/worker/package.json @@ -1,7 +1,7 @@ { "name": "@budibase/worker", "email": "hi@budibase.com", - "version": "0.9.106", + "version": "0.9.107", "description": "Budibase background service", "main": "src/index.js", "repository": { @@ -21,8 +21,8 @@ "author": "Budibase", "license": "AGPL-3.0-or-later", "dependencies": { - "@budibase/auth": "^0.9.106", - "@budibase/string-templates": "^0.9.106", + "@budibase/auth": "^0.9.107", + "@budibase/string-templates": "^0.9.107", "@koa/router": "^8.0.0", "@techpass/passport-openidconnect": "^0.3.0", "aws-sdk": "^2.811.0", From 8168e06b5b1f083fec3f06f31b420598d858eb97 Mon Sep 17 00:00:00 2001 From: Martin McKeaveney Date: Tue, 17 Aug 2021 11:01:06 +0100 Subject: [PATCH 016/110] 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 23da0fcff..000000000 --- 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 8021a4603..000000000 --- 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 017/110] 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 daee03620..5a4a95ab2 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 af6212751..946bf17b7 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 018/110] 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 b2a2021cc..3a5fc5dc7 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 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%0Av~dcbhZz|K{-y|F=@M&Hs1NKEAE87XEMe0AMNq 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 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 019/110] 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 1f60f58f1..0c4a466c9 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 020/110] 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 5a5025f81..f4cbcc9dd 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 021/110] 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 022/110] 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 946bf17b7..e69d6f6b7 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 0c4a466c9..7b2ac7cb0 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 19eeda608f35dcfbf3327c21e5651de769a47f91 Mon Sep 17 00:00:00 2001 From: Maurits Lourens Date: Tue, 17 Aug 2021 22:16:57 +0200 Subject: [PATCH 023/110] update the modal text for updating the name of an app --- packages/builder/src/components/start/UpdateAppModal.svelte | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/packages/builder/src/components/start/UpdateAppModal.svelte b/packages/builder/src/components/start/UpdateAppModal.svelte index dbbc000d1..1b7ec6be9 100644 --- a/packages/builder/src/components/start/UpdateAppModal.svelte +++ b/packages/builder/src/components/start/UpdateAppModal.svelte @@ -97,8 +97,7 @@ disabled={!(valid && dirty)} > - Give your new app a name, and choose which groups have access (paid plans - only). + Update the name of your app. Date: Tue, 17 Aug 2021 21:27:30 +0100 Subject: [PATCH 024/110] 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 d06f4651e..dde857c46 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 025/110] 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 f4cbcc9dd..5a5025f81 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 50af03172..000000000 --- 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 36526e082..000000000 --- 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 70e0df619..000000000 --- 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 392bc92f5..000000000 --- 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 d5a2c3ca8..000000000 --- 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 e12b53421..000000000 --- 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 cfc545f69..000000000 --- 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 ff82cd9c7..000000000 --- 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 d8948d634..000000000 --- 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 6d6605f0e..000000000 --- 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 04ac58dbd..000000000 --- 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 afb5487c5..000000000 --- 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 7b4d7cbe7..000000000 --- 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 a359a6a40..000000000 --- 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 e63a7f5db..000000000 --- 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 1e5190afc..000000000 --- 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 017b60a9c..000000000 --- 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 88aafc66f..000000000 --- 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 6e3b371da..000000000 --- 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 5314cecb3..000000000 --- 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 f232531ac..000000000 --- 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 9eda282b1..000000000 --- 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 93a393c97..000000000 --- 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 665fd48d3..000000000 --- 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 c7f22d636..000000000 --- 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 887ed0f62..000000000 --- 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 84f1f7582..000000000 --- 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 b65a0910b..000000000 --- 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 e339c69c3..000000000 --- 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 141e06b68..000000000 --- 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 bc29abeba..000000000 --- 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 b7f54c09f..000000000 --- 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 a829c3614..000000000 --- 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 4f18a70b9..000000000 --- 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 60fb2c1f6..000000000 --- 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 8b1fd0951..000000000 --- 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 fd762f935..000000000 --- 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 4990fb1c3..000000000 --- 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 3656be487..000000000 --- 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 9e9bd0138..000000000 --- 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 d2c7de685..000000000 --- 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 9b083ee6e..000000000 --- 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 edda07f5d..000000000 --- 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 1ff0f7f0e..000000000 --- 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 2f3dd7784..000000000 --- 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 8ec5f49fa..000000000 --- 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 81be52b87..000000000 --- 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 e0b7a0f21..000000000 --- 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 91f22f03d..000000000 --- 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 aaf336fb3..000000000 --- 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 7f46791ec..000000000 --- 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 630545140..000000000 --- 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 2f6def589..000000000 --- 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 7e2d223a9..000000000 --- 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 fb14bdf6a..000000000 --- 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 f94b9590d..000000000 --- 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 c7eebf5c8..000000000 --- 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 a5a425f74..000000000 --- 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 ca5427523..000000000 --- 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 bdb856310..000000000 --- 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 1a5ccd29b..000000000 --- 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 5ec3bc774..000000000 --- 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 0bb966127..000000000 --- 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 1b690192c..000000000 --- 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 228cb59d8..000000000 --- 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 908291cff..000000000 --- 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 50a718d32..000000000 --- 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 066488a04..000000000 --- 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 99345269b..000000000 --- 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 e31fda3f9..000000000 --- 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 153f005e2..000000000 --- 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 716dbf16f..000000000 --- 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 5d29a2d52..000000000 --- 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 4a9cb9284..000000000 --- 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 7624ab36c..000000000 --- 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 0c00e9369..000000000 --- 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 12e7a4f63..000000000 --- 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 f5496eb61..000000000 --- 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 7b2ac7cb0..8b63fe5f9 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 026/110] 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 5a4a95ab2..daee03620 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 e69d6f6b7..1af289f4a 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 af9bb40bdd7bca06144210a8c70dd1c00a6175b7 Mon Sep 17 00:00:00 2001 From: Peter Clement Date: Wed, 18 Aug 2021 00:02:17 +0100 Subject: [PATCH 027/110] fixes organisation logo not showing --- .../src/pages/builder/portal/settings/organisation.svelte | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/packages/builder/src/pages/builder/portal/settings/organisation.svelte b/packages/builder/src/pages/builder/portal/settings/organisation.svelte index b274d3af9..b036585e2 100644 --- a/packages/builder/src/pages/builder/portal/settings/organisation.svelte +++ b/packages/builder/src/pages/builder/portal/settings/organisation.svelte @@ -37,7 +37,11 @@ async function uploadLogo(file) { let data = new FormData() data.append("file", file) - const res = await post("/api/global/configs/upload/settings/logo", data, {}) + const res = await post( + "/api/admin/configs/upload/settings/logoUrl", + data, + {} + ) return await res.json() } From e1ef03f55dadfaeea5ab15533c22f0aa862ac42c Mon Sep 17 00:00:00 2001 From: Maurits Lourens Date: Wed, 18 Aug 2021 11:24:25 +0200 Subject: [PATCH 028/110] fix lint issue --- packages/builder/src/components/start/UpdateAppModal.svelte | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/packages/builder/src/components/start/UpdateAppModal.svelte b/packages/builder/src/components/start/UpdateAppModal.svelte index 1b7ec6be9..16718da36 100644 --- a/packages/builder/src/components/start/UpdateAppModal.svelte +++ b/packages/builder/src/components/start/UpdateAppModal.svelte @@ -96,9 +96,7 @@ onConfirm={updateApp} disabled={!(valid && dirty)} > - - Update the name of your app. - + Update the name of your app. Date: Wed, 18 Aug 2021 10:37:14 +0100 Subject: [PATCH 029/110] linting --- .../data/datasource/[selectedDatasource]/index.svelte | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/builder/src/pages/builder/app/[application]/data/datasource/[selectedDatasource]/index.svelte b/packages/builder/src/pages/builder/app/[application]/data/datasource/[selectedDatasource]/index.svelte index 0cd667a82..fc82e3a11 100644 --- a/packages/builder/src/pages/builder/app/[application]/data/datasource/[selectedDatasource]/index.svelte +++ b/packages/builder/src/pages/builder/app/[application]/data/datasource/[selectedDatasource]/index.svelte @@ -1,5 +1,5 @@ + +
+ {#if error} + + {/if} + +
+ +
+ + + + +
+ + diff --git a/packages/bbui/src/Form/Core/index.js b/packages/bbui/src/Form/Core/index.js index a31c5941e..440c4a1b1 100644 --- a/packages/bbui/src/Form/Core/index.js +++ b/packages/bbui/src/Form/Core/index.js @@ -9,3 +9,4 @@ export { default as CoreSwitch } from "./Switch.svelte" export { default as CoreSearch } from "./Search.svelte" export { default as CoreDatePicker } from "./DatePicker.svelte" export { default as CoreDropzone } from "./Dropzone.svelte" +export { default as CoreStepper } from "./Stepper.svelte" diff --git a/packages/bbui/src/Form/Stepper.svelte b/packages/bbui/src/Form/Stepper.svelte new file mode 100644 index 000000000..9eff2e368 --- /dev/null +++ b/packages/bbui/src/Form/Stepper.svelte @@ -0,0 +1,45 @@ + + + + + diff --git a/packages/bbui/src/index.js b/packages/bbui/src/index.js index 3a491bfb5..08ece6b93 100644 --- a/packages/bbui/src/index.js +++ b/packages/bbui/src/index.js @@ -5,6 +5,7 @@ import "@spectrum-css/icon/dist/index-vars.css" // Components export { default as Input } from "./Form/Input.svelte" +export { default as Stepper } from "./Form/Stepper.svelte" export { default as TextArea } from "./Form/TextArea.svelte" export { default as Select } from "./Form/Select.svelte" export { default as Combobox } from "./Form/Combobox.svelte" diff --git a/packages/bbui/yarn.lock b/packages/bbui/yarn.lock index 6deff28fa..a3b20aa86 100644 --- a/packages/bbui/yarn.lock +++ b/packages/bbui/yarn.lock @@ -206,6 +206,11 @@ resolved "https://registry.yarnpkg.com/@spectrum-css/statuslight/-/statuslight-3.0.2.tgz#dc54b6cd113413dcdb909c486b5d7bae60db65c5" integrity sha512-xodB8g8vGJH20XmUj9ZsPlM1jHrGeRbvmVXkz0q7YvQrYAhim8pP3W+XKKZAletPFAuu8cmUOc6SWn6i4X4z6w== +"@spectrum-css/stepper@^3.0.3": + version "3.0.3" + resolved "https://registry.yarnpkg.com/@spectrum-css/stepper/-/stepper-3.0.3.tgz#ae89846886431e3edeee060207b8f81540f73a34" + integrity sha512-prAD61ImlOTs9b6PfB3cB08x4lAfxtvnW+RZiTYky0E8GgZdrc/MfCkL5/oqQaIQUtyQv/3Lb7ELAf/0K8QTXw== + "@spectrum-css/switch@^1.0.2": version "1.0.2" resolved "https://registry.yarnpkg.com/@spectrum-css/switch/-/switch-1.0.2.tgz#f0b4c69271964573e02b08e90998096e49e1de44" diff --git a/packages/builder/src/components/design/PropertiesPanel/PropertyControls/componentSettings.js b/packages/builder/src/components/design/PropertiesPanel/PropertyControls/componentSettings.js index 64668d05c..213ffbbc9 100644 --- a/packages/builder/src/components/design/PropertiesPanel/PropertyControls/componentSettings.js +++ b/packages/builder/src/components/design/PropertiesPanel/PropertyControls/componentSettings.js @@ -1,4 +1,4 @@ -import { Checkbox, Input, Select } from "@budibase/bbui" +import { Checkbox, Input, Select, Stepper } from "@budibase/bbui" import DataSourceSelect from "./DataSourceSelect.svelte" import DataProviderSelect from "./DataProviderSelect.svelte" import EventsEditor from "./EventsEditor" @@ -22,7 +22,7 @@ const componentMap = { dataSource: DataSourceSelect, dataProvider: DataProviderSelect, boolean: Checkbox, - number: Input, + number: Stepper, event: EventsEditor, table: TableSelect, color: ColorPicker, diff --git a/packages/standard-components/manifest.json b/packages/standard-components/manifest.json index 95f41a85e..b8c7bdc41 100644 --- a/packages/standard-components/manifest.json +++ b/packages/standard-components/manifest.json @@ -1725,6 +1725,12 @@ "label": "Custom" } }, + { + "type": "number", + "label": "Number of steps", + "key": "steps", + "defaultValue": 1 + }, { "type": "boolean", "label": "Disabled", From c1957208a29f963f23382bd7bf5e08558af0da16 Mon Sep 17 00:00:00 2001 From: Martin McKeaveney Date: Wed, 18 Aug 2021 11:42:04 +0100 Subject: [PATCH 032/110] 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 1c0a914ed..011ba27f2 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 033/110] 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 011ba27f2..1c0a914ed 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 741794bcadb0fbb9e233694d409bc0a2aa366047 Mon Sep 17 00:00:00 2001 From: Budibase Staging Release Bot <> Date: Wed, 18 Aug 2021 13:19:18 +0000 Subject: [PATCH 034/110] v0.9.105-alpha.23 --- lerna.json | 2 +- packages/auth/package.json | 2 +- packages/bbui/package.json | 2 +- packages/builder/package.json | 8 ++++---- packages/cli/package.json | 2 +- packages/client/package.json | 8 ++++---- packages/server/package.json | 10 +++++----- packages/standard-components/package.json | 4 ++-- packages/string-templates/package.json | 2 +- packages/worker/package.json | 6 +++--- 10 files changed, 23 insertions(+), 23 deletions(-) diff --git a/lerna.json b/lerna.json index 4e586c3bf..e32eb7d05 100644 --- a/lerna.json +++ b/lerna.json @@ -1,5 +1,5 @@ { - "version": "0.9.105-alpha.22", + "version": "0.9.105-alpha.23", "npmClient": "yarn", "packages": [ "packages/*" diff --git a/packages/auth/package.json b/packages/auth/package.json index 833136694..fd7cd230f 100644 --- a/packages/auth/package.json +++ b/packages/auth/package.json @@ -1,6 +1,6 @@ { "name": "@budibase/auth", - "version": "0.9.105-alpha.22", + "version": "0.9.105-alpha.23", "description": "Authentication middlewares for budibase builder and apps", "main": "src/index.js", "author": "Budibase", diff --git a/packages/bbui/package.json b/packages/bbui/package.json index b134f98c0..7729e5956 100644 --- a/packages/bbui/package.json +++ b/packages/bbui/package.json @@ -1,7 +1,7 @@ { "name": "@budibase/bbui", "description": "A UI solution used in the different Budibase projects.", - "version": "0.9.105-alpha.22", + "version": "0.9.105-alpha.23", "license": "AGPL-3.0", "svelte": "src/index.js", "module": "dist/bbui.es.js", diff --git a/packages/builder/package.json b/packages/builder/package.json index af8c52fae..0c5884789 100644 --- a/packages/builder/package.json +++ b/packages/builder/package.json @@ -1,6 +1,6 @@ { "name": "@budibase/builder", - "version": "0.9.105-alpha.22", + "version": "0.9.105-alpha.23", "license": "AGPL-3.0", "private": true, "scripts": { @@ -65,10 +65,10 @@ } }, "dependencies": { - "@budibase/bbui": "^0.9.105-alpha.22", - "@budibase/client": "^0.9.105-alpha.22", + "@budibase/bbui": "^0.9.105-alpha.23", + "@budibase/client": "^0.9.105-alpha.23", "@budibase/colorpicker": "1.1.2", - "@budibase/string-templates": "^0.9.105-alpha.22", + "@budibase/string-templates": "^0.9.105-alpha.23", "@sentry/browser": "5.19.1", "@spectrum-css/page": "^3.0.1", "@spectrum-css/vars": "^3.0.1", diff --git a/packages/cli/package.json b/packages/cli/package.json index 02b5d798e..bccb336af 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -1,6 +1,6 @@ { "name": "@budibase/cli", - "version": "0.9.105-alpha.22", + "version": "0.9.105-alpha.23", "description": "Budibase CLI, for developers, self hosting and migrations.", "main": "src/index.js", "bin": { diff --git a/packages/client/package.json b/packages/client/package.json index 9f1c00801..2f3c0ee13 100644 --- a/packages/client/package.json +++ b/packages/client/package.json @@ -1,6 +1,6 @@ { "name": "@budibase/client", - "version": "0.9.105-alpha.22", + "version": "0.9.105-alpha.23", "license": "MPL-2.0", "module": "dist/budibase-client.js", "main": "dist/budibase-client.js", @@ -18,9 +18,9 @@ "dev:builder": "rollup -cw" }, "dependencies": { - "@budibase/bbui": "^0.9.105-alpha.22", - "@budibase/standard-components": "^0.9.105-alpha.22", - "@budibase/string-templates": "^0.9.105-alpha.22", + "@budibase/bbui": "^0.9.105-alpha.23", + "@budibase/standard-components": "^0.9.105-alpha.23", + "@budibase/string-templates": "^0.9.105-alpha.23", "regexparam": "^1.3.0", "shortid": "^2.2.15", "svelte-spa-router": "^3.0.5" diff --git a/packages/server/package.json b/packages/server/package.json index 4d33c281c..3e4bea404 100644 --- a/packages/server/package.json +++ b/packages/server/package.json @@ -1,7 +1,7 @@ { "name": "@budibase/server", "email": "hi@budibase.com", - "version": "0.9.105-alpha.22", + "version": "0.9.105-alpha.23", "description": "Budibase Web Server", "main": "src/index.js", "repository": { @@ -62,9 +62,9 @@ "author": "Budibase", "license": "AGPL-3.0-or-later", "dependencies": { - "@budibase/auth": "^0.9.105-alpha.22", - "@budibase/client": "^0.9.105-alpha.22", - "@budibase/string-templates": "^0.9.105-alpha.22", + "@budibase/auth": "^0.9.105-alpha.23", + "@budibase/client": "^0.9.105-alpha.23", + "@budibase/string-templates": "^0.9.105-alpha.23", "@elastic/elasticsearch": "7.10.0", "@koa/router": "8.0.0", "@sendgrid/mail": "7.1.1", @@ -117,7 +117,7 @@ "devDependencies": { "@babel/core": "^7.14.3", "@babel/preset-env": "^7.14.4", - "@budibase/standard-components": "^0.9.105-alpha.22", + "@budibase/standard-components": "^0.9.105-alpha.23", "@jest/test-sequencer": "^24.8.0", "@types/bull": "^3.15.1", "@types/jest": "^26.0.23", diff --git a/packages/standard-components/package.json b/packages/standard-components/package.json index 0adc06771..92811534c 100644 --- a/packages/standard-components/package.json +++ b/packages/standard-components/package.json @@ -29,11 +29,11 @@ "keywords": [ "svelte" ], - "version": "0.9.105-alpha.22", + "version": "0.9.105-alpha.23", "license": "MIT", "gitHead": "d1836a898cab3f8ab80ee6d8f42be1a9eed7dcdc", "dependencies": { - "@budibase/bbui": "^0.9.105-alpha.22", + "@budibase/bbui": "^0.9.105-alpha.23", "@spectrum-css/button": "^3.0.3", "@spectrum-css/card": "^3.0.3", "@spectrum-css/divider": "^1.0.3", diff --git a/packages/string-templates/package.json b/packages/string-templates/package.json index b36427640..8337235e0 100644 --- a/packages/string-templates/package.json +++ b/packages/string-templates/package.json @@ -1,6 +1,6 @@ { "name": "@budibase/string-templates", - "version": "0.9.105-alpha.22", + "version": "0.9.105-alpha.23", "description": "Handlebars wrapper for Budibase templating.", "main": "src/index.cjs", "module": "dist/bundle.mjs", diff --git a/packages/worker/package.json b/packages/worker/package.json index a543bc2c4..dd3670433 100644 --- a/packages/worker/package.json +++ b/packages/worker/package.json @@ -1,7 +1,7 @@ { "name": "@budibase/worker", "email": "hi@budibase.com", - "version": "0.9.105-alpha.22", + "version": "0.9.105-alpha.23", "description": "Budibase background service", "main": "src/index.js", "repository": { @@ -23,8 +23,8 @@ "author": "Budibase", "license": "AGPL-3.0-or-later", "dependencies": { - "@budibase/auth": "^0.9.105-alpha.22", - "@budibase/string-templates": "^0.9.105-alpha.22", + "@budibase/auth": "^0.9.105-alpha.23", + "@budibase/string-templates": "^0.9.105-alpha.23", "@koa/router": "^8.0.0", "@techpass/passport-openidconnect": "^0.3.0", "aws-sdk": "^2.811.0", From a0bba3466402a35bffd59ef4b5f7aeb1544ace66 Mon Sep 17 00:00:00 2001 From: Martin McKeaveney Date: Wed, 18 Aug 2021 14:55:22 +0100 Subject: [PATCH 035/110] 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 eb9eeebb3..18fe4e576 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 b9c04c2cc..f24e9482c 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 fa275b2c4b96becd581130237a1e288412e56f57 Mon Sep 17 00:00:00 2001 From: Andrew Kingston Date: Wed, 18 Aug 2021 14:58:35 +0100 Subject: [PATCH 036/110] Add initial form step component --- packages/standard-components/src/forms/FormStep.svelte | 0 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 packages/standard-components/src/forms/FormStep.svelte diff --git a/packages/standard-components/src/forms/FormStep.svelte b/packages/standard-components/src/forms/FormStep.svelte new file mode 100644 index 000000000..e69de29bb From 32fe0ea0726c9e122f5def2198dcf15d4a09a4d2 Mon Sep 17 00:00:00 2001 From: Andrew Kingston Date: Wed, 18 Aug 2021 14:58:58 +0100 Subject: [PATCH 037/110] Support multiple component context exports and export additional context from forms --- .../builder/src/builderStore/dataBinding.js | 126 ++++++++++-------- .../design/AppPreview/componentStructure.json | 1 + .../ComponentSettingsSection.svelte | 2 + packages/client/src/constants.js | 2 + packages/standard-components/manifest.json | 51 ++++++- .../src/forms/FormStep.svelte | 18 +++ .../src/forms/InnerForm.svelte | 28 +++- .../standard-components/src/forms/index.js | 1 + 8 files changed, 165 insertions(+), 64 deletions(-) diff --git a/packages/builder/src/builderStore/dataBinding.js b/packages/builder/src/builderStore/dataBinding.js index dc995d611..00eaaf024 100644 --- a/packages/builder/src/builderStore/dataBinding.js +++ b/packages/builder/src/builderStore/dataBinding.js @@ -120,71 +120,79 @@ const getContextBindings = (asset, componentId) => { // Create bindings for each data provider dataProviders.forEach(component => { const def = store.actions.components.getDefinition(component._component) - const contextDefinition = def.context - let schema - let readablePrefix - - if (contextDefinition.type === "form") { - // Forms do not need table schemas - // Their schemas are built from their component field names - schema = buildFormSchema(component) - readablePrefix = "Fields" - } else if (contextDefinition.type === "static") { - // Static contexts are fully defined by the components - schema = {} - const values = contextDefinition.values || [] - values.forEach(value => { - schema[value.key] = { name: value.label, type: "string" } - }) - } else if (contextDefinition.type === "schema") { - // Schema contexts are generated dynamically depending on their data - const datasource = getDatasourceForProvider(asset, component) - if (!datasource) { + const contexts = Array.isArray(def.context) ? def.context : [def.context] + + // Create bindings for each context block provided by this data provider + contexts.forEach(context => { + if (!context?.type) { return } - const info = getSchemaForDatasource(asset, datasource) - schema = info.schema - readablePrefix = info.table?.name - } - if (!schema) { - return - } - const keys = Object.keys(schema).sort() + let schema + let readablePrefix - // Create bindable properties for each schema field - const safeComponentId = makePropSafe(component._id) - keys.forEach(key => { - const fieldSchema = schema[key] - - // Make safe runtime binding and replace certain bindings with a - // new property to help display components - let runtimeBoundKey = key - if (fieldSchema.type === "link") { - runtimeBoundKey = `${key}_text` - } else if (fieldSchema.type === "attachment") { - runtimeBoundKey = `${key}_first` + if (context.type === "form") { + // Forms do not need table schemas + // Their schemas are built from their component field names + schema = buildFormSchema(component) + readablePrefix = "Fields" + } else if (context.type === "static") { + // Static contexts are fully defined by the components + schema = {} + const values = context.values || [] + values.forEach(value => { + schema[value.key] = { name: value.label, type: "string" } + }) + } else if (context.type === "schema") { + // Schema contexts are generated dynamically depending on their data + const datasource = getDatasourceForProvider(asset, component) + if (!datasource) { + return + } + const info = getSchemaForDatasource(asset, datasource) + schema = info.schema + readablePrefix = info.table?.name } - const runtimeBinding = `${safeComponentId}.${makePropSafe( - runtimeBoundKey - )}` - - // Optionally use a prefix with readable bindings - let readableBinding = component._instanceName - if (readablePrefix) { - readableBinding += `.${readablePrefix}` + if (!schema) { + return } - readableBinding += `.${fieldSchema.name || key}` - - // Create the binding object - bindings.push({ - type: "context", - runtimeBinding, - readableBinding, - // Field schema and provider are required to construct relationship - // datasource options, based on bindable properties - fieldSchema, - providerId: component._id, + + const keys = Object.keys(schema).sort() + + // Create bindable properties for each schema field + const safeComponentId = makePropSafe(component._id) + keys.forEach(key => { + const fieldSchema = schema[key] + + // Make safe runtime binding and replace certain bindings with a + // new property to help display components + let runtimeBoundKey = key + if (fieldSchema.type === "link") { + runtimeBoundKey = `${key}_text` + } else if (fieldSchema.type === "attachment") { + runtimeBoundKey = `${key}_first` + } + const runtimeBinding = `${safeComponentId}.${makePropSafe( + runtimeBoundKey + )}` + + // Optionally use a prefix with readable bindings + let readableBinding = component._instanceName + if (readablePrefix) { + readableBinding += `.${readablePrefix}` + } + readableBinding += `.${fieldSchema.name || key}` + + // Create the binding object + bindings.push({ + type: "context", + runtimeBinding, + readableBinding, + // Field schema and provider are required to construct relationship + // datasource options, based on bindable properties + fieldSchema, + providerId: component._id, + }) }) }) }) diff --git a/packages/builder/src/components/design/AppPreview/componentStructure.json b/packages/builder/src/components/design/AppPreview/componentStructure.json index c83686158..cea20a7dc 100644 --- a/packages/builder/src/components/design/AppPreview/componentStructure.json +++ b/packages/builder/src/components/design/AppPreview/componentStructure.json @@ -10,6 +10,7 @@ "icon": "Form", "children": [ "form", + "formstep", "fieldgroup", "stringfield", "numberfield", diff --git a/packages/builder/src/components/design/PropertiesPanel/ComponentSettingsSection.svelte b/packages/builder/src/components/design/PropertiesPanel/ComponentSettingsSection.svelte index 9ec110898..f047e9316 100644 --- a/packages/builder/src/components/design/PropertiesPanel/ComponentSettingsSection.svelte +++ b/packages/builder/src/components/design/PropertiesPanel/ComponentSettingsSection.svelte @@ -85,6 +85,8 @@ props={{ options: setting.options || [], placeholder: setting.placeholder || null, + min: setting.min || null, + max: setting.max || null, }} {bindings} {componentDefinition} diff --git a/packages/client/src/constants.js b/packages/client/src/constants.js index 7204b8c95..99e95e839 100644 --- a/packages/client/src/constants.js +++ b/packages/client/src/constants.js @@ -7,6 +7,8 @@ export const ActionTypes = { RefreshDatasource: "RefreshDatasource", SetDataProviderQuery: "SetDataProviderQuery", ClearForm: "ClearForm", + NextFormStep: "NextFormStep", + PrevFormStep: "PrevFormStep", } export const ApiVersion = "1" diff --git a/packages/standard-components/manifest.json b/packages/standard-components/manifest.json index b8c7bdc41..d5d22aba3 100644 --- a/packages/standard-components/manifest.json +++ b/packages/standard-components/manifest.json @@ -1705,7 +1705,9 @@ "illegalChildren": ["section"], "actions": [ "ValidateForm", - "ClearForm" + "ClearForm", + "NextFormStep", + "PrevFormStep" ], "styles": ["size"], "settings": [ @@ -1727,7 +1729,7 @@ }, { "type": "number", - "label": "Number of steps", + "label": "Steps", "key": "steps", "defaultValue": 1 }, @@ -1738,8 +1740,51 @@ "defaultValue": false } ], + "context": [ + { + "type": "static", + "values": [ + { + "label": "Valid", + "key": "valid" + }, + { + "label": "Step", + "key": "step" + } + ] + }, + { + "type": "form" + } + ] + }, + "formstep": { + "name": "Form Step", + "icon": "Form", + "hasChildren": true, + "illegalChildren": ["section"], + "actions": [ + "ValidateFormStep" + ], + "styles": ["size"], + "settings": [ + { + "type": "number", + "label": "Step", + "key": "step", + "defaultValue": 1, + "min": 1 + } + ], "context": { - "type": "form" + "type": "static", + "values": [ + { + "label": "Valid", + "key": "valid" + } + ] } }, "fieldgroup": { diff --git a/packages/standard-components/src/forms/FormStep.svelte b/packages/standard-components/src/forms/FormStep.svelte index e69de29bb..449978ec0 100644 --- a/packages/standard-components/src/forms/FormStep.svelte +++ b/packages/standard-components/src/forms/FormStep.svelte @@ -0,0 +1,18 @@ + + +{#if !formContext} + +{:else} +
+ +
+{/if} diff --git a/packages/standard-components/src/forms/InnerForm.svelte b/packages/standard-components/src/forms/InnerForm.svelte index eea2e6ebf..9355c6766 100644 --- a/packages/standard-components/src/forms/InnerForm.svelte +++ b/packages/standard-components/src/forms/InnerForm.svelte @@ -17,7 +17,12 @@ let fieldMap = {} // Form state contains observable data about the form - const formState = writable({ values: initialValues, errors: {}, valid: true }) + const formState = writable({ + values: initialValues, + errors: {}, + valid: true, + step: 1, + }) // Form API contains functions to control the form const formApi = { @@ -82,6 +87,18 @@ fieldApi.clearValue() }) }, + nextStep: () => { + formState.update(state => ({ + ...state, + step: state.step + 1, + })) + }, + prevStep: () => { + formState.update(state => ({ + ...state, + step: Math.max(1, state.step - 1), + })) + }, } // Provide both form API and state to children @@ -91,6 +108,8 @@ const actions = [ { type: ActionTypes.ValidateForm, callback: formApi.validate }, { type: ActionTypes.ClearForm, callback: formApi.clear }, + { type: ActionTypes.NextFormStep, callback: formApi.nextStep }, + { type: ActionTypes.PrevFormStep, callback: formApi.prevStep }, ] // Creates an API for a specific field @@ -229,7 +248,12 @@
{#if loaded} diff --git a/packages/standard-components/src/forms/index.js b/packages/standard-components/src/forms/index.js index fed371278..4f3eaa5ad 100644 --- a/packages/standard-components/src/forms/index.js +++ b/packages/standard-components/src/forms/index.js @@ -9,3 +9,4 @@ export { default as datetimefield } from "./DateTimeField.svelte" export { default as attachmentfield } from "./AttachmentField.svelte" export { default as relationshipfield } from "./RelationshipField.svelte" export { default as passwordfield } from "./PasswordField.svelte" +export { default as formstep } from "./FormStep.svelte" From 9b87d1a03ebf0855e238f16e38234b91a09e1bf7 Mon Sep 17 00:00:00 2001 From: Andrew Kingston Date: Wed, 18 Aug 2021 15:21:18 +0100 Subject: [PATCH 038/110] Add button actions for navigating form steps --- .../EventsEditor/actions/NextFormStep.svelte | 35 +++++++++++++++++++ .../EventsEditor/actions/PrevFormStep.svelte | 35 +++++++++++++++++++ .../EventsEditor/actions/index.js | 10 ++++++ packages/client/src/utils/buttonActions.js | 18 ++++++++++ .../src/forms/FormStep.svelte | 9 ++++- 5 files changed, 106 insertions(+), 1 deletion(-) create mode 100644 packages/builder/src/components/design/PropertiesPanel/PropertyControls/EventsEditor/actions/NextFormStep.svelte create mode 100644 packages/builder/src/components/design/PropertiesPanel/PropertyControls/EventsEditor/actions/PrevFormStep.svelte diff --git a/packages/builder/src/components/design/PropertiesPanel/PropertyControls/EventsEditor/actions/NextFormStep.svelte b/packages/builder/src/components/design/PropertiesPanel/PropertyControls/EventsEditor/actions/NextFormStep.svelte new file mode 100644 index 000000000..a635e158c --- /dev/null +++ b/packages/builder/src/components/design/PropertiesPanel/PropertyControls/EventsEditor/actions/NextFormStep.svelte @@ -0,0 +1,35 @@ + + +
+ + x._instanceName} + getOptionValue={x => x._id} + /> +
+ + diff --git a/packages/builder/src/components/design/PropertiesPanel/PropertyControls/EventsEditor/actions/index.js b/packages/builder/src/components/design/PropertiesPanel/PropertyControls/EventsEditor/actions/index.js index c769a2300..75feb533a 100644 --- a/packages/builder/src/components/design/PropertiesPanel/PropertyControls/EventsEditor/actions/index.js +++ b/packages/builder/src/components/design/PropertiesPanel/PropertyControls/EventsEditor/actions/index.js @@ -7,6 +7,8 @@ import ValidateForm from "./ValidateForm.svelte" import LogOut from "./LogOut.svelte" import ClearForm from "./ClearForm.svelte" import CloseScreenModal from "./CloseScreenModal.svelte" +import NextFormStep from "./NextFormStep.svelte" +import PrevFormStep from "./PrevFormStep.svelte" // Defines which actions are available to configure in the front end. // Unfortunately the "name" property is used as the identifier so please don't @@ -52,4 +54,12 @@ export default [ name: "Close Screen Modal", component: CloseScreenModal, }, + { + name: "Next Form Step", + component: NextFormStep, + }, + { + name: "Previous Form Step", + component: PrevFormStep, + }, ] diff --git a/packages/client/src/utils/buttonActions.js b/packages/client/src/utils/buttonActions.js index 408b35a70..dc190bad2 100644 --- a/packages/client/src/utils/buttonActions.js +++ b/packages/client/src/utils/buttonActions.js @@ -99,6 +99,22 @@ const clearFormHandler = async (action, context) => { ) } +const nextFormStepHandler = async (action, context) => { + return await executeActionHandler( + context, + action.parameters.componentId, + ActionTypes.NextFormStep + ) +} + +const prevFormStepHandler = async (action, context) => { + return await executeActionHandler( + context, + action.parameters.componentId, + ActionTypes.PrevFormStep + ) +} + const closeScreenModalHandler = () => { // Emit this as a window event, so parent screens which are iframing us in // can close the modal @@ -116,6 +132,8 @@ const handlerMap = { ["Log Out"]: logoutHandler, ["Clear Form"]: clearFormHandler, ["Close Screen Modal"]: closeScreenModalHandler, + ["Next Form Step"]: nextFormStepHandler, + ["Previous Form Step"]: prevFormStepHandler, } const confirmTextMap = { diff --git a/packages/standard-components/src/forms/FormStep.svelte b/packages/standard-components/src/forms/FormStep.svelte index 449978ec0..45db9d1f5 100644 --- a/packages/standard-components/src/forms/FormStep.svelte +++ b/packages/standard-components/src/forms/FormStep.svelte @@ -7,12 +7,19 @@ const { styleable } = getContext("sdk") const component = getContext("component") const formContext = getContext("form") + + $: formState = formContext?.formState {#if !formContext} -{:else} +{:else if step === $formState.step}
+
+ Step {step} is visible! +
+{:else} +
hiding step {step}!
{/if} From 5843fa0286c82425f31967f8678b3951a2c09086 Mon Sep 17 00:00:00 2001 From: Martin McKeaveney Date: Wed, 18 Aug 2021 18:09:40 +0100 Subject: [PATCH 039/110] 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 18fe4e576..999ff498c 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 aedbc97da..668033bc4 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 c1683cf78..bcba5f505 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 000000000..b1594cca9 --- /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 040/110] 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 999ff498c..8343e6283 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 zcmV)-K!?8{iwG0|00000|0w_~VMtOiV@ORlOnEsqVl!4SWK%V1T2nbTPgYhoO;>Dc zVQyr3R8em|NM&qo0PMYaciT3zFn<5$r@$-so+P~{C3(rB=Sk0XZMXGp;`rK5`#vXc zjt!BJgqkE+0+g-B{(bh}!C)asQJc54eazF-SR|Ig0GJJfnSK$`KIWvoJHojLCOC=z z`Iv@; z2O%rMQPdAeiu*B%JcLr5kRHClXlx+a3^s!;&)1F8beI#){d7pvs~Jt}Hba)QFN!|NQz8g&*K4{RYzG}LB0S7# zrUv)D-WiB;gj2*ak|NHEJR~S)mww11g0dKk0n3whG$L_E(;*TgR&bm~9I*is1ScHz z3mQiVXBkHWmLoADG)I>#zsO=75=2v!(6kVQ2i^slT(Ud@Jey8DA4OPT35vviTyQ~h zPocu>UQYoA^%_uMhSIj>%VOr0Ea-_zMtTy|n1)!Q!5f#Wp&N9)uf1HKo_l^Q{U77F zApAiXzykXJa5dCZC{X1V$ zJ1hi=(16BJV2^|`&IuahoJ#7?kzlA#P=+}t5u&MJXu^sd36e;9De0V-6Cg3WtVl%< zbv=(Jcu0C4LRk^VM=Yk{q=ya$hfEyhgp*Ww2(6(HW0L1Il6{N_f;vdxA-WtfPEe#r zLe=L`N;IhCXn}`h;T~$Wbbm)h9G{RdCxZ9Tr_VrCO&3sX4GoGoo_g(R#u%5jG4GVO0?E5i82hgHFCXAz_hIG1+CQAXmadJ>Wm)tjKz3qtod~3^x}G zafbUerUIl9`39jVXPNoiM?3G{!QVN-(T6mi95W_g9x`Igh;ad(%Hhi&J&C9hPKN~1fu69i;DRM6VhN@xltn1R zVx%j|s`6(Kwe@fH`4mQcl#>CyvLE_5ydY`Rvo8>GkW@ZuF=RQrn)tdv2o8*C_fH>w1(Nt~_PB>sURR89J%Qb^oiby)9IZG3g3XU$R z7@?R75$knjKv_q;2uHH}_cWz)h6A||C#*mbLzg(MjKrx{q-wN6?LHRA?2@KKXg#K> zEP|uo3J%{B*+EJ|!a2?->oESDBn(DRP*~*g1od+axETnNqh}?mp9Q+ngbKUO!mNO4 zN%Zf8Bp`ElH(tM|dWgRlgfCme!-Zfx#4$~Wl`S*CF_$02^LGbr?%v$ z&z^?|1DaAXQJ;q~>*E*(Y?$VLI_{yP;}3fucTW#K9NK~Hq&S`k8uD_08RKHahNtX; z7?L1aT|U`6Ih4O84T#zB9T}5Y>OBJnCE~$Pj0=gWuH_$=jY)bVS2DZ>&4s3d#~J=)(cg=$u@3`1%GpOM74R(FD!g*cV~0WncD!zI1)_moV5 zK8aqx?CchhIL&bguoI@m!g2so+ZR4f zodGoAg!O+Tp*RsN*E}F4Ycrq{kvcz82wxyyGrl=NQjFDXj?dUCe2ySsmK za`yB7ze*raHWAB9gh1cAvZN3SYS@_7wodkUkM~cPZ&I~^ZZSuyzB@-6K`=w839nDa ziCa8Y|F_y%`|!i*{z)LN#C`R#K>y#|+^E}sx3;!7pY;F7c)9`lktl)EG5ev9$L~;o zBIVjtYkQm^N%?RZ@t*hPi!XO>@QX4U8NEc4QFLec_0^h8QI zEESlh9JLNvM2=W4S{7t2mjqJ(Q|*x}NsiIshrRu?qYuZYZ@+miCHYXqXh;O|eX0BK z42J|sK#oZ)6^G!^zP-6YK1088mP#Psw!Q>X;tyo2{8Q(1kZK@qTGq@iTcT+g7ZE9$ zQed?377EbK&5Igr2S+u?WV`%U@KHe$0F4VQ3LfAnl5;`eo&Ist0sz7hgW2n~+uvv~ zdf!-F%|QD*hNJIsEETJK9_kT7l0@~;fj|kKAb~Fk!e~G)jUnACaBh?6;7D7h@EFrr zuGGNQJ^!V}*h?(JkusdXI1wATTjANt)6B;q@-LT}yl3U)o$Md~a)|W4O7e1$HM6QsT>DWP|*@CF^0ybFyJ5BAU)=k6wmm}#(SZmV?gPpk( zt)*p&t$6fd@9c2r{eBH!8H<)(?lEfNEDNeO7g_ObBQ8rku1{B^DHjr7Z|mJjG|k*u z2@yEL0z=yxmuhso9}Z7<4i5K^o2X63qUDD8jX_c)?a_EAIly8mk$tVmk)7byFO*YZ z=DEA^D(D2Apxb-hdEL=QMx_Zlj72Ja2J)pKmQeTumS5sLa=@2;^uDPg#U<=to2~w3 zo(B2vY((OWl@ zK1XCg(`gFmm!HAr8Yov1Aa;goyGJPKTI#fEh+b~OpENls1_OG9T7Ii0pLqb@t~!;# zwg<_@Cg}G9$8Z9{n>dys?Rs82#H=@DlMK|fnct%f_GB#t=3IZ+l* zq7PgsN6-5cK*iqSi7YDx07)ue%)?5_V-02;QMrDeWXohZO~nAU{*(Lv$y>9_yV#wF zNZ)0u=EpZM-rn2g=V}&s6_%!&T?(2sn)+K$GAqK--uK32KqHb0IlK8Zrj57BG@|YJ zOqte04L|{XU`(rfhbMDZgqB(MQr-kl0ROd`7+{N!!zs~+2T{srVNq=rb@T~?k{eq) zaUjekQ;v{c)T&gB`y`&Xo}t)mM7k?T{jJYi(KXnpU>yI|Lanpb>N}q$2}>1-d=7}J zfP!&E;w0dsHh|r07K8PW0@Z0l0j>`L-sy$|xW z6Qnlk=~fb);vtFr{-jqCl1}uYZWGqb3C3c))raXf!k~`v7RbO2^^6SMn-cHlk@-3o zYCgVRyxa8Q{NXH0TrC#pqwg9odp=i%dNHf9)E%QFz_n&u`j4r--PpjC*`mf4bDTyj zL0sgLKT(agLQ35vLKkF$A|`nq6Ux3i2X`7SMTDzpvp)iKjruRQNE+Hz+{kPsSUYhx z!iPnIHac@^O*4hBEf)RTJPqb~i3rusy;{-YD)jA-#@1(%q^X!2c zSL~=RQ^~;x?wOi0E*TD~59zQ7NADbXmVt@p zuJh%KW&cwRX}ej2Xyo&8=o@c>dHosVX|+QoBh1Fa$^LO2njdnOR2{MG>F+TaOu1+o zd9XAdPpf1>6);$lWYR-8N@)6#OSLH>v!OiNIXU_D!|~qNhV)3g0Zc)fp zsrC%`nm}4#qAB>^efaV0-Tp88@0Q!mzzK{q5V|gfcA5@YYu=o_KR7)2;CM%TO>{U8 zCOhERF#}rd((&hkcE%(sXqO`4PGc*P@wxSuAm!kpV)$Sq;dXmig?Pj9#!_Q|#S2Z( zS*OSQdj}^oNo;e&;La3SAHkWeE;U1{u~p_~MvZy10#)WthaS@k6LGTt?uWCVKAfEH z&qtdf_@%Svy!klUKRiAD*V*35;gXGNAJL|^e*JL#^Zs#d8gyIApK&fqRFNs1sOuTl z$5rZxy!!FTxwo+sU*bvmUEfvfS1Q5rqALVeju{s%ZU?lwgSdVta4s6*nKloB^Qk)O zhBm&oPWG730{!2dw%_Sm*aH2(^Qu$7|MhBn`|1AIqdb-Szee@f#^t|>-ft<5UkWM9 z^UZ8|YNRe1EKR&! znnolQXiHW$dmkV;g?(vtmL`)Y(5t%H6QR}Mt=v~~2!M-Jr;M)T-f5ausVt)_5s}l- zMNRV*&c7XH=$3Ddl1h^Ds)ALoCdh?ZVc)0@cE%*fL*jHYb12I}l$y$aVd%c>Owu%Q%guK(Lpi(esoE^qiAqO!8fpWH=|N_1)WU(0%Q9S}53c?t}+F z7ky$b{QpRW{`19)@)`hDcix|mF#nlMruuaRUDTR$|GMQg`OTReB`4zct<4_)g8K_} zfSOSAW?9r|r;m~+rbJw_{6bQ(i`UAPrCagOZt8C9Ub|CLMa84QtwOE@N#&osT2>)N z@$mzN#%AH8T+VkDG*kt+>pgGkq2!bbC94YdFPy})8rDNH;>>c}1vryLxfQLrAy?*LQzrl{Ik<9D9*d%ehBnY)KN6Wmgo0ue09rH#yu&rs{fxhGYQTSaiF-h8UoHmk?Pc9^qTzFA?p zlusEn!hmt=-rSir5$b6OEa3n?lgupz>Ga7%W6TQV3jKiw$TF_fSs9g4H$<{PPBe?A zwq%_^6_{?)^&D%2`fx=U_1Drmu{t>Q`D&2TzoI1j^xWoYkpI;&mgJd!Cbte3Bo$*8 z7YPYtOp}!rgGKh=?e2EX|7&COW#>u$e~hQuDYkl2sqW&}WH4(iKC)}%7g!^^a*b5P zuG^b<%cW?gH|Ld+id5i&_bejX%zI35^lMH9`H+I5Umg)bPi~kRF&PtIMB>)fT? zN?^$-zBAjY1OkSsC#GYP63&lu)~~2<e0>d`SvsxN&Ae^-0u4{9&8R~LjH1HJuC zr_)hTwnqfVV$_y^IJE|t#zjs}M>*jm7Dpz`mwGaiQ;2`b(unuaX21S&>#dOXQm)6*RK4LmOl@+y1J4IW7ne&6v>vV0l^` zb*CS6n8PVq@^2!~t@4B$#{#W8PKZ5?>eYrKI|G(-y#eaE}=P#zx z&k9apmaz|u*J}BLi+#k@_*-5XIlP$*?pq_kMaCPMEZN7`-;+<=&n^kf3~eDr{ zoCy}PxQ9-6kEVFZl7oAY8vqUaU)`H~%=6#Q=G6J`_S5?7p8A&0%>Dm(`rpRJtA_L6C;tB^&#m_VUqDfuO;PGL!0OxObVP-n zdx{oR2kUR99xgNF)z65_7D$eq_r5PFJk3dlId?y-7Slj0qGb-3<@C?#aotw`Yz|Fu z&*yU$7JENhtLb}22dkaWx%~cb$9ex8&qDtH80UYTPM!aEUTr+_|HpXlCjbBW3O{Y|5tjJ7V924aEq{4NN48# za@6YC=QlMK+LgIRLS*`N{d}fWk#(V}?A1J`>6{VOv3>2SZhU6%f1PjP9>~Dr^Pi2{ z{(t$Z`xO73q}cc&Z;kr-;DxyOJEf13-=4f|gZF4X`1kfkXJ zC4wH(|6}`Aga5~q|M#Oj&BwYdYS_!IC78coTQ;q`?&+y5+A{f_MvPswdPsI?rH1;+ zfidD-4(+8Q>te@I4#=FWCRvuj=-njc(`Z{P%I5zrnF?b-h}0ReQbM!LAN}T|e2q?a}TOX#WOh zyHjJnWjdJMcOOqqO={orcyP{zUN?66oQu3qnNaRG6UtM5ou_;)_nNQe>8AZt8kMh{ zMrBD>au11A7PkJBP-XdqD)swAY8q!daLq_mQmb|w|E(E$RQ`0yN*3#XcRvnVWdC`! zQTPAvY<8dg{~zV~3%m8KruQx7*fWRh&-UzT*y@X1d#0)^_U)+;>z*zH+5I1kNSx4N z%5rkwYiyzYw;unkyV>1-I{$r?XAK=;A+-gL1Vd{6(dCGws9(@HqUjK2IK03^!UJy& zosKAn)Z%hCAwD8;j3jGT1`(PL*HKPlEa;d(Bw*(~P9tv(rDO=(9X-!-GN4x^Qb#ub z=S6@%r11o?6lzM4P)2eT)06~Wuy=9>@}9Sbc3G0J6#cS$f+Cu8FBnqMhW{(@y`cX` z-iH617o%ZY{?Gj6uQsc|gLTD(W*PxX5wF5k?K8x0<&aet8mtSSoA$Y%lGqI=yzga*y`~3gdzclB0qv zOc3TtJj4^+Sx=yF8E z5yCk^`aVtsGc6(hi46nf``^Lb9OVQGa4HD*JYP-bcoVT4;XWG^&qpPiQ8(E7FV9Cu zzo0B>`tI)e?dRL+>b;YdAI-;(9Zj0~%ykYL|XY=`6Gt z=2JT&F%bklpC=QGVP|VLB3oB0YfR%gCkY!9j?DDB2$r&VN>afQ=A4BT3nR9a3qj3Q z3>BR9pmFgl&QqEWB|iR#^*L;elnGrUf~0B*7BrMQ942dmQbHmrl=Xoq6tMSo`Pvxe z#tcPlhWQ)o8^Y};beLm7EG^u1XIx=mz~Y!)0@mdoaAw~w5Bb_z3X+bGh+UE;0u&)P zD{!o|9?#n&8BwSDif+I%0r64Jp$V-|qcw~!tJ*iWC&J+mkjfJ-ie643IvCqM@SM0xs5z%e%+P!b_3 z1p~?42^x}=oOjQ?^MY#_cNx2&WWl7@;neW$e)#b7!T!m8fmVmJ zbMBpAl75h}Xs0x2E{60P{Yv^MW07kIeFzveoL_EAK$V-x(o56;O0GnXwGs6Z0M#xt za89w@%g2YDn0N3HY)?zbO%7v@d|7gR!S66rT)TFA?cXrECoe; zjB_ff<9&_Cum=ff$|7>24fD$_=|dI~v*o0^dufOU=X9t5zOTpKdw4LQDHTge2CD9k zu7Nlaf~La<1lI+Wha(adG06{cLY9E`8iC6W8z4{^z^g?NWSgl#eGh;thASZzEGGG~ z+yN?|+8PhO0^CJf$%PQqGZ0L}ehroYQwwFXnn_%MgsFEAKx#m@8{w3TkK%Zb#CW2U z9-J=$r>-UyT`bowRJC@;W0Y1Nt6)gEHzyNz&G zGhH-cv=L5S?cOnZjm|eLW5p3&VJj&(ZLNphO76th0A0A3ngM;Fz2t7C8lVgJQZt|r zw3p^!bm3lV2K0gUQbl;Jz`1ZQHN*Kpd&${KSx#9_#l)r$Ucf^YoKv+BOM!tp2QT*n zXUXSB88%UFmgMu_GZqt^N?k=?kHqLFmeW62DsX(nqMbr89%5HKx#a+^(fI(!oH($C zSs`YV_Y&1@;&gq8E9=s*eU z$;rE~0V}zpT+nn$Gkbjn0Kvj3GOg%CfqR41T>(`{e0@J4M!2cJ7Gm$dObKEUiSm2(HJ1Zt^;$1#b|jB0dD4x1N1dIZ#!@f4)bh_gEN~zE z`)1TWGB8HEs)`5=J0(8}Isf&0^Z-TWHu8wtj)1klVM2wcSr z8X-NJuSS-Twhz)e$oH*HE`itu@_<WVg>C27p@J)pe4r|!e% z(>&;Vu=%KP3y#qzAtI;Ys*2KyP~Q27T*+0SPo=ahgLIp>2ebyXQIrg9mnC`ty%O42RKP&Gij(a% zde=PR5AjSvYMnoqg7m!xZ_4?z4Q{OW3~6=pS1?e|HcY!XHXrT|=&rgjcW~4IZ@k1h zs5qoHZ2x?#*-$`xQ~zq(;jR=*!0HK19}k~SAu@W?kkPJzDMt(%#kKg6}0 zYji&D1l>-sJq7L2QrB)~Li-T+Z?4h#!QhaIqnvP(is?oCo~3eb=gSG#hz_*ezOEfv zIKt>CX9*D_QgHMklPr$q#{RCGMbF{pp1#)0(X?2i?kuXdUl@?hN3&wf%{?CAp3U`p#<~hZnMZIizQ%P_w$W2vR;z?y{E?WtSI4AF@naRO-vg&v#i) zKAfPOge*x&8j*~bNiw36FZuTTD=K5mEe$^|1he-Pryy3$VW9wTZk7`+ISZ~st*^^du7+09 z57qoi@^fi$nw{ZcHoO&3orPfx4K={;_86LRr%O-G7oKpI#Z-j|kTkaRG3Ds_CF!s0 zkYVenPq0`=SzHWhs#E6Pm;FyGr}uTkzvUQgTb^g`h7uf(%J>6LFqL!Pm+a?)Ox{8> zpVsZ4pU)A_hXvqFFdc0|Z_)Dr{lIcWu5gmYWPM(Ej{oN&q&jh;h8R)jEeJL1rLW^# z<*Fzvm9ZvFRF564*3vPFC+i5Kf9ZD5+TX4RlqNFe}BDoH@!_o5vN0aL_ z8ZA!)=XA_+asC1&c%t^oB~}|K9m)wbCdQ_S8mA25F{2TpoWt&s<5xUK&Ao|nv~E_& zqOmLQz@pEU=&BndJWw&;C`VC2fYzj{Qxk8-nKT^|XGC&R8fwi&cxUsg&7Km@GHZv_{KB~&0P0<=3K zQsWpA9r#7j7>lKXh?+AMYP1gQbfiipP(X( zupsLwV}hiDLZlp;hD8pci8xCLir8f;XhH%V;0b&&D#5@stTwzMz-XK{j7()_M6or= zm} z>Vvk*pnE?lKo`V+?RGX_)$@P8+WWPKT+EBAAny;y#V3m;~O4nLM3RKrpF0aE8MX30OXCdo+}=>i~@n z1$O1Dz3=@o?N3%32%ILm#K#?Sjb|=FaEm(!v?T6-^$XG_l+b=nKYFzzk2G0fyRWI_xvXLNws9~ z;YTdIAh|a#XMZ!;dg1_o_h%OUca*?8_c5RTcege-w(9v`yPc=}Z;$e7#>cu)h z!zWyjMAeTOy8!NjkI>ZV2&ZLeqX+R*DX(gH0`MQ2lYS$3`d<~t4 z5F2f$5F_I$bU`LuDY;|#fCYb#%* zwpW{)9s>Q-Ox#q*?$p(AG6NwJkFqt#s_Wng7lQevt{>(kQra31&_^|oBZjh^3`nk3 zBH5T25pK<4Vnh;7;xXY`1Cz`yi(!#!4YlX3t;u@b7hugE?7clYCp_=8I)OF0z z^?c$H!4Z%IP^PCAO%D`S&s(dew<~kvo;w&X{ z7vr49lQX!;O3nzCJK9@A^*Pg7>tJ>NIwGlA%Ij*kD{DEzC?x^{kE=6!Gh6JETY{$} z)5(THmLx?=LoA5G8d#>4!BCP8P2FI%r;)v!#8Tyex&7w#mZv+^>3%$90JE4|le=Mt zAJjS`!7xBAxkxTrOA!Tu`2}wql1Or218Io_@j-CUV&dC=cpFv`s`zmAu>BCzOqo6f zQzTa~n=@LSD#|v?)M0Xsn)T|bsZd1>9gs3uBe-SiY5-5g39iAXlZdH^-+D46%gi$6 zl9JI>%3VepOXZvc%fY#1@Ue38pvu-sQrOjs)nqyA$0UJ!cEBP;ZhbE#seBLl3*;mn zX_Ct7d(mB`=O6M&&X-(hlXI`3fgU>2K&kb`P@f0^A_Dl}syntO?iOE1mjvL-ax#{i z$Mr2N-AQwnfLsV(x)O9Sd5aNFAp?yhB5YuB=K`ORT=l3-rVvS1P~(OYc*cQ6XLwty zm*~MuWSQ+ASW!+CXfz!jK$05$djE=q1=wD6Ex9Sd-9w)~L&2TOgjr8DjHK`arYdu#Trfo7z=-mZrrTgC*GyVA zrp-Z=IC1YIzOGMgu&`}WUP?)BsAtKUBxL{r!X}L{>JBcC`5Q7en6~>d3oq2w9Rvb0 zv?ds^;*h0LIU)(tkc}aw(}-Q7Fs9%Wpp){O{Wzu0nM`9akF{)Xb!M&-S6e??S;C2v z&{KjA%tShMgrYa6IvA2G!I#2cgvK-%1&-nRj}f3w-<`lXNuJPD(R!RP$sa-F5+m#I zWrpK=L6kX26lYVq9GD?lAvo2Pc{-KGYRZ<$>gMZ3L(^efOMY!n|Ix^AO*T-I7cRk+N#P^dU^I;81UV?hyfABT`kQqHI(1;V7-ThnR* zM@hj&CHsU~-denJR1Q_q*VkTDp32IId=Yb9sxojx)0&1~+4IxxXL5mP7dmtNWf!&K zvXzN!p2vcd+#eP+BJGeRSs{pTJ*e7VnXuDChM*e=G%qcm@D?&bWKlvR!f(3vdr5ES24PN*{v)N;6i%u%vvs z+}g%x!dVl}aklXm4UEhxmkdx1Ao7$0;#K~l0m~CC)WQdn8_<+0$DJ6{2+o$l>@lfm z=_*U{6hh>pvl+%ET1eVfGMv`P+BA*G6=2A#3{WD*q%D`5rzohSK3gly8am$p-;W2! z`+Gg~F{Qs3MDwr#)oLn?&)`LS+Q4bB67sD{&~W?Td+0S zJA^_x%1G_)K24)DeVW{p%uerkRVL7@>}-r<8l6FaGOHJvEIc@6=_FwVFSos3{ZgUU z?MZHxGGUmHJYaq%#LS)RbsMLY$Wzsw%#Kt;w)ikQ<4&=1Q}kZh%|$!dOcN zjOp#FvtYNhqS$g@AI(x?jZ)&UJH?x8+zuXKVy#Zlb(M(HbzAd2WRh$3(5Gi495K`y zFxL9Vv(NgoGT*$H$|ZLd95oXeJt|-*nGKBJO6vNxwZLp{`VNq4tN2$Mj|+rqv_D~4^-KvUHw;ynmx}$ zHv8O=2t~hqzr3aO^`S!<|e#3C>dpa9+6tZVn^?K^Ck^>xh6AMQM9wwmXyg&jxb#z*Eo)zxr^{kRe_ftZZ_* zfqwad=m0FtCJlJ~i1g;hr$Uw^X@qWW8ZjcrQ)0#MAU`y+Ao6|iI7cWf@)-GC|I+ei zcjHyi2|7WyC*`E}nHDt8I1xpri~9c;9ep@CHNSpBtz9J!`=_Ak!&QS&ohJUqS=vJX z&-?PlmjjzUDAiVko_%Ry$Sc|Eq1J$=l#dJ$t(#}i4v9Hgx`u(WK>w_qF@2|+0vtkS z!xT~Rj010m%t9o_SHTl^s6315|JjJd8OeDdvRk@>ETI2euU>9e>HkJ&W9upZ`=dNx zzO=tZW193pagsW%m}KN_BGD3#NDqD6zPa&aDR2KO)1K;;+iA)d-qYoLD`=(fCatiU<5&;8_d)%qdp07!HIGicXUG-?|_n6a~H7a7)uQ0=(!Tr(ca;SEDK(d z83|v^K}$){&1f8AYCc+Qm@-*TQ!zlT|K$FE^49F~E_#>3q$I%R$2TzC-rMCTYZiDF zhOL@iWnQP}_&`}6)yWQ+#V(Dn3FmBPmyTTCh-YPBEM+ z4T<0gqe2-@)N+|QiF_R1X%6`~{Ik!a1@YeJ((YGv5eRXiGD$14!V7)9@q$cH#E3qm zzoJ~so)0}xWreQJiv1CwYt(QVl!S^JVg6B@w>bD#C5a@bhTK71 zU^Xa^iye=?IbahK$(k)^VX}zq!tL7LTZ9stqHVh%U3bK5L{p&zLp>!!g1!O!Xh&8p zQbA+4IbREr&s(*#AO@0^_!bui5da) z+N3H-auT>OMX5!HYb;bcf>bJq9i)SUo11m3OK8N+A*ch?a6ujPK$FdO9eiLUom^Kt zq|mpsjQ(Lv$3QLTtpm58Mw;0Kiru$!$~5iY`8w3%&HDqUv4pK+g13hIqJRr!vVTaQ~&(&|W;h!7tuNol;Uc9KXsi{^Il1nyMec!n8D-CJx$O)KE*5zM1B97#M zv+7w{@6&(s&n;UWl?8OW7i6-IzELDgHfURKXzM>YdW)pgC~KkTy4E!k>;#0o7f$n* zQMte1$XAICwR!lY8KUxlFon?D<`q`T9I7ia<5YJ0tALB-zgJruQ}*BOr~UsZPfY=Q!DMpXqUM`!r?Yh7t<9Hj^vglcl1eXigqY{ni0Nv|r%2Q3BrESN8h-C^p|8Eu{+!QZ z`mYYt9#a0_*qXBcZfw1JqW_Qa)X4vI5Qw^@?HCE*O7_E&_^E2Qvl3gf-fDCstP=KC zGi7gi>ekzb4F6sbYbLIRIcOY3?O58n%(+#ys9p_?uySjrs%mXICc-KW)@!5kysp~m zPneI8CdIn{fTmRSmc{6K@B@tr4}K&9wcHY|7w&2NoD;dPy-WVS&ocC1Y3mP1|GTfI z{r@(fSPelDs_AE{Rb)=u$uz^MAf3NEC|2DcWyHEZf zkMhiSz|x+z3z&c|y)-YV`GNgiS%N`gJ;ar)F&*}5CWn~UjKd;sY3W#E|Cg?*x0C_r z@Bi-h%Z=^&{_k!-#eaU3$KBU*+wpd}yPaFd|Hg(~*#QkPuLwo1ld_hHss~0(;HELw zmD5L4Fs+*Wbsu(R1F3G!nP~aMw`lmap+^vx&6*0DjuNMiCe{3t@PZeKc0C28n9iSr ziy8VRIMrYW-(yaWuo$6M%~e%f7x)NoY`^41qM))=wgFzp70t}PIl6PnyBia+97;7* zqOu~*sL^s7cKV#|smcMI)0oP+qy9c;?ag4hjWE!Zid|O5EH5qiYU9iAe#o+kW5hQR z2b?k6@O4hEQgn0EYb+x0&_N4o3RciquMBxtJ(P9g4)D>l^=E$gZ2ei7p|(ge#pxh~ zOIusMg6JQ}d^H|}Qw3s<{5TixyJw%BKI_2@>UdL&vwHI3gfWnP&#dKyKxnRQVVb&@ zYYlV`f=uJ^(H~(x`b$r!Yf?mQ2%r)~ZM59<)Ujb2s~z#zr^@ZK?hR1%Rl%yQv$0PA z)}J%U)t>d(>ck2;;Y6l4Y}E;E*6c2r+1t$O+!?K%Le2-cGQ^|mfo8on$&&iy)v7v& zl%{H}MCHsAA(D=JW@F92$lnOj?uU=NKka>g_VHx@xCR-x&iqh0DK(EW$Nx+w$7C@5 zbu}@p{y-4O%JIWoz>juLPJaDxy!U4V{-|lM)`36S-#y+x{S$#+KUbOF&DDX}IBrd2 zP`lVTbFHIq#;z}w8%^%!2Ek>u3muf=o3Z{wb=|6Sj!I;wY1uLWgCc zX#fcZXEYT~qsXQrSeQrF_>qV@)HF8QB+o>0=H&qYGHM5~=!3ML6v!<&d#+r9UJq+2?w)m>sqBdoB z-50gy?zAy#ZQXun)Ec_O)~K~KcW;ypI$`6c?U-dg>e6#-qgsDC-D#l~Hj| zuW4hQ4p#42>TLM>rEO?tQX6&B8GFlQ+R))Gn`%|e0{WtIm6_a5M5lrzyv~($z(x2;DNt)!T zCH8-1fK{nOEx_Fv)5I8zt^Y*b)QuMP(1SZ8G@R8`L}>QHeN(xujQZKY4wax>}FYdt5wL$sl#YQJy? z=YG%@`-)k4~nyIZb*qh3x3BHZ=sFv+7TJI(;|)`93Ju`P^pgGU(_;MwOb~t^ZOfJQXr*@&UO+JnxiQ zEZJb4Zo)^9K~#Lp^JL=d7w0|C+lit|h`OK5eCa%7_cfP*pPhjq`6XnPmA!#Oa7}QYoSne&$5vl#EFPIbtC6HLu{o4c@K9dj|zJ zrHIeakoiW+efvy`K9a0YT{?M&u94&yFSn5IMTo1yUgF8*k>ofQ+~)1^B03n5Tz*gi9cAk&QG5$W z5yVQ3AwwB-bU`NQ+&$7hH@R4hu6QR*LR9aC=xi$0gtAIO$q(NK?PTItTm=+be>KF= zjSR1I0nrqwVr9^3nBFIVjeqteRBzEgb-LOBzCc005$2=sRKq%0a@s_IM>HlTRgu}} zO{t%CkgYqAu6mnqA@|Lr5I5i$RY}b-6PC_My{1wfn*@;!8Sy?<-hF<)R{8w`nfPTf z<$`;VDG28{A%f&w$)}n$I&xa1BwLMB)tTpghra#xlxE+4+e7atS4o^Tf=#BabC?U+ zmE!RqQ#y+a7v@ttA~Dg)2waxyNKDhyt?QJ<4VsBfV~~@Cjh)mSdO$rR?E9xRQd95|8O zmq3y*QlX$WPZU;l9!T_(%Pn6iS$6Nu@e z@*DxFkQkDrm!>+^yJx0{lLV)NhCD$3(&B(YgJpRD+f<(>Lc8kQBrUmDEyO_L(BD~@ z+;wS{+P)>%QJF`9ZUO!h#s%zdmc|p8TsZ_yX{(-9f6Y#oZ)t9$!J6c)-nC=1J2K6< zhCFm#`L#!Qm{Z8Xh~_`n^%r}(@2P^`wZG_DT;h)PuDxq??l^IuH;zcok3QfeMMI=KXhcp3Qcsqhj*6>x({vmGyE&zwo20ZjH5>0mS?@fhanmYm0>W|I zYF?O-UUHywciKq#S{g6Xh~)7^V!2Eas_IqzE7zN{YF^JX1?g74XDlW#NR^mZ_H;QS zYV^iNgfKe##Ttg>o-xazu@7F^gw?qQ6@&g^(Oy>81}LZ3*@#tH>m_!bBl8CIUZb5T zlHA#(>XX`bExEjtSl+3p7W)!Y>=`CzbU4kh_4w*E0Wy(>A3PdsNAue?>sWZ@2kbD5r8)P!o ziBxeqnKx^5KQ}`(?dx`~8(T5`tCRTElK542;SL((oDwmS8&45$N3?>x;V~~QRT8uS zNv0%eP*z8bN7w9fn$YRDXYW`XaTbwoaYt&g`)XiRL(?aL+qi(t)H7!f) zw7g5fSh-O(bI+*s;meJ>>8{l@j63lqp73Q`ti%kSF*8e(Wv4|50XuWlpzQkO3MW}i zRNm}`Ejss2r`lbOQn+D>XKlo5)V@WFBAzwoRbiax7j$l0JSI5$HK&3=GSwAZbRAu+ zf?!ycIj8!$=o42%uCzFP%ei4@B%_RD(m28O7LBLkJZ)<=JkDD+w` z@xq1(MR(jYwdhhaD8)+Zx)Ype_Aa#U9;?9U1W}r%k05z23FC|`Ugy;oEBXHywAdJ= z_q-0{E$~>oX4EjHf;yUU(ga8*gK@Kxq| z@olB^73X^1oh1j4E6nwR%S(FHcbJ*R$zGvFZ*2|zKuFZb;l(;S;JhGwT{-O0ej#W& z^mN+H7!7neL^--7amW(LFwBY^eJVJ~KY!|eMl464A~Gg1%jBDl&nUqZ&Wz#oCz$fZ z9Kmtnr`->Sr^g51e>^=n{PFXr&Cklr0Oz>czJOSYM(k3ZKMG~4Cy@t5s019&x*)$l zg0zF>7_9j?yC5V}wn)lRmb2eTC<1hlB0gfdSoaD}(2rF7RP-$}A_k8As&RzVi5Ni_ zDcWP<1<9XDZ1g!5`aJoNrG9OUdkP`oa(PN{QX-h;;4k;-zf&x5j(@`w{Q0>#qSI${ zAxqlj=NC|%V-b$BtH!eWn=Y8)6bsf|P<>Mcf6F4?SW14-ZIM3af|AtF@**0sBq7c1 zPM6aS2$JEH3!23XDmo=qRh(dfn*mgxRI%L=O;R%1+Gs3kzpBCs7UH)gOmQ(B36?fi zsg*GmX-cwDG^2+4sEY1HIN8JLu(7E5s9Vj&Bqn)tqw1R~ctOTAy^L|uSk!*i^GYTp z{iAsS=x?gv5lu6iZgzvlqUP%h&->K<+%tD$<>vNr9**dk@Iap_vwYZ2Sag>3*_E|_ zND9&RK5g8lZbZgy@6)EPTAzNXXubRUG3&RL=XTpADnSx?pSGMPA{N>k%g`-J+xCb( z9nfJL^3Mh%k;LAo?FBWJoubW&D6+N@cxBbKEGHSxX*x9SU_P9(`hxj=;{V2IN+O@8 zg8A4t35UP7>-^v1nRWjoq-}F1(qYsYs8{H~ux`+swzTf6`Da{JX&{*Om_j0r^UcXQQtz!>a) z6DU9*ShUKQ?md6^fCNfRT^pUgG8$$@54Ac$w~Qc=kc8!v9_n`9t83Iw$T#IKEj8(X zNKJ!;BZgW7##;YCJeJDJwtSzGe?0sAM2S{=X6=91<^8UGEZYB_SFh^(f8%BMY5zaQ zW3Mqdx0&mCX;gCU#!v)b8~mr|j!%96>qAIC9n$owt%5DzRRCD9|GV8O{{O1eeaipx zC{HZ_GtLDa;Ly6e!_^9Ia_{?SCyGEw;291H8c-733%})S9L{ITd#wz7nSaVHfygkQ^MV7I8t_Qwy0}3hd&-1{$F*aA}3>#bAqy*T}{9n*txn* za&N?>gwtdZvW-^vfJZ|Kim6d#vc2GvUeJU@6oY9){>tR9wsP0!Z80j6emiW3+wDWy z%2~{YEa2l|d%M%guKeoDAWMhd1(`^O%X^-0ay@!JQZPv1ET*1)_qH2!Uj`k&^UO;y zEhE3$(3c}h20Jmi!f8Zu&%|wZM`cAce@YZkkmzuUVQj$K3^uoe&J#!YYd!Pn z|6MIV>X}dfUrG(IPX9MvKIMOSlxJ;Ck(jC%xwp24j>%vpLRm2?M=a#+f|J}I7BnJl zDB)N7(3ayQ^48YiOz4BY+oJBbm+qC8j{t}9)|3mb%};VRB5_7?9*8XV)}WASspqXB z6OyWY=auZFOlMKP^NixFmDwTXLMo;guX!*G^MF>A<0=X&tst}pBkAMtLWMRkw-+Il ziMp5_k-T#4P=c%4MnxW@F~#~arVYxcE)BxP9^Fa73Hp{$F(Uc5C@gZg(rujl%>de} zQI@-H4OpIFAzwQ8R@Ei4{zUT7ad|!2p#>^z!2r`(L*RymK#*5#*h8(c;^|7qkLZwa z(bMS|dgRTZztMe(L$dWI8sIIw{pR&%zx#T#^YRsW^J?qOOY#c8?r(NpMH`(rn}e;5 zSFgipd%F{Eb~azXQSrO8d1i<;ba{E{L+}~@J52OMG}ChF=I_-bp~KMOpb5S1*dM%ywzf7^H^0h>hMSQp zj7DV#yO%{kpt0nlq!G^D$?8^FKpbo;kG5yR{6Tm(3Dp|fadL?WhWbRpSl)oD;&niW zHMni#(3XoO0d;tr_yAd|g@qsSQ#j-sid ze1JsafES1Lw7F)j{?klIa%xw_gt z!efG-jntP*mR~6H@GO9~zjuN{DT}4jPGotL9)Deh+L0_RE?_MdTyJ4n>={RUhbI9t zp*|=dO>JoCOE3gp8qm;K_5#dN(jyuXdpXheDp|yg5o$~sI_j2EIQt&w#7~G&SIvNB z>8)qDlRLsymW7H6(eC4%K>O{rF~zlOhBqY83?MZwV}=BOXz^aAxsh~?;P zoPdQmTtq6#`M%TGh#uZwzDg{jBK6l#hJvm-X4q*^vb5d89qXiG1h^5a2O6V1u z5D}nV6*40tSx!O_%9yM+pQgFw4cvs<8=G%7I?yfUC?WZfL>@ww{Zx)zF001@-_y1N zz%RsPFriQ&=90ZRr{omLy;aW#P8(L%cK2NMi@F%ci%g=BM0x>9V)NZw)D60?YhWGg zdNZd&Pn|RMvhSnvA|3=HjA?qI;*4kxX0-w*4it_es_46JrMjza*sMyZJqkTA%@`0J z)XM0l?9SD05r|xg96J{V1GTX1?~>`LxUo8_Qh9+fa)dq;i&mwD)7jiqyxujPHU#8v zy>d=SSmaWN*kvi)Mb0GkrdoA!Xj{f2ZWc=hewt8ES92sPsZ%vjZpxGZA~3(;Z7}^y zaWXefIBh=Rp@{8Ih}Q@Wrc9C~mZ~XNkz*oFSXCtjn1oFKwgWc5)#yBeTHuIx)-r2f&b*tVWmA8%B(*Zf+E%HS~@qRNajSYqIHmngqh2+>VawHfKadG;c&j^hhHrq7_Dj-s7u8Zh)AEM5;~~1j6q{>@tO;haU-h zM+MnHdpedb&^=3R`!O2^iE0BRsEtndO}o3%?!InwI!x(+hBy_zK8o=L^VKarnht%O zMm~^Ke@Iv?d+{Tu7v1bT1=-J%i`WgeV|k^GAQsbhl0a81pCjqc<_MeWy0fxzcH_MNId)T&XGFegG0 zlol9VOhe9MD+fu^G0j;D?&V{gQx#}Hg}E@b_tP=&p--P-g333V4VlxGA3l80Z)$nO z*0d{2J%iYqy-NU$+^vsn)Ol6Ow*r`!aWP3)dP2ZPR|bePTS6u3b>6RP9&y4P%MMI$ zrv3VY*U|`y13qeJERIif^qU@P{Wlqte1cwf5=+Ca+;e*f;we$cb`I)lhDA84eiAty z4oTjVLQ(7)d6;NH9dVIkL53lXAr*?pQ-| zZzF9Dlw4EA(}kdLtOo5mx+HWs5>f)xBCX0A(e+Js%QabRClz$ZCU;S*QdVGW%K~k+QU`=<9BKsO&{)+>z_5M{Rc~6-ry8Nz} z4$iWmIvqBz<(hMyt6WqE>C{IPC$IkAubrMR34jUf^HqVc!9VW{iYe7tJ%VK{X2S`H z8C4KQj0>2I8LQYvpPE|4iNV#t?*#>oY_R1$_l zS_NhL60xs_6!eL}wHf?F9ayTmmm`vXOgR>m52zAd_Shj4hI872Gxrbdo_9{FBs0ac z7jEqs-KW|VuBTN+83Z=^V~#^|yi1^&tj_nGlCXJIgtTw}VPbzK2l{K2E zJMKuugVvXzQI@m5IlsUA(v9hWgp)8P?KNw^^q~uj(KU7PxX5${RJ8`2yq{`GteFfX zuw+VWXfPr;7Ngw}2``SY7}*LcQCh35zW>oGKR}AmZf9eA%f6GMtyHXjIz2toFB@}v zZtIV=G@a&kJy+(2em|X$Q;YZw*+^wuwOq+)IuYs$=$L_wX;d^IVg?$zN?5C5LBFjd zLD8CK6wE?EJuU9P7X~)9A}YZ*<}L*{3gxg6Nu!L>R0L=T;>_!ChdBue9m9cJ8TT+E z1De9YmpTl#ZT;cHcY6o{YT|%FW)(P=RCLwoyp#hDB|ocSxPsP&Mupi24h=39IRWR< zT~?%Wnmnbu?zpNljA{=C>0-hhKv~^b5jJ(Wh@t|F(Yr?+uPlZ&eogGraCT*h(ztWK8n^$xld5aP)rjlcu=wY5!pH+ zQbG-A8W9qKEl`3Y|FDL@Fe`j9%9#*Svh{N^)Pku$pjU0LcHwFb>CP;jh09jmPA9Rp z34PV-jk#)7zo6yTn5v+Ro`oo29g(XrF1Td5U^ciQ5r*)HJj4=Osl}GRW}4~2cmd18 zZmk4gIj_3ftt=lBv3vCKV>!V`Bo9d{z!`75;}m-jK+i5&1 zC?O)JVZ)M|d3=@^sqeT>w?{0ee@Ge^gIT~dPTFhwc-0@U+}BP%Zg;-6BOU3@rqt_l z#x-oN?NJ#SVLhMe)qPfO+cWhc%b6r+XJ$Q|2`1nyRg2<)SGQFK#?F}Jcu3%Wkn<5c zuVFdK1vPi|jI#RTk?TpV8u#izOTG^Hs6FoDI2+-vk_5^k+PQ~2i?SPBp)p4r!4;I< z2(HjuRBs19b<0l|uVD_I&v#kAnKJuQnb|P87z+2WvT>EC2^{4$py>FDf^Jq zAx&|7Tp^uh|FCMJ5B=XrXnEPw zlQpf>sU?!-EFoe<3htF^cA0;*)B=@Vaz`C|+9(+REbD`7(09Ezoi`n~o?7wBS5j7* zTEk^gQF7&H-A?D3`|bCFNJ7Vp%D&KIQ*6#>DqV9_?&x4A75IgF?}p))5+Sm&hZP`>0YI>Eg~SP-mpt2#phU)I(;7UH-CXP!9$ zB4qqV4vv)TG_<2VJ92J3pdfFQSQcfX5G zpbI@uiv&{6!LR?Hy*J%%8`l~{`?sC~E!TGw=a`x$J6>Jqw#!LX+$V!qoEpBHu9ZL} zEU`=xJOm`$l}fLEjefm;lDjaG0B6#Q?4*cqeRX0A!^Q@%vG>LvL>8C*NH3%tb(Bn|NVami2CO*GF8zk zjj*`LsWXz1^_2*+X+>v{Z5T`KnEsyJ*-VI_OJoV7ZY$b#5{e8o@W(_@%WP6{i|6SU z?7wCk{y(x6zdK%P=U=V1_(g#vKl<`dP1Ps{8IJ}>1LGUJx<kcs(ONr!5_q*6jfjLbkvyRUa??o_Jf0)M9iez~Uq!aC zkM?`2KV2GU)H_N?7Hw^_OD?6a5LgF2@Cyvx=$)5a_X`VUaTKJ2L=39tp275Hn#1H$ zb@dzD<%F?nR3^6vKjP3>@AynU(~NlrJ^p|FK~MHneno7L@RySQ`yl?MHt0cwP<~7snSQhjM_V9vYLLC$)evAyt zzW?s=`I~1SUZ1>resO+s`g}jtx#o_kOdI@-0gsry>P9yz%Ct)YKp2^(%#zd(Li4BO zvYvd<%o&bD{YA!%EGT)eJkI!7W#LLeC;su2142R;-2SyidYSe*?q5 z=gti*_eImbXsV9PFZzgCheih}ub7ebF1b-tbY4A-EV>eZJCO1Qw^CaB%5I6L^a%o;>TqWQ5v8^#bz3T2flIB`tt5o%0Y{u< z8IVeT)B;73<)Z`e$g~o)2uM7i>cO&DT_!0QxvVV;W`cs_i=~EK~~=5+O_ltq7>On&M{cmVtaAB*9G#U zx;ybC2M@xV9AtWw1Lak|8TO{VVf3xg1)Ni*bHTSUfh=?}_nHw-R@@U)Qb&n*vAzt( zKkz*2LcJ@>=G?8x(D7tGb%`I0C;a$x+6EGsj_IR;FguWH|6`3V#4nqB!duBDLe>;% zZv6$%k14!c^=CmwQ*|rp$eY)@%~EXFIjXUGmT|kp4k0@`U3MJ;#X zFHiDY#u?jF0^~6eJ2Jx~gq!}xQ^2Yw^gQ7-P4!Hg@zY0o-1w~oK>yab6GWf-vrn{r zz!Lj7RM^zo2ymU9IicMV*+l(`EH4butfi3 zO{fy*h^T!40+9p)OCzw^96Gwc-le+$mJx>s@PuxcX&eAR)~gj{9L3s~*Q`+Yh#YWH zukrzuViDU!^(#;!Jd6=puTcIn#7yJ!o4^fkGuGlU+${2!f)MWmVolKp#^`<^g3aA$ z+`!`{CUhTIoCSWozYqQ!m<=XJgL_^WLOh+_eoXcScOs_2E>MqQmoAYHAh<^8DsOc{ z^W`VuX!`yC z>P~zA=za$KpwC;DHrxm7$V@Xsrtu3_klXZ`vM^A2OMg8nU-{`MQm!}%*AWI`WI z|AR3L(6pqY>(ECI>)}9fO;K8MPL&{~$d~Gt!qu2;cQqzo@oG%&yc(0z)ey>svlSB{ zkY>OujCy7A@go0c2YHAh<$Ml7WWfp;&oBKBe-+wrmjHHd*$)Tcnw@wi(-PzuUX6t& z!{W_nJOD(uP^K7+2ZMt+m49TGES;-0%j678W~qEV6PAO}rmlet0+8>-Y8FTjq96mD zekp(vSTJ~*YEuapJlGxfJf60R+s<|N_BtWjxWvW&3zsk{l>4J|1r4jN5?oFO z6BX`>wUOK72l3_Dv@p;GE1}|&)l~2MftgNY`K*8 zvzaYXTBz{%VGTB4*J8jm#as=2VKjY*uaFTLyav&%>st$kMmT9MJZ^l<3M)G1}opJnt7@{G;L zMaJfNX5~qBah_Ry8j@K)B$5wkQGT!og`R%m&PU%B4p5O z9#~-yygNOQP93{Fh0GQ}H)Ly7A)32P25f9ZwWVU_gNW@IZ&7LoO!iggk9*3JvMWh~ zs%i0fWgGkW>0mHmX9fKH?0z6#I~-zVqPYjabSz^FSVDXgkMWLAj;CV|>IsjwI-4K| zJaaEEU1YsLp0|R2>}%PPQ8#T7D>aT^&$!%U#$x(}c)(fm7GN@XIs>DBg1&<`eV<9- zf5|b5<{qwGdx^ZV$oo0*q6zUVXK8T&I?j^E*gS4CcKn@7asH1u%fjn7@19@u=nZW! zMoIps*=RCI=6{`z+We0@DevM)r&ja;spQNhBw&uXZk=oDkkjjQK7F!)d#P?n!lYke zQqYZ3#AyL`l`%Ktp}^-C79o;Fs1Bgf|N>^97iKQ_Av#IISXDi zQe?N^V+3@6pN-0&XmCh*1b`2(-#mN%;rz|pci(Gi?Mi!M{V+(7M^I6p-#uoRhfO_T zS#eG*Hd5O9;PYpZreFDCSs465updCW{J#6CM*?K`xX;D=!S4|`pS!F#*SEd3gFx3< zJ$)J~^GO%@333}a( zK1r(6i+a6Y^t$n?8?;1s1gS(%&L~(x^-u*a(RC{HkKtQ#cCPd_N+|7h{!@k|z2-qj zw1~_aO?q^v-FNLe2w2xmHrS&aL`BkQ7knYI8|`Lg@ZV%R^Qe`1yV)@M|J((Z$4$T( z1yPJg5>GK=4x+$gITS}GFFgnaY~Ls01fSWF5h0Z-&q+mPxQ3wvzEhYH5)aoFWSqa@ z=#V=v4Ubv1g7cwHg`g%KEk>ToTEU-dSMZ8wKFkP;eaw|oTBL;7?=p^MFh`V!G7!OI zW!D|UA3xg^AQ`-iH|*IW`9Ss}=$QZniBtl+oLclnnw*K;(~LyRcTjG5)=_V|OHAl7 zxW2ybDd!;@$7T;4;o2K8+i^tyz`B^nud`;6XXdpI-bLhokXhGI6Pe>4gT7Qn8Mzo- z!ruVmkcaLic=z%GxJr>M^&b)Wiq{7Ks@ro2WTd8-hZ~@&3!>>+)FYkFSp-7P;0FK# zp@PsABYf*=p`D@c@Zi_0HK71{gil9>!cXnYO}qs~*Qp)ory$MTD{%wrk)F*K!qsv zuV-1TJ4UhmKO9UC6Zt9&5G;r*P@O;6>xX5#aHzt2pRzBb^0golFp1rcTtz%4Cu?OwBJ=?H zD2sK18I9u*^BMZT{G*m)a#KO`xcl_lr_$TCe5$%d%IiF~$gI|r<~WwroPGKfo%C8q zB+;v{&t5SkiT;)KMz>NYWn4!Rok=S(6PLgeIx^0wbKjl-JIZ=U#%^+nP%%l3RZR+1 zaWx8C6)lcN&*W6A5w%>Zc83*=A!yKd?!vz1{#)uzA;dxG@@pn=e+uzB;Jn3o(!ML> zqDa|*^;8$eiV(Egpzsv00(LQV|M~r}H=I}lCU?Pr$10^RB0YAQNkZ5A$)$?s8P^OO zlMPhFl!Gq1p~&aiUy2!Rxy^adK_icEOfZz}N#TNTTMAZ~P#%UBsUO*;ymX~>?Lk6; ze74Bg*yJ?o`yE{T0zKC;aK#^WQwSUMMu)utM_GoNegzk(N8uvog)X7%f$pi1NncuS z3$GqFojAt1=pNt@EZxNtg;lU-E<0~Ub7e1?8<$gubj=wM67iXog1aMkCOMY zE1Kf}ge$K}w|@cGcaTrPM8c;(yelN!x9W$Ns`|eu0Fnw(L;8Q>zqxqB=fb1f0tN#mJ*DU9uUnJ@LsAZlIZ8q#a;?9b448v7Ryy0z7 zIMDC0cVs zfOvJd^IEkt6T54wWwN+90wS(Ww}6tr*5BH)BlD&)|Cu`f+ou0>*LPfhVO#@c^na4H z{~Qm;{|B+LGOmI49@xS?fITEbv?@|sut1UJhJHWa;Z`PV9&l1#M9L^c zmr6B*kcOmDvm42_?#fn--S_$Y(q$|3pZ!Z{Lxd?$LI^0K|I^82oaFy!qj5|BcTw(< z{?GZ|iD>=*!1Jx}_;M$9Dja!EE1O;fUo*KE_Lb4Ig04b4IX{ytM0zhE0bR9BGSikA zaSxoZKUK&dKwRHO;OdBgVQ)C<06?iH!$X{0QWXB^`Yu@_=NJr3i+O6K&@l}Fx3LF` zltXzN)^yVck&4K4nJ&@~RqQ!l$Rk4(P{;upVZ3VYkRzaJxN7baZZsNM;x@oz(n&go zCBuI@+km)s-w6aJpiVjZMi8ZaZPP29YF%Xk<6!5y?XCFaTkb(bo~4o z{P$0vcnlSow+#^1a}!MqANXi;Pi3cdI2ybyXZQbU+U&km9KTS0;d zAd@7*kgEOi#{W?SKaxMEU-}wL5cLV59z7=BiiwyqP3{q+<8mTYA96j2)91{f<&EIh zyRAYyNg}>bv`2D+u>mu9TRq|tHT*k?fLoLQ_`f7ZYs!Dq;VdQpwfeu^ly6Y}|F!bp!f?&x$;9%6&h9Tr$kE`VN}IB5dvn)9KAIu{BojYdENs6E{QNS5mt60 z4zIL-TfrNVdqL&7S-HN7lpL--bPQgt34P1RFprHbf~h$5tM z_X7`-{L=N;H+n5zx?FAv? zLlc)GVq7~#PVKSe@k_M1<@8T=H+JKWs)It9v@5!cO%cpAC6ct?cp$xptK<%Mx1unD z?-$)jVuO03tqMMG03V@AU2SlXuUJPd}eO(?6wjX`+^#FA^Fx!{(y;+qG$`@mCxv>PP5$UA4}{bC#*f;TB=cJoCk`?61%*j=kF1xoo*lt89Pa2)1eyB~B(p|^YWVQv+G1m$t;Woe4 z20GDcF~r{xn_QLuD+%#bSW5W6(R4aZ@PDK6xV8V-Ny(7^xnfTSH{=<@j30RF*tEsP z9fCRCojIw;X*7P+5^wV>C=(F#2YTNLu*_dCxd-f^_%E6L8`C z)4)8-RU_0tY-XXs{a^6Xn_Wuv|HGvH=Xf@4^*=i)_jCVez3J|r4cL9mtJ+VRd3g}C z0$BG_BHZ8fO|vc4OJ({W=OKyMU<*2qOhH$88I;if!E`oC+J6oYTl&9~QlxlsWV*ln zm)q0GT6w#ayhPg^Ls?(VY_AE$E4FnlxpynR74zAZsg)v>#ZwwRXfk`J^Uu>lmQ#Q- z{%<&*B+vhBIBU=UE{Z(=ILu6HWTyQr%6|F{k_bNgCPbnjS!@!cA{S6TMM>6=5(`MG z5@iO@Q4^`?R=%@pKZlEod0C=EuR0ivz7C|b{^dMulfQ|CjT8K^M8yc!$B+m?WBBl2VLyG zRD8DTM?KxaT@HJ$nc0>EMopz8<{9g)@_X&yMH`lo<+h<5;E)S(njrcG;5S9}^bDfR zm&XEt#yTfxY}A)zT@)x zjqkhMJ1!nAVMX3Fcy7^7s~$u*K{!f~`qH2sN%~RLBons8^s9vKb$oJaFd6_vpGx*@ z+_Xw~M#m?`?kZ7*hZ4EB=n?1cagA%nFV11X}94Q?k%yYzRfa2WmcbkdeB-s zINA6vE0LNS0P<5jDM^jQ_Y#7X(VrDx)3T2&ZzLJFwt!fbk*cCF(b-!lYGH1{GKS@E z7%#dgE_D9p^4v>g-`ZXRS<&Yb`WQElcldCW96B|ntSHreenna8!b)8-8S{|7eLq0^ z3i*ziE?Mu|vSdwbE~;A6wD9`|!g*rQ6w_Kb@dApoo_j7?N;}D-xyYLO9P%tEbNfGs zbXg5zF(&65TTI92lL_J4`9cs1Eq}ueZaVX1fXzJ#v@r6YK1I&E8E{wi2C4#Wj$H}6 z4?_AHJLt@(DD zr^8-+e_T0wI1XvGJ}n!C)V*|VNKxfj{92oeqgTqla@bSWcOJh7fT|(iX&iJ@+!05YdaG4D zdLGo(?~MXB2GP*93piGRV_B^f!385^F|YZU|8gw2n2?^NuI7ZQ@#PJsR(vcnh34c5 zNQk=GbILv~yEy~dW7pfs#dyjT-BWln1@2~e`uW*I#gkH#X6yZh)yq4n+3H0(L%1S1 z?*{#arx|&L*FMcO*2z7A2}xt4^^?38a!j_0z)h|6iuEj*pgy8@-?T(Y;8Sc1p&nIL zECFGnCcE(7_}*Q#>QO4+~=9Sg(C1^X1UTP9A)+PAT+EsbJErH_9cWD+RuJOGDIaxj3_VD8NB4D)Y6#ImR z&^0JbZk195<#k;xL1bon&qfh80H7#bxjtX5pF(J(a}>J#ZM(=Mr?621wGZOzFX@qJ ztbvxrtayV|Ij_NxxD>5Y53*M%_pLdv?+gXA3Op({99;!o_DyhS zcBF(A5pn6vIf`$0N`hPeW|h0D-nFV4RlQSNKV`i%vHwPahi_LXBhyEADYO3`k5cyE zo-lQQ4JlBS~yq*00 zlrdQ;MC{P+oG6R)PU-d8TAjC6=O9V5TC4Nc>ip}lIVTch*L^U)few?^mSAsL;2 z_t-G2*>8=_%`qRAv3ZTv`r=0BHDKAXad~sd?#-yYn}oUB8kM(3+H<$|+^s!#YtP-{>-wfODV7NdudMn8? z^=IEx=KjW|-vCf6t*@t6UABiewc5wkWqYA!t+S^nN7XB|!r|@D)b(Tr@^cj0 z$fuD7odxJ^fR4diDGG}>5+xWPt0xnsC}zE>Z(@X)H4q(XV@1Ef4GQ4`X+H@pWNhAg z(>p!?xyIbgsTnh~gY^TLmEsH)50c^gF-rBG{m=VgTpHZ}vI;jLtHcPD<$sz?Q~qDG zNqhhAq-gj5V}e#26rSQ$07C@2|NMT~8y;DMF6f;LR@ZL`@mH*rak4tO` z)sc?%(AvG91whU5LBf&D7YZmwd9IH5eYhDi=3v;-Ek(*&=m%8XQD@qmzGccrb10|4vG={AY+=Ur6xV14~K+a@_Cp(a5sz zV4L(!5igq>@dk0Vm_%PN!NYko{{z|g={I~A@g<+K3N&dmyo)Puac7mBdlN!lY|~j` z#k%}MblE~LSi)gfVLFa9P^P%d$7FQZ*Mx~I)9^H3Pve3Ou*W0^ZfCxMUg7Re)D<1r z7<&%B_QiWcVSBS`QZnY=%7yI<#NrB#lQJ<4-Nk}?n-%dcysfG#u%~k`X8zk4t15dE zf8R+dmj9B*=tb7#(XM#A_F})&WOSz$_FalYM&NB6BFy?)#o?aLjb03rd^hI1*w{V+ ze8QzQL1G(plbxJ<*VjyKJV3{B*#!x`m$~Z1=AD%bm$d=r1W9zZ2=O}LsMUwa)&65L8O>7mAJbt=|94R`=)V>&M?@}(*?zPN=xw&; zmP@GXFV0D@pQ8DB9Jo13En=TygLg?u6+6wnUXgQ}a`?^g(Jk;@)WiSCE`Rn3yhImq zRH09Ii7901afAws}S=a+7tO{WFxilN)FYu(-NtlsZ;8A2h z`AB`?d+M1RiVu4hcoc;A4`fs1M0JGqVqC+)*ZwsOos;vkM+K-&nW5J=T~A;tHz=0g ztg^F_`bvtfx(tBs;;y-8Ix_)DD7M#GOb*L%#azLg#Mk7Wkvi3ildT1I!uxW&a2tzR z)d6!)bGVF$uIOB;V5(5P5`-l-!h0a9pe%$-!7g^8ESNxk!^Bi7`cm$teJ8c0)_m}y zg_hrH4Hvm2?xkH28x0pxP_}i?&QD|V$@lWvv-m?Cp+S%{@D}}h?S=@@C5n9>0mZCl z6j&p~n8xIplUBi}9)m}()`Y%gy}ri&TZ}oKB2y5p0B}k8sJk6hdDAf@{OEd!+!Bgb zG3p6=>H6y%11JvAiGTa(p>gKBDGm7l5PN8w@Bib;Y@B@m9}Zjn|4vHcmI>$3?%5$? zGIElQ=fA-<I0`D-)(O!bVHHP z_Fg=rZpuWj3--X(5dp*AaPp5l*yJ{N;K20>g>;<>qX^ZVyC`=Wm>bk>6E&4Lu&Tk& zTU@I`U`wld=*ogAm0-2%G!i2S@>#!Ev_UyXHR@5Gd5-)9<<8gINO>uSMfCID(1)g* ztNMai{+@tZ_VI|(AK8wpd0a#u{jSXcMCJXXH9_*XBI^%Ll@#e?jeR0OkAeD>i2IgR zyV8a5d(sy6;i_%7RvPkubJu5FdNnq%?EP;rOWJ=jV@4a6Rj zPHc)Y7NDe~9NIWXTpJ{^=C+uU?UXjHMgRlT9SWi-C!y?9jUs?sY?VLumKf_cj|^zA*9oSj4~2GZ0Z7 z`>Fx&c7I=sIHi6ngU##$WVx`W{;0YTBx@x|qop8UYAsTc#kRBVKxA>A2ib^B&ti)#u%d?Vb`We2txBduu{t-aY!W>WD`BT83c}*C1KavvXo{ zxM$~e_paVm6Fxk(9~w(H7Dt%(d;nk|1lj+ zTl=q_lqOcjTj2!L-EV;>tk&tn;SQU#WO``kZ82r}*ehqx^4r#B)GGGW&{BR2%v-fi zoA4a@JvV3Q>h!rqAh*WkH5jhwd}lCHTyJHW`KJUsHJOYvxTCn{1Hap=l;-z;brbNC z`+qh~>3_zv*8Y1prK)ng)~&yt7uxn=b_cqhEwT^QnT4jTL32i63)lX(F8RA(>36)q zzvy-SMRYOS*1^=#y_7>m5a&-$j5JM&k7bZ#^ic~2Efx4{LmQFtLm4bNeYFqm?L&L} z(Eb%YwAb*uF3DUQWG6wbfbC>opYrpurst`FC)?#GY|d_N!fRmqE?s@f%|cas3AE(6 zg-LNsmmto4o3jL}uUbo>f!AOOSnDkHJ^Q z;jESacTzs3g4&oqBYDn1N$Fp?r91$yp9an_RYYL8rkE#qHfQ_}Um-KBN&;JKvQtbaxBmSq{6|dy3Xc+(V!sumZ@jny5H;w<9+s)S! z|1&XxyT$)3>#Rz0i70}Fda?;p1}Xr!C5aSr03vn>17v1aXn>?8_7&3@^SRo`C{A9D zq}w?|iO+8l-w&?8&<0HXRL^!Qe}v*x+_k6h2cC1Zh`!WcHCHa`B>P-7KksJV`PAoT zI8FDS(t&F63(|vfobzDq8F4Z)S(UrsLP(GLe%8O3k2!bgRZSpu^b4f7dyQEC)rfSp zN>%<(WnY^^`wIDv6{qI8tNqt-G#aPuzlN>-|87cxKFDSML9B8v7pD^UrjqvVRQyZq z$qGDTVtQY7GL7WyKcy3BC#IW~$%T&rgwj2AAUIJ|x7u3S@} zomD~JE|SDZG-~@N#ULdh3b1pMHW4T_7cB0jnB5Trc*Zg}Ug)E^$J!gc5_XbIX(r>I z+bk2%1TNHBNCD%n-~yemJ?}zFn<{*tvssn+GIQi6=o#i2`Yd0;Shnsp6&;-{iR zklRl^XyRB~r`V_HMl-`F<`302aI&{5o7h&7X0?r6D^r4%y(>Xhc&`M=e0}wODYpgp z^cR>5QGf|!9dFs?!&XH5!aqosS^Ttc3FSZZ+25*vHEHYf=VSJr!iD_mUV_Ix$6X*o zdAxe?-#>kle}DcAw07OZvSLL~> zTEC*F{u6Lw+bE#m3f+<@5;5u-AMgylx0s;iJbq!lgzwgqG2F4Q62KSPsqr7o zGeX5n>2B``Y|8KW`>2dB%_B^w-dfoA)O!o45(T;pS}jw15!05J++FkmZ)*1uH<5ja zw__1Cph$nH_aa`FPtSk;nYx|~!hMcH8~IdIGFsAe?XBdXIwtcKbGQPTcCe1VhyhME zD1-~74RRbkUnlOadXT?DD;(Z#6ZjSHu{r$h-TOBxP4E8zJB~}jb-=dLeC;eyz9B%# z{XZP0;(s3wW^MlWos`)A`|*N;$5kGG{{pU0_rY+#uu0x+5x1_GCHPsi&3@q9?7=Vx ze?uDs&MTa3pzAS4ujr!J>$Mll!!3>Je~l#YW%PeMP1^sDr-##){_mn>$bb2n#7ZQ@ zgdc2519YD$;x!Vs^E<=H7oYj2suI+iS#!1gxR#mdmfr?(B3zNmHcGF{4YGJnVdq(h zgJgbs_4J%bM4aeEz6)n(%~eTrixLgr0eUTIyh^tB3X0s!DqB4NBT5wCiL+nwe`@*R z+ncAbG^PJ_k^q;||KTu6{|`s4{%ofVemrLV}!h$S5sHB|v z7r4UejuLY!{1zqXyzhhbm6p5E|Eftq%jo}ToXY<;nvGiezl-uf>HobX5#9AQu)~a^ zu`IHs%%XM7GTKC=54SX>|8;`Emc9Q^hlg$c=X)y6{Qs&)e9xv6wPU`Kgm3lr0nMH!Ui>@dz-mgnu)%Cp?@{;w1SuH^n798Ly_`+qhVwC_JVDb?-&N)GSW z5HIfTmVUd6Ayqjd{$29WD;S(5-=Doe`H;5Sges-@{P&i~Te%A#hv*Jtl%4 zcsOd$|8B}2IER#pNd!;~#8QLnCGx?1?Rt*uFF*k8E4V{yh3QSt8E^ z3lGnQZJO&Z4nT-JNZk$Mj;7jo=sTS~;G>1`27erc=+eDGjvzn(b-xGR_}(qRK5xlD z0s#tv=lZDE={>vna6xg1I(y(0uU6OxznoqG#|=rRw{U5n|5t$D>COKP`~1J^!*bDQ z|51OEjo*(uV%)$w0GF|qIvqp`;&t2EJ8 zX=K28G-z_NSThM{v?{_ZbIqwWR44kGdwJ<1>jm<>74!j@$Ph)LxK%VThD}ZpYqk$g zJP%*93xXYKf4xGXYsZwCuv5*X7Qh*r2n|C64TneLCgUF-jRhLybalE(F}SdyhnzS3 zfn6JGHf%K3Y{~8%0zbMpqQ+*#4jhj(gMF<>W4DYOk=w)ZU_SxWi}MqHcue)4 z3fn0W8L>0NrzPin9G~;ykpc4Ts1ZGy9UZ9yCMc4{pA41JHOFV9%K=FO5+_V{U|B^*DE@610CrO00P^Y2b=xWh(#!T9h{ zQMFt%0fVx6bB!4pPaDyv@l=q~C;+D|!`u=?DbG=XeRX0LS%?>N(C8wX42FUdXk-xaq z91}s*WTg+yeLHLh$#Aw0UNhz$dYrLMgyD95#q{zqYMu?3kToJ~l?op;6tl}<&>NXTBG{Xaj*cQdl(}%zW(zlc%C1(S*3`sNt>0#|nI7%Od?8rD@XEp0 zKHG;^dgqOZ(P%o3f!=UT4RWwDZZ^PJUXk%;(&#H1AwUtK@sP&yHzTm40iV+)D_IB*_lE&a20rnT_h_+muOA6cW-o}O}Tp5OgS-}9Y!Zw zkwot8%pWYoP@|+6H=H)&%!kv-K7ft`K2jWD4=-*n0(M`XVxQ0ux<38L$z+@+NDXh& z<-t*-sSb_~BPCEI{&Eg0J9WM5$E;Td=RyKxM*y1CbvS5-pTT4wymE;RF5Mf+{PEu{ zSzE3U`#xv_H;IJ<6hbN)$3>{|`lF<1H+b5J4h)`}v~3(|vcKpE+6HKJ zum|j5UtGJ?UJ4eTQWMa{fzNRx;xZUNmC9DBY35ImW?nHf?VZPU(Y|A3EYsjT94|hP)7fDo z^vw>3``{J4La_)Xgr*e^5fCzg2omg5K$p-@N>|a02j+|?O=u2lR!97oV?Q-k_o$J& zgEbS!lgVl9;86_7EwQObz#Q3dE#C9_>x@hp;v?j+YD~rq5kWph4r7A|4Y?Tqm;IgxD>EIg$>OUU@wt$D1z?C^pZ$E_IQ^PR z@%exI{N&lI=XWyxE4Tj}k5cx(vvC{$VK-&ZJYJp7oecZVl+YK;o{anYSJXh>n6H02 zd;J@``XLArBMMw)aC6JKcMn4c%;nQUr}GYjH9-el3TJfcqv_rwA?Vp2B;;5KpC2{N zB)j2n@j8rPi^Apfq`{-$66Y9GCejB4KtJ)69Gl<(>;nki#}kU|5e=yJmA9;%FN6F2 z3*@b0d@@4SBg=hdUJX{zhYJ+)*MD#xBCgaRF1IPPbQErez2WR1yxLEQg10EZ;LN|o zozBOPA8qUt?4b_le?#^Y!<#uB1%d{$T-_L-ay!J`h*wB91Rwd#Clq>~&=hw%PoA8u z0vuB4)8i*k0QS9G&Vr9YY%h+SG=#3~=Kb5}FQ1=WJpb_evKsh zai3kO{15)053*i=U%qS0SxAdp0mcW88?!q3DRZ1d6dBza{ z3Bk4tsWQixY_5EPc96##(AZYDXG}eUqjd67%_`Fm79r$Zci!}Q1(e)9?@wQxynXjO zuq-*2K!*;mXjl%ds8C8tq)d zSY6EZdNL3);f7d@Z~~V;BHR#%u|JHqQOHgjz##~cI2p+4fioY7XMT#orF+9fQ+fc1 zWc|>MjXaN$w>cAI0!%2F--4H}?+|zkUP9jqZ^31VSKt&o$c7GD-5%>?@EE-2z%%p| zOg%>U3OVk2#jg`P>e50C1&Et~Yt%_{e$aQUKT(J+NA_*Gz7<4$TO<4FaA42i6gfxA zCj6DlwTb>hKu@?Gd*9Gda$*u9jz4;h0au}2!&@#Ju6TRI?WFKCKNgU# zkK#xjcRIr!_z{N^XX#6mU6aTW%2e!IcIZ;qj_riVfmk(2Ttp~XA-cp4BeWEQ_u5h1 zi~eo#f%kbc>^ae7*mAv(>u1#V(KWz6QV^#=;cvIm8t{Fk)bYSrtOIrngKO9Gz%^Hn zVBzyO?1A@Zc@RKA_;X?gj#T5f$ZG9`QZv?06~)oa(%rqp3$uec zrb}aK9AWcHCF|fX5EE;nx%^xO{~9#dw^1}AJMcnL;e<>Uas&)6Ro(x{jRz@oJ#O{D zh&sMx-4X-{MFHVw`h9g`#Gq7_Ek^o_j@W*u^BPkmR+&bphTs(fj(d5DLPkS`%@QY^ zj(NKhFh}$nA#nk-!zQGT1K=QHhwdCXjPAU3SCD{L*hU-IcGpB8;eY%X;)S}ryYUPu zP7u%+k?OC@wh!Nz_!7KN(1N&H;>9A_VNA3_ceN&e_IyP9E7h4rv1M|>J#Zqf$_liA z?*!Lh3r&>ZvC=0+D3pM`4mpAqs@d_I--2-M`)pR?YAaz}L$kJ#FL1q9c5s@Lsw z_1yz^}@+FAtG{ zt_WMlY2Je@@wHNHma?ntD#}V_U>>KeDzQx--w>R!o+DwijJV~nV9K=o3@!l+Zf+b3 z4K()AL5IiTbSb#TYtI2Ic*Xy6SHeZc?O-qnA^!XclNcFbzIsJg00BHyY zK5CBU@^=-!LR`)?T%(V@PUk=Ins>`&BdyPbJwL;2^3`6*b6Epr#H9Mz11BDTj-~Df zdAA2_z1ZPi6Tx_CW51D4pyKrNIdjMp9znvAobAn0NMwr$0}}rxvE={#pC1T?bWK12 z7pTwvEhfyw@AD9Cxa;di84&ObCz6|&&;if@gSYQJ`ItCMa}V2B2jF_?+H6MwA72AJ zX9tQR;H;6t_$Br{e9h*j+znk{y1=D3$1#(Q&wTKHzzw|Xy%6Zi%g76|w{rFd!`X0O zFj*T)z~hY@(lzt|_Zm}owLsZEa3EREF$^7W4VAd%L{N>!y_%0_`B06<0|}M3TmVuY zUkMD421f&hSja@O8$hsqg($d2bFrL)e!wU(2c)q(T(1LwIu~qzeze!22P}(qWXX>D zbQMUZSAbxNetYb9AT=RTkD{jBTOXNx>^frK>!(}gjO9-;Jk2d|C0Ojl;4nHZ#CL-L zQE`30Pnj7AOl=d{45ScQ@*px0Sl`jnV5dnr5HiM7p48+uDm*N~d#!2-ke2J)>p7za zeP!oiIX>A>sI?eCx^sadQsTDaX8QU`0rBrVQjQjJPlnaf2kSvESi(ds9k!|>*oOwJ zeEw&1=*8FF$*&jS`Ry`lm;dW&emn~ z>^7a3h#x09A&q*mvF{6J-*O!fS<)Q!7(5!!27^cD#tiYAqUqRL!61-2W_4Mi++!7M z#@?Il85^PNHnKP56%I(Bh6t&B$Xk)V+1I|N=ytd{&uh3shC-XkS05dpd=zfO)i$hW zq>XZXhHN={-DxXIWL3ACkdumP4<=PWI zg^cIn<^fzLiM>C3NAo4n-vtPmuPJb_|AwpUj-Udb7Yc}p45!PO?!H+yIvdbWd3jCXFm=JQ< zHq)6rc{bGu5tmt9OJ|NdowG|mCo#_z+Y{VmRZ5!KzzeMVmvYttm>hNT>qWJruEvdSburOnz-FqlzNJq)0DIt6xT!{%fBy1^=+|1*ID!Fm z*{EG@j1&JBV7lafd=X7!`<}0-?ZFT^V2MH`nF$>SKb=0`ISyX_&;zfO3>&UgjABLX z`TF%17%v1x)Y&+p&X$h~_}Zelue#Oo<@yV4{2$po|MfhUw}!ppp?DZ&GJTxv713)P zvXkFiTp_j$<`RgYz+FX)yNMwX3_TZx(J8IJL?8(g zcrLXzF1qHre8DVSb5%2s=(3)Bt_}WV*T(7VlUL6{h-@4>BTzeD*2EqR0(yOLe#bvkbX!613J(Phu>+^oyPj1>J* zDUSFskjwwB4bILbHu=jR<7@l{_^9))O6jBCPfjAon_q>6DCu@}O z(exjJ4a!r-fB-wv+l3oZs8~8e!O5>oW#6wc(;qj`a~-ZrmsYEW-sq$LVtt7q>vVurbcmnV1X+W z^FN$SlK1~)HfYcPF3J}?{}KCt-*aqBeyMT(1MFx7dW-V^WR&FpC$q!${O_b}>-CF?RW$kE6zoKfb z0&t6^!TI05{x6mPbvzoh`v0Aj-Ms&`C-$M1n&-dv+5az9&i`bb%>O(YPTKRoi}FCu ze|z%psg$381m*cxiRrd}{|_hW_rGbY|KCa3$@#zUNwy`o)HwfA`|<_&|G_AA{%5WH zzmszR`oH_ez~8&nIR63pQvClgDgTcqqxSyaN!bnmA7JM~CT|m&N?1LUO58Uh{-&kI z`BymwpzqY91?A^IW&bl9w)THJDR({pRc`q9_&?mz;QW69`9EdcDTT)H(l69e_*E|6%I< zPsi=~-$^mge?>3dmJWO~rT+P^?*>qI{>Q2J|LJhp=6~BsN!llJ|8H)RjJM3)rIJ?LNYa9N1ZC=!;;k6#oAxxLcA?o#LcHw^$#cK%1x z+SzlmOAIZsR3Xq z|36E<|Bok!?ft)#((L@V)_&hgsdN6D1^_8N|D%-s?`+!Y|8`Qgum8L68MUQOsdN6D z1^_8L|C2#V|38?u=YJ<<*Y|%L?c-sUTIau^13>BhpVI%2X5-fXevtg_M-$|*%|F=Dc zVQyr3R8em|NM&qo0PMZ{cH1_WDE|Ger@)ajzt|m>761_p(EzCTEdUY}6N(v0W&Lb)qqj?rNG=ZF(f^+@wR4M2I2UDb}<**mJur zA5kyCu^ilYYG(jU0gM2NQ3NPS6Ayua+&Uid5r_lGMkEQh!2|^{j>dpZNJ?SkQ$R+5 zAsA9HOmW}?7{?ThNCMad;RM{0b7ps?3N_Hz_ae8kpxsveU8F&9g}ekYIsm`^CWxx;LKNEoqcjLguUqQi zJM=s;00G-oj66KTh=SV*ViS}As1Ha0WkEnEBn^Cy9WOzUiIY7Vatv1&^I!~zIKWJBBm4~jenMjXw*wB(&cxpdg8rK*m|YUWzQ+NgGs;kC;WsV$Z_ zBOpLkrem@+`=d~602Go$o z`$!OwTO5r=>j92<5$OM(Qt>_H9Yn}Ol)_}TEykaqkciPE;H6101H%Lg+>97X!1D~% z&t27Mh?&`DUYv?)3Dxfqg@Whq^6>#iu)_!j zGlo5y4KOB@P009~+#pR7_)(Y4j<3%7Z%zXOGCo676mYp`L_~>T^dq5+W2$G)4|9xh zdc#+;cq=3q95IwcFaU=ar{D(7w!we>at)}gpw@+tsW`B?^dXl2jHE4rIXueuzz+j{ zTreVsu`L#^2O}_ni7&+>aop<;CMe-!)leM9_$M?|qy7Ds$!6t|p-HX4r_V92#2q&v z9t=1@f|y8}VUu3|yTnrfA#~Y=ce~wwWm*2Nfaw+xLK;d64fN_dQ_`y}UZnrU8av`$ zngn8=Hvox(nMJc>f+Hr*Spv`X zsn_iasvrb(AJYo7fDPbd%3oy16uMK3RaCNi*xxyLSvz&h;ZZJkwY0 z3lgVJ93+ktqS(T009@K<4vwq=RN;gSe@7m>VkA*#W{4w9tmQIH$gP-O^G4#~W!-Y; z@1V!&z1m-#mHg%E-Qm&E$<@`npHBXj0eQ6pNRlB$^sOp$3L&8u8#CY5)ydK2$#wH4 zWgFlgb0q8AbEFU?W(ZW_b*-GZ$7AGwBb{~5-&~(ux$KUus*eTo|ISXosQ=yF-S4gC z|EG9*F8BdShSE~|fwz}uU^wG^ZKR|60jt zh#mfnOC(_Ms#IEtExarMS!q|uB=YTqvG~IW-qEvST^8^%^uq_hng36p2A$4_53(!K zF8K86!w1<|)`KkCD*nmo3FmT1#2}6+XrB`wU66#e4akZwak=~_+e3Gp9E0;W$0zSD z-dtY4dbY_qd5;BPj2LhnF8k0JdI)epE>XY*2lw2({qzYq1pH1(#DRR({@`+s-{q~+ zU%S7#kpkjV+o;)jOE~g^)JNH&b4|t73N?O|?5KL=5_nLD9Ctr5xSSoAc|#)AN(dDryrGzu6F^{KN=eW>s=>@?N3O(@_amuVvp?C*#_ zad?%EM)(f2opymgYYFgbw|->CC%v|V4`#_l{sI`*Nee>@Z89lUBjS9Rd!jx^b9BWNPO^tYO1l6sTl z@3hJQ`zT_3c9SxuwYNzb(Pn(QO#870Ab~#Bs@3E3t2rw|@hry?t%AoD|KF}U0NZrx zl~^C@`Vp@gB#{J>&H+&tkT6bA5V~~I5nvCh*f9I zJT!)7{;kXb8qXQd+mxz&1b?K2p~9%kt%NXwW8^!-*&xRxT`8k(71p!`V=>-}!*nWP zkY>CEJa9!l%>xfg?A<&vUp$4X&DV=}n=;Oy*HPkpF;|(s3uoEpToG!kuCd4-BPYOx zX4~qI(%!CYprp1a9E%Bzd=di65`I2mg|?J)-N*+wXa;=3&pN`SesvD+6kL)B=h0?< zxZoog?(d?=Gpo4ru;E}G2Jr-*ry=Nf=hP}63ZI)T`rAAe{6CCiZLN2F6bED$qUa&6 z!HfLAcKZeYuYPx@x7PnY$z%Kf!Z@a#X|HvIBYyyn&D6KFT4GS}QIRexB#a3^Q$v5C ze*lj}QNp_*at0RFr)G~D)Ll(@UCrBlgcH1LMkI2WZvY=f~N^YlqHa%v9elA z7otlf&zlx7&Qq!f3)RA+S44Lsls88^f9g>$%;LG>HW@Gg8s(yOqgBd0jZier8q|K> z-@QFP{qFGU#g{zax zA~fG8B+NTv8SC#Q8kJnMG(VUrkIN#Nn+Hs+NHiM&=!ZCZOS#w-qIxJV4zI3$d2@OE zxgot!ZU7}nPtFg&J3D!Icz$^Huj|vJt41re;0a_)0mb%D&#zA|&kxVu9bTNi`|0Fg zD@R0)O`r-BdEcE9sQ>lLwek%3oIo01q7r;(Z+>`pcJlMdS+m_NP6F)=1U_a=JB~)A zJ#WrlpPrw-vAiQbCps(#lSARyQ3I`V>G*P>t(im#Z6gwHHMSHPpIfg1DHk3p`W&nz z+;$Jk5N~1LXfy_~ctz85*6HQR@#$49iLGu}xHGxNN3gclMl&QETV`%*YRsDzQDyFQ zs4*=u5mzT?-@p6u&DHhEe6(qTpE+yJn~$rL^Xtohy*s`-Z`i2v5v^+Lmp7L`om>{C zLA53Q8Rw!z7AfIGUe7Q-u3Sc>`HvUYy^X`*7S6Ko>aN-_S4o%`Z6+{hW?ZzmEzpV% z;_4lP39E#stR521r_$6d+W6cu*;76X4Ah;I$(#R}TDo;VLifT^)mV-BUU&$f>Hd3uJ zs*-)DsjgC1MpZ&A!Jdtp>M5LmJ4(|n$0#KwC)LU`R)Yc~=f$acBRe>pq6ChS)k*D8 znn7gQx*Cme&c&{K4pLrKd+0bDz*TMGd$RpsLI1Un^~%nGMgCvC-CoK6YwiF4G|vK0 ze&blfiQTz!u9;5?O%*!zI4@+o{^z`YE#oqb{1OP}ax~bC2m_lGg;SIqkuZh{0_|^K z_1xYIr`rbZk##5B{V5$Hec}HH%+#M-TiG=LEbqK;P9XgW%}V`Rj4o);xqscZntb+- zkCGyG|JLfqzu^7?9)T*K?B<1ZW$ESov#Yw>U1@ictH^m2 z*j3OSLlOUz=F3W?$aDNuqOqF!$d>a%84X!M-1VL}^`c}+gyK~h`4x`x$|G&|F5 zyF6^C$j()>M_Ggd>!2(5Sp>LrX6G;Nv;bf>!qwfMy0SX1fu%39YFYP)v@VImTBUF+ z@D*C4oeePiUCD~%ek5st7LFkYoQ}@GdlI*lN+1eNa;77*<1-ZcvF=G_4cPY0Lv1H`AQ}0wF0*1*az*7_E)jZ6$n7t?-L8bPGeIz5lMV;Osx^W*NE38DNf4coz}I2E1OR`6=3{bl`f zY!ED(QPY9Ia+w{qr(ei0=Ote9FT&3)`z+-DR^S1&$o}8$?-%*MUT6gMeak@^%i>(6$V-FNA$NMeCtO%74$zBulWh&|K9#?N&Y|R zt>eEu$@7(o^mV}r^fGqD;%!xj9&dILRMff8P0+@${l%03jmN!M6HvTnkkteWM8bKY z=IVQIJif-0Z_63<-NJ5wEuQ@O7`Eqwf*-=o;vwoUX84| z0`MfZ-)tG0ZEd}J65Fpt!e7wpn?1Gr|LNqve*d6s|J_-i|4;JVYyW=*f?_>IDe3_8 zZ=1=85<4r37E}c5FD4!~8S?Tr;;aRf0PDTua0-tT6hlhw56i_gP=aWd!=;)0SsvGY z<nhut;&)MhwU+(1nGMG8*Thw@@W>M8DCS?&t zCMdx&9ca5c+ICyz$bS>VKT_1vqb_E*tR6=px^fZe`9|bOtqhs>YSYx0&6OMnmu$Au z8Np;7EcJ0O(5qeVwRa*Y1daaO8|W*4D(U|UVm{f=LjCXF&YJ#zrl&Dm_r!r)n7Kk) zoA=8StLyDI1rgfjxh9Oo>UGgRlZnWxP@eay9#c7IM1E|ad#Wp+`u(r+Ev$eHEVlpj z3;Tcnpudj)@gz^9q!@GfKf8ESdQp|2W&e+Zwg2~%Jk@4h;#chDc7wy;DJ+}zL-%xPi?(%srwJi9?E&B&8m^&e zInYX++p)QHWL(VXY)I~m#X;T@0>3`zMW6qsttrWv@|O)pJ}0@UXs)1IKSxuPd;zqh zV*lr(1h2pVykP(D9TfFH{a$x%|9zV0FJRWquUA8^3a^_Pc2)T6qGk8Krri=~e*@cY zY0URb2Xl1x_Uc-v_RY+Lb1wASvCHRNshvYQYJ8;z`Dk)UEkN;Lp9+fXHS;=Dg@8Ra4Mf#tEe$oHG zyVG0y|3As|6}$B;C-*gS?3qLM>pgoaw)!I1o>G;?zCFcZt>`k4+5heY1tA_sBtffQ zV+-}a#rSW%o!j9B5W&%S8zd-z3{R1W1Z=&Bk>Av->nVsGfXf^ zDL6eop)EH~2sTI&_{i^@`ZIg>)bkOO$0%Hmws2IJGyyfsSO9&tlukn+)v@7cjI-T4--Ywt-DP<@DDV3N& z;MX5f5N2H{Yu1*gvLJ*J9HYc-wf^}(90!~-00T+@i8=Hz2xf*JgI_NQW#a^0{m-+k z!7*l&bSNOsVCjhQ(hf@y>V%Mk)S(Ox10-g6oFMu~uq7hH|9Z(~xcj#!M8GX@JHg%r zzytwxAIBFn%_07gj9uV3--@}pNRTK%F+;S~a^z%AcK}HM9Fi$&IUqwb=()T9-g3aj z&md076mz~3CHg?(>xQE^jeL{@Gmdc_jS@&%l6ovn5ZDxFjk$v2A$|W7C{On-wP3_r z(o-h9Nn5RJVip;HE0>UHkpEQ9rGmKx@A=Ac)T{S{R(|Q0)~hb5*X%WKUYaD;qN5sf zeC6@(ES%^yrbe&R6?-Ha;c=RX{*Es&>n*lh(l&|4HV~DEF@Lr&ScO`e}+X&a8 zJ=LE&J_-;+;`94(W-x4R%}QjeYFUjko)Z+3DWX76uZ>{Ni$^G86hKOehauB!OSTZ? zTm_-8zUl2)$m19wGcAw@f|KEYX%~Oe9n{&1yM-F-dE)d zW8@ptV}Ty#?_{V6w-e%V0vR&2@W`HViGdLb0&**`&i8;d`(}CY*Va`~yCM`&7H%bI;CN?}bXuO;AiIW+a)tZ-f`U zazyRNo`m;mFX%n-UPH~`xCywXp*++Kjvp0jp^Re&*x0Boo1yqr0Lg2CSX6sdV5QR1 z0BaPj>ITVtkUxT}7ID7ApvZ-3^G77bB_y7M*!34ITL1X zJi^Eam@@|aa7SQ_B9y=i8s7lW2nX+5?|n=;0lq?BnxHEha0V?w-dvzFNRLmi4!=7) zd3SYk^!D=P-PP6EyUUZ~)60{i>%}lMMa%o%`_}uEDj1Ilxxr|`q!-~-@EyH*^V8|c z)v7=%!r6V_dVha+wi!ie@u^DDJ0oX5vn7Wr1-69O6Z!Nh4I(0;NzhbGN zXo?dO2|;`c6U?dOs>Y*_2M%aNd~~H0^Uap@Iq{L+a$MZK)kK3*JeB~j>aq789F1^< zStHIs);&};07Z=9X#9xa+JI7Tg8Vc<$vF&B186@2;j%+UKnM)NtA!z8n#n=E0zl<~ z%OPbXKuOcnK~%mrH6DEh*o!om3&AaBASlEBQD_2mEfDi+I&lGDy50(aRDkYP!pRpO zhQTok;7lbwc;5g{RgDX}kgr`)aYb-;D*??VZKzxcm4M1>D}*ouv{wx%O@({XvNNx$ zJ;u!UD&fp$x~RfvC7i0-$}##8yziTX75aFItt8JqPd+ zydS}UA`4b8PFX#PFIGRgcx$c37$qJDL7ChHWOa}){d@%P_q!ILA&$66*AS_%aU>YBCQIi%E`GIv>K<{Gx{kZO(}ZEmQb<@NUzVX)H?N2er`P)r1}uZ zV5PJX9mrt4x;pzDu%bIm8IBsN*^4Vc5G)u0-HI{_tPECr1>_<93C&lgd;~wC8D{~; zh)FPoNxFK_4~1HhD#vW6)_Xe|N^`A`Wz$v-h%1Qqhiop)XO?5C1#;9(^-~YB@UVCs zkTVORh8Eq)t5g>aj)xqyx}pJl#b?Dw@cu=&+qFQ|x4xDJRabmEP~j4`myg;V{V7c^ zX0fVd5zUycip8q4O{^ZLmoV~47|eh-ArwVmIJ5fCMqs;#UkMYyC5*<^*13=1FbK%4 z@BlF!E^hz~RSPQ}fug@S8YhU-CB?84Q5vYq;z~|J0?u|DQ^yJ+)u)<u6K2jwu;cyoPJfgg;wl}g7AHYq}2hyrrEwMsx)lA=7Q z*`LRd!J+W)tEqjVVf0nWM`I51+(9L@`YQioXj4`4Q2@1}<-OH``{R7s{3AHg9E@2B zLNb3OaOGK03F*arF*1X+a~c&vUbQsY0I>`Bg(}28njYaJc)z#1(=X71rb1a}el1F1 zjPONlc!bOm{r(8AS_6%RvKo18&Y&M4lzp@tc9v6eEY*v|M=lh5W2s+JaZ&j(NqCNm zOHh~9`kDP8fqgV8Q%35t3aSF6kEyCELb{}SvIyxTDk?vM_xn2sFSC`pq|T=yjk#O_ z%8Pqy6*`~lL9amPBf~9NN}mw11bfR0N>@yJ=L2-dSAnuhDP9JsHt!E;0cb5KX;_!> zp(^`NY3Lrohe``d8qj4Ne(Ick9tB*@%xgfGarUZn^s)h6*;B;mGR|I&9KBWrwV?tU z(q)`%KZ3LB34e^I1gUZUYy|0R1zySdvm@MCR}5)>@|RFh*DI!N9P1Ac2lPl@m^;0w zfH!Ec4ss5u72AJBkQ#V%h+XEwO|}9I?Uy$!vg)e=QUMw#B+Q@(fW7QEZ7t`_tdps3d(GWyt63V-qIi!8G{0>5eyNH zp;t*PK7u#u`ZF-))D2?`UQ9ufMv=UY`XKb?X>I{hVJ(@$YWwR)@FTeeBSB{)76^8c zdI^k?p$?mzsU6PrEy-;#Qg>d5(7WN~vmrg#g{sB1MUc{Ye3!YL$h*8rha$_gL8ZE! zym>?t^yUgA$RlBhA|Lr%+hCY7Kqn*(d_K<dIm?dB|UXk_h?cq0aZ6^8(t{k3xnK%-^o+lCEpAUm<5hGLqw`J5dR=I~qy4KW$b2kVSD*eYzp42D6)C^j}MZGWrvoyEvlRsd|ye&hp4Ip9o_ zGX>PORPPDJl+smN4JeH@M|`CjlTWE+DRtn9s8m@0DJ@;R*aG{KjFLjw1SKlq4h5SF zOq1I+jb_%sIUSRPz25>MoXNd%3*`oi#(V;`im@u9#x((OO0W+wrDFH+@yjzu&b^Lt zw5?ajqOnWwz;s9@>#7<<)Ri&cFa>^!1g(i=r#jw@HEAj&&IBbWQ`G8<@Yd#+n?2DE zEZG%#U5Uy`xWyE?;0hs75siqi+(vPYHq=n0j7|~+e8l*U6^mJ@mGkRZYMSr2gfA_7 zOGiiM5eu_m#;IMvmJLP377+dnQj4EOld>(FJ{sW&$(RFd0*a=M?F^UT_mna~Ld*bW zE;yPXE^$nd3j88zjKNagNkQ$0d#=5rxutfoy)m%LAUe``;ZwPc#)xrQL~#rx0%(eo z8AxLvGPDh1!cfGph?IjPFHJ;fB1%F8d~zEx93oc*coM!CxnN)lRukS(z^I)zG*6{x zL>_DWjGICn2<+*iN0}!QO}zja_^z7Z{ia5hA8%X1--PkqS~iV?sW z9kD{Z2_bT^RXF$PW8&@QnIK}(w&)|{vIK~H_v$N3DDNAi&s4~?BaPeo8sZnOpNBmr z{-dI#YH2zA2)gUYJh~6f0JtFjYp>TY=6^ou_IKCuAD`kWgms1q!z1X4Ll%>c;6)G} zfR8Gj)}ilPD!36wfv@?1>^1dBY{{V}l>yBs5$v~M9GjnAclU+cYx!Atu7M$`mUAa3 zbe6S1lCAR}PS5|F6Iuuoej-r87vIeU-OTx~wkAYp^{fwWnA-y_>~TN;m_T8@2eclQo&hqJ)j4Tt3Mu!2qgOQJtmVB8Xl@3GS za+X4d%vdBOPfv!BGts?w+?_qQyFN3%u4g{|w?x4Q^f90Q?{~Z1V*clY{{A}t|C2l$ z8{D+i8D^Ik2<1+lk1~{BOl$lq2Jor5KQ@-DM}K- zfpa3C35Dhy5hqM*LkNUfx{7xb6vQZ@E{lWKhA5<48niZmPMVXwYh}ktmfSLX*V1W3 z#F3YvF{Uh;4ZzI{>W;m{#YD#Fm#IebD9E(}z=jyf5PCPakPEdQ31i6c5C@pec$XCW zQaof@W5dB!w!<_Dz!XCz2pNN|)L#oPKGg}ALBBzmOHSVaF7J3z%XTG!`jaL=%?cS;be? ze2qyd15`FR^Wwa?KC4=i@%RNBrgxfdIS@)q3P@zPDsoeZp^L!P;6>N`paZbGyCb{# zg-hVmwhgs!oH)E@AGA5pn8+!n}wR96K7tAI<63Whz zh$-$tiZZ3o^NIX&OOhK&9-h0RZCiyXweeCq5&q1PtgtPuU;Ic5Ql)rNTbLJnPQmf{ zl~FR?Vmc{pXwerV;0B>sHZuuf}9XB z7?>kVZ9RQD*&|$LSxA#rX9yD{+V5;kF?84jIlM(jwJ3nlqM*!s18yM&!vID%V2C`J zN_Al(5)(wkTo^@n{8&=wmlP5!L`ob1SRfFYg(*LIc6@cmiPK?#=|tz`0iytprCkdeX*PKin&M7>=ViYuy2TWPC>bNa1wd{;Wh0jduE4)M z-ClGBjQng8pO0l+T;Fo0(e#|x%1$_?PSuTrVoJB%30OJt!BX#7+R||*}n{Qu%p4)p-0P9lKtDOop zb=K7LzArLMC#C>`07t?(3hEQgNCk?e;TskjleuaozpJ&9Ji}vV+|v0q03H>}sHV)$ zRgP~ezHVlbb>+e`zq3O}xroX;s(KpuX_Ya3I@6j+RHmGfRf@n3mD^MB-)iMt=_npY zBx2}}Nf$$`L??^3#l)w2vBV=#cq_;d!><$zlv;?XEUSrl5avc9JRn}G*Ro#T=Yb^7M z`5W&h}CWoN~F{*#2Se|)k5Z{O~4RGd|B(NuT#qSY4J~^5&pLGPgVRK zfA_5_sWuFo7};D4N*mxzIH(KscqBHnS0;htb1iW(An>N;K9;j5wqq_av?$hKIT>aZ zbb!sw@;9ctykPMf#^X_yyFtlS?2y%gpPTg;2|QkDcFpE1%B-R6ua z_2-Q!^`B@&slUXCz-xFH@(mDR4{_1yMqU#0$!#P|4?iGsh8gMu?J^?ho*}lwfQ;Qx zwjnsEez*6s)9ZJ7FFF*DBRs+$jF_WLF%Bb+wBh1t?7+x(1W9$qhy=VB$G3V>&7Mif ze&VNy*!*sztmmdzz!U7Z0BB}@#1NioWU zLhO;1#W8xAj3u?-$QS~L(!jB*4nVs=QM?2(Nl@BC;i4O|7TY2pI%#>=wNIic9e`hd z6BCrZQOA(Bu6+OIje1ka@V%j&;)?mbH;lWSILPh#z+@ekl^nx>DIOQ1kVIEV=;)4( zho9aOGF_|nep&U%v6a#+$$Hc67Z<$ZMo{3=Nhc;jaHTG24M6)pr7y;QH#9Wd@ICj3 zASkgyrgI^##*lfF{3n*+@falo&J+bL%?~5Rr~}Fp$k149QXz$DpcX(Pt6! zI2ykdpUrfK@a}D7oI?hnCqGF0*`-}Y0vO`x5(5v z1Ja3!??fUUraqZ6P|Rinj@%pV8#HTc91?W_WdPbIf21(@cyfm+qvpHH65ba7HE+>q zggiC??Q^16=f?{n0$DH4$;ln^Qie8r?mi$bJ=iz5)2&)~=lV)QrWz66CAgBvvci;1=QWgh@v>X+;nkub$KEc2#>K4+_J2 z%meirWDOsS{BBofot%mss_L~wA|_E*(NWzB7k=|rNFTbFYm|fo>yz5$SGgR>8GRHf zWKZs5e)eSPr?c;B>A*O4^V6X;>2_IrQJqGmkJ83b?0|QXH-k3>KqzD)@ACp-ve&H& zijrt7A4Xylknv2g8F>)2pIWPC6`K^1rA54Gnd+L$uhKb7ZvKK>X1UG5a5aAC&GE9! z>}fi$TVD6*y>emYmQ}lYU1`#Nt;zx#^RrQ?t63sWGLRsVTv|%cnMMZtC}Nsv)qMe< zgb+uYbW3_fiu(uR7!|bCUGgBfCCLpJ1!#vK;`|>+_vA=O9Sc(HoRnp*M3JMcggP}j zh8)glT-~5s$ze#OnW~ZxCJx1qXSZ^X=Nx0aU_n>9}?)H3p5DeHvxTjx0HZc`q$R-2O?#SDVOV@nOdKNH%4I>xEbj^-OQNF&8D&&dAfg5hJ;|&U zu%O-+ksxVJH45qwkk4K5cf-Jr5=1%pTHPh#2A;Tvfg(R9IASh16uFYs9oqzX2v6l@ z2kq#gZhZJ)IFrU;)7GDk_?}&r6Jh^Jatjk*m?b4$)iSS$r^F`wJS$*0ht`Ekg?fWS zg-eA>6nI3^h)+{X%C3jDs4PZxEU&d#n9v3&BO9wkHu4&Zx}J1&@fM^E2U@j=BQQ+q zOv<(`>8!M*W1F&G?jG#yZHT|+Wq9W7?e+TI-Iv`RX)f~-NA0#UMQ3*}BBHK(yE)At z;;kdZIaC)%K0?0G1#(dMA4U=A#i_$431N(LwoZb^iZOLY_^u-`YP@*`1(KZ^IxF(= z^}5~A=qA)1>zCcvSUI^#9P52(*C=g zku)=s#Ih>d6?8m@@{+1GpCxJJSgzBZiOCxx)B?J3)Y(wxRcAyJM>+Y}-8o8+bfGqz z6tA-xSFqW-N2z2K>-n8p-S4t(`%Zp{6T->aJG~y>F~Wo`Rk7j%uXd{vjKe8P;28ZZ z43rkXXT6pe`n%!$)ia5Ui-)f#xoYgI18x4=rIXII2ZMM5dy*5#iYVtEYAwp1dk3Zz z^xZpAw(s77SD@IA_|(fj-MkQUXnlUj@{Oh1?^Fd4;jE>yXOO@rw~=H#CC4eZC1oT* z*W^ut$2fw)WsY=a{fkxOzZw3HJad+oRk!klRqNUgMa_BHE2|0H3qUdOg=-@6t!0)L zi$UsLm3XqDggUuIvQQtbOw3j`??KCw0;OK^P?|mEo*HyI|IF$O*Pw3)FS{?hc0IY` zrLQEfR9eGXQI?&`&wJhObNkyLDdL2Vrr~A+8QQF9>O*$X{#84iIoZ$lr(gTwZ(CbZ z%`rq1IK^B5=GfOfDGbmtxs9wbQwE2IM*V^#pWNnT4^vRv_^rBmLQs&f#Zt`P75SPM z-5gYJ8+(Yaogpv{K z=thmlj_B*PdTTlSW*+IvcTD@7w}3KVnnR;(mK2t@+*`~1M(fS*m3Bq|3WgBj+oGeG z=~k5pNX0GX%y$4$0GW!GRIt5hTdQCbjj=*EwS@et8|rN6i_i{^scq)h?@uozeNcn2 zvlFP1h{4t*Ey$`$toZY(oNzXPuM}YeQ6HQ*A$J(5)dQ&Hb&cxR8e2YKMoyX%T0S8QZANK` z5;5Be15p|dQL<(9Lbg+mB?6hO9hgS{@BjQ?E4BZ&>f5Sp?c z>;L;d|CeT2L4-x7DO!~imJIk~F_OmnN?we2%pT+m#?dRLv!PZt7b4gMInt@yF{@5N zQGo_wOax20O=@oOqFjUjo3A7OqiTt>!%iKfXIp;JAgPx+KZ~Vml%4GKyDz%Acl_w; zOXpFg+0h;{@;rLcebMDbUUXlGw}qy4!|iq+cJrGzmoZwYA8UcvI?<>I8IZ{nCi6}{ z$;##sQE?H9uQEfnNQAar-JUItbLt(!$cOq)PetAqA*F{1q_GQrh6y%yUg6y@J(T5E zPzn;+sUCX;(_85dQ%lwLH@?a#XVsVvMesdIa`T<-$z#izr|XLU>vUb!(&8gxb%cK* z>Hi$WezLZLA)4mkk)zkm*iJ>2 zen|jGC)1K!lKvnye_Aecl|i$nI|22h^l3aOMXMx8L-d-9yxC+p=GJCB6Dm&_IP?Zb zH0vVdHDu!*noK)Wm~;X>%wf4Bt9E2ny=8v3d#u_tHb@m)C4@M#bl3T%S_iQW?y{iH zgR%-hy_Xx{5rr|E9pgmPchT!TyHhJ`fTqshXZL1H zR)wx%vj*UWW>)e}ke!q!9g#AtV|}3FhvY@1CskcJNRbIeVd`LlzoWpT6U=w5!1&(BNHO@6BYGQ>e1{gALQeukCOy{nSgWUCT zpLjRO+#a`%rQZ=_$eRd`!MT;oOZdHxQDn+K$xU+bMcgI_l{d&(?__A2(00XQdU|V_RhYWuhnxvNdtLT{1o?~U z{jPL7P-_2yMHe#5=7zkjr1QJTP-f8>`P=$-t8EtKG}ZQ!Hn(|$S(RdYnXQ_sXF0bk z16~h6o9~NuO(6+UTMQ->5300U4fR?g?z{;*Z-jG93!1GJ>b9)KQK{crDP^(^+C|!= zJFAxmbcx7_^aX0Hf`-o*_zZV(me)oCH1Y#U9(x0^~r4U@zYY5w5@45KORk7zFatw4kIU2*|CmUkWsq z`ps-507kRinIPM=%|2%NfC-66qOob!k=hge!K?^L4la>EG>*pBV#~JIWg4LS&tnyu zlQhUJOdy;%uP2B~PjnGiQU@uJrME#WJ z!w_uQ=E;iwGTt5|* z2JaBBir#TX_Z^jN?iS|;HYbF#E#L?icyntD{1fbVcVBcr^TbdZ*zEQu-ICmi90or? z0~J0q0ubCHbW=Aw35w9ItS8tYiv`adSaej<+DW)31>b;8VXmyV z?Q8Sp2YJ!-)&Fkqx&Pk&1hzm&)Ld9`3-B$oS1~i;QZsW4lSrENm!FtxLO=+k8QA;p z|E6^~e=UVf7=gWi6S<`Fo>VMNBINTHwk6kOa+nCJ1eu5)OQJ=yvAf#Y*!_xUWB0+c zvAb|Kq;laj&M38#G;0-^(r%<>}Nxwx*!3aT? zChGe7k-7b>UMr7mk$k|Rlaz5P=O|T&z^)WLF$MkIF17Usy{;OlaF|uIq-382T`>|H zzUB^j*%H+%AoD_KYi}WD27p$pPJ$D`?nn@b{5!&TuC=kzO3)NjOrjq#=68hl4$fb;ZW)DV z_l0%?_qs#JIIXK24xNlTmE8HO zoW4Pwv-!?EXY)F@@}j!9&aJ)-$$mW~m4NIm3CH@T@v=9KbC-Kt_l)ZE`NHWw1|K& z&JtQNL-#d@mhLvpH3&9$81E~NX0_KHa?1Ql=big|l@^g&tA7829ZNw`<*q!)DlCEZ zl7w7>HDc2R4`8Pl;cDTHN1acrWvB8IO$9CoLo$C zME8#B$T8AWs|c1@rJ0L_3>kDT0xN8Q>!S;^>-gy@WwsdNgwIujXzqf+z{A`^)JrO3 zKB!!d*%D=5%w=Eg{ zz>(scG{*Z89qjE0P!C14)%{)MgJV1zVdVUXf*^!ZcGt3{qHe0hd|CW@&gBj`iy2WG z0DmHCfZguPebE0FbbK`JM5#!UEBlx?_9VpK1OCM z43W^St3-%ytM%c71Mo?M#@Jl}eWmOplupVPI3R;PZMie)}#bpj%tKSD^CpiwFSk&fgrLyt{aFdHw3c zhvqHub~q#`plDv3&o=qt;jsWXPL>mk_f+;i`1DDnG&J|H90Go)B#I%MylQ`NX^cEq z+`0I*`9w$9*wMD^PlMPn^=ZNWFS= ztoBxlRRg>L`}E>IKug6~0bZW|qgTtddgZpWSZAxUpV&=D&lvqh7hKWO#cVjltL_C5>jc@3hBG};D_M0U+;)(-#o zSH5LQ<2K6(dh)tq|tg5Z;K)hZRAw z2SO=jMM}u-U1qTihKPw!1~Pc8PKBGhk6-NyPz>HBlhArH`9Sp~*qHz%iPQqS8k%{d zT+T*>w1Bv7sqCP_^Q@(9x)Va#0Nmc*y87~vM>4kufpE(P%vT(>U+^Y|nRPb*s6BIT zgLh@FA5@mjsfqIa4nRk#qH?(yFcmg{EaV{`f$Os?fVCo7=|2ql%H{_EX8UtUWR!+i zhZ|t23(W8w7SLAf)PPXa`8I$+svvB|h*)}7XlEGtBKUQfQYQ16TlwirD|4DHLD_U^ z1x6)EE7wX^fd$m{_=NmZ3eo^Y9`TVcp#0Vp5vGM=ksCDQC8Vk^zt9Da6vtGt#C0mA zPoG*FtA13A0DxZ(FV9cUe;9!G<*wgnjCz)#<$n~HEGV>So>P|9PY}niFl5*r74k~G zcgroVa?}m+fP|{NL;D9Tf6^_ItZK{k8o66wikboo~PthXd)d zfdj-z;44nUp*KMT@J&aEXq|6bCwDO?@jBPD;XXu;@Rlm)sNi(fjGL2{5!NDCCKwm& zm?o`uvQF?3uq2HtIBrVxWUUD$7=H4z1 zU~viMT9f@Qvr_8*Zn%Ohz26tMkSc}a8YFJk2-ucXy;UnM-eX^isLZJ+rEAXj8{WB) z9nfvHz7zhJQ|Z<$aUz@=DGuXcX099MvYc4C<3y$tj?K_0Dl9sz4W*gk$!)dXQqGxr z(4?dSFw!}lWD+~!bjT%8%(9eD6Ep+2Fk)axQcgTE1n(%k6U_K_&St#BXS`huq)@*Is;28PNa5l)P!*p)dN-Szu zxrTTw4%VrJMCt+RQI_chD=KFp<}37n^;@mPgp%0V05F%*gHFY_5~wai7(!CW5Sj9_TllfkiAwhV-XG7*Lr z>5u$So?s&X z5ySy#ALTOXE6;6ttA`IKi*YWS2P6R#Jf5JW3D$D?h3!PTb(>j~bhDCeweIVcvsIah ztp?1#(RzE6yq6!*jQk6R!F`(j2yo=1h=E-NpKW;8Nch~UAMrHR|Cs!&Otf>0P zUDc8{yeUeCR+TYg=5Livzd+H;T!LOyV%RqD;MeEb+5P;t0+`^}=S5x;BV9~_B>?&L zc}h{Cwr~Wy>zaC+93hSX!?oESQ0nXQOItORKWXxRmiGUu>HiQ%K90t@ zV_*^e-!1xo?(}wc*7W~Lp09@fe}MtPpZ_JbA<~tnAOtL+|9iW;J4OC~ zf2X&m|4;FJM*2S#Ysb*~|02Vc@c3#awi;}CK`ZZj5PZ$#Uiw!Wvx2QcJG?kmGeq4V zAq8zcO)Art>~RAe@}D~751=^m5V(0kLC@{=TL3T?sPGVnBZiXiafImv`2*0+d(5+3 z3Z1+I;40R@JmpZ`g3Vvk2S!DdzcM{fKh&`0Y$9LeL;;N)P!Yy!$rbVfSQ@S+SBY>M zHJ-Tlz+>@}bOuYs{po54;-%XzAg};+>7uWJsC?J9Jj12hO(rl4c5eIbsz3wTGR~YF7Ak#EV z2o?7Vmc6c#k)Z7d)pm4pI4SPJ#%ak|e@}>Gkp^`d$V{ zVj5TZ{pn|+|KCpW{_lQ&ue+B2p5l2z`ERi^SA8g|xlUDECDBqv7}c>(q<~p+M_;=X zKAvc~IPU=fBPtzDq?etH!>jz?LU<>0FX%isA&zvUI1W&VA_fE7Tk;{$tMJqkSm`bK z8ph(6jyElS#0mKwc}&c!qoTQpKYel=-NM8_yf|I@Fj@)yuXFrX&m#KY?-%1g?(ZDz zuj&6&JYTl|UybAU{mAs=x`X^RiTQm0a1~U&!V6qdr-k0$%KG|MbI@P+voiW$<@l|h zh5q0Bh5JAKgM-~Q{eP0@%cuYKj^FE3;LG+7U%Q2W`Oiw|ztWtlTl}RXU=jWA?(~ZG z|J}WH{I@50J|q1%RKBpM*M$DF5&FL(gv64x<&oZ!Org5 z|LaMfW$b-6A>Yu+ro9Q|Yh^4$gbC~IHt^ecLZ zPZ6wiB~r9MXCVCyN67=uZcSk%-!Gd{#0I&tt`0t*14w$1ah^k)_)q>w@e~F+?Q3CuV-_judx(R!o@a`EbRsg_(g8S^38z6e!Ix-?(QOe8j*RDC#)cPQ6fwDZ26;z| zY=p}QVRoIl;r13^vM_C_c>USLYs+CLkN0JEdOk<8hP;?Lx#`qZxAp+v4TJKc(@E*W z(qyUJbVg0~R{%N@T7e?m`L|00U1)SZ#D5_+xhem*AjH$$X954$-`m?O@PGZC-L?P6 zlROpjzfkO{;D#bYSnh*J9b5Lect9|xhchS3aT@nFYDwFC2vq`NVW5xvn5g{qihCeF z%6_Tr-(ZnZEWk9giit#2t@)Z6F%9#yF|t6E*LEy9U{jmsN?3uAME#nKZzTZQb>_g_ zVX!QW2H%x-#-;6+GHcf&!)3*M)%2AGqsqE3ahPqeq3=%Y{(Bv4d0u2Cz||uHO;J?6 zRC70GGY{&g8hl44XRCrt#SW_*>w`KLS+4Gd`Y)SVxX1ZF$I@HM}k zU;+K_?(O%B{-3>rHT{2*XP)B4SLy!hEw@L;TY0sVe1Wz(gK~K_vzJmvLcVkzwRRiY zipA&_sg(@MvLW@qXfpet{V&o&E~WsB_`lxHZgKzb_jlL(|0y1||4CAr)F_|!b6)n- zzaWVaqrVT4n3F8Fh*6mfsGgz}%bUakimI5*;5BL@Jv})-zP|mfg#b-z0QoEG#{Uk_ zgXll==HC0%&m#WspjYJocK6r!f1c(kkSgZIUot7LC#-xaKES!6SiJyaPC>ooX~MjC zX$~_Ez-9qh=EYk%2i>*(?@6A*j!ONnbpP|E_<;RitiHnipZY7= z|Ed4Q?f<40fJO4(K{5YFf4A3L%YRSue03XLuD()yw&q*C+`vONd##(8aZp&Ss9OL3Ya_6YFjMfLIsOyDe;Zt@Z&I5gJ}Q(1Cz-mhVC4pMRDM zYC2~M%jwW7Jad}91R?u>fOUS^e{GMkUS-04y)xLEliGf^5E0P0mXC`FCr`vnB4 zqCF=&rd1nNw~exQJo-8^(*C7ZaX-Jr*u39K;kA3m7NyA^OZy$#d^+8VzSejX(3ocQQ8Vkn7Va2i)?010L1 z^4>+&xMVXIUKGZ=4%;m5buLg~yhm(XgYt!*i!te-t zkfFx0_^mn(d$~c)BmGNx?EY8|;a!L$|20k$JH~wmpDN&lqC^c`b6QdGECax_8S4L_GayCdKH+1{!GSAKLv_zfq9Y*GVk zvDs#zHkRwr9ZTS0GA^A}yp_&3;L!6>%)kwrQNgB?lu7=XazRHht|CS>fGVO0_@-0Q z5*`8c2iPp|+aE!IqAk#`!cV4FC^e=y+BsETWxRb={VI%=Ue;zQ;Ez`bU7dgYgv8~y zI=prMfxcf=^!()d-FK(w$M3FAE`L5Pq~2JV3_)_yxzq=_K5OHB&ugx(R=)LxnM4YM zU2zhTxm`bT5)&|&sL6BdH4Zsmz4CgNpr0MTySO|#dvkbP1@{1SQkrxE;=!O3;35BC zs9JO>x&Hwm$ihi9tt1Qef>c(mPLNuj;bJghX?&58VPP}dUls^>xki=*Ex=P0AxfL{ zlPf!?qXa=8H*GXucuuRFWyw*aWv!S}M{!Zf`DCh<3aV`BM0jmmw^Aw@Cxj7?1Osq= zbW!-Gmcg?c_LR3>$L|54ODH>ykM8C-B+#X8wVFq-gSz>-F^7#oEWzF!j#c7V6OM#${_BxTTp6dCQUs>LBKI@}4LKe9C1Z)uXzIBOxr5W$U#3($6CEHwp- z^#XiZ^_rM(7eH|uVO9kR&b?oNoT`={Vvnm-_gOFNPUMPujr)9X=ERj{NPJ)>#N{#` z&Y@7F5nlw>>7(B)a_rF+ABKdO1Y|tBiaD)1A`xW?#5oF6TVx@E>Y8qrAhI&O=e;6q z0KiZZ;z-Qa4+-?p1xm26ZO?PbnOmuV+ArehU(h18SOY7I+5834lpAmw9`2@ryhdfN z<$MW-1Tz$t46-+<*4A9tc7=jj2OgCRj)ieheG)vF9a+GN$hdU*G0JxjN`kxm$*NY> za>uH!)bvbU-&3}q`}lvOI3TkSRb=|8eir$E@AOOl-#a^dYya=3c$)fun>l}K2XNEz z+5>#;0lo?kaLedgHMEow>cId<+m{XW&y9A&MWuF+Ub1lbPm2Gr}Jl!M^ey|B)p4PtFPl7sNkQ#ZZ!SB`>&>&huij(NS`r zE;xR(1cz7YXoT-T`+r641>Ut^RzAdsx}6)pZ;tJ2n2ZrKwvsAWfBwGY?w>pK?*XXr zM4s=nZO^AwT-AqnOV!VctNK#Sy3Cql0#&b3NRruGh69y>`~oE&ikR`Ba{!ygXaFvi zC@gIxS}-1HlZjRo^Vak`*+a$}$cC)G%ojLC2^=Hqlf**B;$3cdM;C9G7+Zd5xsiEs z`UQ+ibB3A+skr;mFSTC%=jUKt?s5LBBEp5N5hJiD|I_YX>HgRL-unE1lE*s#Hz^9I zC^;fw3=;&}-@fX(y%$cm4csHkr@{Rx9U?`--5)She{O-z1-^uOOJ{m$>)fvbV9EYL z;UZZi6;Pb=+`JIy;k3t@gI>$_6j@Zm-k|b~IQR1@p%B+EJ;z@o*m6~WIzmnn#SA&v_PevNI>d}Y*2`ygepejFD_T!gZ>L% zd>H|7k^FaXP|W|i)7@Xw|0j9o%YQks>yZS%4KQIWrh`sL^hPHgpLn#B7xAj8k*JUr z%SiMU7d-qo!{3qjIqwZWM0}~ItP)Mu2p@8lcYb4yF83CMyv(Pw!Hl)V3v<}QAfCWp zTVpzlG_XibiiX)Del!N)cDe5 zTcEeKCA-;d9(`e}wzFKs1*?aGP#25amqzxJ48jHRkIfqXPORatluuYnANwW6;Rtbb zIE6Uivpgr7gemhk5XL^kb07MjkPxv6N@TU`_v@;;EqjR=6A)xg=xzQ5dsXwdWS53`gS& z8gG?oejNv%?`0`sUt)uwl9Fn6THJa?{!!_|Z^a$mId>QB@IR`_w-JRSbd^OF`tXpL zLXI6rXpSjb1d@_hc>RIAyWN_1BUoKo*Z_yT2;k?ravK#Eco>o;Oe z9pgm4*l@t}I3d3yk0IZbk=~1037_0Xw=nS!FHWD&L2bzmeRAa(^0-%Rn*WAcoW^%Y3R&fLAb(4_z>k4jEC?_u$MofTrz>h z4J%Vw^DX6J{q3Z+)z%xle53Vm8x5B~Nj$7SLELJ%oPx@u`~3WIK|bLuKYvpEP()}@ zZlatXRnyiLCr<)03D+1ha7dhPXBLy-UdfCsK-IiQjq|Yh z70SKXYNPd~0v6TJWyu~|Z*J-d>K|`YJRVcu&V17NY3)T`*m31$g$`~jfBlXVi02PCtdbfmdB9XU zN(ZLZ;%7|d`Y)_CJvcgA^?;{LRi6owE4s#2X)1(?hM6o0d}=y^X?s(vqvq(L>YeawmD%QD}NuwI3K6QO-U+doQpFMOXim))29Jyk^b z4Ni_9W?U4qd;aITBbM54}fv^u2ze>sJREsxq=Z>mW(V)hyuRdc&W&FU7CZ*>$S zy>2A>F6yLF_Wj1i-CM|i+PC!9@?UpwT7Ca<(mmPxf9<4f<7Ip&l3>02JCF(2>-5`^ z4!0M{?9j}2C6p~*z18B`;RN${l)`*4=t6*8s*Z!h?f6>0bX#W#kv~Te0dPU~iq&W%d z8mnFH>(kDzSmX<-(sjkmx`j=MA|?#eAFa^Ei40@K$< zTVR2=#TLlcceDl7uGZdyzkrVZN0#mNf2QnJjpNcKxe_oV&*;)M03h>*;|6JJ3pC$fhWdgU0|GBEO zb&^Zu5iHD=O;U4EL5QwMq_6-W?x#2*<;)6OEYg(4MrbVLTM{8^N-uzRX`6?p)qUGhS_v@8!JnwV$_J(|YeU8>kh( zpgt(9IRtBOh?CJMs@!HRl=f(!XZx4(WzKDSH4~_z{u1r&t46H<)2MVCm38GmlYMQ3 z{c92jZjqYjw*Ft8ZnszS|LW}h|94X=^g(a)4{DbeN^vG}Z!Kx>PSwB4nOs9gtW57u zolK)T`%m=*+Lh@xWpaVB(xyDemBwj95~Q3=r({Ou$#_P&VCnn?jcD1P-Bdwy7foXH zGFs=SCLmQHN?34KcM(|WF1Wmx<>HPSAS`Y3)*Jn>=yBtNzSingh0;dBePOdkKr3ru zaG?eajqrq=XJL4uLedpIRKGNBQLXjrU0JahDP<`Ya|jG3vDvnuS3WI6f`$ET22Bxb z>oJQ3`DnS}^YVxF4sf-%bq=v_BF$O{d814TZu6-`P2s&NAjAIJ=TaXFcd+S^`t=F&Wsr3Mz z`94Vmx+Zg;Mx5C`ypBr23nS&R)DI{Z_<c)1Pq0-ZMRypz{o~=P^i^c_s?Q~b3oRH2)OgKMJ z*@%=+!c>UlCnBoHBf(-iL@nit^LzDU)mxy(5z^XzBEoNOT^_GW*UQhB^<2V~ z2G==Ld^_F9=EtbXpJ**|{x3)}^HQp&l~tcvUm6p7t?X_CLcbi+Ja%psgY%HOE7?!c z#hOqphbU%2#o~>$i_S8G_p(mbQ9SC`@Jkhr%5X~~S}vciU?EAX#scfdH709!aa)}f zm;8lg!M3yTazOIN>#VpSX+&cH`==E9ZhG}J-3&q?Z&SXf3zSHE0)qa`S(EPNFbu3Ah#$?{*+0B19j z;t8<^8Rpm572dTOT_0aoz; zPNx?C`?P(!&;P!YQuu%0p9pk+oy*_Ph{oawb&i%cS;Q@h)@x=7ev%)vAK;jMGs3}t zppOB_3THD+Ln-JjWAt{tJ!AQH%hvRNgCy{)=zs5|>i^#xw0nE{zl+kK|1HiWwn9U! zyo0S-fWBsmc#DMXoM)8s#W&umngq4wthrWxT(_L)7Pvu?2v?=D&C~0GLl)!|4xXee zsph9QSI?D1#Fb9;y>N5ZT$40+S)$=vKyM|DH_7&1VwIcOWQ&JC@Y2}CC zbDpJTTl&9A65y5eztgGG|I^;y{zI=aJS|Oe~Ki+n+y31nakE0g%w%+rjl|l zX5eO3ca)gZtnab}onIS~{-otL^ncwXpsVPAw^z&m);(?S>HjXuH%kA%N)pjsvwAF)$9P|(o39$x z{p(&6)S|L3Sgr+;Sr^n^D>~~sZ5KaBU(kBfD6MOBvv%0g1$}K8t#$2QZUxBgqPA{2 zz!qWE%^SNF?!ZeE)2xazsK?o1oVTWzw|`cd<(B+^tsrnK_<#Gf->&fgLA$fR|Jg}d z-~Vp~^8Oj(#ogZ1Z&xwomap+0hLl?y|EF$(QyjVKmSz3q4{P9m@1$z~@1FMe@jrG_ z>PFw`x!mVv2n^S3w+_wr-BzOccU7b}Fa%RLpI&0#knXJs>y+jA?@dV<(Milwa*Hum z;r~gu691=j+TG*-ZptA#$3m(}90`WhR->CKiP0#dVL;;vO0a*8Cxm;>A$mKd9IpH! z`Z=GHFhrA(jg)U1jVH$_B_S4cM&O~T^*fFO=McqYqF#gFPf{|bA4#Cd&;Nboq1SOZ zM=XYx5+sz66ooV@&EC&vsXVoeewDyFABCFV>$kJ+b8`h{tr&O zd;i~^ltc7*D%JatO&kZXTjBuBx9xTEF}nE}oztzx z=$yiIZ8PearRdrDSw2Q5AEUS37(F$HW*X4HpK-w=l%#BAtyo)+ba>i+)ZOSr>7m+> zx<}{@iP%h@(J4U*fILb=ya<3=BNkb@-0nu)60fcOJgf8?x z7;g@AesNw*_Ee3d4_>JgG;4eP6Zu`&9h4X5^wCx@ojy9!Bq%_({ZGa3kxOEN{hD!4 z%Hy7#ZZ+=7soD^%&xf@m%hc-}ihdc2_QUOVdpt@gDn<%aMB2>9?ycCRzO_r; z5}Xg)+hCS#rs2$YMY&~WK&`RC=qGeMro??l!Z5-y0*#Cak}9rZfzjCpi)6DSbQXr} zMluMA(s34%l==l_<`S!=)`CEw$(Ny1T87hyy=}%neb`gWpr>ooO~KHGk3$l?h6{FU ztU+h1u?8I&ORg{6j&s9>%Elm=y8Hx8RR!m=2j2=iP6wxklA_1zD(4LgwPFCg&k!;y zNhTNw?bY3CcTXRl9GNpMtD2Oou1``iA+ZXzrupDW!3GsebkP$A!7sG|^5>^d&g8QA zSe=w7m_#hTAcA|2i+-dZRgGC!A{<1TVX@U?>9q8=BDbf#_E80>XXj^tJZ1Bq3EQa` zG8bn$k5=G$ufX%p!xG4Yhg;F3!NZ3J!4ySu;mXiBUBi0jwj5M-Gn`xFKicZh4<2=n z&~vFwf&Q2R!)9ZJ&;>QgC%uYYb*%*&s8b?$9!P_$rOp&#-`@OAnXe7{gRN%L9{~E2 z0KF(*`V}M;;?~DFG#H&jaI!9_Wmb6da4S$x9uAIRta}mu7|}R*L46W)@-vy=18GSi zdaQ{Hn0|8z%51Pd*h&cK4<4Cgrzxvm5k=PxzcJ?y#{n`Xd;>?Xyb}HXRx8o(tCi6B zX+{CO1v)#!DV0c?k6UK1C!Oua?eueUE020NmzL#JGj+NHR>(k|jyfS`*!ETg_oRIy zhh=F%K;|#hnUHcJ3GEF_ua$k~Cn{bilFPA>#uEz;n7$Dze5 zB7VQSm2lASYT@8X!{Nd8H7OJ9-lMIyu=l9onZFVth(B#D=AG7Xr(4Mdz0*@e)fT!5 zlqg#`vV@V|$yW5KccMsX9)Q!=VQx7Q0&-Na*kG(73&m!(w_-&7c1KYHi|iPhWJ!JY zINKeXcJBy1S8?tvu~l1Iz$l|m%V`PWd)@6;va2b*2{8!tB@YOv6mn&^r7;RXV&X)U zMd{P>x$SHRNoR0`UP<8{halKi!f?~r2)!`IhPXjF&j^>JoE#aj%Ur#;9mVWPiYbqx zVGtpWvyY{aX=c2W(uQ>(ZN(V7j~=NFvyO(A1&VR(d36U{iK^W}+c4n5HYXW?V#oA% zf^ZN~MO`&%xyNlxIIcuZJgz4)IYtarqdZ!iJbaQqDbsr{YUEt5UzyrgB5il@FsG1$ zNoLUYx@9F19S*t=ALe$b^2VKPw{a(rBx_Y>O-miM`rU3eCl8Mbxe!G-y$;w-EYIP! z-T79;sC&{YfS$ohwHK^PZ@U3{nnf0e$)c~MOOPO36hoG_e>(!(ZNr?#7*3p}Qe2aH zO^= zs_loTxe+K=e?h`(PF+vqLiB2oTzEqK9KbSl?Xs^4?MGYDf%c;^ZR_Qh>_2n_>i}$p*lmf} z7dKS+QzgO!MY(>+y@{WClp#EpYx*w?!9bs|2-6t-7}6x4Ten2G+oxN}J?+zzBXlv3 z{StvOF-SGmb8F1fdiJ&=F74hUZEV$+X1IbZ=M^iby$it>9S5b1WfmOU83o$LDDXdL zFJDMTD{>{SD;3~gX{uXs^>**!5&9vE61}qex)YtBy?y-58me0ib@J$B8^uO8D-D%k zd9?I^)=(XLsI9J>PT;z!E&~BAkoeIzCfsgrN6-U4-0Ch#Hp>}9<9G!=o(xX6TE4+) z=Lo&T*Q8LPl+qNjlpv)Os35_yKw^sHs&#-k|cK6{{<__6R zp{LZ-B;pB%_?-J9YKxSi1{Lt;V#SxxGgDd8j*B#>ed7b&Rm_W_ur0!({_ zPPa0U$Yw|A9Vcjus_gp|DV^tyn6cP;9vcq8oJ!DTjcOpT}I_FjSUe$9eGJd6ckJ;8+YFJU}cC z=OBXjP~k65f;xm$cXRpX>5Hdl7f(ODI(zx_J*t@VOCSLRZ>1QQoJ%rrkrK0 zbu(L{-Dr#nKUXgy*oNbLkPPv%^y~Zkho!%d(1?WW#`|Du+jFUHaZ9pP_yhj)LDy>? z>3eNG3+-{M!1w@*{4S>DWv(t?7B?@TStKftyYh#Wm8uJ3UdC~RIcGk_!nosWF<0L} z2PA|BqVU!AN>k6_SULH;<~q|)CMgD~d(rfv0;!&!%g4{o-n@N}Tvv~!mcxcuHCzp? zs<#?e8`xju0{*`wW#|dJi3L^eS?6#gtqfZ~%=6PHm$ZCvE!sK7g}E5)dO8p?2S+SP zI1v^1I5^@+@ki1&fhDFPmZFp>%pie>p2tXC^9zQ?^rKWw#WCVq^dldA5{8nzjiefr zFfPz&j$Y6>;CPN+;5bOVLJ!S#%aX_N^&?bXJ^a_BN%d=$a#ezp9pjia$#Lv5Q ziCh8#F5nh*(vlyJ1NV25GB?nDyEJx_yl=PbKkBso0X`wYL*o$0fd zP&+0!h{eP}TmuE}Zu32Wb7jo&sI*zfk_)378iwcwj3Z3F`RjP-^7$eNFye5X*sw;X z@f#A*l=wn1`6sU~kWeOZwNqxs7FG$C&#d0v3pOctFk)g_+L}Q5yb3Kk#1kUbntU$r z)>(fG8W#H`A5kK_GE@Yq)P({C0~MzGAHjK0U>btggCy!~EW70daX}IeIDNT3F_KW4 z%C02+%TVmdab7V&)Gmu0YdDJt3g~!DQb|LU&k_*Mpv+x48WC|rh+@DJ*_76Ci~_>_ zl#WOs>CPJ(VUAuhpUkLFGp-ilfBl%UiDBLc#S8_j5YT72>F+=uA2^q6j4ms*pr}^4 zv8Z-f5UrF(8UNjjiD*TpGgmOTECqY$OtH!}XaSrA8fVH9rDUu+rYM6F^0O2c*eta~ zztJ3}Sscq*Db}_^aM3&rPLDd;umei!d~DZs9N?FawFiI^W0h;b4`j?zVx++QT~3Xa zf+toBgy0XrRuBE6KUYkN!VGr={TY5uwR}Ijn>-D?bC}XaerWsj@(`m>fG0hm#6)!?G|y5x$1MXr!LVgy0)N zX4uaZw~JXoJTMJiPNvE#iVjaZ-R4_N*jZyhP`)>^u9BL-GEU<$(=7SCxEp?HUUo=v zUNJ*j*C2fy!yS4m?6&$k{a9jvb_7FL)_HO5Monv*OP$ zSGIadO;_Vs?YrNFtOVD9DIe@i7ZSb|dFzo*U9wKY^CkRBEunqS7U}(t0g-^0p`;Y!)btSuu->y z9I9^5+wk&iF;v}NTSH}S7s3MKE1}NA_QSSWSS(etkBBq>nh115Mru11{lIx*4lIi2 zaGryZ!3%jluly_xk?YEiT-{MiM2Qx96$q~C*OT9owuIz8s+#(2T}l0z2I}0~*llvg z!c`2UxkXfiB{7DjIkxbaCJ7OWeP7nx4Ae^9aQsQbvlpx5ybJUR7WF8Po!Bb;u zm5r(wmgv%|x&la-#(p-E)SzYjJlr7WM-{V{1jv3|V2G6BR^X;@V+tz1L!=xl;+_tx zWe?`zFqz^?EFHP4D%ghwtiJx2bMOjwclOH#dU1-LzW6~g>WhmP=xvdR)N!=CXYD{p zop;J^Vk7ERHaa|2ch=kQv(k!N_=dN_)0SQnH#}AHz74^F=w^-RH6{L--C zhG&e3s4g|W;cm!q;2KM)%XtX*xkH50c!+QK5c|>a-~V?1{cri7;lE88%35?xeJn`J z{OC6PN0n`0$=mx>>!I^kRO;Jq!76uDwmSxx=@|Je zO^GkUd2#B3Lhb|MT)%Hsqv}psOpL()?#&CRFV*jmF>p4>5>OVx8Of#B=C!-WWqTzC z-wVUsFM|`I*1s%}YBc~aJ% zG`t`q+_JKVEX!ZMOYrZdTFdYp$IMFVQD?tgtcU7m+?=bci4KvRsmc0Q$99Ac(PQ;Y zHDCFsFMi0sZbXf97|?+n^}rhAES@7Krtpq0r)l!s!+!c9PDy~KBqdsyu-D+H$4_^@ z1}}c_&`YDkrjdzJY=}MVpEqG_q9~#*#wl~QzEog0u3#=4mDCha}(?Z|s zc%4&qF)DTXBH1euH!PKy?@g|W+=lWNsGz_Hh87=Gh9Ec%DM|BTtk1P6t=aKbn~dta zeD-^mt|`QBu#0OQWI6I~j`Hgi`yoM(0sWuH;}mm|X1@4ZQ)fQ?<0gKlR7xPCa`He@ zxldx8QYPvC6wg#337&*hxHC#_z?QFsg&Qz6LqwNs6jC4kF4@N8S7$GuqLlb74fp~+ zoO2DyFmI+*t8qz6XL5~J^^>om-H)fDpxxlP;@O~60}llTt!0E_b+x$QInL`u2}t2Q zXZDikHZhNKr06SSID%m?=>OD5&(HO0!p)zQYvKvIvSn9e^fC7*XZgdMUzCR^cO1AP zC@ER4rH&b2MmYKB7^fGu5nt(NRY zSN4tdm|y`SIzvfHW^iMPbN2>)VME{~!6Q|@jr!)_a~?cUA+B^cMeqtVDz0QXjeUaJ z5c$l9bJ8pk7PNi@^SB0gutu?N<8Ai0CL}@r0y^`F@saw7Rl- zJ2<#1N%JX*1Co-!`-c6c|7vBq|EEt|`73Z&-P|eS>{?f7E5}&n|J|+Uf9ds3_Wqx{ zC}jatMR?9bB{`^gMQY>)C28jRl*wFMwvK{^kP|t_LE6xvK4+Y`;O=Rg50!}dwU^^R zu566|F$+k-Qn3LASo!{AFsS1HY4>!G|GOx6ga6G0Ab;Kt<_i5suBr+@DYVu{?RI;J zdhPb3w*D;(%AY&xbH{OXL)GL!OYJQ|xEMZcKWra3j&7@3HP!gqtN!hj4e)u63 z{lV!T|94XE3jek5dF89-?JIeuT)m!KfxORO=7w`=l`94{r+#TxBu^?>;(V6_9XXZVc7ux zwe{r>kpJ7=8vdW|_5YoeuW$eR+AHv%T{gh~g#S_Uf2XSdcl*6P|KCa3jr^am;6f*F zQ<+L+y)u>f+6(a?S~kFclT!f4!Dh5zHU8K9e+J#X|KCo^ZSj8{4!=kLZ?|lL|9^n~ zU-SRzp6=~`J1M)s|F1pAeOX-Az<(3_-nEh#-|73m&Y+6_{octw|HDp78UL*vZpQPy zIq)x5Ho^aG9{{h!|I-@&pY-?mzmrnN|21Ff?&-i^rfiP?n?D0sh5x6f9(5bDx2W{whjO*`F}0`N3XxX|KCkHL@&r#pizk9YY3M`xX2@6>HPT1 z;vnx?`k%XOg8$nF09b|p-IFT+@AXgi_`j2~9sl3I_W!GtP4Iu)001lH|4uFbPq)AK z|Jg~oGx>l2+W${kHo^aG9ROF#|AXrNfA6Hd=l?q?+u{G-+wZSZHo^aG1Awf=|8C9y zcQDx7|8`RD-v0NsXS6Syluht|+W;V|@W0=#+5g+8d;H%?*){*)NBj7;%0~FVuZoGfNvI+ig8vtmf{jc`^_q5a7`~U8w{2}(geYBrHP1y$j z@BIF+TfhJB?B)NRls^Ff_Za&pDx2c}A0q$P;(v7q-M#&PCuI})e;>u?uU9s~|7{-t zuf+da{-2ZfUjN@s*^K}1U;F;m$~O3a=lB2h`@jBRkN-OmmP36ByR6Y%;j#Sb$q(*~@(hPA4M;4gn72YUVfjaB=kj?pVxh-p z6X%-DZ&(`eq2nUmspIDBXH#7{Vl1R^+>K9)f^@D+ivn=%q4BL>+TJ>bs!wr~!O;Tr8?0(Pd?}_EsaNE1R+-E*vV zL)3Ge!$b6ZjBpsTo3kLI@i|V#T=uBNN9aX2QhDFy(hHO&zyVk$ART>9K@Sf>9*rp% zDHbe6*!Ni$i@zi45${-zgfdQ2t~whLG)ZwRNMJXkGu4sr6V5&KT1-iLLpeEC>GkJa zkVv%;8M_AVg3$%>QzDR$!;l2B`9I!}kYG+;;fVax)i{Er?2KtFdEgZc`HquRlHjXj z{lhutTr!C($-`tvS3YCcl$>Q^ss_12*JRE;bU_F}bmgZ63jzyhYK%iX@nq|ij0;Xz zc8(92FOfI)Ndj0LkLe^!!N;b>p`Vsh*_u3guiCiU!1mTv)?~qg$aR#xjfn0ou-6D@ zg1HkClT>|42{2k5au2;z`#5DNNy(U`Qas>9$j-!+aH5|g%0D7b!WrRcif062$?TF4 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 diff --git a/docs/index.yaml b/docs/index.yaml index 668033bc4..4e064f3dd 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 5532cf87a..552b8d1b0 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 041/110] 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 8343e6283..eb9eeebb3 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 f24e9482c..d016c1052 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 From 27d9aded234c5467d4ce7ceb11070dae44cff02a Mon Sep 17 00:00:00 2001 From: Martin McKeaveney Date: Thu, 19 Aug 2021 07:57:30 +0100 Subject: [PATCH 042/110] Update README.md --- hosting/kubernetes/budibase/README.md | 147 +------------------------- 1 file changed, 2 insertions(+), 145 deletions(-) diff --git a/hosting/kubernetes/budibase/README.md b/hosting/kubernetes/budibase/README.md index 9de545d36..efa78ba75 100644 --- a/hosting/kubernetes/budibase/README.md +++ b/hosting/kubernetes/budibase/README.md @@ -1,6 +1,6 @@ # Budibase -[Budibase](https://posthog.com/) Budibase is an open source low-code platform, helping thousands of teams build apps for their workplace in minutes. +[Budibase](https://budibase.com/) Budibase is an open source low-code platform, helping thousands of teams build apps for their workplace in minutes. ## TL;DR; ```console @@ -12,8 +12,6 @@ $ helm install budibase . 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 @@ -28,7 +26,7 @@ To install the chart with the release name `budi-release`: $ 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. +The command deploys Budibase 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` @@ -39,144 +37,3 @@ 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. - From 7bc05aa4e48d8e77ba2a416e4cbd94717968efb3 Mon Sep 17 00:00:00 2001 From: Budibase Staging Release Bot <> Date: Thu, 19 Aug 2021 07:08:13 +0000 Subject: [PATCH 043/110] v0.9.105-alpha.24 --- lerna.json | 2 +- packages/auth/package.json | 2 +- packages/bbui/package.json | 2 +- packages/builder/package.json | 8 ++++---- packages/cli/package.json | 2 +- packages/client/package.json | 8 ++++---- packages/server/package.json | 10 +++++----- packages/standard-components/package.json | 4 ++-- packages/string-templates/package.json | 2 +- packages/worker/package.json | 6 +++--- 10 files changed, 23 insertions(+), 23 deletions(-) diff --git a/lerna.json b/lerna.json index e32eb7d05..9fd593bfd 100644 --- a/lerna.json +++ b/lerna.json @@ -1,5 +1,5 @@ { - "version": "0.9.105-alpha.23", + "version": "0.9.105-alpha.24", "npmClient": "yarn", "packages": [ "packages/*" diff --git a/packages/auth/package.json b/packages/auth/package.json index fd7cd230f..eeec47d32 100644 --- a/packages/auth/package.json +++ b/packages/auth/package.json @@ -1,6 +1,6 @@ { "name": "@budibase/auth", - "version": "0.9.105-alpha.23", + "version": "0.9.105-alpha.24", "description": "Authentication middlewares for budibase builder and apps", "main": "src/index.js", "author": "Budibase", diff --git a/packages/bbui/package.json b/packages/bbui/package.json index 7729e5956..49ddadac5 100644 --- a/packages/bbui/package.json +++ b/packages/bbui/package.json @@ -1,7 +1,7 @@ { "name": "@budibase/bbui", "description": "A UI solution used in the different Budibase projects.", - "version": "0.9.105-alpha.23", + "version": "0.9.105-alpha.24", "license": "AGPL-3.0", "svelte": "src/index.js", "module": "dist/bbui.es.js", diff --git a/packages/builder/package.json b/packages/builder/package.json index 0c5884789..2ddd70228 100644 --- a/packages/builder/package.json +++ b/packages/builder/package.json @@ -1,6 +1,6 @@ { "name": "@budibase/builder", - "version": "0.9.105-alpha.23", + "version": "0.9.105-alpha.24", "license": "AGPL-3.0", "private": true, "scripts": { @@ -65,10 +65,10 @@ } }, "dependencies": { - "@budibase/bbui": "^0.9.105-alpha.23", - "@budibase/client": "^0.9.105-alpha.23", + "@budibase/bbui": "^0.9.105-alpha.24", + "@budibase/client": "^0.9.105-alpha.24", "@budibase/colorpicker": "1.1.2", - "@budibase/string-templates": "^0.9.105-alpha.23", + "@budibase/string-templates": "^0.9.105-alpha.24", "@sentry/browser": "5.19.1", "@spectrum-css/page": "^3.0.1", "@spectrum-css/vars": "^3.0.1", diff --git a/packages/cli/package.json b/packages/cli/package.json index bccb336af..6b74bdd98 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -1,6 +1,6 @@ { "name": "@budibase/cli", - "version": "0.9.105-alpha.23", + "version": "0.9.105-alpha.24", "description": "Budibase CLI, for developers, self hosting and migrations.", "main": "src/index.js", "bin": { diff --git a/packages/client/package.json b/packages/client/package.json index 2f3c0ee13..ec61300fd 100644 --- a/packages/client/package.json +++ b/packages/client/package.json @@ -1,6 +1,6 @@ { "name": "@budibase/client", - "version": "0.9.105-alpha.23", + "version": "0.9.105-alpha.24", "license": "MPL-2.0", "module": "dist/budibase-client.js", "main": "dist/budibase-client.js", @@ -18,9 +18,9 @@ "dev:builder": "rollup -cw" }, "dependencies": { - "@budibase/bbui": "^0.9.105-alpha.23", - "@budibase/standard-components": "^0.9.105-alpha.23", - "@budibase/string-templates": "^0.9.105-alpha.23", + "@budibase/bbui": "^0.9.105-alpha.24", + "@budibase/standard-components": "^0.9.105-alpha.24", + "@budibase/string-templates": "^0.9.105-alpha.24", "regexparam": "^1.3.0", "shortid": "^2.2.15", "svelte-spa-router": "^3.0.5" diff --git a/packages/server/package.json b/packages/server/package.json index 3e4bea404..3e86c2588 100644 --- a/packages/server/package.json +++ b/packages/server/package.json @@ -1,7 +1,7 @@ { "name": "@budibase/server", "email": "hi@budibase.com", - "version": "0.9.105-alpha.23", + "version": "0.9.105-alpha.24", "description": "Budibase Web Server", "main": "src/index.js", "repository": { @@ -62,9 +62,9 @@ "author": "Budibase", "license": "AGPL-3.0-or-later", "dependencies": { - "@budibase/auth": "^0.9.105-alpha.23", - "@budibase/client": "^0.9.105-alpha.23", - "@budibase/string-templates": "^0.9.105-alpha.23", + "@budibase/auth": "^0.9.105-alpha.24", + "@budibase/client": "^0.9.105-alpha.24", + "@budibase/string-templates": "^0.9.105-alpha.24", "@elastic/elasticsearch": "7.10.0", "@koa/router": "8.0.0", "@sendgrid/mail": "7.1.1", @@ -117,7 +117,7 @@ "devDependencies": { "@babel/core": "^7.14.3", "@babel/preset-env": "^7.14.4", - "@budibase/standard-components": "^0.9.105-alpha.23", + "@budibase/standard-components": "^0.9.105-alpha.24", "@jest/test-sequencer": "^24.8.0", "@types/bull": "^3.15.1", "@types/jest": "^26.0.23", diff --git a/packages/standard-components/package.json b/packages/standard-components/package.json index 92811534c..30c5e662c 100644 --- a/packages/standard-components/package.json +++ b/packages/standard-components/package.json @@ -29,11 +29,11 @@ "keywords": [ "svelte" ], - "version": "0.9.105-alpha.23", + "version": "0.9.105-alpha.24", "license": "MIT", "gitHead": "d1836a898cab3f8ab80ee6d8f42be1a9eed7dcdc", "dependencies": { - "@budibase/bbui": "^0.9.105-alpha.23", + "@budibase/bbui": "^0.9.105-alpha.24", "@spectrum-css/button": "^3.0.3", "@spectrum-css/card": "^3.0.3", "@spectrum-css/divider": "^1.0.3", diff --git a/packages/string-templates/package.json b/packages/string-templates/package.json index 8337235e0..c8ceb8013 100644 --- a/packages/string-templates/package.json +++ b/packages/string-templates/package.json @@ -1,6 +1,6 @@ { "name": "@budibase/string-templates", - "version": "0.9.105-alpha.23", + "version": "0.9.105-alpha.24", "description": "Handlebars wrapper for Budibase templating.", "main": "src/index.cjs", "module": "dist/bundle.mjs", diff --git a/packages/worker/package.json b/packages/worker/package.json index dd3670433..718cee165 100644 --- a/packages/worker/package.json +++ b/packages/worker/package.json @@ -1,7 +1,7 @@ { "name": "@budibase/worker", "email": "hi@budibase.com", - "version": "0.9.105-alpha.23", + "version": "0.9.105-alpha.24", "description": "Budibase background service", "main": "src/index.js", "repository": { @@ -23,8 +23,8 @@ "author": "Budibase", "license": "AGPL-3.0-or-later", "dependencies": { - "@budibase/auth": "^0.9.105-alpha.23", - "@budibase/string-templates": "^0.9.105-alpha.23", + "@budibase/auth": "^0.9.105-alpha.24", + "@budibase/string-templates": "^0.9.105-alpha.24", "@koa/router": "^8.0.0", "@techpass/passport-openidconnect": "^0.3.0", "aws-sdk": "^2.811.0", From 2f05450d793ad448f35a7528e33b7d831c5d135f Mon Sep 17 00:00:00 2001 From: Andrew Kingston Date: Thu, 19 Aug 2021 12:52:13 +0100 Subject: [PATCH 044/110] Ensure client preview indicator doesn't crash when encountering incorrect node types --- packages/client/src/components/preview/IndicatorSet.svelte | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/client/src/components/preview/IndicatorSet.svelte b/packages/client/src/components/preview/IndicatorSet.svelte index 74c9f06f0..3151a8bd7 100644 --- a/packages/client/src/components/preview/IndicatorSet.svelte +++ b/packages/client/src/components/preview/IndicatorSet.svelte @@ -61,7 +61,7 @@ // Sanity limit of 100 active indicators const children = Array.from(parents) .map(parent => parent?.childNodes?.[0]) - .filter(child => child != null) + .filter(node => node?.nodeType === 1) .slice(0, 100) // If there aren't any nodes then reset From 9ea255b7bc1f66ad7c329d33983cb5d0f9478284 Mon Sep 17 00:00:00 2001 From: Andrew Kingston Date: Thu, 19 Aug 2021 12:52:50 +0100 Subject: [PATCH 045/110] Update validate form action to be able to only validate the current form step --- .../EventsEditor/actions/ValidateForm.svelte | 7 ++++++- packages/client/src/utils/buttonActions.js | 12 +++++++++--- 2 files changed, 15 insertions(+), 4 deletions(-) diff --git a/packages/builder/src/components/design/PropertiesPanel/PropertyControls/EventsEditor/actions/ValidateForm.svelte b/packages/builder/src/components/design/PropertiesPanel/PropertyControls/EventsEditor/actions/ValidateForm.svelte index 462597f26..d61c9567d 100644 --- a/packages/builder/src/components/design/PropertiesPanel/PropertyControls/EventsEditor/actions/ValidateForm.svelte +++ b/packages/builder/src/components/design/PropertiesPanel/PropertyControls/EventsEditor/actions/ValidateForm.svelte @@ -1,5 +1,5 @@
diff --git a/packages/standard-components/src/forms/FieldGroupFallback.svelte b/packages/standard-components/src/forms/FieldGroupFallback.svelte index 782552a54..fd0346a9e 100644 --- a/packages/standard-components/src/forms/FieldGroupFallback.svelte +++ b/packages/standard-components/src/forms/FieldGroupFallback.svelte @@ -1,7 +1,7 @@ {#if fieldGroupContext} diff --git a/packages/standard-components/src/forms/Form.svelte b/packages/standard-components/src/forms/Form.svelte index 2514232eb..a06543203 100644 --- a/packages/standard-components/src/forms/Form.svelte +++ b/packages/standard-components/src/forms/Form.svelte @@ -1,5 +1,5 @@ -{#key resetKey} - - - -{/key} +{#if loaded} + {#key resetKey} + + + + {/key} +{/if} diff --git a/packages/standard-components/src/forms/FormStep.svelte b/packages/standard-components/src/forms/FormStep.svelte index 45db9d1f5..e048a600f 100644 --- a/packages/standard-components/src/forms/FormStep.svelte +++ b/packages/standard-components/src/forms/FormStep.svelte @@ -1,5 +1,5 @@ {#if !formContext} -{:else if step === $formState.step} +{:else if step === currentStep}
-
- Step {step} is visible! -
-{:else} -
hiding step {step}!
{/if} diff --git a/packages/standard-components/src/forms/InnerForm.svelte b/packages/standard-components/src/forms/InnerForm.svelte index 9355c6766..32cae9f8b 100644 --- a/packages/standard-components/src/forms/InnerForm.svelte +++ b/packages/standard-components/src/forms/InnerForm.svelte @@ -1,41 +1,84 @@
- {#if loaded} - - {/if} +
diff --git a/packages/standard-components/src/forms/LongFormField.svelte b/packages/standard-components/src/forms/LongFormField.svelte index f71090425..81ad42bbc 100644 --- a/packages/standard-components/src/forms/LongFormField.svelte +++ b/packages/standard-components/src/forms/LongFormField.svelte @@ -25,11 +25,11 @@ > {#if fieldState} fieldApi.setValue(e.detail)} - disabled={$fieldState.disabled} - error={$fieldState.error} - id={$fieldState.fieldId} + disabled={fieldState.disabled} + error={fieldState.error} + id={fieldState.fieldId} {placeholder} /> {/if} diff --git a/packages/standard-components/src/forms/OptionsField.svelte b/packages/standard-components/src/forms/OptionsField.svelte index b43ddb9f3..9f3e4a455 100644 --- a/packages/standard-components/src/forms/OptionsField.svelte +++ b/packages/standard-components/src/forms/OptionsField.svelte @@ -77,10 +77,10 @@ {#if fieldState} {#if !optionsType || optionsType === "select"} fieldApi.setValue(e.detail)} @@ -90,10 +90,10 @@ /> {:else if optionsType === "radio"} fieldApi.setValue(e.detail)} getOptionLabel={flatOptions ? x => x : x => x.label} diff --git a/packages/standard-components/src/forms/RelationshipField.svelte b/packages/standard-components/src/forms/RelationshipField.svelte index b8600513a..43f4e64ae 100644 --- a/packages/standard-components/src/forms/RelationshipField.svelte +++ b/packages/standard-components/src/forms/RelationshipField.svelte @@ -23,8 +23,8 @@ $: linkedTableId = fieldSchema?.tableId $: fetchRows(linkedTableId) $: fetchTable(linkedTableId) - $: singleValue = flatten($fieldState?.value)?.[0] - $: multiValue = flatten($fieldState?.value) ?? [] + $: singleValue = flatten(fieldState?.value)?.[0] + $: multiValue = flatten(fieldState?.value) ?? [] $: component = multiselect ? CoreMultiselect : CoreSelect const fetchTable = async id => { @@ -81,9 +81,9 @@ {autocomplete} value={multiselect ? multiValue : singleValue} on:change={multiselect ? multiHandler : singleHandler} - id={$fieldState.fieldId} - disabled={$fieldState.disabled} - error={$fieldState.error} + id={fieldState.fieldId} + disabled={fieldState.disabled} + error={fieldState.error} getOptionLabel={getDisplayName} getOptionValue={option => option._id} {placeholder} diff --git a/packages/standard-components/src/forms/StringField.svelte b/packages/standard-components/src/forms/StringField.svelte index f04177ec0..7bc4104cc 100644 --- a/packages/standard-components/src/forms/StringField.svelte +++ b/packages/standard-components/src/forms/StringField.svelte @@ -26,12 +26,12 @@ > {#if fieldState} fieldApi.setValue(e.detail)} - disabled={$fieldState.disabled} - error={$fieldState.error} - id={$fieldState.fieldId} + disabled={fieldState.disabled} + error={fieldState.error} + id={fieldState.fieldId} {placeholder} {type} /> From c3cf48c26815b8b79d130a7a177e4da74c00d9a5 Mon Sep 17 00:00:00 2001 From: Andrew Kingston Date: Thu, 19 Aug 2021 13:41:18 +0100 Subject: [PATCH 047/110] Clear form field error when re-registering the same field --- packages/standard-components/src/forms/InnerForm.svelte | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/packages/standard-components/src/forms/InnerForm.svelte b/packages/standard-components/src/forms/InnerForm.svelte index 32cae9f8b..ec5c06669 100644 --- a/packages/standard-components/src/forms/InnerForm.svelte +++ b/packages/standard-components/src/forms/InnerForm.svelte @@ -73,9 +73,14 @@ return } - // Skip if we've already registered this field + // If we've already registered this field then wipe any errors and + // return the existing field const existingField = getField(field) if (existingField) { + existingField.update(state => { + state.fieldState.error = null + return state + }) return existingField } From 7d4a656ce277c5f03bf9342c5ffb2030b5f5b96e Mon Sep 17 00:00:00 2001 From: Andrew Kingston Date: Thu, 19 Aug 2021 14:02:03 +0100 Subject: [PATCH 048/110] Fix form validation exiting early --- .../src/forms/InnerForm.svelte | 23 ++++++++----------- 1 file changed, 10 insertions(+), 13 deletions(-) diff --git a/packages/standard-components/src/forms/InnerForm.svelte b/packages/standard-components/src/forms/InnerForm.svelte index ec5c06669..c8b76a720 100644 --- a/packages/standard-components/src/forms/InnerForm.svelte +++ b/packages/standard-components/src/forms/InnerForm.svelte @@ -118,24 +118,21 @@ return fieldInfo }, validate: (onlyCurrentStep = false) => { - // Validate only the current step if required + let valid = true + let validationFields = fields + + // Reduce fields to only the current step if required if (onlyCurrentStep) { - const stepFields = fields.filter(f => get(f).step === get(currentStep)) - for (let field of stepFields) { - if (!get(field).fieldApi.validate()) { - return false - } - } - return true + validationFields = fields.filter(f => get(f).step === get(currentStep)) } - // Otherwise validate all fields - for (let field of fields) { + // Validate fields and check if any are invalid + validationFields.forEach(field => { if (!get(field).fieldApi.validate()) { - return false + valid = false } - } - return true + }) + return valid }, clear: () => { // Clear the form by clearing each individual field From 1ed4e75d524967997c42432805a36d7aa1db0d8a Mon Sep 17 00:00:00 2001 From: Andrew Kingston Date: Thu, 19 Aug 2021 14:02:33 +0100 Subject: [PATCH 049/110] Disable updating form values on change due to performance issues of enrichment --- packages/standard-components/src/forms/StringField.svelte | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/standard-components/src/forms/StringField.svelte b/packages/standard-components/src/forms/StringField.svelte index 7bc4104cc..4764cba4d 100644 --- a/packages/standard-components/src/forms/StringField.svelte +++ b/packages/standard-components/src/forms/StringField.svelte @@ -26,7 +26,7 @@ > {#if fieldState} fieldApi.setValue(e.detail)} disabled={fieldState.disabled} From 7605999a595397e2898e40b1dfaef7d015bfc237 Mon Sep 17 00:00:00 2001 From: Andrew Kingston Date: Thu, 19 Aug 2021 14:53:13 +0100 Subject: [PATCH 050/110] Ensure that the correct form step is always visible in builder preview --- packages/client/src/store/builder.js | 34 +++++++++++++++++++ .../src/forms/FormStep.svelte | 15 +++++++- .../src/forms/InnerForm.svelte | 5 +++ 3 files changed, 53 insertions(+), 1 deletion(-) diff --git a/packages/client/src/store/builder.js b/packages/client/src/store/builder.js index 401ccfcc7..65e4f70e6 100644 --- a/packages/client/src/store/builder.js +++ b/packages/client/src/store/builder.js @@ -28,6 +28,26 @@ const findComponentById = (component, componentId) => { return null } +const findComponentIdPath = (component, componentId, path = []) => { + if (!component || !componentId) { + return null + } + path = [...path, component._id] + if (component._id === componentId) { + return path + } + if (!component._children?.length) { + return null + } + for (let child of component._children) { + const result = findComponentIdPath(child, componentId, path) + if (result) { + return result + } + } + return null +} + const createBuilderStore = () => { const initialState = { inBuilder: false, @@ -37,6 +57,7 @@ const createBuilderStore = () => { selectedComponentId: null, previewId: null, previewType: null, + selectedPath: [], } const writableStore = writable(initialState) const derivedStore = derived(writableStore, $state => { @@ -47,10 +68,15 @@ const createBuilderStore = () => { const prefix = "@budibase/standard-components/" const type = component?._component?.replace(prefix, "") const definition = type ? Manifest[type] : null + + // Derive the selected component path + const path = findComponentIdPath(asset.props, selectedComponentId) || [] + return { ...$state, selectedComponent: component, selectedComponentDefinition: definition, + selectedComponentPath: path, } }) @@ -67,6 +93,14 @@ const createBuilderStore = () => { notifyLoaded: () => { dispatchEvent("preview-loaded") }, + setSelectedPath: path => { + console.log("set to ") + console.log(path) + writableStore.update(state => { + state.selectedPath = path + return state + }) + }, } return { ...writableStore, diff --git a/packages/standard-components/src/forms/FormStep.svelte b/packages/standard-components/src/forms/FormStep.svelte index e048a600f..4806cfc86 100644 --- a/packages/standard-components/src/forms/FormStep.svelte +++ b/packages/standard-components/src/forms/FormStep.svelte @@ -4,7 +4,7 @@ export let step - const { styleable } = getContext("sdk") + const { styleable, builderStore } = getContext("sdk") const component = getContext("component") const formContext = getContext("form") @@ -13,6 +13,19 @@ $: formState = formContext?.formState $: currentStep = $formState?.currentStep + + // If in the builder preview, show this step if it is selected + $: { + if (step && formContext && $builderStore.inBuilder) { + console.log($builderStore.selectedPath) + console.log($component.id) + + if ($builderStore.selectedComponentPath?.includes($component.id)) { + console.log("selecting " + step) + formContext.formApi.setStep(step) + } + } + } {#if !formContext} diff --git a/packages/standard-components/src/forms/InnerForm.svelte b/packages/standard-components/src/forms/InnerForm.svelte index c8b76a720..774419bbd 100644 --- a/packages/standard-components/src/forms/InnerForm.svelte +++ b/packages/standard-components/src/forms/InnerForm.svelte @@ -146,6 +146,11 @@ prevStep: () => { currentStep.update(step => Math.max(1, step - 1)) }, + setStep: step => { + if (step) { + currentStep.set(step) + } + }, } // Creates an API for a specific field From 11c7f15089765469f27b12ea2306508c2867e6d8 Mon Sep 17 00:00:00 2001 From: Andrew Kingston Date: Thu, 19 Aug 2021 14:56:56 +0100 Subject: [PATCH 051/110] Only show the selected step if not already selected --- .../src/forms/FormStep.svelte | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/packages/standard-components/src/forms/FormStep.svelte b/packages/standard-components/src/forms/FormStep.svelte index 4806cfc86..34894aa6e 100644 --- a/packages/standard-components/src/forms/FormStep.svelte +++ b/packages/standard-components/src/forms/FormStep.svelte @@ -2,7 +2,7 @@ import { getContext, setContext } from "svelte" import Placeholder from "../Placeholder.svelte" - export let step + export let step = 1 const { styleable, builderStore } = getContext("sdk") const component = getContext("component") @@ -14,16 +14,15 @@ $: formState = formContext?.formState $: currentStep = $formState?.currentStep - // If in the builder preview, show this step if it is selected + // If in the builder preview, show this step if a child is selected $: { - if (step && formContext && $builderStore.inBuilder) { - console.log($builderStore.selectedPath) - console.log($component.id) - - if ($builderStore.selectedComponentPath?.includes($component.id)) { - console.log("selecting " + step) - formContext.formApi.setStep(step) - } + if ( + formContext && + $builderStore.inBuilder && + $builderStore.selectedComponentPath?.includes($component.id) && + $formState?.currentStep !== step + ) { + formContext.formApi.setStep(step) } } From 83c236ee0adca0cce05ed4defe05265fd4a8144f Mon Sep 17 00:00:00 2001 From: Maurits Lourens Date: Thu, 19 Aug 2021 16:55:54 +0200 Subject: [PATCH 052/110] add precommit hook and test error --- package.json | 6 ++++++ packages/builder/src/components/start/UpdateAppModal.svelte | 2 +- yarn.lock | 5 +++++ 3 files changed, 12 insertions(+), 1 deletion(-) diff --git a/package.json b/package.json index 5532cf87a..ca4d109a0 100644 --- a/package.json +++ b/package.json @@ -7,6 +7,7 @@ "eslint": "^7.28.0", "eslint-plugin-cypress": "^2.11.3", "eslint-plugin-svelte3": "^3.2.0", + "husky": "^7.0.1", "kill-port": "^1.6.1", "lerna": "3.14.1", "prettier": "^2.3.1", @@ -46,5 +47,10 @@ "build:docker:develop": "node scripts/pinVersions && lerna run build:docker && cd hosting/scripts/linux/ && ./release-to-docker-hub.sh develop && cd -", "multi:enable": "lerna run multi:enable", "multi:disable": "lerna run multi:disable" + }, + "husky": { + "hooks": { + "pre-commit": "yarn run lint:fix" + } } } diff --git a/packages/builder/src/components/start/UpdateAppModal.svelte b/packages/builder/src/components/start/UpdateAppModal.svelte index dbbc000d1..28af6d71f 100644 --- a/packages/builder/src/components/start/UpdateAppModal.svelte +++ b/packages/builder/src/components/start/UpdateAppModal.svelte @@ -74,7 +74,7 @@ } export const show = () => { - modal.show() + modal.show(); } export const hide = () => { modal.hide() diff --git a/yarn.lock b/yarn.lock index 553702ffc..00422fa63 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2448,6 +2448,11 @@ humanize-ms@^1.2.1: dependencies: ms "^2.0.0" +husky@^7.0.1: + version "7.0.1" + resolved "https://registry.yarnpkg.com/husky/-/husky-7.0.1.tgz#579f4180b5da4520263e8713cc832942b48e1f1c" + integrity sha512-gceRaITVZ+cJH9sNHqx5tFwbzlLCVxtVZcusME8JYQ8Edy5mpGDOqD8QBCdMhpyo9a+JXddnujQ4rpY2Ff9SJA== + iconv-lite@^0.4.24, iconv-lite@~0.4.13: version "0.4.24" resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b" From 121aee21dd693fa5d6307c0f434ee7514873d7ba Mon Sep 17 00:00:00 2001 From: Maurits Lourens Date: Thu, 19 Aug 2021 16:57:23 +0200 Subject: [PATCH 053/110] add prepare script for husky --- package.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/package.json b/package.json index ca4d109a0..86e042e13 100644 --- a/package.json +++ b/package.json @@ -46,7 +46,8 @@ "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 -", "multi:enable": "lerna run multi:enable", - "multi:disable": "lerna run multi:disable" + "multi:disable": "lerna run multi:disable", + "prepare": "husky install" }, "husky": { "hooks": { From 72c1741831cabc4f72506c369293f56b2955204e Mon Sep 17 00:00:00 2001 From: Maurits Lourens Date: Thu, 19 Aug 2021 17:01:48 +0200 Subject: [PATCH 054/110] add git hook --- .husky/pre-commit | 4 ++++ package.json | 7 +------ 2 files changed, 5 insertions(+), 6 deletions(-) create mode 100755 .husky/pre-commit diff --git a/.husky/pre-commit b/.husky/pre-commit new file mode 100755 index 000000000..04225e10a --- /dev/null +++ b/.husky/pre-commit @@ -0,0 +1,4 @@ +#!/bin/sh +. "$(dirname "$0")/_/husky.sh" + +yarn run lint:fix diff --git a/package.json b/package.json index 86e042e13..76f6a49d4 100644 --- a/package.json +++ b/package.json @@ -47,11 +47,6 @@ "build:docker:develop": "node scripts/pinVersions && lerna run build:docker && cd hosting/scripts/linux/ && ./release-to-docker-hub.sh develop && cd -", "multi:enable": "lerna run multi:enable", "multi:disable": "lerna run multi:disable", - "prepare": "husky install" - }, - "husky": { - "hooks": { - "pre-commit": "yarn run lint:fix" - } + "postinstall": "husky install" } } From 29aabb8a59587e4d755561c3c5bcbb7dda638154 Mon Sep 17 00:00:00 2001 From: Andrew Kingston Date: Thu, 19 Aug 2021 16:04:15 +0100 Subject: [PATCH 055/110] Fix crash when evaluating builder preview specific code in prod apps --- packages/client/src/store/builder.js | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/packages/client/src/store/builder.js b/packages/client/src/store/builder.js index 65e4f70e6..0fa109fc1 100644 --- a/packages/client/src/store/builder.js +++ b/packages/client/src/store/builder.js @@ -61,6 +61,11 @@ const createBuilderStore = () => { } const writableStore = writable(initialState) const derivedStore = derived(writableStore, $state => { + // Avoid any of this logic if we aren't in the builder preview + if (!writableStore.inBuilder) { + return $state + } + // Derive the selected component instance and definition const { layout, screen, previewType, selectedComponentId } = $state const asset = previewType === "layout" ? layout : screen From f6d831b797ffd00d24f6c9442807ad1d8e18c734 Mon Sep 17 00:00:00 2001 From: Maurits Lourens Date: Thu, 19 Aug 2021 17:04:41 +0200 Subject: [PATCH 056/110] run 'yarn run lint' before each commit --- .husky/pre-commit | 2 +- packages/builder/src/components/start/UpdateAppModal.svelte | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.husky/pre-commit b/.husky/pre-commit index 04225e10a..3b614330e 100755 --- a/.husky/pre-commit +++ b/.husky/pre-commit @@ -1,4 +1,4 @@ #!/bin/sh . "$(dirname "$0")/_/husky.sh" -yarn run lint:fix +yarn run lint diff --git a/packages/builder/src/components/start/UpdateAppModal.svelte b/packages/builder/src/components/start/UpdateAppModal.svelte index 28af6d71f..dbbc000d1 100644 --- a/packages/builder/src/components/start/UpdateAppModal.svelte +++ b/packages/builder/src/components/start/UpdateAppModal.svelte @@ -74,7 +74,7 @@ } export const show = () => { - modal.show(); + modal.show() } export const hide = () => { modal.hide() From 8a8e13119e24c3e44628f177fc5baf81689c94ba Mon Sep 17 00:00:00 2001 From: Andrew Kingston Date: Thu, 19 Aug 2021 16:06:33 +0100 Subject: [PATCH 057/110] Fix bug determining whether an app is inside the preview or not --- packages/client/src/store/builder.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/client/src/store/builder.js b/packages/client/src/store/builder.js index 0fa109fc1..530f49843 100644 --- a/packages/client/src/store/builder.js +++ b/packages/client/src/store/builder.js @@ -62,7 +62,7 @@ const createBuilderStore = () => { const writableStore = writable(initialState) const derivedStore = derived(writableStore, $state => { // Avoid any of this logic if we aren't in the builder preview - if (!writableStore.inBuilder) { + if (!$state.inBuilder) { return $state } From 7f737523de0434911455f4623ce87660f7361503 Mon Sep 17 00:00:00 2001 From: Budibase Staging Release Bot <> Date: Thu, 19 Aug 2021 16:00:21 +0000 Subject: [PATCH 058/110] v0.9.105-alpha.25 --- lerna.json | 2 +- packages/auth/package.json | 2 +- packages/bbui/package.json | 2 +- packages/builder/package.json | 8 ++++---- packages/cli/package.json | 2 +- packages/client/package.json | 8 ++++---- packages/server/package.json | 10 +++++----- packages/standard-components/package.json | 4 ++-- packages/string-templates/package.json | 2 +- packages/worker/package.json | 6 +++--- 10 files changed, 23 insertions(+), 23 deletions(-) diff --git a/lerna.json b/lerna.json index 9fd593bfd..ca66b2050 100644 --- a/lerna.json +++ b/lerna.json @@ -1,5 +1,5 @@ { - "version": "0.9.105-alpha.24", + "version": "0.9.105-alpha.25", "npmClient": "yarn", "packages": [ "packages/*" diff --git a/packages/auth/package.json b/packages/auth/package.json index eeec47d32..e99761681 100644 --- a/packages/auth/package.json +++ b/packages/auth/package.json @@ -1,6 +1,6 @@ { "name": "@budibase/auth", - "version": "0.9.105-alpha.24", + "version": "0.9.105-alpha.25", "description": "Authentication middlewares for budibase builder and apps", "main": "src/index.js", "author": "Budibase", diff --git a/packages/bbui/package.json b/packages/bbui/package.json index 49ddadac5..e4c7f8475 100644 --- a/packages/bbui/package.json +++ b/packages/bbui/package.json @@ -1,7 +1,7 @@ { "name": "@budibase/bbui", "description": "A UI solution used in the different Budibase projects.", - "version": "0.9.105-alpha.24", + "version": "0.9.105-alpha.25", "license": "AGPL-3.0", "svelte": "src/index.js", "module": "dist/bbui.es.js", diff --git a/packages/builder/package.json b/packages/builder/package.json index 2ddd70228..7baad0f2b 100644 --- a/packages/builder/package.json +++ b/packages/builder/package.json @@ -1,6 +1,6 @@ { "name": "@budibase/builder", - "version": "0.9.105-alpha.24", + "version": "0.9.105-alpha.25", "license": "AGPL-3.0", "private": true, "scripts": { @@ -65,10 +65,10 @@ } }, "dependencies": { - "@budibase/bbui": "^0.9.105-alpha.24", - "@budibase/client": "^0.9.105-alpha.24", + "@budibase/bbui": "^0.9.105-alpha.25", + "@budibase/client": "^0.9.105-alpha.25", "@budibase/colorpicker": "1.1.2", - "@budibase/string-templates": "^0.9.105-alpha.24", + "@budibase/string-templates": "^0.9.105-alpha.25", "@sentry/browser": "5.19.1", "@spectrum-css/page": "^3.0.1", "@spectrum-css/vars": "^3.0.1", diff --git a/packages/cli/package.json b/packages/cli/package.json index 6b74bdd98..fbaf4bcb5 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -1,6 +1,6 @@ { "name": "@budibase/cli", - "version": "0.9.105-alpha.24", + "version": "0.9.105-alpha.25", "description": "Budibase CLI, for developers, self hosting and migrations.", "main": "src/index.js", "bin": { diff --git a/packages/client/package.json b/packages/client/package.json index ec61300fd..3ed8935bb 100644 --- a/packages/client/package.json +++ b/packages/client/package.json @@ -1,6 +1,6 @@ { "name": "@budibase/client", - "version": "0.9.105-alpha.24", + "version": "0.9.105-alpha.25", "license": "MPL-2.0", "module": "dist/budibase-client.js", "main": "dist/budibase-client.js", @@ -18,9 +18,9 @@ "dev:builder": "rollup -cw" }, "dependencies": { - "@budibase/bbui": "^0.9.105-alpha.24", - "@budibase/standard-components": "^0.9.105-alpha.24", - "@budibase/string-templates": "^0.9.105-alpha.24", + "@budibase/bbui": "^0.9.105-alpha.25", + "@budibase/standard-components": "^0.9.105-alpha.25", + "@budibase/string-templates": "^0.9.105-alpha.25", "regexparam": "^1.3.0", "shortid": "^2.2.15", "svelte-spa-router": "^3.0.5" diff --git a/packages/server/package.json b/packages/server/package.json index 3e86c2588..6e051c665 100644 --- a/packages/server/package.json +++ b/packages/server/package.json @@ -1,7 +1,7 @@ { "name": "@budibase/server", "email": "hi@budibase.com", - "version": "0.9.105-alpha.24", + "version": "0.9.105-alpha.25", "description": "Budibase Web Server", "main": "src/index.js", "repository": { @@ -62,9 +62,9 @@ "author": "Budibase", "license": "AGPL-3.0-or-later", "dependencies": { - "@budibase/auth": "^0.9.105-alpha.24", - "@budibase/client": "^0.9.105-alpha.24", - "@budibase/string-templates": "^0.9.105-alpha.24", + "@budibase/auth": "^0.9.105-alpha.25", + "@budibase/client": "^0.9.105-alpha.25", + "@budibase/string-templates": "^0.9.105-alpha.25", "@elastic/elasticsearch": "7.10.0", "@koa/router": "8.0.0", "@sendgrid/mail": "7.1.1", @@ -117,7 +117,7 @@ "devDependencies": { "@babel/core": "^7.14.3", "@babel/preset-env": "^7.14.4", - "@budibase/standard-components": "^0.9.105-alpha.24", + "@budibase/standard-components": "^0.9.105-alpha.25", "@jest/test-sequencer": "^24.8.0", "@types/bull": "^3.15.1", "@types/jest": "^26.0.23", diff --git a/packages/standard-components/package.json b/packages/standard-components/package.json index 30c5e662c..cfbde5fc0 100644 --- a/packages/standard-components/package.json +++ b/packages/standard-components/package.json @@ -29,11 +29,11 @@ "keywords": [ "svelte" ], - "version": "0.9.105-alpha.24", + "version": "0.9.105-alpha.25", "license": "MIT", "gitHead": "d1836a898cab3f8ab80ee6d8f42be1a9eed7dcdc", "dependencies": { - "@budibase/bbui": "^0.9.105-alpha.24", + "@budibase/bbui": "^0.9.105-alpha.25", "@spectrum-css/button": "^3.0.3", "@spectrum-css/card": "^3.0.3", "@spectrum-css/divider": "^1.0.3", diff --git a/packages/string-templates/package.json b/packages/string-templates/package.json index c8ceb8013..f64edbe90 100644 --- a/packages/string-templates/package.json +++ b/packages/string-templates/package.json @@ -1,6 +1,6 @@ { "name": "@budibase/string-templates", - "version": "0.9.105-alpha.24", + "version": "0.9.105-alpha.25", "description": "Handlebars wrapper for Budibase templating.", "main": "src/index.cjs", "module": "dist/bundle.mjs", diff --git a/packages/worker/package.json b/packages/worker/package.json index 718cee165..7bf04501d 100644 --- a/packages/worker/package.json +++ b/packages/worker/package.json @@ -1,7 +1,7 @@ { "name": "@budibase/worker", "email": "hi@budibase.com", - "version": "0.9.105-alpha.24", + "version": "0.9.105-alpha.25", "description": "Budibase background service", "main": "src/index.js", "repository": { @@ -23,8 +23,8 @@ "author": "Budibase", "license": "AGPL-3.0-or-later", "dependencies": { - "@budibase/auth": "^0.9.105-alpha.24", - "@budibase/string-templates": "^0.9.105-alpha.24", + "@budibase/auth": "^0.9.105-alpha.25", + "@budibase/string-templates": "^0.9.105-alpha.25", "@koa/router": "^8.0.0", "@techpass/passport-openidconnect": "^0.3.0", "aws-sdk": "^2.811.0", From 29c33d7ed45dd12efc3c5d4d4daea4b3550fd1a4 Mon Sep 17 00:00:00 2001 From: Andrew Kingston Date: Thu, 19 Aug 2021 17:18:41 +0100 Subject: [PATCH 059/110] Remove schema validation when inheriting initial values in forms --- packages/standard-components/src/forms/Form.svelte | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/packages/standard-components/src/forms/Form.svelte b/packages/standard-components/src/forms/Form.svelte index 2514232eb..b08a85343 100644 --- a/packages/standard-components/src/forms/Form.svelte +++ b/packages/standard-components/src/forms/Form.svelte @@ -24,12 +24,9 @@ if (["user", "url"].includes(context.closestComponentId)) { return {} } - // Only inherit values if the table ID matches + // Always inherit the closest data source const closestContext = context[`${context.closestComponentId}`] || {} - if (dataSource.tableId !== closestContext?.tableId) { - return {} - } - return closestContext + return closestContext || {} } $: initialValues = getInitialValues(actionType, dataSource, $context) From 73ea7a803261b2f530866499f57d3d7209d3db3c Mon Sep 17 00:00:00 2001 From: Budibase Release Bot <> Date: Thu, 19 Aug 2021 16:28:01 +0000 Subject: [PATCH 060/110] v0.9.108 --- lerna.json | 2 +- packages/auth/package.json | 2 +- packages/bbui/package.json | 2 +- packages/builder/package.json | 8 ++++---- packages/cli/package.json | 2 +- packages/client/package.json | 8 ++++---- packages/server/package.json | 10 +++++----- packages/standard-components/package.json | 4 ++-- packages/string-templates/package.json | 2 +- packages/worker/package.json | 6 +++--- 10 files changed, 23 insertions(+), 23 deletions(-) diff --git a/lerna.json b/lerna.json index 1b2dfb6bb..f77edaab1 100644 --- a/lerna.json +++ b/lerna.json @@ -1,5 +1,5 @@ { - "version": "0.9.107", + "version": "0.9.108", "npmClient": "yarn", "packages": [ "packages/*" diff --git a/packages/auth/package.json b/packages/auth/package.json index 8e8395b21..1ef8877eb 100644 --- a/packages/auth/package.json +++ b/packages/auth/package.json @@ -1,6 +1,6 @@ { "name": "@budibase/auth", - "version": "0.9.107", + "version": "0.9.108", "description": "Authentication middlewares for budibase builder and apps", "main": "src/index.js", "author": "Budibase", diff --git a/packages/bbui/package.json b/packages/bbui/package.json index 7ea6d15a5..9fc53e0f5 100644 --- a/packages/bbui/package.json +++ b/packages/bbui/package.json @@ -1,7 +1,7 @@ { "name": "@budibase/bbui", "description": "A UI solution used in the different Budibase projects.", - "version": "0.9.107", + "version": "0.9.108", "license": "AGPL-3.0", "svelte": "src/index.js", "module": "dist/bbui.es.js", diff --git a/packages/builder/package.json b/packages/builder/package.json index 2aa61c79b..07ad4c935 100644 --- a/packages/builder/package.json +++ b/packages/builder/package.json @@ -1,6 +1,6 @@ { "name": "@budibase/builder", - "version": "0.9.107", + "version": "0.9.108", "license": "AGPL-3.0", "private": true, "scripts": { @@ -65,10 +65,10 @@ } }, "dependencies": { - "@budibase/bbui": "^0.9.107", - "@budibase/client": "^0.9.107", + "@budibase/bbui": "^0.9.108", + "@budibase/client": "^0.9.108", "@budibase/colorpicker": "1.1.2", - "@budibase/string-templates": "^0.9.107", + "@budibase/string-templates": "^0.9.108", "@sentry/browser": "5.19.1", "@spectrum-css/page": "^3.0.1", "@spectrum-css/vars": "^3.0.1", diff --git a/packages/cli/package.json b/packages/cli/package.json index c328df69c..68934a0c7 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -1,6 +1,6 @@ { "name": "@budibase/cli", - "version": "0.9.107", + "version": "0.9.108", "description": "Budibase CLI, for developers, self hosting and migrations.", "main": "src/index.js", "bin": { diff --git a/packages/client/package.json b/packages/client/package.json index 897217186..08d98ac4b 100644 --- a/packages/client/package.json +++ b/packages/client/package.json @@ -1,6 +1,6 @@ { "name": "@budibase/client", - "version": "0.9.107", + "version": "0.9.108", "license": "MPL-2.0", "module": "dist/budibase-client.js", "main": "dist/budibase-client.js", @@ -18,9 +18,9 @@ "dev:builder": "rollup -cw" }, "dependencies": { - "@budibase/bbui": "^0.9.107", - "@budibase/standard-components": "^0.9.107", - "@budibase/string-templates": "^0.9.107", + "@budibase/bbui": "^0.9.108", + "@budibase/standard-components": "^0.9.108", + "@budibase/string-templates": "^0.9.108", "regexparam": "^1.3.0", "shortid": "^2.2.15", "svelte-spa-router": "^3.0.5" diff --git a/packages/server/package.json b/packages/server/package.json index 5daca5d40..a2cf814e1 100644 --- a/packages/server/package.json +++ b/packages/server/package.json @@ -1,7 +1,7 @@ { "name": "@budibase/server", "email": "hi@budibase.com", - "version": "0.9.107", + "version": "0.9.108", "description": "Budibase Web Server", "main": "src/index.js", "repository": { @@ -60,9 +60,9 @@ "author": "Budibase", "license": "AGPL-3.0-or-later", "dependencies": { - "@budibase/auth": "^0.9.107", - "@budibase/client": "^0.9.107", - "@budibase/string-templates": "^0.9.107", + "@budibase/auth": "^0.9.108", + "@budibase/client": "^0.9.108", + "@budibase/string-templates": "^0.9.108", "@elastic/elasticsearch": "7.10.0", "@koa/router": "8.0.0", "@sendgrid/mail": "7.1.1", @@ -115,7 +115,7 @@ "devDependencies": { "@babel/core": "^7.14.3", "@babel/preset-env": "^7.14.4", - "@budibase/standard-components": "^0.9.107", + "@budibase/standard-components": "^0.9.108", "@jest/test-sequencer": "^24.8.0", "@types/bull": "^3.15.1", "@types/jest": "^26.0.23", diff --git a/packages/standard-components/package.json b/packages/standard-components/package.json index 96b2deff0..cb29853a3 100644 --- a/packages/standard-components/package.json +++ b/packages/standard-components/package.json @@ -29,11 +29,11 @@ "keywords": [ "svelte" ], - "version": "0.9.107", + "version": "0.9.108", "license": "MIT", "gitHead": "d1836a898cab3f8ab80ee6d8f42be1a9eed7dcdc", "dependencies": { - "@budibase/bbui": "^0.9.107", + "@budibase/bbui": "^0.9.108", "@spectrum-css/card": "^3.0.3", "@spectrum-css/link": "^3.1.3", "@spectrum-css/page": "^3.0.1", diff --git a/packages/string-templates/package.json b/packages/string-templates/package.json index b64b19bb9..13626d1b9 100644 --- a/packages/string-templates/package.json +++ b/packages/string-templates/package.json @@ -1,6 +1,6 @@ { "name": "@budibase/string-templates", - "version": "0.9.107", + "version": "0.9.108", "description": "Handlebars wrapper for Budibase templating.", "main": "src/index.cjs", "module": "dist/bundle.mjs", diff --git a/packages/worker/package.json b/packages/worker/package.json index 9d6b98aa6..5be0d4f92 100644 --- a/packages/worker/package.json +++ b/packages/worker/package.json @@ -1,7 +1,7 @@ { "name": "@budibase/worker", "email": "hi@budibase.com", - "version": "0.9.107", + "version": "0.9.108", "description": "Budibase background service", "main": "src/index.js", "repository": { @@ -21,8 +21,8 @@ "author": "Budibase", "license": "AGPL-3.0-or-later", "dependencies": { - "@budibase/auth": "^0.9.107", - "@budibase/string-templates": "^0.9.107", + "@budibase/auth": "^0.9.108", + "@budibase/string-templates": "^0.9.108", "@koa/router": "^8.0.0", "@techpass/passport-openidconnect": "^0.3.0", "aws-sdk": "^2.811.0", From 08171c1e68562f3c27eedac2ae53659ef19ea23b Mon Sep 17 00:00:00 2001 From: Andrew Kingston Date: Fri, 20 Aug 2021 09:27:38 +0100 Subject: [PATCH 061/110] Fix dependency loop in client stores --- .../client/src/components/Component.svelte | 1 - packages/client/src/store/builder.js | 44 +------------ packages/client/src/store/screens.js | 38 +++++++++++- packages/client/src/utils/components.js | 62 +++++++++++++++++++ .../src/forms/InnerForm.svelte | 2 +- 5 files changed, 103 insertions(+), 44 deletions(-) create mode 100644 packages/client/src/utils/components.js diff --git a/packages/client/src/components/Component.svelte b/packages/client/src/components/Component.svelte index 8c7437e52..ed53a7eb3 100644 --- a/packages/client/src/components/Component.svelte +++ b/packages/client/src/components/Component.svelte @@ -74,7 +74,6 @@ styles: { ...instance._styles, id, empty, interactive }, empty, selected, - props: componentSettings, name, }) diff --git a/packages/client/src/store/builder.js b/packages/client/src/store/builder.js index 530f49843..cf6e8afb9 100644 --- a/packages/client/src/store/builder.js +++ b/packages/client/src/store/builder.js @@ -1,5 +1,6 @@ import { writable, derived } from "svelte/store" import Manifest from "@budibase/standard-components/manifest.json" +import { findComponentById, findComponentPathById } from "../utils/components" const dispatchEvent = (type, data = {}) => { window.dispatchEvent( @@ -9,45 +10,6 @@ const dispatchEvent = (type, data = {}) => { ) } -const findComponentById = (component, componentId) => { - if (!component || !componentId) { - return null - } - if (component._id === componentId) { - return component - } - if (!component._children?.length) { - return null - } - for (let child of component._children) { - const result = findComponentById(child, componentId) - if (result) { - return result - } - } - return null -} - -const findComponentIdPath = (component, componentId, path = []) => { - if (!component || !componentId) { - return null - } - path = [...path, component._id] - if (component._id === componentId) { - return path - } - if (!component._children?.length) { - return null - } - for (let child of component._children) { - const result = findComponentIdPath(child, componentId, path) - if (result) { - return result - } - } - return null -} - const createBuilderStore = () => { const initialState = { inBuilder: false, @@ -75,13 +37,13 @@ const createBuilderStore = () => { const definition = type ? Manifest[type] : null // Derive the selected component path - const path = findComponentIdPath(asset.props, selectedComponentId) || [] + const path = findComponentPathById(asset.props, selectedComponentId) || [] return { ...$state, selectedComponent: component, selectedComponentDefinition: definition, - selectedComponentPath: path, + selectedComponentPath: path?.map(component => component._id), } }) diff --git a/packages/client/src/store/screens.js b/packages/client/src/store/screens.js index 367d9ecfe..702f662f8 100644 --- a/packages/client/src/store/screens.js +++ b/packages/client/src/store/screens.js @@ -1,7 +1,12 @@ -import { derived } from "svelte/store" +import { derived, get } from "svelte/store" import { routeStore } from "./routes" import { builderStore } from "./builder" import { appStore } from "./app" +import { + findComponentPathById, + findChildrenByType, + findComponentById, +} from "../utils/components" const createScreenStore = () => { const store = derived( @@ -36,8 +41,39 @@ const createScreenStore = () => { } ) + // Utils to parse component definitions + const actions = { + findComponentById: componentId => { + const { activeScreen, activeLayout } = get(store) + let result = findComponentById(activeScreen?.props, componentId) + if (result) { + return result + } + return findComponentById(activeLayout?.props) + }, + findComponentPathById: componentId => { + const { activeScreen, activeLayout } = get(store) + let result = findComponentPathById(activeScreen?.props, componentId) + if (result) { + return result + } + return findComponentPathById(activeLayout?.props) + }, + findChildrenByType: (componentId, type) => { + const component = actions.findComponentById(componentId) + if (!component || !component._children) { + return null + } + let children = [] + findChildrenByType(component, type, children) + console.log(children) + return children + }, + } + return { subscribe: store.subscribe, + actions, } } diff --git a/packages/client/src/utils/components.js b/packages/client/src/utils/components.js new file mode 100644 index 000000000..4b1b8a7ad --- /dev/null +++ b/packages/client/src/utils/components.js @@ -0,0 +1,62 @@ +/** + * Finds a component instance by ID + */ +export const findComponentById = (component, componentId) => { + if (!component || !componentId) { + return null + } + if (component._id === componentId) { + return component + } + if (!component._children?.length) { + return null + } + for (let child of component._children) { + const result = findComponentById(child, componentId) + if (result) { + return result + } + } + return null +} + +/** + * Finds the component path to a component + */ +export const findComponentPathById = (component, componentId, path = []) => { + if (!component || !componentId) { + return null + } + path = [...path, component] + if (component._id === componentId) { + return path + } + if (!component._children?.length) { + return null + } + for (let child of component._children) { + const result = findComponentPathById(child, componentId, path) + if (result) { + return result + } + } + return null +} + +/** + * Finds all children instances of a certain component type of a given component + */ +export const findChildrenByType = (component, type, children = []) => { + if (!component) { + return + } + if (component._component.endsWith(`/${type}`)) { + children.push(component) + } + if (!component._children?.length) { + return + } + component._children.forEach(child => { + findChildrenByType(child, type, children) + }) +} diff --git a/packages/standard-components/src/forms/InnerForm.svelte b/packages/standard-components/src/forms/InnerForm.svelte index 774419bbd..3719de475 100644 --- a/packages/standard-components/src/forms/InnerForm.svelte +++ b/packages/standard-components/src/forms/InnerForm.svelte @@ -27,7 +27,7 @@ $: errors = deriveFieldProperty(fields, f => f.fieldState.error) $: valid = !Object.values($errors).some(error => error != null) - // Derive which fields belong in which steps + // Derive whether the current form step is valid $: currentStepValid = derived( [currentStep, ...fields], ([currentStepValue, ...fieldsValue]) => { From 0fb566a3dd7503c8279867ccb4dc98cbf7bd3159 Mon Sep 17 00:00:00 2001 From: Martin McKeaveney Date: Fri, 20 Aug 2021 09:47:08 +0100 Subject: [PATCH 062/110] allow users to override watchtower port --- hosting/docker-compose.yaml | 2 ++ hosting/hosting.properties | 1 + packages/cli/src/hosting/makeEnv.js | 1 + 3 files changed, 4 insertions(+) diff --git a/hosting/docker-compose.yaml b/hosting/docker-compose.yaml index 9cdf2b211..fffadae24 100644 --- a/hosting/docker-compose.yaml +++ b/hosting/docker-compose.yaml @@ -119,6 +119,8 @@ services: watchtower-service: image: containrrr/watchtower + ports: + - "${WATCHTOWER_PORT}:8080" volumes: - /var/run/docker.sock:/var/run/docker.sock command: --debug --http-api-update bbapps bbworker diff --git a/hosting/hosting.properties b/hosting/hosting.properties index d11972bc4..6382bf2f0 100644 --- a/hosting/hosting.properties +++ b/hosting/hosting.properties @@ -17,4 +17,5 @@ WORKER_PORT=4003 MINIO_PORT=4004 COUCH_DB_PORT=4005 REDIS_PORT=6379 +WATCHTOWER_PORT=8080 BUDIBASE_ENVIRONMENT=PRODUCTION diff --git a/packages/cli/src/hosting/makeEnv.js b/packages/cli/src/hosting/makeEnv.js index 8806c2e1e..ff59e6057 100644 --- a/packages/cli/src/hosting/makeEnv.js +++ b/packages/cli/src/hosting/makeEnv.js @@ -26,6 +26,7 @@ WORKER_PORT=4003 MINIO_PORT=4004 COUCH_DB_PORT=4005 REDIS_PORT=6379 +WATCHTOWER_PORT=8080 BUDIBASE_ENVIRONMENT=PRODUCTION` } From c3a6e13103062fff1e2e199b938bde065efff6c8 Mon Sep 17 00:00:00 2001 From: Peter Clement Date: Mon, 16 Aug 2021 09:44:15 +0100 Subject: [PATCH 063/110] Bug fix for unable to navigate from sql screen --- .../[selectedDatasource]/index.svelte | 18 ------------------ 1 file changed, 18 deletions(-) diff --git a/packages/builder/src/pages/builder/app/[application]/data/datasource/[selectedDatasource]/index.svelte b/packages/builder/src/pages/builder/app/[application]/data/datasource/[selectedDatasource]/index.svelte index b0377d2f2..0cd667a82 100644 --- a/packages/builder/src/pages/builder/app/[application]/data/datasource/[selectedDatasource]/index.svelte +++ b/packages/builder/src/pages/builder/app/[application]/data/datasource/[selectedDatasource]/index.svelte @@ -9,7 +9,6 @@ import ICONS from "components/backend/DatasourceNavigator/icons" import { capitalise } from "helpers" - let unsaved = false let relationshipModal let displayColumnModal let selectedFromRelationship, selectedToRelationship @@ -71,7 +70,6 @@ // Create datasource await datasources.save(datasource) notifications.success(`Datasource ${name} updated successfully.`) - unsaved = false } catch (err) { notifications.error(`Error saving datasource: ${err}`) } @@ -81,7 +79,6 @@ try { await datasources.updateSchema(datasource) notifications.success(`Datasource ${name} tables updated successfully.`) - unsaved = false await tables.fetch() } catch (err) { notifications.error(`Error updating datasource schema: ${err}`) @@ -98,10 +95,6 @@ $goto(`../../table/${table._id}`) } - function setUnsaved() { - unsaved = true - } - function openRelationshipModal(fromRelationship, toRelationship) { selectedFromRelationship = fromRelationship || {} selectedToRelationship = toRelationship || {} @@ -111,16 +104,6 @@ function openDisplayColumnModal() { displayColumnModal.show() } - - $beforeUrlChange(() => { - if (unsaved) { - notifications.error( - "Unsaved changes. Please save your datasource configuration before leaving." - ) - return false - } - return true - }) @@ -164,7 +147,6 @@
{#if datasource.plus} From 11dc516b26d2fa4c76d72ff3f898e8171ccdd8c5 Mon Sep 17 00:00:00 2001 From: Andrew Kingston Date: Fri, 20 Aug 2021 09:54:54 +0100 Subject: [PATCH 064/110] Add single button action for changing form step --- .../EventsEditor/EventEditor.svelte | 4 +- .../actions/ChangeFormStep.svelte | 68 +++++++++++++++++++ .../EventsEditor/actions/ClearForm.svelte | 2 +- .../actions/CloseScreenModal.svelte | 1 - .../EventsEditor/actions/LogOut.svelte | 1 - .../EventsEditor/actions/NavigateTo.svelte | 2 +- .../EventsEditor/actions/NextFormStep.svelte | 35 ---------- .../EventsEditor/actions/PrevFormStep.svelte | 35 ---------- .../EventsEditor/actions/ValidateForm.svelte | 2 +- .../EventsEditor/actions/index.js | 11 +-- packages/client/src/constants.js | 3 +- packages/client/src/utils/buttonActions.js | 16 ++--- packages/standard-components/manifest.json | 6 +- .../src/forms/InnerForm.svelte | 18 +++-- 14 files changed, 93 insertions(+), 111 deletions(-) create mode 100644 packages/builder/src/components/design/PropertiesPanel/PropertyControls/EventsEditor/actions/ChangeFormStep.svelte delete mode 100644 packages/builder/src/components/design/PropertiesPanel/PropertyControls/EventsEditor/actions/NextFormStep.svelte delete mode 100644 packages/builder/src/components/design/PropertiesPanel/PropertyControls/EventsEditor/actions/PrevFormStep.svelte diff --git a/packages/builder/src/components/design/PropertiesPanel/PropertyControls/EventsEditor/EventEditor.svelte b/packages/builder/src/components/design/PropertiesPanel/PropertyControls/EventsEditor/EventEditor.svelte index 2e2a31861..4ff746a03 100644 --- a/packages/builder/src/components/design/PropertiesPanel/PropertyControls/EventsEditor/EventEditor.svelte +++ b/packages/builder/src/components/design/PropertiesPanel/PropertyControls/EventsEditor/EventEditor.svelte @@ -33,7 +33,7 @@ $: selectedActionComponent = selectedAction && - actionTypes.find(t => t.name === selectedAction[EVENT_TYPE_KEY]).component + actionTypes.find(t => t.name === selectedAction[EVENT_TYPE_KEY])?.component // Select the first action if we delete an action $: { @@ -116,7 +116,7 @@ - {#if selectedAction} + {#if selectedActionComponent}
+ import { Select, Label, Stepper } from "@budibase/bbui" + import { currentAsset, store } from "builderStore" + import { getActionProviderComponents } from "builderStore/dataBinding" + import { onMount } from "svelte" + + export let parameters + + $: actionProviders = getActionProviderComponents( + $currentAsset, + $store.selectedComponentId, + "ChangeFormStep" + ) + + const typeOptions = [ + { + label: "Next step", + value: "next", + }, + { + label: "Previous step", + value: "prev", + }, + { + label: "First step", + value: "first", + }, + { + label: "Specific step", + value: "specific", + }, + ] + + onMount(() => { + if (!parameters.type) { + parameters.type = "next" + } + }) + + +
+ + + {#if parameters.type === "specific"} + + + {/if} +
+ + diff --git a/packages/builder/src/components/design/PropertiesPanel/PropertyControls/EventsEditor/actions/ClearForm.svelte b/packages/builder/src/components/design/PropertiesPanel/PropertyControls/EventsEditor/actions/ClearForm.svelte index 74eae6283..b3bf7ebf7 100644 --- a/packages/builder/src/components/design/PropertiesPanel/PropertyControls/EventsEditor/actions/ClearForm.svelte +++ b/packages/builder/src/components/design/PropertiesPanel/PropertyControls/EventsEditor/actions/ClearForm.svelte @@ -29,7 +29,7 @@ row-gap: var(--spacing-s); grid-template-columns: 60px 1fr; align-items: center; - max-width: 800px; + max-width: 400px; margin: 0 auto; } diff --git a/packages/builder/src/components/design/PropertiesPanel/PropertyControls/EventsEditor/actions/CloseScreenModal.svelte b/packages/builder/src/components/design/PropertiesPanel/PropertyControls/EventsEditor/actions/CloseScreenModal.svelte index 0afb8dd46..873c9ccf6 100644 --- a/packages/builder/src/components/design/PropertiesPanel/PropertyControls/EventsEditor/actions/CloseScreenModal.svelte +++ b/packages/builder/src/components/design/PropertiesPanel/PropertyControls/EventsEditor/actions/CloseScreenModal.svelte @@ -11,7 +11,6 @@ diff --git a/packages/builder/src/components/design/PropertiesPanel/PropertyControls/EventsEditor/actions/LogOut.svelte b/packages/builder/src/components/design/PropertiesPanel/PropertyControls/EventsEditor/actions/LogOut.svelte index 878212724..3434d6348 100644 --- a/packages/builder/src/components/design/PropertiesPanel/PropertyControls/EventsEditor/actions/LogOut.svelte +++ b/packages/builder/src/components/design/PropertiesPanel/PropertyControls/EventsEditor/actions/LogOut.svelte @@ -8,7 +8,6 @@ diff --git a/packages/builder/src/components/design/PropertiesPanel/PropertyControls/EventsEditor/actions/NavigateTo.svelte b/packages/builder/src/components/design/PropertiesPanel/PropertyControls/EventsEditor/actions/NavigateTo.svelte index 5a0e16365..d90b64531 100644 --- a/packages/builder/src/components/design/PropertiesPanel/PropertyControls/EventsEditor/actions/NavigateTo.svelte +++ b/packages/builder/src/components/design/PropertiesPanel/PropertyControls/EventsEditor/actions/NavigateTo.svelte @@ -25,7 +25,7 @@ align-items: center; gap: var(--spacing-m); grid-template-columns: auto 1fr; - max-width: 800px; + max-width: 400px; margin: 0 auto; } diff --git a/packages/builder/src/components/design/PropertiesPanel/PropertyControls/EventsEditor/actions/NextFormStep.svelte b/packages/builder/src/components/design/PropertiesPanel/PropertyControls/EventsEditor/actions/NextFormStep.svelte deleted file mode 100644 index a635e158c..000000000 --- a/packages/builder/src/components/design/PropertiesPanel/PropertyControls/EventsEditor/actions/NextFormStep.svelte +++ /dev/null @@ -1,35 +0,0 @@ - - -
- - x._instanceName} - getOptionValue={x => x._id} - /> -
- - diff --git a/packages/builder/src/components/design/PropertiesPanel/PropertyControls/EventsEditor/actions/ValidateForm.svelte b/packages/builder/src/components/design/PropertiesPanel/PropertyControls/EventsEditor/actions/ValidateForm.svelte index d61c9567d..e572dc6c1 100644 --- a/packages/builder/src/components/design/PropertiesPanel/PropertyControls/EventsEditor/actions/ValidateForm.svelte +++ b/packages/builder/src/components/design/PropertiesPanel/PropertyControls/EventsEditor/actions/ValidateForm.svelte @@ -34,7 +34,7 @@ row-gap: var(--spacing-s); grid-template-columns: 60px 1fr; align-items: center; - max-width: 800px; + max-width: 400px; margin: 0 auto; } diff --git a/packages/builder/src/components/design/PropertiesPanel/PropertyControls/EventsEditor/actions/index.js b/packages/builder/src/components/design/PropertiesPanel/PropertyControls/EventsEditor/actions/index.js index 75feb533a..9cf2461b7 100644 --- a/packages/builder/src/components/design/PropertiesPanel/PropertyControls/EventsEditor/actions/index.js +++ b/packages/builder/src/components/design/PropertiesPanel/PropertyControls/EventsEditor/actions/index.js @@ -7,8 +7,7 @@ import ValidateForm from "./ValidateForm.svelte" import LogOut from "./LogOut.svelte" import ClearForm from "./ClearForm.svelte" import CloseScreenModal from "./CloseScreenModal.svelte" -import NextFormStep from "./NextFormStep.svelte" -import PrevFormStep from "./PrevFormStep.svelte" +import ChangeFormStep from "./ChangeFormStep.svelte" // Defines which actions are available to configure in the front end. // Unfortunately the "name" property is used as the identifier so please don't @@ -55,11 +54,7 @@ export default [ component: CloseScreenModal, }, { - name: "Next Form Step", - component: NextFormStep, - }, - { - name: "Previous Form Step", - component: PrevFormStep, + name: "Change Form Step", + component: ChangeFormStep, }, ] diff --git a/packages/client/src/constants.js b/packages/client/src/constants.js index 99e95e839..d2237ba3b 100644 --- a/packages/client/src/constants.js +++ b/packages/client/src/constants.js @@ -7,8 +7,7 @@ export const ActionTypes = { RefreshDatasource: "RefreshDatasource", SetDataProviderQuery: "SetDataProviderQuery", ClearForm: "ClearForm", - NextFormStep: "NextFormStep", - PrevFormStep: "PrevFormStep", + ChangeFormStep: "ChangeFormStep", } export const ApiVersion = "1" diff --git a/packages/client/src/utils/buttonActions.js b/packages/client/src/utils/buttonActions.js index 6affe6649..a18cd7489 100644 --- a/packages/client/src/utils/buttonActions.js +++ b/packages/client/src/utils/buttonActions.js @@ -105,19 +105,12 @@ const clearFormHandler = async (action, context) => { ) } -const nextFormStepHandler = async (action, context) => { +const changeFormStepHandler = async (action, context) => { return await executeActionHandler( context, action.parameters.componentId, - ActionTypes.NextFormStep - ) -} - -const prevFormStepHandler = async (action, context) => { - return await executeActionHandler( - context, - action.parameters.componentId, - ActionTypes.PrevFormStep + ActionTypes.ChangeFormStep, + action.parameters ) } @@ -138,8 +131,7 @@ const handlerMap = { ["Log Out"]: logoutHandler, ["Clear Form"]: clearFormHandler, ["Close Screen Modal"]: closeScreenModalHandler, - ["Next Form Step"]: nextFormStepHandler, - ["Previous Form Step"]: prevFormStepHandler, + ["Change Form Step"]: changeFormStepHandler, } const confirmTextMap = { diff --git a/packages/standard-components/manifest.json b/packages/standard-components/manifest.json index 785b549c3..b54812e8b 100644 --- a/packages/standard-components/manifest.json +++ b/packages/standard-components/manifest.json @@ -1706,8 +1706,7 @@ "actions": [ "ValidateForm", "ClearForm", - "NextFormStep", - "PrevFormStep" + "ChangeFormStep" ], "styles": ["size"], "settings": [ @@ -1768,9 +1767,6 @@ "icon": "Form", "hasChildren": true, "illegalChildren": ["section"], - "actions": [ - "ValidateFormStep" - ], "styles": ["size"], "settings": [ { diff --git a/packages/standard-components/src/forms/InnerForm.svelte b/packages/standard-components/src/forms/InnerForm.svelte index 3719de475..5422d50d1 100644 --- a/packages/standard-components/src/forms/InnerForm.svelte +++ b/packages/standard-components/src/forms/InnerForm.svelte @@ -140,11 +140,16 @@ get(field).fieldApi.clearValue() }) }, - nextStep: () => { - currentStep.update(step => step + 1) - }, - prevStep: () => { - currentStep.update(step => Math.max(1, step - 1)) + changeStep: ({ type, number }) => { + if (type === "next") { + currentStep.update(step => step + 1) + } else if (type === "prev") { + currentStep.update(step => Math.max(1, step - 1)) + } else if (type === "first") { + currentStep.set(1) + } else if (type === "specific" && number && !isNaN(number)) { + currentStep.set(number) + } }, setStep: step => { if (step) { @@ -249,8 +254,7 @@ const actions = [ { type: ActionTypes.ValidateForm, callback: formApi.validate }, { type: ActionTypes.ClearForm, callback: formApi.clear }, - { type: ActionTypes.NextFormStep, callback: formApi.nextStep }, - { type: ActionTypes.PrevFormStep, callback: formApi.prevStep }, + { type: ActionTypes.ChangeFormStep, callback: formApi.changeStep }, ] From cb46e763d3193c0317793c5c1c05dd1db4cb8b09 Mon Sep 17 00:00:00 2001 From: Andrew Kingston Date: Fri, 20 Aug 2021 10:03:21 +0100 Subject: [PATCH 065/110] Making picker sorting optional and default to off. Sort by default for client apps --- packages/bbui/src/Form/Core/Multiselect.svelte | 2 ++ packages/bbui/src/Form/Core/Picker.svelte | 8 ++++++-- packages/bbui/src/Form/Core/Select.svelte | 2 ++ packages/bbui/src/Form/Multiselect.svelte | 2 ++ packages/bbui/src/Form/Select.svelte | 2 ++ .../components/backend/DataTable/RowFieldControl.svelte | 1 + .../backend/TableNavigator/TableDataImport.svelte | 1 + .../src/components/common/LinkedRowSelector.svelte | 2 ++ .../standard-components/src/forms/OptionsField.svelte | 1 + .../src/forms/RelationshipField.svelte | 1 + 10 files changed, 20 insertions(+), 2 deletions(-) diff --git a/packages/bbui/src/Form/Core/Multiselect.svelte b/packages/bbui/src/Form/Core/Multiselect.svelte index 94d4f2b76..3eb1add26 100644 --- a/packages/bbui/src/Form/Core/Multiselect.svelte +++ b/packages/bbui/src/Form/Core/Multiselect.svelte @@ -12,6 +12,7 @@ export let getOptionValue = option => option export let readonly = false export let autocomplete = false + export let sort = false const dispatch = createEventDispatcher() $: selectedLookupMap = getSelectedLookupMap(value) @@ -83,4 +84,5 @@ {getOptionLabel} {getOptionValue} onSelectOption={toggleOption} + {sort} /> diff --git a/packages/bbui/src/Form/Core/Picker.svelte b/packages/bbui/src/Form/Core/Picker.svelte index de7bfb02b..8acbc4e1a 100644 --- a/packages/bbui/src/Form/Core/Picker.svelte +++ b/packages/bbui/src/Form/Core/Picker.svelte @@ -25,11 +25,12 @@ export let quiet = false export let autoWidth = false export let autocomplete = false + export let sort = false const dispatch = createEventDispatcher() let searchTerm = null - $: sortedOptions = getSortedOptions(options, getOptionLabel) + $: sortedOptions = getSortedOptions(options, getOptionLabel, sort) $: filteredOptions = getFilteredOptions( sortedOptions, searchTerm, @@ -45,10 +46,13 @@ open = true } - const getSortedOptions = (options, getLabel) => { + const getSortedOptions = (options, getLabel, sort) => { if (!options?.length || !Array.isArray(options)) { return [] } + if (!sort) { + return options + } return options.sort((a, b) => { const labelA = getLabel(a) const labelB = getLabel(b) diff --git a/packages/bbui/src/Form/Core/Select.svelte b/packages/bbui/src/Form/Core/Select.svelte index 3a2daf25c..413b11dd3 100644 --- a/packages/bbui/src/Form/Core/Select.svelte +++ b/packages/bbui/src/Form/Core/Select.svelte @@ -15,6 +15,7 @@ export let quiet = false export let autoWidth = false export let autocomplete = false + export let sort = false const dispatch = createEventDispatcher() let open = false @@ -72,6 +73,7 @@ {getOptionIcon} {fieldIcon} {autocomplete} + {sort} isPlaceholder={value == null || value === ""} placeholderOption={placeholder} isOptionSelected={option => option === value} diff --git a/packages/bbui/src/Form/Multiselect.svelte b/packages/bbui/src/Form/Multiselect.svelte index 1bb003991..957dcccdd 100644 --- a/packages/bbui/src/Form/Multiselect.svelte +++ b/packages/bbui/src/Form/Multiselect.svelte @@ -13,6 +13,7 @@ export let options = [] export let getOptionLabel = option => option export let getOptionValue = option => option + export let sort = false const dispatch = createEventDispatcher() const onChange = e => { @@ -29,6 +30,7 @@ {value} {options} {placeholder} + {sort} {getOptionLabel} {getOptionValue} on:change={onChange} diff --git a/packages/bbui/src/Form/Select.svelte b/packages/bbui/src/Form/Select.svelte index eecc719a9..b6547c0a9 100644 --- a/packages/bbui/src/Form/Select.svelte +++ b/packages/bbui/src/Form/Select.svelte @@ -16,6 +16,7 @@ export let getOptionIcon = option => option?.icon export let quiet = false export let autoWidth = false + export let sort = false const dispatch = createEventDispatcher() const onChange = e => { @@ -41,6 +42,7 @@ {options} {placeholder} {autoWidth} + {sort} {getOptionLabel} {getOptionValue} {getOptionIcon} diff --git a/packages/builder/src/components/backend/DataTable/RowFieldControl.svelte b/packages/builder/src/components/backend/DataTable/RowFieldControl.svelte index 078dbf25b..072401667 100644 --- a/packages/builder/src/components/backend/DataTable/RowFieldControl.svelte +++ b/packages/builder/src/components/backend/DataTable/RowFieldControl.svelte @@ -19,6 +19,7 @@ data-cy="{meta.name}-select" bind:value options={meta.constraints.inclusion} + sort /> {:else if type === "datetime"} diff --git a/packages/builder/src/components/backend/TableNavigator/TableDataImport.svelte b/packages/builder/src/components/backend/TableNavigator/TableDataImport.svelte index 8ce2ebe7f..2bbbb471c 100644 --- a/packages/builder/src/components/backend/TableNavigator/TableDataImport.svelte +++ b/packages/builder/src/components/backend/TableNavigator/TableDataImport.svelte @@ -153,6 +153,7 @@ label="Display Column" bind:value={primaryDisplay} options={fields} + sort />
{/if} diff --git a/packages/builder/src/components/common/LinkedRowSelector.svelte b/packages/builder/src/components/common/LinkedRowSelector.svelte index 080f09809..1f3dea137 100644 --- a/packages/builder/src/components/common/LinkedRowSelector.svelte +++ b/packages/builder/src/components/common/LinkedRowSelector.svelte @@ -47,6 +47,7 @@ getOptionValue={row => row._id} on:change={e => (linkedIds = e.detail ? [e.detail] : [])} {label} + sort /> {:else} row._id} + sort /> {/if} diff --git a/packages/standard-components/src/forms/OptionsField.svelte b/packages/standard-components/src/forms/OptionsField.svelte index 9f3e4a455..c5efa6f58 100644 --- a/packages/standard-components/src/forms/OptionsField.svelte +++ b/packages/standard-components/src/forms/OptionsField.svelte @@ -87,6 +87,7 @@ getOptionLabel={flatOptions ? x => x : x => x.label} getOptionValue={flatOptions ? x => x : x => x.value} {autocomplete} + sort={true} /> {:else if optionsType === "radio"} option._id} {placeholder} + sort={true} /> {/if} From 461311bb06edca4d702e052732ab545f6b44c9b8 Mon Sep 17 00:00:00 2001 From: Peter Clement Date: Wed, 18 Aug 2021 00:02:17 +0100 Subject: [PATCH 066/110] fixes organisation logo not showing --- .../data/datasource/[selectedDatasource]/index.svelte | 2 +- .../src/pages/builder/portal/settings/organisation.svelte | 6 +++++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/packages/builder/src/pages/builder/app/[application]/data/datasource/[selectedDatasource]/index.svelte b/packages/builder/src/pages/builder/app/[application]/data/datasource/[selectedDatasource]/index.svelte index 0cd667a82..fc82e3a11 100644 --- a/packages/builder/src/pages/builder/app/[application]/data/datasource/[selectedDatasource]/index.svelte +++ b/packages/builder/src/pages/builder/app/[application]/data/datasource/[selectedDatasource]/index.svelte @@ -1,5 +1,5 @@ - + + // These static values are prefixed to avoid clashes with actual columns + __valid: valid, + __currentStep: $currentStep, + __currentStepValid: $currentStepValid, + } + + +
From 52d2f6741e4baa1fe9c3fedfec687312164e1834 Mon Sep 17 00:00:00 2001 From: Andrew Kingston Date: Mon, 23 Aug 2021 11:46:50 +0100 Subject: [PATCH 079/110] Always limit data providers to 1 row on details screens, even for external tables --- .../src/builderStore/store/screenTemplates/rowDetailScreen.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/builder/src/builderStore/store/screenTemplates/rowDetailScreen.js b/packages/builder/src/builderStore/store/screenTemplates/rowDetailScreen.js index f19f4e365..ec737fe36 100644 --- a/packages/builder/src/builderStore/store/screenTemplates/rowDetailScreen.js +++ b/packages/builder/src/builderStore/store/screenTemplates/rowDetailScreen.js @@ -86,7 +86,7 @@ const createScreen = table => { valueType: "Binding", }, ], - limit: table.type === "external" ? undefined : 1, + limit: 1, paginate: false, }) From 4a67de926a68bb8b68b1c26215079a8952b97504 Mon Sep 17 00:00:00 2001 From: Andrew Kingston Date: Mon, 23 Aug 2021 14:00:45 +0100 Subject: [PATCH 080/110] Fix button casing --- .../PropertyControls/ValidationEditor/ValidationEditor.svelte | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/builder/src/components/design/PropertiesPanel/PropertyControls/ValidationEditor/ValidationEditor.svelte b/packages/builder/src/components/design/PropertiesPanel/PropertyControls/ValidationEditor/ValidationEditor.svelte index 0b7fd12de..6c62c9f5a 100644 --- a/packages/builder/src/components/design/PropertiesPanel/PropertyControls/ValidationEditor/ValidationEditor.svelte +++ b/packages/builder/src/components/design/PropertiesPanel/PropertyControls/ValidationEditor/ValidationEditor.svelte @@ -17,7 +17,7 @@ } -Configure Validation +Configure validation Configure validation rules for this field. From 796dc24b06cc658053acc18c971402544d8bf012 Mon Sep 17 00:00:00 2001 From: Andrew Kingston Date: Mon, 23 Aug 2021 14:01:57 +0100 Subject: [PATCH 081/110] Show component hidden by conditional UI if they would otherwise hide the selected component --- packages/client/src/components/Component.svelte | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/client/src/components/Component.svelte b/packages/client/src/components/Component.svelte index ed53a7eb3..a10c88f41 100644 --- a/packages/client/src/components/Component.svelte +++ b/packages/client/src/components/Component.svelte @@ -63,6 +63,7 @@ $: selected = $builderStore.inBuilder && $builderStore.selectedComponentId === instance._id + $: inSelectedPath = $builderStore.selectedComponentPath?.includes(id) $: interactive = $builderStore.previewType === "layout" || insideScreenslot $: evaluateConditions(enrichedSettings?._conditions) $: componentSettings = { ...enrichedSettings, ...conditionalSettings } @@ -174,13 +175,12 @@ {#key propsHash} - {#if constructor && componentSettings && visible} + {#if constructor && componentSettings && (visible || inSelectedPath)}
{#if children.length} From 3fc345a8feaa71908f6a0c55078c80add10450e8 Mon Sep 17 00:00:00 2001 From: Andrew Kingston Date: Mon, 23 Aug 2021 15:13:41 +0100 Subject: [PATCH 082/110] Fix issue with svelte reactive statements not being sufficiently 'reactive' in core form field in client lib by replacing with manual store subscription --- packages/standard-components/src/forms/Field.svelte | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/packages/standard-components/src/forms/Field.svelte b/packages/standard-components/src/forms/Field.svelte index b202bc506..9f08f204c 100644 --- a/packages/standard-components/src/forms/Field.svelte +++ b/packages/standard-components/src/forms/Field.svelte @@ -1,7 +1,7 @@ diff --git a/packages/builder/src/components/design/PropertiesPanel/ScreenSettingsSection.svelte b/packages/builder/src/components/design/PropertiesPanel/ScreenSettingsSection.svelte index cfe225e0f..d782d1cd4 100644 --- a/packages/builder/src/components/design/PropertiesPanel/ScreenSettingsSection.svelte +++ b/packages/builder/src/components/design/PropertiesPanel/ScreenSettingsSection.svelte @@ -37,7 +37,7 @@ key: "routing.route", label: "Route", control: Input, - parser: val => val.replaceAll(" ", "-"), + parser: val => val.replace(/ +/g, "-"), }, { key: "routing.roleId", label: "Access", control: RoleSelect }, { key: "layoutId", label: "Layout", control: LayoutSelect }, From 336c3ac1f240657329c111c67a58e672542f82e0 Mon Sep 17 00:00:00 2001 From: Budibase Staging Release Bot <> Date: Tue, 24 Aug 2021 09:33:04 +0000 Subject: [PATCH 091/110] v0.9.105-alpha.27 --- lerna.json | 2 +- packages/auth/package.json | 2 +- packages/bbui/package.json | 2 +- packages/builder/package.json | 8 ++++---- packages/cli/package.json | 2 +- packages/client/package.json | 8 ++++---- packages/server/package.json | 10 +++++----- packages/standard-components/package.json | 4 ++-- packages/string-templates/package.json | 2 +- packages/worker/package.json | 6 +++--- 10 files changed, 23 insertions(+), 23 deletions(-) diff --git a/lerna.json b/lerna.json index e25f8279b..2600a0663 100644 --- a/lerna.json +++ b/lerna.json @@ -1,5 +1,5 @@ { - "version": "0.9.105-alpha.26", + "version": "0.9.105-alpha.27", "npmClient": "yarn", "packages": [ "packages/*" diff --git a/packages/auth/package.json b/packages/auth/package.json index cb72ad382..6159dfcee 100644 --- a/packages/auth/package.json +++ b/packages/auth/package.json @@ -1,6 +1,6 @@ { "name": "@budibase/auth", - "version": "0.9.105-alpha.26", + "version": "0.9.105-alpha.27", "description": "Authentication middlewares for budibase builder and apps", "main": "src/index.js", "author": "Budibase", diff --git a/packages/bbui/package.json b/packages/bbui/package.json index 701bfdd8c..f1213dc9e 100644 --- a/packages/bbui/package.json +++ b/packages/bbui/package.json @@ -1,7 +1,7 @@ { "name": "@budibase/bbui", "description": "A UI solution used in the different Budibase projects.", - "version": "0.9.105-alpha.26", + "version": "0.9.105-alpha.27", "license": "AGPL-3.0", "svelte": "src/index.js", "module": "dist/bbui.es.js", diff --git a/packages/builder/package.json b/packages/builder/package.json index 549714473..229f1c314 100644 --- a/packages/builder/package.json +++ b/packages/builder/package.json @@ -1,6 +1,6 @@ { "name": "@budibase/builder", - "version": "0.9.105-alpha.26", + "version": "0.9.105-alpha.27", "license": "AGPL-3.0", "private": true, "scripts": { @@ -65,10 +65,10 @@ } }, "dependencies": { - "@budibase/bbui": "^0.9.105-alpha.26", - "@budibase/client": "^0.9.105-alpha.26", + "@budibase/bbui": "^0.9.105-alpha.27", + "@budibase/client": "^0.9.105-alpha.27", "@budibase/colorpicker": "1.1.2", - "@budibase/string-templates": "^0.9.105-alpha.26", + "@budibase/string-templates": "^0.9.105-alpha.27", "@sentry/browser": "5.19.1", "@spectrum-css/page": "^3.0.1", "@spectrum-css/vars": "^3.0.1", diff --git a/packages/cli/package.json b/packages/cli/package.json index c2dc4f739..70a1940cb 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -1,6 +1,6 @@ { "name": "@budibase/cli", - "version": "0.9.105-alpha.26", + "version": "0.9.105-alpha.27", "description": "Budibase CLI, for developers, self hosting and migrations.", "main": "src/index.js", "bin": { diff --git a/packages/client/package.json b/packages/client/package.json index f82aea3e7..7b24667b9 100644 --- a/packages/client/package.json +++ b/packages/client/package.json @@ -1,6 +1,6 @@ { "name": "@budibase/client", - "version": "0.9.105-alpha.26", + "version": "0.9.105-alpha.27", "license": "MPL-2.0", "module": "dist/budibase-client.js", "main": "dist/budibase-client.js", @@ -18,9 +18,9 @@ "dev:builder": "rollup -cw" }, "dependencies": { - "@budibase/bbui": "^0.9.105-alpha.26", - "@budibase/standard-components": "^0.9.105-alpha.26", - "@budibase/string-templates": "^0.9.105-alpha.26", + "@budibase/bbui": "^0.9.105-alpha.27", + "@budibase/standard-components": "^0.9.105-alpha.27", + "@budibase/string-templates": "^0.9.105-alpha.27", "regexparam": "^1.3.0", "shortid": "^2.2.15", "svelte-spa-router": "^3.0.5" diff --git a/packages/server/package.json b/packages/server/package.json index 5911e837e..c13b76fea 100644 --- a/packages/server/package.json +++ b/packages/server/package.json @@ -1,7 +1,7 @@ { "name": "@budibase/server", "email": "hi@budibase.com", - "version": "0.9.105-alpha.26", + "version": "0.9.105-alpha.27", "description": "Budibase Web Server", "main": "src/index.js", "repository": { @@ -62,9 +62,9 @@ "author": "Budibase", "license": "AGPL-3.0-or-later", "dependencies": { - "@budibase/auth": "^0.9.105-alpha.26", - "@budibase/client": "^0.9.105-alpha.26", - "@budibase/string-templates": "^0.9.105-alpha.26", + "@budibase/auth": "^0.9.105-alpha.27", + "@budibase/client": "^0.9.105-alpha.27", + "@budibase/string-templates": "^0.9.105-alpha.27", "@elastic/elasticsearch": "7.10.0", "@koa/router": "8.0.0", "@sendgrid/mail": "7.1.1", @@ -117,7 +117,7 @@ "devDependencies": { "@babel/core": "^7.14.3", "@babel/preset-env": "^7.14.4", - "@budibase/standard-components": "^0.9.105-alpha.26", + "@budibase/standard-components": "^0.9.105-alpha.27", "@jest/test-sequencer": "^24.8.0", "@types/bull": "^3.15.1", "@types/jest": "^26.0.23", diff --git a/packages/standard-components/package.json b/packages/standard-components/package.json index 4e1b17db2..e15e0cb65 100644 --- a/packages/standard-components/package.json +++ b/packages/standard-components/package.json @@ -29,11 +29,11 @@ "keywords": [ "svelte" ], - "version": "0.9.105-alpha.26", + "version": "0.9.105-alpha.27", "license": "MIT", "gitHead": "d1836a898cab3f8ab80ee6d8f42be1a9eed7dcdc", "dependencies": { - "@budibase/bbui": "^0.9.105-alpha.26", + "@budibase/bbui": "^0.9.105-alpha.27", "@spectrum-css/button": "^3.0.3", "@spectrum-css/card": "^3.0.3", "@spectrum-css/divider": "^1.0.3", diff --git a/packages/string-templates/package.json b/packages/string-templates/package.json index af56bcabd..f2c626b44 100644 --- a/packages/string-templates/package.json +++ b/packages/string-templates/package.json @@ -1,6 +1,6 @@ { "name": "@budibase/string-templates", - "version": "0.9.105-alpha.26", + "version": "0.9.105-alpha.27", "description": "Handlebars wrapper for Budibase templating.", "main": "src/index.cjs", "module": "dist/bundle.mjs", diff --git a/packages/worker/package.json b/packages/worker/package.json index 5800fa6de..d906fe9e9 100644 --- a/packages/worker/package.json +++ b/packages/worker/package.json @@ -1,7 +1,7 @@ { "name": "@budibase/worker", "email": "hi@budibase.com", - "version": "0.9.105-alpha.26", + "version": "0.9.105-alpha.27", "description": "Budibase background service", "main": "src/index.js", "repository": { @@ -23,8 +23,8 @@ "author": "Budibase", "license": "AGPL-3.0-or-later", "dependencies": { - "@budibase/auth": "^0.9.105-alpha.26", - "@budibase/string-templates": "^0.9.105-alpha.26", + "@budibase/auth": "^0.9.105-alpha.27", + "@budibase/string-templates": "^0.9.105-alpha.27", "@koa/router": "^8.0.0", "@techpass/passport-openidconnect": "^0.3.0", "aws-sdk": "^2.811.0", From 9f99f7c36d9f9172729162a585065318ddc4ad4d Mon Sep 17 00:00:00 2001 From: Martin McKeaveney Date: Tue, 24 Aug 2021 10:42:22 +0100 Subject: [PATCH 092/110] remove pageviews from PH --- packages/builder/src/analytics.js | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/builder/src/analytics.js b/packages/builder/src/analytics.js index 7442816aa..5b130a8e6 100644 --- a/packages/builder/src/analytics.js +++ b/packages/builder/src/analytics.js @@ -23,6 +23,7 @@ async function activate() { if (posthogConfigured) { posthog.init(process.env.POSTHOG_TOKEN, { autocapture: false, + capture_pageview: false, api_host: process.env.POSTHOG_URL, }) posthog.set_config({ persistence: "cookie" }) From 6e03449906a8e341c61991018a74234e87fca4b6 Mon Sep 17 00:00:00 2001 From: Budibase Release Bot <> Date: Tue, 24 Aug 2021 09:55:51 +0000 Subject: [PATCH 093/110] v0.9.114 --- lerna.json | 2 +- packages/auth/package.json | 2 +- packages/bbui/package.json | 2 +- packages/builder/package.json | 8 ++++---- packages/cli/package.json | 2 +- packages/client/package.json | 8 ++++---- packages/server/package.json | 10 +++++----- packages/standard-components/package.json | 4 ++-- packages/string-templates/package.json | 2 +- packages/worker/package.json | 6 +++--- 10 files changed, 23 insertions(+), 23 deletions(-) diff --git a/lerna.json b/lerna.json index 1754992fc..d190a75cf 100644 --- a/lerna.json +++ b/lerna.json @@ -1,5 +1,5 @@ { - "version": "0.9.113", + "version": "0.9.114", "npmClient": "yarn", "packages": [ "packages/*" diff --git a/packages/auth/package.json b/packages/auth/package.json index 43379c6ed..8b6b3434b 100644 --- a/packages/auth/package.json +++ b/packages/auth/package.json @@ -1,6 +1,6 @@ { "name": "@budibase/auth", - "version": "0.9.113", + "version": "0.9.114", "description": "Authentication middlewares for budibase builder and apps", "main": "src/index.js", "author": "Budibase", diff --git a/packages/bbui/package.json b/packages/bbui/package.json index e90c6c821..e41d95563 100644 --- a/packages/bbui/package.json +++ b/packages/bbui/package.json @@ -1,7 +1,7 @@ { "name": "@budibase/bbui", "description": "A UI solution used in the different Budibase projects.", - "version": "0.9.113", + "version": "0.9.114", "license": "AGPL-3.0", "svelte": "src/index.js", "module": "dist/bbui.es.js", diff --git a/packages/builder/package.json b/packages/builder/package.json index b703aeef2..d0a765f20 100644 --- a/packages/builder/package.json +++ b/packages/builder/package.json @@ -1,6 +1,6 @@ { "name": "@budibase/builder", - "version": "0.9.113", + "version": "0.9.114", "license": "AGPL-3.0", "private": true, "scripts": { @@ -65,10 +65,10 @@ } }, "dependencies": { - "@budibase/bbui": "^0.9.113", - "@budibase/client": "^0.9.113", + "@budibase/bbui": "^0.9.114", + "@budibase/client": "^0.9.114", "@budibase/colorpicker": "1.1.2", - "@budibase/string-templates": "^0.9.113", + "@budibase/string-templates": "^0.9.114", "@sentry/browser": "5.19.1", "@spectrum-css/page": "^3.0.1", "@spectrum-css/vars": "^3.0.1", diff --git a/packages/cli/package.json b/packages/cli/package.json index 6f64ce69c..8676c915b 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -1,6 +1,6 @@ { "name": "@budibase/cli", - "version": "0.9.113", + "version": "0.9.114", "description": "Budibase CLI, for developers, self hosting and migrations.", "main": "src/index.js", "bin": { diff --git a/packages/client/package.json b/packages/client/package.json index 120afdc5a..c7976cc9c 100644 --- a/packages/client/package.json +++ b/packages/client/package.json @@ -1,6 +1,6 @@ { "name": "@budibase/client", - "version": "0.9.113", + "version": "0.9.114", "license": "MPL-2.0", "module": "dist/budibase-client.js", "main": "dist/budibase-client.js", @@ -18,9 +18,9 @@ "dev:builder": "rollup -cw" }, "dependencies": { - "@budibase/bbui": "^0.9.113", - "@budibase/standard-components": "^0.9.113", - "@budibase/string-templates": "^0.9.113", + "@budibase/bbui": "^0.9.114", + "@budibase/standard-components": "^0.9.114", + "@budibase/string-templates": "^0.9.114", "regexparam": "^1.3.0", "shortid": "^2.2.15", "svelte-spa-router": "^3.0.5" diff --git a/packages/server/package.json b/packages/server/package.json index 4b8b38377..dacbbbc10 100644 --- a/packages/server/package.json +++ b/packages/server/package.json @@ -1,7 +1,7 @@ { "name": "@budibase/server", "email": "hi@budibase.com", - "version": "0.9.113", + "version": "0.9.114", "description": "Budibase Web Server", "main": "src/index.js", "repository": { @@ -60,9 +60,9 @@ "author": "Budibase", "license": "AGPL-3.0-or-later", "dependencies": { - "@budibase/auth": "^0.9.113", - "@budibase/client": "^0.9.113", - "@budibase/string-templates": "^0.9.113", + "@budibase/auth": "^0.9.114", + "@budibase/client": "^0.9.114", + "@budibase/string-templates": "^0.9.114", "@elastic/elasticsearch": "7.10.0", "@koa/router": "8.0.0", "@sendgrid/mail": "7.1.1", @@ -115,7 +115,7 @@ "devDependencies": { "@babel/core": "^7.14.3", "@babel/preset-env": "^7.14.4", - "@budibase/standard-components": "^0.9.113", + "@budibase/standard-components": "^0.9.114", "@jest/test-sequencer": "^24.8.0", "@types/bull": "^3.15.1", "@types/jest": "^26.0.23", diff --git a/packages/standard-components/package.json b/packages/standard-components/package.json index 17ccf0aec..d586988f5 100644 --- a/packages/standard-components/package.json +++ b/packages/standard-components/package.json @@ -29,11 +29,11 @@ "keywords": [ "svelte" ], - "version": "0.9.113", + "version": "0.9.114", "license": "MIT", "gitHead": "d1836a898cab3f8ab80ee6d8f42be1a9eed7dcdc", "dependencies": { - "@budibase/bbui": "^0.9.113", + "@budibase/bbui": "^0.9.114", "@spectrum-css/card": "^3.0.3", "@spectrum-css/link": "^3.1.3", "@spectrum-css/page": "^3.0.1", diff --git a/packages/string-templates/package.json b/packages/string-templates/package.json index 28c921610..a3b692023 100644 --- a/packages/string-templates/package.json +++ b/packages/string-templates/package.json @@ -1,6 +1,6 @@ { "name": "@budibase/string-templates", - "version": "0.9.113", + "version": "0.9.114", "description": "Handlebars wrapper for Budibase templating.", "main": "src/index.cjs", "module": "dist/bundle.mjs", diff --git a/packages/worker/package.json b/packages/worker/package.json index cac8ec7d3..65fb92ecc 100644 --- a/packages/worker/package.json +++ b/packages/worker/package.json @@ -1,7 +1,7 @@ { "name": "@budibase/worker", "email": "hi@budibase.com", - "version": "0.9.113", + "version": "0.9.114", "description": "Budibase background service", "main": "src/index.js", "repository": { @@ -21,8 +21,8 @@ "author": "Budibase", "license": "AGPL-3.0-or-later", "dependencies": { - "@budibase/auth": "^0.9.113", - "@budibase/string-templates": "^0.9.113", + "@budibase/auth": "^0.9.114", + "@budibase/string-templates": "^0.9.114", "@koa/router": "^8.0.0", "@techpass/passport-openidconnect": "^0.3.0", "aws-sdk": "^2.811.0", From 180d857d1d08ffcd245b02e3d6f24681b964def7 Mon Sep 17 00:00:00 2001 From: Maurits Lourens Date: Tue, 24 Aug 2021 12:33:02 +0200 Subject: [PATCH 094/110] Filter users table out of DataSource component and NewScreenModal --- .../components/design/NavigationPanel/NewScreenModal.svelte | 5 ++--- .../PropertiesPanel/PropertyControls/DataSourceSelect.svelte | 2 +- packages/builder/src/stores/backend/tables.js | 1 + 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/builder/src/components/design/NavigationPanel/NewScreenModal.svelte b/packages/builder/src/components/design/NavigationPanel/NewScreenModal.svelte index fb6b1c5ff..65c287ac5 100644 --- a/packages/builder/src/components/design/NavigationPanel/NewScreenModal.svelte +++ b/packages/builder/src/components/design/NavigationPanel/NewScreenModal.svelte @@ -1,7 +1,6 @@