mirror of
https://github.com/coder/coder.git
synced 2026-09-24 15:04:27 +08:00
fix(coderd/agentapi): make sub agent slugs more unique (#18581)
The incorrect assumption that slugs were unique per-agent was made when the subagent API was implemented. Whilst this PR doesn't completely enforce that, we instead compute a stable hash to prefix the slug that should provide a reasonable level of probability that the slug will be unique.
This commit is contained in:
@@ -2,7 +2,9 @@ package agentapi
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/sha256"
|
||||
"database/sql"
|
||||
"encoding/base32"
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
@@ -165,11 +167,20 @@ func (a *SubAgentAPI) CreateSubAgent(ctx context.Context, req *agentproto.Create
|
||||
}
|
||||
}
|
||||
|
||||
// NOTE(DanielleMaywood):
|
||||
// Slugs must be unique PER workspace/template. As of 2025-06-25,
|
||||
// there is no database-layer enforcement of this constraint.
|
||||
// We can get around this by creating a slug that *should* be
|
||||
// unique (at least highly probable).
|
||||
slugHash := sha256.Sum256([]byte(subAgent.Name + "/" + app.Slug))
|
||||
slugHashEnc := base32.HexEncoding.WithPadding(base32.NoPadding).EncodeToString(slugHash[:])
|
||||
computedSlug := strings.ToLower(slugHashEnc[:8]) + "-" + app.Slug
|
||||
|
||||
_, err := a.Database.UpsertWorkspaceApp(ctx, database.UpsertWorkspaceAppParams{
|
||||
ID: uuid.New(), // NOTE: we may need to maintain the app's ID here for stability, but for now we'll leave this as-is.
|
||||
CreatedAt: createdAt,
|
||||
AgentID: subAgent.ID,
|
||||
Slug: app.Slug,
|
||||
Slug: computedSlug,
|
||||
DisplayName: app.GetDisplayName(),
|
||||
Icon: app.GetIcon(),
|
||||
Command: sql.NullString{
|
||||
|
||||
@@ -216,7 +216,7 @@ func TestSubAgentAPI(t *testing.T) {
|
||||
},
|
||||
expectApps: []database.WorkspaceApp{
|
||||
{
|
||||
Slug: "code-server",
|
||||
Slug: "fdqf0lpd-code-server",
|
||||
DisplayName: "VS Code",
|
||||
Icon: "/icon/code.svg",
|
||||
Command: sql.NullString{},
|
||||
@@ -234,7 +234,7 @@ func TestSubAgentAPI(t *testing.T) {
|
||||
DisplayGroup: sql.NullString{},
|
||||
},
|
||||
{
|
||||
Slug: "vim",
|
||||
Slug: "547knu0f-vim",
|
||||
DisplayName: "Vim",
|
||||
Icon: "/icon/vim.svg",
|
||||
Command: sql.NullString{Valid: true, String: "vim"},
|
||||
@@ -377,7 +377,7 @@ func TestSubAgentAPI(t *testing.T) {
|
||||
},
|
||||
expectApps: []database.WorkspaceApp{
|
||||
{
|
||||
Slug: "valid-app",
|
||||
Slug: "511ctirn-valid-app",
|
||||
DisplayName: "Valid App",
|
||||
SharingLevel: database.AppSharingLevelOwner,
|
||||
Health: database.WorkspaceAppHealthDisabled,
|
||||
@@ -410,19 +410,19 @@ func TestSubAgentAPI(t *testing.T) {
|
||||
},
|
||||
expectApps: []database.WorkspaceApp{
|
||||
{
|
||||
Slug: "authenticated-app",
|
||||
Slug: "atpt261l-authenticated-app",
|
||||
SharingLevel: database.AppSharingLevelAuthenticated,
|
||||
Health: database.WorkspaceAppHealthDisabled,
|
||||
OpenIn: database.WorkspaceAppOpenInSlimWindow,
|
||||
},
|
||||
{
|
||||
Slug: "owner-app",
|
||||
Slug: "eh5gp1he-owner-app",
|
||||
SharingLevel: database.AppSharingLevelOwner,
|
||||
Health: database.WorkspaceAppHealthDisabled,
|
||||
OpenIn: database.WorkspaceAppOpenInSlimWindow,
|
||||
},
|
||||
{
|
||||
Slug: "public-app",
|
||||
Slug: "oopjevf1-public-app",
|
||||
SharingLevel: database.AppSharingLevelPublic,
|
||||
Health: database.WorkspaceAppHealthDisabled,
|
||||
OpenIn: database.WorkspaceAppOpenInSlimWindow,
|
||||
@@ -443,13 +443,13 @@ func TestSubAgentAPI(t *testing.T) {
|
||||
},
|
||||
expectApps: []database.WorkspaceApp{
|
||||
{
|
||||
Slug: "tab-app",
|
||||
Slug: "ci9500rm-tab-app",
|
||||
SharingLevel: database.AppSharingLevelOwner,
|
||||
Health: database.WorkspaceAppHealthDisabled,
|
||||
OpenIn: database.WorkspaceAppOpenInTab,
|
||||
},
|
||||
{
|
||||
Slug: "window-app",
|
||||
Slug: "p17s76re-window-app",
|
||||
SharingLevel: database.AppSharingLevelOwner,
|
||||
Health: database.WorkspaceAppHealthDisabled,
|
||||
OpenIn: database.WorkspaceAppOpenInSlimWindow,
|
||||
@@ -479,7 +479,7 @@ func TestSubAgentAPI(t *testing.T) {
|
||||
},
|
||||
expectApps: []database.WorkspaceApp{
|
||||
{
|
||||
Slug: "full-app",
|
||||
Slug: "0ccdbg39-full-app",
|
||||
Command: sql.NullString{Valid: true, String: "echo hello"},
|
||||
DisplayName: "Full Featured App",
|
||||
External: true,
|
||||
@@ -507,7 +507,7 @@ func TestSubAgentAPI(t *testing.T) {
|
||||
},
|
||||
expectApps: []database.WorkspaceApp{
|
||||
{
|
||||
Slug: "no-health-app",
|
||||
Slug: "nphrhbh6-no-health-app",
|
||||
Health: database.WorkspaceAppHealthDisabled,
|
||||
SharingLevel: database.AppSharingLevelOwner,
|
||||
OpenIn: database.WorkspaceAppOpenInSlimWindow,
|
||||
@@ -531,7 +531,7 @@ func TestSubAgentAPI(t *testing.T) {
|
||||
},
|
||||
expectApps: []database.WorkspaceApp{
|
||||
{
|
||||
Slug: "duplicate-app",
|
||||
Slug: "uiklfckv-duplicate-app",
|
||||
DisplayName: "First App",
|
||||
SharingLevel: database.AppSharingLevelOwner,
|
||||
Health: database.WorkspaceAppHealthDisabled,
|
||||
@@ -568,14 +568,14 @@ func TestSubAgentAPI(t *testing.T) {
|
||||
},
|
||||
expectApps: []database.WorkspaceApp{
|
||||
{
|
||||
Slug: "duplicate-app",
|
||||
Slug: "uiklfckv-duplicate-app",
|
||||
DisplayName: "First Duplicate",
|
||||
SharingLevel: database.AppSharingLevelOwner,
|
||||
Health: database.WorkspaceAppHealthDisabled,
|
||||
OpenIn: database.WorkspaceAppOpenInSlimWindow,
|
||||
},
|
||||
{
|
||||
Slug: "valid-app",
|
||||
Slug: "511ctirn-valid-app",
|
||||
DisplayName: "Valid App",
|
||||
SharingLevel: database.AppSharingLevelOwner,
|
||||
Health: database.WorkspaceAppHealthDisabled,
|
||||
@@ -754,7 +754,7 @@ func TestSubAgentAPI(t *testing.T) {
|
||||
apps, err := db.GetWorkspaceAppsByAgentID(dbauthz.AsSystemRestricted(ctx), agentID) //nolint:gocritic // this is a test.
|
||||
require.NoError(t, err)
|
||||
require.Len(t, apps, 1)
|
||||
require.Equal(t, "duplicate-slug", apps[0].Slug)
|
||||
require.Equal(t, "k5jd7a99-duplicate-slug", apps[0].Slug)
|
||||
require.Equal(t, "First Duplicate", apps[0].DisplayName)
|
||||
})
|
||||
})
|
||||
@@ -1128,7 +1128,7 @@ func TestSubAgentAPI(t *testing.T) {
|
||||
apps, err := api.Database.GetWorkspaceAppsByAgentID(dbauthz.AsSystemRestricted(ctx), agentID) //nolint:gocritic // this is a test.
|
||||
require.NoError(t, err)
|
||||
require.Len(t, apps, 1)
|
||||
require.Equal(t, "custom-app", apps[0].Slug)
|
||||
require.Equal(t, "v4qhkq17-custom-app", apps[0].Slug)
|
||||
require.Equal(t, "Custom App", apps[0].DisplayName)
|
||||
})
|
||||
|
||||
|
||||
Reference in New Issue
Block a user