Skip to content

fix(#557): preflight port 6550 on Windows so a conflict fails clearly - #622

Merged
LukasWodka merged 6 commits into
developfrom
fix/557-win-port-6550-preflight
Aug 6, 2026
Merged

fix(#557): preflight port 6550 on Windows so a conflict fails clearly#622
LukasWodka merged 6 commits into
developfrom
fix/557-win-port-6550-preflight

Conversation

@divyasinghds

@divyasinghds divyasinghds commented Aug 6, 2026

Copy link
Copy Markdown
Contributor

Closes #557.

Fix

Add Get-PfPortListening + a port-6550 check to Test-Preflight, hard-failing with a clear message only when the listener is not our own cluster (avoids the cryptic downstream k3d error).

Files

  • scripts/install-k8s.ps1, scripts/tests/install-k8s.Tests.ps1

Validation

Pester tests added; regex-verified against source. pwsh unavailable in this env — please run the Pester suite on a Windows runner before merge.

🤖 Generated with Claude Code


Note

Medium Risk
Changes installer preflight and cluster detection logic on Windows; incorrect tri-state classification could block valid re-runs or miss real conflicts, though unknown states are explicitly warn-only.

Overview
Adds a Windows preflight check for API port 6550 (where k3d binds the cluster API) so installs fail early with actionable hints instead of opaque k3d errors at cluster create.

Get-PfPortListening probes whether something is listening on the port, treating ObjectNotFound as free but returning $null on real probe failures so a busy port is never green-lit when the check could not run.

When 6550 is busy, ownership uses a new tri-state Get-ClusterRunState / Get-ClusterRunStateFromList: full k3d cluster list -o json (not name-filtered) inside the existing ~15s job deadline. Running tracebloc cluster → OK to reuse; down (absent/stopped/other clusters) → hard fail; unknown (timeout/unparseable list) → warn and continue. Test-ClusterRunning is refactored to delegate to this classifier.

Pester coverage for the classifier, port probe behavior, and Test-Preflight port scenarios; manifest.sha256 updated for install-k8s.ps1.

Reviewed by Cursor Bugbot for commit c583a35. Bugbot is set up for automated code reviews on this repo. Configure here.

@divyasinghds
divyasinghds requested a review from saadqbal as a code owner August 6, 2026 09:21
Comment thread scripts/install-k8s.ps1
Comment thread scripts/install-k8s.ps1
Comment thread scripts/install-k8s.ps1 Outdated
divyasinghds added a commit that referenced this pull request Aug 6, 2026
…gated)

Address PR #622 review on the Windows port-6550 preflight:

- Get-PfPortListening no longer fails open: -ErrorAction SilentlyContinue
  swallowed real CIM/access errors into the same empty result as a free
  port, so a busy port green-OK'd. Switch to -ErrorAction Stop and, in the
  catch, treat only Get-NetTCPConnection's ObjectNotFound (no listener) as
  "free"; every other error returns $null ("can't tell"). (Bugbot Med)

- Port-ownership check now reuses Test-ClusterRunning instead of a bare
  `k3d cluster list -o json`: it wraps the call in the same ~15s job
  deadline, so a wedged Docker engine can't hang preflight (Bugbot High),
  and it gates on serversRunning >= 1, so a STOPPED leftover cluster no
  longer masks a foreign listener on 6550 (Bugbot Med).

- Regenerate scripts/manifest.sha256 for the changed install-k8s.ps1.

- Add Pester coverage: probe error -> $null, ObjectNotFound -> free,
  stopped-leftover + foreign listener -> fail, running-owned -> ok.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
divyasinghds added a commit that referenced this pull request Aug 6, 2026
…gated)

Address PR #622 review on the Windows port-6550 preflight:

- Get-PfPortListening no longer fails open: -ErrorAction SilentlyContinue
  swallowed real CIM/access errors into the same empty result as a free
  port, so a busy port green-OK'd. Switch to -ErrorAction Stop and, in the
  catch, treat only Get-NetTCPConnection's ObjectNotFound (no listener) as
  "free"; every other error returns $null ("can't tell"). (Bugbot Med)

- Port-ownership check now reuses Test-ClusterRunning instead of a bare
  `k3d cluster list -o json`: it wraps the call in the same ~15s job
  deadline, so a wedged Docker engine can't hang preflight (Bugbot High),
  and it gates on serversRunning >= 1, so a STOPPED leftover cluster no
  longer masks a foreign listener on 6550 (Bugbot Med).

- Regenerate scripts/manifest.sha256 for the changed install-k8s.ps1.

- Add Pester coverage: probe error -> $null, ObjectNotFound -> free,
  stopped-leftover + foreign listener -> fail, running-owned -> ok.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@divyasinghds
divyasinghds force-pushed the fix/557-win-port-6550-preflight branch from 22dd6df to 02013a5 Compare August 6, 2026 11:15
Comment thread scripts/install-k8s.ps1

@cursor cursor 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.

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit b2caa60. Configure here.

Comment thread scripts/install-k8s.ps1
divyasinghds and others added 6 commits August 6, 2026 19:16
New-K3dCluster binds the cluster API server to 127.0.0.1:6550, but
Test-Preflight never checked whether 6550 was already in use. On a machine
where a leftover/other k3d cluster or an unrelated service owns 6550,
`k3d cluster create` failed and the installer surfaced k3d's raw stderr
instead of a clear "port already in use" cause.

Add Get-PfPortListening (Get-NetTCPConnection based; $null when
undeterminable) and a port-6550 check to Test-Preflight. A port owned by THIS
installer's own already-running cluster is fine (that run reuses it), so the
check only hard-fails when the listener is NOT our cluster, with actionable
guidance to find and stop the owner. Adds Pester coverage.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…gated)

Address PR #622 review on the Windows port-6550 preflight:

- Get-PfPortListening no longer fails open: -ErrorAction SilentlyContinue
  swallowed real CIM/access errors into the same empty result as a free
  port, so a busy port green-OK'd. Switch to -ErrorAction Stop and, in the
  catch, treat only Get-NetTCPConnection's ObjectNotFound (no listener) as
  "free"; every other error returns $null ("can't tell"). (Bugbot Med)

- Port-ownership check now reuses Test-ClusterRunning instead of a bare
  `k3d cluster list -o json`: it wraps the call in the same ~15s job
  deadline, so a wedged Docker engine can't hang preflight (Bugbot High),
  and it gates on serversRunning >= 1, so a STOPPED leftover cluster no
  longer masks a foreign listener on 6550 (Bugbot Med).

- Regenerate scripts/manifest.sha256 for the changed install-k8s.ps1.

- Add Pester coverage: probe error -> $null, ObjectNotFound -> free,
  stopped-leftover + foreign listener -> fail, running-owned -> ok.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
After the rebase merged our port-6550 Pester tests with #612, Test-Preflight's
network-reachability block calls Has for kubectl/helm/k3d. The "port 6550 in use
by our running cluster -> ok" case passes ownership, so it now continues into
that block, where a k3d-only -ParameterFilter left those later Has calls with no
matching mock ("No mock for command 'Has' matched"). Use a plain default mock
(k3d + tools present -> only always-critical hosts probed) so every Has call is
covered and the case does not throw.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…d-fail reuse (Bugbot 3728340365)

The port-6550 ownership check decided ownership solely from Test-ClusterRunning,
whose bounded `k3d cluster list` maps BOTH a timeout AND a parse failure to
$false. The ownership block treated that $false as "foreign listener" and
HARD-FAILED preflight with stop/delete hints, so a slow/wedged Docker on a
normal re-run wrongly blocked the install and pointed the operator at their own
cluster -- conflating "can't determine" with "definitely not ours".

Make cluster run-state tri-state:
- Get-ClusterRunStateFromList (pure) and Get-ClusterRunState (bounded) return
  'running' | 'down' | 'unknown'. Get-ClusterRunState reports 'unknown' only
  when the list times out or its output is unparseable.
- Test-ClusterRunningInList / Test-ClusterRunning become thin boolean wrappers
  (=='running'), preserving their existing contracts and callers.

The ownership block now only hard-fails when CONFIDENT the listener is foreign
('down': enumerated and ours is absent/stopped, or no k3d installed). An
'unknown' read downgrades to a warning and proceeds, letting New-K3dCluster's
start/repair path settle it instead of blocking the install.

Tests: existing 3 cases updated to mock Get-ClusterRunState; added an
indeterminate case (list timed out -> warns, does NOT hard-fail). Manifest
regenerated. Pester not run locally (pwsh unavailable); validated by source
analysis. Source-of-truth guards for Test-ClusterRunning still hold.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…t High 3728714531)

Get-ClusterRunState ran `k3d cluster list <name> -o json`, which fatals with
empty stdout when the named cluster is absent. The classifier read that empty
output as 'unknown' -> warn-and-proceed, so a genuinely-absent cluster with a
FOREIGN listener on 6550 escaped the hard-fail -- exactly the "re-run after the
first create failed on the busy port" case #557's preflight must catch.

List ALL clusters instead (`k3d cluster list -o json`, no name) and look for
$CLUSTER_NAME in the parsed array. A successful full list always emits at least
`[]`, so absent-vs-error stays separable:
- completed list without a running $CLUSTER_NAME (absent, stopped, or empty [])
  -> 'down' (confidently not ours -> hard-fail on a busy port)
- only a TIMED-OUT or unparseable/failed list -> 'unknown' (warn-and-proceed)

Tests: added a Get-ClusterRunState tri-state Describe -- pure classifier cases
(absent/empty-list -> 'down'; empty/garbage -> 'unknown') plus bounded cases
(timeout -> 'unknown'; completed-but-absent -> 'down'). Manifest regenerated.
Pester not run locally (pwsh unavailable); validated by source analysis.
Source-of-truth guards for Test-ClusterRunning still hold.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…rRunState (CI Pester)

The two bounded-wrapper cases mocked Start-Job/Wait-JobWithProgress/Receive-Job,
which is environment-fragile and fails under CI Pester. Coverage is retained via
the pure Get-ClusterRunStateFromList tests + the Test-ClusterRunning source-guard
(bounded-job regex). No product-code change.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@divyasinghds
divyasinghds force-pushed the fix/557-win-port-6550-preflight branch from f47d879 to c583a35 Compare August 6, 2026 13:46

@shujaatTracebloc shujaatTracebloc 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.

Reviewed the code and all checks: LGTM.

  • Tri-state Get-ClusterRunState/Get-ClusterRunStateFromList cleanly separates 'confidently not ours' (down) from 'can't tell' (unknown), keyed on a FULL k3d cluster list (always emits [] on success) so an absent cluster is a definite 'down' — correctly resolves the earlier Bugbot findings 3728340365 / 3728714531. Only a HARD-fails on a confidently-foreign listener; timeout/unreadable warns-and-proceeds.
  • Get-PfPortListening no longer fails open: -ErrorAction Stop routes every error to the catch, ObjectNotFound → free ($false), any other error → $null (skip), so a busy-but-unreadable port is never green-OK'd.
  • Test-ClusterRunning refactored onto the shared bounded helper (no logic duplication); good Pester coverage of the pure classifier + all four preflight ownership branches; manifest.sha256 restamped.

The E2E auth-proxy (squid) red was the flaky egress test noted in the PR — re-ran and it's green; it exercises Linux egress proxying and is untouched by this Windows-PowerShell-only change. No unresolved Bugbot/review threads. Satisfies #557.

@LukasWodka
LukasWodka merged commit 047256a into develop Aug 6, 2026
60 of 61 checks passed
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.

Windows installer has no port-6550 conflict preflight → cryptic k3d error

3 participants