Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Port manager #42

Merged
merged 27 commits into from
Nov 9, 2023
Merged

Port manager #42

merged 27 commits into from
Nov 9, 2023

Conversation

camden11
Copy link
Contributor

@camden11 camden11 commented Sep 29, 2023

Description and Context

See https://git.hubteam.com/HubSpot/hubspot-cli-issues/issues/462

This PR is a proof of concept for the Port Manager Server, which seeks to address the above issue by creating a separate service that the CLI interfaces to assign ports to dev servers, assuring that no port conflicts happen when running multiple servers. The Port Manager Server also provides an API for the UIExtensionManager (and future extension managers) to interface with from the UI, telling it which ports to look for dev servers on.

The bulk of the functionality of this PR is in the following three files

  • utils/PortManagerServer.ts - Class that handles spinning the the express server
  • utils/detectPort.ts - Logic for assigning ports based on what's available
  • lib/portManager.ts - set of functions that the CLI (and other repos, potentially) can use to easily interact with the port manager.

Related port manager PRs:
HubSpot/hubspot-cli#936
https://git.hubteam.com/HubSpot/ui-extensibility/pull/1566

TODO

  • Figure out what port to run the port manager on

Who to Notify

@brandenrodgers @kemmerle

@camden11 camden11 changed the title WIP: Port manager POC: Port manager Oct 4, 2023
@camden11 camden11 marked this pull request as ready for review October 4, 2023 18:13
export const MAX_PORT_NUMBER = 65535;

// TODO: What's a good port for the port manager?
export const PORT_MANAGER_SERVER_PORT = 12345;
Copy link
Contributor

Choose a reason for hiding this comment

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

Maybe 8080? That's a classic one. Maybe we could also make it configurable?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

8080 is good. Making it configurable could be tricky because the UI needs to know where to look for it

this.app.use(express.json());
this.app.use(cors());
this.setupRoutes();
this.server = this.app.listen(PORT_MANAGER_SERVER_PORT, () =>
Copy link
Contributor

Choose a reason for hiding this comment

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

You may want to pull some of the logic from here that forces the rest of the process to wait to make sure that the server started up successfully. It makes it easier to catch + log errors in the calling function

this.app.post('/close', this.closeServer);
}

setPort(instanceId: string, port: number) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Should this protect against initializing duplicate instanceId's?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

setPort is only called by assignPortToServer, which does protect against duplicates

export const MIN_PORT_NUMBER = 1024;
export const MAX_PORT_NUMBER = 65535;

// TODO: What's a good port for the port manager?
Copy link
Contributor

Choose a reason for hiding this comment

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

lingering todo (unless you still plan to revisit this)

lang/en.lyaml Outdated
@@ -136,6 +136,12 @@ en:
processFieldsJs:
converting: 'Converting "{{ src }}" to "{{ dest }}".'
converted: 'Finished converting "{{ src }}" to "{{ dest }}".'
utils:
Copy link
Contributor

Choose a reason for hiding this comment

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

I'm realizing we're going to hit some merge conflicts with all of the WIP PR's and my translations spike PR

lang/en.lyaml Outdated
started: "PortManagerServer running on port {{ port }}"
setPort: "Server with instanceId {{ instanceId }} assigned to port {{ port }}"
deletedPort: "Server with instanceId {{ instanceId }} unassigned from port {{ port }}"
close: "PortManagerServer shutting down."
Copy link
Contributor

Choose a reason for hiding this comment

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

I wonder if we should refer to this differently in logs. End users probably don't need to know about portManagerServer

Copy link
Contributor Author

Choose a reason for hiding this comment

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

That's true, though these are all debug logs so theoretically end users wouldn't be seeing them. Seemed helpful if anyone was ever debugging something related to this or the inner workings became relevant to their workflow

Copy link
Contributor

Choose a reason for hiding this comment

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

Ah ok, I was thinking that they were standard logs. That makes sense 👌


export function detectPort(port?: number | null): Promise<number> {
if (port && (port < MIN_PORT_NUMBER || port > MAX_PORT_NUMBER)) {
throw new Error('Port must be between 1024 and 65535');
Copy link
Contributor

Choose a reason for hiding this comment

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

We should put this copy in the lang file

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Ah good call, I guess I'll hold off on that until the lang stuff is merged

@camden11 camden11 changed the title POC: Port manager Port manager Nov 8, 2023

const i18nKey = 'utils.PortManagerServer';

class PortManagerServer {
Copy link
Contributor

Choose a reason for hiding this comment

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

Not a blocker - I wonder if we should even bother having a models/ folder in this repo. I think we have a few other classes in here that also aren't in that folder.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yeah 🤷 . Was left over from cli-lib but definitely don't think we need to make the distinction. I'm down to remove in the future

});
};

getServerPortByInstanceId = (req: Request, res: Response): void => {
Copy link
Contributor

Choose a reason for hiding this comment

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

Should the return on this be Promise?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Don't thinks so, this isn't async. Express knows how to handle it as is

@@ -0,0 +1,90 @@
import net, { AddressInfo } from 'net';
Copy link
Contributor

Choose a reason for hiding this comment

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

We may need to include the copyright info in here per the license: https://github.com/node-modules/detect-port/blob/master/LICENSE

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Good call. I could have sworn I had already done this

@brandenrodgers
Copy link
Contributor

I'm ok with it being a fast-follow, but we should probably also get some unit tests in place for these new utils. This will be a good start to us getting in the habit of making sure everything new is tested.

lang/en.json Outdated
},
"detectPort": {
"errors": {
"invalidPort": "Port must be between 1024 and 65535"
Copy link
Contributor

Choose a reason for hiding this comment

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

It might be useful to have these min and max numbers be dynamic so changing the variable values will automatically change the message here.

lang/en.json Outdated
"duplicateInstance": "Failed to start PortManagerServer. An instance of PortManagerServer is already running.",
"404": "Could not find a server with instanceId {{ instanceId }}",
"409": "Failed to assign port. Server with instanceId {{ instanceId }} is already running on port {{ port }}",
"400": "Invalid port requested. Port must be between 1024 and 65535."
Copy link
Contributor

Choose a reason for hiding this comment

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

Same dynamic suggestion here

@camden11 camden11 merged commit 7faaeb2 into main Nov 9, 2023
1 check 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.

2 participants