Skip to content

Commit 61f22a5

Browse files
feat(agent): add ParentId to agent manifest (#17888)
Closes coder/internal#648 This change introduces a new `ParentId` field to the agent's manifest. This will allow an agent to know if it is a child or not, as well as knowing who the owner is. This is part of the Dev Container Agents work
1 parent f044cc3 commit 61f22a5

File tree

11 files changed

+152
-35
lines changed

11 files changed

+152
-35
lines changed

agent/agent.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -95,8 +95,8 @@ type Options struct {
9595
}
9696

9797
type Client interface {
98-
ConnectRPC24(ctx context.Context) (
99-
proto.DRPCAgentClient24, tailnetproto.DRPCTailnetClient24, error,
98+
ConnectRPC25(ctx context.Context) (
99+
proto.DRPCAgentClient25, tailnetproto.DRPCTailnetClient25, error,
100100
)
101101
RewriteDERPMap(derpMap *tailcfg.DERPMap)
102102
}
@@ -908,7 +908,7 @@ func (a *agent) run() (retErr error) {
908908
a.sessionToken.Store(&sessionToken)
909909

910910
// ConnectRPC returns the dRPC connection we use for the Agent and Tailnet v2+ APIs
911-
aAPI, tAPI, err := a.client.ConnectRPC24(a.hardCtx)
911+
aAPI, tAPI, err := a.client.ConnectRPC25(a.hardCtx)
912912
if err != nil {
913913
return err
914914
}

agent/agenttest/client.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -98,8 +98,8 @@ func (c *Client) Close() {
9898
c.derpMapOnce.Do(func() { close(c.derpMapUpdates) })
9999
}
100100

101-
func (c *Client) ConnectRPC24(ctx context.Context) (
102-
agentproto.DRPCAgentClient24, proto.DRPCTailnetClient24, error,
101+
func (c *Client) ConnectRPC25(ctx context.Context) (
102+
agentproto.DRPCAgentClient25, proto.DRPCTailnetClient25, error,
103103
) {
104104
conn, lis := drpcsdk.MemTransportPipe()
105105
c.LastWorkspaceAgent = func() {

agent/proto/agent.pb.go

Lines changed: 39 additions & 27 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

agent/proto/agent.proto

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,7 @@ message Manifest {
9090
string motd_path = 6;
9191
bool disable_direct_connections = 7;
9292
bool derp_force_websockets = 8;
93+
optional bytes parent_id = 18;
9394

9495
coder.tailnet.v2.DERPMap derp_map = 9;
9596
repeated WorkspaceAgentScript scripts = 10;

agent/proto/agent_drpc_old.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,3 +50,8 @@ type DRPCAgentClient24 interface {
5050
PushResourcesMonitoringUsage(ctx context.Context, in *PushResourcesMonitoringUsageRequest) (*PushResourcesMonitoringUsageResponse, error)
5151
ReportConnection(ctx context.Context, in *ReportConnectionRequest) (*emptypb.Empty, error)
5252
}
53+
54+
// DRPCAgentClient25 is the Agent API at v2.5.
55+
type DRPCAgentClient25 interface {
56+
DRPCAgentClient24
57+
}

coderd/agentapi/manifest.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,11 @@ func (a *ManifestAPI) GetManifest(ctx context.Context, _ *agentproto.GetManifest
120120
return nil, xerrors.Errorf("converting workspace apps: %w", err)
121121
}
122122

123+
var parentID []byte
124+
if workspaceAgent.ParentID.Valid {
125+
parentID = workspaceAgent.ParentID.UUID[:]
126+
}
127+
123128
return &agentproto.Manifest{
124129
AgentId: workspaceAgent.ID[:],
125130
AgentName: workspaceAgent.Name,
@@ -133,6 +138,7 @@ func (a *ManifestAPI) GetManifest(ctx context.Context, _ *agentproto.GetManifest
133138
MotdPath: workspaceAgent.MOTDFile,
134139
DisableDirectConnections: a.DisableDirectConnections,
135140
DerpForceWebsockets: a.DerpForceWebSockets,
141+
ParentId: parentID,
136142

137143
DerpMap: tailnet.DERPMapToProto(a.DerpMapFn()),
138144
Scripts: dbAgentScriptsToProto(scripts),

coderd/agentapi/manifest_test.go

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,13 @@ func TestGetManifest(t *testing.T) {
6060
Directory: "/cool/dir",
6161
MOTDFile: "/cool/motd",
6262
}
63+
childAgent = database.WorkspaceAgent{
64+
ID: uuid.New(),
65+
Name: "cool-child-agent",
66+
ParentID: uuid.NullUUID{Valid: true, UUID: agent.ID},
67+
Directory: "/workspace/dir",
68+
MOTDFile: "/workspace/motd",
69+
}
6370
apps = []database.WorkspaceApp{
6471
{
6572
ID: uuid.New(),
@@ -337,6 +344,7 @@ func TestGetManifest(t *testing.T) {
337344
expected := &agentproto.Manifest{
338345
AgentId: agent.ID[:],
339346
AgentName: agent.Name,
347+
ParentId: nil,
340348
OwnerUsername: owner.Username,
341349
WorkspaceId: workspace.ID[:],
342350
WorkspaceName: workspace.Name,
@@ -364,6 +372,70 @@ func TestGetManifest(t *testing.T) {
364372
require.Equal(t, expected, got)
365373
})
366374

375+
t.Run("OK/Child", func(t *testing.T) {
376+
t.Parallel()
377+
378+
mDB := dbmock.NewMockStore(gomock.NewController(t))
379+
380+
api := &agentapi.ManifestAPI{
381+
AccessURL: &url.URL{Scheme: "https", Host: "example.com"},
382+
AppHostname: "*--apps.example.com",
383+
ExternalAuthConfigs: []*externalauth.Config{
384+
{Type: string(codersdk.EnhancedExternalAuthProviderGitHub)},
385+
{Type: "some-provider"},
386+
{Type: string(codersdk.EnhancedExternalAuthProviderGitLab)},
387+
},
388+
DisableDirectConnections: true,
389+
DerpForceWebSockets: true,
390+
391+
AgentFn: func(ctx context.Context) (database.WorkspaceAgent, error) {
392+
return childAgent, nil
393+
},
394+
WorkspaceID: workspace.ID,
395+
Database: mDB,
396+
DerpMapFn: derpMapFn,
397+
}
398+
399+
mDB.EXPECT().GetWorkspaceAppsByAgentID(gomock.Any(), childAgent.ID).Return([]database.WorkspaceApp{}, nil)
400+
mDB.EXPECT().GetWorkspaceAgentScriptsByAgentIDs(gomock.Any(), []uuid.UUID{childAgent.ID}).Return([]database.WorkspaceAgentScript{}, nil)
401+
mDB.EXPECT().GetWorkspaceAgentMetadata(gomock.Any(), database.GetWorkspaceAgentMetadataParams{
402+
WorkspaceAgentID: childAgent.ID,
403+
Keys: nil, // all
404+
}).Return([]database.WorkspaceAgentMetadatum{}, nil)
405+
mDB.EXPECT().GetWorkspaceAgentDevcontainersByAgentID(gomock.Any(), childAgent.ID).Return([]database.WorkspaceAgentDevcontainer{}, nil)
406+
mDB.EXPECT().GetWorkspaceByID(gomock.Any(), workspace.ID).Return(workspace, nil)
407+
mDB.EXPECT().GetUserByID(gomock.Any(), workspace.OwnerID).Return(owner, nil)
408+
409+
got, err := api.GetManifest(context.Background(), &agentproto.GetManifestRequest{})
410+
require.NoError(t, err)
411+
412+
expected := &agentproto.Manifest{
413+
AgentId: childAgent.ID[:],
414+
AgentName: childAgent.Name,
415+
ParentId: agent.ID[:],
416+
OwnerUsername: owner.Username,
417+
WorkspaceId: workspace.ID[:],
418+
WorkspaceName: workspace.Name,
419+
GitAuthConfigs: 2, // two "enhanced" external auth configs
420+
EnvironmentVariables: nil,
421+
Directory: childAgent.Directory,
422+
VsCodePortProxyUri: fmt.Sprintf("https://{{port}}--%s--%s--%s--apps.example.com", childAgent.Name, workspace.Name, owner.Username),
423+
MotdPath: childAgent.MOTDFile,
424+
DisableDirectConnections: true,
425+
DerpForceWebsockets: true,
426+
// tailnet.DERPMapToProto() is extensively tested elsewhere, so it's
427+
// not necessary to manually recreate a big DERP map here like we
428+
// did for apps and metadata.
429+
DerpMap: tailnet.DERPMapToProto(derpMapFn()),
430+
Scripts: []*agentproto.WorkspaceAgentScript{},
431+
Apps: []*agentproto.WorkspaceApp{},
432+
Metadata: []*agentproto.WorkspaceAgentMetadata_Description{},
433+
Devcontainers: []*agentproto.WorkspaceAgentDevcontainer{},
434+
}
435+
436+
require.Equal(t, expected, got)
437+
})
438+
367439
t.Run("NoAppHostname", func(t *testing.T) {
368440
t.Parallel()
369441

coderd/workspaceagents_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2575,7 +2575,7 @@ func requireGetManifest(ctx context.Context, t testing.TB, aAPI agentproto.DRPCA
25752575
}
25762576

25772577
func postStartup(ctx context.Context, t testing.TB, client agent.Client, startup *agentproto.Startup) error {
2578-
aAPI, _, err := client.ConnectRPC24(ctx)
2578+
aAPI, _, err := client.ConnectRPC25(ctx)
25792579
require.NoError(t, err)
25802580
defer func() {
25812581
cErr := aAPI.DRPCConn().Close()

codersdk/agentsdk/agentsdk.go

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -246,7 +246,7 @@ func (c *Client) ConnectRPC23(ctx context.Context) (
246246
}
247247

248248
// ConnectRPC24 returns a dRPC client to the Agent API v2.4. It is useful when you want to be
249-
// maximally compatible with Coderd Release Versions from 2.xx+ // TODO @vincent: define version
249+
// maximally compatible with Coderd Release Versions from 2.20+
250250
func (c *Client) ConnectRPC24(ctx context.Context) (
251251
proto.DRPCAgentClient24, tailnetproto.DRPCTailnetClient24, error,
252252
) {
@@ -257,6 +257,18 @@ func (c *Client) ConnectRPC24(ctx context.Context) (
257257
return proto.NewDRPCAgentClient(conn), tailnetproto.NewDRPCTailnetClient(conn), nil
258258
}
259259

260+
// ConnectRPC25 returns a dRPC client to the Agent API v2.5. It is useful when you want to be
261+
// maximally compatible with Coderd Release Versions from 2.xx+ // TODO(DanielleMaywood): Update version
262+
func (c *Client) ConnectRPC25(ctx context.Context) (
263+
proto.DRPCAgentClient25, tailnetproto.DRPCTailnetClient25, error,
264+
) {
265+
conn, err := c.connectRPCVersion(ctx, apiversion.New(2, 5))
266+
if err != nil {
267+
return nil, nil, err
268+
}
269+
return proto.NewDRPCAgentClient(conn), tailnetproto.NewDRPCTailnetClient(conn), nil
270+
}
271+
260272
// ConnectRPC connects to the workspace agent API and tailnet API
261273
func (c *Client) ConnectRPC(ctx context.Context) (drpc.Conn, error) {
262274
return c.connectRPCVersion(ctx, proto.CurrentVersion)

tailnet/proto/tailnet_drpc_old.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,3 +40,8 @@ type DRPCTailnetClient23 interface {
4040
type DRPCTailnetClient24 interface {
4141
DRPCTailnetClient23
4242
}
43+
44+
// DRPCTailnetClient25 is the Tailnet API at v2.5.
45+
type DRPCTailnetClient25 interface {
46+
DRPCTailnetClient24
47+
}

tailnet/proto/version.go

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,9 +45,13 @@ import (
4545
// PushResourcesMonitoringUsage RPCs on the Agent API.
4646
// - Added support for reporting connection events for auditing via the
4747
// ReportConnection RPC on the Agent API.
48+
//
49+
// API v2.5:
50+
// - Shipped in Coder v2.xx.x // TODO(DanielleMaywood): Update version
51+
// - Added `ParentId` to the agent manifest.
4852
const (
4953
CurrentMajor = 2
50-
CurrentMinor = 4
54+
CurrentMinor = 5
5155
)
5256

5357
var CurrentVersion = apiversion.New(CurrentMajor, CurrentMinor)

0 commit comments

Comments
 (0)