Skip to content

Add label option for labelling the node - #27

Merged
alicefr merged 3 commits into
mainfrom
node-label
May 27, 2026
Merged

Add label option for labelling the node#27
alicefr merged 3 commits into
mainfrom
node-label

Conversation

@alicefr

@alicefr alicefr commented May 26, 2026

Copy link
Copy Markdown
Collaborator

Summary by Sourcery

Add support for applying custom Kubernetes labels when adding a node and update node join behavior accordingly.

New Features:

  • Allow specifying node labels via a --label / -l flag on the node add CLI command, supporting multiple key=value entries.

Enhancements:

  • Pass user-specified labels through join options so they are applied to the node alongside the default worker role label.
  • Generalize node labeling during cluster join to handle both default role labels and arbitrary custom labels with updated logging.

Tests:

  • Extend multi-node integration tests to cover adding worker nodes with custom labels and to verify that the labels are correctly applied and that nodes are not incorrectly marked as control-plane.

@alicefr

alicefr commented May 26, 2026

Copy link
Copy Markdown
Collaborator Author

@SourceryAI review

@sourcery-ai

sourcery-ai Bot commented May 26, 2026

Copy link
Copy Markdown

Reviewer's Guide

Adds support for passing arbitrary Kubernetes node labels via the node add CLI, propagating them through cluster join, and extends integration tests to validate label behavior on worker nodes.

Sequence diagram for node add label propagation

sequenceDiagram
    actor User
    participant AddCmd as newAddCmd
    participant Parser as parseLabels
    participant Runner as runAdd
    participant Cluster as Cluster.Join
    participant KubeClientFactory as Cluster.newKubeClient
    participant KubeClient as kubeClient

    User->>AddCmd: run `node add --label key=value`
    AddCmd->>Parser: parseLabels(labelFlags)
    Parser-->>AddCmd: labels map[string]string
    AddCmd->>Runner: runAdd(ctx, nodeName, controlPlane, nodeImage, role, memory, maxMemory, labels, logger)
    Runner->>Cluster: Join(ctx, JoinOptions{Labels: labels, ...})
    Cluster->>Cluster: build labels map
    alt needs kube client
        Cluster->>KubeClientFactory: newKubeClient(ctx, cpSSHClient, containerName)
        KubeClientFactory-->>Cluster: kubeClient
        Cluster->>KubeClient: LabelNode(ctx, nodeName, labels)
        KubeClient-->>Cluster: error or nil
    end
Loading

File-Level Changes

Change Details Files
Allow node add CLI to accept multiple key=value labels and pass them into the node-join flow.
  • Introduce a --label/-l StringArray flag on the node add command that can be specified multiple times.
  • Parse label flag values into a map via a new parseLabels helper that validates key=value format and surfaces errors.
  • Extend runAdd to accept a labels map argument and forward it into cluster join options.
internal/cli/node/add.go
Propagate labels through cluster join and apply them on the Kubernetes node along with the worker role label.
  • Extend JoinOptions to include a Labels map that is populated from the CLI.
  • Refactor join logic to construct a labels map that always includes the worker role label for non-control-plane nodes plus any user-specified labels.
  • Update logging and error messages to be generic to labeling, not just worker-role labeling, and skip label calls when there are no labels.
internal/cluster/join.go
Update multinode integration tests to exercise and assert the new label behavior for worker nodes.
  • Change node2 and node3 creation to pass --label env=test1 and --label env=test2 respectively.
  • Simplify role label assertion to only ensure node2 is not labeled as control-plane and confirm it has the expected env label.
  • Add an assertion that node3 has the expected env label after joining and becoming Ready.
test/integration/multinode_test.go

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Hey - I've found 3 issues, and left some high level feedback:

  • In Cluster.Join, user-provided labels currently override the built-in node-role.kubernetes.io/worker label on key collision; consider enforcing or at least documenting a clear precedence rule so callers cannot accidentally remove the worker role label.
  • The runAdd signature change to accept a labels map requires callers to construct and pass this argument; if there are non-CLI callers, consider providing a helper or defaulting logic to reduce the impact on existing code paths.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- In `Cluster.Join`, user-provided labels currently override the built-in `node-role.kubernetes.io/worker` label on key collision; consider enforcing or at least documenting a clear precedence rule so callers cannot accidentally remove the worker role label.
- The `runAdd` signature change to accept a `labels` map requires callers to construct and pass this argument; if there are non-CLI callers, consider providing a helper or defaulting logic to reduce the impact on existing code paths.

## Individual Comments

### Comment 1
<location path="internal/cli/node/add.go" line_range="56-59" />
<code_context>
 }

-func runAdd(ctx context.Context, nodeName, controlPlane, nodeImage, role string, memory int, maxMemory int, logger *logrus.Logger) error {
+func parseLabels(labelFlags []string) (map[string]string, error) {
+	labels := make(map[string]string, len(labelFlags))
+	for _, l := range labelFlags {
+		k, v, ok := strings.Cut(l, "=")
+		if !ok || k == "" {
+			return nil, fmt.Errorf("invalid label %q: must be in key=value format", l)
</code_context>
<issue_to_address>
**suggestion:** Clarify behavior for whitespace and duplicate label keys in parseLabels

This will currently treat `--label 'env =prod'` as key `"env "` (with a trailing space) and will silently overwrite earlier values on duplicate keys (e.g. `--label env=prod --label env=staging`). Consider trimming whitespace on keys/values and either rejecting or clearly warning on duplicate keys so users don’t accidentally override labels.
</issue_to_address>

### Comment 2
<location path="internal/cluster/join.go" line_range="96-97" />
<code_context>
 	if !opts.IsControlPlane {
+		labels["node-role.kubernetes.io/worker"] = "worker"
+	}
+	for k, v := range opts.Labels {
+		labels[k] = v
+	}
+
</code_context>
<issue_to_address>
**question (bug_risk):** Decide whether user labels should be allowed to override system role labels

Because the worker role label is set before merging user labels, a user-supplied `--label node-role.kubernetes.io/worker=...` can override it. If this label is meant to be enforced as a system role, consider either applying user labels first and then setting this label, or explicitly preventing overrides for this key. If overrides are acceptable, this behavior should be clearly documented for users.
</issue_to_address>

### Comment 3
<location path="test/integration/multinode_test.go" line_range="100-103" />
<code_context>
 			Expect(ready).To(BeTrue(), "Node %s should be Ready", node.Name)
 		}

+		By("Verifying custom labels on node3")
+		n3, err := kubeClient.CoreV1().Nodes().Get(context.Background(), node3, metav1.GetOptions{})
+		Expect(err).ToNot(HaveOccurred())
+		Expect(n3.Labels).To(HaveKeyWithValue("env", "test2"), "node3 should have label env=test2")
+
 		By("Verifying DNS entries for all nodes")
</code_context>
<issue_to_address>
**suggestion (testing):** Add a negative/validation test for invalid label syntax at the CLI level

Since `--label` now enforces `key=value` syntax, please add a focused test (ideally unit/CLI-level rather than integration) that covers invalid inputs such as `--label=foo`, `--label==bar`, or an empty key, and verifies the command fails with a clear error. This will explicitly cover the parsing/validation logic and protect against regressions where invalid labels are accepted or mishandled.

Suggested implementation:

```golang
		By("Verifying DNS entries for all nodes")
		hostsFile = helpers.PodmanExec(helpers.DNSContainerName(clusterName), "cat /var/lib/dnsmasq/cluster-hosts")
		Expect(hostsFile).To(ContainSubstring(node1), "cluster-hosts should contain node1")

		By("Rejecting invalid label syntax at the CLI level")
		for _, label := range []string{"foo", "==bar", "=baz"} {
			cmd := exec.Command(
				"kube-spawn",
				"node",
				"create",
				"--cluster-name", clusterName,
				"--role", "worker",
				"--label", label,
			)

			output, err := cmd.CombinedOutput()
			Expect(err).To(HaveOccurred(), "command with invalid label %q should fail", label)
			Expect(string(output)).To(ContainSubstring("invalid label"), "error output for %q should explain invalid label syntax", label)
		}

```

To fully implement the CLI-level validation test and keep it compiling and consistent with the existing test suite, you should also:

1. **Add imports** at the top of `test/integration/multinode_test.go`:
   - Add `os/exec` to the import list:
   ```go
   import (
       // existing imports...
       "os/exec"
   )
   ```

2. **Adjust the CLI invocation if needed**:
   - If `kube-spawn` is not on `PATH` in your test environment, replace `"kube-spawn"` with however you normally locate the binary in tests (for example a helper like `helpers.KubeSpawnBinary()` or using an environment variable).
   - If your CLI subcommand for adding nodes differs (e.g. `node add` instead of `node create`), update the `exec.Command` arguments accordingly.

3. **Align the error assertion with the real message**:
   - If the actual error text for invalid labels is something like `"invalid label syntax: KEY=VALUE expected"`, update the expectation:
   ```go
   Expect(string(output)).To(ContainSubstring("invalid label syntax"))
   ```
   or to whatever substring is stable and user-facing in your command’s validation error.

4. **(Optional, but closer to your comment) Move this into a more focused/CLI-level test file**:
   - For a pure CLI/unit-level test (instead of integration-level), consider:
     - Creating a new test file such as `test/cli/label_validation_test.go`.
     - Using your existing CLI helpers (e.g. helpers that return a `gexec.Session`) so you can assert on exit codes more explicitly.
   - If you do that, you can keep or remove the integration-level check above; the important part is that at least one focused test covers invalid `--label` inputs and asserts the command fails with a clear error.
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Comment thread internal/cli/node/add.go
Comment thread internal/cluster/join.go
Comment thread test/integration/multinode_test.go
alicefr added 3 commits May 27, 2026 06:13
Closes #23

Assisted-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Assisted-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Assisted-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@alicefr
alicefr merged commit 2299e05 into main May 27, 2026
6 checks passed
@alicefr
alicefr deleted the node-label branch June 18, 2026 10:24
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.

1 participant