Skip to content

feat: direct Connect content pods to Karpenter session node pool#356

Open
amdove wants to merge 2 commits into
mainfrom
connect-content-node-placement
Open

feat: direct Connect content pods to Karpenter session node pool#356
amdove wants to merge 2 commits into
mainfrom
connect-content-node-placement

Conversation

@amdove

@amdove amdove commented Jul 20, 2026

Copy link
Copy Markdown
Contributor

Summary

Extends the existing Workbench session-pod placement capability to Posit Connect content pods.

Today PTD writes spec.workbench.sessionTolerations into the Site CR so Workbench session pods can schedule onto Karpenter session-tainted nodes. This PR adds a parallel spec.connect block so Connect content pods are both:

  • permitted onto the session node pool via contentTolerations (the same workload-type=session:NoSchedule toleration used for Workbench), and
  • directed to the session node pool via contentNodeSelector (karpenter.sh/nodepool: <session pool name>).

The node pool name is resolved at runtime from config (the first Karpenter node pool with session_taints=true); nothing is hardcoded. This is AWS-only, in buildAWSSiteSpec, which is correct since session taints are a Karpenter/AWS feature.

Pairs with a team-operator change adding contentTolerations and contentNodeSelector fields to the Connect site spec; the JSON key names here match those CRD tags exactly.

Changes

  • lib/steps/sites.go: new sessionPoolName helper (mirrors sessionTolerations); buildAWSSiteSpec now emits spec.connect.contentTolerations / contentNodeSelector when a session-tainted pool exists.
  • lib/steps/sites_test.go: unit tests for sessionPoolName and for the new connect block (present with session taints, absent without).

Testing

just test-lib passes.


Companion PR: posit-dev/team-operator#150 — adds the contentTolerations / contentNodeSelector CRD fields this PR populates. These two PRs go together; merge team-operator#150 first.

Extends the existing Workbench sessionTolerations wiring so Connect
content pods receive the same toleration plus a nodeSelector onto the
session node pool, via new spec.connect.contentTolerations /
contentNodeSelector fields.
@amdove
amdove marked this pull request as ready for review July 20, 2026 21:54
@amdove
amdove requested a review from a team as a code owner July 20, 2026 21:54
@amdove
amdove requested a review from Lytol July 20, 2026 21:55
@claude

claude Bot commented Jul 20, 2026

Copy link
Copy Markdown

Claude finished @amdove's task in 1m 50s —— View job


PR Review

  • Read review guidelines
  • Read changed files
  • Analyze the implementation
  • Post review feedback

The implementation is clean and follows existing patterns well. The sessionPoolName helper mirrors sessionTolerations correctly, and the test coverage is solid (nil config, no session taints, first-match semantics, and integration via buildAWSSiteSpec).

Two notes:

  1. Duplicate sessionTolerations call (actionable): The new connect block calls sessionTolerations a second time, but the workbench block immediately above already calls it under the same condition. See the inline comment — merging the two if blocks eliminates the redundant call and makes the co-placement intent more obvious.

  2. sessionPoolName scans the same slice again (minor): Both sessionTolerations and sessionPoolName walk kc.NodePools to find the first session-tainted pool. For now the slice is short and this is fine, but if this pattern grows it might be worth a single helper returning both. Not blocking.

No correctness or security issues found.

Comment thread lib/steps/sites.go Outdated
Comment on lines +291 to +302
// Karpenter content placement: allow and direct Connect content pods onto the session node pool.
if tolerations := sessionTolerations(clusterCfg.KarpenterConfig); len(tolerations) > 0 {
connectSpec := map[string]interface{}{
"contentTolerations": tolerations,
}
if pool := sessionPoolName(clusterCfg.KarpenterConfig); pool != "" {
connectSpec["contentNodeSelector"] = map[string]string{
"karpenter.sh/nodepool": pool,
}
}
spec["connect"] = connectSpec
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Important: sessionTolerations is called twice here with the same argument — once for the workbench block above and once here. Since both blocks are gated on the same condition, they can share a single call. Consider merging the two if blocks:

if tolerations := sessionTolerations(clusterCfg.KarpenterConfig); len(tolerations) > 0 {
    spec["workbench"] = map[string]interface{}{
        "sessionTolerations": tolerations,
    }
    connectSpec := map[string]interface{}{
        "contentTolerations": tolerations,
    }
    if pool := sessionPoolName(clusterCfg.KarpenterConfig); pool != "" {
        connectSpec["contentNodeSelector"] = map[string]string{
            "karpenter.sh/nodepool": pool,
        }
    }
    spec["connect"] = connectSpec
}

Comment thread lib/steps/sites.go
Comment on lines +329 to +342
// sessionPoolName returns the Name of the first node pool that has
// session_taints=true, or an empty string if none. This mirrors the
// selection logic in sessionTolerations.
func sessionPoolName(kc *types.KarpenterConfig) string {
if kc == nil {
return ""
}
for _, pool := range kc.NodePools {
if pool.SessionTaints {
return pool.Name
}
}
return ""
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Suggestion: sessionPoolName iterates kc.NodePools independently, but sessionTolerations already does the same scan to find the first session-tainted pool. If those two functions are always called together (as they are in buildAWSSiteSpec), a single helper that returns both the tolerations and the pool name would eliminate duplicate traversal and keep the "find first session-tainted pool" logic in one place. That said, the slice is expected to be short so this is minor — flag it only if you anticipate this pattern growing.

@amdove

amdove commented Jul 20, 2026

Copy link
Copy Markdown
Contributor Author

Thanks for the review.

  • Duplicate sessionTolerations call — fixed in commit e288474: merged the two if blocks so sessionTolerations is computed once and used for both the workbench and connect spec blocks. just test-lib still passes.
  • Combining sessionPoolName into a single helper — leaving as-is intentionally. Keeping sessionPoolName as a small, independently unit-tested helper reads more clearly than a dual-return function, and the node-pool slice is short so the second traversal is negligible.

@Lytol Lytol left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Awesome, thanks for adding this! I'd make sure to test on an internal workload before merging by the way.

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.

2 participants