schedule-action-controller

module
v0.0.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Oct 12, 2026 License: Apache-2.0

README

Scheduled Action Controller

A standalone Capsule addon for scheduled Kubernetes resource changes and maintenance admission rules. The cluster-scoped ChangeWindow API belongs to addons.projectcapsule.dev/v1alpha1. Capsule core and Tenant objects are not required.

Select namespaces and namespaced resource kinds, define cron windows, then apply Go-templated patches at the start and end. Capture existing values before mutation to restore replicas afterward. Admission can deny writes during or outside windows and return advance or active-window warnings. Each denial and warning supports User/Group/ServiceAccount audiences and CEL match conditions. Admission can also protect Node pools selected by Node labels.

See the complete ChangeWindow example and executor RBAC example.

Documentation

The Hugo/Doks documentation includes installation, scheduling, resource actions, admission and warnings, operations, a dedicated Examples section, a development guide, and a generated API reference. Preview it with devbox run -- task docs:serve, then open http://localhost:1313. Use task docs:build for static output in dist/docs/ and task docs:check to validate the reference, examples, and rendered links.

The site requires Hugo Extended 0.166.0 or newer and Node 24. Documentation tasks install the locked Doks dependencies automatically. Use the pinned Devbox environment; a system/Homebrew Hugo earlier on PATH can otherwise cause a misleading template for shortcode "example" not found error. Documentation tasks check the Hugo version before generating or building anything.

Headlamp plugin

The plugin in headlamp/ adds Change Windows to Headlamp's main navigation: a weekly calendar, searchable action table, detail pages, and a maintenance section on namespaced resources, including Pods through verified workload owner references. Run task headlamp:install headlamp:check in Devbox. The plugin uses the viewer's existing Kubernetes permissions and does not execute actions. See the Headlamp guide for installation and RBAC.

Semantics

The alpha API is now ChangeWindow (changewindows, short name cw), replacing ScheduledAction. This is a breaking rename, not an additional served kind. Before upgrading an existing installation, use the old controller to finish or delete old ScheduledActions and wait for restoration/finalizers to complete. Save their specs, update the kind to ChangeWindow, upgrade the CRDs/controller, then reapply them. Do not remove the old CRD or force-remove finalizers while restorations are pending. Helm does not upgrade or remove CRDs automatically. Update RBAC and clients to changewindows; metrics now use change_window_. The repository, module, chart, and controller deployment names are unchanged.

  • namespaceSelectors are ORed. An explicit [{}] selects every namespace except the controller namespace and kube-system, kube-public, and kube-node-lease. These namespaces keep control-plane startup and recovery outside maintenance. Resource selectors use standard Kubernetes label semantics.
  • Node targets are admission-only and use labels on Nodes. Omit namespace selectors for Node-only windows; other cluster-scoped targets and actions on Nodes are rejected.
  • Admission targets reuse Capsule core GVK patterns: apps, apps/*, *Set, or apiVersion: "*" with kind: "*". Patterns select namespaced resources, including new CRDs, and do not require Capsule to be installed. Actions use concrete GVKs. Admission subresources: ["", "*"] covers parent and all subresource writes.
  • schedules use five-field cron and IANA time zones (UTC by default). Durations are elapsed time, greater than zero and at most 168h. Starts are inclusive and ends exclusive. Overlapping and adjacent schedules within one CR form one window. DST follows the named timezone: nonexistent local times are skipped and repeated times may produce overlapping occurrences.
  • Windows use bounded 14-day lookback/lookahead. window.end/status end are absent or empty when a continuous union extends beyond lookahead. Start is the earliest known start within lookback, retained in the journal for that occurrence.
  • Actions snapshot matching objects at the first reconciliation in an active window. Newly matching resources join the next window. Entirely missed windows do not replay obsolete start actions. Restores pending from an earlier window finish before a new window can begin.
  • Capture uses RFC 6901 JSON pointers and is persisted in a private Secret before any mutation. A window supports at most 256 action/resource participants and a 512 KiB journal. Limits fail before workload mutation. Captures and rendered patches are retained only while required for retry/restore.
  • Starts and ends retry until successful, then stop. The controller does not continuously enforce the patched value. A crash after an accepted write may repeat the same SSA patch. Deletion triggers restoration through a finalizer; failed restores retain their journal and block deletion. Spec edits are rejected while the restoration finalizer is present.
  • End actions use recorded UIDs regardless of label changes. Missing resources and replacements are skipped. UID/resourceVersion checks guard concurrent writes. force defaults to false: ownership conflicts are errors, never implicit force. Restoring a value does not restore its previous SSA ownership. Different CRs are independent; avoid overlapping forced writes to the same fields.

Templates and subresources

Templates use Go text/template with missingkey=error, standard template functions, toJson, and quote. Context includes .target (the parent object), .captured.<name>, .window.start, .window.end, .window.startsIn, and .window.phase. Action manifests are rendered once when the journal is prepared, so retries reuse stable content. Templates cannot redirect identity or overwrite UID/resourceVersion preconditions. Output is bounded; no network or shell functions are exposed. This addon does not implement Capsule resource-loading template context.

Both capture and apply accept subresource. Discovery determines the payload kind: Deployment /scale receives autoscaling/v1 Scale. Unsupported endpoints and unsupported SSA return errors without falling back to the parent. Deployment rollout restart is a parent patch to spec.template.metadata.annotations, not /rollout; use .window.start as a stable restart timestamp.

Admission and security

Admission rules support when: Active or Inactive, action: Deny, explicit operations, target references, subresources, and a custom templated message. Any deny wins. Parent resources are the default; include scale explicitly to cover scaling. Old and new labels are checked on updates to prevent label removal from bypassing a restriction. Invalid message rendering still denies the request.

Every denial and warning can independently define audience.include and audience.exclude (User, Group, or ServiceAccount), plus named CEL matchConditions. Includes use OR; exclusions win. Conditions use AND and receive object, oldObject, request, and namespaceObject. Omitted filters preserve existing behavior. Denial evaluation errors fail closed; warning errors produce diagnostic warnings. See the admission guide and node-pool example.

Warnings support BeforeStart, Active, and BeforeEnd. BeforeStart/BeforeEnd require positive within (at most 168h). They are Kubernetes admission warnings on writes, including dry runs and denied requests, not proactive notifications or GET/LIST/WATCH responses. Up to eight distinct messages of 256 bytes are returned.

The policy cache is compiled on spec changes and indexed by parent GVR. Namespace labels and policy updates follow informer consistency. Admission evaluates the clock directly and does not rely on status to activate a restriction. Unsynchronized or invalid policy caches fail closed. The webhook uses failurePolicy: Fail and is installed for namespaced writes cluster-wide, except these protected namespaces, and parent Node writes (not Node status or leases). Run multiple replicas for availability; every replica serves admission and only the leader executes actions.

Only cluster administrators should manage ChangeWindows. Resource actions run as the explicitly configured service account. Grant the controller permission to impersonate only approved executors, and give those executors workload permissions only in intended namespaces. The default installation grants no arbitrary executor impersonation or workload writes. The controller namespace holds sensitive capture Secrets; restrict access and enable Kubernetes encryption at rest as appropriate.

The authenticated executor marker bypasses only its own ChangeWindow; using the same service account directly is insufficient. Other policies still apply. Parent reads for admission to /scale are restricted to Deployments, StatefulSets and ReplicaSets by default; other target kinds require explicit controller read RBAC.

Install and develop

Helm is the only supported installation method. Install cert-manager first, then use the local chart with an image available to your cluster:

helm upgrade --install scheduled-action charts/change-windows \
  --namespace capsule-system --create-namespace \
  --set image.registry=registry.example.com \
  --set image.repository=scheduled-action \
  --set image.tag=dev

Follow the installation guide to build the image and verify the release. Configure executor RBAC for your Helm release before creating action-bearing ChangeWindows. Admission-only CRs do not need a service account.

For development:

devbox shell
task build test lint
task e2e IMG=ghcr.io/projectcapsule/change-windows:e2e

See DEVELOPMENT.md, CONTRIBUTING.md, and AGENT.md for project conventions.

Metrics

Metrics are served on authenticated HTTPS port 8443. Grant a Prometheus service account access to the /metrics non-resource URL and configure its bearer token. Controller-runtime workqueue/reconcile metrics are included. Addon metrics use only finite phase/result/decision labels:

Metric Meaning
change_window_transitions_total{phase} Observed start/end transitions persisted in status
change_window_executions_total{phase,result} SSA attempts, including retries and missing/replaced skips
change_window_execution_duration_seconds{phase} SSA attempt duration including API reads/writes
change_window_admission_requests_total{decision} Allow, deny and error responses
change_window_admission_warnings_total Deduplicated emitted warnings
change_window_admission_duration_seconds Admission handler duration

Counters reset on process restart and are operational telemetry, not an exactly-once audit ledger. No captured values, resource names or namespace names are metric labels.

License

Apache-2.0. See LICENSE.

Directories

Path Synopsis
api
v1alpha1
Package v1alpha1 contains API Schema definitions for the addons v1alpha1 API group.
Package v1alpha1 contains API Schema definitions for the addons v1alpha1 API group.
internal
executor
Package executor performs identity-constrained server-side apply as an explicit service account.
Package executor performs identity-constrained server-side apply as an explicit service account.
policy
Package policy compiles immutable selectors and templates shared by reconciliation and admission.
Package policy compiles immutable selectors and templates shared by reconciliation and admission.
schedule
Package schedule evaluates unions of cron windows without running background jobs.
Package schedule evaluates unions of cron windows without running background jobs.
testutil
Package testutil provides shared API fixtures without a second test framework.
Package testutil provides shared API fixtures without a second test framework.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL