Skip to content

Conversation

nicktrn
Copy link
Collaborator

@nicktrn nicktrn commented Aug 15, 2025

No description provided.

Copy link

changeset-bot bot commented Aug 15, 2025

⚠️ No Changeset found

Latest commit: 084cb47

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

Copy link
Contributor

coderabbitai bot commented Aug 15, 2025

Walkthrough

Introduces a new PlacementTagProcessor in packages/core/src/v3/serverOnly/placementTags.ts to convert placement tags into Kubernetes nodeSelector entries, with configurable enabled flag and prefix. Exports it via serverOnly index and adds a placementTag helper there. Removes the prior placementTag helper from packages/core/src/v3/schemas/runEngine.ts. Updates internal-packages/run-engine dequeueSystem to import placementTag from the serverOnly submodule. Refactors apps/supervisor KubernetesWorkloadManager to instantiate and use PlacementTagProcessor for nodeSelector generation, removing its local PlacementConfig, getter, and warning method.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

✨ Finishing Touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch fix/placement-tags

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

CodeRabbit Commands (Invoked using PR/Issue comments)

Type @coderabbitai help to get the list of available commands.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Status, Documentation and Community

  • Visit our Status Page to check the current availability of CodeRabbit.
  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (3)
packages/core/src/v3/utils/placementTags.ts (3)

4-7: Prefer type aliases over interfaces in TS (repo convention)

In this repo we prefer types over interfaces. Consider switching PlacementConfig to a type alias for consistency.

Apply:

-export interface PlacementConfig {
+export type PlacementConfig = {
   enabled: boolean;
   prefix: string;
-}
+}

28-36: Optional: avoid unschedulable pods when tag has no values; warn on conflicting values

Setting a nodeSelector value to an empty string usually won’t match any nodes, potentially making pods unschedulable. Also, silently overwriting an existing nodeSelector value for the same key can be surprising.

Apply:

-    for (const tag of placementTags) {
+    for (const tag of placementTags) {
       const labelKey = `${this.config.prefix}/${tag.key}`;
 
       // Print warnings (if any)
       this.printTagWarnings(tag);
 
       // For now we only support single values via nodeSelector
-      nodeSelector[labelKey] = tag.values?.[0] ?? "";
+      const candidateValue = tag.values?.[0];
+
+      // If no usable value, skip setting the label to avoid unschedulable pods
+      if (candidateValue === undefined || candidateValue === null || candidateValue === "") {
+        continue;
+      }
+
+      // Warn if we're overwriting a different existing value
+      if (labelKey in nodeSelector && nodeSelector[labelKey] !== candidateValue) {
+        this.logger.warn("Overriding existing nodeSelector value for placement tag", {
+          key: labelKey,
+          previous: nodeSelector[labelKey],
+          next: candidateValue,
+        });
+      }
+
+      nodeSelector[labelKey] = candidateValue;
     }

Note: If you adopt the normalizedPrefix fix above, update the labelKey line accordingly.


1-64: Add unit tests for core conversion behavior

Recommend adding focused tests for:

  • disabled=true returns existing selector untouched
  • no tags returns existing selector untouched
  • single tag with single value maps to prefix/key correctly
  • tags with empty values are skipped (if you adopt the optional change) or produce empty string if you keep current behavior
  • multiple values emits a warning and uses the first
  • merging with existing nodeSelector (no overwrite, overwrite warning)
  • blank and trailing-slash prefix normalization

I can scaffold these tests if useful.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 1a88809 and 084cb47.

📒 Files selected for processing (4)
  • apps/supervisor/src/workloadManager/kubernetes.ts (3 hunks)
  • packages/core/src/v3/index.ts (1 hunks)
  • packages/core/src/v3/schemas/runEngine.ts (0 hunks)
  • packages/core/src/v3/utils/placementTags.ts (1 hunks)
💤 Files with no reviewable changes (1)
  • packages/core/src/v3/schemas/runEngine.ts
🧰 Additional context used
📓 Path-based instructions (2)
**/*.{ts,tsx}

📄 CodeRabbit Inference Engine (.github/copilot-instructions.md)

**/*.{ts,tsx}: Always prefer using isomorphic code like fetch, ReadableStream, etc. instead of Node.js specific code
For TypeScript, we usually use types over interfaces
Avoid enums
No default exports, use function declarations

Files:

  • packages/core/src/v3/index.ts
  • packages/core/src/v3/utils/placementTags.ts
  • apps/supervisor/src/workloadManager/kubernetes.ts
{packages/core,apps/webapp}/**/*.{ts,tsx}

📄 CodeRabbit Inference Engine (.github/copilot-instructions.md)

We use zod a lot in packages/core and in the webapp

Files:

  • packages/core/src/v3/index.ts
  • packages/core/src/v3/utils/placementTags.ts
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (23)
  • GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (4, 8)
  • GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (7, 8)
  • GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (8, 8)
  • GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (6, 8)
  • GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (5, 8)
  • GitHub Check: units / internal / 🧪 Unit Tests: Internal (7, 8)
  • GitHub Check: units / internal / 🧪 Unit Tests: Internal (4, 8)
  • GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (2, 8)
  • GitHub Check: units / internal / 🧪 Unit Tests: Internal (8, 8)
  • GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (1, 8)
  • GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (3, 8)
  • GitHub Check: units / internal / 🧪 Unit Tests: Internal (1, 8)
  • GitHub Check: units / internal / 🧪 Unit Tests: Internal (6, 8)
  • GitHub Check: units / internal / 🧪 Unit Tests: Internal (3, 8)
  • GitHub Check: units / internal / 🧪 Unit Tests: Internal (2, 8)
  • GitHub Check: units / internal / 🧪 Unit Tests: Internal (5, 8)
  • GitHub Check: units / packages / 🧪 Unit Tests: Packages (1, 1)
  • GitHub Check: e2e / 🧪 CLI v3 tests (ubuntu-latest - npm)
  • GitHub Check: e2e / 🧪 CLI v3 tests (ubuntu-latest - pnpm)
  • GitHub Check: e2e / 🧪 CLI v3 tests (windows-latest - npm)
  • GitHub Check: e2e / 🧪 CLI v3 tests (windows-latest - pnpm)
  • GitHub Check: typecheck / typecheck
  • GitHub Check: Analyze (javascript-typescript)
🔇 Additional comments (9)
packages/core/src/v3/index.ts (1)

72-72: Public re-export for placement tags looks good

Re-exporting the placement tag utilities from the v3 barrel keeps consumers off deep imports. No concerns.

packages/core/src/v3/utils/placementTags.ts (4)

21-23: Early-return behavior matches previous logic

Returning the existing nodeSelector when disabled or when no tags are provided maintains the previous behavior path. Looks good.


41-55: Structured warnings are appropriate and non-intrusive

Using SimpleStructuredLogger with clear messages for “no values” and “multiple values” is a good balance. No changes needed.


62-64: placementTag helper is fine for ergonomics

Helper matches the current PlacementTag shape and simplifies callers. No issues.


1-64: No stale deep imports found — placementTag is re-exported and used correctly

I searched the repo. The helper remains at packages/core/src/v3/utils/placementTags.ts and is re-exported from packages/core/src/v3/index.ts. The only non-test usage found imports placementTag from "@trigger.dev/core/v3" (no direct deep imports to the old helper path).

Files of interest:

  • packages/core/src/v3/utils/placementTags.ts — placementTag definition (export).
  • packages/core/src/v3/index.ts:72 — re-exports "./utils/placementTags.js".
  • internal-packages/run-engine/src/engine/systems/dequeueSystem.ts — imports placementTag from "@trigger.dev/core/v3" (import at ~line 4; usage at ~line 477).
apps/supervisor/src/workloadManager/kubernetes.ts (4)

8-8: Good: using the new PlacementTagProcessor from core

Import via the v3 barrel avoids deep imports and keeps the surface area consistent.


21-21: Private processor field: good encapsulation

Keeps placement logic centralized and testable.


25-28: Confirm env types for processor config

Ensure env.PLACEMENT_TAGS_ENABLED is a boolean and env.PLACEMENT_TAGS_PREFIX is a string (not undefined). If env is stringly-typed, coerce/parse here before passing to the processor.

If needed, consider:

this.placementTagProcessor = new PlacementTagProcessor({
  enabled: Boolean(env.PLACEMENT_TAGS_ENABLED),
  prefix: env.PLACEMENT_TAGS_PREFIX ?? "",
});

Note: With the normalizedPrefix fix in core, empty or trailing slashes are handled gracefully.


41-44: Delegation to processor simplifies podSpec construction

The new call cleanly merges existing nodeSelector with placement-generated labels and centralizes warnings. Assuming the core fix for prefix normalization, this should be robust.

@nicktrn nicktrn merged commit 26d4d08 into main Aug 15, 2025
28 of 29 checks passed
@nicktrn nicktrn deleted the fix/placement-tags branch August 15, 2025 16:19
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (5)
internal-packages/run-engine/src/engine/systems/dequeueSystem.ts (1)

4-5: Use a type-only import for DequeuedMessage; keep RetryOptions as a value import

DequeuedMessage is only used for typing (via satisfies). This avoids emitting unnecessary runtime imports and aligns with existing style (Line 1 already uses import type).

-import { DequeuedMessage, RetryOptions } from "@trigger.dev/core/v3";
+import type { DequeuedMessage } from "@trigger.dev/core/v3";
+import { RetryOptions } from "@trigger.dev/core/v3";
 import { placementTag } from "@trigger.dev/core/v3/serverOnly";
packages/core/src/v3/serverOnly/placementTags.ts (4)

4-7: Prefer type alias over interface in core

Per repo guidelines, we usually use types over interfaces in TypeScript.

-export interface PlacementConfig {
-  enabled: boolean;
-  prefix: string;
-}
+export type PlacementConfig = {
+  enabled: boolean;
+  prefix: string;
+};

25-36: Normalize prefix and avoid generating invalid label keys; skip empty values instead of assigning ""

  • If prefix is empty or contains a trailing slash, ${prefix}/${key} can produce invalid keys like /key or a double slash.
  • Assigning an empty string for a nodeSelector value can unintentionally render pods unschedulable. Better to skip tags with no usable value.
-    // Convert placement tags to nodeSelector labels
-    for (const tag of placementTags) {
-      const labelKey = `${this.config.prefix}/${tag.key}`;
-
-      // Print warnings (if any)
-      this.printTagWarnings(tag);
-
-      // For now we only support single values via nodeSelector
-      nodeSelector[labelKey] = tag.values?.[0] ?? "";
-    }
+    // Convert placement tags to nodeSelector labels
+    const normalizedPrefix = this.#normalizePrefix(this.config.prefix);
+    for (const tag of placementTags) {
+      const labelKey = normalizedPrefix ? `${normalizedPrefix}/${tag.key}` : tag.key;
+
+      // Print warnings (if any)
+      this.printTagWarnings(tag);
+
+      // For now we only support single values via nodeSelector
+      const firstValue = tag.values?.[0];
+      if (typeof firstValue !== "string" || firstValue.length === 0) {
+        // Skip tags with no usable value to avoid creating a selector that never matches
+        continue;
+      }
+      nodeSelector[labelKey] = firstValue;
+    }

Add the helper (outside the selected range, near other privates):

// Private helper to normalize/clean the prefix
#normalizePrefix(prefix: string): string {
  const p = (prefix ?? "").trim().replace(/\/+$/, "");
  return p.length > 0 ? p : "";
}

41-49: Reduce potential PII in logs; log minimal tag metadata

Passing the full tag object to logs could leak values. Prefer logging key and counts. Keep warnings but trim payload.

   private printTagWarnings(tag: PlacementTag) {
     if (!tag.values || tag.values.length === 0) {
       // No values provided
-      this.logger.warn("Placement tag has no values, using empty string", tag);
+      this.logger.warn("Placement tag has no values; skipping", {
+        key: tag.key,
+        valuesCount: tag.values?.length ?? 0,
+      });
     } else if (tag.values.length > 1) {
       // Multiple values provided
-      this.logger.warn("Placement tag has multiple values, only using first one", tag);
+      this.logger.warn("Placement tag has multiple values; only using first", {
+        key: tag.key,
+        valuesCount: tag.values.length,
+      });
     }
   }

12-13: Optional: validate config on construction to catch misconfiguration early

When enabled but prefix is empty/invalid, warn once at construction time to aid debugging.

I can add a small zod schema for PlacementConfig and validate here. Want me to follow up with a patch?

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 084cb47 and 77424d5.

📒 Files selected for processing (4)
  • apps/supervisor/src/workloadManager/kubernetes.ts (3 hunks)
  • internal-packages/run-engine/src/engine/systems/dequeueSystem.ts (1 hunks)
  • packages/core/src/v3/serverOnly/index.ts (1 hunks)
  • packages/core/src/v3/serverOnly/placementTags.ts (1 hunks)
🧰 Additional context used
📓 Path-based instructions (2)
**/*.{ts,tsx}

📄 CodeRabbit Inference Engine (.github/copilot-instructions.md)

**/*.{ts,tsx}: Always prefer using isomorphic code like fetch, ReadableStream, etc. instead of Node.js specific code
For TypeScript, we usually use types over interfaces
Avoid enums
No default exports, use function declarations

Files:

  • packages/core/src/v3/serverOnly/index.ts
  • packages/core/src/v3/serverOnly/placementTags.ts
  • internal-packages/run-engine/src/engine/systems/dequeueSystem.ts
  • apps/supervisor/src/workloadManager/kubernetes.ts
{packages/core,apps/webapp}/**/*.{ts,tsx}

📄 CodeRabbit Inference Engine (.github/copilot-instructions.md)

We use zod a lot in packages/core and in the webapp

Files:

  • packages/core/src/v3/serverOnly/index.ts
  • packages/core/src/v3/serverOnly/placementTags.ts
🧬 Code Graph Analysis (2)
packages/core/src/v3/serverOnly/placementTags.ts (1)
packages/core/src/v3/utils/structuredLogger.ts (1)
  • SimpleStructuredLogger (21-102)
apps/supervisor/src/workloadManager/kubernetes.ts (1)
packages/core/src/v3/serverOnly/placementTags.ts (1)
  • PlacementTagProcessor (9-50)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (21)
  • GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (8, 8)
  • GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (1, 8)
  • GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (4, 8)
  • GitHub Check: e2e / 🧪 CLI v3 tests (windows-latest - pnpm)
  • GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (2, 8)
  • GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (5, 8)
  • GitHub Check: units / internal / 🧪 Unit Tests: Internal (5, 8)
  • GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (6, 8)
  • GitHub Check: units / internal / 🧪 Unit Tests: Internal (6, 8)
  • GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (3, 8)
  • GitHub Check: units / internal / 🧪 Unit Tests: Internal (7, 8)
  • GitHub Check: units / internal / 🧪 Unit Tests: Internal (8, 8)
  • GitHub Check: units / internal / 🧪 Unit Tests: Internal (1, 8)
  • GitHub Check: units / internal / 🧪 Unit Tests: Internal (4, 8)
  • GitHub Check: units / internal / 🧪 Unit Tests: Internal (2, 8)
  • GitHub Check: units / internal / 🧪 Unit Tests: Internal (3, 8)
  • GitHub Check: units / packages / 🧪 Unit Tests: Packages (1, 1)
  • GitHub Check: e2e / 🧪 CLI v3 tests (ubuntu-latest - npm)
  • GitHub Check: typecheck / typecheck
  • GitHub Check: e2e / 🧪 CLI v3 tests (windows-latest - npm)
  • GitHub Check: e2e / 🧪 CLI v3 tests (ubuntu-latest - pnpm)
🔇 Additional comments (5)
packages/core/src/v3/serverOnly/index.ts (1)

9-9: Expose server-only placement tags API via index: LGTM

Re-export looks correct and keeps the server-only surface tidy.

internal-packages/run-engine/src/engine/systems/dequeueSystem.ts (1)

5-5: All placementTag imports point to serverOnly — no action required

Verified: placementTag is defined under packages/core/src/v3/serverOnly and the only import of placementTag is from "@trigger.dev/core/v3/serverOnly". No imports of placementTag from "@trigger.dev/core/v3" (non-serverOnly) were found.

Files checked (relevant hits):

  • packages/core/src/v3/serverOnly/placementTags.ts — export function placementTag(...) (definition)
  • internal-packages/run-engine/src/engine/systems/dequeueSystem.ts — import { placementTag } from "@trigger.dev/core/v3/serverOnly"; (line 5) and usage at line 478: placementTags: [placementTag("paid", ...)]
packages/core/src/v3/serverOnly/placementTags.ts (1)

56-58: Helper is straightforward: LGTM

The minimal builder promotes consistent tag shape and pairs well with the processor.

apps/supervisor/src/workloadManager/kubernetes.ts (2)

8-8: Server-only import: LGTM

Correctly sources the processor from the server-only module.


41-44: Delegating nodeSelector generation to the processor: LGTM

Centralizes logic and keeps K8s manager lean.

Comment on lines +21 to 29
private placementTagProcessor: PlacementTagProcessor;

constructor(private opts: WorkloadManagerOptions) {
this.k8s = createK8sApi();
this.placementTagProcessor = new PlacementTagProcessor({
enabled: env.PLACEMENT_TAGS_ENABLED,
prefix: env.PLACEMENT_TAGS_PREFIX,
});

Copy link
Contributor

Choose a reason for hiding this comment

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

💡 Verification agent

🧩 Analysis chain

Make the processor field readonly; ensure env prefix is set when enabled

  • Marking the field readonly communicates intent and prevents accidental reassignment.
  • If PLACEMENT_TAGS_ENABLED is true but the prefix is missing or has a trailing slash, label keys could be malformed unless normalized in the processor (recommended). Validate or normalize centrally.
-  private placementTagProcessor: PlacementTagProcessor;
+  private readonly placementTagProcessor: PlacementTagProcessor;

To verify env wiring and defaults:


🏁 Script executed:

#!/bin/bash
# Check env.ts/js for PLACEMENT_TAGS_ENABLED/PLACEMENT_TAGS_PREFIX definitions and defaults
fd -t f -g '*env.*' apps | xargs -I{} rg -n -C2 'PLACEMENT_TAGS_(ENABLED|PREFIX)' {}

Length of output: 272


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "---- Search: placementTagProcessor usages ----"
rg -n --hidden -S 'placementTagProcessor' || true

echo
echo "---- Search: PlacementTagProcessor symbols ----"
rg -n --hidden -S 'PlacementTagProcessor' || true

echo
echo "---- Search: PLACEMENT_TAGS_* env defs ----"
rg -n --hidden -S 'PLACEMENT_TAGS_(ENABLED|PREFIX)' || true

echo
echo "---- Show files that define PLACEMENT_TAGS_PREFIX (first 200 lines) ----"
files=$(rg -l --hidden -S 'PLACEMENT_TAGS_PREFIX' || true)
if [ -n "$files" ]; then
  for f in $files; do
    echo "---- File: $f ----"
    sed -n '1,200p' "$f"
    echo
  done
else
  echo "No files found defining PLACEMENT_TAGS_PREFIX"
fi

echo
echo "---- Show files that define PlacementTagProcessor (first 200 lines) ----"
files2=$(rg -l --hidden -S 'class PlacementTagProcessor|function PlacementTagProcessor|new PlacementTagProcessor' || true)
if [ -n "$files2" ]; then
  for f in $files2; do
    echo "---- File: $f ----"
    sed -n '1,200p' "$f"
    echo
  done
else
  echo "No files found with obvious PlacementTagProcessor definition; showing files that reference it instead"
  rg -n --hidden -S 'PlacementTagProcessor' || true
fi

Length of output: 24031


Make placementTagProcessor readonly and normalize/validate PLACEMENT_TAGS_PREFIX

Quick summary: I verified that apps/supervisor/src/env.ts provides a default PLACEMENT_TAGS_PREFIX ("node.cluster.x-k8s.io") but PlacementTagProcessor uses the prefix directly and does not normalize or validate it. Also the field in KubernetesWorkloadManager is not readonly. Recommend making the field readonly and normalizing/validating the prefix centrally.

Files to change:

  • apps/supervisor/src/workloadManager/kubernetes.ts — mark the processor field readonly.
  • packages/core/src/v3/serverOnly/placementTags.ts — normalize (trim trailing slashes) and validate prefix when enabled; use the normalized prefix when building label keys.

Suggested diffs:

Make field readonly in KubernetesWorkloadManager:

-  private placementTagProcessor: PlacementTagProcessor;
+  private readonly placementTagProcessor: PlacementTagProcessor;

Normalize/validate prefix in PlacementTagProcessor (outline):

-export class PlacementTagProcessor {
-  private readonly logger = new SimpleStructuredLogger("placement-tag-processor");
-
-  constructor(private readonly config: PlacementConfig) {}
+export class PlacementTagProcessor {
+  private readonly logger = new SimpleStructuredLogger("placement-tag-processor");
+  private readonly config: PlacementConfig;
+  private readonly prefix: string;
+
+  constructor(config: PlacementConfig) {
+    this.config = config;
+    // normalize prefix: remove trailing slashes
+    this.prefix = (config.prefix ?? "").replace(/\/+$/, "");
+    if (config.enabled && this.prefix.length === 0) {
+      throw new Error(
+        "PlacementTagProcessor: PLACEMENT_TAGS_PREFIX must be a non-empty string when PLACEMENT_TAGS_ENABLED is true"
+      );
+    }
+  }

And replace label construction in convertToNodeSelector:

-      const labelKey = `${this.config.prefix}/${tag.key}`;
+      const labelKey = `${this.prefix}/${tag.key}`;

Note: apps/supervisor/src/env.ts currently defines:

  • PLACEMENT_TAGS_ENABLED: BoolEnv.default(false)
  • PLACEMENT_TAGS_PREFIX: z.string().default("node.cluster.x-k8s.io")

Even with that default, normalizing/validating is recommended because the prefix can be overridden via environment and a trailing slash or empty value would produce malformed label keys.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
private placementTagProcessor: PlacementTagProcessor;
constructor(private opts: WorkloadManagerOptions) {
this.k8s = createK8sApi();
this.placementTagProcessor = new PlacementTagProcessor({
enabled: env.PLACEMENT_TAGS_ENABLED,
prefix: env.PLACEMENT_TAGS_PREFIX,
});
private readonly placementTagProcessor: PlacementTagProcessor;
constructor(private opts: WorkloadManagerOptions) {
this.k8s = createK8sApi();
this.placementTagProcessor = new PlacementTagProcessor({
enabled: env.PLACEMENT_TAGS_ENABLED,
prefix: env.PLACEMENT_TAGS_PREFIX,
});

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