Description
GithubOrganization spec has four list fields that Permission Manager writes via Server-Side Apply (cloudoperators/permission-manager#67):
spec.defaultPublicRepositoryTeams ([]GithubTeamWithPermission)
spec.defaultPrivateRepositoryTeams ([]GithubTeamWithPermission)
spec.defaultInternalRepositoryTeams ([]GithubTeamWithPermission)
spec.organizationOwnerTeams ([]string)
These are currently untagged, so Kubernetes treats them as listType=atomic — the entire list is a single owned value. With max-concurrent-reconciles=5, two ASR reconciles on the same org both do SSA applies with the same field manager (permission-manager). Because the lists are atomic, the second apply clobbers the first ASR's entries.
Fix: Add kubebuilder list-type markers so the apiserver merges by element key instead of replacing the whole list atomically.
Changes
api/v1/githuborganization_types.go
// +listType=map
// +listMapKey=team
DefaultPublicRepositoryTeams []GithubTeamWithPermission
// +listType=map
// +listMapKey=team
DefaultPrivateRepositoryTeams []GithubTeamWithPermission
// +listType=map
// +listMapKey=team
DefaultInternalRepositoryTeams []GithubTeamWithPermission
// +listType=set
OrganizationOwnerTeams []string
GithubTeamWithPermission.team is the natural unique key. OrganizationOwnerTeams is []string so it becomes a set.
Then run make manifests — this adds x-kubernetes-list-type: map / x-kubernetes-list-map-keys: [team] to the three struct-list fields and x-kubernetes-list-type: set to organizationOwnerTeams in:
config/crd/bases/repo-guard.cloudoperators.dev_githuborganizations.yaml
charts/repo-guard/crds/githuborganization-crd.yaml
No controller logic changes needed — listType is a pure apiserver/SSA instruction.
Result
Two PM reconciles applying different team entries concurrently produce the correct merged result — no silent overwrites. This unblocks cloudoperators/permission-manager#67 from safely removing RetryOnConflict without introducing the race described in cloudoperators/permission-manager#66.
Reference Issues
Part of epic: cloudoperators/permission-manager#66 — GithubOrganization: Ownership, Concurrency, and Missing Fields
Unblocks: cloudoperators/permission-manager#67 — Implement GithubOrganization writes via Server-Side Apply
Description
GithubOrganizationspec has four list fields that Permission Manager writes via Server-Side Apply (cloudoperators/permission-manager#67):spec.defaultPublicRepositoryTeams([]GithubTeamWithPermission)spec.defaultPrivateRepositoryTeams([]GithubTeamWithPermission)spec.defaultInternalRepositoryTeams([]GithubTeamWithPermission)spec.organizationOwnerTeams([]string)These are currently untagged, so Kubernetes treats them as
listType=atomic— the entire list is a single owned value. Withmax-concurrent-reconciles=5, two ASR reconciles on the same org both do SSA applies with the same field manager (permission-manager). Because the lists are atomic, the second apply clobbers the first ASR's entries.Fix: Add kubebuilder list-type markers so the apiserver merges by element key instead of replacing the whole list atomically.
Changes
api/v1/githuborganization_types.goGithubTeamWithPermission.teamis the natural unique key.OrganizationOwnerTeamsis[]stringso it becomes a set.Then run
make manifests— this addsx-kubernetes-list-type: map/x-kubernetes-list-map-keys: [team]to the three struct-list fields andx-kubernetes-list-type: settoorganizationOwnerTeamsin:config/crd/bases/repo-guard.cloudoperators.dev_githuborganizations.yamlcharts/repo-guard/crds/githuborganization-crd.yamlNo controller logic changes needed —
listTypeis a pure apiserver/SSA instruction.Result
Two PM reconciles applying different team entries concurrently produce the correct merged result — no silent overwrites. This unblocks cloudoperators/permission-manager#67 from safely removing
RetryOnConflictwithout introducing the race described in cloudoperators/permission-manager#66.Reference Issues
Part of epic: cloudoperators/permission-manager#66 — GithubOrganization: Ownership, Concurrency, and Missing Fields
Unblocks: cloudoperators/permission-manager#67 — Implement GithubOrganization writes via Server-Side Apply