Thanks to visit codestin.com
Credit goes to github.com

Skip to content

feat: add debug server for tailnet coordinators #5861

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 7 commits into from
Jan 25, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion agent/agent_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1193,7 +1193,7 @@ func (c *client) ListenWorkspaceAgent(_ context.Context) (net.Conn, error) {
}
c.t.Cleanup(c.lastWorkspaceAgent)
go func() {
_ = c.coordinator.ServeAgent(serverConn, c.agentID)
_ = c.coordinator.ServeAgent(serverConn, c.agentID, "")
close(closed)
}()
return clientConn, nil
Expand Down
22 changes: 22 additions & 0 deletions coderd/apidoc/docs.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

18 changes: 18 additions & 0 deletions coderd/apidoc/swagger.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

19 changes: 19 additions & 0 deletions coderd/coderd.go
Original file line number Diff line number Diff line change
Expand Up @@ -613,6 +613,25 @@ func New(options *Options) *API {
r.Get("/", api.workspaceApplicationAuth)
})
})

r.Route("/debug", func(r chi.Router) {
r.Use(
apiKeyMiddleware,
// Ensure only owners can access debug endpoints.
func(next http.Handler) http.Handler {
return http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
if !api.Authorize(r, rbac.ActionRead, rbac.ResourceDebugInfo) {
httpapi.ResourceNotFound(rw)
return
}

next.ServeHTTP(rw, r)
})
},
)

r.Get("/coordinator", api.debugCoordinator)
})
})

if options.SwaggerEndpoint {
Expand Down
5 changes: 5 additions & 0 deletions coderd/coderdtest/authorize.go
Original file line number Diff line number Diff line change
Expand Up @@ -272,6 +272,11 @@ func AGPLRoutes(a *AuthTester) (map[string]string, map[string]RouteCheck) {
AssertAction: rbac.ActionRead,
AssertObject: rbac.ResourceTemplate,
},

"GET:/api/v2/debug/coordinator": {
AssertAction: rbac.ActionRead,
AssertObject: rbac.ResourceDebugInfo,
},
}

// Routes like proxy routes support all HTTP methods. A helper func to expand
Expand Down
5 changes: 3 additions & 2 deletions coderd/coderdtest/swaggerparser.go
Original file line number Diff line number Diff line change
Expand Up @@ -327,7 +327,7 @@ func assertAccept(t *testing.T, comment SwaggerComment) {
}
}

var allowedProduceTypes = []string{"json", "text/event-stream"}
var allowedProduceTypes = []string{"json", "text/event-stream", "text/html"}

func assertProduce(t *testing.T, comment SwaggerComment) {
var hasResponseModel bool
Expand All @@ -344,7 +344,8 @@ func assertProduce(t *testing.T, comment SwaggerComment) {
} else {
if (comment.router == "/workspaceagents/me/app-health" && comment.method == "post") ||
(comment.router == "/workspaceagents/me/version" && comment.method == "post") ||
(comment.router == "/licenses/{id}" && comment.method == "delete") {
(comment.router == "/licenses/{id}" && comment.method == "delete") ||
(comment.router == "/debug/coordinator" && comment.method == "get") {
return // Exception: HTTP 200 is returned without response entity
}

Expand Down
14 changes: 14 additions & 0 deletions coderd/debug.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package coderd

import "net/http"

// @Summary Debug Info Wireguard Coordinator
// @ID debug-info-wireguard-coordinator
// @Security CoderSessionToken
// @Produce text/html
// @Tags Debug
// @Success 200
// @Router /debug/coordinator [get]
func (api *API) debugCoordinator(rw http.ResponseWriter, r *http.Request) {
(*api.TailnetCoordinator.Load()).ServeHTTPDebug(rw, r)
}
5 changes: 5 additions & 0 deletions coderd/rbac/object.go
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,11 @@ var (
ResourceReplicas = Object{
Type: "replicas",
}

// ResourceDebugInfo controls access to the debug routes `/api/v2/debug/*`.
ResourceDebugInfo = Object{
Type: "debug_info",
}
)

// Object is used to create objects for authz checks when you have none in
Expand Down
12 changes: 11 additions & 1 deletion coderd/workspaceagents.go
Original file line number Diff line number Diff line change
Expand Up @@ -521,6 +521,16 @@ func (api *API) workspaceAgentCoordinate(rw http.ResponseWriter, r *http.Request
})
return
}

workspace, err := api.Database.GetWorkspaceByID(ctx, build.WorkspaceID)
if err != nil {
httpapi.Write(ctx, rw, http.StatusBadRequest, codersdk.Response{
Message: "Internal error fetching workspace.",
Detail: err.Error(),
})
return
}

// Ensure the resource is still valid!
// We only accept agents for resources on the latest build.
ensureLatestBuild := func() error {
Expand Down Expand Up @@ -618,7 +628,7 @@ func (api *API) workspaceAgentCoordinate(rw http.ResponseWriter, r *http.Request
closeChan := make(chan struct{})
go func() {
defer close(closeChan)
err := (*api.TailnetCoordinator.Load()).ServeAgent(wsNetConn, workspaceAgent.ID)
err := (*api.TailnetCoordinator.Load()).ServeAgent(wsNetConn, workspaceAgent.ID, fmt.Sprintf("%s-%s", workspace.Name, workspaceAgent.Name))
if err != nil {
api.Logger.Warn(ctx, "tailnet coordinator agent error", slog.Error(err))
_ = conn.Close(websocket.StatusInternalError, err.Error())
Expand Down
2 changes: 1 addition & 1 deletion coderd/wsconncache/wsconncache_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -207,7 +207,7 @@ func (c *client) ListenWorkspaceAgent(_ context.Context) (net.Conn, error) {
<-closed
})
go func() {
_ = c.coordinator.ServeAgent(serverConn, c.agentID)
_ = c.coordinator.ServeAgent(serverConn, c.agentID, "")
close(closed)
}()
return clientConn, nil
Expand Down
21 changes: 21 additions & 0 deletions docs/api/debug.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# Debug

## Debug Info Wireguard Coordinator

### Code samples

```shell
# Example request using curl
curl -X GET http://coder-server:8080/api/v2/debug/coordinator \
-H 'Coder-Session-Token: API_KEY'
```

`GET /debug/coordinator`

### Responses

| Status | Meaning | Description | Schema |
| ------ | ------------------------------------------------------- | ----------- | ------ |
| 200 | [OK](https://tools.ietf.org/html/rfc7231#section-6.3.1) | OK | |

To perform this operation, you must be authenticated. [Learn more](authentication.md).
4 changes: 4 additions & 0 deletions docs/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -364,6 +364,10 @@
"title": "Builds",
"path": "./api/builds.md"
},
{
"title": "Debug",
"path": "./api/debug.md"
},
{
"title": "Enterprise",
"path": "./api/enterprise.md"
Expand Down
10 changes: 9 additions & 1 deletion enterprise/tailnet/coordinator.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,10 @@ import (
"context"
"encoding/json"
"errors"
"fmt"
"io"
"net"
"net/http"
"sync"
"time"

Expand Down Expand Up @@ -174,7 +176,7 @@ func (c *haCoordinator) handleNextClientMessage(id, agent uuid.UUID, decoder *js

// ServeAgent accepts a WebSocket connection to an agent that listens to
// incoming connections and publishes node updates.
func (c *haCoordinator) ServeAgent(conn net.Conn, id uuid.UUID) error {
func (c *haCoordinator) ServeAgent(conn net.Conn, id uuid.UUID, _ string) error {
// Tell clients on other instances to send a callmemaybe to us.
err := c.publishAgentHello(id)
if err != nil {
Expand Down Expand Up @@ -573,3 +575,9 @@ func (c *haCoordinator) formatAgentUpdate(id uuid.UUID, node *agpl.Node) ([]byte

return buf.Bytes(), nil
}

func (*haCoordinator) ServeHTTPDebug(w http.ResponseWriter, _ *http.Request) {
w.Header().Set("Content-Type", "text/html; charset=utf-8")
fmt.Fprintf(w, "<h1>coordinator</h1>")
fmt.Fprintf(w, "<h2>ha debug coming soon</h2>")
}
10 changes: 5 additions & 5 deletions enterprise/tailnet/coordinator_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ func TestCoordinatorSingle(t *testing.T) {
id := uuid.New()
closeChan := make(chan struct{})
go func() {
err := coordinator.ServeAgent(server, id)
err := coordinator.ServeAgent(server, id, "")
assert.NoError(t, err)
close(closeChan)
}()
Expand Down Expand Up @@ -91,7 +91,7 @@ func TestCoordinatorSingle(t *testing.T) {
agentID := uuid.New()
closeAgentChan := make(chan struct{})
go func() {
err := coordinator.ServeAgent(agentServerWS, agentID)
err := coordinator.ServeAgent(agentServerWS, agentID, "")
assert.NoError(t, err)
close(closeAgentChan)
}()
Expand Down Expand Up @@ -142,7 +142,7 @@ func TestCoordinatorSingle(t *testing.T) {
})
closeAgentChan = make(chan struct{})
go func() {
err := coordinator.ServeAgent(agentServerWS, agentID)
err := coordinator.ServeAgent(agentServerWS, agentID, "")
assert.NoError(t, err)
close(closeAgentChan)
}()
Expand Down Expand Up @@ -184,7 +184,7 @@ func TestCoordinatorHA(t *testing.T) {
agentID := uuid.New()
closeAgentChan := make(chan struct{})
go func() {
err := coordinator1.ServeAgent(agentServerWS, agentID)
err := coordinator1.ServeAgent(agentServerWS, agentID, "")
assert.NoError(t, err)
close(closeAgentChan)
}()
Expand Down Expand Up @@ -240,7 +240,7 @@ func TestCoordinatorHA(t *testing.T) {
})
closeAgentChan = make(chan struct{})
go func() {
err := coordinator1.ServeAgent(agentServerWS, agentID)
err := coordinator1.ServeAgent(agentServerWS, agentID, "")
assert.NoError(t, err)
close(closeAgentChan)
}()
Expand Down
Loading