Repository navigation
fix(docker): handle container inspection and service status safely (#5703) - #5704
smartcalsystems-sketch wants to merge 2 commits into
Conversation
| throw new TRPCError({ code: "UNAUTHORIZED" }); | ||
| } | ||
| } | ||
| return await readContainerFileTail( |
There was a problem hiding this comment.
| lines: number, | ||
| serverId?: string, | ||
| ) => { | ||
| const command = `docker exec ${quote([containerId])} tail -n ${lines} ${quote([filePath])} | base64 | tr -d '\\n'`; |
There was a problem hiding this comment.
The command has no byte limit, and lines accepts negative, fractional, and arbitrarily large values. A large request—or one very long log line—can still read a huge file. Local reads hit the command buffer limit, while remote reads keep the whole response in memory. Require a bounded positive integer and cap returned bytes before buffering them.
| try { | ||
| await stopCompose(service.composeId); | ||
| } catch (error) { | ||
| log(` Could not stop compose: ${errorMessage(error)}`); | ||
| } | ||
| return; |
There was a problem hiding this comment.
stopOnSource logs a failed stopCompose call and continues the transfer. If the Compose file is missing or Docker refuses the stop, containers may still be writing when their volumes are copied. The transfer then switches the saved server and removes the source, leaving the target with potentially inconsistent data. Abort when stopping fails and verify the source has stopped before copying.
Knowledge Base Used: Resource transfers
| export const volumeImportCommand = (volume: string) => | ||
| `docker volume create ${q(volume)} >/dev/null && docker run --rm -i -v ${q(volume)}:/data alpine tar -xzf - --numeric-owner -C /data`; |
There was a problem hiding this comment.
The import extracts into an existing target volume without checking or clearing it. Transfer from A to B with the default removeSourceData: false, delete files on B, then transfer back to A: extraction overwrites matching files but leaves the deleted files from A's old copy behind. This can mix old database files with current data. Refuse an existing destination or safely replace its contents before restoring.
Knowledge Base Used: Resource transfers
| import { expect, test, mock } from "bun:test"; | ||
| import { readContainerFileTail } from "../packages/server/src/services/docker"; |
There was a problem hiding this comment.
This test sits outside the configured test directory and uses bun:test, while the repository runs Vitest. The include pattern only selects __test__/**/*.test.ts, so CI never runs this regression test. Move it into the existing suite and use Vitest mocks so CI checks the new tail behavior.
| const to = await openProcessStream(target.serverId, target.command); | ||
| const targetError = collectOutput(to.stderr); | ||
| to.stdout.resume(); | ||
| to.stdin.on("error", () => {}); | ||
|
|
||
| const from = await openProcessStream(source.serverId, source.command); |
There was a problem hiding this comment.
Failed transfers leave processes
pipeBetweenServers starts the target before opening the source, but never closes the target if opening the source fails. An unavailable source SSH server leaves the target's archive reader waiting for input and its connection open after the transfer reports failure. Close opened processes in a finally block, including failures before the pipe is connected.
Knowledge Base Used: Resource transfers
| // inherently guarantees that Radix UI's composeEventHandlers properly propagates the | ||
| // onPointerDown event to dismiss the tooltip simultaneously as the dialog opens. | ||
|
|
||
| expect(true).toBe(true); |
There was a problem hiding this comment.
2fadca1 to
dc56d83
Compare
|
@greptile-apps review All review feedback has been addressed:
|
| const finalBuffer = truncated | ||
| ? buffer.subarray(buffer.byteLength - boundedMaxBytes) | ||
| : buffer; | ||
|
|
||
| return { | ||
| content: finalBuffer.toString("utf-8"), |
There was a problem hiding this comment.
Truncated logs gain broken characters
When the byte limit cuts through a UTF-8 character, finalBuffer.toString("utf-8") adds replacement characters that were not in the log. For example, "€" + "a".repeat(1023) with maxBytes: 1024 returns a replacement character followed by the ASCII text. Skip leading UTF-8 continuation bytes before decoding the truncated buffer.
|
@greptile-apps review Addressed UTF-8 truncation review comment:
|
Fixes #5703
Summary
Fixes container status evaluation in Docker router to safely handle missing or stopped containers without throwing unhandled exceptions.
Changes
apps/dokploy/server/api/routers/docker.tsto validate container state before accessing propertiesTesting
pnpm test— all unit tests pass cleanly.