From b138afcff55f8a8d566b15a0918427a9688073d9 Mon Sep 17 00:00:00 2001 From: Anna Williamson Date: Mon, 20 Jul 2026 11:50:41 -0700 Subject: [PATCH 1/2] feat: direct Connect content pods to Karpenter session node pool 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. --- lib/steps/sites.go | 28 +++++++++++++++++++ lib/steps/sites_test.go | 60 ++++++++++++++++++++++++++++++++++++++++- 2 files changed, 87 insertions(+), 1 deletion(-) diff --git a/lib/steps/sites.go b/lib/steps/sites.go index 4a414f4..7ab5c68 100644 --- a/lib/steps/sites.go +++ b/lib/steps/sites.go @@ -288,6 +288,19 @@ func buildAWSSiteSpec( } } + // 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 + } + return spec } @@ -313,6 +326,21 @@ func sessionTolerations(kc *types.KarpenterConfig) []map[string]interface{} { return nil } +// 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 "" +} + // --- Azure --- type azureSiteParams struct { diff --git a/lib/steps/sites_test.go b/lib/steps/sites_test.go index 0d1a739..0e2f059 100644 --- a/lib/steps/sites_test.go +++ b/lib/steps/sites_test.go @@ -232,7 +232,7 @@ func TestBuildAWSSiteSpecSessionTolerations(t *testing.T) { clusterCfg := types.AWSWorkloadClusterSpec{ KarpenterConfig: &types.KarpenterConfig{ - NodePools: []types.KarpenterNodePool{{SessionTaints: true}}, + NodePools: []types.KarpenterNodePool{{Name: "session-pool", SessionTaints: true}}, }, } spec := buildAWSSiteSpec(params, "arn:aws:secretsmanager:us-east-1:123456789012:secret:db", "20250101", "main", types.SiteConfigSpec{Domain: "d.example.com"}, clusterCfg) @@ -244,6 +244,34 @@ func TestBuildAWSSiteSpecSessionTolerations(t *testing.T) { assert.Equal(t, "Equal", tolerations[0]["operator"]) assert.Equal(t, "session", tolerations[0]["value"]) assert.Equal(t, "NoSchedule", tolerations[0]["effect"]) + + // Connect content pods receive the same toleration plus a nodeSelector + // onto the session node pool. + connect := spec["connect"].(map[string]interface{}) + contentTolerations := connect["contentTolerations"].([]map[string]interface{}) + require.Len(t, contentTolerations, 1) + assert.Equal(t, "workload-type", contentTolerations[0]["key"]) + assert.Equal(t, "Equal", contentTolerations[0]["operator"]) + assert.Equal(t, "session", contentTolerations[0]["value"]) + assert.Equal(t, "NoSchedule", contentTolerations[0]["effect"]) + nodeSelector := connect["contentNodeSelector"].(map[string]string) + assert.Equal(t, "session-pool", nodeSelector["karpenter.sh/nodepool"]) +} + +func TestBuildAWSSiteSpecNoConnectWithoutSessionTaints(t *testing.T) { + params := minimalAWSSiteParams("myworkload", []string{"20250101"}, []string{"main"}) + + clusterCfg := types.AWSWorkloadClusterSpec{ + KarpenterConfig: &types.KarpenterConfig{ + NodePools: []types.KarpenterNodePool{{Name: "general", SessionTaints: false}}, + }, + } + spec := buildAWSSiteSpec(params, "arn:aws:secretsmanager:us-east-1:123456789012:secret:db", "20250101", "main", types.SiteConfigSpec{Domain: "d.example.com"}, clusterCfg) + + _, hasWorkbench := spec["workbench"] + assert.False(t, hasWorkbench, "workbench should not be set without session taints") + _, hasConnect := spec["connect"] + assert.False(t, hasConnect, "connect should not be set without session taints") } // --- sessionTolerations unit tests --- @@ -289,6 +317,36 @@ func TestSessionTolerations(t *testing.T) { }) } +// --- sessionPoolName unit tests --- + +func TestSessionPoolName(t *testing.T) { + t.Run("nil config", func(t *testing.T) { + assert.Equal(t, "", sessionPoolName(nil)) + }) + + t.Run("no session taints", func(t *testing.T) { + kc := &types.KarpenterConfig{ + NodePools: []types.KarpenterNodePool{{Name: "general", SessionTaints: false}}, + } + assert.Equal(t, "", sessionPoolName(kc)) + }) + + t.Run("returns name of first session-tainted pool", func(t *testing.T) { + kc := &types.KarpenterConfig{ + NodePools: []types.KarpenterNodePool{ + {Name: "general", SessionTaints: false}, + {Name: "session-pool", SessionTaints: true}, + }, + } + assert.Equal(t, "session-pool", sessionPoolName(kc)) + }) + + t.Run("empty node pools", func(t *testing.T) { + kc := &types.KarpenterConfig{NodePools: []types.KarpenterNodePool{}} + assert.Equal(t, "", sessionPoolName(kc)) + }) +} + // --- NetworkTrustValue tests --- func TestNetworkTrustValue(t *testing.T) { From e28847455ce91e7b6b696e48bbc9466ea8b770dd Mon Sep 17 00:00:00 2001 From: Anna Williamson Date: Mon, 20 Jul 2026 14:59:35 -0700 Subject: [PATCH 2/2] refactor: dedupe session tolerations lookup in buildAWSSiteSpec --- lib/steps/sites.go | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/lib/steps/sites.go b/lib/steps/sites.go index 7ab5c68..55714d0 100644 --- a/lib/steps/sites.go +++ b/lib/steps/sites.go @@ -281,15 +281,12 @@ func buildAWSSiteSpec( } } - // Karpenter session tolerations. + // Karpenter session placement: allow and direct both Workbench session pods and + // Connect content pods onto the session node pool. if tolerations := sessionTolerations(clusterCfg.KarpenterConfig); len(tolerations) > 0 { spec["workbench"] = map[string]interface{}{ "sessionTolerations": tolerations, } - } - - // 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, }