Skip to content

Conversation

@joaquimrocha
Copy link
Contributor

@joaquimrocha joaquimrocha commented Nov 3, 2025

Summary

This PR improves the UX when there's already a service using the default port that Headlamp's backend uses.

It also stops showing the dialog when there's already a running headlamp-server process, unless it ran out of ports to try.

Related Issue

Fixes #3447

Steps to Test

Regular conflict:

  1. Run headlamp-server (will use port 4466 by default)
  2. Run the Healdamp app -> should still run fine. Check the output of the electron process (or the dev tools' network requests) to see that the backend was started using port 4467

No ports available because headlamp-server is occupying them:

  1. Run headlamp-server (will use port 4466 by default)
  2. export HEADLAMP_MAX_PORT_ATTEMPTS=1 and run the Healdamp app -> Should show a dialog saying that there are no ports free and offering the user the possibility to terminate the other processes or quit the app

No ports available because another process is occupying them:

  1. Run python3 -m http.server 4466
  2. export HEADLAMP_MAX_PORT_ATTEMPTS=1 and run the Healdamp app -> Should show a dialog saying that there are no ports free but not offer the possibility to kill those processes.

Testing done

  • windows cmd
  • mac zsh
  • linux ubuntu (windows WSL)
  • cd app && npm run star
  • compiled apps still work
  • performance is not negatively impacted (too badly on each platform)

@k8s-ci-robot k8s-ci-robot requested review from illume and skoeva November 3, 2025 18:57
@k8s-ci-robot
Copy link
Contributor

[APPROVALNOTIFIER] This PR is APPROVED

This pull-request has been approved by: joaquimrocha

The full list of commands accepted by this bot can be found here.

The pull request process is described here

Needs approval from an approver in each of these files:

Approvers can indicate their approval by writing /approve in a comment
Approvers can cancel approval by writing /approve cancel in a comment

@k8s-ci-robot k8s-ci-robot added the cncf-cla: yes Indicates the PR's author has signed the CNCF CLA. label Nov 3, 2025
@joaquimrocha joaquimrocha force-pushed the improve-headlamp-port-clashes branch from b28437e to ea98bad Compare November 3, 2025 18:57
@k8s-ci-robot k8s-ci-robot added approved Indicates a PR has been approved by an approver from all required OWNERS files. size/XL Denotes a PR that changes 500-999 lines, ignoring generated files. labels Nov 3, 2025
@joaquimrocha joaquimrocha force-pushed the improve-headlamp-port-clashes branch 2 times, most recently from b921cb2 to 2b17d29 Compare November 4, 2025 13:10
@k8s-ci-robot k8s-ci-robot added needs-rebase Indicates a PR cannot be merged because it has merge conflicts with HEAD. size/XXL Denotes a PR that changes 1000+ lines, ignoring generated files. and removed size/XL Denotes a PR that changes 500-999 lines, ignoring generated files. labels Nov 4, 2025
@joaquimrocha joaquimrocha force-pushed the improve-headlamp-port-clashes branch 2 times, most recently from 1e11684 to 574e504 Compare November 4, 2025 13:16
@k8s-ci-robot k8s-ci-robot removed the needs-rebase Indicates a PR cannot be merged because it has merge conflicts with HEAD. label Nov 4, 2025
@joaquimrocha joaquimrocha force-pushed the improve-headlamp-port-clashes branch from 574e504 to b921cb2 Compare November 4, 2025 13:20
@k8s-ci-robot k8s-ci-robot added size/XL Denotes a PR that changes 500-999 lines, ignoring generated files. and removed size/XXL Denotes a PR that changes 1000+ lines, ignoring generated files. labels Nov 4, 2025
@joaquimrocha
Copy link
Contributor Author

Nevermind those commits I added before. That was a branch name confusion. I have added the right commits back.

if (err.code === 'EADDRINUSE') {
resolve(false);
} else {
resolve(false);
Copy link
Contributor

Choose a reason for hiding this comment

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

seems like this if is not needed

@skoeva skoeva force-pushed the improve-headlamp-port-clashes branch from b921cb2 to f35248e Compare November 4, 2025 21:51
@skoeva
Copy link
Contributor

skoeva commented Nov 4, 2025

rebased against main

Copy link
Contributor

@skoeva skoeva left a comment

Choose a reason for hiding this comment

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

looks like headlamp app runs now with the backend server running in another terminal, but the output for the app start command still shows localhost:4466 as the listen address.

maybe the testing steps could be updated too because I got inconsistent output across different start commands (npm run star sometimes showed 4467 but npm run start consistently shows 4466)

@joaquimrocha
Copy link
Contributor Author

looks like headlamp app runs now with the backend server running in another terminal, but the output for the app start command still shows localhost:4466 as the listen address.

Can you share how you tested?

maybe the testing steps could be updated too because I got inconsistent output across different start commands (npm run star sometimes showed 4467 but npm run start consistently shows 4466)

Are you sure you do not have any instance running when you start the commands?

@illume illume requested a review from Copilot November 5, 2025 11:46
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull Request Overview

This PR implements dynamic backend port configuration for Headlamp's Electron app to support multiple running instances. It introduces automatic port discovery (trying ports 4466-4565 by default), allows the frontend to dynamically connect to the backend's actual port, and adds user dialogs to handle port conflicts with existing Headlamp processes.

  • Adds automatic port selection with fallback when default port 4466 is occupied
  • Refactors frontend URL generation to use runtime port configuration via window.headlampBackendPort
  • Implements port conflict detection and resolution with user prompts
  • Adds internationalization strings for new port-related error dialogs

Reviewed Changes

Copilot reviewed 22 out of 22 changed files in this pull request and generated 7 comments.

Show a summary per file
File Description
frontend/vite.config.ts Adds HEADLAMP_PORT environment variable support for configurable backend port in Vite dev proxy
frontend/src/lib/k8s/api/v2/webSocket.ts Replaces static BASE_WS_URL with getBaseWsUrl() function for runtime port resolution
frontend/src/lib/k8s/api/v2/fetch.ts Updates backendFetch to use getAppUrl() directly instead of static BASE_HTTP_URL
frontend/src/lib/k8s/api/v1/streamingApi.ts Replaces static BASE_WS_URL with getBaseWsUrl() function for WebSocket connections
frontend/src/lib/k8s/api/v1/constants.ts Adds deprecation warning for BASE_HTTP_URL constant
frontend/src/lib/k8s/api/v1/clusterRequests.ts Updates clusterRequest to use getAppUrl() directly instead of static constant
frontend/src/i18n/locales/*/app.json Adds translation placeholders for port conflict error messages
frontend/src/helpers/getAppUrl.ts Implements window.headlampBackendPort support and refactors port selection logic
app/scripts/start.js Passes environment variables to frontend process (minor change)
app/electron/preload.ts Adds IPC channels for backend port communication
app/electron/main.ts Implements port discovery, conflict detection, and injects actualPort into window object

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +592 to +596
if (err.code === 'EADDRINUSE') {
resolve(false);
} else {
resolve(false);
}
Copy link

Copilot AI Nov 5, 2025

Choose a reason for hiding this comment

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

The if-else branches have identical logic (both resolve(false)). This can be simplified to just resolve(false) without the conditional check.

Suggested change
if (err.code === 'EADDRINUSE') {
resolve(false);
} else {
resolve(false);
}
resolve(false);

Copilot uses AI. Check for mistakes.
Copy link
Contributor

Choose a reason for hiding this comment

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

This looks like a bug. Not sure about the solution suggested.

}

// Inject the backend port into the window object
mainWindow?.webContents.executeJavaScript(`window.headlampBackendPort = ${actualPort};`);
Copy link

Copilot AI Nov 5, 2025

Choose a reason for hiding this comment

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

Direct string interpolation in executeJavaScript can be vulnerable to injection if actualPort is not properly validated. Although actualPort is a number in this context, it's better to use JSON.stringify or validate that actualPort is indeed a number before injection.

Suggested change
mainWindow?.webContents.executeJavaScript(`window.headlampBackendPort = ${actualPort};`);
mainWindow?.webContents.executeJavaScript(`window.headlampBackendPort = ${JSON.stringify(actualPort)};`);

Copilot uses AI. Check for mistakes.

// Update the environment variable for the frontend
if (mainWindow) {
mainWindow.webContents.executeJavaScript(`window.headlampBackendPort = ${actualPort};`);
Copy link

Copilot AI Nov 5, 2025

Choose a reason for hiding this comment

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

Direct string interpolation in executeJavaScript can be vulnerable to injection if actualPort is not properly validated. Although actualPort is a number in this context, it's better to use JSON.stringify or validate that actualPort is indeed a number before injection.

Suggested change
mainWindow.webContents.executeJavaScript(`window.headlampBackendPort = ${actualPort};`);
mainWindow.webContents.executeJavaScript(`window.headlampBackendPort = ${JSON.stringify(actualPort)};`);

Copilot uses AI. Check for mistakes.
);
return headlampOnPort.map(p => p.pid);
} catch (error) {
console.error(`Error checking if port ${port} is used by Headlamp:`, error);
Copy link
Contributor

@illume illume Nov 5, 2025

Choose a reason for hiding this comment

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

Remove "Headlamp"? Because it might be called something else.

(and in other parts of this PR/file too)

@illume
Copy link
Contributor

illume commented Nov 5, 2025

I closed the copilot notes which were wrong.

Reviewed it, except for the main.ts electron stuff in detail.

I added a bit of a testing checklist for things I think could be broken.
(Mostly we need to make sure we test each platform. The npm star is important because windows dev mode is basically broken with npm start. We cover container testing in CI, but not compiled apps.)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

approved Indicates a PR has been approved by an approver from all required OWNERS files. cncf-cla: yes Indicates the PR's author has signed the CNCF CLA. size/XL Denotes a PR that changes 500-999 lines, ignoring generated files.

Projects

Development

Successfully merging this pull request may close these issues.

Cannot start headlamp on a multi user machine

5 participants