Conversation
Contributor
✅ Snyk checks have passed. No issues have been found so far.
💻 Catch issues earlier using the plugins for VS Code, JetBrains IDEs, Visual Studio, and Eclipse. |
greglum
force-pushed
the
fix/rclone-exit-code
branch
2 times, most recently
from
August 20, 2026 01:36
b24f09e to
b7f38fc
Compare
The three process runners in AbstractRCloneStorageEngine called p.waitFor() and discarded the exit code, so every rclone invocation reported success no matter what rclone did. A failed copy or sync returned normally and PushToStorageReactor returned true on it. Capture the exit code and fail on it. Reads (lsf, lsjson) and deletes tolerate 3 and 4, where a missing path is an empty result or an already finished delete rather than an error; everything else fails on any non-zero exit, which rclone only returns once it has given up retrying. Two things fall out of the same six lines: - waitFor() ran before either pipe was drained. A child that filled the OS pipe buffer blocked on write and waitFor() never returned, hanging the calling thread. lsjson --metadata on a large container reaches this. Both pipes are now drained while the process runs, stderr on its own thread. - The failure message names only the rclone sub-command, never the arguments, and drops process error output for config calls. A config create carries the storage credentials in its argv and the message travels back to the caller. deleteRcloneConfig no longer propagates a cleanup failure. Every caller runs it from a finally block, so throwing there replaced the transfer failure being unwound with a misleading one.
The storage reactors caught every failure and rethrew a fixed string, so the reason rclone gave up only ever reached the server log. Append the exception message the way the other reactors do. The engine builds that message from the rclone sub-command and capped stderr and never includes the command arguments, so no credentials can travel with it. Covers push, pull, both syncs, delete, both lists, get-as-base64, and update-metadata. The metadata one also stops hiding the UnsupportedOperationException that every non-native engine throws.
greglum
marked this pull request as ready for review
August 20, 2026 02:26
greglum
force-pushed
the
fix/rclone-exit-code
branch
from
August 20, 2026 02:26
b7f38fc to
525970a
Compare
runProcessJsonOutput, the no-tolerance runProcessListJsonOutput overload, and parseJsonOutput have no callers in this repo or Monolith - the map-shaped runner was dead before the rework as well. Same justification as the streamed variants already removed: keeping them means reworking and maintaining API surface nobody uses.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Description
Every rclone invocation in the storage engines ran through three process helpers in
AbstractRCloneStorageEnginethat calledp.waitFor()and discarded the exit code. A failed transfer returned normally, soPushToStorageansweredtrueafter a copy that never happened. Listings against the same engine succeed, which makes the false success look like a working pipeline.The same lines held a second defect:
waitFor()ran before either pipe was drained, with stdout and stderr both onRedirect.PIPE. Once a child filled the OS pipe buffer (~64KB) it blocked on write andwaitFor()never returned, leaking the calling thread.lsjson --metadataon a large container reaches this.Changes Made
IOExceptionon failure (IOExceptionwas already declared on every signature, so no caller changes).lsf/lsjsonand the deletes (deleteandpurge) tolerate 3 (directory not found) and 4 (file not found), where a missing path is an empty result / no-op rather than an error.waitFor()- removes the pipe-buffer deadlock.configcommands - the config argv carries storage credentials and the message travels back to the caller.deleteRcloneConfiglogs instead of throwing: every caller runs it from afinallyblock, so a cleanup failure was replacing the real transfer failure being unwound.PushToStorage,PullFromStorage, both syncs,DeleteFromStorage, both lists,GetStorageFileAsBase64,UpdateStorageFileMetadata) append the exception message to their thrown error instead of a fixed string, matching the idiom used elsewhere. The metadata one also stops hiding theUnsupportedOperationExceptionthat every non-native engine throws.S3StorageEngine.copyToStorage()against a stub rclone whose copy fails.How to Test
mvn test -Dtest=AbstractRCloneStorageEngineUnitTests- all six pass; against the previous code the exit-code test fails ("Expected IOException... nothing was thrown") and the deadlock test times out.PushToStorage(...). Before: returnstrue, file absent. After: pixel errors withrclone copy failed with exit code N - <reason>.ListStoragePath) - still returns an empty list, unchanged.Notes
CentralCloudStorage(engine push/pull, image push) will surface rclone failures instead of continuing with missing files - that is the intent, but reviewers should confirm no flow depends on the old silent behavior.streamJsonOutput/streamListJsonOutput/streamError(protected statics orphaned by the rework) were removed, along withrunProcessJsonOutputand the no-tolerancerunProcessListJsonOutputoverload, which had no callers even before this change; no references exist in this repo or Monolith.