CRD-SPEC.md
Scope
This document defines the Kubernetes Custom Resource Definition (CRD) contract for Kron.
It specifies:
API group and versions
Resource schema
Defaulting rules
Validation rules
Status contract
Immutability rules
Upgrade guarantees
This specification applies to the Kubernetes adapter (kron-operator).
API Group
Group: kron.io
Version: v1alpha1
Kind: KronJob
Plural: kronjobs
Scope: Namespaced
Future versions must follow Kubernetes API versioning rules.
Resource Structure
apiVersion: kron.io/v1alpha1
kind: KronJob
metadata:
name: string
namespace: string
spec: KronJobSpec
status: KronJobStatus
Spec
KronJobSpec {
schedule: string
timezone: string (optional)
window: WindowSpec
distribution: DistributionSpec
seed: SeedSpec
constraints: ConstraintSpec (optional)
policy: PolicySpec (optional)
jobTemplate: batchv1.JobTemplateSpec
}
schedule
Required.
Standard 5-field cron expression.
Must pass validation at admission time.
Interpreted in
timezoneif provided, otherwiseUTC.
Invalid schedule causes resource rejection.
timezone
Optional.
IANA timezone name.
Default:
UTC.
Invalid timezone causes resource rejection.
window
WindowSpec {
mode: "after" | "around"
duration: string (Go duration format)
}
Default:
mode: "after"
duration: "0s"
Validation:
duration must be ≥ 0.
If
around, duration may be zero but must not overflow when halved.Excessively large durations may be rejected by validation policy.
distribution
DistributionSpec {
name: string
params: map[string]string (optional)
}
Default:
name: "uniform"
params: {}
Allowed names:
uniformnormalskewEarlyskewLateexponential
Unknown names cause rejection.
Parameter validation must match CORE-SPEC.md.
seed
SeedSpec {
strategy: "stable" | "daily" | "weekly"
salt: string (optional)
}
Default:
strategy: "stable"
salt: ""
Unknown strategy causes rejection.
constraints
ConstraintSpec {
only: []ConstraintClause (optional)
avoid: []ConstraintClause (optional)
}
Each clause corresponds to SYNTAX.md semantics.
Constraint schema must be validated structurally but deep semantic validation may occur in controller.
If constraints are unsatisfiable at runtime, period becomes unschedulable.
policy
PolicySpec {
concurrency: "allow" | "forbid" | "replace"
deadline: string (Go duration format)
suspend: boolean
}
Defaults:
concurrency: "forbid"
deadline: "0s"
suspend: false
Validation:
deadline ≥ 0.
Unknown concurrency value rejected.
jobTemplate
Standard batch/v1 JobTemplateSpec.
Requirements:
spec.template.spec.restartPolicymust be valid for Job.No fields may be mutated by controller except labels and owner references.
Controller must:
Add owner reference to KronJob.
Add deterministic labels:
kron.io/namekron.io/period-idkron.io/chosen-time
Status
KronJobStatus {
observedGeneration: int64
lastPeriodID: string
lastNominalTime: string
lastChosenTime: string
lastOutcome: string
nextPeriodID: string
nextNominalTime: string
nextChosenTime: string
conditions: []Condition
}
All timestamps are RFC3339 UTC.
lastOutcome
One of:
executedskippedmissedunschedulable
Empty if no period handled yet.
next* Fields
Represent the next scheduled period as of last reconciliation.
Must reflect deterministic decision.
These fields are informational and may change if spec changes.
Conditions
Standard Kubernetes Condition structure:
Condition {
type: string
status: "True" | "False" | "Unknown"
reason: string
message: string
lastTransitionTime: string
}
Defined condition types:
ReadyInvalidSpecSchedulingErrorUnschedulable
Defaulting Rules
Defaults must be applied at admission or reconciliation:
timezone →
UTCwindow.mode →
afterwindow.duration →
0sdistribution.name →
uniformseed.strategy →
stableseed.salt →
""policy.concurrency →
forbidpolicy.deadline →
0spolicy.suspend →
false
Defaulting must be stable within version.
Immutability Rules
The following fields are immutable after creation:
spec.schedulespec.timezonespec.seed.strategy
If changed:
Controller must treat as new scheduling configuration.
Future periods use new config.
Already handled periods remain handled.
Alternatively, strict mode may reject mutation via validation webhook.
Immutability policy must be consistent within version.
Reconciliation Semantics
Controller must:
Observe current time.
Compute decision for current period using kron-core.
Compare with status.
Enforce idempotency:
Do not create duplicate Job for same
period-id.
Apply policy.
Update status atomically.
Reconciliation must be idempotent.
Idempotency Rules
Before creating a Job:
Check for existing Job with:
matching owner reference
matching
kron.io/period-id
If exists:
Do not create another.
Update status accordingly.
Suspension
If policy.suspend=true:
No Jobs created.
Periods are not marked handled.
Status reflects suspended state.
When suspension lifted:
Only future periods are considered.
Missed periods follow deadline semantics.
Deadline Handling
If now > chosenTime + deadline:
Mark period
missed.Do not create Job.
Update status.
Deadline default 0s means:
If
now > chosenTime, period ismissed.
Deletion Semantics
When KronJob is deleted:
Owner reference ensures child Jobs are garbage collected.
Controller must not create new Jobs.
Finalizers are optional but must not block deletion unnecessarily.
Upgrade Rules
Within v1alpha1:
Field names must not change.
Status fields must not change meaning.
Default values must remain stable.
Breaking changes require new API version (e.g., v1beta1).
Conversion webhook required if version upgrade introduces structural change.
Validation Guarantees
CRD schema must enforce:
Required fields present.
Enum values constrained.
Duration strings valid.
Basic structural validation.
Deep semantic validation may occur in controller and reflected in InvalidSpec condition.
Compatibility Guarantees
For identical spec and same Kron version:
Same decisions must be produced.
Same period-id labeling must occur.
Same Job naming strategy must be used.
Behavioral changes require new API version.
Naming of Jobs
Controller must name Jobs deterministically:
<kronjob-name>-<period-hash>
period-hash derived from period-id truncated to safe length.
Name must:
Be DNS-1123 compliant.
Avoid collisions.
Invariants
At most one Job per
(namespace, kronjob, period-id).chosenTime always within window.
Status reflects last handled period.
Reconciliation is idempotent.
No duplicate Jobs on controller restart.
Spec changes affect only future periods.
Suspension prevents execution without losing configuration.
Non-Goals
CRD does not:
Provide workflow chaining.
Provide retries.
Provide backoff policies.
Provide distributed locking beyond Job semantics.
KronJob defines scheduling, not orchestration.