Fix fleetctl apply ignoring spec.fleet (#44894)

**Related issue:** Resolves #44892

Claude also added tests, since this wasn't covered before, but I've kept
them in a separate commit in case they're not needed.

# Checklist for submitter

## Testing

- [x] Added/updated automated tests
- [ ] QA'd all new/changed functionality manually

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **Bug Fixes**
* Improved spec parsing to correctly accept resources declared as either
team or fleet, handling nested spec keys consistently and preserving
backward-compatible behavior.

* **Tests**
* Added and updated tests and fixtures to validate parsing across both
team/fleet variants and to assert specific conflict/reporting behavior
when both keys are present.

<!-- review_stack_entry_start -->

[![Review Change
Stack](https://storage.googleapis.com/coderabbit_public_assets/review-stack-in-coderabbit-ui.svg)](https://app.coderabbit.ai/change-stack/fleetdm/fleet/pull/44894?utm_source=github_walkthrough&utm_medium=github&utm_campaign=change_stack)

<!-- review_stack_entry_end -->
<!-- end of auto-generated comment: release notes by coderabbit.ai -->

---------

Co-authored-by: Scott Gress <scott@fleetdm.com>
This commit is contained in:
Steven Palmesano
2026-05-29 11:23:19 -05:00
committed by GitHub
co-authored by Scott Gress
parent 4504b11792
commit 64f601891e
3 changed files with 80 additions and 12 deletions
+12 -12
View File
@@ -318,13 +318,13 @@ func TestApplyTeamSpecs(t *testing.T) {
apiVersion: v1
kind: fleet
spec:
team:
fleet:
name: team2
---
apiVersion: v1
kind: fleet
spec:
team:
fleet:
agent_options:
config:
views:
@@ -375,7 +375,7 @@ spec:
apiVersion: v1
kind: fleet
spec:
team:
fleet:
name: team1
mdm:
windows_updates:
@@ -407,7 +407,7 @@ spec:
apiVersion: v1
kind: fleet
spec:
team:
fleet:
name: team1
mdm:
macos_settings:
@@ -449,7 +449,7 @@ spec:
apiVersion: v1
kind: fleet
spec:
team:
fleet:
agent_options:
config:
views:
@@ -507,7 +507,7 @@ spec:
apiVersion: v1
kind: fleet
spec:
team:
fleet:
agent_options:
name: team1
mdm:
@@ -528,7 +528,7 @@ spec:
apiVersion: v1
kind: fleet
spec:
team:
fleet:
name: team1
mdm:
macos_updates:
@@ -543,7 +543,7 @@ spec:
apiVersion: v1
kind: fleet
spec:
team:
fleet:
name: team1
mdm:
macos_updates:
@@ -604,7 +604,7 @@ spec:
apiVersion: v1
kind: fleet
spec:
team:
fleet:
name: team1
webhook_settings:
host_status_webhook:
@@ -635,7 +635,7 @@ spec:
apiVersion: v1
kind: fleet
spec:
team:
fleet:
name: team1
webhook_settings:
`,
@@ -658,7 +658,7 @@ spec:
apiVersion: v1
kind: fleet
spec:
team:
fleet:
name: team1
integrations:
google_calendar:
@@ -681,7 +681,7 @@ spec:
apiVersion: v1
kind: fleet
spec:
team:
fleet:
name: team1
integrations:
google_calendar:
+5
View File
@@ -234,7 +234,12 @@ func GroupFromBytes(b []byte, options ...GroupFromBytesOpts) (*Group, error) {
if err := yaml.Unmarshal(s.Spec, &rawTeam); err != nil {
return nil, fmt.Errorf("unmarshaling %s spec: %w", kind, err)
}
// Support `team` (for backwards compatibility) but defer to `fleet` if available.
teamRaw := rawTeam["team"]
if fleetRaw, ok := rawTeam["fleet"]; ok {
teamRaw = fleetRaw
}
var err error
teamRaw, deprecatedKeysMap, err = rewriteNewToOldKeys(teamRaw, fleet.TeamSpec{})
if err != nil {
+63
View File
@@ -460,3 +460,66 @@ func TestRewriteNewToOldKeys(t *testing.T) {
assert.Equal(t, "fleet", conflictErr.New)
})
}
func TestGroupFromBytesTeamKinds(t *testing.T) {
tests := []struct {
name string
in []byte
}{
{
"kind: team with team: key",
[]byte(`
apiVersion: v1
kind: team
spec:
team:
name: macOS
`),
},
{
"kind: fleet with fleet: key",
[]byte(`
apiVersion: v1
kind: fleet
spec:
fleet:
name: macOS
`),
},
{
"kind: fleet with team: key",
[]byte(`
apiVersion: v1
kind: fleet
spec:
team:
name: macOS
`),
},
{
"kind: team with fleet: key",
[]byte(`
apiVersion: v1
kind: team
spec:
fleet:
name: macOS
`),
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
g, err := GroupFromBytes(tt.in)
require.NoError(t, err)
require.Len(t, g.Teams, 1)
require.NotNil(t, g.Teams[0])
var team map[string]json.RawMessage
require.NoError(t, json.Unmarshal(g.Teams[0], &team))
name, ok := team["name"]
require.True(t, ok)
assert.Equal(t, `"macOS"`, string(name))
})
}
}