Skip to content

Validate volume mount source paths before detached process#4359

Open
mvanhorn wants to merge 2 commits intostacklok:mainfrom
mvanhorn:osc/2485-volume-mount-validation
Open

Validate volume mount source paths before detached process#4359
mvanhorn wants to merge 2 commits intostacklok:mainfrom
mvanhorn:osc/2485-volume-mount-validation

Conversation

@mvanhorn
Copy link
Copy Markdown

Summary

Volume mount errors were silently buried in proxy log files when the source path didn't exist. Users saw a success message but the server immediately entered error state. This adds pre-flight validation in processVolumeMounts() so path errors surface immediately in the terminal.

This picks up where #3932 left off, addressing the two must-fix items from @JAORMX's review:

  1. Uses mount.IsResourceURI() instead of strings.HasPrefix for resource URI detection
  2. Uses os.IsNotExist(err) to distinguish "not exists" from "permission denied"

Fixes #2485

Type of change

  • Bug fix

Test plan

  • Ran go build ./... - passes
  • Ran go test ./pkg/runner/... -count=1 - all tests pass
  • Added test case for non-existent source path error
  • Updated existing volume mount tests to use real temp directories
  • Verified resource URIs skip validation (via mount.IsResourceURI())
  • Verified operator context skips validation (via BuildContextCLI check)

Changes

File Change
pkg/runner/config_builder.go Add source path validation after mount.Parse(), before duplicate check
pkg/runner/config_builder_test.go Use real temp dirs, add non-existent path test case
pkg/runner/config_test.go Use real temp dirs for volume processing test

Generated with Claude Code

When running with --volume, validate that the source path exists on
the host filesystem before spawning the detached process. Previously,
path errors were silently buried in proxy log files.

The validation:
- Uses mount.IsResourceURI() to skip resource URIs
- Distinguishes "not exists" from "permission denied" via os.IsNotExist
- Only runs in CLI context (skipped for Kubernetes operator)
- Resolves relative paths to absolute before checking

Updates tests to use real temp directories and adds a test case
for the non-existent path error.

Fixes stacklok#2485
@github-actions github-actions bot added the size/XS Extra small PR: < 100 lines changed label Mar 25, 2026
@codecov
Copy link
Copy Markdown

codecov bot commented Mar 25, 2026

Codecov Report

❌ Patch coverage is 36.36364% with 7 lines in your changes missing coverage. Please review.
✅ Project coverage is 68.14%. Comparing base (b29f60f) to head (c3c095d).
⚠️ Report is 3 commits behind head on main.

Files with missing lines Patch % Lines
pkg/runner/config_builder.go 36.36% 5 Missing and 2 partials ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #4359      +/-   ##
==========================================
- Coverage   68.35%   68.14%   -0.21%     
==========================================
  Files         479      479              
  Lines       48736    48769      +33     
==========================================
- Hits        33315    33236      -79     
- Misses      12441    12466      +25     
- Partials     2980     3067      +87     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
Copy link
Copy Markdown
Contributor

@jhrozek jhrozek left a comment

Choose a reason for hiding this comment

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

Review from Claude Code — 2 inline comments on the new validation logic and test coverage.

if err != nil {
return fmt.Errorf("failed to resolve relative path %s: %w", source, err)
}
absSource = filepath.Join(cwd, source)
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.

Suggestion: Consider using filepath.Abs(source) instead of the manual os.Getwd() + filepath.Join() approach. filepath.Abs does the same thing internally but also calls filepath.Clean, normalizing .. segments in the resolved path. This would also be consistent with the existing convertRelativePathToAbsolute in pkg/container/docker/client.go:807 which already uses filepath.Abs.

Suggested change
absSource = filepath.Join(cwd, source)
absSource, err := filepath.Abs(source)
if err != nil {
return fmt.Errorf("failed to resolve volume source path %s: %w", source, err)
}
builderOptions: []RunConfigBuilderOption{
WithVolumes([]string{"/nonexistent/path:/container"}),
WithPermissionProfile(permissions.BuiltinNoneProfile()),
},
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.

Suggestion: Consider adding test cases for the two skip conditions in the new validation:

  1. Operator context: NewOperatorRunConfigBuilder with WithVolumes([]string{"/nonexistent/path:/container"}) should succeed — operator paths reference K8s node paths, not the operator pod's filesystem.
  2. Resource URI: A CLI-context build with WithVolumes([]string{"volume://my-volume:/container/data"}) should succeed — resource URIs have no host path to stat.

Both guards are new code and currently have no test coverage.

Use filepath.Abs instead of manual Getwd+Join for resolving relative
paths. Add test verifying operator context skips host path validation.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@mvanhorn
Copy link
Copy Markdown
Author

Addressed both suggestions in 53298a0:

  • Switched to filepath.Abs(source) which handles the Getwd+Join+Clean in one call
  • Added TestOperatorContextSkipsVolumeValidation confirming non-existent K8s node paths pass in operator context
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size/XS Extra small PR: < 100 lines changed

2 participants