mirror of
https://github.com/coder/coder.git
synced 2026-09-24 15:04:27 +08:00
chore: remove legacy wsconncache (#11816)
Fixes #8218 Removes `wsconncache` and related "is legacy?" functions and API calls that were used by it. The only leftover is that Agents still use the legacy IP, so that back level clients or workspace proxies can dial them correctly. We should eventually remove this: #11819
This commit is contained in:
@@ -187,15 +187,6 @@ func New(ctx context.Context, options *Options) (_ *API, err error) {
|
||||
r.Use(apiKeyMiddleware)
|
||||
r.Post("/", api.reconnectingPTYSignedToken)
|
||||
})
|
||||
|
||||
r.With(
|
||||
apiKeyMiddlewareOptional,
|
||||
httpmw.ExtractWorkspaceProxy(httpmw.ExtractWorkspaceProxyConfig{
|
||||
DB: options.Database,
|
||||
Optional: true,
|
||||
}),
|
||||
httpmw.RequireAPIKeyOrWorkspaceProxyAuth(),
|
||||
).Get("/workspaceagents/{workspaceagent}/legacy", api.agentIsLegacy)
|
||||
r.Route("/workspaceproxies", func(r chi.Router) {
|
||||
r.Use(
|
||||
api.moonsEnabledMW,
|
||||
|
||||
@@ -7,42 +7,11 @@ import (
|
||||
"nhooyr.io/websocket"
|
||||
|
||||
"github.com/coder/coder/v2/coderd/httpapi"
|
||||
"github.com/coder/coder/v2/coderd/httpmw"
|
||||
"github.com/coder/coder/v2/coderd/util/apiversion"
|
||||
"github.com/coder/coder/v2/codersdk"
|
||||
"github.com/coder/coder/v2/enterprise/wsproxy/wsproxysdk"
|
||||
"github.com/coder/coder/v2/tailnet/proto"
|
||||
)
|
||||
|
||||
// @Summary Agent is legacy
|
||||
// @ID agent-is-legacy
|
||||
// @Security CoderSessionToken
|
||||
// @Produce json
|
||||
// @Tags Enterprise
|
||||
// @Param workspaceagent path string true "Workspace Agent ID" format(uuid)
|
||||
// @Success 200 {object} wsproxysdk.AgentIsLegacyResponse
|
||||
// @Router /workspaceagents/{workspaceagent}/legacy [get]
|
||||
// @x-apidocgen {"skip": true}
|
||||
func (api *API) agentIsLegacy(rw http.ResponseWriter, r *http.Request) {
|
||||
ctx := r.Context()
|
||||
|
||||
agentID, ok := httpmw.ParseUUIDParam(rw, r, "workspaceagent")
|
||||
if !ok {
|
||||
httpapi.Write(r.Context(), rw, http.StatusBadRequest, codersdk.Response{
|
||||
Message: "Missing UUID in URL.",
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
node := (*api.AGPL.TailnetCoordinator.Load()).Node(agentID)
|
||||
httpapi.Write(ctx, rw, http.StatusOK, wsproxysdk.AgentIsLegacyResponse{
|
||||
Found: node != nil,
|
||||
Legacy: node != nil &&
|
||||
len(node.Addresses) > 0 &&
|
||||
node.Addresses[0].Addr() == codersdk.WorkspaceAgentIP,
|
||||
})
|
||||
}
|
||||
|
||||
// @Summary Workspace Proxy Coordinate
|
||||
// @ID workspace-proxy-coordinate
|
||||
// @Security CoderSessionToken
|
||||
|
||||
@@ -1,158 +0,0 @@
|
||||
package coderd_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/netip"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"github.com/moby/moby/pkg/namesgenerator"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"google.golang.org/protobuf/types/known/timestamppb"
|
||||
"tailscale.com/types/key"
|
||||
|
||||
"cdr.dev/slog/sloggers/slogtest"
|
||||
"github.com/coder/coder/v2/coderd/coderdtest"
|
||||
"github.com/coder/coder/v2/coderd/database/dbtestutil"
|
||||
"github.com/coder/coder/v2/codersdk"
|
||||
"github.com/coder/coder/v2/enterprise/coderd/coderdenttest"
|
||||
"github.com/coder/coder/v2/enterprise/coderd/license"
|
||||
"github.com/coder/coder/v2/enterprise/wsproxy/wsproxysdk"
|
||||
agpl "github.com/coder/coder/v2/tailnet"
|
||||
"github.com/coder/coder/v2/tailnet/proto"
|
||||
"github.com/coder/coder/v2/testutil"
|
||||
)
|
||||
|
||||
// workspaceProxyCoordinate and agentIsLegacy are both tested by wsproxy tests.
|
||||
|
||||
func Test_agentIsLegacy(t *testing.T) {
|
||||
t.Parallel()
|
||||
nodeKey := key.NewNode().Public()
|
||||
discoKey := key.NewDisco().Public()
|
||||
nkBin, err := nodeKey.MarshalBinary()
|
||||
require.NoError(t, err)
|
||||
dkBin, err := discoKey.MarshalText()
|
||||
require.NoError(t, err)
|
||||
|
||||
t.Run("Legacy", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
var (
|
||||
ctx, cancel = context.WithTimeout(context.Background(), testutil.WaitShort)
|
||||
db, pubsub = dbtestutil.NewDB(t)
|
||||
logger = slogtest.Make(t, nil)
|
||||
coordinator = agpl.NewCoordinator(logger)
|
||||
client, _ = coderdenttest.New(t, &coderdenttest.Options{
|
||||
Options: &coderdtest.Options{
|
||||
Database: db,
|
||||
Pubsub: pubsub,
|
||||
Coordinator: coordinator,
|
||||
},
|
||||
LicenseOptions: &coderdenttest.LicenseOptions{
|
||||
Features: license.Features{
|
||||
codersdk.FeatureWorkspaceProxy: 1,
|
||||
},
|
||||
},
|
||||
})
|
||||
)
|
||||
defer cancel()
|
||||
|
||||
nodeID := uuid.New()
|
||||
ma := coordinator.ServeMultiAgent(nodeID)
|
||||
defer ma.Close()
|
||||
require.NoError(t, ma.UpdateSelf(&proto.Node{
|
||||
Id: 55,
|
||||
AsOf: timestamppb.New(time.Unix(1689653252, 0)),
|
||||
Key: nkBin,
|
||||
Disco: string(dkBin),
|
||||
PreferredDerp: 0,
|
||||
DerpLatency: map[string]float64{
|
||||
"0": 1.0,
|
||||
},
|
||||
DerpForcedWebsocket: map[int32]string{},
|
||||
Addresses: []string{codersdk.WorkspaceAgentIP.String() + "/128"},
|
||||
AllowedIps: []string{codersdk.WorkspaceAgentIP.String() + "/128"},
|
||||
Endpoints: []string{"192.168.1.1:18842"},
|
||||
}))
|
||||
require.Eventually(t, func() bool {
|
||||
return coordinator.Node(nodeID) != nil
|
||||
}, testutil.WaitShort, testutil.IntervalFast)
|
||||
|
||||
proxyRes, err := client.CreateWorkspaceProxy(ctx, codersdk.CreateWorkspaceProxyRequest{
|
||||
Name: namesgenerator.GetRandomName(1),
|
||||
Icon: "/emojis/flag.png",
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
proxyClient := wsproxysdk.New(client.URL)
|
||||
proxyClient.SetSessionToken(proxyRes.ProxyToken)
|
||||
|
||||
legacyRes, err := proxyClient.AgentIsLegacy(ctx, nodeID)
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.True(t, legacyRes.Found)
|
||||
assert.True(t, legacyRes.Legacy)
|
||||
})
|
||||
|
||||
t.Run("NotLegacy", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
var (
|
||||
ctx, cancel = context.WithTimeout(context.Background(), testutil.WaitShort)
|
||||
db, pubsub = dbtestutil.NewDB(t)
|
||||
logger = slogtest.Make(t, nil)
|
||||
coordinator = agpl.NewCoordinator(logger)
|
||||
client, _ = coderdenttest.New(t, &coderdenttest.Options{
|
||||
Options: &coderdtest.Options{
|
||||
Database: db,
|
||||
Pubsub: pubsub,
|
||||
Coordinator: coordinator,
|
||||
},
|
||||
LicenseOptions: &coderdenttest.LicenseOptions{
|
||||
Features: license.Features{
|
||||
codersdk.FeatureWorkspaceProxy: 1,
|
||||
},
|
||||
},
|
||||
})
|
||||
)
|
||||
defer cancel()
|
||||
|
||||
nodeID := uuid.New()
|
||||
ma := coordinator.ServeMultiAgent(nodeID)
|
||||
defer ma.Close()
|
||||
require.NoError(t, ma.UpdateSelf(&proto.Node{
|
||||
Id: 55,
|
||||
AsOf: timestamppb.New(time.Unix(1689653252, 0)),
|
||||
Key: nkBin,
|
||||
Disco: string(dkBin),
|
||||
PreferredDerp: 0,
|
||||
DerpLatency: map[string]float64{
|
||||
"0": 1.0,
|
||||
},
|
||||
DerpForcedWebsocket: map[int32]string{},
|
||||
Addresses: []string{netip.PrefixFrom(agpl.IPFromUUID(nodeID), 128).String()},
|
||||
AllowedIps: []string{netip.PrefixFrom(agpl.IPFromUUID(nodeID), 128).String()},
|
||||
Endpoints: []string{"192.168.1.1:18842"},
|
||||
}))
|
||||
require.Eventually(t, func() bool {
|
||||
return coordinator.Node(nodeID) != nil
|
||||
}, testutil.WaitShort, testutil.IntervalFast)
|
||||
|
||||
proxyRes, err := client.CreateWorkspaceProxy(ctx, codersdk.CreateWorkspaceProxyRequest{
|
||||
Name: namesgenerator.GetRandomName(1),
|
||||
Icon: "/emojis/flag.png",
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
proxyClient := wsproxysdk.New(client.URL)
|
||||
proxyClient.SetSessionToken(proxyRes.ProxyToken)
|
||||
|
||||
legacyRes, err := proxyClient.AgentIsLegacy(ctx, nodeID)
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.True(t, legacyRes.Found)
|
||||
assert.False(t, legacyRes.Legacy)
|
||||
})
|
||||
}
|
||||
@@ -33,7 +33,6 @@ import (
|
||||
"github.com/coder/coder/v2/coderd/httpmw"
|
||||
"github.com/coder/coder/v2/coderd/tracing"
|
||||
"github.com/coder/coder/v2/coderd/workspaceapps"
|
||||
"github.com/coder/coder/v2/coderd/wsconncache"
|
||||
"github.com/coder/coder/v2/codersdk"
|
||||
"github.com/coder/coder/v2/enterprise/derpmesh"
|
||||
"github.com/coder/coder/v2/enterprise/wsproxy/wsproxysdk"
|
||||
@@ -251,7 +250,6 @@ func New(ctx context.Context, opts *Options) (*Server, error) {
|
||||
},
|
||||
regResp.DERPForceWebSockets,
|
||||
s.DialCoordinator,
|
||||
wsconncache.New(s.DialWorkspaceAgent, 0),
|
||||
s.TracerProvider,
|
||||
)
|
||||
if err != nil {
|
||||
@@ -434,10 +432,6 @@ func (s *Server) Close() error {
|
||||
return err
|
||||
}
|
||||
|
||||
func (s *Server) DialWorkspaceAgent(id uuid.UUID) (*codersdk.WorkspaceAgentConn, error) {
|
||||
return s.SDKClient.DialWorkspaceAgent(s.ctx, id, nil)
|
||||
}
|
||||
|
||||
func (*Server) mutateRegister(_ *wsproxysdk.RegisterWorkspaceProxyRequest) {
|
||||
// TODO: we should probably ping replicas similarly to the replicasync
|
||||
// package in the primary and update req.ReplicaError accordingly.
|
||||
|
||||
@@ -3,18 +3,15 @@ package wsproxysdk
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"golang.org/x/xerrors"
|
||||
"nhooyr.io/websocket"
|
||||
"tailscale.com/tailcfg"
|
||||
"tailscale.com/util/singleflight"
|
||||
|
||||
"cdr.dev/slog"
|
||||
"github.com/coder/coder/v2/coderd/httpapi"
|
||||
@@ -475,20 +472,18 @@ func (c *Client) DialCoordinator(ctx context.Context) (agpl.MultiAgentConn, erro
|
||||
}
|
||||
|
||||
rma := remoteMultiAgentHandler{
|
||||
sdk: c,
|
||||
logger: logger,
|
||||
protocol: protocol,
|
||||
cancel: cancel,
|
||||
legacyAgentCache: map[uuid.UUID]bool{},
|
||||
sdk: c,
|
||||
logger: logger,
|
||||
protocol: protocol,
|
||||
cancel: cancel,
|
||||
}
|
||||
|
||||
ma := (&agpl.MultiAgent{
|
||||
ID: uuid.New(),
|
||||
AgentIsLegacyFunc: rma.AgentIsLegacy,
|
||||
OnSubscribe: rma.OnSubscribe,
|
||||
OnUnsubscribe: rma.OnUnsubscribe,
|
||||
OnNodeUpdate: rma.OnNodeUpdate,
|
||||
OnRemove: rma.OnRemove,
|
||||
ID: uuid.New(),
|
||||
OnSubscribe: rma.OnSubscribe,
|
||||
OnUnsubscribe: rma.OnUnsubscribe,
|
||||
OnNodeUpdate: rma.OnNodeUpdate,
|
||||
OnRemove: rma.OnRemove,
|
||||
}).Init()
|
||||
|
||||
go func() {
|
||||
@@ -509,10 +504,6 @@ type remoteMultiAgentHandler struct {
|
||||
protocol proto.DRPCTailnet_CoordinateClient
|
||||
ma *agpl.MultiAgent
|
||||
cancel func()
|
||||
|
||||
legacyMu sync.RWMutex
|
||||
legacyAgentCache map[uuid.UUID]bool
|
||||
legacySingleflight singleflight.Group[uuid.UUID, AgentIsLegacyResponse]
|
||||
}
|
||||
|
||||
func (a *remoteMultiAgentHandler) respLoop() {
|
||||
@@ -558,59 +549,3 @@ func (a *remoteMultiAgentHandler) OnRemove(_ agpl.Queue) {
|
||||
}
|
||||
_ = a.protocol.CloseSend()
|
||||
}
|
||||
|
||||
func (a *remoteMultiAgentHandler) AgentIsLegacy(agentID uuid.UUID) bool {
|
||||
a.legacyMu.RLock()
|
||||
if isLegacy, ok := a.legacyAgentCache[agentID]; ok {
|
||||
a.legacyMu.RUnlock()
|
||||
return isLegacy
|
||||
}
|
||||
a.legacyMu.RUnlock()
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
|
||||
defer cancel()
|
||||
|
||||
resp, err, _ := a.legacySingleflight.Do(agentID, func() (AgentIsLegacyResponse, error) {
|
||||
return a.sdk.AgentIsLegacy(ctx, agentID)
|
||||
})
|
||||
if err != nil {
|
||||
a.sdk.SDKClient.Logger().Error(ctx, "failed to check agent legacy status", slog.F("agent_id", agentID), slog.Error(err))
|
||||
|
||||
// Assume that the agent is legacy since this failed, while less
|
||||
// efficient it will always work.
|
||||
return true
|
||||
}
|
||||
// Assume legacy since the agent didn't exist.
|
||||
if !resp.Found {
|
||||
return true
|
||||
}
|
||||
|
||||
a.legacyMu.Lock()
|
||||
a.legacyAgentCache[agentID] = resp.Legacy
|
||||
a.legacyMu.Unlock()
|
||||
|
||||
return resp.Legacy
|
||||
}
|
||||
|
||||
type AgentIsLegacyResponse struct {
|
||||
Found bool `json:"found"`
|
||||
Legacy bool `json:"legacy"`
|
||||
}
|
||||
|
||||
func (c *Client) AgentIsLegacy(ctx context.Context, agentID uuid.UUID) (AgentIsLegacyResponse, error) {
|
||||
res, err := c.Request(ctx, http.MethodGet,
|
||||
fmt.Sprintf("/api/v2/workspaceagents/%s/legacy", agentID.String()),
|
||||
nil,
|
||||
)
|
||||
if err != nil {
|
||||
return AgentIsLegacyResponse{}, xerrors.Errorf("make request: %w", err)
|
||||
}
|
||||
defer res.Body.Close()
|
||||
|
||||
if res.StatusCode != http.StatusOK {
|
||||
return AgentIsLegacyResponse{}, codersdk.ReadBodyAsError(res)
|
||||
}
|
||||
|
||||
var resp AgentIsLegacyResponse
|
||||
return resp, json.NewDecoder(res.Body).Decode(&resp)
|
||||
}
|
||||
|
||||
@@ -25,7 +25,6 @@ import (
|
||||
|
||||
"cdr.dev/slog"
|
||||
"cdr.dev/slog/sloggers/slogtest"
|
||||
"github.com/coder/coder/v2/coderd/httpapi"
|
||||
"github.com/coder/coder/v2/coderd/httpmw"
|
||||
"github.com/coder/coder/v2/coderd/workspaceapps"
|
||||
"github.com/coder/coder/v2/enterprise/tailnet"
|
||||
@@ -201,12 +200,6 @@ func TestDialCoordinator(t *testing.T) {
|
||||
err = cSrv.ServeMultiAgentClient(ctx, version, nc, proxyID)
|
||||
serveMACErr <- err
|
||||
})
|
||||
r.Get("/api/v2/workspaceagents/{workspaceagent}/legacy", func(w http.ResponseWriter, r *http.Request) {
|
||||
httpapi.Write(ctx, w, http.StatusOK, wsproxysdk.AgentIsLegacyResponse{
|
||||
Found: true,
|
||||
Legacy: true,
|
||||
})
|
||||
})
|
||||
|
||||
u, err := url.Parse(srv.URL)
|
||||
require.NoError(t, err)
|
||||
@@ -258,11 +251,6 @@ func TestDialCoordinator(t *testing.T) {
|
||||
assert.NoError(t, err)
|
||||
assert.True(t, eq)
|
||||
}
|
||||
// Check legacy
|
||||
{
|
||||
isLegacy := rma.AgentIsLegacy(agentID)
|
||||
assert.True(t, isLegacy)
|
||||
}
|
||||
// UpdateSelf
|
||||
{
|
||||
require.NoError(t, rma.UpdateSelf(expected.PeerUpdates[0].GetNode()))
|
||||
|
||||
Reference in New Issue
Block a user