diff --git a/.github/workflows/ecs_deploy.yml b/.github/workflows/ecs_deploy.yml
deleted file mode 100644
index 0d662c9c28..0000000000
--- a/.github/workflows/ecs_deploy.yml
+++ /dev/null
@@ -1,85 +0,0 @@
-# This workflow will build and push a new container image to Amazon ECR,
-# and then will deploy a new task definition to Amazon ECS, when a release is created
-#
-# To use this workflow, you will need to complete the following set-up steps:
-#
-# 1. Create an ECR repository to store your images.
-# For example: `aws ecr create-repository --repository-name my-ecr-repo --region us-east-2`.
-# Replace the value of `ECR_REPOSITORY` in the workflow below with your repository's name.
-# Replace the value of `aws-region` in the workflow below with your repository's region.
-#
-# 2. Create an ECS task definition, an ECS cluster, and an ECS service.
-# For example, follow the Getting Started guide on the ECS console:
-# https://us-east-2.console.aws.amazon.com/ecs/home?region=us-east-2#/firstRun
-# Replace the values for `service` and `cluster` in the workflow below with your service and cluster names.
-#
-# 3. Store your ECS task definition as a JSON file in your repository.
-# The format should follow the output of `aws ecs register-task-definition --generate-cli-skeleton`.
-# Replace the value of `task-definition` in the workflow below with your JSON file's name.
-# Replace the value of `container-name` in the workflow below with the name of the container
-# in the `containerDefinitions` section of the task definition.
-#
-# 4. Store an IAM user access key in GitHub Actions secrets named `AWS_ACCESS_KEY_ID` and `AWS_SECRET_ACCESS_KEY`.
-# See the documentation for each action used below for the recommended IAM policies for this IAM user,
-# and best practices on handling the access key credentials.
-
-on:
- push:
- tags:
- - 'v*'
-
-name: Deploy to Amazon ECS
-
-jobs:
- deploy:
- name: deploy
- runs-on: ubuntu-16.04
-
- steps:
- - name: Checkout
- uses: actions/checkout@v2
-
- - name: Configure AWS credentials
- uses: aws-actions/configure-aws-credentials@v1
- with:
- aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
- aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
- aws-region: eu-west-1
-
- - name: Download task definition
- run: |
- aws ecs describe-task-definition --task-definition ProdAppServerStackprodbudiapplbfargateserviceprodbudiappserverfargatetaskdefinition2EF7F1E7 --query taskDefinition > task-definition.json
-
- - name: Login to Amazon ECR
- id: login-ecr
- uses: aws-actions/amazon-ecr-login@v1
-
- - name: Build, tag, and push image to Amazon ECR
- id: build-image
- env:
- ECR_REGISTRY: ${{ steps.login-ecr.outputs.registry }}
- ECR_REPOSITORY: prod-budi-app-server
- IMAGE_TAG: ${{ github.sha }}
- run: |
- # Build a docker container and
- # push it to ECR so that it can
- # be deployed to ECS
- cd packages/server
- docker build -t $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG .
- docker push $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG
- echo "::set-output name=image::$ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG"
- - name: Fill in the new image ID in the Amazon ECS task definition
- id: task-def
- uses: aws-actions/amazon-ecs-render-task-definition@v1
- with:
- task-definition: task-definition.json
- container-name: prod-budi-app-server
- image: ${{ steps.build-image.outputs.image }}
-
- - name: Deploy Amazon ECS task definition
- uses: aws-actions/amazon-ecs-deploy-task-definition@v1
- with:
- task-definition: ${{ steps.task-def.outputs.task-definition }}
- service: prod-budi-app-server-service
- cluster: prod-budi-app-server
- wait-for-service-stability: true
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index 4e76626d2c..588f0c54ae 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -42,6 +42,10 @@ jobs:
echo //registry.npmjs.org/:_authToken=${NPM_TOKEN} >> .npmrc
yarn release
+ - name: Get Previous tag
+ id: previoustag
+ uses: "WyriHaximus/github-action-get-previous-tag@v1"
+
- name: Build/release Docker images
run: |
docker login -u $DOCKER_USER -p $DOCKER_PASSWORD
@@ -50,3 +54,18 @@ jobs:
env:
DOCKER_USER: ${{ secrets.DOCKER_USERNAME }}
DOCKER_PASSWORD: ${{ secrets.DOCKER_API_KEY }}
+ BUDIBASE_RELEASE_VERSION: ${{ steps.previoustag.outputs.tag }}
+
+ - uses: azure/setup-helm@v1
+ id: install
+
+ # So, we need to inject the values into this
+ - 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/.gitignore b/.gitignore
index b2a2021cc9..3a5fc5dc7b 100644
--- a/.gitignore
+++ b/.gitignore
@@ -55,7 +55,7 @@ typings/
.node_repl_history
# Output of 'npm pack'
-*.tgz
+# *.tgz
# Yarn Integrity file
.yarn-integrity
@@ -91,4 +91,4 @@ hosting/.generated-envoy.dev.yaml
# Sublime text
*.sublime-project
-*.sublime-workspace
\ No newline at end of file
+*.sublime-workspace
diff --git a/.husky/pre-commit b/.husky/pre-commit
new file mode 100755
index 0000000000..3b614330e0
--- /dev/null
+++ b/.husky/pre-commit
@@ -0,0 +1,4 @@
+#!/bin/sh
+. "$(dirname "$0")/_/husky.sh"
+
+yarn run lint
diff --git a/docs/budibase-0.1.0.tgz b/docs/budibase-0.1.0.tgz
new file mode 100644
index 0000000000..7873874ab0
Binary files /dev/null and b/docs/budibase-0.1.0.tgz differ
diff --git a/docs/budibase-0.1.1.tgz b/docs/budibase-0.1.1.tgz
new file mode 100644
index 0000000000..b38527c4a4
Binary files /dev/null and b/docs/budibase-0.1.1.tgz differ
diff --git a/docs/index.html b/docs/index.html
new file mode 100644
index 0000000000..0fa6060f8f
--- /dev/null
+++ b/docs/index.html
@@ -0,0 +1,9 @@
+
+
+ Budibase Helm Chart Repo
+
+
+
Budibase Charts Repo
+
Point Helm at this repo to see charts.
+
+
\ No newline at end of file
diff --git a/docs/index.yaml b/docs/index.yaml
new file mode 100644
index 0000000000..4e064f3dd0
--- /dev/null
+++ b/docs/index.yaml
@@ -0,0 +1,54 @@
+apiVersion: v1
+entries:
+ budibase:
+ - apiVersion: v2
+ appVersion: 0.9.56
+ created: "2021-08-18T18:41:52.640176+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: 8dc4f2ed4d98cad5adf25936aefea680042d3e4e17832f846b961fd8708ad192
+ 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:41:52.635603+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
+ - 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-18T18:41:52.629415+01:00"
diff --git a/hosting/docker-compose.yaml b/hosting/docker-compose.yaml
index 9cdf2b2114..560b273668 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
@@ -128,8 +130,6 @@ services:
- WATCHTOWER_CLEANUP=true
labels:
- "com.centurylinklabs.watchtower.enable=false"
- ports:
- - 6161:8080
volumes:
diff --git a/hosting/hosting.properties b/hosting/hosting.properties
index d11972bc4b..c8e2f5c606 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=6161
BUDIBASE_ENVIRONMENT=PRODUCTION
diff --git a/hosting/kubernetes/budibase/.helmignore b/hosting/kubernetes/budibase/.helmignore
new file mode 100644
index 0000000000..0e8a0eb36f
--- /dev/null
+++ b/hosting/kubernetes/budibase/.helmignore
@@ -0,0 +1,23 @@
+# Patterns to ignore when building packages.
+# This supports shell glob matching, relative path matching, and
+# negation (prefixed with !). Only one pattern per line.
+.DS_Store
+# Common VCS dirs
+.git/
+.gitignore
+.bzr/
+.bzrignore
+.hg/
+.hgignore
+.svn/
+# Common backup files
+*.swp
+*.bak
+*.tmp
+*.orig
+*~
+# Various IDEs
+.project
+.idea/
+*.tmproj
+.vscode/
diff --git a/hosting/kubernetes/budibase/Chart.yaml b/hosting/kubernetes/budibase/Chart.yaml
new file mode 100644
index 0000000000..b82cb3bab2
--- /dev/null
+++ b/hosting/kubernetes/budibase/Chart.yaml
@@ -0,0 +1,41 @@
+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.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
+# 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
+ condition: services.couchdb.enabled
+ - name: ingress-nginx
+ version: 3.35.0
+ repository: https://github.com/kubernetes/ingress-nginx
+ condition: services.ingress.nginx
diff --git a/hosting/kubernetes/budibase/README.md b/hosting/kubernetes/budibase/README.md
new file mode 100644
index 0000000000..efa78ba75c
--- /dev/null
+++ b/hosting/kubernetes/budibase/README.md
@@ -0,0 +1,39 @@
+# Budibase
+
+[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
+$ 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 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`
+
+## Uninstalling the Chart
+
+To uninstall/delete the `my-release` deployment:
+
+```console
+$ helm delete my-release
+```
diff --git a/hosting/kubernetes/budibase/charts/couchdb/Chart.yaml b/hosting/kubernetes/budibase/charts/couchdb/Chart.yaml
new file mode 100755
index 0000000000..74ae734a17
--- /dev/null
+++ b/hosting/kubernetes/budibase/charts/couchdb/Chart.yaml
@@ -0,0 +1,19 @@
+apiVersion: v1
+appVersion: 3.1.0
+description: A database featuring seamless multi-master sync, that scales from big
+ data to mobile, with an intuitive HTTP/JSON API and designed for reliability.
+home: https://couchdb.apache.org/
+icon: http://couchdb.apache.org/CouchDB-visual-identity/logo/CouchDB-couch-symbol.svg
+keywords:
+- couchdb
+- database
+- nosql
+maintainers:
+- email: kocolosk@apache.org
+ name: kocolosk
+- email: willholley@apache.org
+ name: willholley
+name: couchdb
+sources:
+- https://github.com/apache/couchdb-docker
+version: 3.3.4
diff --git a/hosting/kubernetes/budibase/charts/couchdb/README.md b/hosting/kubernetes/budibase/charts/couchdb/README.md
new file mode 100755
index 0000000000..3227123d06
--- /dev/null
+++ b/hosting/kubernetes/budibase/charts/couchdb/README.md
@@ -0,0 +1,244 @@
+# CouchDB
+
+Apache CouchDB is a database featuring seamless multi-master sync, that scales
+from big data to mobile, with an intuitive HTTP/JSON API and designed for
+reliability.
+
+This chart deploys a CouchDB cluster as a StatefulSet. It creates a ClusterIP
+Service in front of the Deployment for load balancing by default, but can also
+be configured to deploy other Service types or an Ingress Controller. The
+default persistence mechanism is simply the ephemeral local filesystem, but
+production deployments should set `persistentVolume.enabled` to `true` to attach
+storage volumes to each Pod in the Deployment.
+
+## TL;DR
+
+```bash
+$ helm repo add couchdb https://apache.github.io/couchdb-helm
+$ helm install couchdb/couchdb \
+ --set allowAdminParty=true \
+ --set couchdbConfig.couchdb.uuid=$(curl https://www.uuidgenerator.net/api/version4 2>/dev/null | tr -d -)
+```
+
+## Prerequisites
+
+- Kubernetes 1.9+ with Beta APIs enabled
+- Ingress requires Kubernetes 1.14+
+
+## Installing the Chart
+
+To install the chart with the release name `my-release`:
+
+Add the CouchDB Helm repository:
+
+```bash
+$ helm repo add couchdb https://apache.github.io/couchdb-helm
+```
+
+Afterwards install the chart replacing the UUID
+`decafbaddecafbaddecafbaddecafbad` with a custom one:
+
+```bash
+$ helm install \
+ --name my-release \
+ --set couchdbConfig.couchdb.uuid=decafbaddecafbaddecafbaddecafbad \
+ couchdb/couchdb
+```
+
+This will create a Secret containing the admin credentials for the cluster.
+Those credentials can be retrieved as follows:
+
+```bash
+$ kubectl get secret my-release-couchdb -o go-template='{{ .data.adminPassword }}' | base64 --decode
+```
+
+If you prefer to configure the admin credentials directly you can create a
+Secret containing `adminUsername`, `adminPassword` and `cookieAuthSecret` keys:
+
+```bash
+$ kubectl create secret generic my-release-couchdb --from-literal=adminUsername=foo --from-literal=adminPassword=bar --from-literal=cookieAuthSecret=baz
+```
+
+If you want to set the `adminHash` directly to achieve consistent salts between
+different nodes you need to addionally add the key `password.ini` to the secret:
+
+```bash
+$ kubectl create secret generic my-release-couchdb \
+ --from-literal=adminUsername=foo \
+ --from-literal=cookieAuthSecret=baz \
+ --from-file=./my-password.ini
+```
+
+With the following contents in `my-password.ini`:
+
+```
+[admins]
+foo =
+```
+
+and then install the chart while overriding the `createAdminSecret` setting:
+
+```bash
+$ helm install \
+ --name my-release \
+ --set createAdminSecret=false \
+ --set couchdbConfig.couchdb.uuid=decafbaddecafbaddecafbaddecafbad \
+ couchdb/couchdb
+```
+
+This Helm chart deploys CouchDB on the Kubernetes cluster in a default
+configuration. The [configuration](#configuration) section lists
+the parameters that can be configured during installation.
+
+> **Tip**: List all releases using `helm list`
+
+## Uninstalling the Chart
+
+To uninstall/delete the `my-release` Deployment:
+
+```bash
+$ helm delete my-release
+```
+
+The command removes all the Kubernetes components associated with the chart and
+deletes the release.
+
+## Upgrading an existing Release to a new major version
+
+A major chart version change (like v0.2.3 -> v1.0.0) indicates that there is an
+incompatible breaking change needing manual actions.
+
+### Upgrade to 3.0.0
+
+Since version 3.0.0 setting the CouchDB server instance UUID is mandatory.
+Therefore you need to generate a UUID and supply it as a value during the
+upgrade as follows:
+
+```bash
+$ helm upgrade \
+ --reuse-values \
+ --set couchdbConfig.couchdb.uuid= \
+ couchdb/couchdb
+```
+
+## Migrating from stable/couchdb
+
+This chart replaces the `stable/couchdb` chart previously hosted by Helm and continues the
+version semantics. You can upgrade directly from `stable/couchdb` to this chart using:
+
+```bash
+$ helm repo add couchdb https://apache.github.io/couchdb-helm
+$ helm upgrade my-release couchdb/couchdb
+```
+
+## Configuration
+
+The following table lists the most commonly configured parameters of the
+CouchDB chart and their default values:
+
+| Parameter | Description | Default |
+|---------------------------------|-------------------------------------------------------|----------------------------------------|
+| `clusterSize` | The initial number of nodes in the CouchDB cluster | 3 |
+| `couchdbConfig` | Map allowing override elements of server .ini config | *See below* |
+| `allowAdminParty` | If enabled, start cluster without admin account | false (requires creating a Secret) |
+| `createAdminSecret` | If enabled, create an admin account and cookie secret | true |
+| `schedulerName` | Name of the k8s scheduler (other than default) | `nil` |
+| `erlangFlags` | Map of flags supplied to the underlying Erlang VM | name: couchdb, setcookie: monster
+| `persistentVolume.enabled` | Boolean determining whether to attach a PV to each node | false
+| `persistentVolume.size` | If enabled, the size of the persistent volume to attach | 10Gi
+| `enableSearch` | Adds a sidecar for Lucene-powered text search | false |
+
+You can set the values of the `couchdbConfig` map according to the
+[official configuration][4]. The following shows the map's default values and
+required options to set:
+
+| Parameter | Description | Default |
+|---------------------------------|--------------------------------------------------------------------|----------------------------------------|
+| `couchdb.uuid` | UUID for this CouchDB server instance ([Required in a cluster][5]) | |
+| `chttpd.bind_address` | listens on all interfaces when set to any | any |
+| `chttpd.require_valid_user` | disables all the anonymous requests to the port 5984 when true | false |
+
+A variety of other parameters are also configurable. See the comments in the
+`values.yaml` file for further details:
+
+| Parameter | Default |
+|--------------------------------------|----------------------------------------|
+| `adminUsername` | admin |
+| `adminPassword` | auto-generated |
+| `adminHash` | |
+| `cookieAuthSecret` | auto-generated |
+| `image.repository` | couchdb |
+| `image.tag` | 3.1.0 |
+| `image.pullPolicy` | IfNotPresent |
+| `searchImage.repository` | kocolosk/couchdb-search |
+| `searchImage.tag` | 0.1.0 |
+| `searchImage.pullPolicy` | IfNotPresent |
+| `initImage.repository` | busybox |
+| `initImage.tag` | latest |
+| `initImage.pullPolicy` | Always |
+| `ingress.enabled` | false |
+| `ingress.hosts` | chart-example.local |
+| `ingress.annotations` | |
+| `ingress.path` | / |
+| `ingress.tls` | |
+| `persistentVolume.accessModes` | ReadWriteOnce |
+| `persistentVolume.storageClass` | Default for the Kube cluster |
+| `podManagementPolicy` | Parallel |
+| `affinity` | |
+| `annotations` | |
+| `tolerations` | |
+| `resources` | |
+| `service.annotations` | |
+| `service.enabled` | true |
+| `service.type` | ClusterIP |
+| `service.externalPort` | 5984 |
+| `dns.clusterDomainSuffix` | cluster.local |
+| `networkPolicy.enabled` | true |
+| `serviceAccount.enabled` | true |
+| `serviceAccount.create` | true |
+| `serviceAccount.imagePullSecrets` | |
+| `sidecars` | {} |
+| `livenessProbe.enabled` | true |
+| `livenessProbe.failureThreshold` | 3 |
+| `livenessProbe.initialDelaySeconds` | 0 |
+| `livenessProbe.periodSeconds` | 10 |
+| `livenessProbe.successThreshold` | 1 |
+| `livenessProbe.timeoutSeconds` | 1 |
+| `readinessProbe.enabled` | true |
+| `readinessProbe.failureThreshold` | 3 |
+| `readinessProbe.initialDelaySeconds` | 0 |
+| `readinessProbe.periodSeconds` | 10 |
+| `readinessProbe.successThreshold` | 1 |
+| `readinessProbe.timeoutSeconds` | 1 |
+
+## Feedback, Issues, Contributing
+
+General feedback is welcome at our [user][1] or [developer][2] mailing lists.
+
+Apache CouchDB has a [CONTRIBUTING][3] file with details on how to get started
+with issue reporting or contributing to the upkeep of this project. In short,
+use GitHub Issues, do not report anything on Docker's website.
+
+## Non-Apache CouchDB Development Team Contributors
+
+- [@natarajaya](https://github.com/natarajaya)
+- [@satchpx](https://github.com/satchpx)
+- [@spanato](https://github.com/spanato)
+- [@jpds](https://github.com/jpds)
+- [@sebastien-prudhomme](https://github.com/sebastien-prudhomme)
+- [@stepanstipl](https://github.com/sebastien-stepanstipl)
+- [@amatas](https://github.com/amatas)
+- [@Chimney42](https://github.com/Chimney42)
+- [@mattjmcnaughton](https://github.com/mattjmcnaughton)
+- [@mainephd](https://github.com/mainephd)
+- [@AdamDang](https://github.com/AdamDang)
+- [@mrtyler](https://github.com/mrtyler)
+- [@kevinwlau](https://github.com/kevinwlau)
+- [@jeyenzo](https://github.com/jeyenzo)
+- [@Pinpin31.](https://github.com/Pinpin31)
+
+[1]: http://mail-archives.apache.org/mod_mbox/couchdb-user/
+[2]: http://mail-archives.apache.org/mod_mbox/couchdb-dev/
+[3]: https://github.com/apache/couchdb/blob/master/CONTRIBUTING.md
+[4]: https://docs.couchdb.org/en/stable/config/index.html
+[5]: https://docs.couchdb.org/en/latest/setup/cluster.html#preparing-couchdb-nodes-to-be-joined-into-a-cluster
diff --git a/hosting/kubernetes/budibase/charts/couchdb/ci/required-values.yaml b/hosting/kubernetes/budibase/charts/couchdb/ci/required-values.yaml
new file mode 100755
index 0000000000..79589d2e04
--- /dev/null
+++ b/hosting/kubernetes/budibase/charts/couchdb/ci/required-values.yaml
@@ -0,0 +1,3 @@
+couchdbConfig:
+ couchdb:
+ uuid: "decafbaddecafbaddecafbaddecafbad"
diff --git a/hosting/kubernetes/budibase/charts/couchdb/ci/sidecar.yaml b/hosting/kubernetes/budibase/charts/couchdb/ci/sidecar.yaml
new file mode 100755
index 0000000000..aa570bdf74
--- /dev/null
+++ b/hosting/kubernetes/budibase/charts/couchdb/ci/sidecar.yaml
@@ -0,0 +1,9 @@
+sidecars:
+ - name: foo
+ image: "busybox"
+ imagePullPolicy: IfNotPresent
+ resources:
+ requests:
+ cpu: "0.1"
+ memory: 10Mi
+ command: ['while true; do echo "foo"; sleep 5; done;']
diff --git a/hosting/kubernetes/budibase/charts/couchdb/password.ini b/hosting/kubernetes/budibase/charts/couchdb/password.ini
new file mode 100755
index 0000000000..4ce8445aae
--- /dev/null
+++ b/hosting/kubernetes/budibase/charts/couchdb/password.ini
@@ -0,0 +1,2 @@
+[admins]
+{{ .Values.adminUsername }} = {{ .Values.adminHash }}
diff --git a/hosting/kubernetes/budibase/charts/couchdb/templates/NOTES.txt b/hosting/kubernetes/budibase/charts/couchdb/templates/NOTES.txt
new file mode 100755
index 0000000000..a3658bd37f
--- /dev/null
+++ b/hosting/kubernetes/budibase/charts/couchdb/templates/NOTES.txt
@@ -0,0 +1,20 @@
+Apache CouchDB is starting. Check the status of the Pods using:
+
+ kubectl get pods --namespace {{ .Release.Namespace }} -l "app={{ template "couchdb.name" . }},release={{ .Release.Name }}"
+
+Once all of the Pods are fully Ready, execute the following command to create
+some required system databases:
+
+ kubectl exec --namespace {{ .Release.Namespace }} {{ if not .Values.allowAdminParty }}-it {{ end }}{{ template "couchdb.fullname" . }}-0 -c couchdb -- \
+ curl -s \
+ http://127.0.0.1:5984/_cluster_setup \
+ -X POST \
+ -H "Content-Type: application/json" \
+{{- if .Values.allowAdminParty }}
+ -d '{"action": "finish_cluster"}'
+{{- else }}
+ -d '{"action": "finish_cluster"}' \
+ -u
+{{- end }}
+
+Then it's time to relax.
diff --git a/hosting/kubernetes/budibase/charts/couchdb/templates/_helpers.tpl b/hosting/kubernetes/budibase/charts/couchdb/templates/_helpers.tpl
new file mode 100755
index 0000000000..f9d013e487
--- /dev/null
+++ b/hosting/kubernetes/budibase/charts/couchdb/templates/_helpers.tpl
@@ -0,0 +1,81 @@
+{{/* vim: set filetype=mustache: */}}
+{{/*
+Expand the name of the chart.
+*/}}
+{{- define "couchdb.name" -}}
+{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" -}}
+{{- end -}}
+
+{{/*
+Create a default fully qualified app name.
+We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec).
+*/}}
+{{- define "couchdb.fullname" -}}
+{{- if .Values.fullnameOverride -}}
+{{- printf "%s-%s" .Values.fullnameOverride .Chart.Name | trunc 63 | trimSuffix "-" -}}
+{{- else -}}
+{{- $name := default .Chart.Name .Values.nameOverride -}}
+{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" -}}
+{{- end -}}
+{{- end -}}
+
+{{/*
+In the event that we create both a headless service and a traditional one,
+ensure that the latter gets a unique name.
+*/}}
+{{- define "couchdb.svcname" -}}
+{{- if .Values.fullnameOverride -}}
+{{- printf "%s-svc-%s" .Values.fullnameOverride .Chart.Name | trunc 63 | trimSuffix "-" -}}
+{{- else -}}
+{{- $name := default .Chart.Name .Values.nameOverride -}}
+{{- printf "%s-svc-%s" .Release.Name $name | trunc 63 | trimSuffix "-" -}}
+{{- end -}}
+{{- end -}}
+
+{{/*
+Create a random string if the supplied key does not exist
+*/}}
+{{- define "couchdb.defaultsecret" -}}
+{{- if . -}}
+{{- . | b64enc | quote -}}
+{{- else -}}
+{{- randAlphaNum 20 | b64enc | quote -}}
+{{- end -}}
+{{- end -}}
+
+{{/*
+Labels used to define Pods in the CouchDB statefulset
+*/}}
+{{- define "couchdb.ss.selector" -}}
+app: {{ template "couchdb.name" . }}
+release: {{ .Release.Name }}
+{{- end -}}
+
+{{/*
+Generates a comma delimited list of nodes in the cluster
+*/}}
+{{- define "couchdb.seedlist" -}}
+{{- $nodeCount := min 5 .Values.clusterSize | int }}
+ {{- range $index0 := until $nodeCount -}}
+ {{- $index1 := $index0 | add1 -}}
+ {{ $.Values.erlangFlags.name }}@{{ template "couchdb.fullname" $ }}-{{ $index0 }}.{{ template "couchdb.fullname" $ }}.{{ $.Release.Namespace }}.svc.{{ $.Values.dns.clusterDomainSuffix }}{{ if ne $index1 $nodeCount }},{{ end }}
+ {{- end -}}
+{{- end -}}
+
+{{/*
+If serviceAccount.name is specified, use that, else use the couchdb instance name
+*/}}
+{{- define "couchdb.serviceAccount" -}}
+{{- if .Values.serviceAccount.name -}}
+{{- .Values.serviceAccount.name }}
+{{- else -}}
+{{- template "couchdb.fullname" . -}}
+{{- end -}}
+{{- end -}}
+
+{{/*
+Fail if couchdbConfig.couchdb.uuid is undefined
+*/}}
+{{- define "couchdb.uuid" -}}
+{{- required "A value for couchdbConfig.couchdb.uuid must be set" (.Values.couchdbConfig.couchdb | default dict).uuid -}}
+{{- end -}}
\ No newline at end of file
diff --git a/hosting/kubernetes/budibase/charts/couchdb/templates/configmap.yaml b/hosting/kubernetes/budibase/charts/couchdb/templates/configmap.yaml
new file mode 100755
index 0000000000..a6a20e0574
--- /dev/null
+++ b/hosting/kubernetes/budibase/charts/couchdb/templates/configmap.yaml
@@ -0,0 +1,23 @@
+apiVersion: v1
+kind: ConfigMap
+metadata:
+ name: {{ template "couchdb.fullname" . }}
+ labels:
+ app: {{ template "couchdb.name" . }}
+ chart: "{{ .Chart.Name }}-{{ .Chart.Version }}"
+ heritage: {{ .Release.Service | quote }}
+ release: {{ .Release.Name | quote }}
+data:
+ inifile: |
+ {{ $couchdbConfig := dict "couchdb" (dict "uuid" (include "couchdb.uuid" .)) -}}
+ {{- $couchdbConfig := merge $couchdbConfig .Values.couchdbConfig -}}
+ {{- range $section, $settings := $couchdbConfig -}}
+ {{ printf "[%s]" $section }}
+ {{ range $key, $value := $settings -}}
+ {{ printf "%s = %s" $key ($value | toString) }}
+ {{ end }}
+ {{ end }}
+
+ seedlistinifile: |
+ [cluster]
+ seedlist = {{ template "couchdb.seedlist" . }}
diff --git a/hosting/kubernetes/budibase/charts/couchdb/templates/headless.yaml b/hosting/kubernetes/budibase/charts/couchdb/templates/headless.yaml
new file mode 100755
index 0000000000..0ce3ef0f35
--- /dev/null
+++ b/hosting/kubernetes/budibase/charts/couchdb/templates/headless.yaml
@@ -0,0 +1,17 @@
+apiVersion: v1
+kind: Service
+metadata:
+ name: {{ template "couchdb.fullname" . }}
+ labels:
+ app: {{ template "couchdb.name" . }}
+ chart: {{ .Chart.Name }}-{{ .Chart.Version | replace "+" "_" }}
+ release: {{ .Release.Name }}
+ heritage: {{ .Release.Service }}
+spec:
+ clusterIP: None
+ publishNotReadyAddresses: true
+ ports:
+ - name: couchdb
+ port: 5984
+ selector:
+{{ include "couchdb.ss.selector" . | indent 4 }}
diff --git a/hosting/kubernetes/budibase/charts/couchdb/templates/ingress.yaml b/hosting/kubernetes/budibase/charts/couchdb/templates/ingress.yaml
new file mode 100755
index 0000000000..c547847ce5
--- /dev/null
+++ b/hosting/kubernetes/budibase/charts/couchdb/templates/ingress.yaml
@@ -0,0 +1,33 @@
+{{- if .Values.ingress.enabled -}}
+{{- $serviceName := include "couchdb.fullname" . -}}
+{{- $servicePort := .Values.service.externalPort -}}
+{{- $path := .Values.ingress.path | quote -}}
+apiVersion: networking.k8s.io/v1beta1
+kind: Ingress
+metadata:
+ name: {{ template "couchdb.fullname" . }}
+ labels:
+ app: {{ template "couchdb.name" . }}
+ chart: {{ .Chart.Name }}-{{ .Chart.Version | replace "+" "_" }}
+ release: {{ .Release.Name }}
+ heritage: {{ .Release.Service }}
+ annotations:
+ {{- range $key, $value := .Values.ingress.annotations }}
+ {{ $key }}: {{ $value | quote }}
+ {{- end }}
+spec:
+ rules:
+ {{- range $host := .Values.ingress.hosts }}
+ - host: {{ $host }}
+ http:
+ paths:
+ - path: {{ $path }}
+ backend:
+ serviceName: {{ $serviceName }}
+ servicePort: {{ $servicePort }}
+ {{- end -}}
+ {{- if .Values.ingress.tls }}
+ tls:
+{{ toYaml .Values.ingress.tls | indent 4 }}
+ {{- end -}}
+{{- end -}}
diff --git a/hosting/kubernetes/budibase/charts/couchdb/templates/networkpolicy.yaml b/hosting/kubernetes/budibase/charts/couchdb/templates/networkpolicy.yaml
new file mode 100755
index 0000000000..2830708bef
--- /dev/null
+++ b/hosting/kubernetes/budibase/charts/couchdb/templates/networkpolicy.yaml
@@ -0,0 +1,31 @@
+
+{{- if .Values.networkPolicy.enabled }}
+kind: NetworkPolicy
+apiVersion: networking.k8s.io/v1
+metadata:
+ name: {{ template "couchdb.fullname" . }}
+ labels:
+ app: {{ template "couchdb.name" . }}
+ chart: {{ .Chart.Name }}-{{ .Chart.Version | replace "+" "_" }}
+ release: {{ .Release.Name }}
+ heritage: {{ .Release.Service }}
+spec:
+ podSelector:
+ matchLabels:
+{{ include "couchdb.ss.selector" . | indent 6 }}
+ ingress:
+ - ports:
+ - protocol: TCP
+ port: 5984
+ - ports:
+ - protocol: TCP
+ port: 9100
+ - protocol: TCP
+ port: 4369
+ from:
+ - podSelector:
+ matchLabels:
+{{ include "couchdb.ss.selector" . | indent 14 }}
+ policyTypes:
+ - Ingress
+{{- end }}
diff --git a/hosting/kubernetes/budibase/charts/couchdb/templates/secrets.yaml b/hosting/kubernetes/budibase/charts/couchdb/templates/secrets.yaml
new file mode 100755
index 0000000000..92f55c6d6b
--- /dev/null
+++ b/hosting/kubernetes/budibase/charts/couchdb/templates/secrets.yaml
@@ -0,0 +1,19 @@
+{{- if .Values.createAdminSecret -}}
+apiVersion: v1
+kind: Secret
+metadata:
+ name: {{ template "couchdb.fullname" . }}
+ labels:
+ app: {{ template "couchdb.fullname" . }}
+ chart: "{{ .Chart.Name }}-{{ .Chart.Version }}"
+ release: "{{ .Release.Name }}"
+ heritage: "{{ .Release.Service }}"
+type: Opaque
+data:
+ adminUsername: {{ template "couchdb.defaultsecret" .Values.adminUsername }}
+ adminPassword: {{ template "couchdb.defaultsecret" .Values.adminPassword }}
+ cookieAuthSecret: {{ template "couchdb.defaultsecret" .Values.cookieAuthSecret }}
+{{- if .Values.adminHash }}
+ password.ini: {{ tpl (.Files.Get "password.ini") . | b64enc }}
+{{- end -}}
+{{- end -}}
diff --git a/hosting/kubernetes/budibase/charts/couchdb/templates/service.yaml b/hosting/kubernetes/budibase/charts/couchdb/templates/service.yaml
new file mode 100755
index 0000000000..6d0382477d
--- /dev/null
+++ b/hosting/kubernetes/budibase/charts/couchdb/templates/service.yaml
@@ -0,0 +1,23 @@
+{{- if .Values.service.enabled -}}
+apiVersion: v1
+kind: Service
+metadata:
+ name: {{ template "couchdb.svcname" . }}
+ labels:
+ app: {{ template "couchdb.name" . }}
+ chart: {{ .Chart.Name }}-{{ .Chart.Version | replace "+" "_" }}
+ release: {{ .Release.Name }}
+ heritage: {{ .Release.Service }}
+{{- if .Values.service.annotations }}
+ annotations:
+{{ toYaml .Values.service.annotations | indent 4 }}
+{{- end }}
+spec:
+ ports:
+ - port: {{ .Values.service.externalPort }}
+ protocol: TCP
+ targetPort: 5984
+ type: {{ .Values.service.type }}
+ selector:
+{{ include "couchdb.ss.selector" . | indent 4 }}
+{{- end -}}
diff --git a/hosting/kubernetes/budibase/charts/couchdb/templates/serviceaccount.yaml b/hosting/kubernetes/budibase/charts/couchdb/templates/serviceaccount.yaml
new file mode 100755
index 0000000000..bb82799a49
--- /dev/null
+++ b/hosting/kubernetes/budibase/charts/couchdb/templates/serviceaccount.yaml
@@ -0,0 +1,15 @@
+{{- if .Values.serviceAccount.create }}
+apiVersion: v1
+kind: ServiceAccount
+metadata:
+ name: {{ template "couchdb.serviceAccount" . }}
+ labels:
+ app: {{ template "couchdb.name" . }}
+ chart: {{ .Chart.Name }}-{{ .Chart.Version | replace "+" "_" }}
+ release: {{ .Release.Name }}
+ heritage: {{ .Release.Service }}
+{{- if .Values.serviceAccount.imagePullSecrets }}
+imagePullSecrets:
+{{ toYaml .Values.serviceAccount.imagePullSecrets }}
+{{- end }}
+{{- end }}
diff --git a/hosting/kubernetes/budibase/charts/couchdb/templates/statefulset.yaml b/hosting/kubernetes/budibase/charts/couchdb/templates/statefulset.yaml
new file mode 100755
index 0000000000..6225fbe98c
--- /dev/null
+++ b/hosting/kubernetes/budibase/charts/couchdb/templates/statefulset.yaml
@@ -0,0 +1,202 @@
+apiVersion: apps/v1
+kind: StatefulSet
+metadata:
+ name: {{ template "couchdb.fullname" . }}
+ labels:
+ app: {{ template "couchdb.name" . }}
+ chart: {{ .Chart.Name }}-{{ .Chart.Version | replace "+" "_" }}
+ release: {{ .Release.Name }}
+ heritage: {{ .Release.Service }}
+spec:
+ replicas: {{ .Values.clusterSize }}
+ serviceName: {{ template "couchdb.fullname" . }}
+ podManagementPolicy: {{ .Values.podManagementPolicy }}
+ selector:
+ matchLabels:
+{{ include "couchdb.ss.selector" . | indent 6 }}
+ template:
+ metadata:
+ labels:
+{{ include "couchdb.ss.selector" . | indent 8 }}
+{{- with .Values.annotations }}
+ annotations:
+ checksum/config: {{ include (print $.Template.BasePath "/configmap.yaml") . | sha256sum }}
+{{ toYaml . | indent 8 }}
+{{- end }}
+ spec:
+ {{- if .Values.schedulerName }}
+ schedulerName: "{{ .Values.schedulerName }}"
+ {{- end }}
+ {{- if .Values.serviceAccount.enabled }}
+ serviceAccountName: {{ template "couchdb.serviceAccount" . }}
+ {{- end }}
+ initContainers:
+ - name: init-copy
+ image: "{{ .Values.initImage.repository }}:{{ .Values.initImage.tag }}"
+ imagePullPolicy: {{ .Values.initImage.pullPolicy }}
+ command: ['sh','-c','cp /tmp/chart.ini /default.d; cp /tmp/seedlist.ini /default.d; ls -lrt /default.d;']
+ volumeMounts:
+ - name: config
+ mountPath: /tmp/
+ - name: config-storage
+ mountPath: /default.d
+{{- if .Values.adminHash }}
+ - name: admin-hash-copy
+ image: "{{ .Values.initImage.repository }}:{{ .Values.initImage.tag }}"
+ imagePullPolicy: {{ .Values.initImage.pullPolicy }}
+ command: ['sh','-c','cp /tmp/password.ini /local.d/ ;']
+ volumeMounts:
+ - name: admin-password
+ mountPath: /tmp/password.ini
+ subPath: "password.ini"
+ - name: local-config-storage
+ mountPath: /local.d
+{{- end }}
+ containers:
+ - name: couchdb
+ image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}"
+ imagePullPolicy: {{ .Values.image.pullPolicy }}
+ ports:
+ - name: couchdb
+ containerPort: 5984
+ - name: epmd
+ containerPort: 4369
+ - containerPort: 9100
+ env:
+{{- if not .Values.allowAdminParty }}
+ - name: COUCHDB_USER
+ valueFrom:
+ secretKeyRef:
+ name: {{ template "couchdb.fullname" . }}
+ key: adminUsername
+ - name: COUCHDB_PASSWORD
+ valueFrom:
+ secretKeyRef:
+ name: {{ template "couchdb.fullname" . }}
+ key: adminPassword
+ - name: COUCHDB_SECRET
+ valueFrom:
+ secretKeyRef:
+ name: {{ template "couchdb.fullname" . }}
+ key: cookieAuthSecret
+{{- end }}
+ - name: ERL_FLAGS
+ value: "{{ range $k, $v := .Values.erlangFlags }} -{{ $k }} {{ $v }} {{ end }}"
+{{- if .Values.livenessProbe.enabled }}
+ livenessProbe:
+{{- if .Values.couchdbConfig.chttpd.require_valid_user }}
+ exec:
+ command:
+ - sh
+ - -c
+ - curl -G --silent --fail -u ${COUCHDB_USER}:${COUCHDB_PASSWORD} http://localhost:5984/_up
+{{- else }}
+ httpGet:
+ path: /_up
+ port: 5984
+{{- end }}
+ failureThreshold: {{ .Values.livenessProbe.failureThreshold }}
+ initialDelaySeconds: {{ .Values.livenessProbe.initialDelaySeconds }}
+ periodSeconds: {{ .Values.livenessProbe.periodSeconds }}
+ successThreshold: {{ .Values.livenessProbe.successThreshold }}
+ timeoutSeconds: {{ .Values.livenessProbe.timeoutSeconds }}
+{{- end }}
+{{- if .Values.readinessProbe.enabled }}
+ readinessProbe:
+{{- if .Values.couchdbConfig.chttpd.require_valid_user }}
+ exec:
+ command:
+ - sh
+ - -c
+ - curl -G --silent --fail -u ${COUCHDB_USER}:${COUCHDB_PASSWORD} http://localhost:5984/_up
+{{- else }}
+ httpGet:
+ path: /_up
+ port: 5984
+{{- end }}
+ failureThreshold: {{ .Values.readinessProbe.failureThreshold }}
+ initialDelaySeconds: {{ .Values.readinessProbe.initialDelaySeconds }}
+ periodSeconds: {{ .Values.readinessProbe.periodSeconds }}
+ successThreshold: {{ .Values.readinessProbe.successThreshold }}
+ timeoutSeconds: {{ .Values.readinessProbe.timeoutSeconds }}
+{{- end }}
+ resources:
+{{ toYaml .Values.resources | indent 12 }}
+ volumeMounts:
+ - name: config-storage
+ mountPath: /opt/couchdb/etc/default.d
+{{- if .Values.adminHash }}
+ - name: local-config-storage
+ mountPath: /opt/couchdb/etc/local.d
+{{- end }}
+ - name: database-storage
+ mountPath: /opt/couchdb/data
+{{- if .Values.enableSearch }}
+ - name: clouseau
+ image: "{{ .Values.searchImage.repository }}:{{ .Values.searchImage.tag }}"
+ imagePullPolicy: {{ .Values.searchImage.pullPolicy }}
+ volumeMounts:
+ - name: database-storage
+ mountPath: /opt/couchdb-search/data
+{{- end }}
+{{- if .Values.sidecars }}
+{{ toYaml .Values.sidecars | indent 8}}
+{{- end }}
+{{- if .Values.nodeSelector }}
+ nodeSelector:
+{{ toYaml .Values.nodeSelector | indent 8 }}
+{{- end }}
+{{- with .Values.tolerations }}
+ tolerations:
+{{ toYaml . | indent 8 }}
+{{- end }}
+{{- with .Values.affinity }}
+ affinity:
+{{ toYaml . | indent 8 }}
+{{- end }}
+ volumes:
+ - name: config-storage
+ emptyDir: {}
+ - name: config
+ configMap:
+ name: {{ template "couchdb.fullname" . }}
+ items:
+ - key: inifile
+ path: chart.ini
+ - key: seedlistinifile
+ path: seedlist.ini
+
+{{- if .Values.adminHash }}
+ - name: local-config-storage
+ emptyDir: {}
+ - name: admin-password
+ secret:
+ secretName: {{ template "couchdb.fullname" . }}
+{{- end -}}
+
+{{- if not .Values.persistentVolume.enabled }}
+ - name: database-storage
+ emptyDir: {}
+{{- else }}
+ volumeClaimTemplates:
+ - metadata:
+ name: database-storage
+ labels:
+ app: {{ template "couchdb.name" . }}
+ release: {{ .Release.Name }}
+ spec:
+ accessModes:
+ {{- range .Values.persistentVolume.accessModes }}
+ - {{ . | quote }}
+ {{- end }}
+ resources:
+ requests:
+ storage: {{ .Values.persistentVolume.size | quote }}
+ {{- if .Values.persistentVolume.storageClass }}
+ {{- if (eq "-" .Values.persistentVolume.storageClass) }}
+ storageClassName: ""
+ {{- else }}
+ storageClassName: "{{ .Values.persistentVolume.storageClass }}"
+ {{- end }}
+ {{- end }}
+{{- end }}
diff --git a/hosting/kubernetes/budibase/charts/couchdb/values.yaml b/hosting/kubernetes/budibase/charts/couchdb/values.yaml
new file mode 100755
index 0000000000..5a5025f816
--- /dev/null
+++ b/hosting/kubernetes/budibase/charts/couchdb/values.yaml
@@ -0,0 +1,201 @@
+## clusterSize is the initial size of the CouchDB cluster.
+clusterSize: 3
+
+## If allowAdminParty is enabled the cluster will start up without any database
+## administrator account; i.e., all users will be granted administrative
+## access. Otherwise, the system will look for a Secret called
+## -couchdb containing `adminUsername`, `adminPassword` and
+## `cookieAuthSecret` keys. See the `createAdminSecret` flag.
+## ref: https://kubernetes.io/docs/concepts/configuration/secret/
+allowAdminParty: false
+
+## If createAdminSecret is enabled a Secret called -couchdb will
+## be created containing auto-generated credentials. Users who prefer to set
+## these values themselves have a couple of options:
+##
+## 1) The `adminUsername`, `adminPassword`, `adminHash`, and `cookieAuthSecret`
+## can be defined directly in the chart's values. Note that all of a chart's
+## values are currently stored in plaintext in a ConfigMap in the tiller
+## namespace.
+##
+## 2) This flag can be disabled and a Secret with the required keys can be
+## created ahead of time.
+createAdminSecret: true
+
+# adminUsername: budibase
+# adminPassword: budibase
+# adminHash: -pbkdf2-this_is_not_necessarily_secure_either
+# cookieAuthSecret: admin
+
+## When enabled, will deploy a networkpolicy that allows CouchDB pods to
+## communicate with each other for clustering and ingress on port 5984
+networkPolicy:
+ enabled: true
+
+## Use an alternate scheduler, e.g. "stork".
+## ref: https://kubernetes.io/docs/tasks/administer-cluster/configure-multiple-schedulers/
+##
+# schedulerName:
+
+# Use a service account
+serviceAccount:
+ enabled: true
+ create: true
+# name:
+# imagePullSecrets:
+# - name: myimagepullsecret
+
+## The storage volume used by each Pod in the StatefulSet. If a
+## persistentVolume is not enabled, the Pods will use `emptyDir` ephemeral
+## local storage. Setting the storageClass attribute to "-" disables dynamic
+## provisioning of Persistent Volumes; leaving it unset will invoke the default
+## provisioner.
+persistentVolume:
+ enabled: false
+ accessModes:
+ - ReadWriteOnce
+ size: 10Gi
+ storageClass: ""
+
+## The CouchDB image
+image:
+ repository: couchdb
+ tag: 3.1.0
+ pullPolicy: IfNotPresent
+
+## Experimental integration with Lucene-powered fulltext search
+searchImage:
+ repository: kocolosk/couchdb-search
+ tag: 0.2.0
+ pullPolicy: IfNotPresent
+
+## Flip this to flag to include the Search container in each Pod
+enableSearch: true
+
+initImage:
+ repository: busybox
+ tag: latest
+ pullPolicy: Always
+
+## CouchDB is happy to spin up cluster nodes in parallel, but if you encounter
+## problems you can try setting podManagementPolicy to the StatefulSet default
+## `OrderedReady`
+podManagementPolicy: Parallel
+
+## To better tolerate Node failures, we can prevent Kubernetes scheduler from
+## assigning more than one Pod of CouchDB StatefulSet per Node using podAntiAffinity.
+affinity: {}
+ # podAntiAffinity:
+ # requiredDuringSchedulingIgnoredDuringExecution:
+ # - labelSelector:
+ # matchExpressions:
+ # - key: "app"
+ # operator: In
+ # values:
+ # - couchdb
+ # topologyKey: "kubernetes.io/hostname"
+
+## Optional pod annotations
+annotations: {}
+
+## Optional tolerations
+tolerations: []
+
+## A StatefulSet requires a headless Service to establish the stable network
+## identities of the Pods, and that Service is created automatically by this
+## chart without any additional configuration. The Service block below refers
+## to a second Service that governs how clients connect to the CouchDB cluster.
+service:
+ # annotations:
+ enabled: true
+ type: ClusterIP
+ externalPort: 5984
+
+## An Ingress resource can provide name-based virtual hosting and TLS
+## termination among other things for CouchDB deployments which are accessed
+## from outside the Kubernetes cluster.
+## ref: https://kubernetes.io/docs/concepts/services-networking/ingress/
+ingress:
+ enabled: false
+ hosts:
+ - chart-example.local
+ path: /
+ annotations: []
+ # kubernetes.io/ingress.class: nginx
+ # kubernetes.io/tls-acme: "true"
+ tls:
+ # Secrets must be manually created in the namespace.
+ # - secretName: chart-example-tls
+ # hosts:
+ # - chart-example.local
+
+## Optional resource requests and limits for the CouchDB container
+## ref: http://kubernetes.io/docs/user-guide/compute-resources/
+resources:
+ {}
+ # requests:
+ # cpu: 100m
+ # memory: 128Mi
+ # limits:
+ # cpu: 56
+ # memory: 256Gi
+
+## erlangFlags is a map that is passed to the Erlang VM as flags using the
+## ERL_FLAGS env. `name` and `setcookie` flags are minimally required to
+## establish connectivity between cluster nodes.
+## ref: http://erlang.org/doc/man/erl.html#init_flags
+erlangFlags:
+ name: couchdb
+ setcookie: monster
+
+## couchdbConfig will override default CouchDB configuration settings.
+## The contents of this map are reformatted into a .ini file laid down
+## by a ConfigMap object.
+## ref: http://docs.couchdb.org/en/latest/config/index.html
+couchdbConfig:
+ couchdb:
+ uuid: budibase-couchdb # REQUIRED: Unique identifier for this CouchDB server instance
+ # cluster:
+ # q: 8 # Create 8 shards for each database
+ chttpd:
+ bind_address: any
+ # chttpd.require_valid_user disables all the anonymous requests to the port
+ # 5984 when is set to true.
+ require_valid_user: false
+
+# Kubernetes local cluster domain.
+# This is used to generate FQDNs for peers when joining the CouchDB cluster.
+dns:
+ clusterDomainSuffix: cluster.local
+
+## Configure liveness and readiness probe values
+## Ref: https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-probes/#configure-probes
+livenessProbe:
+ enabled: true
+ failureThreshold: 3
+ initialDelaySeconds: 0
+ periodSeconds: 10
+ successThreshold: 1
+ timeoutSeconds: 1
+readinessProbe:
+ enabled: true
+ failureThreshold: 3
+ initialDelaySeconds: 0
+ periodSeconds: 10
+ successThreshold: 1
+ timeoutSeconds: 1
+
+# Configure arbitrary sidecar containers for CouchDB pods created by the
+# StatefulSet
+sidecars: {}
+ # - name: foo
+ # image: "busybox"
+ # imagePullPolicy: IfNotPresent
+ # resources:
+ # requests:
+ # cpu: "0.1"
+ # memory: 10Mi
+ # command: ['echo "foo";']
+ # volumeMounts:
+ # - name: database-storage
+ # mountPath: /opt/couchdb/data/
diff --git a/hosting/kubernetes/budibase/charts/ingress-nginx-3.35.0.tgz b/hosting/kubernetes/budibase/charts/ingress-nginx-3.35.0.tgz
new file mode 100644
index 0000000000..ee5214c497
Binary files /dev/null and b/hosting/kubernetes/budibase/charts/ingress-nginx-3.35.0.tgz differ
diff --git a/hosting/kubernetes/budibase/templates/NOTES.txt b/hosting/kubernetes/budibase/templates/NOTES.txt
new file mode 100644
index 0000000000..2ace0aa38d
--- /dev/null
+++ b/hosting/kubernetes/budibase/templates/NOTES.txt
@@ -0,0 +1,22 @@
+1. Get the application URL by running these commands:
+{{- if .Values.ingress.enabled }}
+{{- range $host := .Values.ingress.hosts }}
+ {{- range .paths }}
+ http{{ if $.Values.ingress.tls }}s{{ end }}://{{ $host.host }}{{ .path }}
+ {{- end }}
+{{- end }}
+{{- else if contains "NodePort" .Values.service.type }}
+ export NODE_PORT=$(kubectl get --namespace {{ .Release.Namespace }} -o jsonpath="{.spec.ports[0].nodePort}" services {{ include "budibase.fullname" . }})
+ export NODE_IP=$(kubectl get nodes --namespace {{ .Release.Namespace }} -o jsonpath="{.items[0].status.addresses[0].address}")
+ echo http://$NODE_IP:$NODE_PORT
+{{- else if contains "LoadBalancer" .Values.service.type }}
+ NOTE: It may take a few minutes for the LoadBalancer IP to be available.
+ You can watch the status of by running 'kubectl get --namespace {{ .Release.Namespace }} svc -w {{ include "budibase.fullname" . }}'
+ export SERVICE_IP=$(kubectl get svc --namespace {{ .Release.Namespace }} {{ include "budibase.fullname" . }} --template "{{"{{ range (index .status.loadBalancer.ingress 0) }}{{.}}{{ end }}"}}")
+ echo http://$SERVICE_IP:{{ .Values.service.port }}
+{{- else if contains "ClusterIP" .Values.service.type }}
+ export POD_NAME=$(kubectl get pods --namespace {{ .Release.Namespace }} -l "app.kubernetes.io/name={{ include "budibase.name" . }},app.kubernetes.io/instance={{ .Release.Name }}" -o jsonpath="{.items[0].metadata.name}")
+ export CONTAINER_PORT=$(kubectl get pod --namespace {{ .Release.Namespace }} $POD_NAME -o jsonpath="{.spec.containers[0].ports[0].containerPort}")
+ echo "Visit http://127.0.0.1:8080 to use your application"
+ kubectl --namespace {{ .Release.Namespace }} port-forward $POD_NAME 8080:$CONTAINER_PORT
+{{- end }}
diff --git a/hosting/kubernetes/budibase/templates/_helpers.tpl b/hosting/kubernetes/budibase/templates/_helpers.tpl
new file mode 100644
index 0000000000..3b0853e19f
--- /dev/null
+++ b/hosting/kubernetes/budibase/templates/_helpers.tpl
@@ -0,0 +1,83 @@
+{{/*
+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 -}}
+
+{{/*
+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.
+*/}}
+{{- 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/alb-ingress.yaml b/hosting/kubernetes/budibase/templates/alb-ingress.yaml
new file mode 100644
index 0000000000..ea3bd674d5
--- /dev/null
+++ b/hosting/kubernetes/budibase/templates/alb-ingress.yaml
@@ -0,0 +1,35 @@
+{{- if .Values.ingress.aws }}
+apiVersion: networking.k8s.io/v1
+kind: Ingress
+metadata:
+ name: ingress-budibase
+ annotations:
+ kubernetes.io/ingress.class: alb
+ alb.ingress.kubernetes.io/scheme: internet-facing
+ alb.ingress.kubernetes.io/target-type: ip
+ {{- if .Values.ingress.certificateArn }}
+ alb.ingress.kubernetes.io/actions.ssl-redirect: '{"Type": "redirect", "RedirectConfig": { "Protocol": "HTTPS", "Port": "443", "StatusCode": "HTTP_301"}}'
+ alb.ingress.kubernetes.io/listen-ports: '[{"HTTP": 80}, {"HTTPS":443}]'
+ alb.ingress.kubernetes.io/certificate-arn: {{ .Values.ingress.certificateArn }}
+ {{- end }}
+spec:
+ rules:
+ - http:
+ paths:
+ {{- if .Values.ingress.certificateArn }}
+ - path: /
+ pathType: Prefix
+ backend:
+ service:
+ name: ssl-redirect
+ port:
+ name: use-annotation
+ {{- end }}
+ - path: /
+ pathType: Prefix
+ backend:
+ service:
+ name: proxy-service
+ port:
+ number: {{ .Values.services.proxy.port }}
+{{- end }}
\ No newline at end of file
diff --git a/hosting/kubernetes/budibase/templates/app-service-deployment.yaml b/hosting/kubernetes/budibase/templates/app-service-deployment.yaml
new file mode 100644
index 0000000000..b101ab7854
--- /dev/null
+++ b/hosting/kubernetes/budibase/templates/app-service-deployment.yaml
@@ -0,0 +1,105 @@
+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
+ {{ if .Values.services.couchdb.url }}
+ value: {{ .Values.services.couchdb.url }}
+ {{ else }}
+ 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
+ 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 }}
+ {{ if .Values.services.objectStore.region }}
+ - name: AWS_REGION
+ value: {{ .Values.services.objectStore.region }}
+ {{ end }}
+ - 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
+ {{ 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
+ {{ 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:{{ .Values.services.worker.port }}
+ image: budibase/apps
+ imagePullPolicy: Always
+ name: bbapps
+ ports:
+ - containerPort: {{ .Values.services.apps.port }}
+ resources: {}
+ restartPolicy: Always
+ serviceAccountName: ""
+status: {}
diff --git a/hosting/kubernetes/budibase/templates/app-service-service.yaml b/hosting/kubernetes/budibase/templates/app-service-service.yaml
new file mode 100644
index 0000000000..5247b4de09
--- /dev/null
+++ b/hosting/kubernetes/budibase/templates/app-service-service.yaml
@@ -0,0 +1,19 @@
+apiVersion: v1
+kind: Service
+metadata:
+ annotations:
+ kompose.cmd: kompose convert
+ kompose.version: 1.21.0 (992df58d8)
+ creationTimestamp: null
+ labels:
+ io.kompose.service: app-service
+ name: app-service
+spec:
+ ports:
+ - name: {{ .Values.services.apps.port | quote }}
+ port: {{ .Values.services.apps.port }}
+ targetPort: {{ .Values.services.apps.port }}
+ selector:
+ io.kompose.service: app-service
+status:
+ loadBalancer: {}
diff --git a/hosting/kubernetes/budibase/templates/hpa.yaml b/hosting/kubernetes/budibase/templates/hpa.yaml
new file mode 100644
index 0000000000..2f901b4664
--- /dev/null
+++ b/hosting/kubernetes/budibase/templates/hpa.yaml
@@ -0,0 +1,28 @@
+{{- if .Values.autoscaling.enabled }}
+apiVersion: autoscaling/v2beta1
+kind: HorizontalPodAutoscaler
+metadata:
+ name: {{ include "budibase.fullname" . }}
+ labels:
+ {{- include "budibase.labels" . | nindent 4 }}
+spec:
+ scaleTargetRef:
+ apiVersion: apps/v1
+ kind: Deployment
+ name: {{ include "budibase.fullname" . }}
+ minReplicas: {{ .Values.autoscaling.minReplicas }}
+ maxReplicas: {{ .Values.autoscaling.maxReplicas }}
+ metrics:
+ {{- if .Values.autoscaling.targetCPUUtilizationPercentage }}
+ - type: Resource
+ resource:
+ name: cpu
+ targetAverageUtilization: {{ .Values.autoscaling.targetCPUUtilizationPercentage }}
+ {{- end }}
+ {{- if .Values.autoscaling.targetMemoryUtilizationPercentage }}
+ - type: Resource
+ resource:
+ name: memory
+ targetAverageUtilization: {{ .Values.autoscaling.targetMemoryUtilizationPercentage }}
+ {{- end }}
+{{- end }}
diff --git a/hosting/kubernetes/budibase/templates/ingress.yaml b/hosting/kubernetes/budibase/templates/ingress.yaml
new file mode 100644
index 0000000000..4de295e18a
--- /dev/null
+++ b/hosting/kubernetes/budibase/templates/ingress.yaml
@@ -0,0 +1,61 @@
+{{- if .Values.ingress.enabled -}}
+{{- $fullName := include "budibase.fullname" . -}}
+{{- $svcPort := .Values.service.port -}}
+{{- if and .Values.ingress.className (not (semverCompare ">=1.18-0" .Capabilities.KubeVersion.GitVersion)) }}
+ {{- if not (hasKey .Values.ingress.annotations "kubernetes.io/ingress.class") }}
+ {{- $_ := set .Values.ingress.annotations "kubernetes.io/ingress.class" .Values.ingress.className}}
+ {{- end }}
+{{- end }}
+{{- if semverCompare ">=1.19-0" .Capabilities.KubeVersion.GitVersion -}}
+apiVersion: networking.k8s.io/v1
+{{- else if semverCompare ">=1.14-0" .Capabilities.KubeVersion.GitVersion -}}
+apiVersion: networking.k8s.io/v1beta1
+{{- else -}}
+apiVersion: extensions/v1beta1
+{{- end }}
+kind: Ingress
+metadata:
+ name: {{ $fullName }}
+ labels:
+ {{- include "budibase.labels" . | nindent 4 }}
+ {{- with .Values.ingress.annotations }}
+ annotations:
+ {{- toYaml . | nindent 4 }}
+ {{- end }}
+spec:
+ {{- if and .Values.ingress.className (semverCompare ">=1.18-0" .Capabilities.KubeVersion.GitVersion) }}
+ ingressClassName: {{ .Values.ingress.className }}
+ {{- end }}
+ {{- if .Values.ingress.tls }}
+ tls:
+ {{- range .Values.ingress.tls }}
+ - hosts:
+ {{- range .hosts }}
+ - {{ . | quote }}
+ {{- end }}
+ secretName: {{ .secretName }}
+ {{- end }}
+ {{- end }}
+ rules:
+ {{- range .Values.ingress.hosts }}
+ - host: {{ .host | quote }}
+ http:
+ paths:
+ {{- range .paths }}
+ - path: {{ .path }}
+ {{- if and .pathType (semverCompare ">=1.18-0" $.Capabilities.KubeVersion.GitVersion) }}
+ pathType: {{ .pathType }}
+ {{- end }}
+ backend:
+ {{- if semverCompare ">=1.19-0" $.Capabilities.KubeVersion.GitVersion }}
+ service:
+ name: {{ .backend.service.name }}
+ port:
+ number: {{ .backend.service.port.number }}
+ {{- else }}
+ serviceName: {{ .backend.service.name }}
+ servicePort: {{ .backend.service.port.number }}
+ {{- end }}
+ {{- end }}
+ {{- end }}
+{{- end }}
diff --git a/hosting/kubernetes/budibase/templates/minio-data-persistentvolumeclaim.yaml b/hosting/kubernetes/budibase/templates/minio-data-persistentvolumeclaim.yaml
new file mode 100644
index 0000000000..d122ad0a3e
--- /dev/null
+++ b/hosting/kubernetes/budibase/templates/minio-data-persistentvolumeclaim.yaml
@@ -0,0 +1,16 @@
+{{- if .Values.services.objectStore.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.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
new file mode 100644
index 0000000000..a23d0c1d89
--- /dev/null
+++ b/hosting/kubernetes/budibase/templates/minio-service-deployment.yaml
@@ -0,0 +1,70 @@
+{{- if .Values.services.objectStore.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.objectStore.browser | 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
+ 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.objectStore.port }}
+ resources: {}
+ volumeMounts:
+ - mountPath: /data
+ name: minio-data
+ restartPolicy: Always
+ serviceAccountName: ""
+ volumes:
+ - name: minio-data
+ persistentVolumeClaim:
+ claimName: minio-data
+status: {}
+{{- end }}
\ No newline at end of file
diff --git a/hosting/kubernetes/budibase/templates/minio-service-service.yaml b/hosting/kubernetes/budibase/templates/minio-service-service.yaml
new file mode 100644
index 0000000000..cfdb22002b
--- /dev/null
+++ b/hosting/kubernetes/budibase/templates/minio-service-service.yaml
@@ -0,0 +1,21 @@
+{{- if .Values.services.objectStore.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.objectStore.port | quote }}
+ port: {{ .Values.services.objectStore.port }}
+ targetPort: {{ .Values.services.objectStore.port }}
+ selector:
+ io.kompose.service: minio-service
+status:
+ loadBalancer: {}
+{{- end }}
\ No newline at end of file
diff --git a/hosting/kubernetes/budibase/templates/proxy-service-deployment.yaml b/hosting/kubernetes/budibase/templates/proxy-service-deployment.yaml
new file mode 100644
index 0000000000..0f802da843
--- /dev/null
+++ b/hosting/kubernetes/budibase/templates/proxy-service-deployment.yaml
@@ -0,0 +1,38 @@
+apiVersion: apps/v1
+kind: Deployment
+metadata:
+ annotations:
+ kompose.cmd: kompose convert
+ kompose.version: 1.21.0 (992df58d8)
+ creationTimestamp: null
+ labels:
+ app.kubernetes.io/name: budibase-proxy
+ name: proxy-service
+spec:
+ replicas: {{ .Values.services.proxy.replicaCount }}
+ selector:
+ matchLabels:
+ app.kubernetes.io/name: budibase-proxy
+ strategy:
+ type: Recreate
+ template:
+ metadata:
+ annotations:
+ kompose.cmd: kompose convert
+ kompose.version: 1.21.0 (992df58d8)
+ creationTimestamp: null
+ labels:
+ app.kubernetes.io/name: budibase-proxy
+ spec:
+ containers:
+ - image: budibase/proxy
+ imagePullPolicy: ""
+ name: proxy-service
+ ports:
+ - containerPort: {{ .Values.services.proxy.port }}
+ resources: {}
+ volumeMounts:
+ restartPolicy: Always
+ serviceAccountName: ""
+ volumes:
+status: {}
diff --git a/hosting/kubernetes/budibase/templates/proxy-service-service.yaml b/hosting/kubernetes/budibase/templates/proxy-service-service.yaml
new file mode 100644
index 0000000000..8f14d97862
--- /dev/null
+++ b/hosting/kubernetes/budibase/templates/proxy-service-service.yaml
@@ -0,0 +1,20 @@
+apiVersion: v1
+kind: Service
+metadata:
+ annotations:
+ kompose.cmd: kompose convert
+ kompose.version: 1.21.0 (992df58d8)
+ creationTimestamp: null
+ labels:
+ app.kubernetes.io/name: budibase-proxy
+ name: proxy-service
+spec:
+ type: NodePort
+ ports:
+ - port: {{ .Values.services.proxy.port }}
+ targetPort: {{ .Values.services.proxy.port }}
+ protocol: TCP
+ selector:
+ app.kubernetes.io/name: budibase-proxy
+status:
+ loadBalancer: {}
diff --git a/hosting/kubernetes/budibase/templates/redis-data-persistentvolumeclaim.yaml b/hosting/kubernetes/budibase/templates/redis-data-persistentvolumeclaim.yaml
new file mode 100644
index 0000000000..2cb5ee8eab
--- /dev/null
+++ b/hosting/kubernetes/budibase/templates/redis-data-persistentvolumeclaim.yaml
@@ -0,0 +1,16 @@
+{{- if .Values.services.redis.enabled }}
+apiVersion: v1
+kind: PersistentVolumeClaim
+metadata:
+ creationTimestamp: null
+ labels:
+ io.kompose.service: redis-data
+ name: redis-data
+spec:
+ accessModes:
+ - ReadWriteOnce
+ resources:
+ requests:
+ storage: {{ .Values.services.redis.storage }}
+status: {}
+{{- end }}
\ No newline at end of file
diff --git a/hosting/kubernetes/budibase/templates/redis-service-deployment.yaml b/hosting/kubernetes/budibase/templates/redis-service-deployment.yaml
new file mode 100644
index 0000000000..9235b0b11d
--- /dev/null
+++ b/hosting/kubernetes/budibase/templates/redis-service-deployment.yaml
@@ -0,0 +1,49 @@
+{{- if .Values.services.redis.enabled }}
+apiVersion: apps/v1
+kind: Deployment
+metadata:
+ annotations:
+ kompose.cmd: kompose convert
+ kompose.version: 1.21.0 (992df58d8)
+ creationTimestamp: null
+ labels:
+ io.kompose.service: redis-service
+ name: redis-service
+spec:
+ replicas: {{ .Values.services.redis.replicaCount }}
+ selector:
+ matchLabels:
+ io.kompose.service: redis-service
+ strategy:
+ type: Recreate
+ template:
+ metadata:
+ annotations:
+ kompose.cmd: kompose convert
+ kompose.version: 1.21.0 (992df58d8)
+ creationTimestamp: null
+ labels:
+ io.kompose.service: redis-service
+ spec:
+ containers:
+ - args:
+ - redis-server
+ - --requirepass
+ - {{ .Values.services.redis.password }}
+ image: redis
+ imagePullPolicy: ""
+ name: redis-service
+ ports:
+ - containerPort: {{ .Values.services.redis.port }}
+ resources: {}
+ volumeMounts:
+ - mountPath: /data
+ name: redis-data
+ restartPolicy: Always
+ serviceAccountName: ""
+ volumes:
+ - name: redis-data
+ persistentVolumeClaim:
+ claimName: redis-data
+status: {}
+{{- end }}
\ No newline at end of file
diff --git a/hosting/kubernetes/budibase/templates/redis-service-service.yaml b/hosting/kubernetes/budibase/templates/redis-service-service.yaml
new file mode 100644
index 0000000000..55ca40ed88
--- /dev/null
+++ b/hosting/kubernetes/budibase/templates/redis-service-service.yaml
@@ -0,0 +1,21 @@
+{{- if .Values.services.redis.enabled }}
+apiVersion: v1
+kind: Service
+metadata:
+ annotations:
+ kompose.cmd: kompose convert
+ kompose.version: 1.21.0 (992df58d8)
+ creationTimestamp: null
+ labels:
+ io.kompose.service: redis-service
+ name: redis-service
+spec:
+ ports:
+ - name: {{ .Values.services.redis.port | quote }}
+ port: {{ .Values.services.redis.port }}
+ targetPort: {{ .Values.services.redis.port }}
+ selector:
+ io.kompose.service: redis-service
+status:
+ loadBalancer: {}
+{{- end }}
\ No newline at end of file
diff --git a/hosting/kubernetes/budibase/templates/secrets.yaml b/hosting/kubernetes/budibase/templates/secrets.yaml
new file mode 100644
index 0000000000..1c0a914ed3
--- /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.objectStore.accessKey }}
+ objectStoreSecret: {{ template "budibase.defaultsecret" .Values.services.objectStore.secretKey }}
+{{- end -}}
diff --git a/hosting/kubernetes/budibase/templates/service.yaml b/hosting/kubernetes/budibase/templates/service.yaml
new file mode 100644
index 0000000000..be4d932b59
--- /dev/null
+++ b/hosting/kubernetes/budibase/templates/service.yaml
@@ -0,0 +1,15 @@
+apiVersion: v1
+kind: Service
+metadata:
+ name: {{ include "budibase.fullname" . }}
+ labels:
+ {{- include "budibase.labels" . | nindent 4 }}
+spec:
+ type: {{ .Values.service.type }}
+ ports:
+ - port: {{ .Values.service.port }}
+ targetPort: http
+ protocol: TCP
+ name: http
+ selector:
+ {{- include "budibase.selectorLabels" . | nindent 4 }}
\ No newline at end of file
diff --git a/hosting/kubernetes/budibase/templates/serviceaccount.yaml b/hosting/kubernetes/budibase/templates/serviceaccount.yaml
new file mode 100644
index 0000000000..1aa1020088
--- /dev/null
+++ b/hosting/kubernetes/budibase/templates/serviceaccount.yaml
@@ -0,0 +1,12 @@
+{{- if .Values.serviceAccount.create -}}
+apiVersion: v1
+kind: ServiceAccount
+metadata:
+ name: {{ include "budibase.serviceAccountName" . }}
+ labels:
+ {{- include "budibase.labels" . | nindent 4 }}
+ {{- with .Values.serviceAccount.annotations }}
+ annotations:
+ {{- toYaml . | nindent 4 }}
+ {{- end }}
+{{- end }}
diff --git a/hosting/kubernetes/budibase/templates/tests/test-connection.yaml b/hosting/kubernetes/budibase/templates/tests/test-connection.yaml
new file mode 100644
index 0000000000..ecd1f361de
--- /dev/null
+++ b/hosting/kubernetes/budibase/templates/tests/test-connection.yaml
@@ -0,0 +1,15 @@
+apiVersion: v1
+kind: Pod
+metadata:
+ name: "{{ include "budibase.fullname" . }}-test-connection"
+ labels:
+ {{- include "budibase.labels" . | nindent 4 }}
+ annotations:
+ "helm.sh/hook": test
+spec:
+ containers:
+ - name: wget
+ image: busybox
+ command: ['wget']
+ args: ['{{ include "budibase.fullname" . }}:{{ .Values.service.port }}']
+ restartPolicy: Never
diff --git a/hosting/kubernetes/budibase/templates/worker-service-deployment.yaml b/hosting/kubernetes/budibase/templates/worker-service-deployment.yaml
new file mode 100644
index 0000000000..703d59c075
--- /dev/null
+++ b/hosting/kubernetes/budibase/templates/worker-service-deployment.yaml
@@ -0,0 +1,98 @@
+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_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
+ {{ 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:
+ name: {{ template "budibase.fullname" . }}
+ key: internalApiKey
+ - name: JWT_SECRET
+ valueFrom:
+ secretKeyRef:
+ name: {{ template "budibase.fullname" . }}
+ key: jwtSecret
+ {{ if .Values.services.objectStore.region }}
+ - name: AWS_REGION
+ value: {{ .Values.services.objectStore.region }}
+ {{ end }}
+ - 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
+ {{ 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
+ {{ 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
+ imagePullPolicy: Always
+ name: bbworker
+ ports:
+ - containerPort: {{ .Values.services.worker.port }}
+ resources: {}
+ restartPolicy: Always
+ serviceAccountName: ""
+status: {}
diff --git a/hosting/kubernetes/budibase/templates/worker-service-service.yaml b/hosting/kubernetes/budibase/templates/worker-service-service.yaml
new file mode 100644
index 0000000000..a79ba1e04b
--- /dev/null
+++ b/hosting/kubernetes/budibase/templates/worker-service-service.yaml
@@ -0,0 +1,19 @@
+apiVersion: v1
+kind: Service
+metadata:
+ annotations:
+ kompose.cmd: kompose convert
+ kompose.version: 1.21.0 (992df58d8)
+ creationTimestamp: null
+ labels:
+ io.kompose.service: worker-service
+ name: worker-service
+spec:
+ ports:
+ - name: {{ .Values.services.worker.port | quote }}
+ port: {{ .Values.services.worker.port }}
+ targetPort: {{ .Values.services.worker.port }}
+ selector:
+ io.kompose.service: worker-service
+status:
+ loadBalancer: {}
diff --git a/hosting/kubernetes/budibase/values.yaml b/hosting/kubernetes/budibase/values.yaml
new file mode 100644
index 0000000000..30594f95e3
--- /dev/null
+++ b/hosting/kubernetes/budibase/values.yaml
@@ -0,0 +1,142 @@
+# 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
+ nginx: true
+ certificateArn: ""
+ className: ""
+ annotations:
+ kubernetes.io/ingress.class: nginx
+ hosts:
+ - host: # change if using custom domain
+ 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
+ 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
+
+ apps:
+ port: 4002
+ replicaCount: 1
+ logLevel: info
+
+ worker:
+ port: 4001
+ replicaCount: 1
+
+ couchdb:
+ enabled: true
+ replicaCount: 3
+ url: "" # only change if pointing to existing couch server
+ user: "" # only change if pointing to existing couch server
+ password: "" # only change if pointing to existing couch server
+ port: 5984
+ storage: 100Mi
+
+ redis:
+ enabled: true # disable if using external redis
+ port: 6379
+ replicaCount: 1
+ url: "" # only change if pointing to existing redis cluster and enabled: false
+ password: "budibase" # recommended to override if using built-in redis
+ storage: 100Mi
+
+ objectStore:
+ minio: true
+ browser: true
+ 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
+ region: "" # AWS_REGION if using S3 or existing minio secret
+ url: "" # only change if pointing to existing minio cluster and minio: false
+ storage: 100Mi
+
diff --git a/hosting/kubernetes/envoy/Dockerfile b/hosting/kubernetes/envoy/Dockerfile
new file mode 100644
index 0000000000..96334fa723
--- /dev/null
+++ b/hosting/kubernetes/envoy/Dockerfile
@@ -0,0 +1,4 @@
+FROM envoyproxy/envoy:v1.16-latest
+COPY envoy.yaml /etc/envoy/envoy.yaml
+RUN chmod go+r /etc/envoy/envoy.yaml
+
diff --git a/hosting/kubernetes/envoy/envoy.yaml b/hosting/kubernetes/envoy/envoy.yaml
new file mode 100644
index 0000000000..0e7859d887
--- /dev/null
+++ b/hosting/kubernetes/envoy/envoy.yaml
@@ -0,0 +1,125 @@
+static_resources:
+ listeners:
+ - name: main_listener
+ address:
+ socket_address: { address: 0.0.0.0, port_value: 10000 }
+ filter_chains:
+ - filters:
+ - name: envoy.filters.network.http_connection_manager
+ typed_config:
+ "@type": type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager
+ stat_prefix: ingress
+ codec_type: auto
+ route_config:
+ name: local_route
+ virtual_hosts:
+ - name: local_services
+ domains: ["*"]
+ routes:
+ - match: { prefix: "/app/" }
+ route:
+ cluster: app-service
+ prefix_rewrite: "/"
+
+ - match: { prefix: "/builder/" }
+ route:
+ cluster: app-service
+
+ - match: { prefix: "/builder" }
+ route:
+ cluster: app-service
+
+ - match: { prefix: "/app_" }
+ route:
+ cluster: app-service
+
+ # special case for worker admin API
+ - match: { prefix: "/api/admin/" }
+ route:
+ cluster: worker-service
+
+ - match: { path: "/" }
+ route:
+ cluster: app-service
+
+ # special case for when API requests are made, can just forward, not to minio
+ - match: { prefix: "/api/" }
+ route:
+ cluster: app-service
+
+ - match: { prefix: "/worker/" }
+ route:
+ cluster: worker-service
+ prefix_rewrite: "/"
+
+ - match: { prefix: "/db/" }
+ route:
+ cluster: couchdb-service
+ prefix_rewrite: "/"
+
+ # minio is on the default route because this works
+ # best, minio + AWS SDK doesn't handle path proxy
+ - match: { prefix: "/" }
+ route:
+ cluster: minio-service
+
+ http_filters:
+ - name: envoy.filters.http.router
+
+ clusters:
+ - name: app-service
+ connect_timeout: 0.25s
+ type: strict_dns
+ lb_policy: round_robin
+ load_assignment:
+ cluster_name: app-service
+ endpoints:
+ - lb_endpoints:
+ - endpoint:
+ address:
+ socket_address:
+ address: app-service.budibase.svc.cluster.local
+ port_value: 4002
+
+ - name: minio-service
+ connect_timeout: 0.25s
+ type: strict_dns
+ lb_policy: round_robin
+ load_assignment:
+ cluster_name: minio-service
+ endpoints:
+ - lb_endpoints:
+ - endpoint:
+ address:
+ socket_address:
+ address: minio-service.budibase.svc.cluster.local
+ port_value: 9000
+
+ - name: worker-service
+ connect_timeout: 0.25s
+ type: strict_dns
+ lb_policy: round_robin
+ load_assignment:
+ cluster_name: worker-service
+ endpoints:
+ - lb_endpoints:
+ - endpoint:
+ address:
+ socket_address:
+ address: worker-service.budibase.svc.cluster.local
+ port_value: 4001
+
+ - name: couchdb-service
+ connect_timeout: 0.25s
+ type: strict_dns
+ lb_policy: round_robin
+ load_assignment:
+ cluster_name: couchdb-service
+ endpoints:
+ - lb_endpoints:
+ - endpoint:
+ address:
+ socket_address:
+ address: couchdb-service.budibase.svc.cluster.local
+ port_value: 5984
+
diff --git a/hosting/scripts/linux/release-to-docker-hub.sh b/hosting/scripts/linux/release-to-docker-hub.sh
index b3c380f729..ccb5fa09a0 100755
--- a/hosting/scripts/linux/release-to-docker-hub.sh
+++ b/hosting/scripts/linux/release-to-docker-hub.sh
@@ -1,12 +1,23 @@
#!/bin/bash
tag=$1
-tag=${tag:-latest}
+production=$2
-echo "Tagging images with SHA: $GITHUB_SHA and tag: $tag"
+if [[ ! "$tag" ]]; then
+ echo "No tag present. You must pass a tag to this script"
+ exit 1
+fi
+
+echo "Tagging images with tag: $tag"
docker tag app-service budibase/apps:$tag
docker tag worker-service budibase/worker:$tag
-docker push budibase/apps:$tag
-docker push budibase/worker:$tag
+if [[ "$production" ]]; then
+ echo "Production Deployment. Tagging latest.."
+ docker tag app-service budibase/apps:latest
+ docker tag worker-service budibase/worker:latest
+fi
+
+docker push --all-tags budibase/apps
+docker push --all-tags budibase/worker
diff --git a/lerna.json b/lerna.json
index 4e586c3bf3..b00fa15ba4 100644
--- a/lerna.json
+++ b/lerna.json
@@ -1,5 +1,5 @@
{
- "version": "0.9.105-alpha.22",
+ "version": "0.9.115",
"npmClient": "yarn",
"packages": [
"packages/*"
diff --git a/package.json b/package.json
index 5532cf87a9..05c69e54dc 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",
@@ -42,9 +43,11 @@
"lint:fix": "yarn run lint:fix:ts && yarn run lint:fix:prettier && yarn run lint:fix:eslint",
"test:e2e": "lerna run cy:test",
"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": "lerna run build:docker && cd hosting/scripts/linux/ && ./release-to-docker-hub.sh $BUDIBASE_RELEASE_VERSION release && 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"
+ "multi:disable": "lerna run multi:disable",
+ "postinstall": "husky install"
}
}
diff --git a/packages/auth/package.json b/packages/auth/package.json
index 8331366945..898a209171 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.115",
"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 b134f98c04..47b3ece07d 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.115",
"license": "AGPL-3.0",
"svelte": "src/index.js",
"module": "dist/bbui.es.js",
@@ -65,6 +65,7 @@
"@spectrum-css/search": "^3.0.2",
"@spectrum-css/sidenav": "^3.0.2",
"@spectrum-css/statuslight": "^3.0.2",
+ "@spectrum-css/stepper": "^3.0.3",
"@spectrum-css/switch": "^1.0.2",
"@spectrum-css/table": "^3.0.1",
"@spectrum-css/tabs": "^3.0.1",
diff --git a/packages/bbui/src/Form/Core/Multiselect.svelte b/packages/bbui/src/Form/Core/Multiselect.svelte
index 94d4f2b768..3eb1add267 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 de7bfb02b8..8acbc4e1a6 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 3a2daf25cf..413b11dd34 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/Core/Stepper.svelte b/packages/bbui/src/Form/Core/Stepper.svelte
new file mode 100644
index 0000000000..895dc2d3de
--- /dev/null
+++ b/packages/bbui/src/Form/Core/Stepper.svelte
@@ -0,0 +1,172 @@
+
+
+
+ {#if error}
+
+ {/if}
+
+
+
+
+
+
+
+
+
+
+
diff --git a/packages/bbui/src/Form/Core/index.js b/packages/bbui/src/Form/Core/index.js
index a31c5941ec..440c4a1b15 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/Multiselect.svelte b/packages/bbui/src/Form/Multiselect.svelte
index 1bb0039914..957dcccddf 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 eecc719a9a..b6547c0a98 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/bbui/src/Form/Stepper.svelte b/packages/bbui/src/Form/Stepper.svelte
new file mode 100644
index 0000000000..9eff2e368e
--- /dev/null
+++ b/packages/bbui/src/Form/Stepper.svelte
@@ -0,0 +1,45 @@
+
+
+
+
+
diff --git a/packages/bbui/src/Menu/Item.svelte b/packages/bbui/src/Menu/Item.svelte
index b6ab8862df..a5609683a8 100644
--- a/packages/bbui/src/Menu/Item.svelte
+++ b/packages/bbui/src/Menu/Item.svelte
@@ -19,7 +19,7 @@
{
const feedbackMilliseconds = feedbackHours * 60 * 60 * 1000
return Date.now() > sinceDate + feedbackMilliseconds
}
+
function submitFeedback(values) {
if (!analyticsEnabled || !process.env.POSTHOG_TOKEN) return
localStorage.setItem(FEEDBACK_SUBMITTED_KEY, Date.now())
diff --git a/packages/builder/src/builderStore/dataBinding.js b/packages/builder/src/builderStore/dataBinding.js
index dc995d611c..00eaaf0249 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
+ const contexts = Array.isArray(def.context) ? def.context : [def.context]
- 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) {
+ // 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/builderStore/store/frontend.js b/packages/builder/src/builderStore/store/frontend.js
index 1924ae07a2..192ade9e5d 100644
--- a/packages/builder/src/builderStore/store/frontend.js
+++ b/packages/builder/src/builderStore/store/frontend.js
@@ -20,7 +20,12 @@ import { fetchComponentLibDefinitions } from "../loadComponentLibraries"
import api from "../api"
import { FrontendTypes } from "constants"
import analytics from "analytics"
-import { findComponentType, findComponentParent } from "../storeUtils"
+import {
+ findComponentType,
+ findComponentParent,
+ findClosestMatchingComponent,
+ findAllMatchingComponents,
+} from "../storeUtils"
import { uuid } from "../uuid"
import { removeBindings } from "../dataBinding"
@@ -334,6 +339,18 @@ export const getFrontendStore = () => {
if (definition.hasChildren) {
extras._children = []
}
+ if (componentName.endsWith("/formstep")) {
+ const parentForm = findClosestMatchingComponent(
+ get(currentAsset).props,
+ get(selectedComponent)._id,
+ component => component._component.endsWith("/form")
+ )
+ const formSteps = findAllMatchingComponents(parentForm, component =>
+ component._component.endsWith("/formstep")
+ )
+ extras.step = formSteps.length + 1
+ extras._instanceName = `Step ${formSteps.length + 1}`
+ }
return {
_id: uuid(),
diff --git a/packages/builder/src/builderStore/store/screenTemplates/rowDetailScreen.js b/packages/builder/src/builderStore/store/screenTemplates/rowDetailScreen.js
index a1a1e17fd5..ec737fe36b 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,
})
@@ -94,6 +94,7 @@ const createScreen = table => {
.instanceName("Repeater")
.customProps({
dataProvider: `{{ literal ${makePropSafe(provider._json._id)} }}`,
+ noRowsMessage: "We couldn't find a row to display",
})
const form = makeMainForm()
diff --git a/packages/builder/src/components/backend/DataTable/RowFieldControl.svelte b/packages/builder/src/components/backend/DataTable/RowFieldControl.svelte
index 1fa8b1ac06..e82c55679a 100644
--- a/packages/builder/src/components/backend/DataTable/RowFieldControl.svelte
+++ b/packages/builder/src/components/backend/DataTable/RowFieldControl.svelte
@@ -26,6 +26,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/DatasourceNavigator/modals/UpdateDatasourceModal.svelte b/packages/builder/src/components/backend/DatasourceNavigator/modals/UpdateDatasourceModal.svelte
index 561548fb59..f93af59a38 100644
--- a/packages/builder/src/components/backend/DatasourceNavigator/modals/UpdateDatasourceModal.svelte
+++ b/packages/builder/src/components/backend/DatasourceNavigator/modals/UpdateDatasourceModal.svelte
@@ -42,9 +42,9 @@
diff --git a/packages/builder/src/components/backend/DatasourceNavigator/popovers/EditDatasourcePopover.svelte b/packages/builder/src/components/backend/DatasourceNavigator/popovers/EditDatasourcePopover.svelte
index 7f2f104278..d837535267 100644
--- a/packages/builder/src/components/backend/DatasourceNavigator/popovers/EditDatasourcePopover.svelte
+++ b/packages/builder/src/components/backend/DatasourceNavigator/popovers/EditDatasourcePopover.svelte
@@ -26,7 +26,7 @@