diff --git a/charts/retool/.helmignore b/.helmignore similarity index 100% rename from charts/retool/.helmignore rename to .helmignore diff --git a/charts/retool/Chart.lock b/charts/retool/Chart.lock index 7eb7dc00..372d29c3 100644 --- a/charts/retool/Chart.lock +++ b/charts/retool/Chart.lock @@ -2,5 +2,8 @@ dependencies: - name: postgresql repository: https://charts.bitnami.com/bitnami version: 12.1.5 -digest: sha256:8607eadb8d79b34833608ccbb74e2d45be7ce35545eb8815254d6bed42854f6f -generated: "2023-03-20T13:18:05.170549-07:00" +- name: retool-temporal-services + repository: "" + version: 1.1.2 +digest: sha256:5ed0c5db868514a511b428b847d5b8c570cf09bf009177328208989029124e94 +generated: "2023-09-07T13:59:44.649522-04:00" diff --git a/charts/retool/Chart.yaml b/charts/retool/Chart.yaml index ff991315..08ed994e 100644 --- a/charts/retool/Chart.yaml +++ b/charts/retool/Chart.yaml @@ -11,3 +11,6 @@ dependencies: version: 12.1.5 repository: https://charts.bitnami.com/bitnami condition: postgresql.enabled + - name: retool-temporal-services + version: 1.1.2 + condition: retool-temporal-services.enabled,workflows.enabled diff --git a/charts/retool/templates/_helpers.tpl b/charts/retool/templates/_helpers.tpl index 3c18d28b..eb133b8e 100644 --- a/charts/retool/templates/_helpers.tpl +++ b/charts/retool/templates/_helpers.tpl @@ -122,3 +122,43 @@ Set postgresql user {{- .Values.config.postgresql.user | quote -}} {{- end -}} {{- end -}} + +{{/* +Set Temporal frontend host +*/}} +{{- define "retool.temporal.host" -}} +{{- if (.Values.workflows.temporal).enabled -}} +{{- .Values.workflows.temporal.host | quote -}} +{{- else -}} +{{- printf "%s-%s" (include "temporal.fullname" (index .Subcharts "retool-temporal-services")) "frontend" -}} +{{- end -}} +{{- end -}} + +{{/* +Set Temporal frontend port +*/}} +{{- define "retool.temporal.port" -}} +{{- if (.Values.workflows.temporal).enabled -}} +{{- .Values.workflows.temporal.port | quote -}} +{{- else -}} +{{- "7233" | quote -}} +{{- end -}} +{{- end -}} + +{{/* +Set Temporal namespace +*/}} +{{- define "retool.temporal.namespace" -}} +{{- if (.Values.workflows.temporal).enabled -}} +{{- .Values.workflows.temporal.namespace | quote -}} +{{- else -}} +{{- "workflows" | quote -}} +{{- end -}} +{{- end -}} + +{{/* +Set code executor service name +*/}} +{{- define "retool.codeExecutor.name" -}} +{{ template "retool.fullname" . }}-code-executor +{{- end -}} \ No newline at end of file diff --git a/charts/retool/templates/deployment_backend.yaml b/charts/retool/templates/deployment_backend.yaml index 3560ddcc..89a7e1f0 100644 --- a/charts/retool/templates/deployment_backend.yaml +++ b/charts/retool/templates/deployment_backend.yaml @@ -1,7 +1,7 @@ apiVersion: apps/v1 kind: Deployment metadata: - name: {{ template "retool.fullname" . }} + name: {{ template "retool.fullname" . }}-main-backend labels: {{- include "retool.labels" . | nindent 4 }} {{- if .Values.deployment.annotations }} @@ -60,6 +60,9 @@ spec: {{- if gt (int (toString (.Values.replicaCount))) 1 }} - name: SERVICE_TYPE value: MAIN_BACKEND,DB_CONNECTOR,DB_SSH_CONNECTOR + {{- else }} + - name: SERVICE_TYPE + value: MAIN_BACKEND,DB_CONNECTOR,DB_SSH_CONNECTOR,JOBS_RUNNER {{- end }} - name: CLIENT_ID value: {{ default "" .Values.config.auth.google.clientId }} @@ -77,6 +80,44 @@ spec: value: {{ template "retool.postgresql.user" . }} - name: POSTGRES_SSL_ENABLED value: {{ template "retool.postgresql.ssl_enabled" . }} + {{- if .Values.config.dbConnectorTimeout }} + - name: DBCONNECTOR_QUERY_TIMEOUT_MS + value: {{ .Values.config.dbConnectorTimeout | quote }} + {{- end }} + {{- if and (.Values.workflows.enabled) (or (index .Values "retool-temporal-services-helm" "enabled") (.Values.workflows.temporal.enabled)) }} + - name: WORKFLOW_TEMPORAL_CLUSTER_FRONTEND_HOST + value: {{ template "retool.temporal.host" . }} + - name: WORKFLOW_TEMPORAL_CLUSTER_FRONTEND_PORT + value: {{ template "retool.temporal.port" . }} + - name: WORKFLOW_TEMPORAL_CLUSTER_NAMESPACE + value: {{ template "retool.temporal.namespace" . }} + {{- end }} + {{- if (.Values.workflows.enabled) }} + - name: WORKFLOW_BACKEND_HOST + value: http://{{ template "retool.fullname" . }}-workflow-backend + {{- end }} + {{- if (.Values.workflows.temporal).sslEnabled }} + - name: WORKFLOW_TEMPORAL_TLS_ENABLED + value: "true" + {{- if (and (.Values.workflows.temporal).sslCert (.Values.workflows.temporal).sslKey) }} + - name: WORKFLOW_TEMPORAL_TLS_CRT + value: {{ .Values.workflows.temporal.sslCert }} + - name: WORKFLOW_TEMPORAL_TLS_KEY + valueFrom: + secretKeyRef: + {{- if (.Values.workflows.temporal).sslKeySecretName }} + name: {{ .Values.workflows.temporal.sslKeySecretName }} + key: {{ .Values.workflows.temporal.sslKeySecretKey | default "temporal-tls-key" }} + {{- else }} + name: {{ template "retool.fullname" . }} + key: "temporal-tls-key" + {{- end }} + {{- end }} + {{- end }} + {{- if .Values.codeExecutor.enabled }} + - name: CODE_EXECUTOR_INGRESS_DOMAIN + value: http://{{ template "retool.codeExecutor.name" . }} + {{- end }} {{- if and (not .Values.externalSecrets.enabled) (not .Values.externalSecrets.externalSecretsOperator.enabled) }} - name: LICENSE_KEY valueFrom: @@ -158,6 +199,10 @@ spec: envFrom: - secretRef: name: {{ .Values.externalSecrets.name }} + {{- range .Values.externalSecrets.secrets }} + - secretRef: + name: {{ .name }} + {{- end }} {{- end }} {{- if .Values.externalSecrets.externalSecretsOperator.enabled }} envFrom: @@ -257,7 +302,7 @@ spec: {{- end }} --- {{- if .Values.podDisruptionBudget }} -{{- if semverCompare ">=1.21-0" .Capabilities.KubeVersion.GitVersion -}} +{{- if semverCompare ">=1.21-0" .Capabilities.KubeVersion.Version -}} apiVersion: policy/v1 {{- else -}} apiVersion: policy/v1beta1 diff --git a/charts/retool/templates/deployment_code_executor.yaml b/charts/retool/templates/deployment_code_executor.yaml new file mode 100644 index 00000000..7f52e795 --- /dev/null +++ b/charts/retool/templates/deployment_code_executor.yaml @@ -0,0 +1,125 @@ +{{- if .Values.codeExecutor.enabled }} +apiVersion: apps/v1 +kind: Deployment +metadata: + name: {{ template "retool.codeExecutor.name" . }} + labels: + retoolService: {{ template "retool.codeExecutor.name" . }} +{{- include "retool.labels" . | nindent 4 }} +{{- if .Values.deployment.annotations }} + annotations: +{{ toYaml .Values.deployment.annotations | indent 4 }} +{{- end }} +spec: + replicas: {{ .Values.codeExecutor.replicaCount }} + selector: + matchLabels: + retoolService: {{ template "retool.codeExecutor.name" . }} + revisionHistoryLimit: {{ .Values.revisionHistoryLimit }} + template: + metadata: + annotations: + prometheus.io/job: {{ template "retool.codeExecutor.name" . }} + prometheus.io/scrape: 'true' + prometheus.io/port: '9090' +{{- if .Values.podAnnotations }} +{{ toYaml .Values.podAnnotations | indent 8 }} +{{- end }} +{{- if .Values.codeExecutor.annotations }} +{{ toYaml .Values.codeExecutor.annotations | indent 8 }} +{{- end }} + labels: + retoolService: {{ template "retool.codeExecutor.name" . }} +{{- if .Values.podLabels }} +{{ toYaml .Values.podLabels | indent 8 }} +{{- end }} +{{- if .Values.codeExecutor.labels }} +{{ toYaml .Values.codeExecutor.labels | indent 8 }} +{{- end }} + spec: + serviceAccountName: {{ template "retool.serviceAccountName" . }} + {{- if .Values.priorityClassName }} + priorityClassName: "{{ .Values.priorityClassName }}" + {{- end }} +{{- if .Values.initContainers }} + initContainers: +{{- range $key, $value := .Values.initContainers }} + - name: "{{ $key }}" +{{ toYaml $value | indent 8 }} +{{- end }} +{{- end }} + containers: + - name: {{ .Chart.Name }} + image: "{{ .Values.codeExecutor.image.repository }}:{{ required "Please set a value for .Values.codeExecutor.image.tag" .Values.codeExecutor.image.tag }}" + imagePullPolicy: {{ .Values.image.pullPolicy }} + securityContext: + privileged: true + env: + - name: NODE_ENV + value: production + - name: NODE_OPTIONS + value: {{(.Values.codeExecutor.config).nodeOptions | default "--max_old_space_size=1024" }} + {{- range $key, $value := .Values.env }} + - name: "{{ $key }}" + value: "{{ $value }}" + {{- end }} + {{- range .Values.environmentSecrets }} + - name: {{ .name }} + valueFrom: + secretKeyRef: + name: {{ .secretKeyRef.name }} + key: {{ .secretKeyRef.key }} + {{- end }} + {{- with .Values.environmentVariables }} +{{ toYaml . | indent 10 }} + {{- end }} + ports: + - containerPort: 3004 + name: {{ template "retool.name" . }} + protocol: TCP + - containerPort: 9090 + name: metrics + protocol: TCP +{{- if .Values.livenessProbe.enabled }} + livenessProbe: + httpGet: + path: {{ .Values.livenessProbe.path }} + port: 3004 + initialDelaySeconds: {{ .Values.livenessProbe.initialDelaySeconds }} + timeoutSeconds: {{ .Values.livenessProbe.timeoutSeconds }} + failureThreshold: {{ .Values.livenessProbe.failureThreshold }} +{{- end }} +{{- if .Values.readinessProbe.enabled }} + readinessProbe: + httpGet: + path: {{ .Values.readinessProbe.path }} + port: 3004 + initialDelaySeconds: {{ .Values.readinessProbe.initialDelaySeconds }} + timeoutSeconds: {{ .Values.readinessProbe.timeoutSeconds }} + successThreshold: {{ .Values.readinessProbe.successThreshold }} + periodSeconds: {{ .Values.readinessProbe.periodSeconds }} +{{- end }} + resources: +{{ toYaml .Values.codeExecutor.resources | indent 10 }} +{{- if .Values.image.pullSecrets }} + imagePullSecrets: +{{ toYaml .Values.image.pullSecrets | indent 8 }} +{{- end }} +--- +apiVersion: v1 +kind: Service +metadata: + name: {{ template "retool.codeExecutor.name" . }} +spec: + selector: + retoolService: {{ template "retool.codeExecutor.name" . }} + ports: + - protocol: TCP + port: 80 + targetPort: 3004 + name: {{ template "retool.name" . }} + - protocol: TCP + port: 9090 + targetPort: metrics + name: metrics +{{- end }} diff --git a/charts/retool/templates/deployment_jobs.yaml b/charts/retool/templates/deployment_jobs.yaml index 54b366ce..de9d12b9 100644 --- a/charts/retool/templates/deployment_jobs.yaml +++ b/charts/retool/templates/deployment_jobs.yaml @@ -153,6 +153,10 @@ spec: envFrom: - secretRef: name: {{ .Values.externalSecrets.name }} + {{- range .Values.externalSecrets.secrets }} + - secretRef: + name: {{ .name }} + {{- end }} {{- end }} {{- if .Values.externalSecrets.externalSecretsOperator.enabled }} envFrom: diff --git a/charts/retool/templates/deployment_workflows.yaml b/charts/retool/templates/deployment_workflows.yaml new file mode 100644 index 00000000..ecad55bb --- /dev/null +++ b/charts/retool/templates/deployment_workflows.yaml @@ -0,0 +1,310 @@ +{{- if .Values.workflows.enabled }} +apiVersion: apps/v1 +kind: Deployment +metadata: + name: {{ template "retool.fullname" . }}-workflow-backend + labels: + retoolService: {{ template "retool.fullname" . }}-workflow-backend +{{- include "retool.labels" . | nindent 4 }} +{{- if .Values.deployment.annotations }} + annotations: +{{ toYaml .Values.deployment.annotations | indent 4 }} +{{- end }} +spec: + replicas: 1 + selector: + matchLabels: + retoolService: {{ template "retool.fullname" . }}-workflow-backend + revisionHistoryLimit: {{ .Values.revisionHistoryLimit }} + template: + metadata: + annotations: +{{- if .Values.podAnnotations }} +{{ toYaml .Values.podAnnotations | indent 8 }} +{{- end }} +{{- if .Values.backend.annotations }} +{{ toYaml .Values.backend.annotations | indent 8 }} +{{- end }} + labels: + retoolService: {{ template "retool.fullname" . }}-workflow-backend +{{- if .Values.podLabels }} +{{ toYaml .Values.podLabels | indent 8 }} +{{- end }} +{{- if .Values.workflows.labels }} +{{ toYaml .Values.workflows.labels | indent 8 }} +{{- end }} + spec: + serviceAccountName: {{ template "retool.serviceAccountName" . }} + {{- if .Values.priorityClassName }} + priorityClassName: "{{ .Values.priorityClassName }}" + {{- end }} +{{- if .Values.initContainers }} + initContainers: +{{- range $key, $value := .Values.initContainers }} + - name: "{{ $key }}" +{{ toYaml $value | indent 8 }} +{{- end }} +{{- end }} + containers: + - name: {{ .Chart.Name }} + image: "{{ .Values.image.repository }}:{{ required "Please set a value for .Values.image.tag" .Values.image.tag }}" + imagePullPolicy: {{ .Values.image.pullPolicy }} + args: + - bash + - -c + - chmod -R +x ./docker_scripts; sync; ./docker_scripts/wait-for-it.sh -t 0 {{ template "retool.postgresql.host" . }}:{{ template "retool.postgresql.port" . }}; ./docker_scripts/start_api.sh + {{- if .Values.commandline.args }} +{{ toYaml .Values.commandline.args | indent 10 }} + {{- end }} + env: + - name: NODE_ENV + value: production + - name: SERVICE_TYPE + value: DB_CONNECTOR,DB_SSH_CONNECTOR,WORKFLOW_BACKEND + - name: DBCONNECTOR_POSTGRES_POOL_MAX_SIZE + value: "100" + - name: DBCONNECTOR_QUERY_TIMEOUT_MS + {{- if .Values.workflows.dbConnectorTimeout }} + value: {{ .Values.workflows.dbConnectorTimeout | quote}} + {{- else if .Values.config.dbConnectorTimeout }} + value: {{ .Values.config.dbConnectorTimeout | quote}} + {{- else }} + value: "5400000" + {{- end }} + - name: DISABLE_DATABASE_MIGRATIONS + value: "true" + {{- if or (index .Values "retool-temporal-services-helm" "enabled") (.Values.workflows.temporal.enabled) }} + - name: WORKFLOW_TEMPORAL_CLUSTER_FRONTEND_HOST + value: {{ template "retool.temporal.host" . }} + - name: WORKFLOW_TEMPORAL_CLUSTER_FRONTEND_PORT + value: {{ template "retool.temporal.port" . }} + - name: WORKFLOW_TEMPORAL_CLUSTER_NAMESPACE + value: {{ template "retool.temporal.namespace" . }} + {{- end }} + {{- if (.Values.workflows.temporal).sslEnabled }} + - name: WORKFLOW_TEMPORAL_TLS_ENABLED + value: "true" + {{- if (and (.Values.workflows.temporal).sslCert (.Values.workflows.temporal).sslKey) }} + - name: WORKFLOW_TEMPORAL_TLS_CRT + value: {{ .Values.workflows.temporal.sslCert }} + - name: WORKFLOW_TEMPORAL_TLS_KEY + valueFrom: + secretKeyRef: + {{- if (.Values.workflows.temporal).sslKeySecretName }} + name: {{ .Values.workflows.temporal.sslKeySecretName }} + key: {{ .Values.workflows.temporal.sslKeySecretKey | default "temporal-tls-key" }} + {{- else }} + name: {{ template "retool.fullname" . }} + key: "temporal-tls-key" + {{- end }} + {{- end }} + {{- end }} + - name: CLIENT_ID + value: {{ default "" .Values.config.auth.google.clientId }} + - name: COOKIE_INSECURE + value: {{ .Values.config.useInsecureCookies | quote }} + - name: RESTRICTED_DOMAIN + value: {{ default "" .Values.config.auth.google.domain }} + - name: POSTGRES_HOST + value: {{ template "retool.postgresql.host" . }} + - name: POSTGRES_PORT + value: {{ template "retool.postgresql.port" . }} + - name: POSTGRES_DB + value: {{ template "retool.postgresql.db" . }} + - name: POSTGRES_USER + value: {{ template "retool.postgresql.user" . }} + - name: POSTGRES_SSL_ENABLED + value: {{ template "retool.postgresql.ssl_enabled" . }} + - name: WORKFLOW_BACKEND_HOST + value: http://{{ template "retool.fullname" . }}-workflow-backend + {{- if .Values.codeExecutor.enabled }} + - name: CODE_EXECUTOR_INGRESS_DOMAIN + value: http://{{ template "retool.codeExecutor.name" . }} + {{- end }} + {{- if and (not .Values.externalSecrets.enabled) (not .Values.externalSecrets.externalSecretsOperator.enabled) }} + - name: LICENSE_KEY + valueFrom: + secretKeyRef: + {{- if .Values.config.licenseKeySecretName }} + name: {{ .Values.config.licenseKeySecretName }} + key: {{ .Values.config.licenseKeySecretKey | default "license-key" }} + {{- else }} + name: {{ template "retool.fullname" . }} + key: license-key + {{- end }} + - name: JWT_SECRET + valueFrom: + secretKeyRef: + {{- if .Values.config.jwtSecretSecretName }} + name: {{ .Values.config.jwtSecretSecretName }} + key: {{ .Values.config.jwtSecretSecretKey | default "jwt-secret" }} + {{- else }} + name: {{ template "retool.fullname" . }} + key: jwt-secret + {{- end }} + - name: ENCRYPTION_KEY + valueFrom: + secretKeyRef: + {{- if .Values.config.encryptionKeySecretName }} + name: {{ .Values.config.encryptionKeySecretName }} + key: {{ .Values.config.encryptionKeySecretKey | default "encryption-key" }} + {{- else }} + name: {{ template "retool.fullname" . }} + key: encryption-key + {{- end }} + - name: POSTGRES_PASSWORD + valueFrom: + secretKeyRef: + {{- if .Values.postgresql.enabled }} + name: {{ template "retool.postgresql.fullname" . }} + key: postgres-password + {{- else }} + {{- if .Values.config.postgresql.passwordSecretName }} + name: {{ .Values.config.postgresql.passwordSecretName }} + key: {{ .Values.config.postgresql.passwordSecretKey | default "postgresql-password" }} + {{- else }} + name: {{ template "retool.fullname" . }} + key: postgresql-password + {{- end }} + {{- end }} + - name: CLIENT_SECRET + valueFrom: + secretKeyRef: + {{- if .Values.config.auth.google.clientSecretSecretName }} + name: {{ .Values.config.auth.google.clientSecretSecretName }} + key: {{ .Values.config.auth.google.clientSecretSecretKey | default "google-client-secret" }} + {{- else }} + name: {{ template "retool.fullname" . }} + key: google-client-secret + {{- end }} + {{- end }} + {{- range $key, $value := .Values.env }} + - name: "{{ $key }}" + value: "{{ $value }}" + {{- end }} + {{- range .Values.environmentSecrets }} + - name: {{ .name }} + valueFrom: + secretKeyRef: + name: {{ .secretKeyRef.name }} + key: {{ .secretKeyRef.key }} + {{- end }} + {{- with .Values.environmentVariables }} +{{ toYaml . | indent 10 }} + {{- end }} + {{- if .Values.externalSecrets.enabled }} + envFrom: + - secretRef: + name: {{ .Values.externalSecrets.name }} + {{- range .Values.externalSecrets.secrets }} + - secretRef: + name: {{ .name }} + {{- end }} + {{- end }} + {{- if .Values.externalSecrets.externalSecretsOperator.enabled }} + envFrom: + {{- range .Values.externalSecrets.externalSecretsOperator.secretRef }} + - secretRef: + name: {{ .name }} + {{- end }} + {{- end }} + ports: + - containerPort: {{ .Values.service.internalPort }} + name: {{ template "retool.name" . }} + protocol: TCP +{{- if .Values.livenessProbe.enabled }} + livenessProbe: + httpGet: + path: {{ .Values.livenessProbe.path }} + port: {{ .Values.service.internalPort }} + initialDelaySeconds: {{ .Values.livenessProbe.initialDelaySeconds }} + timeoutSeconds: {{ .Values.livenessProbe.timeoutSeconds }} + failureThreshold: {{ .Values.livenessProbe.failureThreshold }} +{{- end }} +{{- if .Values.readinessProbe.enabled }} + readinessProbe: + httpGet: + path: {{ .Values.readinessProbe.path }} + port: {{ .Values.service.internalPort }} + initialDelaySeconds: {{ .Values.readinessProbe.initialDelaySeconds }} + timeoutSeconds: {{ .Values.readinessProbe.timeoutSeconds }} + successThreshold: {{ .Values.readinessProbe.successThreshold }} + periodSeconds: {{ .Values.readinessProbe.periodSeconds }} +{{- end }} + resources: +{{ toYaml .Values.resources | indent 10 }} + volumeMounts: + {{- range $configFile := (keys .Values.files) }} + - name: {{ template "retool.name" $ }} + mountPath: "/usr/share/retool/config/{{ $configFile }}" + subPath: {{ $configFile }} + {{- end }} +{{- if .Values.extraVolumeMounts }} +{{ toYaml .Values.extraVolumeMounts | indent 8 }} +{{- end }} +{{- with .Values.extraContainers }} +{{ tpl . $ | indent 6 }} +{{- end }} +{{- range .Values.extraConfigMapMounts }} + - name: {{ .name }} + mountPath: {{ .mountPath }} + subPath: {{ .subPath }} +{{- end }} + {{- if .Values.image.pullSecrets }} + imagePullSecrets: +{{ toYaml .Values.image.pullSecrets | indent 8 }} + {{- end }} + {{- if .Values.affinity }} + affinity: +{{ toYaml .Values.affinity | indent 8 }} + {{- end }} + {{- if .Values.nodeSelector }} + nodeSelector: +{{ toYaml .Values.nodeSelector | indent 8 }} + {{- end }} + tolerations: +{{ toYaml .Values.tolerations | indent 8 }} +{{- if .Values.securityContext.enabled }} + securityContext: + runAsUser: {{ .Values.securityContext.runAsUser }} + fsGroup: {{ .Values.securityContext.fsGroup }} +{{- end }} + volumes: +{{- range .Values.extraConfigMapMounts }} + - name: {{ .name }} + configMap: + name: {{ .configMap }} +{{- end }} +{{- if .Values.extraVolumes }} +{{ toYaml .Values.extraVolumes | indent 8 }} +{{- end }} +--- +{{- if .Values.podDisruptionBudget }} +{{- if semverCompare ">=1.21-0" .Capabilities.KubeVersion.Version -}} +apiVersion: policy/v1 +{{- else -}} +apiVersion: policy/v1beta1 +{{- end }} +kind: PodDisruptionBudget +metadata: + name: {{ template "retool.fullname" . }} +spec: + {{ toYaml .Values.podDisruptionBudget }} + selector: + matchLabels: + retoolService: {{ template "retool.fullname" . }}-workflow-backend + {{- include "retool.selectorLabels" . | nindent 6 }} +{{- end }} +--- +apiVersion: v1 +kind: Service +metadata: + name: {{ template "retool.fullname" . }}-workflow-backend +spec: + selector: + retoolService: {{ template "retool.fullname" . }}-workflow-backend + ports: + - protocol: TCP + port: 80 + targetPort: {{ .Values.service.internalPort }} +{{- end }} diff --git a/charts/retool/templates/deployment_workflows_worker.yaml b/charts/retool/templates/deployment_workflows_worker.yaml new file mode 100644 index 00000000..0380adff --- /dev/null +++ b/charts/retool/templates/deployment_workflows_worker.yaml @@ -0,0 +1,342 @@ +{{- if .Values.workflows.enabled }} +apiVersion: apps/v1 +kind: Deployment +metadata: + name: {{ template "retool.fullname" . }}-workflow-worker + labels: + retoolService: {{ template "retool.fullname" . }}-workflow-worker +{{- include "retool.labels" . | nindent 4 }} +{{- if .Values.deployment.annotations }} + annotations: +{{ toYaml .Values.deployment.annotations | indent 4 }} +{{- end }} +spec: + replicas: {{ .Values.workflows.replicaCount }} + selector: + matchLabels: + retoolService: {{ template "retool.fullname" . }}-workflow-worker + revisionHistoryLimit: {{ .Values.revisionHistoryLimit }} + template: + metadata: + annotations: + prometheus.io/job: {{ template "retool.fullname" . }}-workflow-worker + prometheus.io/scrape: 'true' + prometheus.io/port: '9090' +{{- if .Values.podAnnotations }} +{{ toYaml .Values.podAnnotations | indent 8 }} +{{- end }} +{{- if .Values.backend.annotations }} +{{ toYaml .Values.backend.annotations | indent 8 }} +{{- end }} +{{- if .Values.workflows.annotations }} +{{ toYaml .Values.workflows.annotations | indent 8 }} +{{- end }} + labels: + retoolService: {{ template "retool.fullname" . }}-workflow-worker +{{- if .Values.podLabels }} +{{ toYaml .Values.podLabels | indent 8 }} +{{- end }} +{{- if .Values.workflows.labels }} +{{ toYaml .Values.workflows.labels | indent 8 }} +{{- end }} + spec: + serviceAccountName: {{ template "retool.serviceAccountName" . }} + {{- if .Values.priorityClassName }} + priorityClassName: "{{ .Values.priorityClassName }}" + {{- end }} +{{- if .Values.initContainers }} + initContainers: +{{- range $key, $value := .Values.initContainers }} + - name: "{{ $key }}" +{{ toYaml $value | indent 8 }} +{{- end }} +{{- end }} + containers: + - name: {{ .Chart.Name }} + image: "{{ .Values.image.repository }}:{{ required "Please set a value for .Values.image.tag" .Values.image.tag }}" + imagePullPolicy: {{ .Values.image.pullPolicy }} + args: + - bash + - -c + - chmod -R +x ./docker_scripts; sync; ./docker_scripts/wait-for-it.sh -t 0 {{ template "retool.postgresql.host" . }}:{{ template "retool.postgresql.port" . }}; ./docker_scripts/start_api.sh + {{- if .Values.commandline.args }} +{{ toYaml .Values.commandline.args | indent 10 }} + {{- end }} + env: + - name: NODE_ENV + value: production + - name: NODE_OPTIONS + value: {{(.Values.workflows.config).nodeOptions | default "--max_old_space_size=1024" }} + - name: SERVICE_TYPE + value: WORKFLOW_TEMPORAL_WORKER + - name: DBCONNECTOR_POSTGRES_POOL_MAX_SIZE + value: "100" + - name: DBCONNECTOR_QUERY_TIMEOUT_MS + {{- if .Values.workflows.dbConnectorTimeout }} + value: {{ .Values.workflows.dbConnectorTimeout | quote}} + {{- else if .Values.config.dbConnectorTimeout }} + value: {{ .Values.config.dbConnectorTimeout | quote}} + {{- else }} + value: "5400000" + {{- end }} + - name: DISABLE_DATABASE_MIGRATIONS + value: "true" + {{- if or (index .Values "retool-temporal-services-helm" "enabled") (.Values.workflows.temporal.enabled) }} + - name: WORKFLOW_TEMPORAL_CLUSTER_FRONTEND_HOST + value: {{ template "retool.temporal.host" . }} + - name: WORKFLOW_TEMPORAL_CLUSTER_FRONTEND_PORT + value: {{ template "retool.temporal.port" . }} + - name: WORKFLOW_TEMPORAL_CLUSTER_NAMESPACE + value: {{ template "retool.temporal.namespace" . }} + {{- end }} + {{- if (.Values.workflows.temporal).sslEnabled }} + - name: WORKFLOW_TEMPORAL_TLS_ENABLED + value: "true" + {{- if (and (.Values.workflows.temporal).sslCert (.Values.workflows.temporal).sslKey) }} + - name: WORKFLOW_TEMPORAL_TLS_CRT + value: {{ .Values.workflows.temporal.sslCert }} + - name: WORKFLOW_TEMPORAL_TLS_KEY + valueFrom: + secretKeyRef: + {{- if (.Values.workflows.temporal).sslKeySecretName }} + name: {{ .Values.workflows.temporal.sslKeySecretName }} + key: {{ .Values.workflows.temporal.sslKeySecretKey | default "temporal-tls-key" }} + {{- else }} + name: {{ template "retool.fullname" . }} + key: "temporal-tls-key" + {{- end }} + {{- end }} + {{- end }} + - name: WORKFLOW_WORKER_HEALTHCHECK_PORT + value: "3005" + - name: WORKFLOW_BACKEND_HOST + value: http://{{ template "retool.fullname" . }}-workflow-backend + - name: CLIENT_ID + value: {{ default "" .Values.config.auth.google.clientId }} + - name: COOKIE_INSECURE + value: {{ .Values.config.useInsecureCookies | quote }} + - name: RESTRICTED_DOMAIN + value: {{ default "" .Values.config.auth.google.domain }} + - name: POSTGRES_HOST + value: {{ template "retool.postgresql.host" . }} + - name: POSTGRES_PORT + value: {{ template "retool.postgresql.port" . }} + - name: POSTGRES_DB + value: {{ template "retool.postgresql.db" . }} + - name: POSTGRES_USER + value: {{ template "retool.postgresql.user" . }} + - name: POSTGRES_SSL_ENABLED + value: {{ template "retool.postgresql.ssl_enabled" . }} + {{- if .Values.codeExecutor.enabled }} + - name: CODE_EXECUTOR_INGRESS_DOMAIN + value: http://{{ template "retool.codeExecutor.name" . }} + {{- end }} + {{- if and (((.Values.workflows.config).otelCollector).enabled) (((.Values.workflows.config).otelCollector).endpoint) }} + - name: OTEL_EXPORTER_OTLP_ENDPOINT + value: {{ ((.Values.workflows.config).otelCollector).endpoint }} + {{- else if ((.Values.workflows.config).otelCollector).enabled }} + - name: HOST_IP + valueFrom: + fieldRef: + fieldPath: status.hostIP + - name: OTEL_EXPORTER_OTLP_ENDPOINT + value: "http://$(HOST_IP):4317" + {{- end }} + {{- if and (not .Values.externalSecrets.enabled) (not .Values.externalSecrets.externalSecretsOperator.enabled) }} + - name: LICENSE_KEY + valueFrom: + secretKeyRef: + {{- if .Values.config.licenseKeySecretName }} + name: {{ .Values.config.licenseKeySecretName }} + key: {{ .Values.config.licenseKeySecretKey | default "license-key" }} + {{- else }} + name: {{ template "retool.fullname" . }} + key: license-key + {{- end }} + - name: JWT_SECRET + valueFrom: + secretKeyRef: + {{- if .Values.config.jwtSecretSecretName }} + name: {{ .Values.config.jwtSecretSecretName }} + key: {{ .Values.config.jwtSecretSecretKey | default "jwt-secret" }} + {{- else }} + name: {{ template "retool.fullname" . }} + key: jwt-secret + {{- end }} + - name: ENCRYPTION_KEY + valueFrom: + secretKeyRef: + {{- if .Values.config.encryptionKeySecretName }} + name: {{ .Values.config.encryptionKeySecretName }} + key: {{ .Values.config.encryptionKeySecretKey | default "encryption-key" }} + {{- else }} + name: {{ template "retool.fullname" . }} + key: encryption-key + {{- end }} + - name: POSTGRES_PASSWORD + valueFrom: + secretKeyRef: + {{- if .Values.postgresql.enabled }} + name: {{ template "retool.postgresql.fullname" . }} + key: postgres-password + {{- else }} + {{- if .Values.config.postgresql.passwordSecretName }} + name: {{ .Values.config.postgresql.passwordSecretName }} + key: {{ .Values.config.postgresql.passwordSecretKey | default "postgresql-password" }} + {{- else }} + name: {{ template "retool.fullname" . }} + key: postgresql-password + {{- end }} + {{- end }} + - name: CLIENT_SECRET + valueFrom: + secretKeyRef: + {{- if .Values.config.auth.google.clientSecretSecretName }} + name: {{ .Values.config.auth.google.clientSecretSecretName }} + key: {{ .Values.config.auth.google.clientSecretSecretKey | default "google-client-secret" }} + {{- else }} + name: {{ template "retool.fullname" . }} + key: google-client-secret + {{- end }} + {{- end }} + {{- range $key, $value := .Values.env }} + - name: "{{ $key }}" + value: "{{ $value }}" + {{- end }} + {{- range .Values.environmentSecrets }} + - name: {{ .name }} + valueFrom: + secretKeyRef: + name: {{ .secretKeyRef.name }} + key: {{ .secretKeyRef.key }} + {{- end }} + {{- with .Values.environmentVariables }} +{{ toYaml . | indent 10 }} + {{- end }} + {{- with .Values.workflows.config.environmentVariables }} +{{ toYaml . | indent 10 }} + {{- end }} + {{- if .Values.externalSecrets.enabled }} + envFrom: + - secretRef: + name: {{ .Values.externalSecrets.name }} + {{- range .Values.externalSecrets.secrets }} + - secretRef: + name: {{ .name }} + {{- end }} + {{- end }} + {{- if .Values.externalSecrets.externalSecretsOperator.enabled }} + envFrom: + {{- range .Values.externalSecrets.externalSecretsOperator.secretRef }} + - secretRef: + name: {{ .name }} + {{- end }} + {{- end }} + ports: + - containerPort: 3005 + name: {{ template "retool.name" . }} + protocol: TCP + - containerPort: 9090 + name: metrics + protocol: TCP + +{{- if .Values.livenessProbe.enabled }} + livenessProbe: + httpGet: + path: {{ .Values.livenessProbe.path }} + port: 3005 + initialDelaySeconds: {{ .Values.livenessProbe.initialDelaySeconds }} + timeoutSeconds: {{ .Values.livenessProbe.timeoutSeconds }} + failureThreshold: {{ .Values.livenessProbe.failureThreshold }} +{{- end }} +{{- if .Values.readinessProbe.enabled }} + readinessProbe: + httpGet: + path: {{ .Values.readinessProbe.path }} + port: 3005 + initialDelaySeconds: {{ .Values.readinessProbe.initialDelaySeconds }} + timeoutSeconds: {{ .Values.readinessProbe.timeoutSeconds }} + successThreshold: {{ .Values.readinessProbe.successThreshold }} + periodSeconds: {{ .Values.readinessProbe.periodSeconds }} +{{- end }} + resources: +{{ toYaml .Values.workflows.resources | indent 10 }} + volumeMounts: + {{- range $configFile := (keys .Values.files) }} + - name: {{ template "retool.name" $ }} + mountPath: "/usr/share/retool/config/{{ $configFile }}" + subPath: {{ $configFile }} + {{- end }} +{{- if .Values.extraVolumeMounts }} +{{ toYaml .Values.extraVolumeMounts | indent 8 }} +{{- end }} +{{- with .Values.extraContainers }} +{{ tpl . $ | indent 6 }} +{{- end }} +{{- range .Values.extraConfigMapMounts }} + - name: {{ .name }} + mountPath: {{ .mountPath }} + subPath: {{ .subPath }} +{{- end }} + {{- if .Values.image.pullSecrets }} + imagePullSecrets: +{{ toYaml .Values.image.pullSecrets | indent 8 }} + {{- end }} + {{- if .Values.affinity }} + affinity: +{{ toYaml .Values.affinity | indent 8 }} + {{- end }} + {{- if .Values.nodeSelector }} + nodeSelector: +{{ toYaml .Values.nodeSelector | indent 8 }} + {{- end }} + tolerations: +{{ toYaml .Values.tolerations | indent 8 }} +{{- if .Values.securityContext.enabled }} + securityContext: + runAsUser: {{ .Values.securityContext.runAsUser }} + fsGroup: {{ .Values.securityContext.fsGroup }} +{{- end }} + volumes: +{{- range .Values.extraConfigMapMounts }} + - name: {{ .name }} + configMap: + name: {{ .configMap }} +{{- end }} +{{- if .Values.extraVolumes }} +{{ toYaml .Values.extraVolumes | indent 8 }} +{{- end }} +--- +{{- if .Values.podDisruptionBudget }} +{{- if semverCompare ">=1.21-0" .Capabilities.KubeVersion.Version -}} +apiVersion: policy/v1 +{{- else -}} +apiVersion: policy/v1beta1 +{{- end }} +kind: PodDisruptionBudget +metadata: + name: {{ template "retool.fullname" . }} +spec: + {{ toYaml .Values.podDisruptionBudget }} + selector: + matchLabels: + {{- include "retool.selectorLabels" . | nindent 6 }} +{{- end }} +--- +apiVersion: v1 +kind: Service +metadata: + name: {{ template "retool.fullname" . }}-workflow-worker +spec: + selector: + retoolService: {{ template "retool.fullname" . }}-workflow-worker + ports: + - protocol: TCP + port: 3005 + targetPort: 3005 + name: {{ template "retool.name" . }} + - protocol: TCP + port: 9090 + targetPort: metrics + name: metrics +{{- end }} diff --git a/charts/retool/templates/ingress.yaml b/charts/retool/templates/ingress.yaml index bf4e7c7d..dca69f18 100644 --- a/charts/retool/templates/ingress.yaml +++ b/charts/retool/templates/ingress.yaml @@ -2,9 +2,9 @@ {{- $fullName := include "retool.fullname" . -}} {{- $svcPort := .Values.service.externalPort -}} {{- $pathType := .Values.ingress.pathType -}} -{{- if semverCompare ">=1.19-0" .Capabilities.KubeVersion.GitVersion -}} +{{- if semverCompare ">=1.19-0" .Capabilities.KubeVersion.Version -}} apiVersion: networking.k8s.io/v1 -{{- else if semverCompare ">=1.14-0" .Capabilities.KubeVersion.GitVersion -}} +{{- else if semverCompare ">=1.14-0" .Capabilities.KubeVersion.Version -}} apiVersion: networking.k8s.io/v1beta1 {{- else -}} apiVersion: extensions/v1beta1 @@ -22,7 +22,7 @@ metadata: {{- end }} name: {{ template "retool.fullname" . }} spec: - {{- if and .Values.ingress.ingressClassName (semverCompare ">=1.18-0" $.Capabilities.KubeVersion.GitVersion) }} + {{- if and .Values.ingress.ingressClassName (semverCompare ">=1.18-0" $.Capabilities.KubeVersion.Version) }} ingressClassName: {{ .Values.ingress.ingressClassName }} {{- end }} rules: @@ -31,11 +31,11 @@ spec: http: paths: - path: - {{- if and $pathType (semverCompare ">=1.18-0" $.Capabilities.KubeVersion.GitVersion) }} + {{- if and $pathType (semverCompare ">=1.18-0" $.Capabilities.KubeVersion.Version) }} pathType: {{ $pathType }} {{- end }} backend: - {{- if semverCompare ">=1.19-0" $.Capabilities.KubeVersion.GitVersion }} + {{- if semverCompare ">=1.19-0" $.Capabilities.KubeVersion.Version }} service: name: {{ $fullName }} port: @@ -51,11 +51,11 @@ spec: paths: {{- range .paths }} - path: {{ .path }} - {{- if and $pathType (semverCompare ">=1.18-0" $.Capabilities.KubeVersion.GitVersion) }} + {{- if and $pathType (semverCompare ">=1.18-0" $.Capabilities.KubeVersion.Version) }} pathType: {{ $pathType }} {{- end }} backend: - {{- if semverCompare ">=1.19-0" $.Capabilities.KubeVersion.GitVersion }} + {{- if semverCompare ">=1.19-0" $.Capabilities.KubeVersion.Version }} service: name: {{ $fullName }} port: diff --git a/values.yaml b/values.yaml index 83ba9d76..ba791976 100644 --- a/values.yaml +++ b/values.yaml @@ -8,6 +8,8 @@ config: # licenseKeySecretKey is the key in the k8s secret, default: license-key # licenseKeySecretKey: useInsecureCookies: false + # Timeout for queries, in ms. + # dbConnectorTimeout: 120000 auth: google: clientId: @@ -91,6 +93,10 @@ externalSecrets: # This mode only allows a single secret name to be provided. enabled: false name: retool-config + # Array of secrets to be use as env variables. (Optional) + secrets: [] + # - name: retool-config + # - name: retool-db # Support for External Secrets Operator: https://github.com/external-secrets/external-secrets externalSecretsOperator: enabled: false @@ -143,11 +149,22 @@ ingress: # - retool.example.com # servicePort: service-port pathType: ImplementationSpecific + # For supporting other ingress controllers that require customizing the .backend.service.name and .backend.service.port.name, + # like AWS ALB extraPaths allows that customization, and it takes precedence in the list of paths of for the host, + # this is in order to allow a rule like ssl-redirect from port 80-->443 to be first ( otherwise there wouldn't be a redirect ) + # extraPaths: + # - path: /* + # backend: + # service: + # name: ssl-redirect + # port: + # name: use-annotation + # pathType: ImplementationSpecific postgresql: # We highly recommend you do NOT use this subchart as is to run Postgres in a container # for your production instance of Retool; it is a default. Please use a managed Postgres, - # or self-host more permanantly. Use enabled: false and set in config above to do so. + # or self-host more permanently. Use enabled: false and set in config above to do so. enabled: true ssl_enabled: false auth: @@ -279,6 +296,216 @@ backend: # Labels for backend pods labels: {} +workflows: + enabled: true + + # A replicaCount of 1 will launch 6 pods -- 1 workflow backend, 1 workflow worker, and 4 pods that make up the executor temporal cluster + # Scaling this number will increase the number of workflow workers, e.g. a replicaCount of 4 + # will launch 9 pods -- 1 workflow backend, 4 workflow workers, and 4 for temporal cluster + + # ADVANCED: The temporal cluster can be scaled separately in the subchart (charts/retool-temporal-services/values.yaml) + # If your needs require scaling temporal, reach out to us for guidance -- it is likely the bottleneck is DB or worker replicaCount + replicaCount: 1 + + # Timeout for queries, in ms. This will set the timeout for workflows-related pods only + # If this value is not set but config.dbConnectorTimeout is, we will set workflows pod timeouts + # to .Values.config.dbConnectorTimeout + # dbConnectorTimeout: 120000 + + # Annotations for workflows worker pods + annotations: {} + + # Labels for workflows worker pods + labels: {} + + # IMPORTANT: Incompatible with retool-temporal-services-helm subchart + # This allows configuring a Retool Workflows deployment that uses your own Temporal cluster + # instead of deploying a new one. Set enabled to true and add the config variables. + # NOTE: Temporal Frontend with required TLS or mTLS not currently supported + temporal: + # set enabled to true if using a pre-existing temporal cluster + enabled: false + # discoverable service name for the temporal frontend service + # host: + # discoverable service port for the temporal frontend service + # port: + # temporal namespace to use for Temporal Workflows related to Retool + # namespace: + # whether to use TLS/SSL when connecting to Temporal + # sslEnabled: false + # base64 encoded string of TLS/SSL client certificate to use for mTLS + # sslCert: + # base64 encoded string of TLS/SSL client certificate secret key to use for mTLS + # sslKey: + # recommended alternative to sslKey. Name and key of k8s secret containing base64 encoded sslKey + # sslKeySecretName: + # sslKeySecretKey + + # Config for workflows worker pods. Node heap size limits can be overridden here + # otelCollector can be set to an OpenTelemetry Collector in your k8s cluster. This will configure Temporal metrics collection which + # provides observability into Workflows worker performance, particularly useful in high QPS use-cases + # environmentVariables will only be set on the workflows worker + # only change the CONCURRENT_*_LIMIT values if you have higher load usecases and deploy + # code_executor. Otherwise, the worker may OOM if the Workflows blocks use too much memory. + config: {} + # config: { + # nodeOptions: --max_old_space_size=1024 + # otelCollector: { + # enabled: true + # endpoint: http://$(HOST_IP):4317 + # } + # environmentVariables: [] + # - name: WORKFLOW_TEMPORAL_CONCURRENT_TASKS_LIMIT + # value: "100" + # - name: WORKFLOW_TEMPORAL_CONCURRENT_ACTIVITIES_LIMIT + # value: "100" + # } + + # Resources for the workflow worker - these are sane inputs that bias towards stability + # Can adjust but may see OOM errors if memory too low for heavy workflow load + resources: + limits: + cpu: 2000m + memory: 8192Mi + requests: + cpu: 1000m + memory: 2048Mi + +codeExecutor: + # Enable this for Python support and running code more securely within a separate + # sandboxed environment + enabled: false + + replicaCount: 1 + + # Annotations for code executor pods + annotations: {} + + # Labels for code executor pods + labels: {} + + # Config for code executor. Node heap size limits can be overridden here + config: {} + # config: { + # nodeOptions: --max_old_space_size=1024 + # } + + # Resources for the code executor. Most common issues will be seen with CPU usage as this will + # most likely be CPU bound. Adjust the CPU if latency increases under load. + resources: + limits: + cpu: 2000m + memory: 2048Mi + requests: + cpu: 1000m + memory: 1024Mi + + image: + repository: tryretool/code-executor-service + tag: + pullPolicy: IfNotPresent + +retool-temporal-services-helm: + # Disable this if using your own Temporal Cluster + enabled: false + server: + # Defines image to be used for temporal server + image: + repository: tryretool/one-offs + tag: retool-temporal-1.1.3 + pullPolicy: IfNotPresent + # this configures grpc_health_probe (https://github.com/grpc-ecosystem/grpc-health-probe) + # for healthchecks instead of native k8s. + # Set this to true if deploying in a k8s cluster on version <1.24 + useLegacyHealthProbe: false + config: + # the below values specify the database for temporal internals and workflow state + # both can point to the same db, and even the same as retool main above, although throughput + # will be limited. We strongly suggest using two total DBs: one for retool-main and one + # for default and visibility below + persistence: + default: + sql: + # host: + # port: + # the dbname used for temporal + # database: temporal + # user: + # password: + # existingSecret is the name of the secret where password is stored + # existingSecret: + # secretKey is the key in the k8s secret + # secretKey: + # options for SSL connections to database + # tls: + # enabled: true + # сaFile: + # certFile: + # keyFile: + # enableHostVerification: false + # serverName: + visibility: + sql: + # host: + # port: + # the dbname used for temporal visibility + # database: temporal_visibility + # user: + # password: + # existingSecret is the name of the secret where password is stored + # existingSecret: + # secretKey is the key in the k8s secret + # secretKey: + # options for SSL connections to database + # tls: + # enabled: true + # сaFile: + # certFile: + # keyFile: + # enableHostVerification: false + # serverName: + + # use-cases with very high throughput demands (>10k workflow blocks/sec) can modify + # below value to be higher, such as 512 or 1024 + numHistoryShards: 128 + + # define resources for each temporal service -- these are sane starting points that allow + # for scaling to ~3 workflow workers without hitting bottlenecks + resources: + limits: + cpu: 500m + memory: 1024Mi + requests: + cpu: 100m + memory: 128Mi + # example of setting service-specific resources, here we increase memory limit for history server + history: + resources: + limits: + cpu: 500m + memory: 2Gi + requests: + cpu: 100m + memory: 128Mi + + # Setting this value to true will spin up the temporal web UI, admintools, + # along with prometheus and grafana clusters for debugging performance + # TODO: define this in _helpers.tpl + # visibilityDebugMode: false + # DEBUGGING: below pods can be used for debugging and watching metrics + # launches prometheus pods and listeners on temporal services and worker + prometheus: + enabled: false + # launches a grafana pod with helpful workflows executor metrics and graphs + grafana: + enabled: false + # launches the temporal web UI, which allows you to see which workflows are currently running + web: + enabled: false + # launches the temporal admintools pod, which allows you to manage your cluster, e.g. terminate workflows + admintools: + enabled: false + persistentVolumeClaim: # set to true to use pvc enabled: false