feat(agent): disable devcontainers for sub agents (#18303)

Updates coder/internal#621
Refs #18245
This commit is contained in:
Mathias Fredriksson
2025-06-10 13:47:02 +03:00
committed by GitHub
parent fca99174ad
commit ae0c8701bb
5 changed files with 53 additions and 0 deletions

View File

@ -1080,6 +1080,18 @@ func (a *agent) handleManifest(manifestOK *checkpoint) func(ctx context.Context,
if manifest.AgentID == uuid.Nil {
return xerrors.New("nil agentID returned by manifest")
}
if manifest.ParentID != uuid.Nil {
// This is a sub agent, disable all the features that should not
// be used by sub agents.
a.logger.Debug(ctx, "sub agent detected, disabling features",
slog.F("parent_id", manifest.ParentID),
slog.F("agent_id", manifest.AgentID),
)
if a.experimentalDevcontainersEnabled {
a.logger.Info(ctx, "devcontainers are not supported on sub agents, disabling feature")
a.experimentalDevcontainersEnabled = false
}
}
a.client.RewriteDERPMap(manifest.DERPMap)
// Expand the directory and send it back to coderd so external

View File

@ -2423,6 +2423,34 @@ waitForOutcomeLoop:
}(container)
}
func TestAgent_DevcontainersDisabledForSubAgent(t *testing.T) {
t.Parallel()
// Create a manifest with a ParentID to make this a sub agent.
manifest := agentsdk.Manifest{
AgentID: uuid.New(),
ParentID: uuid.New(),
}
// Setup the agent with devcontainers enabled initially.
//nolint:dogsled
conn, _, _, _, _ := setupAgent(t, manifest, 0, func(_ *agenttest.Client, o *agent.Options) {
o.ExperimentalDevcontainersEnabled = true
})
// Query the containers API endpoint. This should fail because
// devcontainers have been disabled for the sub agent.
ctx, cancel := context.WithTimeout(context.Background(), testutil.WaitMedium)
defer cancel()
_, err := conn.ListContainers(ctx)
require.Error(t, err)
// Verify the error message contains the expected text.
require.Contains(t, err.Error(), "The agent dev containers feature is experimental and not enabled by default.")
require.Contains(t, err.Error(), "To enable this feature, set CODER_AGENT_DEVCONTAINERS_ENABLE=true in your template.")
}
func TestAgent_Dial(t *testing.T) {
t.Parallel()

View File

@ -102,6 +102,7 @@ type PostMetadataRequest struct {
type PostMetadataRequestDeprecated = codersdk.WorkspaceAgentMetadataResult
type Manifest struct {
ParentID uuid.UUID `json:"parent_id"`
AgentID uuid.UUID `json:"agent_id"`
AgentName string `json:"agent_name"`
// OwnerUsername and WorkspaceID are used by an open-source user to identify the workspace.

View File

@ -15,6 +15,14 @@ import (
)
func ManifestFromProto(manifest *proto.Manifest) (Manifest, error) {
parentID := uuid.Nil
if pid := manifest.GetParentId(); pid != nil {
var err error
parentID, err = uuid.FromBytes(pid)
if err != nil {
return Manifest{}, xerrors.Errorf("error converting workspace agent parent ID: %w", err)
}
}
apps, err := AppsFromProto(manifest.Apps)
if err != nil {
return Manifest{}, xerrors.Errorf("error converting workspace agent apps: %w", err)
@ -36,6 +44,7 @@ func ManifestFromProto(manifest *proto.Manifest) (Manifest, error) {
return Manifest{}, xerrors.Errorf("error converting workspace agent devcontainers: %w", err)
}
return Manifest{
ParentID: parentID,
AgentID: agentID,
AgentName: manifest.AgentName,
OwnerName: manifest.OwnerUsername,
@ -62,6 +71,7 @@ func ProtoFromManifest(manifest Manifest) (*proto.Manifest, error) {
return nil, xerrors.Errorf("convert workspace apps: %w", err)
}
return &proto.Manifest{
ParentId: manifest.ParentID[:],
AgentId: manifest.AgentID[:],
AgentName: manifest.AgentName,
OwnerUsername: manifest.OwnerName,

View File

@ -19,6 +19,7 @@ import (
func TestManifest(t *testing.T) {
t.Parallel()
manifest := agentsdk.Manifest{
ParentID: uuid.New(),
AgentID: uuid.New(),
AgentName: "test-agent",
OwnerName: "test-owner",
@ -142,6 +143,7 @@ func TestManifest(t *testing.T) {
require.NoError(t, err)
back, err := agentsdk.ManifestFromProto(p)
require.NoError(t, err)
require.Equal(t, manifest.ParentID, back.ParentID)
require.Equal(t, manifest.AgentID, back.AgentID)
require.Equal(t, manifest.AgentName, back.AgentName)
require.Equal(t, manifest.OwnerName, back.OwnerName)