Skip to content

feat(scheduled-task): add run-once job mode that runs on deploy - #206

Open
fedemaleh wants to merge 3 commits into
betafrom
feat/scheduled-task-run-once
Open

feat(scheduled-task): add run-once job mode that runs on deploy#206
fedemaleh wants to merge 3 commits into
betafrom
feat/scheduled-task-run-once

Conversation

@fedemaleh

Copy link
Copy Markdown
Collaborator

What

Adds a "Run once" mode to the scheduled_task scope. When selected, deploying the scope creates a Kubernetes Job (instead of a CronJob) that executes immediately on deploy, and the deployment waits for the Job to finish, reporting success or failure based on the Job's result.

How it works

The whole feature keys off a single sentinel value: scope.capabilities.cron == "run-once".

  • Schema (service-spec.json.tpl): a new "Run once" option is added to the existing Task Frequency (cron) dropdown — no new required field, fully retro-compatible. When "Run once" is picked, the CronJob-only fields Concurrency policy and History are hidden in the UI (they have no meaning for a one-shot Job). Retries stays (it maps to the Job's backoffLimit).
  • Template (deployment.yaml.tpl): a plain gomplate if/else renders either a Job (with backoffLimit, activeDeadlineSeconds, ttlSecondsAfterFinished: 86400) or the unchanged CronJob. The CronJob render is byte-identical to before (verified by diffing real-gomplate output against the base template).
  • Wait (deployment/wait_job): polls the Job's Complete/Failed conditions up to JOB_WAIT_TIMEOUT (default 600s, configurable in values.yaml). It's wired into initial.yaml and blue_green.yaml by replacing the inherited wait deployment active step, and is a no-op for CronJob deployments (so the step is safe on both paths).
  • Trigger action (scope/trigger): for run-once scopes there's no CronJob to spawn from, so the action falls back to cloning the last Job of the scope (stripping server-managed fields) and creating a fresh one. Scheduled scopes are unchanged.

Notes / decisions

  • The Job's activeDeadlineSeconds equals JOB_WAIT_TIMEOUT; the wait poller runs a short grace period longer so Kubernetes marks the Job DeadlineExceeded first and the operator sees the specific failure reason.
  • The pod block is intentionally duplicated across the Job/CronJob template branches (a plain if/else, no define/tmpl.Exec) — an explicit design choice to keep the CronJob output untouched and each branch a complete, readable manifest.
  • Rollback is intentionally not wired to wait: the rollback workflow's workload step runs apply_templates with ACTION: delete (it removes the Job, never creates/runs one), so there is nothing to wait for.

Test plan

  • bats scheduled_task/deployment/tests/{build_deployment,wait_job,workflow_overrides}.bats scheduled_task/scope/tests/{trigger,workflow_overrides}.bats38 passing, including:
    • CronJob-vs-Job rendering + JOB_WAIT_TIMEOUT override + file-param volumes preserved in Job mode
    • wait_job success / failure / timeout / no-op / namespace resolution / set -u guard
    • trigger clone-last-Job fallback and clear errors
    • workflow-wiring assertions that both deploy paths replace wait deployment active with wait_job
  • Verified the rendered CronJob YAML is byte-identical to base, and both modes parse as valid YAML.

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.

1 participant