mirror of
https://github.com/formbricks/formbricks.git
synced 2026-05-06 19:35:53 -05:00
Compare commits
3 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| e6b6f5e6d3 | |||
| ed42df34c4 | |||
| c5d629ef25 |
+3
-2
@@ -183,11 +183,12 @@ AZUREAD_TENANT_ID=
|
||||
# Configure Formbricks AI at the instance level
|
||||
# Set the provider used for AI features on this instance.
|
||||
# Accepted values for AI_PROVIDER: aws, google, azure
|
||||
# Set AI_MODEL to the provider-specific model or deployment name and configure the matching credentials below.
|
||||
# Set AI_MODEL to the provider-specific model or deployment name and configure the matching provider settings below.
|
||||
# AI_PROVIDER=google
|
||||
# AI_MODEL=gemini-2.5-flash
|
||||
|
||||
# Google Cloud credentials for Gemini models
|
||||
# Google Cloud settings for Gemini models
|
||||
# Credentials are optional when Application Default Credentials are available.
|
||||
# AI_GOOGLE_CLOUD_PROJECT=
|
||||
# AI_GOOGLE_CLOUD_LOCATION=
|
||||
# AI_GOOGLE_CLOUD_CREDENTIALS_JSON=
|
||||
|
||||
@@ -79,6 +79,35 @@ describe("env", () => {
|
||||
expect(env.DEBUG_SHOW_RESET_LINK).toBe("1");
|
||||
});
|
||||
|
||||
test("allows Google Cloud AI configuration to rely on ADC credentials", async () => {
|
||||
setTestEnv({
|
||||
AI_PROVIDER: "google",
|
||||
AI_MODEL: "gemini-2.5-flash",
|
||||
AI_GOOGLE_CLOUD_PROJECT: "test-project",
|
||||
AI_GOOGLE_CLOUD_LOCATION: "us-central1",
|
||||
AI_GOOGLE_CLOUD_CREDENTIALS_JSON: undefined,
|
||||
AI_GOOGLE_CLOUD_APPLICATION_CREDENTIALS: undefined,
|
||||
});
|
||||
|
||||
const { env } = await import("./env");
|
||||
|
||||
expect(env.AI_PROVIDER).toBe("google");
|
||||
expect(env.AI_GOOGLE_CLOUD_PROJECT).toBe("test-project");
|
||||
expect(env.AI_GOOGLE_CLOUD_LOCATION).toBe("us-central1");
|
||||
});
|
||||
|
||||
test("fails to load when Google Cloud credentials JSON is invalid", async () => {
|
||||
setTestEnv({
|
||||
AI_PROVIDER: "google",
|
||||
AI_MODEL: "gemini-2.5-flash",
|
||||
AI_GOOGLE_CLOUD_PROJECT: "test-project",
|
||||
AI_GOOGLE_CLOUD_LOCATION: "us-central1",
|
||||
AI_GOOGLE_CLOUD_CREDENTIALS_JSON: "{not-json}",
|
||||
});
|
||||
|
||||
await expect(import("./env")).rejects.toThrow("AI_GOOGLE_CLOUD_CREDENTIALS_JSON");
|
||||
});
|
||||
|
||||
test("uses the configured Cube environment variables", async () => {
|
||||
setTestEnv();
|
||||
const { env } = await import("./env");
|
||||
|
||||
@@ -68,14 +68,6 @@ const validateGoogleAIConfiguration = (values: TAIConfigurationEnv, ctx: z.Refin
|
||||
);
|
||||
}
|
||||
|
||||
if (!values.AI_GOOGLE_CLOUD_CREDENTIALS_JSON && !values.AI_GOOGLE_CLOUD_APPLICATION_CREDENTIALS) {
|
||||
addEnvIssue(
|
||||
ctx,
|
||||
"AI_GOOGLE_CLOUD_CREDENTIALS_JSON",
|
||||
"AI_GOOGLE_CLOUD_CREDENTIALS_JSON or AI_GOOGLE_CLOUD_APPLICATION_CREDENTIALS is required when AI_PROVIDER=google"
|
||||
);
|
||||
}
|
||||
|
||||
if (values.AI_GOOGLE_CLOUD_CREDENTIALS_JSON) {
|
||||
try {
|
||||
const parsedCredentials = JSON.parse(values.AI_GOOGLE_CLOUD_CREDENTIALS_JSON) as unknown;
|
||||
|
||||
@@ -54,6 +54,38 @@ This chart does not deploy Cube.js. XM Suite v5 dashboard and analysis features
|
||||
- Provide `CUBEJS_API_SECRET` through your existing secret management flow, such as the generated app secret override or `deployment.envFrom`.
|
||||
- Keep Hub enabled. Cube should point at the same feedback records database that Hub writes to, unless you intentionally split that storage.
|
||||
|
||||
## Hub worker and self-hosted embeddings
|
||||
|
||||
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`.
|
||||
|
||||
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
|
||||
hub:
|
||||
worker:
|
||||
enabled: true
|
||||
|
||||
embeddings:
|
||||
enabled: true
|
||||
model: google/embeddinggemma-300m
|
||||
servedModelName: google/embeddinggemma-300m
|
||||
huggingFace:
|
||||
token: hf_...
|
||||
```
|
||||
|
||||
The generated Hub embedding configuration is:
|
||||
|
||||
- `EMBEDDING_PROVIDER=openai`
|
||||
- `EMBEDDING_MODEL=<hub.embeddings.servedModelName or hub.embeddings.model>`
|
||||
- `EMBEDDING_BASE_URL=http://<release>-hub-embeddings:8080/v1`
|
||||
- `EMBEDDING_PROVIDER_API_KEY` from a dedicated embeddings Secret
|
||||
|
||||
The TEI service is internal-only (`ClusterIP`) and not exposed through ingress. For gated models such as `google/embeddinggemma-300m`, provide `hub.embeddings.huggingFace.token` or set `hub.embeddings.huggingFace.existingSecret`.
|
||||
|
||||
When TEI auth is enabled, configure the shared key through `hub.embeddings.auth.apiKey` or `hub.embeddings.auth.existingSecret`; the chart manages both TEI `API_KEY` and Hub `EMBEDDING_PROVIDER_API_KEY` from that source.
|
||||
|
||||
Autoscaling is opt-in for Hub API, Hub worker, and the embeddings runtime. If you scale the embeddings runtime above one replica while persistence is enabled, the cache PVC must support `ReadWriteMany`; otherwise set `hub.embeddings.persistence.enabled=false` or provide a compatible `existingClaim`.
|
||||
|
||||
## Values
|
||||
|
||||
| Key | Type | Default | Description |
|
||||
@@ -139,7 +171,40 @@ This chart does not deploy Cube.js. XM Suite v5 dashboard and analysis features
|
||||
| externalSecret.secretStore.name | string | `"aws-secrets-manager"` | |
|
||||
| formbricks.publicUrl | string | `""` | |
|
||||
| formbricks.webappUrl | string | `""` | |
|
||||
| hub.autoscaling.enabled | bool | `false` | |
|
||||
| hub.autoscaling.maxReplicas | int | `3` | |
|
||||
| hub.autoscaling.minReplicas | int | `1` | |
|
||||
| hub.enabled | bool | `true` | |
|
||||
| hub.embeddings.auth.enabled | bool | `true` | |
|
||||
| hub.embeddings.auth.existingSecret | string | `""` | |
|
||||
| hub.embeddings.auth.secretKey | string | `"EMBEDDING_PROVIDER_API_KEY"` | |
|
||||
| hub.embeddings.autoscaling.enabled | bool | `false` | |
|
||||
| hub.embeddings.autoscaling.maxReplicas | int | `2` | |
|
||||
| hub.embeddings.autoscaling.minReplicas | int | `1` | |
|
||||
| hub.embeddings.baseUrl | string | `""` | Defaults to the internal TEI service URL ending in `/v1`. |
|
||||
| hub.embeddings.enabled | bool | `false` | |
|
||||
| hub.embeddings.huggingFace.existingSecret | string | `""` | |
|
||||
| hub.embeddings.huggingFace.token | string | `""` | |
|
||||
| hub.embeddings.huggingFace.tokenKey | string | `"HF_TOKEN"` | |
|
||||
| hub.embeddings.image.pullPolicy | string | `"IfNotPresent"` | |
|
||||
| hub.embeddings.image.repository | string | `"ghcr.io/huggingface/text-embeddings-inference"` | |
|
||||
| hub.embeddings.image.tag | string | `"cpu-1.9"` | |
|
||||
| hub.embeddings.maxConcurrent | string | `"5"` | |
|
||||
| hub.embeddings.model | string | `"google/embeddinggemma-300m"` | |
|
||||
| hub.embeddings.persistence.enabled | bool | `true` | |
|
||||
| hub.embeddings.persistence.mountPath | string | `"/data"` | |
|
||||
| hub.embeddings.persistence.size | string | `"10Gi"` | |
|
||||
| hub.embeddings.pdb.enabled | bool | `false` | |
|
||||
| hub.embeddings.port | int | `8080` | |
|
||||
| hub.embeddings.prometheusPort | int | `9000` | |
|
||||
| hub.embeddings.replicas | int | `1` | |
|
||||
| hub.embeddings.resources.limits.memory | string | `"8Gi"` | |
|
||||
| hub.embeddings.resources.requests.cpu | string | `"4"` | |
|
||||
| hub.embeddings.resources.requests.memory | string | `"8Gi"` | |
|
||||
| hub.embeddings.runtime | string | `"tei"` | |
|
||||
| hub.embeddings.servedModelName | string | `""` | Defaults to `hub.embeddings.model`. |
|
||||
| hub.embeddings.service.port | int | `8080` | |
|
||||
| hub.embeddings.service.type | string | `"ClusterIP"` | |
|
||||
| hub.env | object | `{}` | |
|
||||
| hub.existingSecret | string | `""` | |
|
||||
| hub.image.digest | string | `"sha256:14db7b3d285b6e9165b55693f9b83d08beff840a255fd77dd12882ee0a62f5cb"` | When set, takes precedence over tag (immutable pin). |
|
||||
@@ -149,10 +214,21 @@ This chart does not deploy Cube.js. XM Suite v5 dashboard and analysis features
|
||||
| hub.migration.activeDeadlineSeconds | int | `900` | |
|
||||
| hub.migration.backoffLimit | int | `3` | |
|
||||
| hub.migration.ttlSecondsAfterFinished | int | `300` | |
|
||||
| hub.pdb.enabled | bool | `false` | |
|
||||
| hub.replicas | int | `1` | |
|
||||
| hub.resources.limits.memory | string | `"512Mi"` | |
|
||||
| hub.resources.requests.cpu | string | `"100m"` | |
|
||||
| hub.resources.requests.memory | string | `"256Mi"` | |
|
||||
| hub.worker.autoscaling.enabled | bool | `false` | |
|
||||
| hub.worker.autoscaling.maxReplicas | int | `5` | |
|
||||
| hub.worker.autoscaling.minReplicas | int | `1` | |
|
||||
| hub.worker.enabled | bool | `true` | |
|
||||
| hub.worker.env | object | `{}` | |
|
||||
| hub.worker.pdb.enabled | bool | `false` | |
|
||||
| hub.worker.replicas | int | `1` | |
|
||||
| hub.worker.resources.limits.memory | string | `"512Mi"` | |
|
||||
| hub.worker.resources.requests.cpu | string | `"100m"` | |
|
||||
| hub.worker.resources.requests.memory | string | `"256Mi"` | |
|
||||
| ingress.annotations | object | `{}` | |
|
||||
| ingress.enabled | bool | `false` | |
|
||||
| ingress.hosts[0].host | string | `"k8s.formbricks.com"` | |
|
||||
|
||||
@@ -114,6 +114,105 @@ hub-worker) must use this helper so they cannot drift apart.
|
||||
{{- end -}}
|
||||
{{- end }}
|
||||
|
||||
{{/*
|
||||
Hub worker resource name.
|
||||
*/}}
|
||||
{{- define "formbricks.hubWorkerName" -}}
|
||||
{{- $base := include "formbricks.name" . | trunc 52 | trimSuffix "-" }}
|
||||
{{- printf "%s-hub-worker" $base | trimSuffix "-" }}
|
||||
{{- end }}
|
||||
|
||||
{{/*
|
||||
Hub embeddings runtime resource name.
|
||||
*/}}
|
||||
{{- define "formbricks.hubEmbeddingsName" -}}
|
||||
{{- $base := include "formbricks.name" . | trunc 48 | trimSuffix "-" }}
|
||||
{{- printf "%s-hub-embeddings" $base | trimSuffix "-" }}
|
||||
{{- end }}
|
||||
|
||||
{{/*
|
||||
Secret used by Hub and the embeddings runtime for the embeddings API key.
|
||||
*/}}
|
||||
{{- define "formbricks.hubEmbeddingsSecretName" -}}
|
||||
{{- default (printf "%s-secret" (include "formbricks.hubEmbeddingsName" .)) .Values.hub.embeddings.auth.existingSecret -}}
|
||||
{{- end }}
|
||||
|
||||
{{/*
|
||||
Secret used by the embeddings runtime for Hugging Face access.
|
||||
*/}}
|
||||
{{- define "formbricks.hubEmbeddingsHuggingFaceSecretName" -}}
|
||||
{{- default (include "formbricks.hubEmbeddingsSecretName" .) .Values.hub.embeddings.huggingFace.existingSecret -}}
|
||||
{{- end }}
|
||||
|
||||
{{/*
|
||||
Model name Hub sends to the OpenAI-compatible embeddings endpoint.
|
||||
*/}}
|
||||
{{- define "formbricks.hubEmbeddingsServedModelName" -}}
|
||||
{{- default .Values.hub.embeddings.model .Values.hub.embeddings.servedModelName -}}
|
||||
{{- end }}
|
||||
|
||||
{{/*
|
||||
OpenAI-compatible embeddings base URL used by Hub.
|
||||
*/}}
|
||||
{{- define "formbricks.hubEmbeddingsBaseURL" -}}
|
||||
{{- if .Values.hub.embeddings.baseUrl -}}
|
||||
{{- .Values.hub.embeddings.baseUrl -}}
|
||||
{{- else -}}
|
||||
{{- printf "http://%s:%v/v1" (include "formbricks.hubEmbeddingsName" .) (.Values.hub.embeddings.service.port | default .Values.hub.embeddings.port) -}}
|
||||
{{- end -}}
|
||||
{{- end }}
|
||||
|
||||
{{/*
|
||||
Embedding API key value for the generated embeddings secret.
|
||||
*/}}
|
||||
{{- define "formbricks.hubEmbeddingsApiKey" -}}
|
||||
{{- $secretName := include "formbricks.hubEmbeddingsSecretName" . }}
|
||||
{{- $secretKey := .Values.hub.embeddings.auth.secretKey | default "EMBEDDING_PROVIDER_API_KEY" }}
|
||||
{{- $secret := (lookup "v1" "Secret" .Release.Namespace $secretName) }}
|
||||
{{- if and $secret (index $secret.data $secretKey) }}
|
||||
{{- index $secret.data $secretKey | b64dec -}}
|
||||
{{- else if .Values.hub.embeddings.auth.apiKey }}
|
||||
{{- .Values.hub.embeddings.auth.apiKey -}}
|
||||
{{- else }}
|
||||
{{- randAlphaNum 32 -}}
|
||||
{{- end -}}
|
||||
{{- end }}
|
||||
|
||||
{{/*
|
||||
Shared Hub embedding env. These values are managed from hub.embeddings when the
|
||||
self-hosted runtime is enabled so Hub API and Hub worker cannot drift.
|
||||
*/}}
|
||||
{{- define "formbricks.hubEmbeddingEnv" -}}
|
||||
{{- $root := .root -}}
|
||||
{{- if $root.Values.hub.embeddings.enabled }}
|
||||
- name: EMBEDDING_PROVIDER
|
||||
value: "openai"
|
||||
- name: EMBEDDING_MODEL
|
||||
value: {{ include "formbricks.hubEmbeddingsServedModelName" $root | quote }}
|
||||
- name: EMBEDDING_BASE_URL
|
||||
value: {{ include "formbricks.hubEmbeddingsBaseURL" $root | quote }}
|
||||
- name: EMBEDDING_PROVIDER_API_KEY
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: {{ include "formbricks.hubEmbeddingsSecretName" $root }}
|
||||
key: {{ $root.Values.hub.embeddings.auth.secretKey | default "EMBEDDING_PROVIDER_API_KEY" }}
|
||||
- name: EMBEDDING_MAX_CONCURRENT
|
||||
value: {{ $root.Values.hub.embeddings.maxConcurrent | quote }}
|
||||
- name: EMBEDDING_NORMALIZE
|
||||
value: {{ $root.Values.hub.embeddings.normalize | quote }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
|
||||
{{/*
|
||||
Returns true when an env var is managed by hub.embeddings and should not be rendered from hub.env/worker.env.
|
||||
*/}}
|
||||
{{- define "formbricks.hubEmbeddingEnvManaged" -}}
|
||||
{{- $key := .key -}}
|
||||
{{- if has $key (list "EMBEDDING_PROVIDER" "EMBEDDING_MODEL" "EMBEDDING_BASE_URL" "EMBEDDING_PROVIDER_API_KEY" "EMBEDDING_MAX_CONCURRENT" "EMBEDDING_NORMALIZE") -}}
|
||||
true
|
||||
{{- end -}}
|
||||
{{- end }}
|
||||
|
||||
|
||||
{{- define "formbricks.postgresAdminPassword" -}}
|
||||
{{- $secret := (lookup "v1" "Secret" .Release.Namespace (include "formbricks.appSecretName" .)) }}
|
||||
@@ -142,13 +241,13 @@ hub-worker) must use this helper so they cannot drift apart.
|
||||
{{- end -}}
|
||||
{{- end }}
|
||||
|
||||
{{- define "formbricks.cronSecret" -}}
|
||||
{{- $secret := (lookup "v1" "Secret" .Release.Namespace (include "formbricks.appSecretName" .)) }}
|
||||
{{- if $secret }}
|
||||
{{- index $secret.data "CRON_SECRET" | b64dec -}}
|
||||
{{- else }}
|
||||
{{- randAlphaNum 32 -}}
|
||||
{{- end -}}
|
||||
{{- define "formbricks.cronSecret" -}}
|
||||
{{- $secret := (lookup "v1" "Secret" .Release.Namespace (include "formbricks.appSecretName" .)) }}
|
||||
{{- if $secret }}
|
||||
{{- index $secret.data "CRON_SECRET" | b64dec -}}
|
||||
{{- else }}
|
||||
{{- randAlphaNum 32 -}}
|
||||
{{- end -}}
|
||||
{{- end }}
|
||||
|
||||
{{- define "formbricks.encryptionKey" -}}
|
||||
|
||||
@@ -14,7 +14,9 @@ metadata:
|
||||
app.kubernetes.io/managed-by: {{ .Release.Service }}
|
||||
app.kubernetes.io/part-of: {{ .Values.partOfOverride | default (include "formbricks.name" .) }}
|
||||
spec:
|
||||
{{- if not .Values.hub.autoscaling.enabled }}
|
||||
replicas: {{ .Values.hub.replicas | default 1 }}
|
||||
{{- end }}
|
||||
selector:
|
||||
matchLabels:
|
||||
app.kubernetes.io/name: {{ include "formbricks.hubname" . }}
|
||||
@@ -66,10 +68,13 @@ spec:
|
||||
secretKeyRef:
|
||||
name: {{ include "formbricks.hubSecretName" . }}
|
||||
key: HUB_API_KEY
|
||||
{{- include "formbricks.hubEmbeddingEnv" (dict "root" $ "env" .Values.hub.env) | nindent 12 }}
|
||||
{{- range $key, $value := .Values.hub.env }}
|
||||
{{- if not (and $.Values.hub.embeddings.enabled (include "formbricks.hubEmbeddingEnvManaged" (dict "key" $key))) }}
|
||||
- name: {{ $key }}
|
||||
value: {{ $value | quote }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{- if .Values.hub.resources }}
|
||||
resources:
|
||||
{{- toYaml .Values.hub.resources | nindent 12 }}
|
||||
|
||||
@@ -0,0 +1,158 @@
|
||||
{{- if and .Values.hub.enabled .Values.hub.embeddings.enabled }}
|
||||
{{- $embeddingsReplicas := int (.Values.hub.embeddings.replicas | default 1) -}}
|
||||
{{- $embeddingsMaxReplicas := int (.Values.hub.embeddings.autoscaling.maxReplicas | default 1) -}}
|
||||
{{- if and .Values.hub.embeddings.persistence.enabled (not (has "ReadWriteMany" .Values.hub.embeddings.persistence.accessModes)) (or (gt $embeddingsReplicas 1) (and .Values.hub.embeddings.autoscaling.enabled (gt $embeddingsMaxReplicas 1))) }}
|
||||
{{- fail "hub.embeddings persistence with multiple replicas requires persistence.accessModes to include ReadWriteMany, or set hub.embeddings.persistence.enabled=false/use a ReadWriteMany existingClaim" }}
|
||||
{{- end }}
|
||||
{{- if and .Values.hub.embeddings.auth.existingSecret .Values.hub.embeddings.huggingFace.token (not .Values.hub.embeddings.huggingFace.existingSecret) }}
|
||||
{{- fail "hub.embeddings.huggingFace.token cannot be stored when hub.embeddings.auth.existingSecret is set; put HF_TOKEN in the existing auth secret or set hub.embeddings.huggingFace.existingSecret" }}
|
||||
{{- end }}
|
||||
---
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: {{ include "formbricks.hubEmbeddingsName" . }}
|
||||
labels:
|
||||
helm.sh/chart: {{ include "formbricks.chart" . }}
|
||||
app.kubernetes.io/name: {{ include "formbricks.hubEmbeddingsName" . }}
|
||||
app.kubernetes.io/instance: {{ .Release.Name }}
|
||||
app.kubernetes.io/component: hub-embeddings
|
||||
app.kubernetes.io/managed-by: {{ .Release.Service }}
|
||||
app.kubernetes.io/part-of: {{ .Values.partOfOverride | default (include "formbricks.name" .) }}
|
||||
spec:
|
||||
{{- if not .Values.hub.embeddings.autoscaling.enabled }}
|
||||
replicas: {{ .Values.hub.embeddings.replicas | default 1 }}
|
||||
{{- end }}
|
||||
selector:
|
||||
matchLabels:
|
||||
app.kubernetes.io/name: {{ include "formbricks.hubEmbeddingsName" . }}
|
||||
app.kubernetes.io/instance: {{ .Release.Name }}
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app.kubernetes.io/name: {{ include "formbricks.hubEmbeddingsName" . }}
|
||||
app.kubernetes.io/instance: {{ .Release.Name }}
|
||||
app.kubernetes.io/component: hub-embeddings
|
||||
{{- with .Values.hub.embeddings.podLabels }}
|
||||
{{- toYaml . | nindent 8 }}
|
||||
{{- end }}
|
||||
{{- with .Values.hub.embeddings.podAnnotations }}
|
||||
annotations:
|
||||
{{- toYaml . | nindent 8 }}
|
||||
{{- end }}
|
||||
spec:
|
||||
{{- with .Values.hub.embeddings.podSecurityContext }}
|
||||
securityContext:
|
||||
{{- toYaml . | nindent 8 }}
|
||||
{{- end }}
|
||||
{{- with .Values.hub.embeddings.nodeSelector }}
|
||||
nodeSelector:
|
||||
{{- toYaml . | nindent 8 }}
|
||||
{{- end }}
|
||||
{{- with .Values.hub.embeddings.tolerations }}
|
||||
tolerations:
|
||||
{{- toYaml . | nindent 8 }}
|
||||
{{- end }}
|
||||
{{- with .Values.hub.embeddings.affinity }}
|
||||
affinity:
|
||||
{{- toYaml . | nindent 8 }}
|
||||
{{- end }}
|
||||
{{- with .Values.hub.embeddings.topologySpreadConstraints }}
|
||||
topologySpreadConstraints:
|
||||
{{- toYaml . | nindent 8 }}
|
||||
{{- end }}
|
||||
{{- if .Values.deployment.imagePullSecrets }}
|
||||
imagePullSecrets:
|
||||
{{- toYaml .Values.deployment.imagePullSecrets | nindent 8 }}
|
||||
{{- end }}
|
||||
containers:
|
||||
- name: hub-embeddings
|
||||
image: "{{ .Values.hub.embeddings.image.repository }}:{{ .Values.hub.embeddings.image.tag }}"
|
||||
imagePullPolicy: {{ .Values.hub.embeddings.image.pullPolicy }}
|
||||
{{- with .Values.hub.embeddings.command }}
|
||||
command:
|
||||
{{- toYaml . | nindent 12 }}
|
||||
{{- end }}
|
||||
{{- if .Values.hub.embeddings.args }}
|
||||
args:
|
||||
{{- toYaml .Values.hub.embeddings.args | nindent 12 }}
|
||||
{{- else }}
|
||||
args:
|
||||
- --model-id
|
||||
- {{ .Values.hub.embeddings.model | quote }}
|
||||
- --port
|
||||
- {{ .Values.hub.embeddings.port | quote }}
|
||||
- --huggingface-hub-cache
|
||||
- {{ .Values.hub.embeddings.persistence.mountPath | quote }}
|
||||
- --served-model-name
|
||||
- {{ include "formbricks.hubEmbeddingsServedModelName" . | quote }}
|
||||
{{- with .Values.hub.embeddings.revision }}
|
||||
- --revision
|
||||
- {{ . | quote }}
|
||||
{{- end }}
|
||||
{{- with .Values.hub.embeddings.extraArgs }}
|
||||
{{- toYaml . | nindent 12 }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
ports:
|
||||
- name: http
|
||||
containerPort: {{ .Values.hub.embeddings.port }}
|
||||
protocol: TCP
|
||||
- name: metrics
|
||||
containerPort: {{ .Values.hub.embeddings.prometheusPort }}
|
||||
protocol: TCP
|
||||
{{- if or .Values.hub.embeddings.auth.enabled .Values.hub.embeddings.huggingFace.existingSecret .Values.hub.embeddings.huggingFace.token (gt (len .Values.hub.embeddings.env) 0) }}
|
||||
env:
|
||||
{{- if .Values.hub.embeddings.auth.enabled }}
|
||||
- name: API_KEY
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: {{ include "formbricks.hubEmbeddingsSecretName" . }}
|
||||
key: {{ .Values.hub.embeddings.auth.secretKey | default "EMBEDDING_PROVIDER_API_KEY" }}
|
||||
{{- end }}
|
||||
{{- if or .Values.hub.embeddings.huggingFace.existingSecret .Values.hub.embeddings.huggingFace.token }}
|
||||
- name: HF_TOKEN
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: {{ include "formbricks.hubEmbeddingsHuggingFaceSecretName" . }}
|
||||
key: {{ .Values.hub.embeddings.huggingFace.tokenKey | default "HF_TOKEN" }}
|
||||
{{- end }}
|
||||
{{- range $key, $value := .Values.hub.embeddings.env }}
|
||||
{{- if not (or (and $.Values.hub.embeddings.auth.enabled (eq $key "API_KEY")) (and (or $.Values.hub.embeddings.huggingFace.existingSecret $.Values.hub.embeddings.huggingFace.token) (eq $key "HF_TOKEN"))) }}
|
||||
- name: {{ $key }}
|
||||
value: {{ $value | quote }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{- with .Values.hub.embeddings.probes.startupProbe }}
|
||||
startupProbe:
|
||||
{{- toYaml . | nindent 12 }}
|
||||
{{- end }}
|
||||
{{- with .Values.hub.embeddings.probes.readinessProbe }}
|
||||
readinessProbe:
|
||||
{{- toYaml . | nindent 12 }}
|
||||
{{- end }}
|
||||
{{- with .Values.hub.embeddings.probes.livenessProbe }}
|
||||
livenessProbe:
|
||||
{{- toYaml . | nindent 12 }}
|
||||
{{- end }}
|
||||
{{- with .Values.hub.embeddings.resources }}
|
||||
resources:
|
||||
{{- toYaml . | nindent 12 }}
|
||||
{{- end }}
|
||||
{{- with .Values.hub.embeddings.securityContext }}
|
||||
securityContext:
|
||||
{{- toYaml . | nindent 12 }}
|
||||
{{- end }}
|
||||
{{- if .Values.hub.embeddings.persistence.enabled }}
|
||||
volumeMounts:
|
||||
- name: model-cache
|
||||
mountPath: {{ .Values.hub.embeddings.persistence.mountPath }}
|
||||
{{- end }}
|
||||
{{- if .Values.hub.embeddings.persistence.enabled }}
|
||||
volumes:
|
||||
- name: model-cache
|
||||
persistentVolumeClaim:
|
||||
claimName: {{ default (include "formbricks.hubEmbeddingsName" .) .Values.hub.embeddings.persistence.existingClaim }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
@@ -0,0 +1,23 @@
|
||||
{{- if and .Values.hub.enabled .Values.hub.embeddings.enabled .Values.hub.embeddings.persistence.enabled (not .Values.hub.embeddings.persistence.existingClaim) }}
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: PersistentVolumeClaim
|
||||
metadata:
|
||||
name: {{ include "formbricks.hubEmbeddingsName" . }}
|
||||
labels:
|
||||
helm.sh/chart: {{ include "formbricks.chart" . }}
|
||||
app.kubernetes.io/name: {{ include "formbricks.hubEmbeddingsName" . }}
|
||||
app.kubernetes.io/instance: {{ .Release.Name }}
|
||||
app.kubernetes.io/component: hub-embeddings
|
||||
app.kubernetes.io/managed-by: {{ .Release.Service }}
|
||||
app.kubernetes.io/part-of: {{ .Values.partOfOverride | default (include "formbricks.name" .) }}
|
||||
spec:
|
||||
accessModes:
|
||||
{{- toYaml .Values.hub.embeddings.persistence.accessModes | nindent 4 }}
|
||||
resources:
|
||||
requests:
|
||||
storage: {{ .Values.hub.embeddings.persistence.size }}
|
||||
{{- with .Values.hub.embeddings.persistence.storageClass }}
|
||||
storageClassName: {{ . | quote }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
@@ -0,0 +1,20 @@
|
||||
{{- if and .Values.hub.enabled .Values.hub.embeddings.enabled (not .Values.hub.embeddings.auth.existingSecret) }}
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Secret
|
||||
metadata:
|
||||
name: {{ include "formbricks.hubEmbeddingsSecretName" . }}
|
||||
labels:
|
||||
helm.sh/chart: {{ include "formbricks.chart" . }}
|
||||
app.kubernetes.io/name: {{ include "formbricks.hubEmbeddingsName" . }}
|
||||
app.kubernetes.io/instance: {{ .Release.Name }}
|
||||
app.kubernetes.io/component: hub-embeddings
|
||||
app.kubernetes.io/managed-by: {{ .Release.Service }}
|
||||
app.kubernetes.io/part-of: {{ .Values.partOfOverride | default (include "formbricks.name" .) }}
|
||||
type: Opaque
|
||||
data:
|
||||
{{ .Values.hub.embeddings.auth.secretKey | default "EMBEDDING_PROVIDER_API_KEY" }}: {{ include "formbricks.hubEmbeddingsApiKey" . | b64enc }}
|
||||
{{- if and (not .Values.hub.embeddings.huggingFace.existingSecret) .Values.hub.embeddings.huggingFace.token }}
|
||||
{{ .Values.hub.embeddings.huggingFace.tokenKey | default "HF_TOKEN" }}: {{ .Values.hub.embeddings.huggingFace.token | b64enc }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
@@ -0,0 +1,35 @@
|
||||
{{- if and .Values.hub.enabled .Values.hub.embeddings.enabled }}
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: {{ include "formbricks.hubEmbeddingsName" . }}
|
||||
labels:
|
||||
helm.sh/chart: {{ include "formbricks.chart" . }}
|
||||
app.kubernetes.io/name: {{ include "formbricks.hubEmbeddingsName" . }}
|
||||
app.kubernetes.io/instance: {{ .Release.Name }}
|
||||
app.kubernetes.io/component: hub-embeddings
|
||||
app.kubernetes.io/managed-by: {{ .Release.Service }}
|
||||
app.kubernetes.io/part-of: {{ .Values.partOfOverride | default (include "formbricks.name" .) }}
|
||||
{{- with .Values.hub.embeddings.service.additionalLabels }}
|
||||
{{- toYaml . | nindent 4 }}
|
||||
{{- end }}
|
||||
{{- with .Values.hub.embeddings.service.annotations }}
|
||||
annotations:
|
||||
{{- toYaml . | nindent 4 }}
|
||||
{{- end }}
|
||||
spec:
|
||||
type: {{ .Values.hub.embeddings.service.type }}
|
||||
selector:
|
||||
app.kubernetes.io/name: {{ include "formbricks.hubEmbeddingsName" . }}
|
||||
app.kubernetes.io/instance: {{ .Release.Name }}
|
||||
ports:
|
||||
- name: http
|
||||
port: {{ .Values.hub.embeddings.service.port }}
|
||||
targetPort: http
|
||||
protocol: TCP
|
||||
- name: metrics
|
||||
port: {{ .Values.hub.embeddings.prometheusPort }}
|
||||
targetPort: metrics
|
||||
protocol: TCP
|
||||
{{- end }}
|
||||
@@ -0,0 +1,102 @@
|
||||
{{- if and .Values.hub.enabled .Values.hub.autoscaling.enabled }}
|
||||
---
|
||||
apiVersion: autoscaling/v2
|
||||
kind: HorizontalPodAutoscaler
|
||||
metadata:
|
||||
name: {{ include "formbricks.hubname" . }}
|
||||
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
|
||||
app.kubernetes.io/managed-by: {{ .Release.Service }}
|
||||
app.kubernetes.io/part-of: {{ .Values.partOfOverride | default (include "formbricks.name" .) }}
|
||||
{{- with .Values.hub.autoscaling.additionalLabels }}
|
||||
{{- toYaml . | nindent 4 }}
|
||||
{{- end }}
|
||||
{{- with .Values.hub.autoscaling.annotations }}
|
||||
annotations:
|
||||
{{- toYaml . | nindent 4 }}
|
||||
{{- end }}
|
||||
spec:
|
||||
scaleTargetRef:
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
name: {{ include "formbricks.hubname" . }}
|
||||
minReplicas: {{ .Values.hub.autoscaling.minReplicas }}
|
||||
maxReplicas: {{ .Values.hub.autoscaling.maxReplicas }}
|
||||
metrics:
|
||||
{{- toYaml .Values.hub.autoscaling.metrics | nindent 4 }}
|
||||
{{- with .Values.hub.autoscaling.behavior }}
|
||||
behavior:
|
||||
{{- toYaml . | nindent 4 }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{- if and .Values.hub.enabled .Values.hub.worker.enabled .Values.hub.worker.autoscaling.enabled }}
|
||||
---
|
||||
apiVersion: autoscaling/v2
|
||||
kind: HorizontalPodAutoscaler
|
||||
metadata:
|
||||
name: {{ include "formbricks.hubWorkerName" . }}
|
||||
labels:
|
||||
helm.sh/chart: {{ include "formbricks.chart" . }}
|
||||
app.kubernetes.io/name: {{ include "formbricks.hubWorkerName" . }}
|
||||
app.kubernetes.io/instance: {{ .Release.Name }}
|
||||
app.kubernetes.io/component: hub-worker
|
||||
app.kubernetes.io/managed-by: {{ .Release.Service }}
|
||||
app.kubernetes.io/part-of: {{ .Values.partOfOverride | default (include "formbricks.name" .) }}
|
||||
{{- with .Values.hub.worker.autoscaling.additionalLabels }}
|
||||
{{- toYaml . | nindent 4 }}
|
||||
{{- end }}
|
||||
{{- with .Values.hub.worker.autoscaling.annotations }}
|
||||
annotations:
|
||||
{{- toYaml . | nindent 4 }}
|
||||
{{- end }}
|
||||
spec:
|
||||
scaleTargetRef:
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
name: {{ include "formbricks.hubWorkerName" . }}
|
||||
minReplicas: {{ .Values.hub.worker.autoscaling.minReplicas }}
|
||||
maxReplicas: {{ .Values.hub.worker.autoscaling.maxReplicas }}
|
||||
metrics:
|
||||
{{- toYaml .Values.hub.worker.autoscaling.metrics | nindent 4 }}
|
||||
{{- with .Values.hub.worker.autoscaling.behavior }}
|
||||
behavior:
|
||||
{{- toYaml . | nindent 4 }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{- if and .Values.hub.enabled .Values.hub.embeddings.enabled .Values.hub.embeddings.autoscaling.enabled }}
|
||||
---
|
||||
apiVersion: autoscaling/v2
|
||||
kind: HorizontalPodAutoscaler
|
||||
metadata:
|
||||
name: {{ include "formbricks.hubEmbeddingsName" . }}
|
||||
labels:
|
||||
helm.sh/chart: {{ include "formbricks.chart" . }}
|
||||
app.kubernetes.io/name: {{ include "formbricks.hubEmbeddingsName" . }}
|
||||
app.kubernetes.io/instance: {{ .Release.Name }}
|
||||
app.kubernetes.io/component: hub-embeddings
|
||||
app.kubernetes.io/managed-by: {{ .Release.Service }}
|
||||
app.kubernetes.io/part-of: {{ .Values.partOfOverride | default (include "formbricks.name" .) }}
|
||||
{{- with .Values.hub.embeddings.autoscaling.additionalLabels }}
|
||||
{{- toYaml . | nindent 4 }}
|
||||
{{- end }}
|
||||
{{- with .Values.hub.embeddings.autoscaling.annotations }}
|
||||
annotations:
|
||||
{{- toYaml . | nindent 4 }}
|
||||
{{- end }}
|
||||
spec:
|
||||
scaleTargetRef:
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
name: {{ include "formbricks.hubEmbeddingsName" . }}
|
||||
minReplicas: {{ .Values.hub.embeddings.autoscaling.minReplicas }}
|
||||
maxReplicas: {{ .Values.hub.embeddings.autoscaling.maxReplicas }}
|
||||
metrics:
|
||||
{{- toYaml .Values.hub.embeddings.autoscaling.metrics | nindent 4 }}
|
||||
{{- with .Values.hub.embeddings.autoscaling.behavior }}
|
||||
behavior:
|
||||
{{- toYaml . | nindent 4 }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
@@ -0,0 +1,129 @@
|
||||
{{- if and .Values.hub.enabled .Values.hub.pdb.enabled }}
|
||||
{{- $hasMinAvailable := not (kindIs "invalid" .Values.hub.pdb.minAvailable) -}}
|
||||
{{- $hasMaxUnavailable := not (kindIs "invalid" .Values.hub.pdb.maxUnavailable) -}}
|
||||
{{- if and $hasMinAvailable $hasMaxUnavailable }}
|
||||
{{- fail "hub.pdb.minAvailable and hub.pdb.maxUnavailable are mutually exclusive; set only one" }}
|
||||
{{- end }}
|
||||
{{- if not (or $hasMinAvailable $hasMaxUnavailable) }}
|
||||
{{- fail "hub.pdb.enabled is true but neither hub.pdb.minAvailable nor hub.pdb.maxUnavailable is set; set exactly one" }}
|
||||
{{- end }}
|
||||
---
|
||||
apiVersion: policy/v1
|
||||
kind: PodDisruptionBudget
|
||||
metadata:
|
||||
name: {{ include "formbricks.hubname" . }}
|
||||
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
|
||||
app.kubernetes.io/managed-by: {{ .Release.Service }}
|
||||
app.kubernetes.io/part-of: {{ .Values.partOfOverride | default (include "formbricks.name" .) }}
|
||||
{{- with .Values.hub.pdb.additionalLabels }}
|
||||
{{- toYaml . | nindent 4 }}
|
||||
{{- end }}
|
||||
{{- with .Values.hub.pdb.annotations }}
|
||||
annotations:
|
||||
{{- toYaml . | nindent 4 }}
|
||||
{{- end }}
|
||||
spec:
|
||||
{{- if $hasMinAvailable }}
|
||||
minAvailable: {{ .Values.hub.pdb.minAvailable }}
|
||||
{{- end }}
|
||||
{{- if $hasMaxUnavailable }}
|
||||
maxUnavailable: {{ .Values.hub.pdb.maxUnavailable }}
|
||||
{{- end }}
|
||||
{{- with .Values.hub.pdb.unhealthyPodEvictionPolicy }}
|
||||
unhealthyPodEvictionPolicy: {{ . }}
|
||||
{{- end }}
|
||||
selector:
|
||||
matchLabels:
|
||||
app.kubernetes.io/name: {{ include "formbricks.hubname" . }}
|
||||
app.kubernetes.io/instance: {{ .Release.Name }}
|
||||
{{- end }}
|
||||
{{- if and .Values.hub.enabled .Values.hub.worker.enabled .Values.hub.worker.pdb.enabled }}
|
||||
{{- $hasMinAvailable := not (kindIs "invalid" .Values.hub.worker.pdb.minAvailable) -}}
|
||||
{{- $hasMaxUnavailable := not (kindIs "invalid" .Values.hub.worker.pdb.maxUnavailable) -}}
|
||||
{{- if and $hasMinAvailable $hasMaxUnavailable }}
|
||||
{{- fail "hub.worker.pdb.minAvailable and hub.worker.pdb.maxUnavailable are mutually exclusive; set only one" }}
|
||||
{{- end }}
|
||||
{{- if not (or $hasMinAvailable $hasMaxUnavailable) }}
|
||||
{{- fail "hub.worker.pdb.enabled is true but neither hub.worker.pdb.minAvailable nor hub.worker.pdb.maxUnavailable is set; set exactly one" }}
|
||||
{{- end }}
|
||||
---
|
||||
apiVersion: policy/v1
|
||||
kind: PodDisruptionBudget
|
||||
metadata:
|
||||
name: {{ include "formbricks.hubWorkerName" . }}
|
||||
labels:
|
||||
helm.sh/chart: {{ include "formbricks.chart" . }}
|
||||
app.kubernetes.io/name: {{ include "formbricks.hubWorkerName" . }}
|
||||
app.kubernetes.io/instance: {{ .Release.Name }}
|
||||
app.kubernetes.io/component: hub-worker
|
||||
app.kubernetes.io/managed-by: {{ .Release.Service }}
|
||||
app.kubernetes.io/part-of: {{ .Values.partOfOverride | default (include "formbricks.name" .) }}
|
||||
{{- with .Values.hub.worker.pdb.additionalLabels }}
|
||||
{{- toYaml . | nindent 4 }}
|
||||
{{- end }}
|
||||
{{- with .Values.hub.worker.pdb.annotations }}
|
||||
annotations:
|
||||
{{- toYaml . | nindent 4 }}
|
||||
{{- end }}
|
||||
spec:
|
||||
{{- if $hasMinAvailable }}
|
||||
minAvailable: {{ .Values.hub.worker.pdb.minAvailable }}
|
||||
{{- end }}
|
||||
{{- if $hasMaxUnavailable }}
|
||||
maxUnavailable: {{ .Values.hub.worker.pdb.maxUnavailable }}
|
||||
{{- end }}
|
||||
{{- with .Values.hub.worker.pdb.unhealthyPodEvictionPolicy }}
|
||||
unhealthyPodEvictionPolicy: {{ . }}
|
||||
{{- end }}
|
||||
selector:
|
||||
matchLabels:
|
||||
app.kubernetes.io/name: {{ include "formbricks.hubWorkerName" . }}
|
||||
app.kubernetes.io/instance: {{ .Release.Name }}
|
||||
{{- end }}
|
||||
{{- if and .Values.hub.enabled .Values.hub.embeddings.enabled .Values.hub.embeddings.pdb.enabled }}
|
||||
{{- $hasMinAvailable := not (kindIs "invalid" .Values.hub.embeddings.pdb.minAvailable) -}}
|
||||
{{- $hasMaxUnavailable := not (kindIs "invalid" .Values.hub.embeddings.pdb.maxUnavailable) -}}
|
||||
{{- if and $hasMinAvailable $hasMaxUnavailable }}
|
||||
{{- fail "hub.embeddings.pdb.minAvailable and hub.embeddings.pdb.maxUnavailable are mutually exclusive; set only one" }}
|
||||
{{- end }}
|
||||
{{- if not (or $hasMinAvailable $hasMaxUnavailable) }}
|
||||
{{- fail "hub.embeddings.pdb.enabled is true but neither hub.embeddings.pdb.minAvailable nor hub.embeddings.pdb.maxUnavailable is set; set exactly one" }}
|
||||
{{- end }}
|
||||
---
|
||||
apiVersion: policy/v1
|
||||
kind: PodDisruptionBudget
|
||||
metadata:
|
||||
name: {{ include "formbricks.hubEmbeddingsName" . }}
|
||||
labels:
|
||||
helm.sh/chart: {{ include "formbricks.chart" . }}
|
||||
app.kubernetes.io/name: {{ include "formbricks.hubEmbeddingsName" . }}
|
||||
app.kubernetes.io/instance: {{ .Release.Name }}
|
||||
app.kubernetes.io/component: hub-embeddings
|
||||
app.kubernetes.io/managed-by: {{ .Release.Service }}
|
||||
app.kubernetes.io/part-of: {{ .Values.partOfOverride | default (include "formbricks.name" .) }}
|
||||
{{- with .Values.hub.embeddings.pdb.additionalLabels }}
|
||||
{{- toYaml . | nindent 4 }}
|
||||
{{- end }}
|
||||
{{- with .Values.hub.embeddings.pdb.annotations }}
|
||||
annotations:
|
||||
{{- toYaml . | nindent 4 }}
|
||||
{{- end }}
|
||||
spec:
|
||||
{{- if $hasMinAvailable }}
|
||||
minAvailable: {{ .Values.hub.embeddings.pdb.minAvailable }}
|
||||
{{- end }}
|
||||
{{- if $hasMaxUnavailable }}
|
||||
maxUnavailable: {{ .Values.hub.embeddings.pdb.maxUnavailable }}
|
||||
{{- end }}
|
||||
{{- with .Values.hub.embeddings.pdb.unhealthyPodEvictionPolicy }}
|
||||
unhealthyPodEvictionPolicy: {{ . }}
|
||||
{{- end }}
|
||||
selector:
|
||||
matchLabels:
|
||||
app.kubernetes.io/name: {{ include "formbricks.hubEmbeddingsName" . }}
|
||||
app.kubernetes.io/instance: {{ .Release.Name }}
|
||||
{{- end }}
|
||||
@@ -0,0 +1,97 @@
|
||||
{{- if and .Values.hub.enabled .Values.hub.worker.enabled }}
|
||||
---
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: {{ include "formbricks.hubWorkerName" . }}
|
||||
labels:
|
||||
helm.sh/chart: {{ include "formbricks.chart" . }}
|
||||
app.kubernetes.io/name: {{ include "formbricks.hubWorkerName" . }}
|
||||
app.kubernetes.io/instance: {{ .Release.Name }}
|
||||
app.kubernetes.io/component: hub-worker
|
||||
app.kubernetes.io/managed-by: {{ .Release.Service }}
|
||||
app.kubernetes.io/part-of: {{ .Values.partOfOverride | default (include "formbricks.name" .) }}
|
||||
spec:
|
||||
{{- if not .Values.hub.worker.autoscaling.enabled }}
|
||||
replicas: {{ .Values.hub.worker.replicas | default 1 }}
|
||||
{{- end }}
|
||||
selector:
|
||||
matchLabels:
|
||||
app.kubernetes.io/name: {{ include "formbricks.hubWorkerName" . }}
|
||||
app.kubernetes.io/instance: {{ .Release.Name }}
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app.kubernetes.io/name: {{ include "formbricks.hubWorkerName" . }}
|
||||
app.kubernetes.io/instance: {{ .Release.Name }}
|
||||
app.kubernetes.io/component: hub-worker
|
||||
spec:
|
||||
{{- with .Values.hub.worker.nodeSelector }}
|
||||
nodeSelector:
|
||||
{{- toYaml . | nindent 8 }}
|
||||
{{- end }}
|
||||
{{- with .Values.hub.worker.tolerations }}
|
||||
tolerations:
|
||||
{{- toYaml . | nindent 8 }}
|
||||
{{- end }}
|
||||
{{- with .Values.hub.worker.affinity }}
|
||||
affinity:
|
||||
{{- toYaml . | nindent 8 }}
|
||||
{{- end }}
|
||||
{{- with .Values.hub.worker.topologySpreadConstraints }}
|
||||
topologySpreadConstraints:
|
||||
{{- toYaml . | nindent 8 }}
|
||||
{{- end }}
|
||||
{{- if .Values.deployment.imagePullSecrets }}
|
||||
imagePullSecrets:
|
||||
{{- toYaml .Values.deployment.imagePullSecrets | nindent 8 }}
|
||||
{{- end }}
|
||||
initContainers:
|
||||
- name: wait-for-hub-api
|
||||
image: {{ include "formbricks.hubImage" . }}
|
||||
imagePullPolicy: {{ .Values.hub.image.pullPolicy }}
|
||||
securityContext:
|
||||
readOnlyRootFilesystem: true
|
||||
runAsNonRoot: true
|
||||
command:
|
||||
- sh
|
||||
- -c
|
||||
- |
|
||||
until wget --no-verbose --tries=1 --spider http://{{ include "formbricks.hubname" . }}:8080/health; do
|
||||
echo "Waiting for Hub API migrations and health check..."
|
||||
sleep 5
|
||||
done
|
||||
containers:
|
||||
- name: hub-worker
|
||||
image: {{ include "formbricks.hubImage" . }}
|
||||
imagePullPolicy: {{ .Values.hub.image.pullPolicy }}
|
||||
command:
|
||||
- /app/hub-worker
|
||||
securityContext:
|
||||
readOnlyRootFilesystem: true
|
||||
runAsNonRoot: true
|
||||
envFrom:
|
||||
- secretRef:
|
||||
name: {{ include "formbricks.hubSecretName" . }}
|
||||
{{- if or .Values.hub.embeddings.enabled (gt (len .Values.hub.env) 0) (gt (len .Values.hub.worker.env) 0) }}
|
||||
env:
|
||||
{{- $workerEnv := merge (dict) .Values.hub.env .Values.hub.worker.env }}
|
||||
{{- include "formbricks.hubEmbeddingEnv" (dict "root" $ "env" $workerEnv) | nindent 12 }}
|
||||
{{- range $key, $value := .Values.hub.env }}
|
||||
{{- if and (not (hasKey $.Values.hub.worker.env $key)) (not (and $.Values.hub.embeddings.enabled (include "formbricks.hubEmbeddingEnvManaged" (dict "key" $key)))) }}
|
||||
- name: {{ $key }}
|
||||
value: {{ $value | quote }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{- range $key, $value := .Values.hub.worker.env }}
|
||||
{{- if not (and $.Values.hub.embeddings.enabled (include "formbricks.hubEmbeddingEnvManaged" (dict "key" $key))) }}
|
||||
- name: {{ $key }}
|
||||
value: {{ $value | quote }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{- if .Values.hub.worker.resources }}
|
||||
resources:
|
||||
{{- toYaml .Values.hub.worker.resources | nindent 12 }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
@@ -588,6 +588,241 @@ hub:
|
||||
# Optional env vars (non-secret). Use existingSecret for secret values such as DATABASE_URL and HUB_API_KEY.
|
||||
env: {}
|
||||
|
||||
# Optional autoscaling for the Hub API deployment.
|
||||
autoscaling:
|
||||
enabled: false
|
||||
additionalLabels: {}
|
||||
annotations: {}
|
||||
minReplicas: 1
|
||||
maxReplicas: 3
|
||||
metrics:
|
||||
- type: Resource
|
||||
resource:
|
||||
name: cpu
|
||||
target:
|
||||
type: Utilization
|
||||
averageUtilization: 70
|
||||
- type: Resource
|
||||
resource:
|
||||
name: memory
|
||||
target:
|
||||
type: Utilization
|
||||
averageUtilization: 80
|
||||
behavior: {}
|
||||
|
||||
# Optional PDB for the Hub API deployment. Disabled by default because a single
|
||||
# Hub replica with minAvailable: 1 blocks voluntary node drains.
|
||||
pdb:
|
||||
enabled: false
|
||||
additionalLabels: {}
|
||||
annotations: {}
|
||||
minAvailable: 1
|
||||
# maxUnavailable: 1
|
||||
# unhealthyPodEvictionPolicy: AlwaysAllow
|
||||
|
||||
worker:
|
||||
# Hub async jobs (webhook dispatch, embeddings) run in hub-worker. Keep this
|
||||
# enabled unless another worker deployment processes the same River queues.
|
||||
enabled: true
|
||||
replicas: 1
|
||||
|
||||
# Optional env vars (non-secret) added only to hub-worker.
|
||||
env: {}
|
||||
|
||||
resources:
|
||||
limits:
|
||||
memory: 512Mi
|
||||
requests:
|
||||
memory: 256Mi
|
||||
cpu: "100m"
|
||||
|
||||
autoscaling:
|
||||
enabled: false
|
||||
additionalLabels: {}
|
||||
annotations: {}
|
||||
minReplicas: 1
|
||||
maxReplicas: 5
|
||||
metrics:
|
||||
- type: Resource
|
||||
resource:
|
||||
name: cpu
|
||||
target:
|
||||
type: Utilization
|
||||
averageUtilization: 70
|
||||
- type: Resource
|
||||
resource:
|
||||
name: memory
|
||||
target:
|
||||
type: Utilization
|
||||
averageUtilization: 80
|
||||
behavior:
|
||||
scaleDown:
|
||||
stabilizationWindowSeconds: 300
|
||||
policies:
|
||||
- type: Pods
|
||||
value: 1
|
||||
periodSeconds: 120
|
||||
scaleUp:
|
||||
stabilizationWindowSeconds: 60
|
||||
policies:
|
||||
- type: Pods
|
||||
value: 2
|
||||
periodSeconds: 60
|
||||
|
||||
# Disabled by default because the default worker replica count is 1.
|
||||
pdb:
|
||||
enabled: false
|
||||
additionalLabels: {}
|
||||
annotations: {}
|
||||
minAvailable: 1
|
||||
# maxUnavailable: 1
|
||||
# unhealthyPodEvictionPolicy: AlwaysAllow
|
||||
|
||||
nodeSelector: {}
|
||||
tolerations: []
|
||||
affinity: {}
|
||||
topologySpreadConstraints: []
|
||||
|
||||
embeddings:
|
||||
# Optional self-hosted OpenAI-compatible embeddings runtime for Hub.
|
||||
enabled: false
|
||||
runtime: tei
|
||||
model: google/embeddinggemma-300m
|
||||
revision: ""
|
||||
# Defaults to `model` when empty. Used by TEI OpenAI-compatible responses
|
||||
# and as Hub's EMBEDDING_MODEL.
|
||||
servedModelName: ""
|
||||
# Defaults to http://<release>-hub-embeddings:<service.port>/v1 when empty.
|
||||
baseUrl: ""
|
||||
maxConcurrent: "5"
|
||||
normalize: "false"
|
||||
replicas: 1
|
||||
|
||||
image:
|
||||
repository: "ghcr.io/huggingface/text-embeddings-inference"
|
||||
tag: "cpu-1.9"
|
||||
pullPolicy: IfNotPresent
|
||||
|
||||
command: []
|
||||
# When empty, the chart renders TEI args from model, servedModelName, port,
|
||||
# revision, and persistence.mountPath. Set this to fully override args.
|
||||
args: []
|
||||
extraArgs: []
|
||||
env: {}
|
||||
|
||||
port: 8080
|
||||
prometheusPort: 9000
|
||||
|
||||
service:
|
||||
type: ClusterIP
|
||||
port: 8080
|
||||
annotations: {}
|
||||
additionalLabels: {}
|
||||
|
||||
auth:
|
||||
# TEI can enforce bearer-token auth with API_KEY. Hub always receives the
|
||||
# same key as EMBEDDING_PROVIDER_API_KEY because the OpenAI-compatible Hub
|
||||
# provider requires an API key.
|
||||
enabled: true
|
||||
existingSecret: ""
|
||||
secretKey: EMBEDDING_PROVIDER_API_KEY
|
||||
apiKey: ""
|
||||
|
||||
huggingFace:
|
||||
# Required for gated models such as google/embeddinggemma-300m unless the
|
||||
# model is pre-cached or a different ungated model is configured.
|
||||
existingSecret: ""
|
||||
tokenKey: HF_TOKEN
|
||||
token: ""
|
||||
|
||||
persistence:
|
||||
enabled: true
|
||||
existingClaim: ""
|
||||
storageClass: ""
|
||||
accessModes:
|
||||
- ReadWriteOnce
|
||||
size: 10Gi
|
||||
mountPath: /data
|
||||
|
||||
resources:
|
||||
requests:
|
||||
cpu: "4"
|
||||
memory: 8Gi
|
||||
limits:
|
||||
memory: 8Gi
|
||||
|
||||
autoscaling:
|
||||
enabled: false
|
||||
additionalLabels: {}
|
||||
annotations: {}
|
||||
minReplicas: 1
|
||||
maxReplicas: 2
|
||||
metrics:
|
||||
- type: Resource
|
||||
resource:
|
||||
name: cpu
|
||||
target:
|
||||
type: Utilization
|
||||
averageUtilization: 70
|
||||
- type: Resource
|
||||
resource:
|
||||
name: memory
|
||||
target:
|
||||
type: Utilization
|
||||
averageUtilization: 80
|
||||
behavior:
|
||||
scaleDown:
|
||||
stabilizationWindowSeconds: 600
|
||||
policies:
|
||||
- type: Pods
|
||||
value: 1
|
||||
periodSeconds: 300
|
||||
scaleUp:
|
||||
stabilizationWindowSeconds: 120
|
||||
policies:
|
||||
- type: Pods
|
||||
value: 1
|
||||
periodSeconds: 120
|
||||
|
||||
# Disabled by default because the default embeddings replica count is 1.
|
||||
pdb:
|
||||
enabled: false
|
||||
additionalLabels: {}
|
||||
annotations: {}
|
||||
minAvailable: 1
|
||||
# maxUnavailable: 1
|
||||
# unhealthyPodEvictionPolicy: AlwaysAllow
|
||||
|
||||
probes:
|
||||
startupProbe:
|
||||
failureThreshold: 60
|
||||
periodSeconds: 10
|
||||
tcpSocket:
|
||||
port: http
|
||||
readinessProbe:
|
||||
failureThreshold: 6
|
||||
periodSeconds: 10
|
||||
timeoutSeconds: 5
|
||||
tcpSocket:
|
||||
port: http
|
||||
livenessProbe:
|
||||
failureThreshold: 6
|
||||
periodSeconds: 20
|
||||
timeoutSeconds: 5
|
||||
tcpSocket:
|
||||
port: http
|
||||
|
||||
podAnnotations: {}
|
||||
podLabels: {}
|
||||
podSecurityContext: {}
|
||||
# Keep empty by default because upstream model-serving images may define
|
||||
# their own user and need write access to the model cache path.
|
||||
securityContext: {}
|
||||
nodeSelector: {}
|
||||
tolerations: []
|
||||
affinity: {}
|
||||
topologySpreadConstraints: []
|
||||
|
||||
# Helm does not deploy Cube. XM Suite v5 analytics requires operators to provide an external Cube instance,
|
||||
# set deployment.env.CUBEJS_API_URL, and supply CUBEJS_API_SECRET via an existing secret.
|
||||
|
||||
|
||||
@@ -83,6 +83,30 @@ describe("packages/ai provider helpers", () => {
|
||||
});
|
||||
});
|
||||
|
||||
test("reports a fully configured Google Cloud instance when ADC provides credentials", () => {
|
||||
expect(
|
||||
getAiConfigurationStatus({
|
||||
AI_PROVIDER: "google",
|
||||
AI_MODEL: "gemini-2.5-flash",
|
||||
AI_GOOGLE_CLOUD_PROJECT: "test-project",
|
||||
AI_GOOGLE_CLOUD_LOCATION: "us-central1",
|
||||
})
|
||||
).toEqual({
|
||||
provider: "google",
|
||||
model: "gemini-2.5-flash",
|
||||
isConfigured: true,
|
||||
missingFields: [],
|
||||
invalidFields: [],
|
||||
providerStatus: {
|
||||
provider: "google",
|
||||
model: "gemini-2.5-flash",
|
||||
isConfigured: true,
|
||||
missingFields: [],
|
||||
invalidFields: [],
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
test("treats the instance as not configured when AI_PROVIDER is missing", () => {
|
||||
expect(
|
||||
isAiConfigured({
|
||||
@@ -207,6 +231,48 @@ describe("packages/ai provider helpers", () => {
|
||||
expect(mocks.createVertex).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
test("creates a Google Cloud model with application credentials file", () => {
|
||||
const vertexProvider = createMockProvider("google");
|
||||
mocks.createVertex.mockReturnValue(vertexProvider);
|
||||
|
||||
const model = getAiModel({
|
||||
AI_PROVIDER: "google",
|
||||
AI_MODEL: "gemini-2.5-flash",
|
||||
AI_GOOGLE_CLOUD_PROJECT: "test-project",
|
||||
AI_GOOGLE_CLOUD_LOCATION: "us-central1",
|
||||
AI_GOOGLE_CLOUD_APPLICATION_CREDENTIALS: "/tmp/google-cloud.json",
|
||||
});
|
||||
|
||||
expect(model).toEqual({ providerName: "google", modelName: "gemini-2.5-flash" });
|
||||
expect(mocks.createVertex).toHaveBeenCalledWith({
|
||||
project: "test-project",
|
||||
location: "us-central1",
|
||||
googleAuthOptions: {
|
||||
keyFilename: "/tmp/google-cloud.json",
|
||||
},
|
||||
});
|
||||
expect(vertexProvider).toHaveBeenCalledWith("gemini-2.5-flash");
|
||||
});
|
||||
|
||||
test("creates a Google Cloud model using ADC when no credential override is configured", () => {
|
||||
const vertexProvider = createMockProvider("google");
|
||||
mocks.createVertex.mockReturnValue(vertexProvider);
|
||||
|
||||
const model = getAiModel({
|
||||
AI_PROVIDER: "google",
|
||||
AI_MODEL: "gemini-2.5-flash",
|
||||
AI_GOOGLE_CLOUD_PROJECT: "test-project",
|
||||
AI_GOOGLE_CLOUD_LOCATION: "us-central1",
|
||||
});
|
||||
|
||||
expect(model).toEqual({ providerName: "google", modelName: "gemini-2.5-flash" });
|
||||
expect(mocks.createVertex).toHaveBeenCalledWith({
|
||||
project: "test-project",
|
||||
location: "us-central1",
|
||||
});
|
||||
expect(vertexProvider).toHaveBeenCalledWith("gemini-2.5-flash");
|
||||
});
|
||||
|
||||
test("creates an AWS model with explicit AWS credentials", () => {
|
||||
const bedrockProvider = createMockProvider("aws");
|
||||
mocks.createAmazonBedrock.mockReturnValue(bedrockProvider);
|
||||
|
||||
@@ -39,11 +39,6 @@ export const googleProviderAdapter: AIProviderAdapter = {
|
||||
}
|
||||
|
||||
const credentialsJson = normalizeValue(environment.AI_GOOGLE_CLOUD_CREDENTIALS_JSON);
|
||||
const applicationCredentials = normalizeValue(environment.AI_GOOGLE_CLOUD_APPLICATION_CREDENTIALS);
|
||||
|
||||
if (!credentialsJson && !applicationCredentials) {
|
||||
missingFields.push("AI_GOOGLE_CLOUD_CREDENTIALS_JSON or AI_GOOGLE_CLOUD_APPLICATION_CREDENTIALS");
|
||||
}
|
||||
|
||||
if (credentialsJson) {
|
||||
try {
|
||||
@@ -73,15 +68,12 @@ export const googleProviderAdapter: AIProviderAdapter = {
|
||||
const credentialsJson = normalizeValue(environment.AI_GOOGLE_CLOUD_CREDENTIALS_JSON);
|
||||
const applicationCredentials = normalizeValue(environment.AI_GOOGLE_CLOUD_APPLICATION_CREDENTIALS);
|
||||
|
||||
if (!project || !location || (!credentialsJson && !applicationCredentials)) {
|
||||
throw new AIConfigurationError("providerNotConfigured", "Google Cloud AI credentials are incomplete", {
|
||||
if (!project || !location) {
|
||||
throw new AIConfigurationError("providerNotConfigured", "Google Cloud AI configuration is incomplete", {
|
||||
provider: "google",
|
||||
missingFields: [
|
||||
...(!project ? ["AI_GOOGLE_CLOUD_PROJECT"] : []),
|
||||
...(!location ? ["AI_GOOGLE_CLOUD_LOCATION"] : []),
|
||||
...(!credentialsJson && !applicationCredentials
|
||||
? ["AI_GOOGLE_CLOUD_CREDENTIALS_JSON or AI_GOOGLE_CLOUD_APPLICATION_CREDENTIALS"]
|
||||
: []),
|
||||
],
|
||||
});
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user