Skip to content

Commit 2aadf9f

Browse files
fix: unique per resource not build
1 parent 2417efc commit 2aadf9f

File tree

3 files changed

+83
-75
lines changed

3 files changed

+83
-75
lines changed

coderd/database/dump.sql

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

coderd/database/migrations/000330_workspace_agent_name_unique_trigger.up.sql

Lines changed: 37 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,37 +1,49 @@
11
CREATE OR REPLACE FUNCTION check_workspace_agent_name_unique()
22
RETURNS TRIGGER AS $$
33
DECLARE
4-
workspace_build_id uuid;
5-
existing_count integer;
4+
provisioner_job_id uuid;
5+
agents_with_name integer;
66
BEGIN
7-
-- Get the workspace_build.id for this agent by following the relationship chain:
8-
-- workspace_agents -> workspace_resources -> provisioner_jobs -> workspace_builds
9-
SELECT wb.id INTO workspace_build_id
10-
FROM workspace_resources wr
11-
JOIN provisioner_jobs pj ON wr.job_id = pj.id
12-
JOIN workspace_builds wb ON pj.id = wb.job_id
13-
WHERE wr.id = NEW.resource_id;
7+
-- Count how many agents in this resource already have
8+
-- the given agent ID.
9+
SELECT COUNT(*) INTO agents_with_name
10+
FROM workspace_agents
11+
WHERE workspace_agents.resource_id = NEW.resource_id
12+
AND workspace_agents.name = NEW.name
13+
AND workspace_agents.id != NEW.id;
1414

15-
-- If we couldn't find a workspace_build_id, allow the insert (might be a template import or other edge case)
16-
IF workspace_build_id IS NULL THEN
17-
RETURN NEW;
15+
-- If there's already an agent with this name, raise an error
16+
IF agents_with_name > 0 THEN
17+
RAISE EXCEPTION 'workspace agent name "%" already exists in this workspace resource', NEW.name
18+
USING ERRCODE = 'unique_violation';
1819
END IF;
1920

20-
-- Check if there's already an agent with this name for this workspace build
21-
SELECT COUNT(*) INTO existing_count
22-
FROM workspace_agents wa
23-
JOIN workspace_resources wr ON wa.resource_id = wr.id
24-
JOIN provisioner_jobs pj ON wr.job_id = pj.id
25-
JOIN workspace_builds wb ON pj.id = wb.job_id
26-
WHERE wb.id = workspace_build_id
27-
AND wa.name = NEW.name
28-
AND wa.id != NEW.id; -- Exclude the current agent (for updates)
21+
-- Get the provisioner_jobs.id for this agent by following the relationship chain:
22+
-- workspace_agents -> workspace_resources -> provisioner_jobs
23+
-- SELECT pj.id INTO provisioner_job_id
24+
-- FROM workspace_resources wr
25+
-- JOIN provisioner_jobs pj ON wr.job_id = pj.id
26+
-- WHERE wr.id = NEW.resource_id;
27+
28+
-- If we couldn't find a provisioner_job.id, allow the insert (might be a template import or other edge case)
29+
-- IF provisioner_job_id IS NULL THEN
30+
-- RETURN NEW;
31+
-- END IF;
32+
33+
-- Check if there's already an agent with this name for this provisioner job
34+
-- SELECT COUNT(*) INTO existing_count
35+
-- FROM workspace_agents wa
36+
-- JOIN workspace_resources wr ON wa.resource_id = wr.id
37+
-- JOIN provisioner_jobs pj ON wr.job_id = pj.id
38+
-- WHERE pj.id = provisioner_job_id
39+
-- AND wa.name = NEW.name
40+
-- AND wa.id != NEW.id;
2941

3042
-- If there's already an agent with this name, raise an error
31-
IF existing_count > 0 THEN
32-
RAISE EXCEPTION 'workspace agent name "%" already exists in this workspace', NEW.name
33-
USING ERRCODE = 'unique_violation';
34-
END IF;
43+
-- IF existing_count > 0 THEN
44+
-- RAISE EXCEPTION 'workspace agent name "%" already exists in this provisioner job', NEW.name
45+
-- USING ERRCODE = 'unique_violation';
46+
-- END IF;
3547

3648
RETURN NEW;
3749
END;

coderd/database/querier_test.go

Lines changed: 8 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@ import (
77
"errors"
88
"fmt"
99
"sort"
10-
"sync/atomic"
1110
"testing"
1211
"time"
1312

@@ -4711,13 +4710,11 @@ func TestGetPresetsAtFailureLimit(t *testing.T) {
47114710
func TestWorkspaceAgentNameUniqueTrigger(t *testing.T) {
47124711
t.Parallel()
47134712

4714-
var builds atomic.Int32
4715-
47164713
if !dbtestutil.WillUsePostgres() {
47174714
t.Skip("This test makes use of a database trigger not implemented in dbmem")
47184715
}
47194716

4720-
createWorkspaceWithAgent := func(t *testing.T, db database.Store, org database.Organization, agentName string) (database.WorkspaceTable, database.TemplateVersion, database.WorkspaceAgent) {
4717+
createWorkspaceWithAgent := func(t *testing.T, db database.Store, org database.Organization, agentName string) (database.WorkspaceTable, database.WorkspaceResource, database.WorkspaceAgent) {
47214718
t.Helper()
47224719

47234720
user := dbgen.User(t, db, database.User{})
@@ -4740,7 +4737,7 @@ func TestWorkspaceAgentNameUniqueTrigger(t *testing.T) {
47404737
OrganizationID: org.ID,
47414738
})
47424739
build := dbgen.WorkspaceBuild(t, db, database.WorkspaceBuild{
4743-
BuildNumber: builds.Add(1),
4740+
BuildNumber: 1,
47444741
JobID: job.ID,
47454742
WorkspaceID: workspace.ID,
47464743
TemplateVersionID: templateVersion.ID,
@@ -4753,40 +4750,27 @@ func TestWorkspaceAgentNameUniqueTrigger(t *testing.T) {
47534750
Name: agentName,
47544751
})
47554752

4756-
return workspace, templateVersion, agent
4753+
return workspace, resource, agent
47574754
}
47584755

4759-
t.Run("DuplicateNamesInSameWorkspace", func(t *testing.T) {
4756+
t.Run("DuplicateNamesInSameWorkspaceBuild", func(t *testing.T) {
47604757
t.Parallel()
47614758

47624759
db, _ := dbtestutil.NewDB(t)
47634760
org := dbgen.Organization(t, db, database.Organization{})
47644761
ctx := testutil.Context(t, testutil.WaitShort)
47654762

47664763
// Given: A workspace with an agent
4767-
workspace1, templateVersion1, agent1 := createWorkspaceWithAgent(t, db, org, "duplicate-agent")
4768-
require.Equal(t, "duplicate-agent", agent1.Name)
4764+
_, resource, agent := createWorkspaceWithAgent(t, db, org, "duplicate-agent")
4765+
require.Equal(t, "duplicate-agent", agent.Name)
47694766

47704767
// When: Another agent is created for that workspace with the same name.
4771-
job2 := dbgen.ProvisionerJob(t, db, nil, database.ProvisionerJob{
4772-
Type: database.ProvisionerJobTypeWorkspaceBuild,
4773-
OrganizationID: org.ID,
4774-
})
4775-
build2 := dbgen.WorkspaceBuild(t, db, database.WorkspaceBuild{
4776-
BuildNumber: builds.Add(1),
4777-
JobID: job2.ID,
4778-
WorkspaceID: workspace1.ID,
4779-
TemplateVersionID: templateVersion1.ID,
4780-
})
4781-
resource2 := dbgen.WorkspaceResource(t, db, database.WorkspaceResource{
4782-
JobID: build2.JobID,
4783-
})
47844768
_, err := db.InsertWorkspaceAgent(ctx, database.InsertWorkspaceAgentParams{
47854769
ID: uuid.New(),
47864770
CreatedAt: time.Now(),
47874771
UpdatedAt: time.Now(),
47884772
Name: "duplicate-agent", // Same name as agent1
4789-
ResourceID: resource2.ID,
4773+
ResourceID: resource.ID,
47904774
AuthToken: uuid.New(),
47914775
Architecture: "amd64",
47924776
OperatingSystem: "linux",
@@ -4798,7 +4782,7 @@ func TestWorkspaceAgentNameUniqueTrigger(t *testing.T) {
47984782
var pqErr *pq.Error
47994783
require.True(t, errors.As(err, &pqErr))
48004784
require.Equal(t, pq.ErrorCode("23505"), pqErr.Code) // unique_violation
4801-
require.Contains(t, pqErr.Message, `workspace agent name "duplicate-agent" already exists in this workspace`)
4785+
require.Contains(t, pqErr.Message, `workspace agent name "duplicate-agent" already exists in this workspace resource`)
48024786
})
48034787

48044788
t.Run("SameNamesInDifferentWorkspaces", func(t *testing.T) {

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