Skip to content

fix(controller-manager): renumber CronJob day-of-week for the cron crate - #111

Open
vyncint wants to merge 1 commit into
calfonso:mainfrom
vyncint:fix/cronjob-day-of-week
Open

fix(controller-manager): renumber CronJob day-of-week for the cron crate#111
vyncint wants to merge 1 commit into
calfonso:mainfrom
vyncint:fix/cronjob-day-of-week

Conversation

@vyncint

@vyncint vyncint commented Aug 7, 2026

Copy link
Copy Markdown
Contributor

Summary

Fixes #110. should_run_now padded a Kubernetes schedule out to the seven fields
the cron crate wants, but the two number days of the week differently:

  • Kubernetes, following standard cron: 0-6, Sunday = 0
  • cron 0.17: 1-7, Sunday = 1

Nothing renumbered the field, so every numeric day was read as the day before the
one requested, and 0 was out of range entirely. Feeding the expressions the
controller built straight to cron::Schedule, searching forward from Saturday
2026-08-01:

schedule expression built first fire intended
0 0 * * 0 0 0 0 * * 0 * parse error Sunday
@weekly 0 0 0 * * 0 * parse error Sunday
0 0 * * 1 0 0 0 * * 1 * Sun 2026-08-02 Monday
0 0 * * 6 0 0 0 * * 6 * Fri 2026-08-07 Saturday
0 0 * * SUN 0 0 0 * * SUN * Sun 2026-08-02 Sunday

@weekly is caught by this because it expands to 0 0 * * 0 at cronjob.rs:354,
which is the one value the crate rejects. Day names are unaffected — both
numberings agree on those.

The failure is silent. A parse error is swallowed at cronjob.rs:379-382 into a
warning and Ok(false), and status.lastScheduleTime is only written on the
job-creation path (:396), so an affected CronJob reports nothing and keeps an
empty lastScheduleTime forever.

Change

Renumber the day-of-week field before handing the expression to the crate. Only
value positions shift:

input output why
0 1 Sunday in both numberings
0,3 1,4 each list entry is a day
1-5 2-6 both range endpoints are days
1-5/2 2-6/2 the step counts intervals, not days
*/2 */2 no day values to shift
MON-FRI MON-FRI names already agree
9 9 left for the parser to reject

The conversion moves out of should_run_now into to_cron_expression, a free
function, so the mapping is testable without a controller or storage. Seven-field
input is left alone: it is already in the crate's own format, and renumbering it
would shift a day that was never in Kubernetes numbering.

Validation

No cluster and no container runtime — plain cargo test.

test_cron_schedule_parsing asserted string literals
(assert_eq!("@hourly", "@hourly")) and exercised neither the conversion nor the
parse, which is why none of this was visible. It is replaced by five tests that
assert the weekday a schedule actually fires on:

test before after
test_day_of_week_numbers_follow_kubernetes (all seven days) fails passes
test_weekly_shortcut_runs_on_sunday fails passes
test_day_of_week_lists_ranges_and_steps fails passes
test_day_of_week_names_are_unchanged passes passes
test_non_weekday_fields_are_untouched passes passes

Reverting only the renumbering with the tests in place fails exactly those three
and leaves the name and non-weekday tests passing, which is what shows the change
is confined to numeric day-of-week values:

"@weekly" -> "0 0 0 * * 0 *" did not parse: 0 0 0 * * 0 *
"0 0 * * 0" -> "0 0 0 * * 0 *" did not parse: 0 0 0 * * 0 *
assertion `left == right` failed
  left: Sun
 right: Mon
  • cargo test --workspace --locked: 140 suites, 4250 tests, 0 failed
  • cargo fmt (workspace members, as the fmt workflow runs it): clean
  • cargo clippy --workspace --all-targets --locked: no warnings in
    crates/controller-manager/src/controllers/cronjob.rs

`should_run_now` padded a Kubernetes schedule out to the seven fields the `cron`
crate wants, but the two number days of the week differently: Kubernetes follows
standard cron with 0-6 and Sunday as 0, while `cron` 0.17 uses 1-7 with Sunday as
1. Nothing renumbered the field, so every numeric day was read as the day before
the one requested, and 0 was out of range entirely.

Feeding the expressions the controller built straight to `cron::Schedule`,
searching forward from Saturday 2026-08-01:

    schedule       expression built     first fire        intended
    0 0 * * 0      0 0 0 * * 0 *        parse error       Sunday
    @Weekly        0 0 0 * * 0 *        parse error       Sunday
    0 0 * * 1      0 0 0 * * 1 *        Sun 2026-08-02    Monday
    0 0 * * 6      0 0 0 * * 6 *        Fri 2026-08-07    Saturday
    0 0 * * SUN    0 0 0 * * SUN *      Sun 2026-08-02    Sunday

`@weekly` is caught by this because it expands to `0 0 * * 0`, which is the one
value the crate rejects. The failure is silent: a parse error is swallowed into a
warning and `Ok(false)`, and `status.lastScheduleTime` is only written on the
job-creation path, so an affected CronJob reports nothing and keeps an empty
`lastScheduleTime` forever.

Renumber the day-of-week field before handing the expression over. Only value
positions shift: a step (`*/2`) counts intervals rather than days, and day names
already mean the same thing to both, so both pass through. A number outside 0-6
also passes through, which leaves it for the parser to reject rather than
silently turning it into a different day.

The conversion moves out of `should_run_now` into `to_cron_expression`, a free
function, so the mapping can be tested without a controller or storage. Seven-field
input is left alone — it is already in the crate's own format, and renumbering it
would shift a day that was never in Kubernetes numbering.

The existing `test_cron_schedule_parsing` asserted string literals
(`assert_eq!("@hourly", "@hourly")`) and exercised neither the conversion nor the
parse, which is why none of this was visible. It is replaced by five tests that assert
the weekday a schedule actually fires on, covering all seven day numbers,
day names, `@weekly`, lists, ranges, steps, and the untouched fields.

Reverting only the renumbering with the tests in place fails exactly three of
them — the day numbers, `@weekly`, and the list/range/step cases — and leaves the
name and non-weekday tests passing.

Fixes calfonso#110

Signed-off-by: Vyncint Ng <vyncint@icloud.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

CronJob day-of-week is off by one; @weekly and Sunday schedules never run

1 participant