Skip to content

feat: add endpoint to get listening ports in agent #4260

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

Merged
merged 13 commits into from
Oct 6, 2022

Conversation

deansheather
Copy link
Member

@deansheather deansheather commented Sep 29, 2022

Similar to #1824 but done through a HTTP server and a HTTP endpoint rather than a raw TCP protocol.

This will be used for showing a list of currently listening ports in the port-forwarding popup in the dashboard.

  • Adds new internal TCP port 4 on tailnet agent which serves a HTTP server called the "statistics server", we will probably add resource usage and other handlers here as time goes on. I opted for a HTTP server as it feels simpler to me than implementing request/response over TCP by hand, and we don't need streaming support as ports don't change super often.
  • Add route /api/v0/listening-ports to the agent statistics server which returns all currently listening TCP ports. This result is cached in the agent for 1 second. Ports above 9 (inclusive) will be returned.
  • Enforces minimum user port 9 (inclusive) on port forwarding traffic via devurls. This is not enforced for CLI port-forwarding intentionally.
  • Add route /api/v2/workspaceagents/{id}/listening-ports on coderd to get listening ports. This just connects to the corresponding agent on the statistics server and forwards the response.
  • Add tests for listening-ports endpoint.
  • Add tests for minimum port 9 devurls.

Co-authored-by: Asher asher@coder.com

@deansheather deansheather marked this pull request as ready for review September 29, 2022 12:02
@deansheather deansheather requested a review from a team as a code owner September 29, 2022 12:07
Comment on lines +230 to +247
agentConn, release, err := api.workspaceAgentCache.Acquire(r, workspaceAgent.ID)
if err != nil {
httpapi.Write(ctx, rw, http.StatusInternalServerError, codersdk.Response{
Message: "Internal error dialing workspace agent.",
Detail: err.Error(),
})
return
}
defer release()

portsResponse, err := agentConn.ListeningPorts(ctx)
if err != nil {
httpapi.Write(ctx, rw, http.StatusInternalServerError, codersdk.Response{
Message: "Internal error fetching listening ports.",
Detail: err.Error(),
})
return
}
Copy link
Member

Choose a reason for hiding this comment

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

What do you think about pushing port data instead of pulling it? This seems like it could lead to a lot of workspace traffic with page reloads, and we've learned from v1 that dynamic data like this (especially on main pages) can be sketchy.

Copy link
Member Author

Choose a reason for hiding this comment

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

This will only be loaded when people click the port forward button. There will be more traffic if workspaces push it to coderd rather than if we load it when the user clicks the button which won't be that often.

Copy link
Contributor

Choose a reason for hiding this comment

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

To add to this, I talked with dean and I think the syscall overhead is way too much for a responsive push system for such a small feature like this. Because we cache the response for 1 second on the agent side this already has built in protection for the workspace.

@deansheather deansheather requested a review from f0ssel October 4, 2022 14:01
}

func (c *AgentConn) ListeningPorts(ctx context.Context) (ListeningPortsResponse, error) {
req, err := http.NewRequestWithContext(ctx, http.MethodGet, "http://agent-stats/api/v0/listening-ports", nil)
Copy link
Contributor

Choose a reason for hiding this comment

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

Can we make this something less magical and explains to the reader that this calls out to localhost:4?

Copy link
Member Author

Choose a reason for hiding this comment

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

yep! done by making a doStatisticsRequest method that does the magic for you instead

Comment on lines +25 to +26
lp := &listeningPortsHandler{}
r.Get("/api/v0/listening-ports", lp.handler)
Copy link
Member

Choose a reason for hiding this comment

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

Instead of creating an API for statistics, we should just handle this single port for right now on a handler. We probably don't even need Chi and can just directly serve it.

Copy link
Member Author

Choose a reason for hiding this comment

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

@f0ssel and I would like to convert the stats websocket code in the agent to be in this webserver too, which is why it has path based routing. I've only reserved 8 ports for Coder so we can't just create a new http server for each function

@deansheather deansheather merged commit 1386465 into main Oct 6, 2022
@deansheather deansheather deleted the dean/listening-ports branch October 6, 2022 12:38
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.

3 participants