Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

### Changed

- **Plan retention is bounded per phase, so replan churn no longer evicts the record of what ran.** Terminal plans were trimmed as one pool of 10 by creation time. `Superseded` is generated churn — every replan supersedes its predecessor — so on an active policy it filled the pool and deleted the `Applied` plans, which are the audit record of what actually executed against the database. The least informative state was evicting the most informative one. The bounds are now `Applied` 25 (never fewer than 30 days' worth, hard ceiling 200), `Failed` and `Rejected` 10 shared, `Superseded` 3; `Pending`, `Approved` and `Applying` are live and never evicted. The age floor makes the retained span a stated period rather than a function of how often a policy applies, and the ceiling stops that promise becoming unbounded growth. `pgroles.io/keep=true` still exempts a plan from every bound. Each bound is operator-level configuration — `PLAN_RETENTION_APPLIED`, `PLAN_RETENTION_APPLIED_MIN_AGE`, `PLAN_RETENTION_APPLIED_CEILING`, `PLAN_RETENTION_DECIDED`, `PLAN_RETENTION_SUPERSEDED` on the operator environment, replacing the `max_plans` parameter that nothing could ever set — and an invalid value refuses operator startup with the variable named. Deliberately not a `PostgresPolicy` field: retention caps object growth in the cluster, and the per-object need is what the `keep` label is for. The `Applied` bounds measure — and order — by `status.appliedAt`, not object creation, so a plan that waited on a reviewer is not already outside its floor the moment it executes. They also govern terminal-candidate pruning: deleting a candidate cascades to the plan it owns, so a promoted candidate owning an `Applied` plan is held to the `Applied` bounds instead of the flat terminal-candidate bound, and `pgroles.io/keep=true` on either the candidate or its plan exempts the pair. (#194)

- **`spec.mode: plan` is renamed to `spec.mode: observe`, with a deprecation window.** "Plan" now names exactly one thing, the `PostgresPolicyPlan` resource; the `ApprovalIgnored` reason `PlanModeNeverExecutes` is now `ObserveModeNeverExecutes`. The old value keeps working: `mode: plan` stays an accepted schema value with identical behaviour, so a GitOps controller re-applying an existing manifest is unaffected by the upgrade. A policy using it reports a `ModeValueDeprecated` condition, warns in the operator log, and counts toward `pgroles.deprecated.mode_plan`.
**Upgrade:** change `mode: plan` to `mode: observe` in your manifests at your convenience — a future release removes the `plan` value, and that removal will be the breaking change.

Expand Down
2 changes: 1 addition & 1 deletion charts/pgroles-operator/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ these policies, pinned to the `pgroles-system` namespace and the
| fullnameOverride | string | `""` | Replaces the full generated name in resource names. |
| nameOverride | string | `""` | Replaces the generated name in resource names, keeping the release prefix. |
| operator.affinity | object | `{}` | Affinity rules for operator pod scheduling. |
| operator.env | list | `[{"name":"RUST_LOG","value":"info,pgroles_operator=debug"}]` | Additional environment variables for the operator container. This is also where operator-wide settings live, since the operator is configured by environment rather than by flags. Notable variables: `OTEL_EXPORTER_OTLP_ENDPOINT` enables OTLP metrics and logs, including structured ephemeral-access audit events; `OTEL_LOGS_EXPORTER=none` disables the log half when another agent already ships container logs off-cluster; `EPHEMERAL_ACCESS_MAXIMUM_DURATION` (default `24h`) and `EPHEMERAL_ACCESS_MAX_PENDING_TTL` (default `1h`) are the only cluster-wide ceilings on ephemeral access, and an access policy exceeding either is rejected with `Accepted=False`. |
| operator.env | list | `[{"name":"RUST_LOG","value":"info,pgroles_operator=debug"}]` | Additional environment variables for the operator container. This is also where operator-wide settings live, since the operator is configured by environment rather than by flags. Notable variables: `OTEL_EXPORTER_OTLP_ENDPOINT` enables OTLP metrics and logs, including structured ephemeral-access audit events; `OTEL_LOGS_EXPORTER=none` disables the log half when another agent already ships container logs off-cluster; `EPHEMERAL_ACCESS_MAXIMUM_DURATION` (default `24h`) and `EPHEMERAL_ACCESS_MAX_PENDING_TTL` (default `1h`) are the only cluster-wide ceilings on ephemeral access, and an access policy exceeding either is rejected with `Accepted=False`; the `PLAN_RETENTION_*` variables bound how many terminal plans are kept per policy — `PLAN_RETENTION_APPLIED` (default `25`), `PLAN_RETENTION_APPLIED_MIN_AGE` (default `720h`), `PLAN_RETENTION_APPLIED_CEILING` (default `200`), `PLAN_RETENTION_DECIDED` (default `10`, Failed and Rejected shared) and `PLAN_RETENTION_SUPERSEDED` (default `3`) — and an invalid value refuses operator startup with the variable named. |
| operator.http.port | int | `8080` | Port serving the `/livez` and `/readyz` probes. |
| operator.image.pullPolicy | string | `"IfNotPresent"` | Image pull policy for the operator container. |
| operator.image.repository | string | `"ghcr.io/hardbyte/pgroles-operator"` | Operator container image repository. |
Expand Down
18 changes: 17 additions & 1 deletion charts/pgroles-operator/values.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,13 @@ operator:
# `EPHEMERAL_ACCESS_MAXIMUM_DURATION` (default `24h`) and
# `EPHEMERAL_ACCESS_MAX_PENDING_TTL` (default `1h`) are the only cluster-wide
# ceilings on ephemeral access, and an access policy exceeding either is
# rejected with `Accepted=False`.
# rejected with `Accepted=False`; the `PLAN_RETENTION_*` variables bound how
# many terminal plans are kept per policy — `PLAN_RETENTION_APPLIED` (default
# `25`), `PLAN_RETENTION_APPLIED_MIN_AGE` (default `720h`),
# `PLAN_RETENTION_APPLIED_CEILING` (default `200`), `PLAN_RETENTION_DECIDED`
# (default `10`, Failed and Rejected shared) and `PLAN_RETENTION_SUPERSEDED`
# (default `3`) — and an invalid value refuses operator startup with the
# variable named.
env:
- name: RUST_LOG
value: "info,pgroles_operator=debug"
Expand All @@ -83,6 +89,16 @@ operator:
# value: 24h
# - name: EPHEMERAL_ACCESS_MAX_PENDING_TTL
# value: 1h
# - name: PLAN_RETENTION_APPLIED
# value: "25"
# - name: PLAN_RETENTION_APPLIED_MIN_AGE
# value: 720h
# - name: PLAN_RETENTION_APPLIED_CEILING
# value: "200"
# - name: PLAN_RETENTION_DECIDED
# value: "10"
# - name: PLAN_RETENTION_SUPERSEDED
# value: "3"

# -- Annotations to add to the operator pod.
podAnnotations: {}
Expand Down
Loading
Loading