You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
feat(config): validate jobs, not just the global section (#778)
Closes#773. Depends on nothing; complements #777, which fixed the
daemon half.
## The validator never saw a job
It walks structs and skipped maps — and every job lives in one. So an
unparsable schedule passed `validate` with the strict flag on or off,
and the only later sign was a warning while the daemon started and the
job never fired.
Two things were needed to reach a job's fields:
- **Job sections are maps**, so the walk descends into them.
- **Every job type embeds `core.<Kind>Job`, which embeds `BareJob`** —
where `schedule` and `command` live. Squashed structs were skipped too,
so even after traversing the maps the schedule stayed invisible. They
are now descended into **only inside a job**, so the global section
keeps the behaviour it had.
## Strict, but not the wrong kind of strict
Applying the existing rule to jobs would have rejected configurations
that work today. *"A field with no `default` tag is required"* is a
heuristic tuned to the global section; on a job it demands nearly every
key a job can carry.
So that rule is suppressed inside jobs, and what a job needs is stated
explicitly — taken from what the runtime already demands, not invented
here:
| section | required | source |
|---|---|---|
| `job-exec` | `container`, `command` | `RunExec(ctx, j.Container, …)` |
| `job-run` | `image` **or** `container` | `core.RunJob.Validate` →
`ErrImageOrContainer` |
| `job-service-run` | `image` | `core.RunServiceJob.Validate` →
`ErrImageRequired` |
| `job-local` | `command` | `args.GetArgs(j.Command)` |
| `job-compose` | `file`, `service` | the `docker compose -f … run` it
builds |
| all | `schedule` | nothing to register the job under otherwise |
Errors name the section and the job the user wrote:
```
config validation error for field 'job-exec "foo": container':
is required (the container to exec in)
```
## Impact measured, not assumed
Every config in the repo still validates clean: `example/ofelia.ini`,
`test/test-config.ini`, `test/run-job/ofelia.ini`. There is an e2e test
pinning the shipped example specifically, so adding job checks cannot
start rejecting the file new users are handed.
**One existing fixture had to change rather than the rule.**
`TestValidateExecuteValidFile` declared a `job-exec` with no `container`
and asserted the config was valid. It is not — verified against the
running daemon, that job fails on *every tick*:
```
error: job run: exec run: run_exec container "": invalid container name or ID: value is empty
```
The test was pinning a config that cannot work. This is precisely the
class of failure the change exists to catch, so the fixture gained a
container.
## Note on the constants
`command`, `image`, `container` and `schedule` are now constants. They
were literals in the format switch, which carried a comment arguing that
extracting them only relocates duplication — true while they appeared
once. The requirements table makes them appear in two independent lists
with nothing tying them together, which is what goconst was pointing at.
## Test plan
- [x] `go test ./...` — green
- [x] `go test -race -tags=e2e ./e2e/...` — green
- [x] `golangci-lint run` incl. `--build-tags="e2e unix"` — 0 issues
- [x] `lefthook run pre-push` — exit 0
- [x] All three repo configs validate clean, before and after
- [x] Each requirement verified to fire, and each valid form verified to
pass (including `job-run` with only a container)
0 commit comments