Skip to content

Comments

feat: add passthrough config mechanism for Connect, Package Manager, and Workbench#75

Merged
ian-flores merged 6 commits intomainfrom
feat-passthrough-config
Feb 20, 2026
Merged

feat: add passthrough config mechanism for Connect, Package Manager, and Workbench#75
ian-flores merged 6 commits intomainfrom
feat-passthrough-config

Conversation

@ian-flores
Copy link
Collaborator

@ian-flores ian-flores commented Feb 9, 2026

Summary

  • Adds escape-hatch passthrough fields to all three product configs (Connect, Package Manager, Workbench) so users can set arbitrary config values without waiting for operator releases
  • Connect & PM: additionalConfig string, raw content appended after the generated config
  • Workbench: additionalConfigs map with config file name keys, raw content appended per file
  • Site CRD propagation for all three products
  • Fixes pre-existing typo: WorkbenchLauncherKubnernetesResourcesConfigSectionWorkbenchLauncherKubernetesResourcesConfigSection

Motivation

The operator currently defines ~25-33% of each product's config values as typed Go struct fields. Adding a new setting requires modifying 4+ files, running codegen, and releasing a new operator version. This passthrough mechanism lets users set any config value immediately.

Design

Connect & Package Manager (gcfg format)

config:
  Server:
    Address: "typed-value.com"
  additionalConfig: |
    [Server]
    DataDir = /custom/path
    [NewSection]
    NewKey = any-value
  • Raw gcfg content appended after the generated config
  • gcfg parsing naturally resolves conflicts: last occurrence wins for scalar values, list values are combined
  • Consistent with how Workbench handles passthrough

Workbench (multiple config files)

config:
  additionalConfigs:
    "rserver.conf": |
      session-timeout-minutes=120
      custom-setting=value
    "rsession.conf": |
      r-cran-repos=https://custom.repo.com
  additionalSessionConfigs:
    "repos.conf": |
      CRAN=https://custom.cran.com
  • Keys are config file names
  • Content appended after generated config
  • Per-file approach handles Workbench's multiple formats (INI, DCF, JSON, newline-delimited)

Test plan

  • Connect: string append, override via gcfg last-write-wins, empty string (3 tests)
  • Package Manager: string append, empty string (2 tests)
  • Workbench: append to existing, new file, empty, nil — for both IniConfig and SessionIniConfig (7 tests)
  • All existing tests pass (go test ./api/core/v1beta1/...)
  • envtest integration tests (require local kubebuilder — pre-existing setup gap)
  • Deploy to test cluster and verify config files are generated correctly

…and Workbench

Add escape-hatch passthrough fields to all three product configs so users
can set arbitrary config values without waiting for operator releases.

Connect & Package Manager:
- Add `additional` map[string]string on config structs
- Keys use "Section.Key" format (e.g., "Server.DataDir")
- Passthrough values override typed fields when both are set
- Rewrite GenerateGcfg() to use intermediate representation for merge

Workbench:
- Add `additionalConfigs` map[string]string on WorkbenchIniConfig and
  WorkbenchSessionIniConfig
- Keys are config file names (e.g., "rserver.conf")
- Raw content appended after generated config per file

Site CRD propagation added for all three products.

Also fixes typo: WorkbenchLauncherKubnernetesResourcesConfigSection
→ WorkbenchLauncherKubernetesResourcesConfigSection
@claude
Copy link

claude bot commented Feb 9, 2026

Claude finished @ian-flores's task —— View job


Review Complete

  • Read review guidelines
  • Create pending review
  • Analyze PR diff and changed files
  • Review API changes (Connect, Package Manager, Workbench configs)
  • Review Controller changes
  • Review generated code
  • Submit review

Submitted a review with 2 issues to address:

  1. Non-deterministic map iteration in Connect/PM config generation - sort keys for consistent output
  2. Potential nil pointer dereference in Workbench controller when propagating additional configs

Overall, the passthrough mechanism is well-designed with good test coverage.

Copy link

@claude claude bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review Summary

This PR implements a well-designed passthrough config mechanism for all three products. The design choices are sound: Section.Key format for gcfg products (Connect/PM) and file-based appending for Workbench's multiple config formats.

Issues to Address

1. Non-deterministic map iteration (Connect & Package Manager)

  • api/core/v1beta1/connect_config.go:310 and api/core/v1beta1/package_manager_config.go:98
  • Iterating over Additional map has non-deterministic ordering in Go
  • When multiple keys target the same new section, their order within that section will vary between runs
  • Suggestion: Sort the keys before processing:
keys := make([]string, 0, len(configStruct.Additional))
for key := range configStruct.Additional {
    keys = append(keys, key)
}
sort.Strings(keys)
for _, key := range keys {
    // process
}

2. Potential nil pointer dereference (Workbench controller)

  • internal/controller/core/site_controller_workbench.go:402 and :407
  • If targetWorkbench.Spec.Config.WorkbenchIniConfig or WorkbenchSessionIniConfig is nil, the assignment will panic
  • Suggestion: Add nil guards:
if site.Spec.Workbench.AdditionalConfigs != nil {
    if targetWorkbench.Spec.Config.WorkbenchIniConfig == nil {
        targetWorkbench.Spec.Config.WorkbenchIniConfig = &v1beta1.WorkbenchIniConfig{}
    }
    targetWorkbench.Spec.Config.WorkbenchIniConfig.AdditionalConfigs = site.Spec.Workbench.AdditionalConfigs
}

Positives

  • Good test coverage: Edge cases like malformed keys, empty maps, nil maps, and override scenarios are well tested
  • Typo fix: WorkbenchLauncherKubnernetesResourcesConfigSectionWorkbenchLauncherKubernetesResourcesConfigSection is a good cleanup
  • Consistent naming: AdditionalConfig for single-file products vs AdditionalConfigs/AdditionalSessionConfigs for Workbench makes the behavior clear
  • SplitN(..., 2) approach: Correctly handles multi-dot keys like Good.Section.Key
  • Shared sectionEntry pattern: Well-structured for maintaining key ordering; duplication between Connect/PM is acceptable for now

Notes

  • The Workbench AdditionalConfigs map iteration (workbench_config.go:173) also has non-deterministic order, but since each filename is independent (not affecting order within a file), this is lower priority
  • CRD updates, deepcopy, and client-go generated code all look correct

…lm chart

- Sort Additional map keys before iterating in Connect and PM
  GenerateGcfg() for deterministic output
- Add nil guards for WorkbenchIniConfig and WorkbenchSessionIniConfig
  in site controller before propagating additional configs
- Regenerate Helm chart CRDs to match kustomize
WorkbenchIniConfig and WorkbenchSessionIniConfig are embedded value
types (not pointers) in WorkbenchConfig, so they cannot be nil.
Remove the incorrect nil guards that caused compilation errors.
Also fix formatting flagged by CI.
@statik
Copy link
Collaborator

statik commented Feb 11, 2026

thanks for creating this! we have long felt the pain of needing this escape hatch. I think this would unblock @brendanhcullen, who needs support for setting Connect OAuth2.RegisterOnFirstLogin = false on academy training sites.

Copy link

@Lytol Lytol left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm curious to hear what you think about my comment regarding just doing string-based config appending vs. the dot-notated keys. If you disagree, I'll defer to you and happily approve.

One other nit, but super subjective. It's a little odd to use additional vs additionalConfigs between PM/Connect and Workbench. I get that they're slightly different because Workbench is multiple config files (and expects file keys because of that), but I personally gravitate toward keeping them as the same key name. 🤷 Ridiculously subjective, so I defer to you.

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For Workbench, you key the file names and then the actual config values are multi-line strings that get appended. I think it represents KISS wonderfully and there's no cleverness/complexity to worry about. Why not do the same for the Package Manager and Connect configs? Skip the file keys (since there's only a single config), but still just have the value as a multi-line string and append to the end of the generated config. You'd get to skip all the code complexity in here with ordering, de-duping, etc. And it "just works" because of how the parsing of the gcfg works... it'll automatically combine "list" values, and it'll keep latest for scalar values. There's cleverness in doing the keys with dot-notation, but I think it makes things harder rather than easier. 🤷

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Switched both Connect and PM to string appending, matching the Workbench pattern. Deleted ~300 lines of section-merge logic in the process.

Also took the naming nit: renamed additionaladditionalConfig across the config structs and JSON tags to align with the existing site-level field names.

…to string appending

Replace the Section.Key map approach with a simple string field that gets
appended verbatim to the generated gcfg output, consistent with how Workbench
already handles passthrough. gcfg naturally handles conflicts (last-write-wins
for scalars, concatenation for lists), so the complex section-merge logic is
no longer needed.

Also renames the field from `additional` to `additionalConfig` for naming
consistency with the site-level spec fields.
Keep both AdditionalConfigs/AdditionalSessionConfigs propagation from this
branch and AuditedJobs propagation from main.
@ian-flores ian-flores requested a review from Lytol February 19, 2026 20:43
@ian-flores ian-flores enabled auto-merge February 19, 2026 20:56
Copy link

@Lytol Lytol left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

❤️ This will make life so much easier!

@ian-flores ian-flores added this pull request to the merge queue Feb 20, 2026
Merged via the queue into main with commit e42c2b9 Feb 20, 2026
4 checks passed
@ian-flores ian-flores deleted the feat-passthrough-config branch February 20, 2026 18:12
ian-flores pushed a commit that referenced this pull request Feb 20, 2026
# [1.13.0](v1.12.0...v1.13.0) (2026-02-20)

### Features

* add passthrough config mechanism for Connect, Package Manager, and Workbench ([#75](#75)) ([e42c2b9](e42c2b9))
stevenolen added a commit to posit-dev/ptd that referenced this pull request Feb 20, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants