From 8a1a5988b17679e183d12f86a99b78a5e06e259e Mon Sep 17 00:00:00 2001 From: Ian Littman Date: Thu, 8 Jan 2026 16:18:47 -0600 Subject: [PATCH] Fix duplicate label check behavior on spec apply endpoint (#38056) ## Testing - [x] Added/updated automated tests For unreleased bug fixes in a release candidate, one of: - [x] Confirmed that the fix is not expected to adversely impact load test results --- server/datastore/mysql/labels.go | 2 +- server/datastore/mysql/labels_test.go | 50 +++++++++++++++++++++++++++ 2 files changed, 51 insertions(+), 1 deletion(-) diff --git a/server/datastore/mysql/labels.go b/server/datastore/mysql/labels.go index fcc6c99b87..2bb27688b4 100644 --- a/server/datastore/mysql/labels.go +++ b/server/datastore/mysql/labels.go @@ -182,7 +182,7 @@ func (ds *Datastore) ApplyLabelSpecsWithAuthor(ctx context.Context, specs []*fle (existingLabel.TeamID != nil && spec.TeamID == nil || existingLabel.TeamID == nil && spec.TeamID != nil || (existingLabel.TeamID != nil && spec.TeamID != nil && *existingLabel.TeamID != *spec.TeamID)) { - return ctxerr.Wrap(ctx, err, "one or more specified labels exists on another team") + return ctxerr.New(ctx, "one or more specified labels exists on another team") } } } diff --git a/server/datastore/mysql/labels_test.go b/server/datastore/mysql/labels_test.go index a3766f318f..32a3b05718 100644 --- a/server/datastore/mysql/labels_test.go +++ b/server/datastore/mysql/labels_test.go @@ -105,6 +105,7 @@ func TestLabels(t *testing.T) { {"UpdateLabelMembershipForTransferredHost", testUpdateLabelMembershipForTransferredHost}, {"SetAsideLabels", testSetAsideLabels}, {"ApplyLabelSpecsWithManualTeamLabels", testApplyLabelSpecsWithManualTeamLabels}, + {"ApplyLabelSpecsErrorsWhenLabelExistsOnAnotherTeam", testApplyLabelSpecsErrorsWhenLabelExistsOnAnotherTeam}, } // call TruncateTables first to remove migration-created labels TruncateTables(t, ds) @@ -3307,6 +3308,55 @@ func testSetAsideLabels(t *testing.T, ds *Datastore) { } } +func testApplyLabelSpecsErrorsWhenLabelExistsOnAnotherTeam(t *testing.T, ds *Datastore) { + ctx := t.Context() + + // Create two teams + team1, err := ds.NewTeam(ctx, &fleet.Team{Name: "team1_label_conflict"}) + require.NoError(t, err) + team2, err := ds.NewTeam(ctx, &fleet.Team{Name: "team2_label_conflict"}) + require.NoError(t, err) + + // Create a label on team1 + err = ds.ApplyLabelSpecs(ctx, []*fleet.LabelSpec{ + {Name: "conflicting-label", Query: "SELECT 1", TeamID: &team1.ID}, + }) + require.NoError(t, err) + + // Try to create a label with the same name on team2 - should error + err = ds.ApplyLabelSpecs(ctx, []*fleet.LabelSpec{ + {Name: "conflicting-label", Query: "SELECT 2", TeamID: &team2.ID}, + }) + require.Error(t, err) + require.Contains(t, err.Error(), "one or more specified labels exists on another team") + + // Try to create a label with the same name globally (no team) - should error + err = ds.ApplyLabelSpecs(ctx, []*fleet.LabelSpec{ + {Name: "conflicting-label", Query: "SELECT 3"}, + }) + require.Error(t, err) + require.Contains(t, err.Error(), "one or more specified labels exists on another team") + + // Create a global label + err = ds.ApplyLabelSpecs(ctx, []*fleet.LabelSpec{ + {Name: "global-label", Query: "SELECT 4"}, + }) + require.NoError(t, err) + + // Try to create a label with the same name on a team - should error + err = ds.ApplyLabelSpecs(ctx, []*fleet.LabelSpec{ + {Name: "global-label", Query: "SELECT 5", TeamID: &team2.ID}, + }) + require.Error(t, err) + require.Contains(t, err.Error(), "one or more specified labels exists on another team") + + // Updating the original label on the same team should still work + err = ds.ApplyLabelSpecs(ctx, []*fleet.LabelSpec{ + {Name: "conflicting-label", Query: "SELECT 1 updated", TeamID: &team1.ID}, + }) + require.NoError(t, err) +} + func testApplyLabelSpecsWithManualTeamLabels(t *testing.T, ds *Datastore) { ctx := t.Context() teamFilter := fleet.TeamFilter{User: test.UserAdmin}