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>
This commit is contained in:
Sharon Katz
2026-05-11 09:54:59 -04:00
committed by GitHub
co-authored by Copilot Autofix powered by AI
parent decdaf0726
commit 89517cce2f
3 changed files with 52 additions and 1 deletions
@@ -0,0 +1 @@
- Improved `fleetctl gitops` to warn when `labels:` is specified in no-team/unassigned files, where it is not supported.
+5 -1
View File
@@ -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)
+46
View File
@@ -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()