1
0
Fork 0
mirror of synced 2024-07-04 05:50:57 +12:00

Merge branch 'master' into fix-deployment-instability

This commit is contained in:
Sam Rose 2023-12-06 10:09:17 +00:00 committed by GitHub
commit 79819795c1
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
50 changed files with 1308 additions and 645 deletions

View file

@ -1,9 +1,6 @@
dependencies:
- name: couchdb
repository: https://apache.github.io/couchdb-helm
version: 3.3.4
- name: ingress-nginx
repository: https://kubernetes.github.io/ingress-nginx
version: 4.0.13
digest: sha256:20892705c2d8e64c98257d181063a514ac55013e2b43399a6e54868a97f97845
generated: "2021-12-30T18:55:30.878411Z"
version: 4.3.0
digest: sha256:94449a7f195b186f5af33ec5aa66d58b36bede240fae710f021ca87837b30606
generated: "2023-11-20T17:43:02.777596Z"

View file

@ -17,10 +17,6 @@ version: 0.0.0
appVersion: 0.0.0
dependencies:
- name: couchdb
version: 3.3.4
version: 4.3.0
repository: https://apache.github.io/couchdb-helm
condition: services.couchdb.enabled
- name: ingress-nginx
version: 4.0.13
repository: https://kubernetes.github.io/ingress-nginx
condition: ingress.nginx

View file

@ -1,39 +1,216 @@
# Budibase
# 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.
Budibase is an open source low-code platform, helping thousands of teams build apps for their workplace in minutes.
## Prerequisites
- helm v3 or above
- `helm` v3 or above
- Kubernetes 1.4+
- PV provisioner support in the underlying infrastructure (with persistence storage enabled)
- A storage controller (if you want to use persistent storage)
- An ingress controller (if you want to define an `Ingress` resource)
- `metrics-server` (if you want to make use of horizontal pod autoscaling)
## Installing the Chart
## Chart dependencies
To install the chart with the release name `budi-release`:
This chart depends on the official Apache CouchDB chart. You can see its
documentation here:
<https://github.com/apache/couchdb-helm/tree/couchdb-4.3.0/couchdb>.
## Upgrading
### `2.x` to `3.0.0`
We made a number of breaking changes in this release to make the chart more
idiomatic and easier to use.
1. We no longer bundle `ingress-nginx`. If you were relying on this to supply
an ingress controller to your cluster, you will now need to deploy that
separately. You'll find guidance for that here:
<https://kubernetes.github.io/ingress-nginx/>.
2. We've upgraded the version of the [CouchDB chart](https://github.com/apache/couchdb-helm)
we use from `3.3.4` to `4.3.0`. The primary motivation for this was to align
the CouchDB chart used with the CouchDB version used, which has also updated
from 3.1.1 to 3.2.1. Additionally, we're moving away from the official CouchDB
to one we're building ourselves.
3. We've separated out the supplied AWS ALB ingress resource for those deploying
into EKS. Where previously you enabled this by setting `ingress.enabled: false`
and `ingress.aws: true`, you now set `awsAlbIngress.enabled: true` and all
configuration for it is under `awsAlbIngress`.
4. The `HorizontalPodAutoscaler` that was configured at `hpa.enabled: true` has
been split into 3 separate HPAs, one for each of `apps`, `worker`, and `proxy`.
They are configured at `services.{apps,worker,proxy}.autoscaling`.
## Installing
To install the chart from our repository:
```console
$ helm install budi-release .
$ helm repo add budibase https://budibase.github.io/budibase/
$ helm repo update
$ helm install --create-namespace --namespace budibase budibase budibase/budibase
```
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:
To install the chart from this repo:
```console
$ helm delete my-release
$ git clone git@github.com:budibase/budibase.git
$ cd budibase/charts/budibase
$ helm install --create-namespace --namespace budibase budibase .
```
## Example minimal configuration
Here's an example `values.yaml` that would get a Budibase instance running in a home
cluster using an nginx ingress controller and NFS as cluster storage (basically one of our
staff's homelabs).
<details>
```yaml
ingress:
enabled: true
className: "nginx"
hosts:
- host: budibase.local # set this to whatever DNS name you'd use
paths:
- backend:
service:
name: proxy-service
port:
number: 10000
path: /
pathType: Prefix
couchdb:
persistentVolume:
enabled: true
storageClass: "nfs-client"
adminPassword: admin
objectStore:
storageClass: "nfs-client"
redis:
storageClass: "nfs-client"
```
If you wanted to use this when bringing up Budibase in your own cluster, you could save it
to your hard disk and run the following:
```console
$ helm install --create-namespace --namespace budibase budibase . -f values.yaml
```
</details>
## Configuring
| Key | Type | Default | Description |
|-----|------|---------|-------------|
| affinity | object | `{}` | Sets the affinity for all pods created by this chart. Should not ordinarily need to be changed. See <https://kubernetes.io/docs/tasks/configure-pod-container/assign-pods-nodes-using-node-affinity/> for more information on affinity. |
| awsAlbIngress.certificateArn | string | `""` | If you're wanting to use HTTPS, you'll need to create an ACM certificate and specify the ARN here. |
| awsAlbIngress.enabled | bool | `false` | Whether to create an ALB Ingress resource pointing to the Budibase proxy. Requires the AWS ALB Ingress Controller. |
| couchdb.clusterSize | int | `1` | The number of replicas to run in the CouchDB cluster. We set this to 1 by default to make things simpler, but you can set it to 3 if you need a high-availability CouchDB cluster. |
| couchdb.couchdbConfig.couchdb.uuid | string | `"budibase-couchdb"` | Unique identifier for this CouchDB server instance. You shouldn't need to change this. |
| couchdb.image | object | `{}` | We use a custom CouchDB image for running Budibase and we don't support using any other CouchDB image. You shouldn't change this, and if you do we can't guarantee that Budibase will work. |
| globals.apiEncryptionKey | string | `""` | Used for encrypting API keys and environment variables when stored in the database. You don't need to set this if `createSecrets` is true. |
| globals.appVersion | string | `""` | The version of Budibase to deploy. Defaults to what's specified by {{ .Chart.AppVersion }}. Ends up being used as the image version tag for the apps, proxy, and worker images. |
| globals.automationMaxIterations | string | `"200"` | The maximum number of iterations allows for an automation loop step. You can read more about looping here: <https://docs.budibase.com/docs/looping>. |
| globals.budibaseEnv | string | `"PRODUCTION"` | Sets the environment variable BUDIBASE_ENVIRONMENT for the apps and worker pods. Should not ordinarily need to be changed. |
| globals.cookieDomain | string | `""` | Sets the domain attribute of the cookie that Budibase uses to store session information. See <https://developer.mozilla.org/en-US/docs/Web/HTTP/Cookies#define_where_cookies_are_sent> for details on why you might want to set this. |
| globals.createSecrets | bool | `true` | Create an internal API key, JWT secret, object store access key and secret, and store them in a Kubernetes `Secret`. |
| globals.enableAnalytics | string | `"1"` | Whether to enable analytics or not. You can read more about our analytics here: <https://docs.budibase.com/docs/analytics>. |
| globals.google | object | `{"clientId":"","secret":""}` | Google OAuth settings. These can also be set in the Budibase UI, see <https://docs.budibase.com/docs/sso-with-google> for details. |
| globals.google.clientId | string | `""` | Client ID of your Google OAuth app. |
| globals.google.secret | string | `""` | Client secret of your Google OAuth app. |
| globals.httpMigrations | string | `"0"` | Whether or not to enable doing data migrations over the HTTP API. If this is set to "0", migrations are run on startup. You shouldn't ordinarily need to change this. |
| globals.internalApiKey | string | `""` | API key used for internal Budibase API calls. You don't need to set this if `createSecrets` is true. |
| globals.internalApiKeyFallback | string | `""` | A fallback value for `internalApiKey`. If you're rotating your encryption key, you can set this to the old value for the duration of the rotation. |
| globals.jwtSecret | string | `""` | Secret used for signing JWTs. You don't need to set this if `createSecrets` is true. |
| globals.jwtSecretFallback | string | `""` | A fallback value for `jwtSecret`. If you're rotating your JWT secret, you can set this to the old value for the duration of the rotation. |
| globals.platformUrl | string | `""` | Set the `platformUrl` binding. You can also do this in Settings > Organisation if you are self-hosting. |
| globals.smtp.enabled | bool | `false` | Whether to enable SMTP or not. |
| globals.smtp.from | string | `""` | The email address to use in the "From:" field of emails sent by Budibase. |
| globals.smtp.host | string | `""` | The hostname of your SMTP server. |
| globals.smtp.password | string | `""` | The password to use when authenticating with your SMTP server. |
| globals.smtp.port | string | `"587"` | The port of your SMTP server. |
| globals.smtp.user | string | `""` | The username to use when authenticating with your SMTP server. |
| globals.tenantFeatureFlags | string | `"*:LICENSING,*:USER_GROUPS,*:ONBOARDING_TOUR"` | Sets what feature flags are enabled and for which tenants. Should not ordinarily need to be changed. |
| imagePullSecrets | list | `[]` | Passed to all pods created by this chart. Should not ordinarily need to be changed. |
| ingress.className | string | `""` | What ingress class to use. |
| ingress.enabled | bool | `true` | Whether to create an Ingress resource pointing to the Budibase proxy. |
| ingress.hosts | list | `[]` | Standard hosts block for the Ingress resource. Defaults to pointing to the Budibase proxy. |
| nameOverride | string | `""` | Override the name of the deploymen. Defaults to {{ .Chart.Name }}. |
| service.port | int | `10000` | Port to expose on the service. |
| service.type | string | `"ClusterIP"` | Service type for the service that points to the main Budibase proxy pod. |
| serviceAccount.annotations | object | `{}` | Annotations to add to the service account |
| serviceAccount.create | bool | `true` | Specifies whether a service account should be created |
| serviceAccount.name | string | `""` | The name of the service account to use. If not set and create is true, a name is generated using the fullname template |
| services.apps.autoscaling.enabled | bool | `false` | Whether to enable horizontal pod autoscaling for the apps service. |
| services.apps.autoscaling.maxReplicas | int | `10` | |
| services.apps.autoscaling.minReplicas | int | `1` | |
| services.apps.autoscaling.targetCPUUtilizationPercentage | int | `80` | Target CPU utilization percentage for the apps service. Note that for autoscaling to work, you will need to have metrics-server configured, and resources set for the apps pods. |
| services.apps.httpLogging | int | `1` | Whether or not to log HTTP requests to the apps service. |
| services.apps.livenessProbe | object | HTTP health checks. | Liveness probe configuration for apps pods. You shouldn't need to change this, but if you want to you can find more information here: <https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-startup-probes/> |
| services.apps.logLevel | string | `"info"` | The log level for the apps service. |
| services.apps.readinessProbe | object | HTTP health checks. | Readiness probe configuration for apps pods. You shouldn't need to change this, but if you want to you can find more information here: <https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-startup-probes/> |
| services.apps.replicaCount | int | `1` | The number of apps replicas to run. |
| services.apps.resources | object | `{}` | The resources to use for apps pods. See <https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/> for more information on how to set these. |
| services.apps.startupProbe | object | HTTP health checks. | Startup probe configuration for apps pods. You shouldn't need to change this, but if you want to you can find more information here: <https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-startup-probes/> |
| services.couchdb.backup.enabled | bool | `false` | Whether or not to enable periodic CouchDB backups. This works by replicating to another CouchDB instance. |
| services.couchdb.backup.interval | string | `""` | Backup interval in seconds |
| services.couchdb.backup.resources | object | `{}` | The resources to use for CouchDB backup pods. See <https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/> for more information on how to set these. |
| services.couchdb.backup.target | string | `""` | Target couchDB instance to back up to, either a hostname or an IP address. |
| services.couchdb.enabled | bool | `true` | Whether or not to spin up a CouchDB instance in your cluster. True by default, and the configuration for the CouchDB instance is under the `couchdb` key at the root of this file. You can see what options are available to you by looking at the official CouchDB Helm chart: <https://github.com/apache/couchdb-helm/tree/couchdb-4.3.0/couchdb>. |
| services.couchdb.port | int | `5984` | |
| services.dns | string | `"cluster.local"` | The DNS suffix to use for service discovery. You only need to change this if you've configured your cluster to use a different DNS suffix. |
| services.objectStore.accessKey | string | `""` | AWS_ACCESS_KEY if using S3 |
| services.objectStore.browser | bool | `true` | Whether to enable the Minio web console or not. If you're exposing Minio to the Internet (via a custom Ingress record, for example), you should set this to false. If you're only exposing Minio to your cluster, you can leave this as true. |
| services.objectStore.cloudfront.cdn | string | `""` | Set the url of a distribution to enable cloudfront. |
| services.objectStore.cloudfront.privateKey64 | string | `""` | Base64 encoded private key for the above public key. |
| services.objectStore.cloudfront.publicKeyId | string | `""` | ID of public key stored in cloudfront. |
| services.objectStore.minio | bool | `true` | Set to false if using another object store, such as S3. You will need to set `services.objectStore.url` to point to your bucket if you do this. |
| services.objectStore.region | string | `""` | AWS_REGION if using S3 |
| services.objectStore.resources | object | `{}` | The resources to use for Minio pods. See <https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/> for more information on how to set these. |
| services.objectStore.secretKey | string | `""` | AWS_SECRET_ACCESS_KEY if using S3 |
| services.objectStore.storage | string | `"100Mi"` | How much storage to give Minio in its PersistentVolumeClaim. |
| services.objectStore.storageClass | string | `""` | If defined, storageClassName: <storageClass> If set to "-", storageClassName: "", which disables dynamic provisioning If undefined (the default) or set to null, no storageClassName spec is set, choosing the default provisioner. |
| services.objectStore.url | string | `"http://minio-service:9000"` | URL to use for object storage. Only change this if you're using an external object store, such as S3. Remember to set `minio: false` if you do this. |
| services.proxy.autoscaling.enabled | bool | `false` | Whether to enable horizontal pod autoscaling for the proxy service. |
| services.proxy.autoscaling.maxReplicas | int | `10` | |
| services.proxy.autoscaling.minReplicas | int | `1` | |
| services.proxy.autoscaling.targetCPUUtilizationPercentage | int | `80` | Target CPU utilization percentage for the proxy service. Note that for autoscaling to work, you will need to have metrics-server configured, and resources set for the proxy pods. |
| services.proxy.livenessProbe | object | HTTP health checks. | Liveness probe configuration for proxy pods. You shouldn't need to change this, but if you want to you can find more information here: <https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-startup-probes/> |
| services.proxy.readinessProbe | object | HTTP health checks. | Readiness probe configuration for proxy pods. You shouldn't need to change this, but if you want to you can find more information here: <https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-startup-probes/> |
| services.proxy.replicaCount | int | `1` | The number of proxy replicas to run. |
| services.proxy.resources | object | `{}` | The resources to use for proxy pods. See <https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/> for more information on how to set these. |
| services.proxy.startupProbe | object | HTTP health checks. | Startup probe configuration for proxy pods. You shouldn't need to change this, but if you want to you can find more information here: <https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-startup-probes/> |
| services.redis.enabled | bool | `true` | Whether or not to deploy a Redis pod into your cluster. |
| services.redis.password | string | `"budibase"` | The password to use when connecting to Redis. It's recommended that you change this from the default if you're running Redis in-cluster. |
| services.redis.port | int | `6379` | Port to expose Redis on. |
| services.redis.resources | object | `{}` | The resources to use for Redis pods. See <https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/> for more information on how to set these. |
| services.redis.storage | string | `"100Mi"` | How much persistent storage to allocate to Redis. |
| services.redis.storageClass | string | `""` | If defined, storageClassName: <storageClass> If set to "-", storageClassName: "", which disables dynamic provisioning If undefined (the default) or set to null, no storageClassName spec is set, choosing the default provisioner. |
| services.redis.url | string | `""` | If you choose to run Redis externally to this chart, you can specify the connection details here. |
| services.worker.autoscaling.enabled | bool | `false` | Whether to enable horizontal pod autoscaling for the worker service. |
| services.worker.autoscaling.maxReplicas | int | `10` | |
| services.worker.autoscaling.minReplicas | int | `1` | |
| services.worker.autoscaling.targetCPUUtilizationPercentage | int | `80` | Target CPU utilization percentage for the worker service. Note that for autoscaling to work, you will need to have metrics-server configured, and resources set for the worker pods. |
| services.worker.httpLogging | int | `1` | Whether or not to log HTTP requests to the worker service. |
| services.worker.livenessProbe | object | HTTP health checks. | Liveness probe configuration for worker pods. You shouldn't need to change this, but if you want to you can find more information here: <https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-startup-probes/> |
| services.worker.logLevel | string | `"info"` | The log level for the worker service. |
| services.worker.readinessProbe | object | HTTP health checks. | Readiness probe configuration for worker pods. You shouldn't need to change this, but if you want to you can find more information here: <https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-startup-probes/> |
| services.worker.replicaCount | int | `1` | The number of worker replicas to run. |
| services.worker.resources | object | `{}` | The resources to use for worker pods. See <https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/> for more information on how to set these. |
| services.worker.startupProbe | object | HTTP health checks. | Startup probe configuration for worker pods. You shouldn't need to change this, but if you want to you can find more information here: <https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-startup-probes/> |
| tolerations | list | `[]` | Sets the tolerations for all pods created by this chart. Should not ordinarily need to be changed. See <https://kubernetes.io/docs/concepts/scheduling-eviction/taint-and-toleration/> for more information on tolerations. |
## Uninstalling
To uninstall the chart, assuming you named the release `budibase` (both commands in the installation section do so):
```console
$ helm uninstall --namespace budibase budibase
```
----------------------------------------------
Autogenerated from chart metadata using [helm-docs v1.11.3](https://github.com/norwoodj/helm-docs/releases/v1.11.3)

View file

@ -0,0 +1,116 @@
{{ template "chart.header" . }}
{{ template "chart.description" . }}
## Prerequisites
- `helm` v3 or above
- Kubernetes 1.4+
- A storage controller (if you want to use persistent storage)
- An ingress controller (if you want to define an `Ingress` resource)
- `metrics-server` (if you want to make use of horizontal pod autoscaling)
## Chart dependencies
This chart depends on the official Apache CouchDB chart. You can see its
documentation here:
<https://github.com/apache/couchdb-helm/tree/couchdb-4.3.0/couchdb>.
## Upgrading
### `2.x` to `3.0.0`
We made a number of breaking changes in this release to make the chart more
idiomatic and easier to use.
1. We no longer bundle `ingress-nginx`. If you were relying on this to supply
an ingress controller to your cluster, you will now need to deploy that
separately. You'll find guidance for that here:
<https://kubernetes.github.io/ingress-nginx/>.
2. We've upgraded the version of the [CouchDB chart](https://github.com/apache/couchdb-helm)
we use from `3.3.4` to `4.3.0`. The primary motivation for this was to align
the CouchDB chart used with the CouchDB version used, which has also updated
from 3.1.1 to 3.2.1. Additionally, we're moving away from the official CouchDB
to one we're building ourselves.
3. We've separated out the supplied AWS ALB ingress resource for those deploying
into EKS. Where previously you enabled this by setting `ingress.enabled: false`
and `ingress.aws: true`, you now set `awsAlbIngress.enabled: true` and all
configuration for it is under `awsAlbIngress`.
4. The `HorizontalPodAutoscaler` that was configured at `hpa.enabled: true` has
been split into 3 separate HPAs, one for each of `apps`, `worker`, and `proxy`.
They are configured at `services.{apps,worker,proxy}.autoscaling`.
## Installing
To install the chart from our repository:
```console
$ helm repo add budibase https://budibase.github.io/budibase/
$ helm repo update
$ helm install --create-namespace --namespace budibase budibase budibase/budibase
```
To install the chart from this repo:
```console
$ git clone git@github.com:budibase/budibase.git
$ cd budibase/charts/budibase
$ helm install --create-namespace --namespace budibase budibase .
```
## Example minimal configuration
Here's an example `values.yaml` that would get a Budibase instance running in a home
cluster using an nginx ingress controller and NFS as cluster storage (basically one of our
staff's homelabs).
<details>
```yaml
ingress:
enabled: true
className: "nginx"
hosts:
- host: budibase.local # set this to whatever DNS name you'd use
paths:
- backend:
service:
name: proxy-service
port:
number: 10000
path: /
pathType: Prefix
couchdb:
persistentVolume:
enabled: true
storageClass: "nfs-client"
adminPassword: admin
objectStore:
storageClass: "nfs-client"
redis:
storageClass: "nfs-client"
```
If you wanted to use this when bringing up Budibase in your own cluster, you could save it
to your hard disk and run the following:
```console
$ helm install --create-namespace --namespace budibase budibase . -f values.yaml
```
</details>
## Configuring
{{ template "chart.valuesTable" . }}
## Uninstalling
To uninstall the chart, assuming you named the release `budibase` (both commands in the installation section do so):
```console
$ helm uninstall --namespace budibase budibase
```
{{ template "helm-docs.versionFooter" . }}

Binary file not shown.

View file

@ -1,4 +1,4 @@
{{- if .Values.ingress.aws }}
{{- if .Values.awsAlbIngress.enabled }}
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
@ -9,22 +9,22 @@ metadata:
alb.ingress.kubernetes.io/target-type: ip
alb.ingress.kubernetes.io/success-codes: '200'
alb.ingress.kubernetes.io/healthcheck-path: '/health'
{{- if .Values.ingress.certificateArn }}
{{- if .Values.awsAlbIngress.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 }}
alb.ingress.kubernetes.io/certificate-arn: {{ .Values.awsAlbIngress.certificateArn }}
{{- end }}
{{- if .Values.ingress.sslPolicy }}
alb.ingress.kubernetes.io/actions.ssl-policy: {{ .Values.ingress.sslPolicy }}
{{- if .Values.awsAlbIngress.sslPolicy }}
alb.ingress.kubernetes.io/actions.ssl-policy: {{ .Values.awsAlbIngress.sslPolicy }}
{{- end }}
{{- if .Values.ingress.securityGroups }}
alb.ingress.kubernetes.io/security-groups: {{ .Values.ingress.securityGroups }}
{{- if .Values.awsAlbIngress.securityGroups }}
alb.ingress.kubernetes.io/security-groups: {{ .Values.awsAlbIngress.securityGroups }}
{{- end }}
spec:
rules:
- http:
paths:
{{- if .Values.ingress.certificateArn }}
{{- if .Values.awsAlbIngress.certificateArn }}
- path: /
pathType: Prefix
backend:

View file

@ -2,12 +2,9 @@ apiVersion: apps/v1
kind: Deployment
metadata:
annotations:
kompose.cmd: kompose convert
kompose.version: 1.21.0 (992df58d8)
{{ if .Values.services.apps.deploymentAnnotations }}
{{- toYaml .Values.services.apps.deploymentAnnotations | indent 4 -}}
{{ end }}
creationTimestamp: null
labels:
io.kompose.service: app-service
{{ if .Values.services.apps.deploymentLabels }}
@ -24,12 +21,9 @@ spec:
template:
metadata:
annotations:
kompose.cmd: kompose convert
kompose.version: 1.21.0 (992df58d8)
{{ if .Values.services.apps.templateAnnotations }}
{{- toYaml .Values.services.apps.templateAnnotations | indent 8 -}}
{{ end }}
creationTimestamp: null
labels:
io.kompose.service: app-service
{{ if .Values.services.apps.templateLabels }}

View file

@ -0,0 +1,32 @@
{{- if .Values.services.apps.autoscaling.enabled }}
apiVersion: {{ ternary "autoscaling/v2" "autoscaling/v2beta2" (.Capabilities.APIVersions.Has "autoscaling/v2") }}
kind: HorizontalPodAutoscaler
metadata:
name: {{ include "budibase.fullname" . }}-apps
labels:
{{- include "budibase.labels" . | nindent 4 }}
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: app-service
minReplicas: {{ .Values.services.apps.autoscaling.minReplicas }}
maxReplicas: {{ .Values.services.apps.autoscaling.maxReplicas }}
metrics:
{{- if .Values.services.apps.autoscaling.targetCPUUtilizationPercentage }}
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: {{ .Values.services.apps.autoscaling.targetCPUUtilizationPercentage }}
{{- end }}
{{- if .Values.services.apps.autoscaling.targetMemoryUtilizationPercentage }}
- type: Resource
resource:
name: memory
target:
type: Utilization
averageUtilization: {{ .Values.services.apps.autoscaling.targetMemoryUtilizationPercentage }}
{{- end }}
{{- end }}

View file

@ -1,10 +1,6 @@
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

View file

@ -2,10 +2,6 @@
apiVersion: apps/v1
kind: Deployment
metadata:
annotations:
kompose.cmd: kompose convert
kompose.version: 1.21.0 (992df58d8)
creationTimestamp: null
labels:
app.kubernetes.io/name: couchdb-backup
name: couchdb-backup
@ -18,10 +14,6 @@ spec:
type: Recreate
template:
metadata:
annotations:
kompose.cmd: kompose convert
kompose.version: 1.21.0 (992df58d8)
creationTimestamp: null
labels:
app.kubernetes.io/name: couchdb-backup
spec:

View file

@ -1,28 +0,0 @@
{{- 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 }}

View file

@ -2,7 +2,6 @@
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
creationTimestamp: null
labels:
io.kompose.service: minio-data
name: minio-data

View file

@ -2,10 +2,6 @@
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
@ -18,10 +14,6 @@ spec:
type: Recreate
template:
metadata:
annotations:
kompose.cmd: kompose convert
kompose.version: 1.21.0 (992df58d8)
creationTimestamp: null
labels:
io.kompose.service: minio-service
spec:

View file

@ -2,10 +2,6 @@
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

View file

@ -2,12 +2,9 @@ apiVersion: apps/v1
kind: Deployment
metadata:
annotations:
kompose.cmd: kompose convert
kompose.version: 1.21.0 (992df58d8)
{{ if .Values.services.proxy.deploymentAnnotations }}
{{- toYaml .Values.services.proxy.deploymentAnnotations | indent 4 -}}
{{ end }}
creationTimestamp: null
labels:
app.kubernetes.io/name: budibase-proxy
{{ if .Values.services.proxy.deploymentLabels }}
@ -25,12 +22,9 @@ spec:
template:
metadata:
annotations:
kompose.cmd: kompose convert
kompose.version: 1.21.0 (992df58d8)
{{ if .Values.services.proxy.templateAnnotations }}
{{- toYaml .Values.services.proxy.templateAnnotations | indent 8 -}}
{{ end }}
creationTimestamp: null
labels:
app.kubernetes.io/name: budibase-proxy
{{ if .Values.services.proxy.templateLabels }}

View file

@ -0,0 +1,32 @@
{{- if .Values.services.proxy.autoscaling.enabled }}
apiVersion: {{ ternary "autoscaling/v2" "autoscaling/v2beta2" (.Capabilities.APIVersions.Has "autoscaling/v2") }}
kind: HorizontalPodAutoscaler
metadata:
name: {{ include "budibase.fullname" . }}-proxy
labels:
{{- include "budibase.labels" . | nindent 4 }}
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: proxy-service
minReplicas: {{ .Values.services.proxy.autoscaling.minReplicas }}
maxReplicas: {{ .Values.services.proxy.autoscaling.maxReplicas }}
metrics:
{{- if .Values.services.proxy.autoscaling.targetCPUUtilizationPercentage }}
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: {{ .Values.services.proxy.autoscaling.targetCPUUtilizationPercentage }}
{{- end }}
{{- if .Values.services.proxy.autoscaling.targetMemoryUtilizationPercentage }}
- type: Resource
resource:
name: memory
target:
type: Utilization
averageUtilization: {{ .Values.services.proxy.autoscaling.targetMemoryUtilizationPercentage }}
{{- end }}
{{- end }}

View file

@ -1,10 +1,6 @@
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
@ -16,4 +12,4 @@ spec:
selector:
app.kubernetes.io/name: budibase-proxy
status:
loadBalancer: {}
loadBalancer: {}

View file

@ -2,7 +2,6 @@
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
creationTimestamp: null
labels:
io.kompose.service: redis-data
name: redis-data

View file

@ -2,10 +2,6 @@
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
@ -18,10 +14,6 @@ spec:
type: Recreate
template:
metadata:
annotations:
kompose.cmd: kompose convert
kompose.version: 1.21.0 (992df58d8)
creationTimestamp: null
labels:
io.kompose.service: redis-service
spec:

View file

@ -2,10 +2,6 @@
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

View file

@ -2,12 +2,9 @@ apiVersion: apps/v1
kind: Deployment
metadata:
annotations:
kompose.cmd: kompose convert
kompose.version: 1.21.0 (992df58d8)
{{ if .Values.services.worker.deploymentAnnotations }}
{{- toYaml .Values.services.worker.deploymentAnnotations | indent 4 -}}
{{ end }}
creationTimestamp: null
labels:
io.kompose.service: worker-service
{{ if .Values.services.worker.deploymentLabels }}
@ -24,12 +21,9 @@ spec:
template:
metadata:
annotations:
kompose.cmd: kompose convert
kompose.version: 1.21.0 (992df58d8)
{{ if .Values.services.worker.templateAnnotations }}
{{- toYaml .Values.services.worker.templateAnnotations | indent 8 -}}
{{ end }}
creationTimestamp: null
labels:
io.kompose.service: worker-service
{{ if .Values.services.worker.templateLabels }}

View file

@ -0,0 +1,32 @@
{{- if .Values.services.worker.autoscaling.enabled }}
apiVersion: {{ ternary "autoscaling/v2" "autoscaling/v2beta2" (.Capabilities.APIVersions.Has "autoscaling/v2") }}
kind: HorizontalPodAutoscaler
metadata:
name: {{ include "budibase.fullname" . }}-worker
labels:
{{- include "budibase.labels" . | nindent 4 }}
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: worker-service
minReplicas: {{ .Values.services.worker.autoscaling.minReplicas }}
maxReplicas: {{ .Values.services.worker.autoscaling.maxReplicas }}
metrics:
{{- if .Values.services.worker.autoscaling.targetCPUUtilizationPercentage }}
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: {{ .Values.services.worker.autoscaling.targetCPUUtilizationPercentage }}
{{- end }}
{{- if .Values.services.worker.autoscaling.targetMemoryUtilizationPercentage }}
- type: Resource
resource:
name: memory
target:
type: Utilization
averageUtilization: {{ .Values.services.worker.autoscaling.targetMemoryUtilizationPercentage }}
{{- end }}
{{- end }}

View file

@ -1,10 +1,6 @@
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

View file

@ -1,56 +1,32 @@
# Default values for budibase.
# This is a YAML-formatted file.
# Declare variables to be passed into your templates.
image:
pullPolicy: IfNotPresent
# Overrides the image tag whose default is the chart appVersion.
tag: ""
# -- Passed to all pods created by this chart. Should not ordinarily need to be changed.
imagePullSecrets: []
# -- Override the name of the deploymen. Defaults to {{ .Chart.Name }}.
nameOverride: ""
# fullnameOverride: ""
serviceAccount:
# Specifies whether a service account should be created
# -- Specifies whether a service account should be created
create: true
# Annotations to add to the service account
# -- Annotations to add to the service account
annotations: {}
# The name of the service account to use.
# -- 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:
# -- Service type for the service that points to the main Budibase proxy pod.
type: ClusterIP
# -- Port to expose on the service.
port: 10000
ingress:
# -- Whether to create an Ingress resource pointing to the Budibase proxy.
enabled: true
aws: false
nginx: true
certificateArn: ""
# -- What ingress class to use.
className: ""
annotations:
kubernetes.io/ingress.class: nginx
nginx.ingress.kubernetes.io/client-max-body-size: 150M
nginx.ingress.kubernetes.io/proxy-body-size: 50m
# -- Standard hosts block for the Ingress resource. Defaults to pointing to the Budibase proxy.
hosts:
- host: # change if using custom domain
# @ignore
- host:
paths:
- path: /
pathType: Prefix
@ -60,361 +36,426 @@ ingress:
port:
number: 10000
autoscaling:
awsAlbIngress:
# -- Whether to create an ALB Ingress resource pointing to the Budibase proxy. Requires the AWS ALB Ingress Controller.
enabled: false
minReplicas: 1
maxReplicas: 100
targetCPUUtilizationPercentage: 80
# targetMemoryUtilizationPercentage: 80
nodeSelector: {}
# -- If you're wanting to use HTTPS, you'll need to create an ACM certificate and specify the ARN here.
certificateArn: ""
# -- Sets the tolerations for all pods created by this chart. Should not ordinarily need to be changed.
# See <https://kubernetes.io/docs/concepts/scheduling-eviction/taint-and-toleration/> for more information
# on tolerations.
tolerations: []
# -- Sets the affinity for all pods created by this chart. Should not ordinarily
# need to be changed. See
# <https://kubernetes.io/docs/tasks/configure-pod-container/assign-pods-nodes-using-node-affinity/>
# for more information on affinity.
affinity: {}
globals:
appVersion: "" # Use as an override to .Chart.AppVersion
# -- The version of Budibase to deploy. Defaults to what's specified by {{ .Chart.AppVersion }}.
# Ends up being used as the image version tag for the apps, proxy, and worker images.
appVersion: ""
# -- Sets the environment variable BUDIBASE_ENVIRONMENT for the apps and worker pods. Should not
# ordinarily need to be changed.
budibaseEnv: PRODUCTION
# -- Sets what feature flags are enabled and for which tenants. Should not ordinarily need to be
# changed.
tenantFeatureFlags: "*:LICENSING,*:USER_GROUPS,*:ONBOARDING_TOUR"
# -- Whether to enable analytics or not. You can read more about our analytics here:
# <https://docs.budibase.com/docs/analytics>.
enableAnalytics: "1"
# @ignore (only used if enableAnalytics is set to 1)
posthogToken: "phc_bIjZL7oh2GEUd2vqvTBH8WvrX0fWTFQMs6H5KQxiUxU"
selfHosted: "1" # set to 0 for budibase cloud environment, set to 1 for self-hosted setup
multiTenancy: "0" # set to 0 to disable multiple orgs, set to 1 to enable multiple orgs
offlineMode: "0" # set to 1 to enable offline mode
# @ignore (should not normally need to be changed, we only set this to "0"
# when deploying to our Cloud environment)
selfHosted: "1"
# @ignore (doesn't work out of the box for self-hosted users, only meant for Budicloud)
multiTenancy: "0"
# @ignore (only currently used to determine whether to fetch licenses offline or not, should
# not normally need to be changed, and only applies to Enterprise customers)
offlineMode: "0"
# @ignore (only needs to be set in our cloud environment)
accountPortalUrl: ""
# @ignore (only needs to be set in our cloud environment)
accountPortalApiKey: ""
# -- Sets the domain attribute of the cookie that Budibase uses to store session information.
# See <https://developer.mozilla.org/en-US/docs/Web/HTTP/Cookies#define_where_cookies_are_sent>
# for details on why you might want to set this.
cookieDomain: ""
# -- Set the `platformUrl` binding. You can also do this in Settings > Organisation if you are
# self-hosting.
platformUrl: ""
# -- Whether or not to enable doing data migrations over the HTTP API. If this is set to "0",
# migrations are run on startup. You shouldn't ordinarily need to change this.
httpMigrations: "0"
# -- Google OAuth settings. These can also be set in the Budibase UI, see
# <https://docs.budibase.com/docs/sso-with-google> for details.
google:
# -- Client ID of your Google OAuth app.
clientId: ""
# -- Client secret of your Google OAuth app.
secret: ""
# -- The maximum number of iterations allows for an automation loop step. You can read more about
# looping here: <https://docs.budibase.com/docs/looping>.
automationMaxIterations: "200"
createSecrets: true # creates an internal API key, JWT secrets and redis password for you
# -- Create an internal API key, JWT secret, object store access key and
# secret, and store them in a Kubernetes `Secret`.
createSecrets: true
# if createSecrets is set to false, you can hard-code your secrets here
# -- Used for encrypting API keys and environment variables when stored in the database.
# You don't need to set this if `createSecrets` is true.
apiEncryptionKey: ""
# -- API key used for internal Budibase API calls. You don't need to set this
# if `createSecrets` is true.
internalApiKey: ""
# -- Secret used for signing JWTs. You don't need to set this if `createSecrets` is true.
jwtSecret: ""
cdnUrl: ""
# fallback values used during live rotation
# -- A fallback value for `internalApiKey`. If you're rotating your encryption key, you can
# set this to the old value for the duration of the rotation.
internalApiKeyFallback: ""
# -- A fallback value for `jwtSecret`. If you're rotating your JWT secret, you can set this
# to the old value for the duration of the rotation.
jwtSecretFallback: ""
smtp:
# -- Whether to enable SMTP or not.
enabled: false
# globalAgentHttpProxy:
# globalAgentHttpsProxy:
# globalAgentNoProxy:
# -- The hostname of your SMTP server.
host: ""
# -- The port of your SMTP server.
port: "587"
# -- The email address to use in the "From:" field of emails sent by Budibase.
from: ""
# -- The username to use when authenticating with your SMTP server.
user: ""
# -- The password to use when authenticating with your SMTP server.
password: ""
services:
budibaseVersion: latest
# -- The DNS suffix to use for service discovery. You only need to change this
# if you've configured your cluster to use a different DNS suffix.
dns: cluster.local
# tlsRejectUnauthorized: 0
proxy:
# @ignore (you shouldn't need to change this)
port: 10000
# -- The number of proxy replicas to run.
replicaCount: 1
# @ignore (you should never need to change this)
upstreams:
apps: "http://app-service.{{ .Release.Namespace }}.svc.{{ .Values.services.dns }}:{{ .Values.services.apps.port }}"
worker: "http://worker-service.{{ .Release.Namespace }}.svc.{{ .Values.services.dns }}:{{ .Values.services.worker.port }}"
minio: "http://minio-service.{{ .Release.Namespace }}.svc.{{ .Values.services.dns }}:{{ .Values.services.objectStore.port }}"
couchdb: "http://{{ .Release.Name }}-svc-couchdb:{{ .Values.services.couchdb.port }}"
# -- The resources to use for proxy pods. See
# <https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/>
# for more information on how to set these.
resources: {}
# -- Startup probe configuration for proxy pods. You shouldn't need to
# change this, but if you want to you can find more information here:
# <https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-startup-probes/>
# @default -- HTTP health checks.
startupProbe:
# @ignore
httpGet:
path: /health
port: 10000
scheme: HTTP
# @ignore
failureThreshold: 30
# @ignore
periodSeconds: 3
# -- Readiness probe configuration for proxy pods. You shouldn't need to
# change this, but if you want to you can find more information here:
# <https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-startup-probes/>
# @default -- HTTP health checks.
readinessProbe:
# @ignore
httpGet:
path: /health
port: 10000
scheme: HTTP
# @ignore
periodSeconds: 3
# @ignore
failureThreshold: 1
# -- Liveness probe configuration for proxy pods. You shouldn't need to
# change this, but if you want to you can find more information here:
# <https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-startup-probes/>
# @default -- HTTP health checks.
livenessProbe:
# @ignore
httpGet:
path: /health
port: 10000
scheme: HTTP
# @ignore
failureThreshold: 3
# @ignore
periodSeconds: 5
# annotations:
# co.elastic.logs/module: nginx
# co.elastic.logs/fileset.stdout: access
# co.elastic.logs/fileset.stderr: error
autoscaling:
# -- Whether to enable horizontal pod autoscaling for the proxy service.
enabled: false
minReplicas: 1
maxReplicas: 10
# -- Target CPU utilization percentage for the proxy service. Note that
# for autoscaling to work, you will need to have metrics-server
# configured, and resources set for the proxy pods.
targetCPUUtilizationPercentage: 80
apps:
# @ignore (you shouldn't need to change this)
port: 4002
# -- The number of apps replicas to run.
replicaCount: 1
# -- The log level for the apps service.
logLevel: info
# -- Whether or not to log HTTP requests to the apps service.
httpLogging: 1
# -- The resources to use for apps pods. See
# <https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/>
# for more information on how to set these.
resources: {}
# -- Startup probe configuration for apps pods. You shouldn't need to
# change this, but if you want to you can find more information here:
# <https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-startup-probes/>
# @default -- HTTP health checks.
startupProbe:
# @ignore
httpGet:
path: /health
port: 4002
scheme: HTTP
# @ignore
failureThreshold: 30
# @ignore
periodSeconds: 3
# -- Readiness probe configuration for apps pods. You shouldn't need to
# change this, but if you want to you can find more information here:
# <https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-startup-probes/>
# @default -- HTTP health checks.
readinessProbe:
# @ignore
httpGet:
path: /health
port: 4002
scheme: HTTP
# @ignore
periodSeconds: 3
# @ignore
failureThreshold: 1
# -- Liveness probe configuration for apps pods. You shouldn't need to
# change this, but if you want to you can find more information here:
# <https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-startup-probes/>
# @default -- HTTP health checks.
livenessProbe:
# @ignore
httpGet:
path: /health
port: 4002
scheme: HTTP
# @ignore
failureThreshold: 3
# @ignore
periodSeconds: 5
# nodeDebug: "" # set the value of NODE_DEBUG
# annotations:
# co.elastic.logs/multiline.type: pattern
# co.elastic.logs/multiline.pattern: '^[[:space:]]'
# co.elastic.logs/multiline.negate: false
# co.elastic.logs/multiline.match: after
autoscaling:
# -- Whether to enable horizontal pod autoscaling for the apps service.
enabled: false
minReplicas: 1
maxReplicas: 10
# -- Target CPU utilization percentage for the apps service. Note that for
# autoscaling to work, you will need to have metrics-server configured,
# and resources set for the apps pods.
targetCPUUtilizationPercentage: 80
worker:
# @ignore (you shouldn't need to change this)
port: 4003
# -- The number of worker replicas to run.
replicaCount: 1
# -- The log level for the worker service.
logLevel: info
# -- Whether or not to log HTTP requests to the worker service.
httpLogging: 1
# -- The resources to use for worker pods. See
# <https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/>
# for more information on how to set these.
resources: {}
# -- Startup probe configuration for worker pods. You shouldn't need to
# change this, but if you want to you can find more information here:
# <https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-startup-probes/>
# @default -- HTTP health checks.
startupProbe:
# @ignore
httpGet:
path: /health
port: 4003
scheme: HTTP
# @ignore
failureThreshold: 30
# @ignore
periodSeconds: 3
# -- Readiness probe configuration for worker pods. You shouldn't need to
# change this, but if you want to you can find more information here:
# <https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-startup-probes/>
# @default -- HTTP health checks.
readinessProbe:
# @ignore
httpGet:
path: /health
port: 4003
scheme: HTTP
# @ignore
periodSeconds: 3
# @ignore
failureThreshold: 1
# -- Liveness probe configuration for worker pods. You shouldn't need to
# change this, but if you want to you can find more information here:
# <https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-startup-probes/>
# @default -- HTTP health checks.
livenessProbe:
# @ignore
httpGet:
path: /health
port: 4003
scheme: HTTP
# @ignore
failureThreshold: 3
# @ignore
periodSeconds: 5
# annotations:
# co.elastic.logs/multiline.type: pattern
# co.elastic.logs/multiline.pattern: '^[[:space:]]'
# co.elastic.logs/multiline.negate: false
# co.elastic.logs/multiline.match: after
autoscaling:
# -- Whether to enable horizontal pod autoscaling for the worker service.
enabled: false
minReplicas: 1
maxReplicas: 10
# -- Target CPU utilization percentage for the worker service. Note that
# for autoscaling to work, you will need to have metrics-server
# configured, and resources set for the worker pods.
targetCPUUtilizationPercentage: 80
couchdb:
# -- Whether or not to spin up a CouchDB instance in your cluster. True by
# default, and the configuration for the CouchDB instance is under the
# `couchdb` key at the root of this file. You can see what options are
# available to you by looking at the official CouchDB Helm chart:
# <https://github.com/apache/couchdb-helm/tree/couchdb-4.3.0/couchdb>.
enabled: true
# 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
backup:
# -- Whether or not to enable periodic CouchDB backups. This works by replicating
# to another CouchDB instance.
enabled: false
# target couchDB instance to back up to
# -- Target couchDB instance to back up to, either a hostname or an IP address.
target: ""
# backup interval in seconds
# -- Backup interval in seconds
interval: ""
# -- The resources to use for CouchDB backup pods. See
# <https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/>
# for more information on how to set these.
resources: {}
redis:
enabled: true # disable if using external redis
# -- Whether or not to deploy a Redis pod into your cluster.
enabled: true
# -- Port to expose Redis on.
port: 6379
# @ignore (you should leave this as 1, we don't support clustering Redis)
replicaCount: 1
url: "" # only change if pointing to existing redis cluster and enabled: false
password: "budibase" # recommended to override if using built-in redis
# -- If you choose to run Redis externally to this chart, you can specify the
# connection details here.
url: ""
# -- The password to use when connecting to Redis. It's recommended that you change
# this from the default if you're running Redis in-cluster.
password: "budibase"
# -- How much persistent storage to allocate to Redis.
storage: 100Mi
## If defined, storageClassName: <storageClass>
## If set to "-", storageClassName: "", which disables dynamic provisioning
## If undefined (the default) or set to null, no storageClassName spec is
## set, choosing the default provisioner.
# -- If defined, storageClassName: <storageClass> If set to "-",
# storageClassName: "", which disables dynamic provisioning If undefined
# (the default) or set to null, no storageClassName spec is set, choosing
# the default provisioner.
storageClass: ""
# -- The resources to use for Redis pods. See
# <https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/>
# for more information on how to set these.
resources: {}
objectStore:
# Set to false if using another object store such as S3
# -- Set to false if using another object store, such as S3. You will need
# to set `services.objectStore.url` to point to your bucket if you do this.
minio: true
# -- Whether to enable the Minio web console or not. If you're exposing
# Minio to the Internet (via a custom Ingress record, for example), you
# should set this to false. If you're only exposing Minio to your cluster,
# you can leave this as true.
browser: true
# @ignore
port: 9000
# @ignore (you should leave this as 1, we don't support clustering Minio)
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: "http://minio-service:9000" # only change if pointing to existing minio cluster or S3 and minio: false
# -- AWS_ACCESS_KEY if using S3
accessKey: ""
# -- AWS_SECRET_ACCESS_KEY if using S3
secretKey: ""
# -- AWS_REGION if using S3
region: ""
# -- URL to use for object storage. Only change this if you're using an
# external object store, such as S3. Remember to set `minio: false` if you
# do this.
url: "http://minio-service:9000"
# -- How much storage to give Minio in its PersistentVolumeClaim.
storage: 100Mi
## If defined, storageClassName: <storageClass>
## If set to "-", storageClassName: "", which disables dynamic provisioning
## If undefined (the default) or set to null, no storageClassName spec is
## set, choosing the default provisioner.
# -- If defined, storageClassName: <storageClass> If set to "-",
# storageClassName: "", which disables dynamic provisioning If undefined
# (the default) or set to null, no storageClassName spec is set, choosing
# the default provisioner.
storageClass: ""
# -- The resources to use for Minio pods. See
# <https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/>
# for more information on how to set these.
resources: {}
cloudfront:
# Set the url of a distribution to enable cloudfront
# -- Set the url of a distribution to enable cloudfront.
cdn: ""
# ID of public key stored in cloudfront
# -- ID of public key stored in cloudfront.
publicKeyId: ""
# Base64 encoded private key for the above public key
# -- Base64 encoded private key for the above public key.
privateKey64: ""
# Override values in couchDB subchart
# Override values in couchDB subchart. We're only specifying the values we're changing.
# If you want to see all of the available values, see:
# https://github.com/apache/couchdb-helm/tree/couchdb-4.3.0/couchdb
couchdb:
## clusterSize is the initial size of the CouchDB cluster.
# -- The number of replicas to run in the CouchDB cluster. We set this to
# 1 by default to make things simpler, but you can set it to 3 if you need
# a high-availability CouchDB cluster.
clusterSize: 1
allowAdminParty: false
# Secret Management
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 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
# -- We use a custom CouchDB image for running Budibase and we don't support
# using any other CouchDB image. You shouldn't change this, and if you do we
# can't guarantee that Budibase will work.
image:
repository: couchdb
tag: 3.1.1
pullPolicy: IfNotPresent
## Experimental integration with Lucene-powered fulltext search
enableSearch: true
searchImage:
repository: kocolosk/couchdb-search
tag: 0.2.0
pullPolicy: IfNotPresent
initImage:
repository: busybox
tag: latest
# @ignore
repository: budibase/couchdb
# @ignore
tag: v3.2.1
# @ignore
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
# @ignore
# This should remain false. We ship Clouseau ourselves as part of the
# budibase/couchdb image, and it's not possible to disable it because it's a
# core part of the Budibase experience.
enableSearch: false
## Optional pod annotations
annotations: {}
## Optional tolerations
tolerations: []
affinity: {}
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
# FOR COUCHDB
livenessProbe:
failureThreshold: 3
initialDelaySeconds: 0
periodSeconds: 10
successThreshold: 1
timeoutSeconds: 1
readinessProbe:
failureThreshold: 3
initialDelaySeconds: 0
periodSeconds: 10
successThreshold: 1
timeoutSeconds: 1
# -- Unique identifier for this CouchDB server instance. You shouldn't need
# to change this.
uuid: budibase-couchdb

View file

@ -1,5 +1,5 @@
{
"version": "2.13.30",
"version": "2.13.31",
"npmClient": "yarn",
"packages": [
"packages/*"

View file

@ -99,6 +99,8 @@ function updateContext(updates: ContextMap): ContextMap {
}
async function newContext<T>(updates: ContextMap, task: () => T) {
guardMigration()
// see if there already is a context setup
let context: ContextMap = updateContext(updates)
return Context.run(context, task)
@ -145,23 +147,27 @@ export async function doInTenant<T>(
}
export async function doInAppContext<T>(
appId: string | null,
appId: string,
task: () => T
): Promise<T> {
if (!appId && !env.isTest()) {
return _doInAppContext(appId, task)
}
async function _doInAppContext<T>(
appId: string,
task: () => T,
extraContextSettings?: ContextMap
): Promise<T> {
if (!appId) {
throw new Error("appId is required")
}
let updates: ContextMap
if (!appId) {
updates = { appId: "" }
} else {
const tenantId = getTenantIDFromAppID(appId)
updates = { appId }
if (tenantId) {
updates.tenantId = tenantId
}
const tenantId = getTenantIDFromAppID(appId)
const updates: ContextMap = { appId, ...extraContextSettings }
if (tenantId) {
updates.tenantId = tenantId
}
return newContext(updates, task)
}
@ -182,6 +188,24 @@ export async function doInIdentityContext<T>(
return newContext(context, task)
}
function guardMigration() {
const context = Context.get()
if (context?.isMigrating) {
throw new Error(
"The context cannot be changed, a migration is currently running"
)
}
}
export async function doInAppMigrationContext<T>(
appId: string,
task: () => T
): Promise<T> {
return _doInAppContext(appId, task, {
isMigrating: true,
})
}
export function getIdentity(): IdentityContext | undefined {
try {
const context = Context.get()

View file

@ -1,6 +1,11 @@
import { testEnv } from "../../../tests/extra"
import * as context from "../"
import { DEFAULT_TENANT_ID } from "../../constants"
import { structures } from "../../../tests"
import { db } from "../.."
import Context from "../Context"
import { ContextMap } from "../types"
import { IdentityType } from "@budibase/types"
describe("context", () => {
describe("doInTenant", () => {
@ -144,4 +149,107 @@ describe("context", () => {
expect(isScim).toBe(false)
})
})
describe("doInAppMigrationContext", () => {
it("the context is set correctly", async () => {
const appId = db.generateAppID()
await context.doInAppMigrationContext(appId, () => {
const context = Context.get()
const expected: ContextMap = {
appId,
isMigrating: true,
}
expect(context).toEqual(expected)
})
})
it("the context is set correctly when running in a tenant id", async () => {
const tenantId = structures.tenant.id()
const appId = db.generateAppID(tenantId)
await context.doInAppMigrationContext(appId, () => {
const context = Context.get()
const expected: ContextMap = {
appId,
isMigrating: true,
tenantId,
}
expect(context).toEqual(expected)
})
})
it("the context is not modified outside the delegate", async () => {
const appId = db.generateAppID()
expect(Context.get()).toBeUndefined()
await context.doInAppMigrationContext(appId, () => {
const context = Context.get()
const expected: ContextMap = {
appId,
isMigrating: true,
}
expect(context).toEqual(expected)
})
expect(Context.get()).toBeUndefined()
})
it.each([
[
"doInAppMigrationContext",
() => context.doInAppMigrationContext(db.generateAppID(), () => {}),
],
[
"doInAppContext",
() => context.doInAppContext(db.generateAppID(), () => {}),
],
[
"doInAutomationContext",
() =>
context.doInAutomationContext({
appId: db.generateAppID(),
automationId: structures.generator.guid(),
task: () => {},
}),
],
["doInContext", () => context.doInContext(db.generateAppID(), () => {})],
[
"doInEnvironmentContext",
() => context.doInEnvironmentContext({}, () => {}),
],
[
"doInIdentityContext",
() =>
context.doInIdentityContext(
{
account: undefined,
type: IdentityType.USER,
_id: structures.users.user()._id!,
},
() => {}
),
],
["doInScimContext", () => context.doInScimContext(() => {})],
[
"doInTenant",
() => context.doInTenant(structures.tenant.id(), () => {}),
],
])(
"a nested context.%s function cannot run",
async (_, otherContextCall: () => Promise<void>) => {
await expect(
context.doInAppMigrationContext(db.generateAppID(), async () => {
await otherContextCall()
})
).rejects.toThrowError(
"The context cannot be changed, a migration is currently running"
)
}
)
})
})

View file

@ -8,4 +8,5 @@ export type ContextMap = {
environmentVariables?: Record<string, string>
isScim?: boolean
automationId?: string
isMigrating?: boolean
}

View file

@ -109,7 +109,7 @@ export async function doWithLock<T>(
): Promise<RedlockExecution<T>> {
const redlock = await getClient(opts.type, opts.customOptions)
let lock: Redlock.Lock | undefined
let timeout: NodeJS.Timeout | undefined
let timeout
try {
const name = getLockName(opts)

View file

@ -93,11 +93,19 @@ export const getTenantIDFromCtx = (
// subdomain
if (isAllowed(TenantResolutionStrategy.SUBDOMAIN)) {
// e.g. budibase.app or local.com:10000
const platformHost = new URL(getPlatformURL()).host.split(":")[0]
let platformHost
try {
platformHost = new URL(getPlatformURL()).host.split(":")[0]
} catch (err: any) {
// if invalid URL, just don't try to process subdomain
if (err.code !== "ERR_INVALID_URL") {
throw err
}
}
// e.g. tenant.budibase.app or tenant.local.com
const requestHost = ctx.host
// parse the tenant id from the difference
if (requestHost.includes(platformHost)) {
if (platformHost && requestHost.includes(platformHost)) {
const tenantId = requestHost.substring(
0,
requestHost.indexOf(`.${platformHost}`)

View file

@ -8,6 +8,7 @@ import { derived, get } from "svelte/store"
import { findComponent, findComponentPath } from "./componentUtils"
import { RoleUtils } from "@budibase/frontend-core"
import { createHistoryStore } from "builderStore/store/history"
import { cloneDeep } from "lodash/fp"
export const store = getFrontendStore()
export const automationStore = getAutomationStore()
@ -69,7 +70,14 @@ export const selectedComponent = derived(
if (!$selectedScreen || !$store.selectedComponentId) {
return null
}
return findComponent($selectedScreen?.props, $store.selectedComponentId)
const selected = findComponent(
$selectedScreen?.props,
$store.selectedComponentId
)
const clone = selected ? cloneDeep(selected) : selected
store.actions.components.migrateSettings(clone)
return clone
}
)

View file

@ -601,6 +601,36 @@ export const getFrontendStore = () => {
// Finally try an external table
return validTables.find(table => table.sourceType === DB_TYPE_EXTERNAL)
},
migrateSettings: enrichedComponent => {
const componentPrefix = "@budibase/standard-components"
let migrated = false
if (enrichedComponent?._component == `${componentPrefix}/formblock`) {
// Use default config if the 'buttons' prop has never been initialised
if (!("buttons" in enrichedComponent)) {
enrichedComponent["buttons"] =
Utils.buildDynamicButtonConfig(enrichedComponent)
migrated = true
} else if (enrichedComponent["buttons"] == null) {
// Ignore legacy config if 'buttons' has been reset by 'resetOn'
const { _id, actionType, dataSource } = enrichedComponent
enrichedComponent["buttons"] = Utils.buildDynamicButtonConfig({
_id,
actionType,
dataSource,
})
migrated = true
}
// Ensure existing Formblocks position their buttons at the top.
if (!("buttonPosition" in enrichedComponent)) {
enrichedComponent["buttonPosition"] = "top"
migrated = true
}
}
return migrated
},
enrichEmptySettings: (component, opts) => {
if (!component?._component) {
return
@ -672,7 +702,6 @@ export const getFrontendStore = () => {
component[setting.key] = setting.defaultValue
}
}
// Validate non-empty settings
else {
if (setting.type === "dataProvider") {
@ -722,6 +751,9 @@ export const getFrontendStore = () => {
useDefaultValues: true,
})
// Migrate nested component settings
store.actions.components.migrateSettings(instance)
// Add any extra properties the component needs
let extras = {}
if (definition.hasChildren) {
@ -845,7 +877,13 @@ export const getFrontendStore = () => {
if (!component) {
return false
}
return patchFn(component, screen)
// Mutates the fetched component with updates
const updated = patchFn(component, screen)
// Mutates the component with any required settings updates
const migrated = store.actions.components.migrateSettings(component)
return updated || migrated
}
await store.actions.screens.patch(patchScreen, screenId)
},
@ -1247,9 +1285,13 @@ export const getFrontendStore = () => {
const settings = getComponentSettings(component._component)
const updatedSetting = settings.find(setting => setting.key === name)
const resetFields = settings.filter(
setting => name === setting.resetOn
)
// Can be a single string or array of strings
const resetFields = settings.filter(setting => {
return (
name === setting.resetOn ||
(Array.isArray(setting.resetOn) && setting.resetOn.includes(name))
)
})
resetFields?.forEach(setting => {
component[setting.key] = null
})
@ -1271,6 +1313,7 @@ export const getFrontendStore = () => {
})
}
component[name] = value
return true
}
},
requestEjectBlock: componentId => {
@ -1278,7 +1321,6 @@ export const getFrontendStore = () => {
},
handleEjectBlock: async (componentId, ejectedDefinition) => {
let nextSelectedComponentId
await store.actions.screens.patch(screen => {
const block = findComponent(screen.props, componentId)
const parent = findComponentParent(screen.props, componentId)

View file

@ -57,16 +57,11 @@
}}
class="buttons"
>
<Icon hoverable size="M" name="Play" />
<Icon size="M" name="Play" />
<div>Run test</div>
</div>
<div class="buttons">
<Icon
disabled={!$automationStore.testResults}
hoverable
size="M"
name="Multiple"
/>
<Icon disabled={!$automationStore.testResults} size="M" name="Multiple" />
<div
class:disabled={!$automationStore.testResults}
on:click={() => {

View file

@ -97,6 +97,7 @@
class:typing={typing && !automationNameError}
class:typing-error={automationNameError}
class="blockSection"
on:click={() => dispatch("toggle")}
>
<div class="splitHeader">
<div class="center-items">
@ -138,7 +139,20 @@
on:input={e => {
automationName = e.target.value.trim()
}}
on:click={startTyping}
on:click={e => {
e.stopPropagation()
startTyping()
}}
on:keydown={async e => {
if (e.key === "Enter") {
typing = false
if (automationNameError) {
automationName = stepNames[block.id] || block?.name
} else {
await saveName()
}
}
}}
on:blur={async () => {
typing = false
if (automationNameError) {
@ -168,7 +182,11 @@
</StatusLight>
</div>
<Icon
on:click={() => dispatch("toggle")}
e.stopPropagation()
on:click={e => {
e.stopPropagation()
dispatch("toggle")
}}
hoverable
name={open ? "ChevronUp" : "ChevronDown"}
/>
@ -195,7 +213,10 @@
{/if}
{#if !showTestStatus}
<Icon
on:click={() => dispatch("toggle")}
on:click={e => {
e.stopPropagation()
dispatch("toggle")
}}
hoverable
name={open ? "ChevronUp" : "ChevronDown"}
/>

View file

@ -1,11 +1,9 @@
<script>
import {
ModalContent,
Tabs,
Tab,
TextArea,
Label,
notifications,
ActionButton,
} from "@budibase/bbui"
import { automationStore, selectedAutomation } from "builderStore"
import AutomationBlockSetup from "../../SetupPanel/AutomationBlockSetup.svelte"
@ -55,50 +53,69 @@
notifications.error(error)
}
}
const toggle = () => {
selectedValues = !selectedValues
selectedJSON = !selectedJSON
}
let selectedValues = true
let selectedJSON = false
</script>
<ModalContent
title="Add test data"
confirmText="Test"
size="M"
confirmText="Run test"
size="L"
showConfirmButton={true}
disabled={isError}
onConfirm={testAutomation}
cancelText="Cancel"
>
<Tabs selected="Form" quiet>
<Tab icon="Form" title="Form">
<div class="tab-content-padding">
<AutomationBlockSetup
{testData}
{schemaProperties}
isTestModal
block={trigger}
/>
</div></Tab
>
<Tab icon="FileJson" title="JSON">
<div class="tab-content-padding">
<Label>JSON</Label>
<div class="text-area-container">
<TextArea
value={JSON.stringify($selectedAutomation.testData, null, 2)}
error={failedParse}
on:change={e => parseTestJSON(e)}
/>
</div>
</div>
</Tab>
</Tabs>
<div class="size">
<div class="options">
<ActionButton quiet selected={selectedValues} on:click={toggle}
>Use values</ActionButton
>
<ActionButton quiet selected={selectedJSON} on:click={toggle}
>Use JSON</ActionButton
>
</div>
</div>
{#if selectedValues}
<div class="tab-content-padding">
<AutomationBlockSetup
{testData}
{schemaProperties}
isTestModal
block={trigger}
/>
</div>
{/if}
{#if selectedJSON}
<div class="text-area-container">
<TextArea
value={JSON.stringify($selectedAutomation.testData, null, 2)}
error={failedParse}
on:change={e => parseTestJSON(e)}
/>
</div>
{/if}
</ModalContent>
<style>
.text-area-container :global(textarea) {
min-height: 200px;
height: 200px;
min-height: 300px;
height: 300px;
}
.tab-content-padding {
padding: 0 var(--spacing-xl);
padding: 0 var(--spacing-s);
}
.options {
display: flex;
align-items: center;
gap: 8px;
}
</style>

View file

@ -9,7 +9,7 @@
<div class="title">
<div class="title-text">
<Icon name="MultipleCheck" />
<div style="padding-left: var(--spacing-l)">Test Details</div>
<div style="padding-left: var(--spacing-l); ">Test Details</div>
</div>
<div style="padding-right: var(--spacing-xl)">
<Icon
@ -40,6 +40,7 @@
display: flex;
flex-direction: row;
align-items: center;
padding-top: var(--spacing-s);
}
.title :global(h1) {

View file

@ -1,20 +1,44 @@
<script>
import AutomationList from "./AutomationList.svelte"
import CreateAutomationModal from "./CreateAutomationModal.svelte"
import { Modal, Button, Layout } from "@budibase/bbui"
import { Modal, Icon } from "@budibase/bbui"
import Panel from "components/design/Panel.svelte"
export let modal
export let webhookModal
</script>
<Panel title="Automations" borderRight>
<Layout paddingX="L" paddingY="XL" gap="S">
<Button cta on:click={modal.show}>Add automation</Button>
</Layout>
<Panel title="Automations" borderRight noHeaderBorder titleCSS={false}>
<span class="panel-title-content" slot="panel-title-content">
<div class="header">
<div>Automations</div>
<div on:click={modal.show} class="add-automation-button">
<Icon name="Add" />
</div>
</div>
</span>
<AutomationList />
</Panel>
<Modal bind:this={modal}>
<CreateAutomationModal {webhookModal} />
</Modal>
<style>
.header {
display: flex;
align-items: center;
justify-content: space-between;
gap: var(--spacing-m);
}
.add-automation-button {
margin-left: 130px;
color: var(--grey-7);
cursor: pointer;
}
.add-automation-button:hover {
color: var(--ink);
}
</style>

View file

@ -149,7 +149,6 @@
}
const initialiseField = (field, savingColumn) => {
isCreating = !field
if (field && !savingColumn) {
editableColumn = cloneDeep(field)
originalName = editableColumn.name ? editableColumn.name + "" : null
@ -171,7 +170,8 @@
relationshipPart2 = part2
}
}
} else if (!savingColumn) {
}
if (!savingColumn) {
let highestNumber = 0
Object.keys(table.schema).forEach(columnName => {
const columnNumber = extractColumnNumber(columnName)
@ -307,12 +307,6 @@
dispatch("updatecolumns")
gridDispatch("close-edit-column")
if (saveColumn.type === LINK_TYPE) {
// Fetching the new tables
tables.fetch()
// Fetching the new relationships
datasources.fetch()
}
if (originalName) {
notifications.success("Column updated successfully")
} else {
@ -339,11 +333,6 @@
confirmDeleteDialog.hide()
dispatch("updatecolumns")
gridDispatch("close-edit-column")
if (editableColumn.type === LINK_TYPE) {
// Updating the relationships
datasources.fetch()
}
}
} catch (error) {
notifications.error(`Error deleting column: ${error.message}`)
@ -540,8 +529,16 @@
<Layout noPadding gap="S">
{#if mounted}
<Input
value={editableColumn.name}
autofocus
bind:value={editableColumn.name}
on:input={e => {
if (
!uneditable &&
!(linkEditDisabled && editableColumn.type === LINK_TYPE)
) {
editableColumn.name = e.target.value
}
}}
disabled={uneditable ||
(linkEditDisabled && editableColumn.type === LINK_TYPE)}
error={errors?.name}

View file

@ -16,7 +16,8 @@
export let wide = false
export let extraWide = false
export let closeButtonIcon = "Close"
export let noHeaderBorder = false
export let titleCSS = true
$: customHeaderContent = $$slots["panel-header-content"]
$: customTitleContent = $$slots["panel-title-content"]
</script>
@ -32,6 +33,7 @@
class="header"
class:custom={customHeaderContent}
class:borderBottom={borderBottomHeader}
class:noHeaderBorder
>
{#if showBackButton}
<Icon name="ArrowLeft" hoverable on:click={onClickBackButton} />
@ -41,7 +43,7 @@
<Icon name={icon} />
</AbsTooltip>
{/if}
<div class="title">
<div class:title={titleCSS}>
{#if customTitleContent}
<slot name="panel-title-content" />
{:else}
@ -106,6 +108,10 @@
padding: 0 var(--spacing-l);
gap: var(--spacing-m);
}
.noHeaderBorder {
border-bottom: none !important;
}
.header.borderBottom {
border-bottom: var(--border-light);
}

View file

@ -110,7 +110,7 @@
}
.setup {
padding-top: var(--spectrum-global-dimension-size-200);
padding-top: 9px;
border-left: var(--border-light);
display: flex;
flex-direction: column;

View file

@ -81,13 +81,21 @@ export function createTablesStore() {
replaceTable(savedTable._id, savedTable)
select(savedTable._id)
// make sure tables up to date (related)
let tableIdsToFetch = []
let newTableIds = []
for (let column of Object.values(updatedTable?.schema || {})) {
if (column.type === FIELDS.LINK.type) {
tableIdsToFetch.push(column.tableId)
newTableIds.push(column.tableId)
}
}
tableIdsToFetch = [...new Set(tableIdsToFetch)]
let oldTableIds = []
for (let column of Object.values(oldTable?.schema || {})) {
if (column.type === FIELDS.LINK.type) {
oldTableIds.push(column.tableId)
}
}
const tableIdsToFetch = [...new Set([...newTableIds, ...oldTableIds])]
// too many tables to fetch, just get all
if (tableIdsToFetch.length > 3) {
await fetch()

View file

@ -6112,54 +6112,32 @@
}
]
},
{
"tag": "style",
"type": "select",
"label": "Button position",
"key": "buttonPosition",
"options": [
{
"label": "Bottom",
"value": "bottom"
},
{
"label": "Top",
"value": "top"
}
],
"defaultValue": "bottom"
},
{
"section": true,
"name": "Buttons",
"dependsOn": {
"setting": "actionType",
"value": "View",
"invert": true
},
"settings": [
{
"type": "text",
"key": "saveButtonLabel",
"label": "Save button",
"type": "buttonConfiguration",
"key": "buttons",
"nested": true,
"defaultValue": "Save"
},
{
"type": "text",
"key": "deleteButtonLabel",
"label": "Delete button",
"nested": true,
"defaultValue": "Delete",
"dependsOn": {
"setting": "actionType",
"value": "Update"
}
},
{
"type": "url",
"label": "Navigate after button press",
"key": "actionUrl",
"placeholder": "Choose a screen",
"dependsOn": {
"setting": "actionType",
"value": "View",
"invert": true
}
},
{
"type": "boolean",
"label": "Hide notifications",
"key": "notificationOverride",
"defaultValue": false,
"dependsOn": {
"setting": "actionType",
"value": "View",
"invert": true
}
"resetOn": ["actionType", "dataSource"]
}
]
},

View file

@ -5,6 +5,7 @@
import BlockComponent from "components/BlockComponent.svelte"
import { makePropSafe as safe } from "@budibase/string-templates"
import { enrichSearchColumns, enrichFilter } from "utils/blocks.js"
import { Utils } from "@budibase/frontend-core"
export let title
export let dataSource
@ -33,6 +34,7 @@
export let notificationOverride
const { fetchDatasourceSchema, API } = getContext("sdk")
const component = getContext("component")
const stateKey = `ID_${generate()}`
let formId
@ -259,16 +261,25 @@
name="Details form block"
type="formblock"
bind:id={detailsFormBlockId}
context="form-edit"
props={{
dataSource,
saveButtonLabel: sidePanelSaveLabel || "Save", //always show
deleteButtonLabel: deleteLabel,
buttonPosition: "top",
buttons: Utils.buildDynamicButtonConfig({
_id: $component.id + "-form-edit",
showDeleteButton: deleteLabel !== "",
showSaveButton: true,
saveButtonLabel: sidePanelSaveLabel || "Save",
deleteButtonLabel: deleteLabel,
notificationOverride,
actionType: "Update",
dataSource,
}),
actionType: "Update",
rowId: `{{ ${safe("state")}.${safe(stateKey)} }}`,
fields: sidePanelFields || normalFields,
title: editTitle,
labelPosition: "left",
notificationOverride,
}}
/>
</BlockComponent>
@ -284,16 +295,23 @@
<BlockComponent
name="New row form block"
type="formblock"
context="form-new"
props={{
dataSource,
showSaveButton: true,
showDeleteButton: false,
saveButtonLabel: sidePanelSaveLabel || "Save", //always show
buttonPosition: "top",
buttons: Utils.buildDynamicButtonConfig({
_id: $component.id + "-form-new",
showDeleteButton: false,
showSaveButton: true,
saveButtonLabel: "Save",
notificationOverride,
actionType: "Create",
dataSource,
}),
actionType: "Create",
fields: sidePanelFields || normalFields,
title: "Create Row",
labelPosition: "left",
notificationOverride,
}}
/>
</BlockComponent>

View file

@ -4,28 +4,31 @@
import Block from "components/Block.svelte"
import { makePropSafe as safe } from "@budibase/string-templates"
import InnerFormBlock from "./InnerFormBlock.svelte"
import { Utils } from "@budibase/frontend-core"
export let actionType
export let dataSource
export let size
export let disabled
export let fields
export let buttons
export let buttonPosition
export let title
export let description
export let showDeleteButton
export let showSaveButton
export let saveButtonLabel
export let deleteButtonLabel
export let rowId
export let actionUrl
export let noRowsMessage
export let notificationOverride
// Accommodate old config to ensure delete button does not reappear
$: deleteLabel = showDeleteButton === false ? "" : deleteButtonLabel?.trim()
$: saveLabel = showSaveButton === false ? "" : saveButtonLabel?.trim()
// Legacy
export let showDeleteButton
export let showSaveButton
export let saveButtonLabel
export let deleteButtonLabel
const { fetchDatasourceSchema } = getContext("sdk")
const component = getContext("component")
const convertOldFieldFormat = fields => {
if (!fields) {
@ -98,11 +101,23 @@
fields: fieldsOrDefault,
title,
description,
saveButtonLabel: saveLabel,
deleteButtonLabel: deleteLabel,
schema,
repeaterId,
notificationOverride,
buttons:
buttons ||
Utils.buildDynamicButtonConfig({
_id: $component.id,
showDeleteButton,
showSaveButton,
saveButtonLabel,
deleteButtonLabel,
notificationOverride,
actionType,
actionUrl,
dataSource,
}),
buttonPosition: buttons ? buttonPosition : "top",
}
const fetchSchema = async () => {
schema = (await fetchDatasourceSchema(dataSource)) || {}

View file

@ -1,22 +1,18 @@
<script>
import BlockComponent from "components/BlockComponent.svelte"
import Placeholder from "components/app/Placeholder.svelte"
import { makePropSafe as safe } from "@budibase/string-templates"
import { getContext } from "svelte"
export let dataSource
export let actionUrl
export let actionType
export let size
export let disabled
export let fields
export let title
export let description
export let saveButtonLabel
export let deleteButtonLabel
export let buttons
export let buttonPosition = "bottom"
export let schema
export let repeaterId
export let notificationOverride
const FieldTypeToComponentMap = {
string: "stringfield",
@ -37,74 +33,7 @@
let formId
$: onSave = [
{
"##eventHandlerType": "Validate Form",
parameters: {
componentId: formId,
},
},
{
"##eventHandlerType": "Save Row",
parameters: {
providerId: formId,
tableId: dataSource?.resourceId,
notificationOverride,
},
},
{
"##eventHandlerType": "Close Screen Modal",
},
{
"##eventHandlerType": "Close Side Panel",
},
// Clear a create form once submitted
...(actionType !== "Create"
? []
: [
{
"##eventHandlerType": "Clear Form",
parameters: {
componentId: formId,
},
},
]),
{
"##eventHandlerType": "Navigate To",
parameters: {
url: actionUrl,
},
},
]
$: onDelete = [
{
"##eventHandlerType": "Delete Row",
parameters: {
confirm: true,
tableId: dataSource?.resourceId,
rowId: `{{ ${safe(repeaterId)}.${safe("_id")} }}`,
revId: `{{ ${safe(repeaterId)}.${safe("_rev")} }}`,
notificationOverride,
},
},
{
"##eventHandlerType": "Close Screen Modal",
},
{
"##eventHandlerType": "Close Side Panel",
},
{
"##eventHandlerType": "Navigate To",
parameters: {
url: actionUrl,
},
},
]
$: renderDeleteButton = deleteButtonLabel && actionType === "Update"
$: renderSaveButton = saveButtonLabel && actionType !== "View"
$: renderButtons = renderDeleteButton || renderSaveButton
$: renderHeader = renderButtons || title
$: renderHeader = buttons || title
const getComponentForField = field => {
const fieldSchemaName = field.field || field.name
@ -184,42 +113,14 @@
props={{ text: title || "" }}
order={0}
/>
{#if renderButtons}
{#if buttonPosition == "top"}
<BlockComponent
type="container"
type="buttongroup"
props={{
direction: "row",
hAlign: "stretch",
vAlign: "center",
gap: "M",
wrap: true,
buttons,
}}
order={1}
>
{#if renderDeleteButton}
<BlockComponent
type="button"
props={{
text: deleteButtonLabel,
onClick: onDelete,
quiet: true,
type: "secondary",
}}
order={0}
/>
{/if}
{#if renderSaveButton}
<BlockComponent
type="button"
props={{
text: saveButtonLabel,
onClick: onSave,
type: "cta",
}}
order={1}
/>
{/if}
</BlockComponent>
order={0}
/>
{/if}
</BlockComponent>
</BlockComponent>
@ -245,6 +146,20 @@
</BlockComponent>
{/key}
</BlockComponent>
{#if buttonPosition === "bottom"}
<BlockComponent
type="buttongroup"
props={{
buttons,
}}
styles={{
normal: {
"margin-top": "16",
},
}}
order={1}
/>
{/if}
</BlockComponent>
{:else}
<Placeholder

View file

@ -1,3 +1,6 @@
import { makePropSafe as safe } from "@budibase/string-templates"
import { Helpers } from "@budibase/bbui"
/**
* Utility to wrap an async function and ensure all invocations happen
* sequentially.
@ -106,3 +109,135 @@ export const domDebounce = callback => {
}
}
}
/**
* Build the default FormBlock button configs per actionType
* Parse any legacy button config and mirror its the outcome
*
* @param {any} props
* */
export const buildDynamicButtonConfig = props => {
const {
_id,
actionType,
dataSource,
notificationOverride,
actionUrl,
showDeleteButton,
deleteButtonLabel,
showSaveButton,
saveButtonLabel,
} = props || {}
if (!_id) {
console.log("MISSING ID")
return
}
const formId = `${_id}-form`
const repeaterId = `${_id}-repeater`
const resourceId = dataSource?.resourceId
// Accommodate old config to ensure delete button does not reappear
const deleteText = showDeleteButton === false ? "" : deleteButtonLabel?.trim()
const saveText = showSaveButton === false ? "" : saveButtonLabel?.trim()
const onSave = [
{
"##eventHandlerType": "Validate Form",
parameters: {
componentId: formId,
},
},
{
"##eventHandlerType": "Save Row",
parameters: {
providerId: formId,
tableId: resourceId,
notificationOverride,
},
},
{
"##eventHandlerType": "Close Screen Modal",
},
{
"##eventHandlerType": "Close Side Panel",
},
// Clear a create form once submitted
...(actionType !== "Create"
? []
: [
{
"##eventHandlerType": "Clear Form",
parameters: {
componentId: formId,
},
},
]),
...(actionUrl
? [
{
"##eventHandlerType": "Navigate To",
parameters: {
url: actionUrl,
},
},
]
: []),
]
const onDelete = [
{
"##eventHandlerType": "Delete Row",
parameters: {
confirm: true,
tableId: resourceId,
rowId: `{{ ${safe(repeaterId)}.${safe("_id")} }}`,
revId: `{{ ${safe(repeaterId)}.${safe("_rev")} }}`,
notificationOverride,
},
},
{
"##eventHandlerType": "Close Screen Modal",
},
{
"##eventHandlerType": "Close Side Panel",
},
...(actionUrl
? [
{
"##eventHandlerType": "Navigate To",
parameters: {
url: actionUrl,
},
},
]
: []),
]
const defaultButtons = []
if (["Update", "Create"].includes(actionType) && showSaveButton !== false) {
defaultButtons.push({
text: saveText || "Save",
_id: Helpers.uuid(),
_component: "@budibase/standard-components/button",
onClick: onSave,
type: "cta",
})
}
if (actionType == "Update" && showDeleteButton !== false) {
defaultButtons.push({
text: deleteText || "Delete",
_id: Helpers.uuid(),
_component: "@budibase/standard-components/button",
onClick: onDelete,
quiet: true,
type: "secondary",
})
}
return defaultButtons
}

View file

@ -5,6 +5,7 @@ import {
tenancy,
context,
users,
auth,
} from "@budibase/backend-core"
import { generateUserMetadataID, isDevAppID } from "../db/utils"
import { getCachedSelf } from "../utilities/global"
@ -69,28 +70,34 @@ export default async (ctx: UserCtx, next: any) => {
return next()
}
return context.doInAppContext(appId, async () => {
// if the user not in the right tenant then make sure they have no permissions
// need to judge this only based on the request app ID,
if (
env.MULTI_TENANCY &&
ctx.user?._id &&
requestAppId &&
!tenancy.isUserInAppTenant(requestAppId, ctx.user)
) {
// don't error, simply remove the users rights (they are a public user)
ctx.user = users.cleanseUserObject(ctx.user) as ContextUser
ctx.isAuthenticated = false
roleId = roles.BUILTIN_ROLE_IDS.PUBLIC
}
const userId = ctx.user ? generateUserMetadataID(ctx.user._id!) : undefined
// if the user is not in the right tenant then make sure to wipe their cookie
// also cleanse any information about them that has been allocated
// this avoids apps making calls to say the worker which are cross tenant,
// we simply remove the authentication
if (
env.MULTI_TENANCY &&
userId &&
requestAppId &&
!tenancy.isUserInAppTenant(requestAppId, ctx.user)
) {
// clear out the user
ctx.user = users.cleanseUserObject(ctx.user) as ContextUser
ctx.isAuthenticated = false
roleId = roles.BUILTIN_ROLE_IDS.PUBLIC
// remove the cookie, so future calls are public
await auth.platformLogout({
ctx,
userId,
})
}
return context.doInAppContext(appId, async () => {
ctx.appId = appId
if (roleId) {
ctx.roleId = roleId
const globalId = ctx.user ? ctx.user._id : undefined
const userId = ctx.user
? generateUserMetadataID(ctx.user._id!)
: undefined
ctx.user = {
...ctx.user!,
// override userID with metadata one

View file

@ -137,6 +137,10 @@ class TestConfiguration {
}
getAppId() {
if (!this.appId) {
throw "appId has not been initialised properly"
}
return this.appId
}
@ -510,7 +514,7 @@ class TestConfiguration {
// create dev app
// clear any old app
this.appId = null
this.app = await context.doInAppContext(null, async () => {
this.app = await context.doInTenant(this.tenantId!, async () => {
const app = await this._req(
{ name: appName },
null,
@ -519,7 +523,7 @@ class TestConfiguration {
this.appId = app.appId!
return app
})
return await context.doInAppContext(this.appId, async () => {
return await context.doInAppContext(this.getAppId(), async () => {
// create production app
this.prodApp = await this.publish()
@ -817,7 +821,7 @@ class TestConfiguration {
}
async getAutomationLogs() {
return context.doInAppContext(this.appId, async () => {
return context.doInAppContext(this.getAppId(), async () => {
const now = new Date()
return await pro.sdk.automations.logs.logSearch({
startDate: new Date(now.getTime() - 100000).toISOString(),