Skip to content

Support multiple volume items in the same directory using subPath#301

Open
guicassolato wants to merge 2 commits into
mainfrom
volume-sub-paths
Open

Support multiple volume items in the same directory using subPath#301
guicassolato wants to merge 2 commits into
mainfrom
volume-sub-paths

Conversation

@guicassolato

@guicassolato guicassolato commented Mar 30, 2026

Copy link
Copy Markdown
Collaborator

Support multiple volume items in the same directory using subPath

Summary

This PR enhances the volume mounting functionality in the Authorino Operator to support mounting multiple files from different volumes into the same directory without conflicts. This is achieved by automatically using Kubernetes subPath volume mounts when the user specifies items in the volume configuration.

Key improvements:

  • Users can now mount multiple certificate files (e.g., CA certs) into /etc/ssl/certs/ alongside Authorino's default TLS server certificates without conflicts
  • Supports both ConfigMaps and Secrets with multiple items per volume
  • Maintains backward compatibility with existing volume configurations
  • Provides an intuitive API where mountPath can be specified as a directory, and individual files are automatically mounted with appropriate subpaths

Use cases enabled:

  • Mounting multiple CA certificates from cert-manager into /etc/ssl/certs/ for custom trust stores
  • Adding application-specific configuration files to standard directories
  • Deploying multiple certificate bundles without path conflicts
  • Co-locating user-defined certificates with Authorino's managed TLS certificates

Implementation Details

The implementation modifies pkg/reconcilers/deployment.go to intelligently handle the mountPath field:

  1. When items are NOT specified: The entire volume is mounted at mountPath (existing behavior, unchanged)
  2. When items ARE specified:
    • If mountPath already ends with /<item-path> → use as-is (backward compatible)
    • If mountPath is a directory → automatically append the item path

This allows users to write intuitive volume specifications like:

volumes:
  items:
    - name: ca-certs
      mountPath: /etc/ssl/certs/
      secrets:
        - ca-secret
      items:
        - key: ca.crt
          path: custom-ca.crt

Which results in the file being mounted at /etc/ssl/certs/custom-ca.crt with subPath: custom-ca.crt.

Tests Implemented

Comprehensive unit tests were added in pkg/reconcilers/deployment_test.go covering:

TestAuthorinoDeployment_VolumeMounts (9 test cases)

  • ✓ Volume without items mounts entire volume to a path
  • ✓ Directory mountPath with trailing slash automatically appends item path
  • ✓ Directory mountPath without trailing slash automatically appends item path
  • ✓ Full-path mountPath is preserved for backward compatibility
  • ✓ Multiple items from a single secret create separate subPath mounts
  • ✓ Multiple volumes mounting to the same directory without conflicts
  • ✓ Empty path field falls back to key name
  • ✓ Custom volumes coexist with Authorino's TLS server certificates
  • ✓ ConfigMaps with items work identically to Secrets

TestAuthorinoDeployment_VolumeProjections (4 test cases)

  • ✓ Single secret creates one projection
  • ✓ Multiple secrets create multiple projections
  • ✓ Single ConfigMap creates one projection
  • ✓ Multiple ConfigMaps create multiple projections

TestAuthorinoDeployment_DefaultMode (1 test case)

  • ✓ Custom file permissions are properly applied to projected volumes

All tests validate the exact volumeMount structure (name, mountPath, subPath, readOnly) and projected volume source configurations.

Related Issues

This is a follow-up to #282, which was closed after a workaround was found. However, the core issue of mounting multiple files in the same directory remained unresolved. This PR addresses that limitation properly by leveraging Kubernetes subPath mounts.

Thanks to @jhernand for the collaboration on the original issue!

How to test this PR

To test this branch in a local Kubernetes Kind cluster:

  1. Create a Kind cluster:

    kind create cluster
  2. Build and load the operator image:

    make docker-build OPERATOR_IMAGE=localhost/authorino-operator:dev
    podman save -o /tmp/image.tar localhost/authorino-operator:dev
    kind load image-archive /tmp/image.tar --name kind
  3. Deploy or update the operator:

    # If not already deployed
    make deploy OPERATOR_IMAGE=localhost/authorino-operator:dev
    
    # Or update existing deployment
    kubectl patch deployment authorino-operator -n authorino-operator-system \
      --type='json' \
      -p='[{"op":"replace","path":"/spec/template/spec/containers/0/image","value":"localhost/authorino-operator:dev"},
           {"op":"replace","path":"/spec/template/spec/containers/0/imagePullPolicy","value":"IfNotPresent"}]'
  4. Create test certificates with cert-manager:

    # Install cert-manager if not present
    kubectl apply -f https://github.com/cert-manager/cert-manager/releases/download/v1.14.0/cert-manager.yaml
    
    # Create CA certificates and server cert (example manifests in testing)
  5. Deploy an Authorino instance with multiple volumes:

    apiVersion: operator.authorino.kuadrant.io/v1beta1
    kind: Authorino
    metadata:
      name: authorino
      namespace: default
    spec:
      listener:
        tls:
          enabled: true
          certSecretRef:
            name: server-tls
      oidcServer:
        tls:
          enabled: true
          certSecretRef:
            name: server-tls
      volumes:
        items:
          - name: ca1-volume
            mountPath: /etc/ssl/certs/
            secrets:
              - ca1-secret
            items:
              - key: ca.crt
                path: ca1.crt
          - name: ca2-volume
            mountPath: /etc/ssl/certs/
            secrets:
              - ca2-secret
            items:
              - key: ca.crt
                path: ca2.crt
  6. Verify the volume mounts:

    # Check the deployment volume mounts
    kubectl get deployment authorino -o jsonpath='{.spec.template.spec.containers[0].volumeMounts}' | jq
    
    # Verify files in the pod
    kubectl exec -it deploy/authorino -- ls -la /etc/ssl/certs/

Co-authored by: 🤖 Claude Code

Summary by CodeRabbit

  • Bug Fixes

    • Improved volume mount construction for selective item mounting: when volume items are specified, each item now gets its own mount with correct mount path and SubPath handling. Empty item lists preserve previous behaviour.
  • Tests

    • Added comprehensive tests for volume mounts and projected volumes, covering multiple items, mount-path formats, DefaultMode, and TLS/OIDC certificate mounts.

… avoid clashing with the default tls cert paths

Signed-off-by: Guilherme Cassolato <guicassolato@gmail.com>
@coderabbitai

coderabbitai Bot commented Mar 30, 2026

Copy link
Copy Markdown
📝 Walkthrough

Walkthrough

AuthorinoDeployment's volume mount logic now creates per-item VolumeMounts when a Volume's Items are present, computing mount paths and SubPath per item; when Items is empty the entire volume is mounted at MountPath. Tests covering mounts, projections and defaultMode were added.

Changes

Cohort / File(s) Summary
Volume Mount Logic
pkg/reconcilers/deployment.go
Changed generation of container VolumeMounts: if volume.Items is empty, mount whole volume.MountPath; if non-empty, create one VolumeMount per item using item Path (or Key fallback), compute per-item MountPath by treating volume.MountPath as a directory (with special handling when single item already matches) and set SubPath to the item path.
Deployment Tests
pkg/reconcilers/deployment_test.go
Added comprehensive tests: TestAuthorinoDeployment_VolumeMounts (table-driven scenarios for mount path formats, item counts, secret/configmap sources, key->path fallback, and TLS/OIDC certificate mounts), TestAuthorinoDeployment_VolumeProjections (projected volume sources and item counts), and TestAuthorinoDeployment_DefaultMode (checks projected DefaultMode).

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Poem

🐰 I nibble paths and keys with glee,

Per-item mounts now hop free,
SubPaths snug in tidy rows,
Tests hop by to check how it goes,
A tiny dance of mounts — whee!

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title directly and clearly summarizes the main change: supporting multiple volume items in the same directory using Kubernetes subPath mounts.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch volume-sub-paths

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@guicassolato guicassolato self-assigned this Mar 30, 2026
@guicassolato guicassolato requested a review from a team March 30, 2026 11:22
@guicassolato guicassolato moved this to Ready For Review in Kuadrant Mar 30, 2026

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@pkg/reconcilers/deployment.go`:
- Around line 109-117: The current logic treats a MountPath that already ends
with a single item's filename as a base and appends additional item paths, which
produces invalid file/dir mixes when volume.Items has multiple entries; change
the logic around volume.MountPath handling so the "file-style" shortcut is only
applied when len(volume.Items) == 1 (i.e., preserve a file MountPath only for
single-item volumes). For cases where len(volume.Items) > 1, either
enforce/normalize MountPath to be a directory (ensure it ends with "/") before
appending each item or return an explicit validation error rejecting a
file-style MountPath for multi-item volumes; update the code around the
mountPath calculation (references: volume.MountPath, volume.Items, mountPath,
path) accordingly and add a unit test that covers the multi-item +
file-MountPath regression.
- Around line 119-123: The current VolumeMount creation sets SubPath
(volumeMount with fields Name, MountPath, SubPath) which prevents live updates
for Secret/ConfigMap volumes; either remove SubPath to mount the whole volume or
implement an explicit rollout on secret/configmap updates: prefer removing
SubPath in the volumeMount construction when the backing Volume is a Secret or
ConfigMap, and if partial-path mounting is required, compute a checksum of the
referenced Secret/ConfigMap and add it as an annotation on the
Deployment/PodTemplate (so the reconciler updates podTemplate.annotations and
triggers a rollout) — update the volumeMount creation site (volumeMount) and the
deployment reconcile logic that builds the pod template to apply one of these
fixes.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 1a6fffa6-4efd-4065-ba99-a11e7d797251

📥 Commits

Reviewing files that changed from the base of the PR and between ad1dec3 and 7cfaf3a.

📒 Files selected for processing (2)
  • pkg/reconcilers/deployment.go
  • pkg/reconcilers/deployment_test.go

Comment thread pkg/reconcilers/deployment.go Outdated
Comment on lines +119 to +123
volumeMount := k8score.VolumeMount{
Name: volume.Name,
MountPath: mountPath,
SubPath: path,
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🌐 Web query:

In Kubernetes, do subPath volume mounts backed by Secret, ConfigMap, or projected volumes receive live updates when the source object changes, or is a pod restart required?

💡 Result:

In Kubernetes, subPath volume mounts backed by Secret, ConfigMap, or projected volumes do not receive live updates when the source object changes. A pod restart is required to pick up the changes.

Citations:


SubPath mounts prevent automatic Secret/ConfigMap updates.

Using subPath with Secret or ConfigMap volumes disables live refresh behaviour. When the source Secret or ConfigMap is updated, pods with subPath mounts will not receive the changes until restarted. Either keep whole-volume mounts where feasible, or implement an explicit pod rollout strategy on source updates.

Code location

Lines 119–123 in pkg/reconcilers/deployment.go

			volumeMount := k8score.VolumeMount{
				Name:      volume.Name,
				MountPath: mountPath,
				SubPath:   path,
			}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@pkg/reconcilers/deployment.go` around lines 119 - 123, The current
VolumeMount creation sets SubPath (volumeMount with fields Name, MountPath,
SubPath) which prevents live updates for Secret/ConfigMap volumes; either remove
SubPath to mount the whole volume or implement an explicit rollout on
secret/configmap updates: prefer removing SubPath in the volumeMount
construction when the backing Volume is a Secret or ConfigMap, and if
partial-path mounting is required, compute a checksum of the referenced
Secret/ConfigMap and add it as an annotation on the Deployment/PodTemplate (so
the reconciler updates podTemplate.annotations and triggers a rollout) — update
the volumeMount creation site (volumeMount) and the deployment reconcile logic
that builds the pod template to apply one of these fixes.

Signed-off-by: Guilherme Cassolato <guicassolato@gmail.com>

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@pkg/reconcilers/deployment.go`:
- Around line 104-107: Guard against an empty mount path by checking if both
item.Path and item.Key are empty before using them: after computing path :=
item.Path; if path == "" { path = item.Key } add a conditional that if path ==
"" then log a warning (including item identifier) and continue/skip this item so
you never set VolumeMount.SubPath or compute mountPath with an empty string;
update any code that builds mountPath to assume path is non-empty (or trim
trailing slashes) to avoid producing a mountPath that ends with "/".
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 2da0879f-5814-436e-ad83-54dad19b4fbb

📥 Commits

Reviewing files that changed from the base of the PR and between 7cfaf3a and d7a1160.

📒 Files selected for processing (2)
  • pkg/reconcilers/deployment.go
  • pkg/reconcilers/deployment_test.go
🚧 Files skipped from review as they are similar to previous changes (1)
  • pkg/reconcilers/deployment_test.go

Comment on lines +104 to +107
path := item.Path
if path == "" {
path = item.Key
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Guard against empty path when both item.Path and item.Key are unset.

If both fields are empty strings, path remains empty, resulting in an empty SubPath (which mounts the entire volume instead of a specific file) and a malformed mountPath ending with just /. Consider skipping items with no resolvable path or logging a warning.

🛡️ Proposed defensive guard
 for _, item := range volume.Items {
     path := item.Path
     if path == "" {
         path = item.Key
     }
+    if path == "" {
+        // Skip items with no resolvable path to avoid malformed mounts
+        continue
+    }

     mountPath := volume.MountPath
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
path := item.Path
if path == "" {
path = item.Key
}
path := item.Path
if path == "" {
path = item.Key
}
if path == "" {
// Skip items with no resolvable path to avoid malformed mounts
continue
}
mountPath := volume.MountPath
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@pkg/reconcilers/deployment.go` around lines 104 - 107, Guard against an empty
mount path by checking if both item.Path and item.Key are empty before using
them: after computing path := item.Path; if path == "" { path = item.Key } add a
conditional that if path == "" then log a warning (including item identifier)
and continue/skip this item so you never set VolumeMount.SubPath or compute
mountPath with an empty string; update any code that builds mountPath to assume
path is non-empty (or trim trailing slashes) to avoid producing a mountPath that
ends with "/".

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

Labels

None yet

Projects

Status: Ready For Review

Development

Successfully merging this pull request may close these issues.

1 participant