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+
}

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy