From d16a4a3ddef78c582442256bf3984c0b6d2a9d1d Mon Sep 17 00:00:00 2001 From: David Kegley Date: Tue, 24 Feb 2026 12:08:05 -0500 Subject: [PATCH 1/4] Add native-Go OHE support to Connect chart and update RBAC - Add `kubernetes.enabled` and related values to rstudio-connect for native-Go off-host execution (OHE) support - Update templates and config to support new OHE mode, overlays, and init container - Add validation to prevent both `launcher.enabled` and `kubernetes.enabled` - Update RBAC in rstudio-library to grant endpointslice list/watch permissions - Bump rstudio-connect to 0.8.27 and rstudio-library to 0.1.37 - Update NEWS.md and README.md.gotmpl for both charts --- charts/rstudio-connect/Chart.yaml | 4 +- charts/rstudio-connect/NEWS.md | 4 + charts/rstudio-connect/README.md | 20 +- charts/rstudio-connect/README.md.gotmpl | 2 +- charts/rstudio-connect/templates/NOTES.txt | 4 + charts/rstudio-connect/templates/_helpers.tpl | 27 ++ .../templates/configmap-prestart.yaml | 2 +- .../rstudio-connect/templates/configmap.yaml | 10 + .../rstudio-connect/templates/deployment.yaml | 22 +- charts/rstudio-connect/templates/rbac.yaml | 9 +- .../tests/kubernetes-values.yaml | 24 ++ .../tests/kubernetes_test.yaml | 266 ++++++++++++++++++ charts/rstudio-connect/values.yaml | 65 ++++- charts/rstudio-library/Chart.yaml | 4 +- charts/rstudio-library/NEWS.md | 4 + charts/rstudio-library/README.md | 2 +- charts/rstudio-library/templates/_rbac.tpl | 10 + 17 files changed, 454 insertions(+), 25 deletions(-) create mode 100644 charts/rstudio-connect/tests/kubernetes-values.yaml create mode 100644 charts/rstudio-connect/tests/kubernetes_test.yaml diff --git a/charts/rstudio-connect/Chart.yaml b/charts/rstudio-connect/Chart.yaml index a22db0af6..8cb329ce3 100644 --- a/charts/rstudio-connect/Chart.yaml +++ b/charts/rstudio-connect/Chart.yaml @@ -1,6 +1,6 @@ name: rstudio-connect description: Official Helm chart for Posit Connect -version: 0.8.26 +version: 0.8.27 apiVersion: v2 appVersion: 2026.01.1 icon: https://raw.githubusercontent.com/rstudio/helm/main/images/posit-icon-fullcolor.svg @@ -13,7 +13,7 @@ maintainers: url: https://github.com/sol-eng dependencies: - name: rstudio-library - version: 0.1.35 + version: 0.1.37 repository: https://helm.rstudio.com annotations: artifacthub.io/images: | diff --git a/charts/rstudio-connect/NEWS.md b/charts/rstudio-connect/NEWS.md index 4752d35f0..d22238981 100644 --- a/charts/rstudio-connect/NEWS.md +++ b/charts/rstudio-connect/NEWS.md @@ -1,5 +1,9 @@ # Changelog +## 0.8.27 + +- Adds support for a native-Go implementation of Off-Host Execution (OHE) mode. Set `kubernetes.enabled = true` to switch to the new implementation. See the [migration guide]() in the Connect admin documentation for guidance on migrating existing configurations. + ## 0.8.26 - Fix bug in chart where sharedStorage.subPath did not propagate to launcher-managed job volumeMounts. diff --git a/charts/rstudio-connect/README.md b/charts/rstudio-connect/README.md index 3a606a3cf..fa5b80fc1 100644 --- a/charts/rstudio-connect/README.md +++ b/charts/rstudio-connect/README.md @@ -1,6 +1,6 @@ # Posit Connect -![Version: 0.8.26](https://img.shields.io/badge/Version-0.8.26-informational?style=flat-square) ![AppVersion: 2026.01.1](https://img.shields.io/badge/AppVersion-2026.01.1-informational?style=flat-square) +![Version: 0.8.27](https://img.shields.io/badge/Version-0.8.27-informational?style=flat-square) ![AppVersion: 2026.01.1](https://img.shields.io/badge/AppVersion-2026.01.1-informational?style=flat-square) #### _Official Helm chart for Posit Connect_ @@ -30,11 +30,11 @@ To ensure reproducibility in your environment and insulate yourself from future ## Installing the chart -To install the chart with the release name `my-release` at version 0.8.26: +To install the chart with the release name `my-release` at version 0.8.27: ```{.bash} helm repo add rstudio https://helm.rstudio.com -helm upgrade --install my-release rstudio/rstudio-connect --version=0.8.26 +helm upgrade --install my-release rstudio/rstudio-connect --version=0.8.27 ``` To explore other chart versions, look at: @@ -48,7 +48,7 @@ helm search repo rstudio/rstudio-connect -l ### 0.8.0 - When upgrading to version 0.8.0 or later, Connect now runs in [Off-Host Execution mode](https://docs.posit.co/connect/admin/getting-started/off-host-install/) by default -- If you desire to run Connect not in Off-Host Execution mode, then set `securityContext.privileged: true` and `launcher.enabled: false` +- If you desire to run Connect in Local Execution mode, then set `securityContext.privileged: true` and `launcher.enabled: false` ## Required configuration @@ -248,6 +248,14 @@ The Helm `config` values are converted into the `rstudio-connect.gcfg` service c | ingress.ingressClassName | string | `""` | The ingressClassName for the ingress resource. Only used for clusters that support networking.k8s.io/v1 Ingress resources | | ingress.tls | list | `[]` | | | initContainers | bool | `false` | The initContainer spec that will be used verbatim | +| kubernetes.defaultInitContainer.enabled | bool | `true` | Whether to enable the defaultInitContainer. If disabled, you must ensure that the session components are available another way. | +| kubernetes.defaultInitContainer.repository | string | `"ghcr.io/rstudio/rstudio-connect-content-init"` | The repository to use for the Content InitContainer image | +| kubernetes.defaultInitContainer.tag | string | `""` | Overrides the image tag whose default is the chart appVersion. | +| kubernetes.defaultInitContainer.tagPrefix | string | `"ubuntu2204-"` | A tag prefix for the Content InitContainer image (common selections: jammy-, ubuntu2204-). Only used if tag is not defined | +| kubernetes.defaultJobOverlay | object | `{"apiVersion":"batch/v1","kind":"Job","spec":{"template":{"spec":{"containers":[{"name":"connect-content","volumeMounts":[{"mountPath":"/opt/rstudio-connect/R","name":"rsc-volume","subPath":"R"},{"mountPath":"/opt/rstudio-connect/python","name":"rsc-volume","subPath":"python"},{"mountPath":"/opt/rstudio-connect/scripts","name":"rsc-volume","subPath":"scripts"},{"mountPath":"/opt/rstudio-connect/ext","name":"rsc-volume","subPath":"ext"}]}],"initContainers":[{"name":"connect-content-init","volumeMounts":[{"mountPath":"/mnt/rstudio-connect-runtime/","name":"rsc-volume"}]}],"volumes":[{"emptyDir":{},"name":"rsc-volume"}]}}}}` | defaultJobOverlay contains the Kubernetes Job definition which is used as an overlay "base" when launching a content job in Kubernetes. Conceptually this is a similar to a Kustomize base. Connect then applies any required job configuration on-top of the overlay base to produce a final job definition before submitting the Job to Kubernetes. https://docs.posit.co/connect/__unreleased__/admin/appendix/off-host/direct-runner/kubernetes-job-overlays.html https://kubernetes.io/docs/tasks/manage-kubernetes-objects/kustomization/#bases-and-overlays | +| kubernetes.defaultServiceOverlay | object | `{}` | defaultServiceOverlay contains the Kubernetes Service definition which is used as an overlay "base" when creating a content job's Service in Kubernetes. Conceptually this is a similar to a Kustomize base. Connect then applies any required Service configuration on-top of the overlay base to produce a final Service definition. | +| kubernetes.enabled | bool | `false` | Whether to enable the native Go implementation of off-host execution for running content-jobs in remote Kubernetes pods. | +| kubernetes.namespace | string | `""` | The namespace to launch connect-content jobs into. Uses the Release namespace by default | | launcher.additionalRuntimeImages | list | `[]` | Optional. Additional images to append to the end of the "launcher.customRuntimeYaml" (in the "images" key). If `customRuntimeYaml` is a "map", then "additionalRuntimeImages" will only be used if it is a "list". | | launcher.customRuntimeYaml | string | `"base"` | Optional. The runtime.yaml definition of Kubernetes runtime containers. Defaults to "base", which pulls in the default runtime.yaml file. If changing this value, be careful to include the images that you have already used. If set to "pro", will pull in the "pro" versions of the default runtime images (i.e. including the pro drivers at the cost of a larger image). Starting with Connect v2023.05.0, this configuration is used to bootstrap the initial set of execution environments the first time the server starts. If any execution environments already exist in the database, these values are ignored; execution environments are not created or modified during subsequent restarts. | | launcher.defaultInitContainer | object | `{"enabled":true,"imagePullPolicy":"","repository":"ghcr.io/rstudio/rstudio-connect-content-init","resources":{},"securityContext":{},"tag":"","tagPrefix":"ubuntu2204-"}` | Image definition for the default Posit Connect Content InitContainer | @@ -309,8 +317,8 @@ The Helm `config` values are converted into the `rstudio-connect.gcfg` service c | prometheusExporter.resources | object | `{}` | resource specification for the prometheus exporter sidecar | | prometheusExporter.securityContext | object | `{}` | securityContext for the prometheus exporter sidecar | | rbac.clusterRoleCreate | bool | `false` | Whether to create the ClusterRole that grants access to the Kubernetes nodes API. This is used by the Launcher to get all of the IP addresses associated with the node that is running a particular job. In most cases, this can be disabled as the node's internal address is sufficient to allow proper functionality. | -| rbac.create | bool | `true` | Whether to create rbac. (also depends on launcher.enabled = true) | -| rbac.serviceAccount | object | `{"annotations":{},"create":true,"labels":{},"name":""}` | The serviceAccount to be associated with rbac (also depends on launcher.enabled = true) | +| rbac.create | bool | `true` | Whether to create rbac. (also depends on launcher.enabled = true or kubernetes.enabled = true) | +| rbac.serviceAccount | object | `{"annotations":{},"create":true,"labels":{},"name":""}` | The serviceAccount to be associated with rbac (also depends on launcher.enabled = true or kubernetes.enabled = true) | | readinessProbe | object | `{"enabled":true,"failureThreshold":3,"httpGet":{"path":"/__ping__","port":3939},"initialDelaySeconds":3,"periodSeconds":3,"successThreshold":1,"timeoutSeconds":1}` | Used to configure the container's readinessProbe. Only included if enabled = true | | replicas | int | `1` | The number of replica pods to maintain for this service | | resources | object | `{}` | Defines resources for the rstudio-connect container | diff --git a/charts/rstudio-connect/README.md.gotmpl b/charts/rstudio-connect/README.md.gotmpl index 7486a5db3..f23b7d343 100644 --- a/charts/rstudio-connect/README.md.gotmpl +++ b/charts/rstudio-connect/README.md.gotmpl @@ -13,7 +13,7 @@ ### 0.8.0 - When upgrading to version 0.8.0 or later, Connect now runs in [Off-Host Execution mode](https://docs.posit.co/connect/admin/getting-started/off-host-install/) by default -- If you desire to run Connect not in Off-Host Execution mode, then set `securityContext.privileged: true` and `launcher.enabled: false` +- If you desire to run Connect in Local Execution mode, then set `securityContext.privileged: true` and `launcher.enabled: false` ## Required configuration diff --git a/charts/rstudio-connect/templates/NOTES.txt b/charts/rstudio-connect/templates/NOTES.txt index e16b73188..698c9b956 100644 --- a/charts/rstudio-connect/templates/NOTES.txt +++ b/charts/rstudio-connect/templates/NOTES.txt @@ -34,6 +34,10 @@ Please consider removing this configuration value. {{- /* chart deprecations and misconfiguration warnings */ -}} +{{- if and .Values.launcher.enabled .Values.kubernetes.enabled }} + {{- fail "\n\n`launcher.enabled` and `kubernetes.enabled` cannot both be true"}} +{{- end }} + {{- if and .Values.launcher.useTemplates .Values.launcher.enabled }} {{- range $k,$v := .Values.launcher.launcherKubernetesProfilesConf }} {{- if hasKey $v "job-json-overrides" }} diff --git a/charts/rstudio-connect/templates/_helpers.tpl b/charts/rstudio-connect/templates/_helpers.tpl index aadfa96ad..1e150a3ee 100644 --- a/charts/rstudio-connect/templates/_helpers.tpl +++ b/charts/rstudio-connect/templates/_helpers.tpl @@ -76,6 +76,33 @@ app.kubernetes.io/instance: {{ .Release.Name }} {{- $launcherDict := dict "Launcher" ( $launcherSettingsDict ) }} {{- $defaultConfig = merge $defaultConfig $launcherDict }} {{- end }} + {{- if .Values.kubernetes.enabled }} + {{- $namespace := default $.Release.Namespace .Values.kubernetes.namespace }} + {{- $kubernetesSettingsDict := dict "Enabled" ("true") "Namespace" ($namespace) }} + {{- if and (or .Values.sharedStorage.create .Values.sharedStorage.mount) .Values.sharedStorage.mountContent }} + {{- $dataDirPVCName := default (print (include "rstudio-connect.fullname" .) "-shared-storage" ) .Values.sharedStorage.name }} + {{- $_ := set $kubernetesSettingsDict "DataDirPVCName" $dataDirPVCName }} + {{- end }} + {{- if .Values.kubernetes.defaultJobOverlay }} + {{- if and .Values.kubernetes.defaultInitContainer.enabled }} + {{- $defaultVersion := .Values.versionOverride | default $.Chart.AppVersion }} + {{- $initContainerImageTag := .Values.kubernetes.defaultInitContainer.tag | default (printf "%s%s" .Values.kubernetes.defaultInitContainer.tagPrefix $defaultVersion )}} + {{- $initContainerImage := print .Values.kubernetes.defaultInitContainer.repository ":" ( $initContainerImageTag ) }} + {{- range (.Values.kubernetes.defaultJobOverlay.spec.template.spec).initContainers | default list }} + {{- if eq .name "connect-content-init" }} + {{- $_ := set . "image" $initContainerImage }} + {{- break }} + {{- end }} + {{- end }} + {{- end }} + {{- $_ := set $kubernetesSettingsDict "DefaultJobOverlay" (default "/etc/rstudio-connect/job.yaml" .Values.config.Kubernetes.DefaultJobOverlay) }} + {{- end }} + {{- if .Values.kubernetes.defaultServiceOverlay }} + {{- $_ = set $kubernetesSettingsDict "DefaultServiceOverlay" (default "/etc/rstudio-connect/service.yaml" .Values.config.Kubernetes.DefaultServiceOverlay) }} + {{- end }} + {{- $kubernetesDict := dict "Kubernetes" ( $kubernetesSettingsDict ) }} + {{- $defaultConfig = merge $defaultConfig $kubernetesDict }} + {{- end }} {{- /* default licensing configuration */}} {{- if .Values.license.server }} {{- $licenseDict := dict "Licensing" ( dict "LicenseType" ("Remote") ) }} diff --git a/charts/rstudio-connect/templates/configmap-prestart.yaml b/charts/rstudio-connect/templates/configmap-prestart.yaml index 775be6449..3c4b43c87 100644 --- a/charts/rstudio-connect/templates/configmap-prestart.yaml +++ b/charts/rstudio-connect/templates/configmap-prestart.yaml @@ -1,4 +1,4 @@ -{{- if .Values.launcher.enabled }} +{{- if or .Values.launcher.enabled .Values.kubernetes.enabled }} --- apiVersion: v1 kind: ConfigMap diff --git a/charts/rstudio-connect/templates/configmap.yaml b/charts/rstudio-connect/templates/configmap.yaml index fa672b80f..281feaee0 100644 --- a/charts/rstudio-connect/templates/configmap.yaml +++ b/charts/rstudio-connect/templates/configmap.yaml @@ -7,6 +7,16 @@ metadata: data: rstudio-connect.gcfg: | {{- include "rstudio-connect.config" . | nindent 4 }} +{{- if .Values.kubernetes.enabled }} +{{- if .Values.kubernetes.defaultJobOverlay }} + job.yaml: | +{{- .Values.kubernetes.defaultJobOverlay | toYaml | nindent 4 }} +{{- end }} +{{- if .Values.kubernetes.defaultServiceOverlay }} + service.yaml: | +{{- .Values.kubernetes.defaultServiceOverlay | toYaml | nindent 4 }} +{{- end }} +{{- end }} {{- $sessionTemplate := deepCopy .Values.launcher.templateValues }} {{- if .Values.launcher.enabled }} runtime.yaml: | diff --git a/charts/rstudio-connect/templates/deployment.yaml b/charts/rstudio-connect/templates/deployment.yaml index b94acbdab..4aa6948ef 100644 --- a/charts/rstudio-connect/templates/deployment.yaml +++ b/charts/rstudio-connect/templates/deployment.yaml @@ -23,7 +23,7 @@ spec: {{- if and .Values.prometheus.legacy .Values.prometheusExporter.enabled }} checksum/config-graphite: {{ include (print $.Template.BasePath "/configmap-graphite-exporter.yaml") . | sha256sum }} {{- end }} - {{- if .Values.launcher.enabled }} + {{- if or .Values.launcher.enabled .Values.kubernetes.enabled }} checksum/config-prestart: {{ include (print $.Template.BasePath "/configmap-prestart.yaml") . | sha256sum }} {{- end }} {{- if .Values.prometheus }} @@ -76,7 +76,7 @@ spec: * and also the "Note" callout at the end of this section: * https://kubernetes.io/docs/tasks/configure-pod-container/configure-service-account/#use-multiple-service-accounts */}} - {{- if and .Values.rbac.create .Values.launcher.enabled }} + {{- if and .Values.rbac.create (or .Values.launcher.enabled .Values.kubernetes.enabled) }} {{ $serviceAccountName := default (include "rstudio-connect.fullname" .) .Values.rbac.serviceAccount.name }} serviceAccountName: {{ $serviceAccountName }} {{- else }} @@ -156,7 +156,7 @@ spec: {{- if .Values.command }} command: {{ toYaml .Values.command | indent 10 }} - {{- else if .Values.launcher.enabled }} + {{- else if (or .Values.launcher.enabled .Values.kubernetes.enabled) }} command: - tini - -s @@ -165,7 +165,7 @@ spec: {{- if .Values.args }} args: {{ toYaml .Values.args | indent 10 }} - {{- else if .Values.launcher.enabled }} + {{- else if (or .Values.launcher.enabled .Values.kubernetes.enabled) }} args: - /scripts/prestart.bash - /usr/local/bin/startup.sh @@ -186,6 +186,18 @@ spec: - name: rstudio-connect-config mountPath: "/etc/rstudio-connect/rstudio-connect.gcfg" subPath: "rstudio-connect.gcfg" + {{- if .Values.kubernetes.enabled }} + {{- if .Values.kubernetes.defaultJobOverlay }} + - name: rstudio-connect-config + mountPath: "/etc/rstudio-connect/job.yaml" + subPath: "job.yaml" + {{- end }} + {{- if .Values.kubernetes.defaultServiceOverlay }} + - name: rstudio-connect-config + mountPath: "/etc/rstudio-connect/service.yaml" + subPath: "service.yaml" + {{- end }} + {{- end }} {{- if .Values.launcher.enabled }} - name: rstudio-connect-config mountPath: "/etc/rstudio-connect/runtime.yaml" @@ -317,6 +329,8 @@ spec: name: {{ include "rstudio-connect.fullname" . }}-overrides defaultMode: 0644 {{- end }} + {{- end }} + {{- if or .Values.launcher.enabled .Values.kubernetes.enabled }} - name: rstudio-connect-prestart configMap: name: {{ include "rstudio-connect.fullname" . }}-prestart diff --git a/charts/rstudio-connect/templates/rbac.yaml b/charts/rstudio-connect/templates/rbac.yaml index bfb253255..d712ed225 100644 --- a/charts/rstudio-connect/templates/rbac.yaml +++ b/charts/rstudio-connect/templates/rbac.yaml @@ -1,6 +1,11 @@ -{{- if and (.Values.rbac.create) (.Values.launcher.enabled) }} +{{- if and (.Values.rbac.create) (or .Values.launcher.enabled .Values.kubernetes.enabled) }} {{ $namespace := $.Release.Namespace }} -{{ $targetNamespace := default $.Release.Namespace .Values.launcher.namespace }} +{{ $targetNamespace := $.Release.Namespace }} +{{- if .Values.launcher.enabled }} +{{ $targetNamespace = default $targetNamespace .Values.launcher.namespace }} +{{- else }} +{{ $targetNamespace = default $targetNamespace .Values.kubernetes.namespace }} +{{- end }} {{ $serviceAccountName := default (include "rstudio-connect.fullname" .) .Values.rbac.serviceAccount.name }} {{ $serviceAccountCreate := .Values.rbac.serviceAccount.create }} {{ $serviceAccountAnnotations := .Values.rbac.serviceAccount.annotations }} diff --git a/charts/rstudio-connect/tests/kubernetes-values.yaml b/charts/rstudio-connect/tests/kubernetes-values.yaml new file mode 100644 index 000000000..63e08c464 --- /dev/null +++ b/charts/rstudio-connect/tests/kubernetes-values.yaml @@ -0,0 +1,24 @@ +launcher: + enabled: false + +kubernetes: + enabled: true + defaultJobOverlay: + apiVersion: batch/v1 + kind: Job + spec: + template: + spec: + containers: + - name: connect-content + volumeMounts: + - mountPath: /opt/rstudio-connect + name: rsc-volume + initContainers: + - name: connect-content-init + volumeMounts: + - mountPath: /mnt/rstudio-connect-runtime/ + name: rsc-volume + volumes: + - emptyDir: {} + name: rsc-volume diff --git a/charts/rstudio-connect/tests/kubernetes_test.yaml b/charts/rstudio-connect/tests/kubernetes_test.yaml new file mode 100644 index 000000000..39f7eb861 --- /dev/null +++ b/charts/rstudio-connect/tests/kubernetes_test.yaml @@ -0,0 +1,266 @@ +suite: test kubernetes configuration +templates: + - NOTES.txt + - configmap-prestart.yaml + - configmap.yaml + - deployment.yaml + - rbac.yaml +tests: + # Test validation: launcher.enabled and kubernetes.enabled cannot both be true + - it: should fail when both launcher.enabled and kubernetes.enabled are true + template: NOTES.txt + set: + launcher: + enabled: true + kubernetes: + enabled: true + asserts: + - failedTemplate: + errorMessage: "`launcher.enabled` and `kubernetes.enabled` cannot both be true" + + # Test kubernetes.enabled creates configmap entries for job overlay + - it: should create job.yaml in configmap when kubernetes.enabled and defaultJobOverlay is set + template: configmap.yaml + values: + - kubernetes-values.yaml + asserts: + - exists: + path: data["job.yaml"] + - matchRegex: + path: data["job.yaml"] + pattern: "apiVersion: batch/v1" + - matchRegex: + path: data["job.yaml"] + pattern: "kind: Job" + + - it: should not create job.yaml when kubernetes.enabled is false + template: configmap.yaml + set: + launcher: + enabled: false + kubernetes: + enabled: false + asserts: + - notExists: + path: data["job.yaml"] + + # Test deployment volumeMounts for kubernetes overlays + - it: should mount job.yaml when kubernetes.enabled and defaultJobOverlay is set + template: deployment.yaml + values: + - kubernetes-values.yaml + asserts: + - contains: + path: spec.template.spec.containers[0].volumeMounts + content: + name: rstudio-connect-config + mountPath: /etc/rstudio-connect/job.yaml + subPath: job.yaml + + - it: should not mount job.yaml when kubernetes.enabled is false + template: deployment.yaml + set: + launcher: + enabled: false + kubernetes: + enabled: false + asserts: + - notContains: + path: spec.template.spec.containers[0].volumeMounts + content: + name: rstudio-connect-config + mountPath: /etc/rstudio-connect/job.yaml + subPath: job.yaml + + # Test RBAC resources are created when kubernetes.enabled + - it: should create RBAC resources when kubernetes.enabled and rbac.create are true + template: rbac.yaml + set: + launcher: + enabled: false + kubernetes: + enabled: true + rbac: + create: true + asserts: + - hasDocuments: + count: 3 # ServiceAccount, Role, RoleBinding + + - it: should not create RBAC resources when kubernetes.enabled is false and launcher.enabled is false + template: rbac.yaml + set: + launcher: + enabled: false + kubernetes: + enabled: false + rbac: + create: true + asserts: + - hasDocuments: + count: 0 + + # Test kubernetes namespace configuration + - it: should use custom namespace when kubernetes.namespace is specified + template: rbac.yaml + set: + launcher: + enabled: false + kubernetes: + enabled: true + namespace: custom-namespace + rbac: + create: true + documentIndex: 1 # Role document + asserts: + - equal: + path: metadata.namespace + value: custom-namespace + + - it: should use release namespace when kubernetes.namespace is not specified + template: rbac.yaml + release: + namespace: test-namespace + set: + launcher: + enabled: false + kubernetes: + enabled: true + rbac: + create: true + documentIndex: 1 # Role document + asserts: + - equal: + path: metadata.namespace + value: test-namespace + + # Test serviceAccount configuration with kubernetes.enabled + - it: should set serviceAccountName when kubernetes.enabled and rbac.create are true + template: deployment.yaml + set: + launcher: + enabled: false + kubernetes: + enabled: true + rbac: + create: true + serviceAccount: + create: true + name: custom-sa + asserts: + - equal: + path: spec.template.spec.serviceAccountName + value: custom-sa + + # Test that sharedStorage.mountContent propagates to kubernetes config + - it: should configure DataDirPVCName when sharedStorage is enabled and mountContent is true + template: configmap.yaml + release: + name: my-connect + set: + launcher: + enabled: false + kubernetes: + enabled: true + sharedStorage: + create: true + mount: true + mountContent: true + documentIndex: 0 + asserts: + - matchRegex: + path: data["rstudio-connect.gcfg"] + pattern: "DataDirPVCName = my-connect-rstudio-connect-shared-storage" + + - it: should not configure DataDirPVCName when sharedStorage.mountContent is false + template: configmap.yaml + set: + launcher: + enabled: false + kubernetes: + enabled: true + sharedStorage: + create: true + mount: true + mountContent: false + documentIndex: 0 + asserts: + - notMatchRegex: + path: data["rstudio-connect.gcfg"] + pattern: "DataDirPVCName" + + # Test kubernetes config section in rstudio-connect.gcfg + - it: should configure Kubernetes.Enabled in gcfg when kubernetes.enabled is true + template: configmap.yaml + set: + launcher: + enabled: false + kubernetes: + enabled: true + namespace: k8s-namespace + asserts: + - matchRegex: + path: data["rstudio-connect.gcfg"] + pattern: "\\[Kubernetes\\]" + - matchRegex: + path: data["rstudio-connect.gcfg"] + pattern: "Enabled = true" + - matchRegex: + path: data["rstudio-connect.gcfg"] + pattern: "Namespace = k8s-namespace" + + - it: should configure DefaultJobOverlay path when defaultJobOverlay is set + template: configmap.yaml + values: + - kubernetes-values.yaml + asserts: + - matchRegex: + path: data["rstudio-connect.gcfg"] + pattern: "DefaultJobOverlay = /etc/rstudio-connect/job.yaml" + + # Test with both launcher disabled and kubernetes enabled + - it: should work correctly with launcher disabled and kubernetes enabled + template: deployment.yaml + set: + launcher: + enabled: false + kubernetes: + enabled: true + rbac: + create: true + asserts: + - isKind: + of: Deployment + - equal: + path: spec.template.spec.serviceAccountName + value: RELEASE-NAME-rstudio-connect + + # Test serviceAccount is set correctly when kubernetes.enabled + - it: should generate default serviceAccountName when kubernetes.enabled + template: deployment.yaml + set: + launcher: + enabled: false + kubernetes: + enabled: true + rbac: + create: true + serviceAccount: + create: true + asserts: + - equal: + path: spec.template.spec.serviceAccountName + value: RELEASE-NAME-rstudio-connect + + # Test that config includes Kubernetes section + - it: should include Kubernetes config section when enabled + template: configmap.yaml + set: + launcher: + enabled: false + kubernetes: + enabled: true + documentIndex: 0 + asserts: + - matchRegex: + path: data["rstudio-connect.gcfg"] + pattern: "\\[Kubernetes\\]" diff --git a/charts/rstudio-connect/values.yaml b/charts/rstudio-connect/values.yaml index a28d63a43..894f69783 100644 --- a/charts/rstudio-connect/values.yaml +++ b/charts/rstudio-connect/values.yaml @@ -36,13 +36,13 @@ sharedStorage: subPath: "" rbac: - # -- Whether to create rbac. (also depends on launcher.enabled = true) + # -- Whether to create rbac. (also depends on launcher.enabled = true or kubernetes.enabled = true) create: true # -- Whether to create the ClusterRole that grants access to the Kubernetes nodes API. This is used by the Launcher # to get all of the IP addresses associated with the node that is running a particular job. In most cases, this can # be disabled as the node's internal address is sufficient to allow proper functionality. clusterRoleCreate: false - # -- The serviceAccount to be associated with rbac (also depends on launcher.enabled = true) + # -- The serviceAccount to be associated with rbac (also depends on launcher.enabled = true or kubernetes.enabled = true) serviceAccount: create: true name: "" @@ -312,6 +312,58 @@ chronicleAgent: # ([reference](https://docs.posit.co/chronicle/appendix/library/advanced-agent.html#environment)) agentEnvironment: "" +kubernetes: + # -- Whether to enable the native Go implementation of off-host execution for running content-jobs in remote Kubernetes pods. + enabled: false + # -- The namespace to launch connect-content jobs into. Uses the Release namespace by default + namespace: "" + defaultInitContainer: + # -- Whether to enable the defaultInitContainer. If disabled, you must ensure that the session components are available another way. + enabled: true + # -- The repository to use for the Content InitContainer image + repository: ghcr.io/rstudio/rstudio-connect-content-init + # -- A tag prefix for the Content InitContainer image (common selections: jammy-, ubuntu2204-). Only used if tag is not defined + tagPrefix: ubuntu2204- + # -- Overrides the image tag whose default is the chart appVersion. + tag: "" + # -- defaultJobOverlay contains the Kubernetes Job definition which is used as an overlay "base" when launching a content job + # in Kubernetes. Conceptually this is a similar to a Kustomize base. Connect then applies any required job configuration + # on-top of the overlay base to produce a final job definition before submitting the Job to Kubernetes. + # https://docs.posit.co/connect/__unreleased__/admin/appendix/off-host/direct-runner/kubernetes-job-overlays.html + # https://kubernetes.io/docs/tasks/manage-kubernetes-objects/kustomization/#bases-and-overlays + defaultJobOverlay: + apiVersion: batch/v1 + kind: Job + spec: + template: + spec: + containers: + - name: "connect-content" # well-known name + volumeMounts: + - mountPath: /opt/rstudio-connect + name: rsc-volume + initContainers: + - name: connect-content-init # well-known name + # Note: Image is set dyncamically by kubernetes.defaultInitContainer + # because the selected image must match the Connect server version + # image: "" + volumeMounts: + - mountPath: /mnt/rstudio-connect-runtime/ + name: rsc-volume + volumes: + - emptyDir: {} + name: rsc-volume + # -- defaultServiceOverlay contains the Kubernetes Service definition which is used as an overlay "base" when creating a content job's + # Service in Kubernetes. Conceptually this is a similar to a Kustomize base. Connect then applies any required Service configuration + # on-top of the overlay base to produce a final Service definition. + # https://docs.posit.co/connect/__unreleased__/admin/appendix/off-host/direct-runner/kubernetes-job-overlays.html + # https://kubernetes.io/docs/tasks/manage-kubernetes-objects/kustomization/#bases-and-overlays + defaultServiceOverlay: {} + # apiVersion: v1 + # kind: Service + # spec: + # type: NodePort + launcher: # -- Whether to enable the launcher enabled: true @@ -393,7 +445,7 @@ launcher: tag: "" # -- The imagePullPolicy for the default initContainer imagePullPolicy: "" - # -- Optional resources for the default initContainer + # -- Optional resources for the default initContainer resources: {} # requests: # cpu: "128m" @@ -455,10 +507,11 @@ config: InitTimeout: 5m Logging: ServiceLog: STDOUT - ServiceLogLevel: INFO # INFO, WARNING or ERROR - ServiceLogFormat: TEXT # TEXT or JSON + ServiceLogLevel: INFO # INFO, WARNING or ERROR + ServiceLogFormat: TEXT # TEXT or JSON AccessLog: STDOUT - AccessLogFormat: COMMON # COMMON, COMBINED, or JSON + AccessLogFormat: COMMON # COMMON, COMBINED, or JSON Metrics: Enabled: true Launcher: {} + Kubernetes: {} diff --git a/charts/rstudio-library/Chart.yaml b/charts/rstudio-library/Chart.yaml index ac3b1d749..d9b67fa6b 100644 --- a/charts/rstudio-library/Chart.yaml +++ b/charts/rstudio-library/Chart.yaml @@ -2,8 +2,8 @@ apiVersion: v2 name: rstudio-library description: Helm library helpers for use by official RStudio charts type: library -version: 0.1.36 -appVersion: 0.1.35 +version: 0.1.37 +appVersion: 0.1.37 icon: https://raw.githubusercontent.com/rstudio/helm/main/images/posit-icon-fullcolor.svg home: https://www.rstudio.com diff --git a/charts/rstudio-library/NEWS.md b/charts/rstudio-library/NEWS.md index a49d5b6f1..df87826a4 100644 --- a/charts/rstudio-library/NEWS.md +++ b/charts/rstudio-library/NEWS.md @@ -1,5 +1,9 @@ # Changelog +## 0.1.37 + +- Update rbac role rules to include endpointslice list/watch permissions + ## 0.1.36 - Update chart icon to Posit icon diff --git a/charts/rstudio-library/README.md b/charts/rstudio-library/README.md index 7b28d09c8..9a757b217 100644 --- a/charts/rstudio-library/README.md +++ b/charts/rstudio-library/README.md @@ -1,6 +1,6 @@ # rstudio-library -![Version: 0.1.36](https://img.shields.io/badge/Version-0.1.36-informational?style=flat-square) ![Type: library](https://img.shields.io/badge/Type-library-informational?style=flat-square) ![AppVersion: 0.1.35](https://img.shields.io/badge/AppVersion-0.1.35-informational?style=flat-square) +![Version: 0.1.37](https://img.shields.io/badge/Version-0.1.37-informational?style=flat-square) ![Type: library](https://img.shields.io/badge/Type-library-informational?style=flat-square) ![AppVersion: 0.1.37](https://img.shields.io/badge/AppVersion-0.1.37-informational?style=flat-square) #### _Helm library helpers for use by official RStudio charts_ diff --git a/charts/rstudio-library/templates/_rbac.tpl b/charts/rstudio-library/templates/_rbac.tpl index 7f3930223..8e13f0a16 100644 --- a/charts/rstudio-library/templates/_rbac.tpl +++ b/charts/rstudio-library/templates/_rbac.tpl @@ -142,6 +142,16 @@ rules: - "pods" verbs: - "get" + rules: + {{/* Connect's native-Go implementation of off-host execution requires permissions to watch endpointslices */}} + - apiGroups: + - "discovery.k8s.io" + resources: + - "endpointslices" + verbs: + - "get" + - "list" + - "watch" --- apiVersion: rbac.authorization.k8s.io/v1 kind: RoleBinding From e0b96333c26d710fd609f2772075f2d300ef3799 Mon Sep 17 00:00:00 2001 From: David Date: Mon, 9 Mar 2026 13:00:36 -0400 Subject: [PATCH 2/4] Update charts/rstudio-connect/values.yaml Co-authored-by: Sam Edwardes --- charts/rstudio-connect/values.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/charts/rstudio-connect/values.yaml b/charts/rstudio-connect/values.yaml index 894f69783..e00491612 100644 --- a/charts/rstudio-connect/values.yaml +++ b/charts/rstudio-connect/values.yaml @@ -331,6 +331,7 @@ kubernetes: # on-top of the overlay base to produce a final job definition before submitting the Job to Kubernetes. # https://docs.posit.co/connect/__unreleased__/admin/appendix/off-host/direct-runner/kubernetes-job-overlays.html # https://kubernetes.io/docs/tasks/manage-kubernetes-objects/kustomization/#bases-and-overlays + # https://kubernetes.io/docs/concepts/workloads/controllers/job/ defaultJobOverlay: apiVersion: batch/v1 kind: Job From 703dce6189bbbfee8e0d0942db54d90428645430 Mon Sep 17 00:00:00 2001 From: David Date: Mon, 9 Mar 2026 13:01:20 -0400 Subject: [PATCH 3/4] Update charts/rstudio-connect/values.yaml Co-authored-by: Sam Edwardes --- charts/rstudio-connect/values.yaml | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/charts/rstudio-connect/values.yaml b/charts/rstudio-connect/values.yaml index e00491612..9a651fc05 100644 --- a/charts/rstudio-connect/values.yaml +++ b/charts/rstudio-connect/values.yaml @@ -318,7 +318,11 @@ kubernetes: # -- The namespace to launch connect-content jobs into. Uses the Release namespace by default namespace: "" defaultInitContainer: - # -- Whether to enable the defaultInitContainer. If disabled, you must ensure that the session components are available another way. + # -- Whether to enable the defaultInitContainer. If disabled, you must ensure + # that the session components are available another way. Changing the default + # setting is an advanced option and not recommended. For more information on + # how Connect uses the session init container refer to + # https://docs.posit.co/connect/admin/appendix/off-host/arch-overview/#runtime-init-container enabled: true # -- The repository to use for the Content InitContainer image repository: ghcr.io/rstudio/rstudio-connect-content-init From c2abc2f555059cabbff054b8ae128047f4e690cd Mon Sep 17 00:00:00 2001 From: David Date: Mon, 9 Mar 2026 13:01:35 -0400 Subject: [PATCH 4/4] Update charts/rstudio-connect/values.yaml Co-authored-by: Sam Edwardes --- charts/rstudio-connect/values.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/charts/rstudio-connect/values.yaml b/charts/rstudio-connect/values.yaml index 9a651fc05..4462ffe23 100644 --- a/charts/rstudio-connect/values.yaml +++ b/charts/rstudio-connect/values.yaml @@ -362,6 +362,7 @@ kubernetes: # Service in Kubernetes. Conceptually this is a similar to a Kustomize base. Connect then applies any required Service configuration # on-top of the overlay base to produce a final Service definition. # https://docs.posit.co/connect/__unreleased__/admin/appendix/off-host/direct-runner/kubernetes-job-overlays.html + # https://kubernetes.io/docs/concepts/services-networking/service/ # https://kubernetes.io/docs/tasks/manage-kubernetes-objects/kustomization/#bases-and-overlays defaultServiceOverlay: {} # apiVersion: v1