Skip to content

Commit d10712d

Browse files
committed
Merge branch 'main' into triallicense
2 parents 4df3f35 + fe7c9f8 commit d10712d

File tree

107 files changed

+1628
-978
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

107 files changed

+1628
-978
lines changed

.github/workflows/coder.yaml

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,6 @@ on:
44
push:
55
branches:
66
- main
7-
tags:
8-
- "*"
97

108
pull_request:
119

@@ -36,7 +34,7 @@ jobs:
3634
- name: Checkout
3735
uses: actions/checkout@v2
3836
- name: typos-action
39-
uses: crate-ci/typos@master
37+
uses: crate-ci/typos@v1.12.8
4038
with:
4139
config: .github/workflows/typos.toml
4240
- name: Fix Helper

.github/workflows/dogfood.yaml

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,6 @@ on:
44
push:
55
branches:
66
- main
7-
tags:
8-
- "*"
97
paths:
108
- "dogfood/**"
119
pull_request:

agent/agent.go

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import (
1010
"fmt"
1111
"io"
1212
"net"
13+
"net/http"
1314
"net/netip"
1415
"os"
1516
"os/exec"
@@ -206,6 +207,7 @@ func (a *agent) runTailnet(ctx context.Context, derpMap *tailcfg.DERPMap) {
206207
go a.sshServer.HandleConn(a.stats.wrapConn(conn))
207208
}
208209
}()
210+
209211
reconnectingPTYListener, err := a.network.Listen("tcp", ":"+strconv.Itoa(codersdk.TailnetReconnectingPTYPort))
210212
if err != nil {
211213
a.logger.Critical(ctx, "listen for reconnecting pty", slog.Error(err))
@@ -240,6 +242,7 @@ func (a *agent) runTailnet(ctx context.Context, derpMap *tailcfg.DERPMap) {
240242
go a.handleReconnectingPTY(ctx, msg, conn)
241243
}
242244
}()
245+
243246
speedtestListener, err := a.network.Listen("tcp", ":"+strconv.Itoa(codersdk.TailnetSpeedtestPort))
244247
if err != nil {
245248
a.logger.Critical(ctx, "listen for speedtest", slog.Error(err))
@@ -261,6 +264,31 @@ func (a *agent) runTailnet(ctx context.Context, derpMap *tailcfg.DERPMap) {
261264
}()
262265
}
263266
}()
267+
268+
statisticsListener, err := a.network.Listen("tcp", ":"+strconv.Itoa(codersdk.TailnetStatisticsPort))
269+
if err != nil {
270+
a.logger.Critical(ctx, "listen for statistics", slog.Error(err))
271+
return
272+
}
273+
go func() {
274+
defer statisticsListener.Close()
275+
server := &http.Server{
276+
Handler: a.statisticsHandler(),
277+
ReadTimeout: 20 * time.Second,
278+
ReadHeaderTimeout: 20 * time.Second,
279+
WriteTimeout: 20 * time.Second,
280+
ErrorLog: slog.Stdlib(ctx, a.logger.Named("statistics_http_server"), slog.LevelInfo),
281+
}
282+
go func() {
283+
<-ctx.Done()
284+
_ = server.Close()
285+
}()
286+
287+
err = server.Serve(statisticsListener)
288+
if err != nil && !xerrors.Is(err, http.ErrServerClosed) && !strings.Contains(err.Error(), "use of closed network connection") {
289+
a.logger.Critical(ctx, "serve statistics HTTP server", slog.Error(err))
290+
}
291+
}()
264292
}
265293

266294
// runCoordinator listens for nodes and updates the self-node as it changes.

agent/ports_supported.go

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
//go:build linux || (windows && amd64)
2+
3+
package agent
4+
5+
import (
6+
"time"
7+
8+
"github.com/cakturk/go-netstat/netstat"
9+
"golang.org/x/xerrors"
10+
11+
"github.com/coder/coder/codersdk"
12+
)
13+
14+
func (lp *listeningPortsHandler) getListeningPorts() ([]codersdk.ListeningPort, error) {
15+
lp.mut.Lock()
16+
defer lp.mut.Unlock()
17+
18+
if time.Since(lp.mtime) < time.Second {
19+
// copy
20+
ports := make([]codersdk.ListeningPort, len(lp.ports))
21+
copy(ports, lp.ports)
22+
return ports, nil
23+
}
24+
25+
tabs, err := netstat.TCPSocks(func(s *netstat.SockTabEntry) bool {
26+
return s.State == netstat.Listen
27+
})
28+
if err != nil {
29+
return nil, xerrors.Errorf("scan listening ports: %w", err)
30+
}
31+
32+
seen := make(map[uint16]struct{}, len(tabs))
33+
ports := []codersdk.ListeningPort{}
34+
for _, tab := range tabs {
35+
if tab.LocalAddr == nil || tab.LocalAddr.Port < uint16(codersdk.MinimumListeningPort) {
36+
continue
37+
}
38+
39+
// Don't include ports that we've already seen. This can happen on
40+
// Windows, and maybe on Linux if you're using a shared listener socket.
41+
if _, ok := seen[tab.LocalAddr.Port]; ok {
42+
continue
43+
}
44+
seen[tab.LocalAddr.Port] = struct{}{}
45+
46+
procName := ""
47+
if tab.Process != nil {
48+
procName = tab.Process.Name
49+
}
50+
ports = append(ports, codersdk.ListeningPort{
51+
ProcessName: procName,
52+
Network: codersdk.ListeningPortNetworkTCP,
53+
Port: tab.LocalAddr.Port,
54+
})
55+
}
56+
57+
lp.ports = ports
58+
lp.mtime = time.Now()
59+
60+
// copy
61+
ports = make([]codersdk.ListeningPort, len(lp.ports))
62+
copy(ports, lp.ports)
63+
return ports, nil
64+
}

agent/ports_unsupported.go

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
//go:build !linux && !(windows && amd64)
2+
3+
package agent
4+
5+
import "github.com/coder/coder/codersdk"
6+
7+
func (lp *listeningPortsHandler) getListeningPorts() ([]codersdk.ListeningPort, error) {
8+
// Can't scan for ports on non-linux or non-windows_amd64 systems at the
9+
// moment. The UI will not show any "no ports found" message to the user, so
10+
// the user won't suspect a thing.
11+
return []codersdk.ListeningPort{}, nil
12+
}

agent/statsendpoint.go

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
package agent
2+
3+
import (
4+
"net/http"
5+
"sync"
6+
"time"
7+
8+
"github.com/go-chi/chi"
9+
10+
"github.com/coder/coder/coderd/httpapi"
11+
"github.com/coder/coder/codersdk"
12+
)
13+
14+
func (*agent) statisticsHandler() http.Handler {
15+
r := chi.NewRouter()
16+
r.Get("/", func(rw http.ResponseWriter, r *http.Request) {
17+
httpapi.Write(r.Context(), rw, http.StatusOK, codersdk.Response{
18+
Message: "Hello from the agent!",
19+
})
20+
})
21+
22+
lp := &listeningPortsHandler{}
23+
r.Get("/api/v0/listening-ports", lp.handler)
24+
25+
return r
26+
}
27+
28+
type listeningPortsHandler struct {
29+
mut sync.Mutex
30+
ports []codersdk.ListeningPort
31+
mtime time.Time
32+
}
33+
34+
// handler returns a list of listening ports. This is tested by coderd's
35+
// TestWorkspaceAgentListeningPorts test.
36+
func (lp *listeningPortsHandler) handler(rw http.ResponseWriter, r *http.Request) {
37+
ports, err := lp.getListeningPorts()
38+
if err != nil {
39+
httpapi.Write(r.Context(), rw, http.StatusInternalServerError, codersdk.Response{
40+
Message: "Could not scan for listening ports.",
41+
Detail: err.Error(),
42+
})
43+
return
44+
}
45+
46+
httpapi.Write(r.Context(), rw, http.StatusOK, codersdk.ListeningPortsResponse{
47+
Ports: ports,
48+
})
49+
}

coderd/coderd.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -438,6 +438,7 @@ func New(options *Options) *API {
438438
)
439439
r.Get("/", api.workspaceAgent)
440440
r.Get("/pty", api.workspaceAgentPTY)
441+
r.Get("/listening-ports", api.workspaceAgentListeningPorts)
441442
r.Get("/connection", api.workspaceAgentConnection)
442443
r.Get("/coordinate", api.workspaceAgentClientCoordinate)
443444
// TODO: This can be removed in October. It allows for a friendly

coderd/database/databasefake/databasefake.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2069,7 +2069,7 @@ func (q *fakeQuerier) InsertWorkspaceApp(_ context.Context, arg database.InsertW
20692069
Icon: arg.Icon,
20702070
Command: arg.Command,
20712071
Url: arg.Url,
2072-
RelativePath: arg.RelativePath,
2072+
Subdomain: arg.Subdomain,
20732073
HealthcheckUrl: arg.HealthcheckUrl,
20742074
HealthcheckInterval: arg.HealthcheckInterval,
20752075
HealthcheckThreshold: arg.HealthcheckThreshold,

coderd/database/dump.sql

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
-- Add column relative_path of type bool to workspace_apps
2+
ALTER TABLE "workspace_apps" ADD COLUMN "relative_path" bool NOT NULL DEFAULT false;
3+
4+
-- Set column relative_path to the opposite of subdomain
5+
UPDATE "workspace_apps" SET "relative_path" = NOT "subdomain";
6+
7+
-- Drop column subdomain
8+
ALTER TABLE "workspace_apps" DROP COLUMN "subdomain";
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
-- Add column subdomain of type bool to workspace_apps
2+
ALTER TABLE "workspace_apps" ADD COLUMN "subdomain" bool NOT NULL DEFAULT false;
3+
4+
-- Set column subdomain to the opposite of relative_path
5+
UPDATE "workspace_apps" SET "subdomain" = NOT "relative_path";
6+
7+
-- Drop column relative_path
8+
ALTER TABLE "workspace_apps" DROP COLUMN "relative_path";
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
-- nothing
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
-- There was a mistake in the last migration which set "subdomain" to be the
2+
-- opposite of the deprecated value "relative_path", however the "relative_path"
3+
-- value may not have been correct as it was not consumed anywhere prior to this
4+
-- point.
5+
--
6+
-- Force all workspace apps to use path based routing until rebuild. This should
7+
-- not impact any existing workspaces as the only supported routing method has
8+
-- been path based routing prior to this point.
9+
--
10+
-- On rebuild the value from the Terraform template will be used instead
11+
-- (defaulting to false if unspecified).
12+
UPDATE "workspace_apps" SET "subdomain" = false;

coderd/database/models.go

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)