mirror of
https://github.com/formbricks/formbricks.git
synced 2026-05-21 11:49:32 -05:00
Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 46dfa15756 |
@@ -65,6 +65,8 @@ Cube is part of the baseline Formbricks v5 stack and is deployed by this chart b
|
||||
|
||||
The chart deploys Hub API and, by default, a `hub-worker` deployment. Hub API is insert-only for River jobs; webhook dispatch and embedding jobs are processed by `hub-worker`.
|
||||
|
||||
When the Formbricks migration job is enabled, Hub waits for the `formbricks-migration` Job to complete before its own goose/river init migrations run. This keeps fresh shared-database installs from creating Hub tables before Prisma has initialized the Formbricks schema.
|
||||
|
||||
Self-hosted embeddings are disabled by default. Set `hub.embeddings.enabled=true` to deploy an internal Hugging Face Text Embeddings Inference (TEI) service and wire Hub API plus Hub worker to it through the OpenAI-compatible endpoint added in Hub:
|
||||
|
||||
```yaml
|
||||
@@ -220,6 +222,10 @@ Autoscaling is opt-in for Hub API, Hub worker, and the embeddings runtime. If yo
|
||||
| hub.migration.activeDeadlineSeconds | int | `900` | |
|
||||
| hub.migration.backoffLimit | int | `3` | |
|
||||
| hub.migration.ttlSecondsAfterFinished | int | `300` | |
|
||||
| hub.migration.waitForFormbricksMigration.enabled | bool | `true` | |
|
||||
| hub.migration.waitForFormbricksMigration.intervalSeconds | int | `5` | |
|
||||
| hub.migration.waitForFormbricksMigration.maxAttempts | int | `180` | |
|
||||
| hub.migration.waitForFormbricksMigration.missingJobMaxAttempts | int | `12` | |
|
||||
| hub.pdb.enabled | bool | `false` | |
|
||||
| hub.replicas | int | `1` | |
|
||||
| hub.resources.limits.memory | string | `"512Mi"` | |
|
||||
|
||||
@@ -125,10 +125,18 @@ If `namespaceOverride` is provided, it will be used; otherwise, it defaults to `
|
||||
{{- printf "%s-app-secrets" (include "formbricks.name" .) -}}
|
||||
{{- end }}
|
||||
|
||||
{{- define "formbricks.migrationJobName" -}}
|
||||
{{- printf "%s-migration" (include "formbricks.name" .) | trunc 63 | trimSuffix "-" -}}
|
||||
{{- end }}
|
||||
|
||||
{{- define "formbricks.hubSecretName" -}}
|
||||
{{- default (include "formbricks.appSecretName" .) .Values.hub.existingSecret -}}
|
||||
{{- end }}
|
||||
|
||||
{{- define "formbricks.hubMigrationWaitServiceAccountName" -}}
|
||||
{{- printf "%s-migration-wait" (include "formbricks.hubname" .) | trunc 63 | trimSuffix "-" -}}
|
||||
{{- end }}
|
||||
|
||||
{{/*
|
||||
Hub image reference. Pin by digest in production (hub.image.digest = "sha256:..."); falls back to
|
||||
hub.image.tag for local/dev. All Hub workloads (deployment, init container, migration job, future
|
||||
|
||||
@@ -32,7 +32,134 @@ spec:
|
||||
imagePullSecrets:
|
||||
{{- toYaml .Values.deployment.imagePullSecrets | nindent 8 }}
|
||||
{{- end }}
|
||||
{{- if and .Values.migration.enabled .Values.hub.migration.waitForFormbricksMigration.enabled }}
|
||||
serviceAccountName: {{ include "formbricks.hubMigrationWaitServiceAccountName" . }}
|
||||
automountServiceAccountToken: true
|
||||
{{- end }}
|
||||
initContainers:
|
||||
{{- if and .Values.migration.enabled .Values.hub.migration.waitForFormbricksMigration.enabled }}
|
||||
- name: wait-for-formbricks-migration
|
||||
image: {{ .Values.deployment.image.repository }}:{{ .Values.deployment.image.tag | default .Chart.AppVersion | default "latest" }}
|
||||
imagePullPolicy: {{ .Values.deployment.image.pullPolicy }}
|
||||
command:
|
||||
- node
|
||||
- -e
|
||||
- |
|
||||
const fs = require("fs");
|
||||
const https = require("https");
|
||||
|
||||
const maxAttempts = Number.parseInt(process.env.FORMBRICKS_MIGRATION_WAIT_MAX_ATTEMPTS || "180", 10);
|
||||
const missingJobMaxAttempts = Number.parseInt(
|
||||
process.env.FORMBRICKS_MIGRATION_WAIT_MISSING_JOB_MAX_ATTEMPTS || "12",
|
||||
10
|
||||
);
|
||||
const intervalSeconds = Number.parseInt(process.env.FORMBRICKS_MIGRATION_WAIT_INTERVAL_SECONDS || "5", 10);
|
||||
const jobName = process.env.FORMBRICKS_MIGRATION_JOB_NAME;
|
||||
const namespace = fs
|
||||
.readFileSync("/var/run/secrets/kubernetes.io/serviceaccount/namespace", "utf8")
|
||||
.trim();
|
||||
const token = fs.readFileSync("/var/run/secrets/kubernetes.io/serviceaccount/token", "utf8");
|
||||
const ca = fs.readFileSync("/var/run/secrets/kubernetes.io/serviceaccount/ca.crt");
|
||||
const host = process.env.KUBERNETES_SERVICE_HOST;
|
||||
const port = process.env.KUBERNETES_SERVICE_PORT || "443";
|
||||
const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
||||
const jobPath = `/apis/batch/v1/namespaces/${namespace}/jobs/${jobName}`;
|
||||
|
||||
const fetchJob = () =>
|
||||
new Promise((resolve, reject) => {
|
||||
const request = https.request(
|
||||
{
|
||||
host,
|
||||
port,
|
||||
path: jobPath,
|
||||
method: "GET",
|
||||
ca,
|
||||
headers: {
|
||||
Authorization: `Bearer ${token}`,
|
||||
},
|
||||
},
|
||||
(response) => {
|
||||
let body = "";
|
||||
|
||||
response.setEncoding("utf8");
|
||||
response.on("data", (chunk) => {
|
||||
body += chunk;
|
||||
});
|
||||
response.on("end", () => {
|
||||
if (response.statusCode >= 200 && response.statusCode < 300) {
|
||||
resolve(JSON.parse(body));
|
||||
return;
|
||||
}
|
||||
|
||||
const error = new Error(`Kubernetes API returned ${response.statusCode}: ${body}`);
|
||||
error.statusCode = response.statusCode;
|
||||
reject(error);
|
||||
});
|
||||
}
|
||||
);
|
||||
|
||||
request.on("error", reject);
|
||||
request.end();
|
||||
});
|
||||
|
||||
(async () => {
|
||||
for (let attempt = 1; attempt <= maxAttempts; attempt += 1) {
|
||||
try {
|
||||
const job = await fetchJob();
|
||||
const conditions = job.status?.conditions || [];
|
||||
const isComplete = conditions.some(
|
||||
(condition) => condition.type === "Complete" && condition.status === "True"
|
||||
);
|
||||
const isFailed = conditions.some(
|
||||
(condition) => condition.type === "Failed" && condition.status === "True"
|
||||
);
|
||||
|
||||
if (isComplete) {
|
||||
console.log(`${jobName} completed; starting Hub migrations.`);
|
||||
return;
|
||||
}
|
||||
|
||||
if (isFailed) {
|
||||
console.error(`${jobName} failed; refusing to start Hub migrations.`);
|
||||
process.exitCode = 1;
|
||||
return;
|
||||
}
|
||||
|
||||
console.log(`Waiting for ${jobName} to complete (${attempt}/${maxAttempts})...`);
|
||||
} catch (error) {
|
||||
const message = error instanceof Error ? error.message : String(error);
|
||||
if (error && error.statusCode === 404 && attempt >= missingJobMaxAttempts) {
|
||||
console.log(`${jobName} was not found after ${attempt} attempts; assuming it was already cleaned up.`);
|
||||
return;
|
||||
}
|
||||
|
||||
console.log(`Waiting for ${jobName} to be available (${attempt}/${maxAttempts}): ${message}`);
|
||||
}
|
||||
|
||||
await sleep(intervalSeconds * 1000);
|
||||
}
|
||||
|
||||
console.error(`Timed out waiting for ${jobName} after ${maxAttempts} attempts.`);
|
||||
process.exitCode = 1;
|
||||
})()
|
||||
.catch((error) => {
|
||||
console.error(error);
|
||||
process.exitCode = 1;
|
||||
});
|
||||
env:
|
||||
- name: FORMBRICKS_MIGRATION_JOB_NAME
|
||||
value: {{ include "formbricks.migrationJobName" . | quote }}
|
||||
- name: FORMBRICKS_MIGRATION_WAIT_MAX_ATTEMPTS
|
||||
value: {{ .Values.hub.migration.waitForFormbricksMigration.maxAttempts | quote }}
|
||||
- name: FORMBRICKS_MIGRATION_WAIT_MISSING_JOB_MAX_ATTEMPTS
|
||||
value: {{ .Values.hub.migration.waitForFormbricksMigration.missingJobMaxAttempts | quote }}
|
||||
- name: FORMBRICKS_MIGRATION_WAIT_INTERVAL_SECONDS
|
||||
value: {{ .Values.hub.migration.waitForFormbricksMigration.intervalSeconds | quote }}
|
||||
{{- if .Values.migration.resources }}
|
||||
resources:
|
||||
{{- toYaml .Values.migration.resources | nindent 12 }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
- name: hub-migrate
|
||||
image: {{ include "formbricks.hubImage" . }}
|
||||
imagePullPolicy: {{ .Values.hub.image.pullPolicy }}
|
||||
|
||||
@@ -0,0 +1,55 @@
|
||||
{{- if and .Values.migration.enabled .Values.hub.migration.waitForFormbricksMigration.enabled }}
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: ServiceAccount
|
||||
metadata:
|
||||
name: {{ include "formbricks.hubMigrationWaitServiceAccountName" . }}
|
||||
labels:
|
||||
helm.sh/chart: {{ include "formbricks.chart" . }}
|
||||
app.kubernetes.io/name: {{ include "formbricks.hubname" . }}
|
||||
app.kubernetes.io/instance: {{ .Release.Name }}
|
||||
app.kubernetes.io/component: hub-migration-wait
|
||||
app.kubernetes.io/managed-by: {{ .Release.Service }}
|
||||
app.kubernetes.io/part-of: {{ .Values.partOfOverride | default (include "formbricks.name" .) }}
|
||||
---
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: Role
|
||||
metadata:
|
||||
name: {{ include "formbricks.hubMigrationWaitServiceAccountName" . }}
|
||||
labels:
|
||||
helm.sh/chart: {{ include "formbricks.chart" . }}
|
||||
app.kubernetes.io/name: {{ include "formbricks.hubname" . }}
|
||||
app.kubernetes.io/instance: {{ .Release.Name }}
|
||||
app.kubernetes.io/component: hub-migration-wait
|
||||
app.kubernetes.io/managed-by: {{ .Release.Service }}
|
||||
app.kubernetes.io/part-of: {{ .Values.partOfOverride | default (include "formbricks.name" .) }}
|
||||
rules:
|
||||
- apiGroups:
|
||||
- batch
|
||||
resources:
|
||||
- jobs
|
||||
resourceNames:
|
||||
- {{ include "formbricks.migrationJobName" . }}
|
||||
verbs:
|
||||
- get
|
||||
---
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: RoleBinding
|
||||
metadata:
|
||||
name: {{ include "formbricks.hubMigrationWaitServiceAccountName" . }}
|
||||
labels:
|
||||
helm.sh/chart: {{ include "formbricks.chart" . }}
|
||||
app.kubernetes.io/name: {{ include "formbricks.hubname" . }}
|
||||
app.kubernetes.io/instance: {{ .Release.Name }}
|
||||
app.kubernetes.io/component: hub-migration-wait
|
||||
app.kubernetes.io/managed-by: {{ .Release.Service }}
|
||||
app.kubernetes.io/part-of: {{ .Values.partOfOverride | default (include "formbricks.name" .) }}
|
||||
roleRef:
|
||||
apiGroup: rbac.authorization.k8s.io
|
||||
kind: Role
|
||||
name: {{ include "formbricks.hubMigrationWaitServiceAccountName" . }}
|
||||
subjects:
|
||||
- kind: ServiceAccount
|
||||
name: {{ include "formbricks.hubMigrationWaitServiceAccountName" . }}
|
||||
namespace: {{ include "formbricks.namespace" . }}
|
||||
{{- end }}
|
||||
@@ -3,7 +3,7 @@
|
||||
apiVersion: batch/v1
|
||||
kind: Job
|
||||
metadata:
|
||||
name: {{ include "formbricks.name" . }}-migration
|
||||
name: {{ include "formbricks.migrationJobName" . }}
|
||||
labels:
|
||||
{{- include "formbricks.labels" . | nindent 4 }}
|
||||
annotations:
|
||||
|
||||
@@ -909,6 +909,11 @@ hub:
|
||||
ttlSecondsAfterFinished: 300
|
||||
backoffLimit: 3
|
||||
activeDeadlineSeconds: 900
|
||||
waitForFormbricksMigration:
|
||||
enabled: true
|
||||
maxAttempts: 180
|
||||
missingJobMaxAttempts: 12
|
||||
intervalSeconds: 5
|
||||
|
||||
resources:
|
||||
limits:
|
||||
|
||||
Reference in New Issue
Block a user