Skip to content

[release/13.5] Set a default fsGroup for Kubernetes persistent volumes - #19401

Merged
Jose Perez Rodriguez (joperezr) merged 3 commits into
release/13.5from
backport/pr-19374-to-release/13.5
Aug 15, 2026
Merged

[release/13.5] Set a default fsGroup for Kubernetes persistent volumes#19401
Jose Perez Rodriguez (joperezr) merged 3 commits into
release/13.5from
backport/pr-19374-to-release/13.5

Conversation

@aspire-repo-bot

@aspire-repo-bot aspire-repo-bot Bot commented Aug 14, 2026

Copy link
Copy Markdown
Contributor

Backport of #19374 to release/13.5

/cc Adam Ratzman (@adamint) Mitch Denny (@mitchdenny)

Customer Impact

Customers using the new first-class Kubernetes persistent-volume support in Aspire 13.5 can get a healthy pod and bound PVC, but non-root workloads fail with permission denied on their first write. Without this fix, users must manually configure fsGroup or use a root init-container workaround.

Testing

Publisher tests verify the default fsGroup: 2000 and OnRootMismatch policy, override to 3000, opt-out, and read-only mounts. A live AKS E2E passed, proving non-root UID 1654 can write, the same PVC survives redeployment, persisted data remains readable, and the replacement pod can write after changing to group 3000. Backport CI is running with no failures.

Risk

Low. The behavior is limited to the new experimental first-class WithPersistentVolume(...) path; ordinary workloads and legacy PVC generation are unchanged. Users can override or remove the generated security context.

Regression?

No. First-class Kubernetes persistent volumes are new in Aspire 13.5, so this completes their usable default behavior rather than fixing functionality that worked in an earlier release.

Copilot AI balanced review requested due to automatic review settings August 14, 2026 22:04
@github-actions

Copy link
Copy Markdown
Contributor

🚀 Dogfood this PR with:

⚠️ WARNING: Do not do this without first carefully reviewing the code of this PR to satisfy yourself it is safe.

curl -fsSL https://raw.githubusercontent.com/microsoft/aspire/main/eng/scripts/get-aspire-cli-pr.sh | bash -s -- 19401

Or

  • Run remotely in PowerShell:
iex "& { $(irm https://raw.githubusercontent.com/microsoft/aspire/main/eng/scripts/get-aspire-cli-pr.ps1) } 19401"

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Note

Copilot could not run the full agentic suite for this review because it was automatically requested on a bot-authored pull request. Request a review from Copilot under Reviewers to retry with the full agentic suite. Improved support for bot-authored pull requests is coming soon.

Updates Kubernetes publishing behavior to apply a default pod securityContext (fsGroup + change policy) when binding to first-class persistent volumes, and adds/updates tests to validate defaults and customizations.

Changes:

  • Apply default fsGroup (2000) and fsGroupChangePolicy (OnRootMismatch) for workloads with first-class persistent volume bindings.
  • Add unit/snapshot coverage for overriding and removing the default pod security context.
  • Expand AKS end-to-end coverage to verify fsGroup behavior and add an additional API write path.

Reviewed changes

Copilot reviewed 9 out of 9 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
tests/Aspire.Hosting.Kubernetes.Tests/Snapshots/KubernetesPublisherTests.PublishAsync_WithFirstClassPersistentVolume_OnProject_BindsViaMountPathOverload#00.verified.yaml Snapshot updated to include default pod securityContext for PV binding.
tests/Aspire.Hosting.Kubernetes.Tests/Snapshots/KubernetesPublisherTests.PublishAsync_WithFirstClassPersistentVolume_KubernetesCustomizationOverridesDefaultFsGroup.verified.yaml New snapshot validating fsGroup override while retaining default change policy.
tests/Aspire.Hosting.Kubernetes.Tests/Snapshots/KubernetesPublisherTests.PublishAsync_WithFirstClassPersistentVolume_KubernetesCustomizationCanRemoveDefaultSecurityContext.verified.yaml New snapshot validating securityContext removal via customization.
tests/Aspire.Hosting.Kubernetes.Tests/Snapshots/KubernetesPublisherTests.PublishAsync_WithFirstClassPersistentVolume_FallsThroughForUnboundVolumes#00.verified.yaml Snapshot updated to include default pod securityContext.
tests/Aspire.Hosting.Kubernetes.Tests/Snapshots/KubernetesPublisherTests.PublishAsync_WithFirstClassPersistentVolume_BindsByName_PromotesToStatefulSet#00.verified.yaml Snapshot updated to include default pod securityContext.
tests/Aspire.Hosting.Kubernetes.Tests/KubernetesPublisherTests.cs New unit tests + assertions around generated securityContext behavior.
tests/Aspire.Deployment.EndToEnd.Tests/AksPersistentVolumeDeploymentTests.cs E2E now verifies filesystem group on AKS and adds a new write action.
src/Aspire.Hosting.Kubernetes/KubernetesResource.cs Implements default fsGroup/fsGroupChangePolicy when PV binding annotation is present.
src/Aspire.Hosting.Kubernetes/KubernetesPersistentVolumeExtensions.cs Docs updated to describe default fsGroup behavior and customization mechanism.

💡 Add a code-review agent skill for context-aware, tailored reviews. Learn more in the docs.

@@ -0,0 +1,40 @@
---
Comment on lines +239 to +242
/// <c>mountPath</c> instead. The generated pod uses an Aspire-managed
/// <c>fsGroup</c> of <c>2000</c> with an <c>OnRootMismatch</c> change policy so
/// non-root containers can access supported volumes without matching the image's
/// primary group. Use
Comment on lines +191 to +193
var securityContext = Workload.PodTemplate.Spec.SecurityContext ??= new();
securityContext.FsGroup ??= DefaultPersistentVolumeFsGroup;
securityContext.FsGroupChangePolicy ??= DefaultPersistentVolumeFsGroupChangePolicy;
Comment on lines +355 to +370
await auto.RunCommandAsync(
$"FS_GROUP=$(kubectl get statefulset apiservice-statefulset --namespace \"$NS\" -o jsonpath='{{.spec.template.spec.securityContext.fsGroup}}') && " +
$"test \"$FS_GROUP\" = \"{expectedFsGroup}\" && " +
// Verify the Linux identity and mount ownership reported as:
// id -u: 1654
// id -G: 1654 2000
// stat -c %g /srv/data: 2000
// This proves the write succeeds through group access rather than root privileges.
"PROCESS_UID=$(kubectl exec pod/apiservice-statefulset-0 --namespace \"$NS\" -- id -u) && " +
"PROCESS_GROUPS=$(kubectl exec pod/apiservice-statefulset-0 --namespace \"$NS\" -- id -G) && " +
"VOLUME_GROUP=$(kubectl exec pod/apiservice-statefulset-0 --namespace \"$NS\" -- stat -c %g /srv/data) && " +
"test \"$PROCESS_UID\" != \"0\" && " +
$"printf ' %s ' \"$PROCESS_GROUPS\" | grep --fixed-strings --quiet ' {expectedFsGroup} ' && " +
$"test \"$VOLUME_GROUP\" = \"{expectedFsGroup}\" && " +
$"echo \"StatefulSet uses fsGroup {expectedFsGroup}; pod UID is $PROCESS_UID with groups $PROCESS_GROUPS; /srv/data group is $VOLUME_GROUP\"",
counter);
@github-actions

This comment has been minimized.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>

Copilot-Session: efbea589-4f81-4baf-9e17-4f1af07af5ee
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: efbea589-4f81-4baf-9e17-4f1af07af5ee
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: efbea589-4f81-4baf-9e17-4f1af07af5ee
@aspire-repo-bot
aspire-repo-bot Bot force-pushed the backport/pr-19374-to-release/13.5 branch from e0f5a16 to 03ebfce Compare August 15, 2026 01:48
@github-actions

Copy link
Copy Markdown
Contributor

Tests selector (audit mode)

The full test matrix and all jobs still run in audit mode. The tests and jobs below are what selective CI would run under enforcement.

4 / 100 test projects · 3 jobs, from 9 changed files.

Selected test projects (4 / 100)

Aspire.Deployment.EndToEnd.Tests, Aspire.Hosting.Azure.Kubernetes.Tests, Aspire.Hosting.Docker.Tests, Aspire.Hosting.Kubernetes.Tests

Selected jobs (3)

deployment-e2e, extension-e2e, typescript-api-compat


How these were chosen — grouped by what changed

🔧 src/Aspire.Hosting.Kubernetes/KubernetesPersistentVolumeExtensions.cs (changed source)
1 directly: Aspire.Hosting.Kubernetes.Tests
2 via the project graph: Aspire.Hosting.Azure.Kubernetes.Tests (2 hops), Aspire.Hosting.Docker.Tests

🔧 src/Aspire.Hosting.Kubernetes/KubernetesResource.cs (changed source)
1 directly: Aspire.Hosting.Kubernetes.Tests

🧪 tests/Aspire.Deployment.EndToEnd.Tests/AksPersistentVolumeDeploymentTests.cs (changed test)
1 directly: Aspire.Deployment.EndToEnd.Tests

🧪 tests/Aspire.Hosting.Kubernetes.Tests/KubernetesPublisherTests.cs (changed test)
1 directly: Aspire.Hosting.Kubernetes.Tests

🧪 tests/Aspire.Hosting.Kubernetes.Tests/Snapshots/KubernetesPublisherTests.PublishAsync_WithFirstClassPersistentVolume_BindsByName_PromotesToStatefulSet#00.verified.yaml (changed test)
1 directly: Aspire.Hosting.Kubernetes.Tests

🧪 tests/Aspire.Hosting.Kubernetes.Tests/Snapshots/KubernetesPublisherTests.PublishAsync_WithFirstClassPersistentVolume_FallsThroughForUnboundVolumes#00.verified.yaml (changed test)
1 directly: Aspire.Hosting.Kubernetes.Tests

🧪 tests/Aspire.Hosting.Kubernetes.Tests/Snapshots/KubernetesPublisherTests.PublishAsync_WithFirstClassPersistentVolume_KubernetesCustomizationCanRemoveDefaultSecurityContext.verified.yaml (changed test)
1 directly: Aspire.Hosting.Kubernetes.Tests

🧪 tests/Aspire.Hosting.Kubernetes.Tests/Snapshots/KubernetesPublisherTests.PublishAsync_WithFirstClassPersistentVolume_KubernetesCustomizationOverridesDefaultFsGroup.verified.yaml (changed test)
1 directly: Aspire.Hosting.Kubernetes.Tests

🧪 tests/Aspire.Hosting.Kubernetes.Tests/Snapshots/KubernetesPublisherTests.PublishAsync_WithFirstClassPersistentVolume_OnProject_BindsViaMountPathOverload#00.verified.yaml (changed test)
1 directly: Aspire.Hosting.Kubernetes.Tests

Job reasons

Job Triggered by
deployment-e2e tests/Aspire.Deployment.EndToEnd.Tests/AksPersistentVolumeDeploymentTests.cs
• affected project Aspire.Hosting.Azure.Kubernetes
extension-e2e src/Aspire.Hosting.Kubernetes/KubernetesPersistentVolumeExtensions.cs, src/Aspire.Hosting.Kubernetes/KubernetesResource.cs
• affected project Aspire.Hosting.Kubernetes
typescript-api-compat affected project Aspire.Hosting.Kubernetes

Selection computed for commit 03ebfce.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Important change for the K8s story. Small scope and low risk. Approved.

@joperezr
Jose Perez Rodriguez (joperezr) merged commit ca6cbec into release/13.5 Aug 15, 2026
341 checks passed
@joperezr
Jose Perez Rodriguez (joperezr) deleted the backport/pr-19374-to-release/13.5 branch August 15, 2026 18:21
@microsoft-github-policy-service microsoft-github-policy-service Bot added this to the 13.5 milestone Aug 15, 2026
@aspire-repo-bot

Copy link
Copy Markdown
Contributor Author

✅ No documentation update needed.

Step 5 branch taken: "excluded → backport"

Exclusion reasons (from signals.json): base_branch_is_release, head_branch_is_backport, title_release_prefix, body_backport_marker.

Evidence:

This PR is a backport of already-merged PR #19374. Per workflow policy, backports are out of scope for docs generation since documentation should be authored against the original (forward) PR on the default branch, not duplicated for each backport. No triggered signals were evaluated since excluded == true overrides the recommendation.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants