Repository navigation
Conversation
Codecov Report❌ Patch coverage is 📢 Thoughts on this report? Let us know! |
docker-agent
left a comment
There was a problem hiding this comment.
Assessment: 🟡 NEEDS ATTENTION
This PR adds a full suite of docker job commands (cancel, inspect, list, logs, pause, prune, resume, rm, run, runs, wait) backed by a new gRPC jobs API and a vendored github.com/moby/extensions framework. The structure follows Docker CLI conventions well. Three medium-severity nil-pointer risks were found in the new code — all straightforward to fix — plus two lower-confidence edge cases in the vendored extensions client.
667d581 to
9c85a81
Compare
docker-agent
left a comment
There was a problem hiding this comment.
Assessment: 🟢 APPROVE
Lower-confidence findings (not posted inline)
-
[low] cli/command/job/list.go:52 — NextFire column can display a negative duration when NextFireAtNano is in the past (unverified; low severity)
The guard
!j.Paused && j.NextFireAtNano != 0does not check whether the timestamp is in the future. If the daemon hasn't refreshedNextFireAtNanoafter the last cron fire,time.Unix(0, j.NextFireAtNano).Sub(now)is negative andunits.HumanDurationrenders it as e.g.in -2 seconds. Adding.After(now)to the condition would prevent confusing output.
9c85a81 to
4fda975
Compare
Copy extpoints/jobs/api/v0/jobs.go from moby/moby at 402d0a16f1 and regenerate protogen/ with mobyextgen at the vendored github.com/moby/extensions version, so it compiles standalone against github.com/docker/cli/internal/jobsapi/v0 instead of moby's internal import path. Vendor github.com/moby/extensions, which the generated code builds on, and make google.golang.org/grpc and google.golang.org/protobuf direct dependencies since they are now imported directly. Signed-off-by: Guillaume Lours <glours@users.noreply.github.com>
Add the `docker job` command group. It reaches the daemon's jobs extension point through github.com/moby/extensions/client, dialed lazily on first use because the command tree is built before the CLI is initialized. - `ls` lists jobs: trigger, state, paused flag, latest run, exit code and next fire. - `logs [-f] JOB` reads the logs of a job's most recent run from the container logs API with the run's container ID (the Jobs API does not proxy logs). When the latest run has no readable container (removed, still pending, or failed before its container was created) it falls back to the most recent run that has one and says so on stderr. With -f on a schedule job it keeps following across runs; on a manual job it returns once the logs end. Jobs API status codes become actionable errors instead of raw gRPC dumps: Unimplemented points at `--extension-enable org.mobyproject.jobs.v0`, Unavailable is reported as retryable, the other codes show the daemon's message, and a cancelled context (Ctrl-C) stays recognisable so the CLI exits quietly. Signed-off-by: Guillaume Lours <glours@users.noreply.github.com>
- `inspect [--run RUN] JOB` prints a job or one of its runs as JSON. - `runs [--limit N] [--before CURSOR] JOB` lists a job's runs, most recent first, with paging; a stale cursor is reported on stderr. Signed-off-by: Guillaume Lours <glours@users.noreply.github.com>
…`rm` and `prune` commands - `run [--reschedule] JOB` triggers a run and prints its ID. - `cancel JOB` stops the in-flight run; cancelling an idle job is a no-op. - `pause JOB` and `unpause JOB` suppress and re-arm a schedule trigger. - `rm [--runs keep|remove|remove-finished] JOB` removes a job; run history is kept unless asked otherwise, and the ID of a removed job stays usable to read or purge it. - `prune [--label LABEL]` removes idle manual jobs. Signed-off-by: Guillaume Lours <glours@users.noreply.github.com>
`wait [--run RUN] [--condition terminal|running] JOB` blocks until a run reaches the condition and exits with the run's exit code, so it can be used in scripts. Without --run the latest run is awaited, which for a schedule job that has not run yet means blocking until its next fire. A run stopped by a timeout or by `docker job cancel` fails with status 1 and its state on stderr, whatever exit code its container reacted with. With `--condition running` the run ID is printed once the run is up. Signed-off-by: Guillaume Lours <glours@users.noreply.github.com>
Add the reference page of each `docker job` command and list the group in docker.md. Generated with `make mddocs`. Signed-off-by: Guillaume Lours <glours@users.noreply.github.com>
4fda975 to
3c890a8
Compare
docker-agent
left a comment
There was a problem hiding this comment.
Assessment: 🟢 APPROVE
The docker job command group is well-structured, follows Docker CLI conventions, and uses a sound lazy-connection pattern. One minor defensive-coding gap is noted inline; a few additional low-severity observations are listed below for the author's awareness.
Lower-confidence findings (not posted inline — unverified low-severity)
- [low]
cli/command/job/logs.go:171—time.Aftertimer goroutine leak insleepOrCancel: thetime.Afterchannel is not stopped on context cancellation, leaving a 2-second timer goroutine per cancelled poll cycle. Usetime.NewTimer+defer t.Stop()to fix. Benign in practice given the short interval. - [low]
cli/command/job/cmd.go:33— gRPC connection not closed when a subcommand'sRunEreturns an error (cobra skipsPersistentPostRunEon error). The in-code comment acknowledges this; the OS reclaims the socket on exit, so it only matters for multi-command-in-process usage. - [low]
cli/command/job/runs.go:127— Paging hint prints job name and cursor without shell-quoting:docker job runs --before $cursor $job. A job name containing spaces produces a broken suggested command. Using%qfor the job name would make the hint copy-pasteable. - [low]
cli/command/job/inspect.go:81—v = reply.Runon the--runbranch assigns a*Runpointer without a nil check; if the daemon returns nil Run with no error,json.MarshalIndentoutputsnullsilently. A nil guard matching thereply.Jobpattern would be more consistent.
Related to: moby/moby#53485
Summary
Add the
docker jobcommand group, a client for the jobs extension of the daemon (org.mobyproject.jobs.v0, served as a gRPC service on the daemon socket). Jobs are one-shot or cron-scheduled containers managed by the daemon; every execution is a run with a durable identity that survives container removal.ls,inspect [--run RUN]andruns [--limit N] [--before CURSOR]read jobs and runs.logs [-f] JOBreads the logs of the most recent run from the container logs API with the run's container ID (the Jobs API does not proxy logs). When the latest run has no readable container (removed, still pending, or failed before its container was created) it falls back to the most recent run that has one and says so on stderr. With-fit keeps following across runs for schedule jobs.run [--reschedule],cancel,pause,unpause,rm [--runs keep|remove|remove-finished]andprune [--label LABEL] [-f]act on jobs.wait [--run RUN] [--condition terminal|running] JOBblocks until a run reaches the condition and exits with the run's exit code; a run stopped by a timeout or a cancellation exits with status 1.Requirements
The daemon must serve Engine API 1.57 or newer and have the extension enabled, which is not the default:
dockerd --extension-enable org.mobyproject.jobs.v0, orextensions.enableindaemon.json. The group carries theversion: 1.57annotation, so older daemons hide it and refuse it with the usual "requires API version" error. On a recent daemon where the extension is off, commands fail with a message pointing at that flag, andUnavailable(daemon starting or unreachable) is reported as retryable instead of dumping the gRPC status.The CLI reaches the extension through
github.com/moby/extensions/client, dialed lazily on first use because the command tree is built before the CLI is initialized.Notes for review
internal/jobsapi/v0is a copy ofextpoints/jobs/api/v0/jobs.gofrom moby/moby at402d0a16f1(it is not published as a standalone Go module).protogen/andjobs.protoare generated from it withmobyextgenat the vendoredgithub.com/moby/extensionsversion.github.com/moby/extensionsis vendored at the pseudo-versionv0.0.0-20260826001921-d37867cb107f;google.golang.org/grpcandgoogle.golang.org/protobufbecome direct dependencies.vendor:commit, so thatgo mod tidy && go mod vendorleaves no diff at any commit. The series is: contract and vendoring,ls/logs,inspect/runs, the action commands,wait, then the generated markdown docs.go test -race ./cli/command/job/...andgolangci-lint.Not in this PR
lsandrunshave no--format,--quietor--filter, andinspectprints the raw JSON of the API types (field names as in the Go contract).Release notes (optional)
A picture of a cute animal (not mandatory but encouraged)