From b6832b2c3e142315f739fd9d5786e7ebf0c672a8 Mon Sep 17 00:00:00 2001 From: lukefoster11 Date: Wed, 22 Apr 2026 11:22:56 -0700 Subject: [PATCH 1/8] first --- charts/retool/files/gvisor-seccomp.json | 447 +++++++++++ charts/retool/templates/_helpers.tpl | 152 ++++ charts/retool/templates/_workers.tpl | 24 +- .../agent_executor_device_plugin.yaml | 93 +++ .../templates/agent_executor_seccomp.yaml | 92 +++ .../templates/deployment_agent_executor.yaml | 698 ++++++++++++++++++ .../retool/templates/deployment_backend.yaml | 1 + .../templates/deployment_workflows.yaml | 1 + charts/retool/values.yaml | 177 +++++ 9 files changed, 1681 insertions(+), 4 deletions(-) create mode 100644 charts/retool/files/gvisor-seccomp.json create mode 100644 charts/retool/templates/agent_executor_device_plugin.yaml create mode 100644 charts/retool/templates/agent_executor_seccomp.yaml create mode 100644 charts/retool/templates/deployment_agent_executor.yaml diff --git a/charts/retool/files/gvisor-seccomp.json b/charts/retool/files/gvisor-seccomp.json new file mode 100644 index 0000000..9b2a1de --- /dev/null +++ b/charts/retool/files/gvisor-seccomp.json @@ -0,0 +1,447 @@ +{ + "comment": "Docker default seccomp profile extended with syscalls required by gVisor runsc (systrap platform, rootless mode). Use with: docker run --security-opt seccomp=gvisor-seccomp.json", + "defaultAction": "SCMP_ACT_ERRNO", + "defaultErrnoRet": 1, + "archMap": [ + { + "architecture": "SCMP_ARCH_X86_64", + "subArchitectures": ["SCMP_ARCH_X86", "SCMP_ARCH_X32"] + }, + { + "architecture": "SCMP_ARCH_AARCH64", + "subArchitectures": [] + } + ], + "syscalls": [ + { + "comment": "Docker default allowlist (Docker 27.x, x86_64 + aarch64)", + "names": [ + "_llseek", + "_newselect", + "accept", + "accept4", + "access", + "acct", + "adjtimex", + "alarm", + "arch_prctl", + "bind", + "bpf", + "brk", + "cachestat", + "capget", + "capset", + "chdir", + "chmod", + "chown", + "chown32", + "chroot", + "clock_adjtime", + "clock_adjtime64", + "clock_getres", + "clock_getres_time64", + "clock_gettime", + "clock_gettime64", + "clock_nanosleep", + "clock_nanosleep_time64", + "clock_settime", + "clock_settime64", + "close", + "close_range", + "connect", + "copy_file_range", + "creat", + "delete_module", + "dup", + "dup2", + "dup3", + "epoll_create", + "epoll_create1", + "epoll_ctl", + "epoll_ctl_old", + "epoll_pwait", + "epoll_pwait2", + "epoll_wait", + "epoll_wait_old", + "eventfd", + "eventfd2", + "execve", + "execveat", + "exit", + "exit_group", + "faccessat", + "faccessat2", + "fadvise64", + "fadvise64_64", + "fallocate", + "fanotify_init", + "fanotify_mark", + "fchdir", + "fchmod", + "fchmodat", + "fchmodat2", + "fchown", + "fchown32", + "fchownat", + "fcntl", + "fcntl64", + "fdatasync", + "fgetxattr", + "finit_module", + "flistxattr", + "flock", + "fork", + "fremovexattr", + "fsconfig", + "fsetxattr", + "fsmount", + "fsopen", + "fspick", + "fstat", + "fstat64", + "fstatat64", + "fstatfs", + "fstatfs64", + "fsync", + "ftruncate", + "ftruncate64", + "futex", + "futex_requeue", + "futex_time64", + "futex_wait", + "futex_waitv", + "futex_wake", + "futimesat", + "get_mempolicy", + "get_robust_list", + "get_thread_area", + "getcpu", + "getcwd", + "getdents", + "getdents64", + "getegid", + "getegid32", + "geteuid", + "geteuid32", + "getgid", + "getgid32", + "getgroups", + "getgroups32", + "getitimer", + "getpeername", + "getpgid", + "getpgrp", + "getpid", + "getppid", + "getpriority", + "getrandom", + "getresgid", + "getresgid32", + "getresuid", + "getresuid32", + "getrlimit", + "getrusage", + "getsid", + "getsockname", + "getsockopt", + "gettid", + "gettimeofday", + "getuid", + "getuid32", + "getxattr", + "init_module", + "inotify_add_watch", + "inotify_init", + "inotify_init1", + "inotify_rm_watch", + "io_cancel", + "io_destroy", + "io_getevents", + "io_pgetevents", + "io_pgetevents_time64", + "io_setup", + "io_submit", + "io_uring_enter", + "io_uring_register", + "io_uring_setup", + "ioctl", + "ioperm", + "iopl", + "ioprio_get", + "ioprio_set", + "ipc", + "kcmp", + "kill", + "landlock_add_rule", + "landlock_create_ruleset", + "landlock_restrict_self", + "lchown", + "lchown32", + "lgetxattr", + "link", + "linkat", + "listen", + "listxattr", + "llistxattr", + "lookup_dcookie", + "lremovexattr", + "lseek", + "lsetxattr", + "lstat", + "lstat64", + "madvise", + "map_shadow_stack", + "mbind", + "membarrier", + "memfd_create", + "memfd_secret", + "mincore", + "mkdir", + "mkdirat", + "mknod", + "mknodat", + "mlock", + "mlock2", + "mlockall", + "mmap", + "mmap2", + "modify_ldt", + "mount_setattr", + "move_mount", + "mprotect", + "mq_getsetattr", + "mq_notify", + "mq_open", + "mq_timedreceive", + "mq_timedreceive_time64", + "mq_timedsend", + "mq_timedsend_time64", + "mq_unlink", + "mremap", + "msgctl", + "msgget", + "msgrcv", + "msgsnd", + "msync", + "munlock", + "munlockall", + "munmap", + "name_to_handle_at", + "nanosleep", + "newfstatat", + "open", + "open_by_handle_at", + "open_tree", + "openat", + "openat2", + "pause", + "perf_event_open", + "pidfd_getfd", + "pidfd_open", + "pidfd_send_signal", + "pipe", + "pipe2", + "pkey_alloc", + "pkey_free", + "pkey_mprotect", + "poll", + "ppoll", + "ppoll_time64", + "prctl", + "pread64", + "preadv", + "preadv2", + "prlimit64", + "process_madvise", + "process_mrelease", + "process_vm_readv", + "process_vm_writev", + "pselect6", + "pselect6_time64", + "pwrite64", + "pwritev", + "pwritev2", + "quotactl", + "quotactl_fd", + "read", + "readahead", + "readlink", + "readlinkat", + "readv", + "reboot", + "recv", + "recvfrom", + "recvmmsg", + "recvmmsg_time64", + "recvmsg", + "remap_file_pages", + "removexattr", + "rename", + "renameat", + "renameat2", + "restart_syscall", + "rmdir", + "rseq", + "rt_sigaction", + "rt_sigpending", + "rt_sigprocmask", + "rt_sigqueueinfo", + "rt_sigreturn", + "rt_sigsuspend", + "rt_sigtimedwait", + "rt_sigtimedwait_time64", + "rt_tgsigqueueinfo", + "sched_get_priority_max", + "sched_get_priority_min", + "sched_getaffinity", + "sched_getattr", + "sched_getparam", + "sched_getscheduler", + "sched_rr_get_interval", + "sched_rr_get_interval_time64", + "sched_setaffinity", + "sched_setattr", + "sched_setparam", + "sched_setscheduler", + "sched_yield", + "seccomp", + "select", + "semctl", + "semget", + "semop", + "semtimedop", + "semtimedop_time64", + "send", + "sendfile", + "sendfile64", + "sendmmsg", + "sendmsg", + "sendto", + "set_mempolicy", + "set_mempolicy_home_node", + "set_robust_list", + "set_thread_area", + "set_tid_address", + "set_tls", + "setdomainname", + "setfsgid", + "setfsgid32", + "setfsuid", + "setfsuid32", + "setgid", + "setgid32", + "setgroups", + "setgroups32", + "setitimer", + "setpgid", + "setpriority", + "setregid", + "setregid32", + "setresgid", + "setresgid32", + "setresuid", + "setresuid32", + "setreuid", + "setreuid32", + "setrlimit", + "setsid", + "setsockopt", + "settimeofday", + "setuid", + "setuid32", + "setxattr", + "shmat", + "shmctl", + "shmdt", + "shmget", + "shutdown", + "sigaltstack", + "signalfd", + "signalfd4", + "sigprocmask", + "sigreturn", + "socket", + "socketcall", + "socketpair", + "splice", + "stat", + "stat64", + "statfs", + "statfs64", + "statx", + "stime", + "symlink", + "symlinkat", + "sync", + "sync_file_range", + "sync_file_range2", + "syncfs", + "sysinfo", + "syslog", + "tee", + "tgkill", + "time", + "timer_create", + "timer_delete", + "timer_getoverrun", + "timer_gettime", + "timer_gettime64", + "timer_settime", + "timer_settime64", + "timerfd_create", + "timerfd_gettime", + "timerfd_gettime64", + "timerfd_settime", + "timerfd_settime64", + "times", + "tkill", + "truncate", + "truncate64", + "ugetrlimit", + "umask", + "umount", + "uname", + "unlink", + "unlinkat", + "utime", + "utimensat", + "utimensat_time64", + "utimes", + "vfork", + "vhangup", + "vmsplice", + "wait4", + "waitid", + "waitpid", + "write", + "writev" + ], + "action": "SCMP_ACT_ALLOW" + }, + { + "comment": "gVisor + pasta: namespace creation and entry (clone/unshare with CLONE_NEW* flags, setns to join namespaces)", + "names": ["clone", "clone3", "unshare", "setns"], + "action": "SCMP_ACT_ALLOW" + }, + { + "comment": "pasta: set hostname inside namespace (cosmetic, avoids warning)", + "names": ["sethostname"], + "action": "SCMP_ACT_ALLOW" + }, + { + "comment": "gVisor: sandbox filesystem setup (tmpfs, proc, bind mounts)", + "names": ["mount", "umount2"], + "action": "SCMP_ACT_ALLOW" + }, + { + "comment": "gVisor: filesystem root isolation for sentry and gofer", + "names": ["pivot_root"], + "action": "SCMP_ACT_ALLOW" + }, + { + "comment": "gVisor systrap platform: workload executor thread initialization", + "names": ["ptrace"], + "action": "SCMP_ACT_ALLOW" + } + ] +} diff --git a/charts/retool/templates/_helpers.tpl b/charts/retool/templates/_helpers.tpl index fde306d..834cb79 100644 --- a/charts/retool/templates/_helpers.tpl +++ b/charts/retool/templates/_helpers.tpl @@ -379,6 +379,158 @@ Set agent eval worker service name {{ template "retool.fullname" . }}-agent-eval-worker {{- end -}} +{{/* +Set R2 agent worker service name +*/}} +{{- define "retool.r2AgentWorker.name" -}} +{{ template "retool.fullname" . }}-r2-agent-worker +{{- end -}} + +{{/* +Selector labels for R2 agent worker. Note changes here will require manual +deployment recreation and incur downtime, so should be avoided. +*/}} +{{- define "retool.r2AgentWorker.selectorLabels" -}} +retoolService: {{ include "retool.r2AgentWorker.name" . }} +{{- end }} + +{{/* +Extra (non-selector) labels for R2 agent worker. +*/}} +{{- define "retool.r2AgentWorker.labels" -}} +app.kubernetes.io/name: {{ include "retool.r2AgentWorker.name" . }} +app.kubernetes.io/instance: {{ .Release.Name }} +telemetry.retool.com/service-name: r2-agent-worker +{{- end }} + +{{/* +Set agent executor base name +*/}} +{{- define "retool.agentExecutor.name" -}} +{{ template "retool.fullname" . }}-agent-executor +{{- end -}} + +{{/* +Set agent executor controller name +*/}} +{{- define "retool.agentExecutor.controller.name" -}} +{{ template "retool.fullname" . }}-agent-executor-controller +{{- end -}} + +{{/* +Set agent executor proxy name +*/}} +{{- define "retool.agentExecutor.proxy.name" -}} +{{ template "retool.fullname" . }}-agent-executor-proxy +{{- end -}} + +{{/* +Secret name for agent executor. +Uses externalSecret.name if set, otherwise the auto-generated name. +*/}} +{{- define "retool.agentExecutor.secretName" -}} +{{- if .Values.agentExecutor.externalSecret.name -}} +{{ .Values.agentExecutor.externalSecret.name }} +{{- else -}} +{{ template "retool.agentExecutor.name" . }} +{{- end -}} +{{- end -}} + +{{/* +Selector labels for agent executor (executor pods / headless service). +*/}} +{{- define "retool.agentExecutor.selectorLabels" -}} +retoolService: {{ include "retool.agentExecutor.name" . }} +{{- end -}} + +{{/* +Extra labels for agent executor. +*/}} +{{- define "retool.agentExecutor.labels" -}} +app.kubernetes.io/name: {{ include "retool.agentExecutor.name" . }} +app.kubernetes.io/instance: {{ .Release.Name }} +telemetry.retool.com/service-name: agent-executor +{{- end -}} + +{{/* +Selector labels for agent executor controller. +*/}} +{{- define "retool.agentExecutor.controller.selectorLabels" -}} +retoolService: {{ include "retool.agentExecutor.controller.name" . }} +{{- end -}} + +{{/* +Extra labels for agent executor controller. +*/}} +{{- define "retool.agentExecutor.controller.labels" -}} +app.kubernetes.io/name: {{ include "retool.agentExecutor.controller.name" . }} +app.kubernetes.io/instance: {{ .Release.Name }} +app.kubernetes.io/component: controller +telemetry.retool.com/service-name: agent-executor-controller +{{- end -}} + +{{/* +Selector labels for agent executor proxy. +*/}} +{{- define "retool.agentExecutor.proxy.selectorLabels" -}} +retoolService: {{ include "retool.agentExecutor.proxy.name" . }} +{{- end -}} + +{{/* +Extra labels for agent executor proxy. +*/}} +{{- define "retool.agentExecutor.proxy.labels" -}} +app.kubernetes.io/name: {{ include "retool.agentExecutor.proxy.name" . }} +app.kubernetes.io/instance: {{ .Release.Name }} +app.kubernetes.io/component: proxy +telemetry.retool.com/service-name: agent-executor-proxy +{{- end -}} + +{{/* +Agent executor env vars for the Retool backend, workflow backend, and workers. +Outputs env entries that tell the backend how to reach the agent executor services. +Usage: {{- include "retool.agentExecutor.backendEnvVars" . | nindent 10 }} +*/}} +{{- define "retool.agentExecutor.backendEnvVars" -}} +{{- if .Values.agentExecutor.enabled }} +- name: AGENT_EXECUTOR_ENABLED + value: "true" +- name: AGENT_EXECUTOR_CONTROLLER_INGRESS_DOMAIN + value: {{ .Values.agentExecutor.controllerUrl | default (printf "http://%s:%s" (include "retool.agentExecutor.controller.name" .) (toString .Values.agentExecutor.controller.port)) | quote }} +- name: AGENT_EXECUTOR_PROXY_INGRESS_DOMAIN + value: {{ .Values.agentExecutor.proxyUrl | default (printf "http://%s:%s" (include "retool.agentExecutor.proxy.name" .) (toString .Values.agentExecutor.proxy.port)) | quote }} +{{- if .Values.agentExecutor.frontendWsProxyDomain }} +- name: AGENT_EXECUTOR_FRONTEND_WS_PROXY_DOMAIN + value: {{ .Values.agentExecutor.frontendWsProxyDomain | quote }} +{{- end }} +{{- if or .Values.agentExecutor.proxyDomain .Values.agentExecutor.frontendWsProxyDomain }} +- name: AGENT_EXECUTOR_PROXY_DOMAIN + value: {{ .Values.agentExecutor.proxyDomain | default .Values.agentExecutor.frontendWsProxyDomain | quote }} +{{- end }} +{{- if or .Values.agentExecutor.jwtPrivateKey .Values.agentExecutor.externalSecret.name }} +- name: AGENT_EXECUTOR_JWT_PRIVATE_KEY + valueFrom: + secretKeyRef: + name: {{ include "retool.agentExecutor.secretName" . }} + key: jwt-private-key +{{- end }} +{{- if or .Values.agentExecutor.jwtPublicKey .Values.agentExecutor.externalSecret.name }} +- name: AGENT_EXECUTOR_JWT_PUBLIC_KEY + valueFrom: + secretKeyRef: + name: {{ include "retool.agentExecutor.secretName" . }} + key: jwt-public-key +{{- end }} +{{- if or .Values.agentExecutor.encryptionKey .Values.agentExecutor.externalSecret.name }} +- name: AGENT_EXECUTOR_ENCRYPTION_KEY + valueFrom: + secretKeyRef: + name: {{ include "retool.agentExecutor.secretName" . }} + key: encryption-key +{{- end }} +{{- end }} +{{- end -}} + {{/* Set code executor image tag Usage: (template "retool.codeExecutor.image.tag" .) diff --git a/charts/retool/templates/_workers.tpl b/charts/retool/templates/_workers.tpl index 2202691..5bd08aa 100644 --- a/charts/retool/templates/_workers.tpl +++ b/charts/retool/templates/_workers.tpl @@ -3,6 +3,8 @@ type: agent - parent: agents type: agentEval +- parent: agents + type: r2Agent - parent: workflows type: workflow {{- end -}} @@ -27,6 +29,8 @@ {{- $workerValues := $parentValues.worker -}} {{- if eq $workerType "agentEval" -}} {{- $workerValues = $parentValues.evalWorker -}} +{{- else if eq $workerType "r2Agent" -}} + {{- $workerValues = $parentValues.r2AgentWorker -}} {{- end -}} {{- $workerPoolMaxSize := 100 -}} @@ -36,9 +40,20 @@ {{- end }} {{- end -}} -{{- $healthcheckPort := ternary 3012 3005 (eq $workerType "agentEval") -}} -{{- $serviceType := ternary "AGENT_EVAL_TEMPORAL_WORKER" "WORKFLOW_TEMPORAL_WORKER" (eq $workerType "agentEval") -}} -{{- $taskqueue := ternary "agent-eval" (ternary "agent" "" (eq $workerType "agent")) (eq $workerType "agentEval") -}} +{{- $healthcheckPort := 3005 -}} +{{- $serviceType := "WORKFLOW_TEMPORAL_WORKER" -}} +{{- $taskqueue := "" -}} +{{- if eq $workerType "agentEval" -}} + {{- $healthcheckPort = 3012 -}} + {{- $serviceType = "AGENT_EVAL_TEMPORAL_WORKER" -}} + {{- $taskqueue = "agent-eval" -}} +{{- else if eq $workerType "r2Agent" -}} + {{- $healthcheckPort = 3016 -}} + {{- $serviceType = "R2_AGENT_TEMPORAL_WORKER" -}} + {{- $taskqueue = "r2-agent" -}} +{{- else if eq $workerType "agent" -}} + {{- $taskqueue = "agent" -}} +{{- end -}} {{/* yaml starts here */}} apiVersion: apps/v1 @@ -100,7 +115,7 @@ spec: {{- end }} {{- end }} containers: - - name: {{ if eq $workerType "agentEval" }}agent-eval-worker{{ else }}{{ $workerType }}-worker{{ end }} + - name: {{ if eq $workerType "agentEval" }}agent-eval-worker{{ else if eq $workerType "r2Agent" }}r2-agent-worker{{ else }}{{ $workerType }}-worker{{ end }} image: "{{ $.Values.image.repository }}:{{ required "Please set a value for .Values.image.tag" $.Values.image.tag }}" imagePullPolicy: {{ $.Values.image.pullPolicy }} args: @@ -200,6 +215,7 @@ spec: value: {{ template "retool.postgresql.ssl_enabled" $ }} - name: CODE_EXECUTOR_INGRESS_DOMAIN value: http://{{ template "retool.codeExecutor.name" $ }} + {{- include "retool.agentExecutor.backendEnvVars" $ | nindent 10 }} {{- include "retool.telemetry.includeEnvVars" $ | nindent 10 }} diff --git a/charts/retool/templates/agent_executor_device_plugin.yaml b/charts/retool/templates/agent_executor_device_plugin.yaml new file mode 100644 index 0000000..0ebdc27 --- /dev/null +++ b/charts/retool/templates/agent_executor_device_plugin.yaml @@ -0,0 +1,93 @@ +{{- if and .Values.agentExecutor.enabled .Values.agentExecutor.sandboxNetwork.devicePlugin }} +{{- $ae := .Values.agentExecutor -}} +{{- $nodeSelector := $ae.nodeSelector | default .Values.nodeSelector -}} +{{- $tolerations := $ae.tolerations | default .Values.tolerations -}} +apiVersion: v1 +kind: ConfigMap +metadata: + name: {{ include "retool.agentExecutor.name" . }}-device-plugin + labels: + {{- include "retool.agentExecutor.labels" . | nindent 4 }} + {{- include "retool.labels" . | nindent 4 }} + app.kubernetes.io/component: device-plugin +data: + conf.yaml: | + - devicematch: ^net/tun$ + nummaxdevices: {{ $ae.devicePlugin.maxDevices | default 130 }} +--- +apiVersion: apps/v1 +kind: DaemonSet +metadata: + name: {{ include "retool.agentExecutor.name" . }}-device-plugin + labels: + {{- include "retool.agentExecutor.labels" . | nindent 4 }} + {{- include "retool.labels" . | nindent 4 }} + app.kubernetes.io/component: device-plugin +spec: + selector: + matchLabels: + retoolService: {{ include "retool.agentExecutor.name" . }}-device-plugin + template: + metadata: + labels: + retoolService: {{ include "retool.agentExecutor.name" . }}-device-plugin + app.kubernetes.io/name: {{ include "retool.agentExecutor.name" . }}-device-plugin + app.kubernetes.io/instance: {{ .Release.Name }} + {{- include "retool.labels" . | nindent 8 }} +{{- if .Values.podLabels }} +{{ toYaml .Values.podLabels | indent 8 }} +{{- end }} + spec: + automountServiceAccountToken: false + serviceAccountName: {{ template "retool.serviceAccountName" . }} + priorityClassName: system-node-critical + hostPID: true +{{- if $nodeSelector }} + nodeSelector: +{{ toYaml $nodeSelector | indent 8 }} +{{- end }} + tolerations: +{{ toYaml $tolerations | indent 8 }} + containers: + - name: smarter-device-manager + image: "{{ $ae.devicePlugin.image.repository }}:{{ $ae.devicePlugin.image.tag }}" + imagePullPolicy: IfNotPresent + terminationMessagePath: /tmp/termination-log + terminationMessagePolicy: FallbackToLogsOnError + securityContext: + allowPrivilegeEscalation: false + readOnlyRootFilesystem: true + capabilities: + drop: ["ALL"] + volumeMounts: + - name: device-plugin + mountPath: /var/lib/kubelet/device-plugins + - name: dev + mountPath: /dev + readOnly: true + - name: sys + mountPath: /sys + readOnly: true + - name: config + mountPath: /root/config + resources: + requests: + cpu: 10m + memory: 16Mi + limits: + cpu: 100m + memory: 32Mi + volumes: + - name: device-plugin + hostPath: + path: /var/lib/kubelet/device-plugins + - name: dev + hostPath: + path: /dev + - name: sys + hostPath: + path: /sys + - name: config + configMap: + name: {{ include "retool.agentExecutor.name" . }}-device-plugin +{{- end }} diff --git a/charts/retool/templates/agent_executor_seccomp.yaml b/charts/retool/templates/agent_executor_seccomp.yaml new file mode 100644 index 0000000..406cfdd --- /dev/null +++ b/charts/retool/templates/agent_executor_seccomp.yaml @@ -0,0 +1,92 @@ +{{- if .Values.agentExecutor.enabled }} +{{- $ae := .Values.agentExecutor -}} +{{- $nodeSelector := $ae.nodeSelector | default .Values.nodeSelector -}} +{{- $tolerations := $ae.tolerations | default .Values.tolerations -}} +apiVersion: v1 +kind: ConfigMap +metadata: + name: {{ include "retool.agentExecutor.name" . }}-seccomp + labels: + {{- include "retool.agentExecutor.labels" . | nindent 4 }} + {{- include "retool.labels" . | nindent 4 }} +data: + gvisor-seccomp.json: | + {{- .Files.Get "files/gvisor-seccomp.json" | nindent 4 }} +--- +apiVersion: apps/v1 +kind: DaemonSet +metadata: + name: {{ include "retool.agentExecutor.name" . }}-node-installer + labels: + {{- include "retool.agentExecutor.labels" . | nindent 4 }} + {{- include "retool.labels" . | nindent 4 }} + app.kubernetes.io/component: node-installer +spec: + selector: + matchLabels: + retoolService: {{ include "retool.agentExecutor.name" . }}-node-installer + template: + metadata: + labels: + retoolService: {{ include "retool.agentExecutor.name" . }}-node-installer + app.kubernetes.io/name: {{ include "retool.agentExecutor.name" . }}-node-installer + app.kubernetes.io/instance: {{ .Release.Name }} + {{- include "retool.labels" . | nindent 8 }} +{{- if .Values.podLabels }} +{{ toYaml .Values.podLabels | indent 8 }} +{{- end }} + spec: + automountServiceAccountToken: false + serviceAccountName: {{ template "retool.serviceAccountName" . }} +{{- if $nodeSelector }} + nodeSelector: +{{ toYaml $nodeSelector | indent 8 }} +{{- end }} + tolerations: +{{ toYaml $tolerations | indent 8 }} + initContainers: + - name: install + image: busybox:1.37.0 + securityContext: + allowPrivilegeEscalation: false + readOnlyRootFilesystem: true + capabilities: + drop: ["ALL"] + command: + - /bin/sh + - -c + - | + DEST="/host-seccomp/{{ $ae.seccompProfile }}" + mkdir -p "$(dirname "$DEST")" + cp /seccomp-profile/gvisor-seccomp.json "$DEST" + echo "seccomp profile installed at $DEST" + volumeMounts: + - name: seccomp-profile + mountPath: /seccomp-profile + - name: host-seccomp + mountPath: /host-seccomp + containers: + - name: pause + image: busybox:1.37.0 + command: ["sleep", "infinity"] + securityContext: + allowPrivilegeEscalation: false + readOnlyRootFilesystem: true + capabilities: + drop: ["ALL"] + resources: + requests: + cpu: 1m + memory: 4Mi + limits: + cpu: 10m + memory: 16Mi + volumes: + - name: seccomp-profile + configMap: + name: {{ include "retool.agentExecutor.name" . }}-seccomp + - name: host-seccomp + hostPath: + path: /var/lib/kubelet/seccomp + type: DirectoryOrCreate +{{- end }} diff --git a/charts/retool/templates/deployment_agent_executor.yaml b/charts/retool/templates/deployment_agent_executor.yaml new file mode 100644 index 0000000..9af1a77 --- /dev/null +++ b/charts/retool/templates/deployment_agent_executor.yaml @@ -0,0 +1,698 @@ +{{- if .Values.agentExecutor.enabled }} +{{- $ae := .Values.agentExecutor -}} +{{- $secretName := include "retool.agentExecutor.secretName" . -}} +{{- $nodeSelector := $ae.nodeSelector | default .Values.nodeSelector -}} +{{- $tolerations := $ae.tolerations | default .Values.tolerations -}} +{{- /* +======================================================================= + Secret (skipped when externalSecret.name is set) +======================================================================= +*/}} +{{- if not $ae.externalSecret.name }} +apiVersion: v1 +kind: Secret +metadata: + name: {{ template "retool.agentExecutor.name" . }} + labels: + {{- include "retool.agentExecutor.labels" . | nindent 4 }} + {{- include "retool.labels" . | nindent 4 }} +type: Opaque +data: + jwt-public-key: {{ $ae.jwtPublicKey | default "" | b64enc | quote }} + jwt-private-key: {{ $ae.jwtPrivateKey | default "" | b64enc | quote }} + encryption-key: {{ $ae.encryptionKey | default "" | b64enc | quote }} + api-secret: {{ $ae.apiSecret | default "" | b64enc | quote }} + redis-password: {{ $ae.redis.password | default "" | b64enc | quote }} +--- +{{- end }} +{{- /* +======================================================================= + RBAC for the controller (needs to manage Jobs, Pods, ConfigMaps) +======================================================================= +*/}} +apiVersion: v1 +kind: ServiceAccount +metadata: + name: {{ include "retool.agentExecutor.controller.name" . }} + labels: + {{- include "retool.agentExecutor.controller.labels" . | nindent 4 }} + {{- include "retool.labels" . | nindent 4 }} +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: Role +metadata: + name: {{ include "retool.agentExecutor.controller.name" . }} + labels: + {{- include "retool.agentExecutor.controller.labels" . | nindent 4 }} + {{- include "retool.labels" . | nindent 4 }} +rules: + - apiGroups: ["batch"] + resources: ["jobs"] + verbs: ["get", "list", "watch", "create", "delete"] + - apiGroups: [""] + resources: ["pods"] + verbs: ["get", "list", "watch"] + - apiGroups: [""] + resources: ["configmaps"] + verbs: ["get"] +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + name: {{ include "retool.agentExecutor.controller.name" . }} + labels: + {{- include "retool.agentExecutor.controller.labels" . | nindent 4 }} + {{- include "retool.labels" . | nindent 4 }} +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: Role + name: {{ include "retool.agentExecutor.controller.name" . }} +subjects: + - kind: ServiceAccount + name: {{ include "retool.agentExecutor.controller.name" . }} + namespace: {{ .Release.Namespace }} +--- +{{- /* +======================================================================= + Job Template ConfigMap — defines the K8s Job spec the controller uses + to create sandbox executor pods. +======================================================================= +*/}} +apiVersion: v1 +kind: ConfigMap +metadata: + name: {{ include "retool.agentExecutor.name" . }}-job-template + labels: + {{- include "retool.agentExecutor.controller.labels" . | nindent 4 }} + {{- include "retool.labels" . | nindent 4 }} +data: + job-template.json: | + { + "apiVersion": "batch/v1", + "kind": "Job", + "metadata": { + "labels": { + "retoolService": "{{ include "retool.agentExecutor.name" . }}" + } + }, + "spec": { + "backoffLimit": 0, + "ttlSecondsAfterFinished": {{ $ae.controller.scaling.jobRetentionSeconds }}, + "template": { + "metadata": { + "labels": { + "retoolService": "{{ include "retool.agentExecutor.name" . }}" + } + }, + "spec": { + "restartPolicy": "Never", + "subdomain": "{{ include "retool.agentExecutor.name" . }}-pods", + "automountServiceAccountToken": false, + {{- if $nodeSelector }} + "nodeSelector": {{ toJson $nodeSelector }}, + {{- end }} + {{- if $tolerations }} + "tolerations": {{ toJson $tolerations }}, + {{- end }} + "initContainers": [ + { + "name": "rootfs-etc-copy", + "image": "{{ $ae.image.repository }}:__IMAGE_TAG__", + "command": ["/bin/sh", "-c", "cp -r /opt/sandbox-env/rootfs/etc/. /mnt/etc/"], + "securityContext": { + "runAsUser": 0, + "allowPrivilegeEscalation": false, + "readOnlyRootFilesystem": true, + "capabilities": {"drop": ["ALL"], "add": ["DAC_READ_SEARCH"]} + }, + "volumeMounts": [ + {"name": "rootfs-etc", "mountPath": "/mnt/etc"} + ], + "resources": { + "requests": {"cpu": "10m", "memory": "16Mi"}, + "limits": {"cpu": "100m", "memory": "32Mi"} + } + } + ], + "containers": [ + { + "name": "agent-executor", + "image": "{{ $ae.image.repository }}:__IMAGE_TAG__", + "ports": [{"containerPort": {{ $ae.executor.port }}, "protocol": "TCP"}], + "securityContext": { + "runAsUser": 1001, + "runAsGroup": 1001, + "allowPrivilegeEscalation": false, + "readOnlyRootFilesystem": true, + "capabilities": {"drop": ["ALL"]}, + "seccompProfile": {"type": "Localhost", "localhostProfile": "{{ $ae.seccompProfile }}"} + }, + "env": [ + {"name": "NODE_ENV", "value": "production"}, + {"name": "EXECUTOR_PORT", "value": "{{ $ae.executor.port }}"}, + {"name": "POD_NAME", "valueFrom": {"fieldRef": {"fieldPath": "metadata.name"}}}, + {"name": "POD_UID", "valueFrom": {"fieldRef": {"fieldPath": "metadata.uid"}}}, + {"name": "POD_IP", "valueFrom": {"fieldRef": {"fieldPath": "status.podIP"}}}, + {"name": "SANDBOX_NETWORK_ENABLED", "value": "{{ $ae.sandboxNetwork.enabled }}"}, + {"name": "SANDBOX_IDLE_TIMEOUT_MS", "value": "{{ $ae.executor.sandboxIdleTimeoutMs }}"} + {{- if or $ae.jwtPublicKey $ae.externalSecret.name }} + ,{"name": "AGENT_EXECUTOR_JWT_PUBLIC_KEY", "valueFrom": {"secretKeyRef": {"name": "{{ $secretName }}", "key": "jwt-public-key"}}} + {{- end }} + {{- if $ae.proxy.backendDomainSuffixes }} + ,{"name": "BACKEND_DOMAIN_SUFFIXES", "value": "{{ $ae.proxy.backendDomainSuffixes }}"} + {{- end }} + {{- if $ae.sandboxNetwork.enabled }} + ,{"name": "SANDBOX_HTTP_PROXY", "value": "{{ $ae.sandboxNetwork.httpProxy | default (printf "http://%s:%s" (include "retool.agentExecutor.proxy.name" .) (toString $ae.proxy.port)) }}"} + {{- end }} + {{- range $ae.executor.extraEnv }} + ,{{ toJson . }} + {{- end }} + ], + "volumeMounts": [ + {{- if $ae.sandboxNetwork.enabled }} + {"name": "dev-tun", "mountPath": "/dev/net/tun"}, + {{- end }} + {"name": "run", "mountPath": "/run"}, + {"name": "tmp", "mountPath": "/tmp"}, + {"name": "rootfs-appjob", "mountPath": "/opt/sandbox-env/rootfs/app/job"}, + {"name": "rootfs-etc", "mountPath": "/opt/sandbox-env/rootfs/etc"} + ], + {{- $res := deepCopy $ae.executor.resources }} + {{- if $ae.sandboxNetwork.devicePlugin }} + {{- $_ := set $res.limits "smarter-devices/net_tun" 1 }} + {{- end }} + "resources": {{ toJson $res }} + } + ], + "volumes": [ + {{- if $ae.sandboxNetwork.enabled }} + {"name": "dev-tun", "hostPath": {"path": "/dev/net/tun", "type": "CharDevice"}}, + {{- end }} + {"name": "run", "emptyDir": {"medium": "Memory", "sizeLimit": "64Mi"}}, + {"name": "tmp", "emptyDir": {"sizeLimit": "{{ $ae.executor.tmpDirSizeLimit | default "20Gi" }}"}}, + {"name": "rootfs-appjob", "emptyDir": {"medium": "Memory", "sizeLimit": "1Mi"}}, + {"name": "rootfs-etc", "emptyDir": {"medium": "Memory", "sizeLimit": "4Mi"}} + ] + } + } + } + } +--- +{{- /* +======================================================================= + Controller Deployment +======================================================================= +*/}} +apiVersion: apps/v1 +kind: Deployment +metadata: + name: {{ include "retool.agentExecutor.controller.name" . }} + labels: + {{- include "retool.agentExecutor.controller.selectorLabels" . | nindent 4 }} + {{- include "retool.agentExecutor.controller.labels" . | nindent 4 }} + {{- include "retool.labels" . | nindent 4 }} +{{- if .Values.deployment.labels }} +{{ toYaml .Values.deployment.labels | indent 4 }} +{{- end }} +{{- if .Values.deployment.annotations }} + annotations: +{{ toYaml .Values.deployment.annotations | indent 4 }} +{{- end }} +spec: + replicas: {{ $ae.controller.replicaCount }} + selector: + matchLabels: + {{- include "retool.agentExecutor.controller.selectorLabels" . | nindent 6 }} + revisionHistoryLimit: {{ .Values.revisionHistoryLimit }} + template: + metadata: + annotations: +{{- if .Values.podAnnotations }} +{{ toYaml .Values.podAnnotations | indent 8 }} +{{- end }} +{{- if $ae.annotations }} +{{ toYaml $ae.annotations | indent 8 }} +{{- end }} + labels: + {{- include "retool.agentExecutor.controller.selectorLabels" . | nindent 8 }} + {{- include "retool.agentExecutor.controller.labels" . | nindent 8 }} + {{- include "retool.labels" . | nindent 8 }} +{{- if .Values.podLabels }} +{{ toYaml .Values.podLabels | indent 8 }} +{{- end }} +{{- if $ae.labels }} +{{ toYaml $ae.labels | indent 8 }} +{{- end }} + spec: + serviceAccountName: {{ include "retool.agentExecutor.controller.name" . }} + automountServiceAccountToken: true + {{- if .Values.priorityClassName }} + priorityClassName: "{{ .Values.priorityClassName }}" + {{- end }} + containers: + - name: controller + image: "{{ $ae.image.repository }}:{{ $ae.image.tag }}" + imagePullPolicy: {{ $ae.image.pullPolicy }} + ports: + - name: http + containerPort: {{ $ae.controller.port }} + protocol: TCP + securityContext: + runAsUser: 1001 + runAsGroup: 1001 + runAsNonRoot: true + allowPrivilegeEscalation: false + readOnlyRootFilesystem: true + capabilities: + drop: ["ALL"] + env: + - name: NODE_ENV + value: "production" + - name: AGENT_EXECUTOR_ROLE + value: "controller" + - name: CONTROLLER_PORT + value: {{ $ae.controller.port | quote }} + - name: REDIS_KEY_PREFIX + value: {{ $ae.redis.keyPrefix | quote }} + - name: REDIS_HOST + value: {{ $ae.redis.host | quote }} + - name: REDIS_PORT + value: {{ $ae.redis.port | quote }} + {{- if or $ae.redis.password $ae.externalSecret.name }} + - name: REDIS_PASSWORD + valueFrom: + secretKeyRef: + name: {{ $secretName }} + key: redis-password + {{- end }} + {{- if $ae.redis.tls }} + - name: REDIS_TLS + value: "true" + {{- end }} + - name: K8S_NAMESPACE + valueFrom: + fieldRef: + fieldPath: metadata.namespace + - name: POD_NAME + valueFrom: + fieldRef: + fieldPath: metadata.name + - name: JOB_NAME_PREFIX + value: {{ include "retool.agentExecutor.name" . }}-job + - name: JOB_APP_LABEL + value: {{ include "retool.agentExecutor.name" . }} + - name: EXECUTOR_DEPLOYMENT_NAME + value: {{ include "retool.agentExecutor.name" . }} + - name: EXECUTOR_SERVICE_NAME + value: {{ include "retool.agentExecutor.name" . }}-pods + - name: SLOTS_PER_POD + value: {{ $ae.controller.scaling.slotsPerPod | quote }} + - name: EXECUTOR_MIN_REPLICAS + value: {{ $ae.controller.scaling.minReplicas | quote }} + - name: EXECUTOR_MAX_REPLICAS + value: {{ $ae.controller.scaling.maxReplicas | quote }} + - name: SCALE_UP_THRESHOLD + value: {{ $ae.controller.scaling.scaleUpThreshold | quote }} + - name: SCALE_DOWN_THRESHOLD + value: {{ $ae.controller.scaling.scaleDownThreshold | quote }} + - name: SCALE_DOWN_GRACE_PERIOD_MS + value: {{ $ae.controller.scaling.scaleDownGracePeriodMs | quote }} + - name: PREWARM_POOL_SIZE + value: {{ $ae.controller.scaling.prewarmPoolSize | quote }} + - name: MAX_TOTAL_JOBS + value: {{ $ae.controller.scaling.maxTotalJobs | quote }} + - name: MAX_CONCURRENT_CREATES + value: {{ $ae.controller.scaling.maxConcurrentCreates | quote }} + - name: JOB_RETENTION_SECONDS + value: {{ $ae.controller.scaling.jobRetentionSeconds | quote }} + - name: ASSIGNED_SANDBOX_TTL_SECONDS + value: {{ $ae.controller.scaling.assignedSandboxTtlSeconds | quote }} + - name: RECONCILE_INTERVAL_MS + value: {{ $ae.controller.scaling.reconcileIntervalMs | quote }} + - name: LEADER_TTL_MS + value: {{ $ae.controller.scaling.leaderTtlMs | quote }} + - name: LEADER_RENEW_MS + value: {{ $ae.controller.scaling.leaderRenewMs | quote }} + - name: DEPLOYED_IMAGE_TAG + value: {{ $ae.image.tag | quote }} + - name: JOB_TEMPLATE_CONFIGMAP + value: {{ include "retool.agentExecutor.name" . }}-job-template + {{- if or $ae.jwtPublicKey $ae.externalSecret.name }} + - name: AGENT_EXECUTOR_JWT_PUBLIC_KEY + valueFrom: + secretKeyRef: + name: {{ $secretName }} + key: jwt-public-key + {{- end }} + livenessProbe: + httpGet: + path: /livez + port: http + initialDelaySeconds: 3 + periodSeconds: 10 + timeoutSeconds: 3 + readinessProbe: + httpGet: + path: /health + port: http + initialDelaySeconds: 1 + periodSeconds: 2 + timeoutSeconds: 3 + resources: + {{- toYaml $ae.controller.resources | nindent 12 }} +{{- if .Values.image.pullSecrets }} + imagePullSecrets: +{{ toYaml .Values.image.pullSecrets | indent 8 }} +{{- end }} +{{- if $ae.affinity }} + affinity: +{{ toYaml $ae.affinity | indent 8 }} +{{- end }} +{{- if $nodeSelector }} + nodeSelector: +{{ toYaml $nodeSelector | indent 8 }} +{{- end }} + tolerations: +{{ toYaml $tolerations | indent 8 }} +--- +{{- /* +======================================================================= + Controller Service (ClusterIP) +======================================================================= +*/}} +apiVersion: v1 +kind: Service +metadata: + name: {{ include "retool.agentExecutor.controller.name" . }} + labels: + {{- include "retool.agentExecutor.controller.labels" . | nindent 4 }} + {{- include "retool.labels" . | nindent 4 }} +spec: + type: ClusterIP + ports: + - port: {{ $ae.controller.port }} + targetPort: http + protocol: TCP + name: http + selector: + {{- include "retool.agentExecutor.controller.selectorLabels" . | nindent 4 }} +--- +{{- /* +======================================================================= + Proxy Deployment +======================================================================= +*/}} +apiVersion: apps/v1 +kind: Deployment +metadata: + name: {{ include "retool.agentExecutor.proxy.name" . }} + labels: + {{- include "retool.agentExecutor.proxy.selectorLabels" . | nindent 4 }} + {{- include "retool.agentExecutor.proxy.labels" . | nindent 4 }} + {{- include "retool.labels" . | nindent 4 }} +{{- if .Values.deployment.labels }} +{{ toYaml .Values.deployment.labels | indent 4 }} +{{- end }} +{{- if .Values.deployment.annotations }} + annotations: +{{ toYaml .Values.deployment.annotations | indent 4 }} +{{- end }} +spec: + replicas: {{ $ae.proxy.replicaCount }} + selector: + matchLabels: + {{- include "retool.agentExecutor.proxy.selectorLabels" . | nindent 6 }} + revisionHistoryLimit: {{ .Values.revisionHistoryLimit }} + template: + metadata: + annotations: +{{- if .Values.podAnnotations }} +{{ toYaml .Values.podAnnotations | indent 8 }} +{{- end }} +{{- if $ae.annotations }} +{{ toYaml $ae.annotations | indent 8 }} +{{- end }} + labels: + {{- include "retool.agentExecutor.proxy.selectorLabels" . | nindent 8 }} + {{- include "retool.agentExecutor.proxy.labels" . | nindent 8 }} + {{- include "retool.labels" . | nindent 8 }} +{{- if .Values.podLabels }} +{{ toYaml .Values.podLabels | indent 8 }} +{{- end }} +{{- if $ae.labels }} +{{ toYaml $ae.labels | indent 8 }} +{{- end }} + spec: + automountServiceAccountToken: false + {{- if .Values.priorityClassName }} + priorityClassName: "{{ .Values.priorityClassName }}" + {{- end }} + containers: + - name: proxy + image: "{{ $ae.image.repository }}:{{ $ae.image.tag }}" + imagePullPolicy: {{ $ae.image.pullPolicy }} + ports: + - name: http + containerPort: {{ $ae.proxy.port }} + protocol: TCP + securityContext: + runAsUser: 1001 + runAsGroup: 1001 + runAsNonRoot: true + allowPrivilegeEscalation: false + readOnlyRootFilesystem: true + capabilities: + drop: ["ALL"] + env: + - name: NODE_ENV + value: "production" + - name: AGENT_EXECUTOR_ROLE + value: "proxy" + - name: PROXY_PORT + value: {{ $ae.proxy.port | quote }} + - name: REDIS_HOST + value: {{ $ae.redis.host | quote }} + - name: REDIS_PORT + value: {{ $ae.redis.port | quote }} + {{- if or $ae.redis.password $ae.externalSecret.name }} + - name: REDIS_PASSWORD + valueFrom: + secretKeyRef: + name: {{ $secretName }} + key: redis-password + {{- end }} + {{- if $ae.redis.tls }} + - name: REDIS_TLS + value: "true" + {{- end }} + {{- if $ae.proxy.allowedDomains }} + - name: ALLOWED_DOMAINS + value: {{ $ae.proxy.allowedDomains | quote }} + {{- end }} + - name: BACKEND_URL + value: {{ $ae.proxy.backendUrl | default (printf "http://%s:%s" (include "retool.fullname" .) (toString .Values.service.internalPort)) | quote }} + {{- if $ae.proxy.backendDomainSuffixes }} + - name: BACKEND_DOMAIN_SUFFIXES + value: {{ $ae.proxy.backendDomainSuffixes | quote }} + {{- end }} + {{- if or $ae.encryptionKey $ae.externalSecret.name }} + - name: AGENT_EXECUTOR_ENCRYPTION_KEY + valueFrom: + secretKeyRef: + name: {{ $secretName }} + key: encryption-key + {{- end }} + {{- if or $ae.jwtPublicKey $ae.externalSecret.name }} + - name: AGENT_EXECUTOR_JWT_PUBLIC_KEY + valueFrom: + secretKeyRef: + name: {{ $secretName }} + key: jwt-public-key + {{- end }} + - name: EXECUTOR_PORT + value: {{ $ae.executor.port | quote }} + - name: EXECUTOR_SERVICE_NAME + value: {{ include "retool.agentExecutor.name" . }}-pods + - name: K8S_NAMESPACE + value: {{ .Release.Namespace | quote }} + - name: REDIS_KEY_PREFIX + value: {{ $ae.redis.keyPrefix | quote }} + {{- if $ae.proxy.sandboxProxyTimeoutMs }} + - name: SANDBOX_PROXY_TIMEOUT_MS + value: {{ $ae.proxy.sandboxProxyTimeoutMs | quote }} + {{- end }} + livenessProbe: + httpGet: + path: /livez + port: http + initialDelaySeconds: 3 + periodSeconds: 10 + timeoutSeconds: 3 + readinessProbe: + httpGet: + path: /health + port: http + initialDelaySeconds: 1 + periodSeconds: 2 + timeoutSeconds: 3 + resources: + {{- toYaml $ae.proxy.resources | nindent 12 }} +{{- if .Values.image.pullSecrets }} + imagePullSecrets: +{{ toYaml .Values.image.pullSecrets | indent 8 }} +{{- end }} +{{- if $ae.affinity }} + affinity: +{{ toYaml $ae.affinity | indent 8 }} +{{- end }} +{{- if $nodeSelector }} + nodeSelector: +{{ toYaml $nodeSelector | indent 8 }} +{{- end }} + tolerations: +{{ toYaml $tolerations | indent 8 }} +--- +{{- /* +======================================================================= + Proxy Service +======================================================================= +*/}} +apiVersion: v1 +kind: Service +metadata: + name: {{ include "retool.agentExecutor.proxy.name" . }} + labels: + {{- include "retool.agentExecutor.proxy.labels" . | nindent 4 }} + {{- include "retool.labels" . | nindent 4 }} +{{- with ($ae.proxy.service).annotations }} + annotations: + {{- toYaml . | nindent 4 }} +{{- end }} +spec: + type: {{ ($ae.proxy.service).type | default "ClusterIP" }} + ports: + - port: {{ $ae.proxy.port }} + targetPort: http + protocol: TCP + name: http + selector: + {{- include "retool.agentExecutor.proxy.selectorLabels" . | nindent 4 }} +--- +{{- /* +======================================================================= + Proxy Ingress (optional — exposes proxy to frontend for WebSocket) +======================================================================= +*/}} +{{- if ($ae.proxy.ingress).enabled }} +{{- if semverCompare ">=1.19-0" .Capabilities.KubeVersion.Version }} +apiVersion: networking.k8s.io/v1 +{{- else }} +apiVersion: networking.k8s.io/v1beta1 +{{- end }} +kind: Ingress +metadata: + name: {{ include "retool.agentExecutor.proxy.name" . }} + labels: + {{- include "retool.agentExecutor.proxy.labels" . | nindent 4 }} + {{- include "retool.labels" . | nindent 4 }} +{{- with $ae.proxy.ingress.annotations }} + annotations: + {{- toYaml . | nindent 4 }} +{{- end }} +spec: + {{- if and $ae.proxy.ingress.ingressClassName (semverCompare ">=1.18-0" .Capabilities.KubeVersion.Version) }} + ingressClassName: {{ $ae.proxy.ingress.ingressClassName }} + {{- end }} + rules: + - host: {{ $ae.proxy.ingress.host | quote }} + http: + paths: + - path: / + {{- if semverCompare ">=1.18-0" .Capabilities.KubeVersion.Version }} + pathType: Prefix + {{- end }} + backend: + {{- if semverCompare ">=1.19-0" .Capabilities.KubeVersion.Version }} + service: + name: {{ include "retool.agentExecutor.proxy.name" . }} + port: + number: {{ $ae.proxy.port }} + {{- else }} + serviceName: {{ include "retool.agentExecutor.proxy.name" . }} + servicePort: {{ $ae.proxy.port }} + {{- end }} +{{- with $ae.proxy.ingress.tls }} + tls: + {{- toYaml . | nindent 4 }} +{{- end }} +--- +{{- end }} +{{- /* +======================================================================= +{{- /* +======================================================================= + Headless Service for direct pod addressing (sandbox routing). + Executor Job pods use subdomain to register DNS: + ...svc.cluster.local: +======================================================================= +*/}} +apiVersion: v1 +kind: Service +metadata: + name: {{ include "retool.agentExecutor.name" . }}-pods + labels: + {{- include "retool.agentExecutor.labels" . | nindent 4 }} + {{- include "retool.labels" . | nindent 4 }} +spec: + clusterIP: None + ports: + - port: {{ $ae.executor.port }} + targetPort: {{ $ae.executor.port }} + protocol: TCP + name: http + selector: + {{- include "retool.agentExecutor.selectorLabels" . | nindent 4 }} +--- +{{- /* +======================================================================= + PodDisruptionBudget for controller (when replicas > 1) +======================================================================= +*/}} +{{- if gt (int $ae.controller.replicaCount) 1 }} +{{- if semverCompare ">=1.21-0" .Capabilities.KubeVersion.Version -}} +apiVersion: policy/v1 +{{- else -}} +apiVersion: policy/v1beta1 +{{- end }} +kind: PodDisruptionBudget +metadata: + name: {{ include "retool.agentExecutor.controller.name" . }} + labels: + {{- include "retool.agentExecutor.controller.labels" . | nindent 4 }} + {{- include "retool.labels" . | nindent 4 }} +spec: + maxUnavailable: 1 + selector: + matchLabels: + {{- include "retool.agentExecutor.controller.selectorLabels" . | nindent 6 }} +--- +{{- end }} +{{- if .Values.podDisruptionBudget }} +{{- if semverCompare ">=1.21-0" .Capabilities.KubeVersion.Version -}} +apiVersion: policy/v1 +{{- else -}} +apiVersion: policy/v1beta1 +{{- end }} +kind: PodDisruptionBudget +metadata: + name: {{ include "retool.agentExecutor.proxy.name" . }} + labels: + {{- include "retool.agentExecutor.proxy.labels" . | nindent 4 }} + {{- include "retool.labels" . | nindent 4 }} +spec: + {{- toYaml .Values.podDisruptionBudget | nindent 2 }} + selector: + matchLabels: + {{- include "retool.agentExecutor.proxy.selectorLabels" . | nindent 6 }} +{{- end }} +{{- end }} diff --git a/charts/retool/templates/deployment_backend.yaml b/charts/retool/templates/deployment_backend.yaml index 57206e5..e5b2338 100644 --- a/charts/retool/templates/deployment_backend.yaml +++ b/charts/retool/templates/deployment_backend.yaml @@ -161,6 +161,7 @@ spec: - name: CODE_EXECUTOR_INGRESS_DOMAIN value: http://{{ template "retool.codeExecutor.name" . }} {{- end }} + {{- include "retool.agentExecutor.backendEnvVars" . | nindent 10 }} {{- if ($temporalConfig).sslEnabled }} - name: WORKFLOW_TEMPORAL_TLS_ENABLED value: "true" diff --git a/charts/retool/templates/deployment_workflows.yaml b/charts/retool/templates/deployment_workflows.yaml index a03f741..359233e 100644 --- a/charts/retool/templates/deployment_workflows.yaml +++ b/charts/retool/templates/deployment_workflows.yaml @@ -176,6 +176,7 @@ spec: value: http://{{ include "retool.workflowBackend.name" . }} - name: CODE_EXECUTOR_INGRESS_DOMAIN value: http://{{ template "retool.codeExecutor.name" . }} + {{- include "retool.agentExecutor.backendEnvVars" . | nindent 10 }} {{- if include "shouldIncludeConfigSecretsEnvVars" . }} - name: LICENSE_KEY valueFrom: diff --git a/charts/retool/values.yaml b/charts/retool/values.yaml index 1c925d2..0f6f4ba 100644 --- a/charts/retool/values.yaml +++ b/charts/retool/values.yaml @@ -640,9 +640,186 @@ agents: cpu: 1000m memory: 2048Mi + # R2 agent worker configuration + r2AgentWorker: + replicaCount: 1 + + resources: + limits: + cpu: 2000m + memory: 4096Mi + requests: + cpu: 1000m + memory: 2048Mi + # Annotations for agent worker pods annotations: {} +# Agent Executor Service: sandboxed code execution for AI agents. +# Deploys a controller (manages sandbox lifecycle), proxy (HTTP proxy for sandbox egress), +# and ephemeral Job-based sandboxes. Requires Redis for controller/proxy state. +agentExecutor: + enabled: false + + image: + repository: tryretool/agent-executor-service + tag: latest + pullPolicy: IfNotPresent + + # Annotations for agent executor pods + annotations: {} + + # Labels for agent executor pods + labels: {} + + # Pre-existing K8s Secret. When set, the chart skips creating its own Secret + # and references this for keys: jwt-public-key, jwt-private-key, encryption-key, + # api-secret, redis-password. + externalSecret: + name: '' + + # Secrets (ignored when externalSecret.name is set) + # JWT key pair (ES256) for sandbox token authentication. + jwtPublicKey: '' + jwtPrivateKey: '' + # Hex-encoded 256-bit key for encrypting credentials stored in Redis. + # Must match the backend's AGENT_EXECUTOR_ENCRYPTION_KEY. + encryptionKey: '' + # API secret for admin/test endpoints. + apiSecret: '' + + # Redis configuration (shared by controller and proxy for state coordination) + redis: + host: '' + port: 6379 + password: '' + tls: false + keyPrefix: 'ae' + + # Sandbox network access via pasta userspace networking. + # When enabled, sandboxes get isolated outbound access with L7 filtering. + sandboxNetwork: + enabled: true + # Deploy smarter-device-manager to register /dev/net/tun with the kubelet. + # Required because containerd's default device cgroup blocks /dev/net/tun; + # the device plugin's DeviceSpec path is the only reliable way to grant + # device cgroup access without privileged mode. + devicePlugin: true + # HTTP proxy for sandbox egress L7 filtering. Defaults to the in-cluster + # agent-executor-proxy service URL when empty. + httpProxy: '' + + # smarter-device-manager: registers /dev/net/tun with the kubelet so executor + # pods can request it via resources.limits. + devicePlugin: + image: + repository: ghcr.io/smarter-project/smarter-device-manager + tag: v1.20.12 + # Number of /dev/net/tun device slots to register. + # Set high enough to accommodate maxTotalJobs + prewarm pool. + maxDevices: 130 + + # Seccomp profile path relative to /var/lib/kubelet/seccomp/. + # The seccomp node-installer DaemonSet copies the profile to this path + # on every node automatically. + seccompProfile: retool/gvisor-seccomp.json + + # Executor (sandbox Job) configuration + executor: + port: 3017 + resources: + requests: + cpu: 500m + memory: 512Mi + limits: + cpu: '2' + memory: 4Gi + # Idle timeout (ms) before an unassigned sandbox self-terminates. + sandboxIdleTimeoutMs: 300000 + tmpDirSizeLimit: 20Gi + # Additional environment variables for executor containers. + extraEnv: [] + + # Controller: tracks capacity, assigns sandbox pods, manages scaling + controller: + replicaCount: 1 + port: 3018 + resources: + requests: + cpu: 250m + memory: 256Mi + limits: + cpu: 500m + memory: 512Mi + scaling: + slotsPerPod: 4 + minReplicas: 1 + maxReplicas: 10 + scaleUpThreshold: 2 + scaleDownThreshold: 8 + scaleDownGracePeriodMs: 300000 + prewarmPoolSize: 5 + maxTotalJobs: 50 + maxConcurrentCreates: 3 + jobRetentionSeconds: 300 + assignedSandboxTtlSeconds: 600 + reconcileIntervalMs: 5000 + leaderTtlMs: 10000 + leaderRenewMs: 3000 + + # Proxy: HTTP proxy for sandbox egress with credential injection. + # The proxy must be reachable by frontend browsers for WebSocket connections. + proxy: + replicaCount: 1 + port: 3019 + resources: + requests: + cpu: 250m + memory: 256Mi + limits: + cpu: 500m + memory: 512Mi + allowedDomains: '' + # URL the proxy uses to reach the Retool backend for token exchange. + # Defaults to http://:3000 (same-cluster backend service). + backendUrl: '' + backendDomainSuffixes: '' + sandboxProxyTimeoutMs: '' + service: + # Set to LoadBalancer or NodePort to expose the proxy externally. + type: ClusterIP + annotations: {} + # Optional ingress to expose the proxy to frontend browsers for WebSocket connections. + # This is separate from the main Retool ingress since the proxy typically runs on its own domain. + ingress: + enabled: false + # ingressClassName: + annotations: {} + # kubernetes.io/ingress.class: nginx + # nginx.ingress.kubernetes.io/proxy-read-timeout: "3600" + # nginx.ingress.kubernetes.io/proxy-send-timeout: "3600" + host: '' + # e.g. sandbox.yourdomain.com + tls: [] + # - secretName: sandbox-tls + # hosts: + # - sandbox.yourdomain.com + + # Backend integration: these tell the Retool backend how to reach agent executor. + # controllerUrl and proxyUrl default to internal service URLs when empty. + controllerUrl: '' + proxyUrl: '' + # Required: public URL for frontend browsers to reach the proxy via WebSocket. + # e.g. https://sandbox.yourdomain.com + frontendWsProxyDomain: '' + # Public URL for proxy domain. Defaults to frontendWsProxyDomain if empty. + proxyDomain: '' + + # Node placement overrides (falls back to global nodeSelector/tolerations if empty) + nodeSelector: {} + tolerations: [] + affinity: {} + # SHARED TEMPORAL CONFIGURATION # This configuration is shared between all workers. # In order to use workers, temporal must be configured. From f1ae5a62be023046e0b059b354b477a26b0770f2 Mon Sep 17 00:00:00 2001 From: lukefoster11 Date: Wed, 22 Apr 2026 11:59:48 -0700 Subject: [PATCH 2/8] separate r2 agent from agents --- charts/retool/templates/_helpers.tpl | 15 +++++++++++++++ charts/retool/templates/_workers.tpl | 4 +--- charts/retool/values.yaml | 21 +++++++++++++++++---- 3 files changed, 33 insertions(+), 7 deletions(-) diff --git a/charts/retool/templates/_helpers.tpl b/charts/retool/templates/_helpers.tpl index 834cb79..3e6a57a 100644 --- a/charts/retool/templates/_helpers.tpl +++ b/charts/retool/templates/_helpers.tpl @@ -274,6 +274,9 @@ Usage: (include "retool.workflows.enabled" .) {{- if (eq (toString .Values.agents.enabled) "true") -}} {{/* workflows (backend) is required to use agents */}} {{- $output = "1" -}} {{- end -}} +{{- if (eq (toString .Values.r2Agent.enabled) "true") -}} {{/* workflows (backend) is required to use r2 agent */}} + {{- $output = "1" -}} +{{- end -}} {{- $output -}} {{- end -}} @@ -289,6 +292,18 @@ Usage: (include "retool.agents.enabled" .) {{- $output -}} {{- end -}} +{{/* +Set R2 agent enabled +Usage: (include "retool.r2Agent.enabled" .) +*/}} +{{- define "retool.r2Agent.enabled" -}} +{{- $output := "" -}} +{{- if (eq (toString .Values.r2Agent.enabled) "true") -}} + {{- $output = "1" -}} +{{- end -}} +{{- $output -}} +{{- end -}} + {{/* Global Temporal configuration */}} {{- define "retool.temporalConfig" -}} {{- .Values.workflows.temporal | default .Values.temporal | toYaml -}} diff --git a/charts/retool/templates/_workers.tpl b/charts/retool/templates/_workers.tpl index 5bd08aa..64e339f 100644 --- a/charts/retool/templates/_workers.tpl +++ b/charts/retool/templates/_workers.tpl @@ -3,7 +3,7 @@ type: agent - parent: agents type: agentEval -- parent: agents +- parent: r2Agent type: r2Agent - parent: workflows type: workflow @@ -29,8 +29,6 @@ {{- $workerValues := $parentValues.worker -}} {{- if eq $workerType "agentEval" -}} {{- $workerValues = $parentValues.evalWorker -}} -{{- else if eq $workerType "r2Agent" -}} - {{- $workerValues = $parentValues.r2AgentWorker -}} {{- end -}} {{- $workerPoolMaxSize := 100 -}} diff --git a/charts/retool/values.yaml b/charts/retool/values.yaml index 0f6f4ba..4e1098a 100644 --- a/charts/retool/values.yaml +++ b/charts/retool/values.yaml @@ -640,8 +640,24 @@ agents: cpu: 1000m memory: 2048Mi + # Annotations for agent worker pods + annotations: {} + +# R2 Agent: server-side agent loop worker (independent from agents above). +r2Agent: + enabled: false + + # Labels for R2 agent worker pods + labels: {} + + # R2 agent configuration + config: {} + + # Annotations for R2 agent worker pods + annotations: {} + # R2 agent worker configuration - r2AgentWorker: + worker: replicaCount: 1 resources: @@ -652,9 +668,6 @@ agents: cpu: 1000m memory: 2048Mi - # Annotations for agent worker pods - annotations: {} - # Agent Executor Service: sandboxed code execution for AI agents. # Deploys a controller (manages sandbox lifecycle), proxy (HTTP proxy for sandbox egress), # and ephemeral Job-based sandboxes. Requires Redis for controller/proxy state. From 2167939565281d893639a7290d20b9fadb6e08c5 Mon Sep 17 00:00:00 2001 From: lukefoster11 Date: Wed, 22 Apr 2026 14:20:35 -0700 Subject: [PATCH 3/8] match k8s better --- charts/retool/templates/deployment_agent_executor.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/charts/retool/templates/deployment_agent_executor.yaml b/charts/retool/templates/deployment_agent_executor.yaml index 9af1a77..34b43a2 100644 --- a/charts/retool/templates/deployment_agent_executor.yaml +++ b/charts/retool/templates/deployment_agent_executor.yaml @@ -169,7 +169,7 @@ data: {{- end }} ], "volumeMounts": [ - {{- if $ae.sandboxNetwork.enabled }} + {{- if and $ae.sandboxNetwork.enabled (not $ae.sandboxNetwork.devicePlugin) }} {"name": "dev-tun", "mountPath": "/dev/net/tun"}, {{- end }} {"name": "run", "mountPath": "/run"}, @@ -185,7 +185,7 @@ data: } ], "volumes": [ - {{- if $ae.sandboxNetwork.enabled }} + {{- if and $ae.sandboxNetwork.enabled (not $ae.sandboxNetwork.devicePlugin) }} {"name": "dev-tun", "hostPath": {"path": "/dev/net/tun", "type": "CharDevice"}}, {{- end }} {"name": "run", "emptyDir": {"medium": "Memory", "sizeLimit": "64Mi"}}, From a374d33b63fa74d572ea957407b380c2b648bbcf Mon Sep 17 00:00:00 2001 From: lukefoster11 Date: Wed, 22 Apr 2026 15:16:27 -0700 Subject: [PATCH 4/8] update values.yaml --- values.yaml | 190 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 190 insertions(+) diff --git a/values.yaml b/values.yaml index 1c925d2..4e1098a 100644 --- a/values.yaml +++ b/values.yaml @@ -643,6 +643,196 @@ agents: # Annotations for agent worker pods annotations: {} +# R2 Agent: server-side agent loop worker (independent from agents above). +r2Agent: + enabled: false + + # Labels for R2 agent worker pods + labels: {} + + # R2 agent configuration + config: {} + + # Annotations for R2 agent worker pods + annotations: {} + + # R2 agent worker configuration + worker: + replicaCount: 1 + + resources: + limits: + cpu: 2000m + memory: 4096Mi + requests: + cpu: 1000m + memory: 2048Mi + +# Agent Executor Service: sandboxed code execution for AI agents. +# Deploys a controller (manages sandbox lifecycle), proxy (HTTP proxy for sandbox egress), +# and ephemeral Job-based sandboxes. Requires Redis for controller/proxy state. +agentExecutor: + enabled: false + + image: + repository: tryretool/agent-executor-service + tag: latest + pullPolicy: IfNotPresent + + # Annotations for agent executor pods + annotations: {} + + # Labels for agent executor pods + labels: {} + + # Pre-existing K8s Secret. When set, the chart skips creating its own Secret + # and references this for keys: jwt-public-key, jwt-private-key, encryption-key, + # api-secret, redis-password. + externalSecret: + name: '' + + # Secrets (ignored when externalSecret.name is set) + # JWT key pair (ES256) for sandbox token authentication. + jwtPublicKey: '' + jwtPrivateKey: '' + # Hex-encoded 256-bit key for encrypting credentials stored in Redis. + # Must match the backend's AGENT_EXECUTOR_ENCRYPTION_KEY. + encryptionKey: '' + # API secret for admin/test endpoints. + apiSecret: '' + + # Redis configuration (shared by controller and proxy for state coordination) + redis: + host: '' + port: 6379 + password: '' + tls: false + keyPrefix: 'ae' + + # Sandbox network access via pasta userspace networking. + # When enabled, sandboxes get isolated outbound access with L7 filtering. + sandboxNetwork: + enabled: true + # Deploy smarter-device-manager to register /dev/net/tun with the kubelet. + # Required because containerd's default device cgroup blocks /dev/net/tun; + # the device plugin's DeviceSpec path is the only reliable way to grant + # device cgroup access without privileged mode. + devicePlugin: true + # HTTP proxy for sandbox egress L7 filtering. Defaults to the in-cluster + # agent-executor-proxy service URL when empty. + httpProxy: '' + + # smarter-device-manager: registers /dev/net/tun with the kubelet so executor + # pods can request it via resources.limits. + devicePlugin: + image: + repository: ghcr.io/smarter-project/smarter-device-manager + tag: v1.20.12 + # Number of /dev/net/tun device slots to register. + # Set high enough to accommodate maxTotalJobs + prewarm pool. + maxDevices: 130 + + # Seccomp profile path relative to /var/lib/kubelet/seccomp/. + # The seccomp node-installer DaemonSet copies the profile to this path + # on every node automatically. + seccompProfile: retool/gvisor-seccomp.json + + # Executor (sandbox Job) configuration + executor: + port: 3017 + resources: + requests: + cpu: 500m + memory: 512Mi + limits: + cpu: '2' + memory: 4Gi + # Idle timeout (ms) before an unassigned sandbox self-terminates. + sandboxIdleTimeoutMs: 300000 + tmpDirSizeLimit: 20Gi + # Additional environment variables for executor containers. + extraEnv: [] + + # Controller: tracks capacity, assigns sandbox pods, manages scaling + controller: + replicaCount: 1 + port: 3018 + resources: + requests: + cpu: 250m + memory: 256Mi + limits: + cpu: 500m + memory: 512Mi + scaling: + slotsPerPod: 4 + minReplicas: 1 + maxReplicas: 10 + scaleUpThreshold: 2 + scaleDownThreshold: 8 + scaleDownGracePeriodMs: 300000 + prewarmPoolSize: 5 + maxTotalJobs: 50 + maxConcurrentCreates: 3 + jobRetentionSeconds: 300 + assignedSandboxTtlSeconds: 600 + reconcileIntervalMs: 5000 + leaderTtlMs: 10000 + leaderRenewMs: 3000 + + # Proxy: HTTP proxy for sandbox egress with credential injection. + # The proxy must be reachable by frontend browsers for WebSocket connections. + proxy: + replicaCount: 1 + port: 3019 + resources: + requests: + cpu: 250m + memory: 256Mi + limits: + cpu: 500m + memory: 512Mi + allowedDomains: '' + # URL the proxy uses to reach the Retool backend for token exchange. + # Defaults to http://:3000 (same-cluster backend service). + backendUrl: '' + backendDomainSuffixes: '' + sandboxProxyTimeoutMs: '' + service: + # Set to LoadBalancer or NodePort to expose the proxy externally. + type: ClusterIP + annotations: {} + # Optional ingress to expose the proxy to frontend browsers for WebSocket connections. + # This is separate from the main Retool ingress since the proxy typically runs on its own domain. + ingress: + enabled: false + # ingressClassName: + annotations: {} + # kubernetes.io/ingress.class: nginx + # nginx.ingress.kubernetes.io/proxy-read-timeout: "3600" + # nginx.ingress.kubernetes.io/proxy-send-timeout: "3600" + host: '' + # e.g. sandbox.yourdomain.com + tls: [] + # - secretName: sandbox-tls + # hosts: + # - sandbox.yourdomain.com + + # Backend integration: these tell the Retool backend how to reach agent executor. + # controllerUrl and proxyUrl default to internal service URLs when empty. + controllerUrl: '' + proxyUrl: '' + # Required: public URL for frontend browsers to reach the proxy via WebSocket. + # e.g. https://sandbox.yourdomain.com + frontendWsProxyDomain: '' + # Public URL for proxy domain. Defaults to frontendWsProxyDomain if empty. + proxyDomain: '' + + # Node placement overrides (falls back to global nodeSelector/tolerations if empty) + nodeSelector: {} + tolerations: [] + affinity: {} + # SHARED TEMPORAL CONFIGURATION # This configuration is shared between all workers. # In order to use workers, temporal must be configured. From e3df430405205ddda3e9a1e561cd16ae47e3ccc3 Mon Sep 17 00:00:00 2001 From: lukefoster11 Date: Wed, 22 Apr 2026 15:21:14 -0700 Subject: [PATCH 5/8] chart version upgrade --- charts/retool/Chart.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/charts/retool/Chart.yaml b/charts/retool/Chart.yaml index d3e6b9b..50dd0ff 100644 --- a/charts/retool/Chart.yaml +++ b/charts/retool/Chart.yaml @@ -2,7 +2,7 @@ apiVersion: v2 name: retool description: A Helm chart for Kubernetes type: application -version: 6.10.2 +version: 6.11.0 maintainers: - name: Retool Engineering email: engineering+helm@retool.com From 08358486b8a5eccad941c8262bbacd18bb15bc13 Mon Sep 17 00:00:00 2001 From: lukefoster11 Date: Wed, 22 Apr 2026 15:58:38 -0700 Subject: [PATCH 6/8] comment --- charts/retool/templates/deployment_agent_executor.yaml | 2 -- 1 file changed, 2 deletions(-) diff --git a/charts/retool/templates/deployment_agent_executor.yaml b/charts/retool/templates/deployment_agent_executor.yaml index 34b43a2..778df20 100644 --- a/charts/retool/templates/deployment_agent_executor.yaml +++ b/charts/retool/templates/deployment_agent_executor.yaml @@ -628,8 +628,6 @@ spec: --- {{- end }} {{- /* -======================================================================= -{{- /* ======================================================================= Headless Service for direct pod addressing (sandbox routing). Executor Job pods use subdomain to register DNS: From 2b4aee7faa37cb6453d268e271e497c55e464bf1 Mon Sep 17 00:00:00 2001 From: lukefoster11 Date: Wed, 22 Apr 2026 16:38:47 -0700 Subject: [PATCH 7/8] remove link to workflows --- charts/retool/Chart.yaml | 2 +- charts/retool/templates/_helpers.tpl | 3 --- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/charts/retool/Chart.yaml b/charts/retool/Chart.yaml index 50dd0ff..dbedb80 100644 --- a/charts/retool/Chart.yaml +++ b/charts/retool/Chart.yaml @@ -13,4 +13,4 @@ dependencies: condition: postgresql.enabled - name: retool-temporal-services-helm version: 1.1.5 - condition: retool-temporal-services-helm.enabled,workflows.enabled + condition: retool-temporal-services-helm.enabled diff --git a/charts/retool/templates/_helpers.tpl b/charts/retool/templates/_helpers.tpl index 3e6a57a..ec0b24e 100644 --- a/charts/retool/templates/_helpers.tpl +++ b/charts/retool/templates/_helpers.tpl @@ -274,9 +274,6 @@ Usage: (include "retool.workflows.enabled" .) {{- if (eq (toString .Values.agents.enabled) "true") -}} {{/* workflows (backend) is required to use agents */}} {{- $output = "1" -}} {{- end -}} -{{- if (eq (toString .Values.r2Agent.enabled) "true") -}} {{/* workflows (backend) is required to use r2 agent */}} - {{- $output = "1" -}} -{{- end -}} {{- $output -}} {{- end -}} From e995909b16f501026aa7f12111160fc72d363e4a Mon Sep 17 00:00:00 2001 From: lukefoster11 Date: Wed, 22 Apr 2026 17:15:17 -0700 Subject: [PATCH 8/8] fix lock --- charts/retool/Chart.lock | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/charts/retool/Chart.lock b/charts/retool/Chart.lock index 832b6f5..7a8aeec 100644 --- a/charts/retool/Chart.lock +++ b/charts/retool/Chart.lock @@ -5,5 +5,5 @@ dependencies: - name: retool-temporal-services-helm repository: "" version: 1.1.5 -digest: sha256:6b027cb2d661c436127fe34c4a5e14c820c691d4ec9e0c08609f416e6fe5af21 -generated: "2024-03-26T15:39:11.463027-04:00" +digest: sha256:7b9440db4914c56407c98faace390fd00374820b0f87987903912de7ac899ce8 +generated: "2026-04-22T17:14:51.109299-07:00"