Skip to content

feat: implement cronjob primitive#19

Open
sourcehawk wants to merge 34 commits intomainfrom
feature/cronjob-primitive
Open

feat: implement cronjob primitive#19
sourcehawk wants to merge 34 commits intomainfrom
feature/cronjob-primitive

Conversation

@sourcehawk
Copy link
Owner

Implements the cronjob Kubernetes resource primitive following the pattern established by the existing ConfigMap and Deployment primitives.

Summary

  • Adds cronjob primitive package under pkg/primitives/cronjob/
  • Implements required lifecycle interfaces
  • Includes editors, mutator, flavors, and builder

Checklist

  • Compiles cleanly
  • Tests pass
  • Follows naming conventions in CONTEXT.md
  • Does not modify shared files

Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds a new cronjob primitive to the Operator Component Framework, extending the primitives layer with first-class support for reconciling Kubernetes batch/v1 CronJobs (builder/resource, mutator + editors, flavors), plus accompanying docs and an example.

Changes:

  • Introduces pkg/primitives/cronjob with builder/resource lifecycle integration, mutation planning/apply, flavors, and default handlers.
  • Adds new mutation editors for CronJobSpec and JobSpec under pkg/mutation/editors.
  • Adds docs and an end-to-end example; also updates repo lint config and a few existing test files.

Reviewed changes

Copilot reviewed 24 out of 24 changed files in this pull request and generated 7 comments.

Show a summary per file
File Description
pkg/primitives/cronjob/resource.go CronJob primitive resource wrapper around generic IntegrationResource
pkg/primitives/cronjob/mutator.go Plan/apply mutator for CronJob + containers/initContainers editing
pkg/primitives/cronjob/handlers.go Default operational + suspension handlers for CronJob
pkg/primitives/cronjob/flavors.go Field-application flavors for labels/annotations + pod template metadata
pkg/primitives/cronjob/builder.go Fluent builder wiring defaults, mutations, flavors, handlers, extractors
pkg/primitives/cronjob/mutator_test.go Extensive mutator behavior tests (ordering, selectors, nil-safety, etc.)
pkg/primitives/cronjob/handlers_test.go Tests for default operational/suspension handlers
pkg/primitives/cronjob/flavors_test.go Tests for flavor behavior and ordering
pkg/primitives/cronjob/builder_test.go Builder validation and option wiring tests
pkg/mutation/editors/cronjobspec.go New typed editor for batchv1.CronJobSpec
pkg/mutation/editors/cronjobspec_test.go Tests for CronJobSpecEditor methods
pkg/mutation/editors/jobspec.go New typed editor for batchv1.JobSpec
pkg/mutation/editors/jobspec_test.go Tests for JobSpecEditor methods
pkg/component/suite_test.go Removes revive nolint from dot-imports
pkg/component/component_test.go Removes revive nolint from dot-imports
internal/generic/resource_task_test.go Removes dupl nolint marker
internal/generic/resource_integration_test.go Removes dupl nolint marker
examples/cronjob-primitive/resources/cronjob.go Example resource factory assembling CronJob primitive with features/flavors
examples/cronjob-primitive/main.go Runnable fake-client reconciliation demo exercising feature/suspend flows
examples/cronjob-primitive/features/mutations.go Example feature mutations using the cronjob mutator/editors
examples/cronjob-primitive/app/controller.go Example controller wiring a CronJob-backed component
examples/cronjob-primitive/README.md Documentation for running and understanding the example
docs/primitives/cronjob.md New primitive documentation for CronJob
.golangci.yml Updates golangci-lint v2 config layout, enabled linters, and exclusions

Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 24 out of 24 changed files in this pull request and generated 3 comments.

@sourcehawk
Copy link
Owner Author

Claude Review Cycle 1 Complete

Addressed:

  • docs/primitives/cronjob.md line 9: Fixed capabilities table to use OperationPending instead of Pending to match the actual status value from concepts.OperationalStatusPending
  • pkg/primitives/cronjob/resource.go line 27: Added resource_test.go with comprehensive resource-level tests covering Identity, Object, Mutate (baseline, with mutation, feature ordering, custom field applicator + error), ConvergingStatus (custom handler, default pending, default operational), DeleteOnSuspend (custom handler, default false), Suspend (default handler, custom handler), SuspensionStatus (custom handler, default suspending, default suspended), ExtractData, and ExtractData error

Intentionally ignored:

  • .golangci.yml line 10: Comment asks to update PR description/checklist or move shared file changes to a separate PR. This is a PR description concern, not a code change. The .golangci.yml change (upgrading to valid v2 config) was a necessary prerequisite for the cronjob primitive to build correctly and is already in a prior commit.

<!-- claude-review-cycle -->

Copilot AI review requested due to automatic review settings March 22, 2026 19:48
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 25 out of 25 changed files in this pull request and generated 2 comments.

Copilot AI review requested due to automatic review settings March 23, 2026 00:42
sourcehawk and others added 11 commits March 23, 2026 00:43
JobSpecEditor provides typed methods for Job spec fields (completions,
parallelism, backoff limit, etc.). CronJobSpecEditor provides typed
methods for CronJob spec fields (schedule, concurrency policy, time
zone, etc.). The suspend field is intentionally excluded from
CronJobSpecEditor as it is managed by the framework's suspension system.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Implements the CronJob primitive using IntegrationBuilder with
Operational and Suspendable concepts. Key behaviors:
- Operational status based on LastScheduleTime (Pending vs Operational)
- Suspension via spec.suspend=true (DeleteOnSuspend=false)
- Full pod-template mutation pipeline: metadata, cronjob spec, job spec,
  pod template metadata, pod spec, containers, init containers
- Flavors for preserving labels, annotations, and pod template metadata

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Documents the Integration lifecycle, mutation pipeline ordering,
editors (CronJobSpec, JobSpec, PodSpec, Container, ObjectMeta),
operational status semantics, and suspension behavior.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Demonstrates building a CronJob resource with version-gated mutations,
tracing/metrics features, field preservation flavors, suspension via
spec.suspend, and data extraction. Uses the shared ExampleApp CRD and
a fake client for standalone execution.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Fix container field paths in docs mutation ordering table to use full
  CronJob path (spec.jobTemplate.spec.template.spec.containers)
- Use correct OperationPending status name in docs to match framework constant
- Explicitly convert LastScheduleTime to UTC before formatting timestamp
- Fix range variable address-of footgun in mutator_test.go findEnv helper

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Fix capabilities table in cronjob.md to use `OperationPending` instead
of `Pending` to match the actual status value from the concepts package.

Add resource_test.go for the cronjob primitive covering Identity, Object,
Mutate, feature ordering, custom field applicator, ConvergingStatus,
DeleteOnSuspend, Suspend, SuspensionStatus, and ExtractData.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
@sourcehawk sourcehawk force-pushed the feature/cronjob-primitive branch from 563d078 to 5058194 Compare March 23, 2026 00:43
@sourcehawk
Copy link
Owner Author

approved

Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 25 out of 25 changed files in this pull request and generated 3 comments.

sourcehawk and others added 2 commits March 23, 2026 02:54
The FeatureMutator interface in internal/generic used an unexported
beginFeature() method, which meant primitive mutators in external
packages (cronjob, deployment, configmap) could never satisfy the
interface. The type assertion in ApplyMutations always returned false,
so feature boundaries were silently skipped for all primitives.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@sourcehawk
Copy link
Owner Author

Claude Review Cycle 1 Complete

Addressed:

  • beginFeature() unexported method bug (mutator.go:64): Exported beginFeature()BeginFeature() on the FeatureMutator interface and all three primitive mutators (cronjob, deployment, configmap). The unexported method meant the interface could never be satisfied by types outside internal/generic, so ApplyMutations silently skipped all feature boundaries. Updated all call sites and tests.

Intentionally ignored:

  • Doc || table delimiters (cronjob.md:13): Invalid — the tables in the file already use standard single-pipe | format. No || delimiters exist.
  • .golangci.yml shared file / PR description checklist (.golangci.yml:21): Valid observation about PR description accuracy, but not a code change. The .golangci.yml timeout change was a necessary build fix. Shared test file changes were required for the BeginFeature export.

@sourcehawk sourcehawk requested review from Copilot and removed request for Copilot March 24, 2026 02:10
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 33 out of 33 changed files in this pull request and generated 1 comment.

Rephrase the note about spec.suspend to acknowledge that Raw() can be
used to set it, while still recommending the framework's suspend mechanism.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@sourcehawk
Copy link
Owner Author

Claude Review Cycle 1 Complete

Addressed:

  • docs/primitives/cronjob.md:138 — Rephrased the spec.suspend note to clarify that no typed helper is provided but Raw() can be used, while recommending the framework's suspend mechanism.

Intentionally ignored:
None

<!-- claude-review-cycle -->

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings March 24, 2026 17:15
@sourcehawk sourcehawk requested review from Copilot and removed request for Copilot March 24, 2026 17:17
@sourcehawk sourcehawk requested review from Copilot and removed request for Copilot March 24, 2026 17:18
@sourcehawk sourcehawk requested review from Copilot and removed request for Copilot March 24, 2026 17:19
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 33 out of 33 changed files in this pull request and generated 4 comments.

…semantics

- Add require.NoError checks for Builder.Build() in all three
  TestMutate_OrderingAndFlavors subtests to fail clearly on builder
  validation errors instead of risking nil dereferences.
- Add comment in ApplyMutations explaining why BeginFeature is not
  called before the first mutation (newMutator creates the initial scope).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@sourcehawk
Copy link
Owner Author

Claude Review Cycle 1 Complete

Addressed:

  • flavors_test.go:37 — Added require.NoError check for Builder.Build() in "flavors run after baseline applicator" test
  • flavors_test.go:67 — Added require.NoError check for Builder.Build() in "flavors run in registration order" test
  • flavors_test.go:83 — Added require.NoError check for Builder.Build() in "flavor error is returned with context" test
  • mutate_helper.go:48 — Added comment documenting that BeginFeature() is intentionally not called before the first mutation because newMutator() creates the initial planning scope

Intentionally ignored:
None

<!-- claude-review-cycle -->

sourcehawk and others added 2 commits March 24, 2026 17:51
Apply the same pattern from deployment/configmap: NewMutator no longer
creates an initial feature plan. BeginFeature must be called before
registering mutations. Also fix mutate_helper to call BeginFeature
before every mutation (including the first).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings March 24, 2026 17:58
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 33 out of 33 changed files in this pull request and generated no new comments.

sourcehawk and others added 2 commits March 25, 2026 14:04
…tive

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Align with framework-wide SSA refactor: remove DefaultFieldApplicator,
WithCustomFieldApplicator, WithFieldApplicationFlavor, flavors.go, and
update tests to use Object() output instead of empty structs in Mutate().

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.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.

2 participants