posit-dev / posit-dev/team-operator

Add Support for Posit Workbench Session Hooks

Open
#13 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

enhancement
Dominant language
Go
Stars
10
Forks
1
Avg merge
1d 8h
Merged PRs (30d)
4

Description

Problem Statement

Posit Workbench supports Session Hooks - executable shell scripts that run at specific points in a session's lifecycle (start/stop). Currently, PTD does not support configuring or deploying session hooks. This feature is needed to allow customers to inject custom behavior into their Workbench sessions.

Background

Session Hooks require:

  1. Shell scripts present in the filesystem accessible by both Workbench server and session pods
  2. Configuration in rserver.conf:
    • session-hooks-enabled=1
    • session-hooks-path=/path/to/scripts
    • session-hooks-start=script1.sh,script2.sh (comma-separated list)
    • session-hooks-stop=script1.sh,script2.sh (comma-separated list)

Proposed Solution

1. Directory Structure

Admins will create an optional session_hooks/ directory at the workload level:

infra/__work__/<customer>-<env>/
├── session_hooks/           # New directory (optional)
│   ├── start.sh
│   ├── stop.sh
│   ├── custom-start.sh
│   └── cleanup.sh
├── site_main/
│   └── site.yaml
└── site_alt/
    └── site.yaml

All sites in a workload share the same pool of available scripts, but each site configures which scripts to execute.

2. Site Configuration

In each site.yaml, admins configure session hooks:

workbench:
  sessionHooks:
    enabled: true
    path: /opt/rstudio/session-hooks  # Optional, defaults to /opt/rstudio/session-hooks
    start:
      - start.sh
      - custom-start.sh
    stop:
      - stop.sh
      - cleanup.sh
3. Implementation Details
A. Python/Pulumi Changes (clusters step)

Create new module python-pulumi/src/ptd/pulumi_resources/session_hooks.py following the pattern from custom_k8s_resources.py:

  1. Check if session_hooks/ directory exists at workload level
  2. For each site that has workbench.sessionHooks.enabled=true:
    • Validate that all scripts referenced in start and stop arrays exist in the directory
    • Create a Kubernetes ConfigMap named <site-name>-session-hooks in the site's namespace
    • ConfigMap data contains all scripts from session_hooks/ directory
    • Apply ConfigMap before site resources are created

Example ConfigMap:

apiVersion: v1
kind: ConfigMap
metadata:
  name: site-main-session-hooks
  namespace: site-main
  labels:
    posit.team/managed-by: ptd-clusters
data:
  start.sh: |
    #!/bin/bash
    echo "Session starting..."
  stop.sh: |
    #!/bin/bash
    echo "Session stopping..."
B. Site CRD Changes (team-operator/api/core/v1beta1/site_types.go)

Add new struct for session hooks configuration:

type SessionHooksConfig struct {
    Enabled bool     `json:"enabled,omitempty"`
    // +kubebuilder:default="/opt/rstudio/session-hooks"
    Path    string   `json:"path,omitempty"`
    Start   []string `json:"start,omitempty"`
    Stop    []string `json:"stop,omitempty"`
}

Add to InternalWorkbenchSpec:

type InternalWorkbenchSpec struct {
    // ... existing fields ...
    
    // SessionHooks configures session lifecycle hooks
    SessionHooks *SessionHooksConfig `json:"sessionHooks,omitempty"`
}
C. Workbench Config Changes (team-operator/api/core/v1beta1/workbench_config.go)

Add fields to WorkbenchRServerConfig struct:

type WorkbenchRServerConfig struct {
    // ... existing fields ...
    
    SessionHooksEnabled  int      `json:"session-hooks-enabled,omitempty"`
    SessionHooksPath     string   `json:"session-hooks-path,omitempty"`
    SessionHooksStart    []string `json:"session-hooks-start,omitempty"`
    SessionHooksStop     []string `json:"session-hooks-stop,omitempty"`
}

The existing GenerateConfigMap() method will automatically convert these to proper rserver.conf format (comma-separated lists for array fields).

D. Workbench Controller Changes

In the Workbench controller where pod specs are constructed:

  1. Populate RServer Config: When building WorkbenchRServerConfig, check if Site.Spec.Workbench.SessionHooks is configured:

    if site.Spec.Workbench.SessionHooks != nil && site.Spec.Workbench.SessionHooks.Enabled {
        rserverConfig.SessionHooksEnabled = 1
        rserverConfig.SessionHooksPath = site.Spec.Workbench.SessionHooks.Path
        rserverConfig.SessionHooksStart = site.Spec.Workbench.SessionHooks.Start
        rserverConfig.SessionHooksStop = site.Spec.Workbench.SessionHooks.Stop
    }
    
  2. Mount ConfigMap in Workbench Server Pods:

    • Add volume referencing <site-name>-session-hooks ConfigMap
    • Mount at the configured path (default: /opt/rstudio/session-hooks)
    • Use defaultMode: 0755 to make scripts executable
  3. Mount ConfigMap in Session Pods:

    • Add the same volume and volumeMount to session pod templates
    • Ensure sessions can execute the scripts at the same path
4. Example End-to-End Flow
  1. Admin creates infra/__work__/pew01-production/session_hooks/start.sh with custom logic
  2. Admin configures in site_main/site.yaml:
    workbench:
      sessionHooks:
        enabled: true
        start: [start.sh]
    
  3. During ptd ensure <workload-name> --only-steps clusters deployment:
    • Pulumi validates start.sh exists
    • Creates ConfigMap site-main-session-hooks with script content
  4. Team Operator sees SessionHooks config in Site CRD
  5. Workbench controller:
    • Generates rserver.conf with session-hooks-enabled=1, session-hooks-path=/opt/rstudio/session-hooks, session-hooks-start=start.sh
    • Mounts ConfigMap in Workbench server pods at /opt/rstudio/session-hooks/
    • Mounts ConfigMap in session pod templates at same path
  6. When users start sessions, start.sh executes automatically
5. Validation & Error Handling
  • Pulumi validation: Fail deployment if referenced scripts don't exist in session_hooks/ directory
  • ConfigMap check: Team Operator should log warning (but not fail) if ConfigMap doesn't exist when sessionHooks.enabled=true
  • Empty configuration: If sessionHooks is not specified or enabled=false, skip all session hooks logic
6. Testing Plan
  1. Unit tests: Test config generation in workbench_config_test.go
  2. Integration test: Deploy to test cluster (e.g., ganso01) with:
    • Simple start.sh that writes to /tmp/session-started
    • Simple stop.sh that writes to /tmp/session-stopped
    • Verify files appear when starting/stopping sessions
  3. Validation test: Ensure Pulumi fails when referencing non-existent scripts
7. Documentation Updates
  • Add infra/SESSION_HOOKS.md documenting the feature (following CUSTOM_K8S_RESOURCES.md pattern)
  • Update site.yaml schema documentation

Migrated from rstudio/ptd#2549

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start by reading python-pulumi/src/ptd/pulumi_resources/custom_k8s_resources.py and the API definitions in team-operator/api/core/v1beta1/site_types.go and workbench_config.go. Run the existing workbench_config_test.go before tracing the Workbench controller pod construction. Done means validated ConfigMaps, CRD and rserver.conf support, mounts in both pod types, tests, and the requested documentation are covered.

Written by the indexing model from the issue text.

Assessment

Tech stack
go, kubernetes, python, shell
Domain
devops, infrastructure
Issue type
Feature
Difficulty
5/5
Estimated time
Over a week
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
35/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.