admission:
  - name: maintenance-freeze
    targets: [deployments]
    operations: [CREATE, UPDATE, DELETE]
    subresources: ["", scale]
    when: Active
    action: Deny
    message: >-
      Maintenance is active for {{ .target.metadata.namespace }}.
      {{ if .window.end }}Retry after {{ .window.end }}.{{ end }}

Admission rules belong under spec.admission. They select named targets, operations, and optionally subresources. when: Active denies inside the union of scheduled windows. Use when: Inactive to permit changes only during those windows. action: Deny is currently the only action; defaults are Active and Deny.

Every matching deny wins, including a deny from another ChangeWindow. A custom message uses the target and window template context. If the message fails to render, the request is still denied. Browse the complete examples for ready-to-adapt manifests.

Audiences

Every entry under spec.admission and spec.config.admission.warnings can define its own audience and matchConditions. They combine with the entry’s schedule, target, operation, and subresource filters using AND.

audience:
  include:
    - kind: User
      name: alice
    - kind: Group
      name: developers
    - kind: ServiceAccount
      name: deployer
      namespace: team-a
  exclude:
    - kind: User
      name: break-glass

Includes use OR: any matching subject selects the caller. An omitted or empty include list selects everyone. Any matching exclusion takes precedence. With no audience, the entry applies to all callers. Subject names are exact, case-sensitive matches, with no wildcard expansion.

User matches the authenticated username, Group matches any authenticated group, and ServiceAccount matches the exact namespace/name identity. Use kind: ServiceAccount (not SA) and always specify its namespace. Namespace must be omitted for User and Group subjects. Each include/exclude list permits at most 64 subjects. Audiences filter this entry; they do not grant RBAC permissions or exempt the caller from other entries or ChangeWindows.

Match conditions

matchConditions:
  - name: replicas-changed
    expression: >-
      object != null && oldObject != null &&
      object.spec.replicas != oldObject.spec.replicas

Conditions are named CEL expressions that return a boolean. Every condition must be true for the entry to match. An empty list matches without extra conditions. Names must be unique DNS labels. Each entry allows up to 64 conditions, with at most 4096 expression bytes each.

VariableValue
objectThe new admission object; null on DELETE.
oldObjectThe previous admission object; null on CREATE.
requestAdmission request metadata, including operation, namespace, subResource, dryRun, and userInfo. Raw objects are exposed through their separate variables.
namespaceObjectThe namespace used for selection, including labels; null for Nodes.

For /scale, object and oldObject are the actual Scale payloads. Target selection still uses the parent resource’s labels. CEL variables are dynamic: the expression is checked for a boolean result when the policy is accepted, but field presence and types depend on the request. Guard optional fields with has() and guard null objects before reading them. No authorizer, API reads, network functions, or Capsule template helpers are available in CEL.

A false condition skips the entry, including when another condition encountered an ordinary evaluation error. If no condition is false and evaluation fails, a denial entry rejects the request with a diagnostic error. A warning entry returns a diagnostic warning and does not block the request. Invalid expressions are rejected when the ChangeWindow is created or updated. Runtime diagnostics identify the policy and entry without exposing evaluated object values.

Expressions are compiled once per policy generation and reused. Each expression has a 10,000-unit runtime cost limit; an admission request has separate 100,000-unit budgets for denials and warnings. Request-budget exhaustion or cancellation stops evaluation. Keep conditions small and predictable, and test them with server-side dry runs before depending on a freeze.

Parent resources and subresources

An omitted or empty subresources list matches only the parent resource. Include scale to cover scaling and "" to retain parent writes. Use endpoint names without a leading slash. "*" matches every named subresource; subresources: ["", "*"] covers both parent and subresource writes. Partial subresource patterns such as sta* are not supported. This option applies to denials and warnings; action endpoints must remain concrete. GET, LIST, WATCH, and CONNECT are not blocked by these rules.

Parent labels are fetched only when needed for a subresource label selector or message’s target context. A rule selecting all labels and rendering only window information needs no parent-object read. Custom subresource targets using parent labels or .target need explicit parent-resource get permissions for the controller; no broad read permission is granted automatically.

The controller’s own authenticated action requests bypass only their originating ChangeWindow. Other policies still apply. Calling Kubernetes directly as the executor service account does not grant this bypass.

Warning periods

config:
  admission:
    warnings:
      - name: upcoming
        targets: [deployments]
        operations: [CREATE, UPDATE, DELETE]
        subresources: ["", scale]
        when: BeforeStart
        within: 1h
        message: "Maintenance begins at {{ .window.start }} (in {{ .window.startsIn }})."
      - name: ending
        targets: [deployments]
        operations: [UPDATE]
        subresources: ["", scale]
        when: BeforeEnd
        within: 15m
        message: "Maintenance ends at {{ .window.end }}."

Warnings live under spec.config.admission.warnings. They are Kubernetes admission warnings returned to clients such as kubectl when users write affected resources. They are not background notifications.

whenNotice periodwithin
BeforeStartBefore the next window starts.Required, positive, at most 168h.
ActiveThroughout the active window.Must be omitted.
BeforeEndBefore the known end of the active window.Required, positive, at most 168h.

Warnings are also returned on dry runs and denied writes. Responses contain at most eight distinct warnings, each limited to 256 bytes. Keep messages concise and avoid sensitive values. For a warning-only policy, omit actions and admission, retaining targets, schedules, and the warnings configuration.