From 89517cce2fd6d5063304788ca73fa89ccf38518e Mon Sep 17 00:00:00 2001 From: Sharon Katz <121527325+sharon-fdm@users.noreply.github.com> Date: Mon, 11 May 2026 09:54:59 -0400 Subject: [PATCH] Warn when labels key appears in no-team/unassigned GitOps files (#44865) Closes #42522 ## Changes When `labels:` appears in a no-team/unassigned GitOps file, log a warning and skip label parsing. This matches the existing pattern used by `agent_options` and `reports` in no-team files. A warning (not an error) is used intentionally to avoid breaking existing customer GitOps pipelines that may already have `labels:` in their no-team file. **After fix:** ``` [!] 'labels' is not supported in unassigned.yml. This key will be ignored. ``` ## Testing ### Manual testing Built `fleetctl` from the fixed branch against a local Fleet server (premium license). | Scenario | Result | |---|---| | `unassigned.yml` dry-run | Warning printed, succeeds | | `unassigned.yml` real run | Warning printed, succeeds | | `no-team.yml` dry-run | Warning printed, succeeds | | `no-team.yml` real run | Warning printed, succeeds | | `unassigned.yml` without labels | No warning, succeeds (no regression) | ### Unit tests - **`TestLabelsIgnoredInNoTeamFile`**: Sub-tests for both `no-team.yml` and `unassigned.yml` assert: (1) no error, (2) `LabelsPresent` is true, (3) no labels parsed, (4) warning logged. --------- Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- changes/42522-labels-not-supported-in-no-team | 1 + pkg/spec/gitops.go | 6 ++- pkg/spec/gitops_test.go | 46 +++++++++++++++++++ 3 files changed, 52 insertions(+), 1 deletion(-) create mode 100644 changes/42522-labels-not-supported-in-no-team diff --git a/changes/42522-labels-not-supported-in-no-team b/changes/42522-labels-not-supported-in-no-team new file mode 100644 index 0000000000..5dbb372e08 --- /dev/null +++ b/changes/42522-labels-not-supported-in-no-team @@ -0,0 +1 @@ +- Improved `fleetctl gitops` to warn when `labels:` is specified in no-team/unassigned files, where it is not supported. diff --git a/pkg/spec/gitops.go b/pkg/spec/gitops.go index a34f33262d..6f6a79c88c 100644 --- a/pkg/spec/gitops.go +++ b/pkg/spec/gitops.go @@ -525,7 +525,11 @@ func GitOpsFromFile(filePath, baseDir string, appConfig *fleet.EnrichedAppConfig // Get the labels. LabelsPresent tracks whether the key was in the YAML. if _, ok := top["labels"]; ok { result.LabelsPresent = true - multiError = parseLabels(top, result, baseDir, logFn, filePath, multiError) + if result.IsNoTeam() { + logFn("[!] 'labels' is not supported in %s. This key will be ignored.\n", filepath.Base(filePath)) + } else { + multiError = parseLabels(top, result, baseDir, logFn, filePath, multiError) + } } // Get other top-level entities. multiError = parseControls(top, result, logFn, filePath, multiError) diff --git a/pkg/spec/gitops_test.go b/pkg/spec/gitops_test.go index 3755909e17..4052d72262 100644 --- a/pkg/spec/gitops_test.go +++ b/pkg/spec/gitops_test.go @@ -2414,6 +2414,52 @@ labels: }) } +func TestLabelsIgnoredInNoTeamFile(t *testing.T) { + t.Parallel() + + t.Run("no-team.yml", func(t *testing.T) { + t.Parallel() + + config := "name: No team\nlabels:\n - name: test-label\n query: \"SELECT 1;\"\n description: test\nsoftware:\npolicies:\n" + noTeamPath, noTeamBasePath := createNamedFileOnTempDir(t, "no-team.yml", config) + + var logMessages []string + captureLogf := func(format string, a ...any) { + logMessages = append(logMessages, fmt.Sprintf(format, a...)) + } + + gitops, err := GitOpsFromFile(noTeamPath, noTeamBasePath, nil, captureLogf) + require.NoError(t, err) + + // LabelsPresent should be true (the key was in the YAML), but labels should not be parsed. + assert.True(t, gitops.LabelsPresent, "labels should be marked as present when explicitly set in no-team file") + assert.Empty(t, gitops.Labels, "labels should not be parsed in no-team file") + + // A warning should have been logged. + assert.Contains(t, strings.Join(logMessages, "\n"), "'labels' is not supported in no-team.yml") + }) + + t.Run("unassigned.yml", func(t *testing.T) { + t.Parallel() + + config := "name: Unassigned\nlabels:\n - name: test-label\n query: \"SELECT 1;\"\n description: test\nsoftware:\npolicies:\n" + unassignedPath, unassignedBasePath := createNamedFileOnTempDir(t, "unassigned.yml", config) + + var logMessages []string + captureLogf := func(format string, a ...any) { + logMessages = append(logMessages, fmt.Sprintf(format, a...)) + } + + gitops, err := GitOpsFromFile(unassignedPath, unassignedBasePath, nil, captureLogf) + require.NoError(t, err) + + assert.True(t, gitops.LabelsPresent, "labels should be marked as present when explicitly set in unassigned file") + assert.Empty(t, gitops.Labels, "labels should not be parsed in unassigned file") + + assert.Contains(t, strings.Join(logMessages, "\n"), "'labels' is not supported in unassigned.yml") + }) +} + func TestParsePoliciesGlob(t *testing.T) { t.Parallel()