Skip to content
Closed
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
43 changes: 34 additions & 9 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,33 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
Install with `sudo formae plugin install k8s` on the host that runs the
formae agent.

## [Unreleased]
## [0.1.10]

Requires formae >= 0.89.0 for the custom resource spec fidelity below; on an
older agent the spec keeps today's behavior.

### Changed

- **A custom resource's `spec` now updates as one whole value.** Changing any
part of the spec sends the complete document to the apiserver in a single
replacement, the way `kubectl apply` of the full manifest behaves, instead
of a series of per-field edits formae computed from a document whose grammar
it cannot know. `minFormaeVersion` is raised to 0.89.0 accordingly.

### Fixed

- **Empty objects and lists in a custom resource `spec` reach the cluster
exactly as written.** For many custom resources an empty member is itself
the configuration: a cert-manager `ClusterIssuer` selects the selfSigned
issuer type with `selfSigned = new Dynamic {}`. Previously formae cleaned
empty objects and lists out of the spec before writing, so the apiserver
received an empty spec and admission webhooks rejected it, and adding a
placeholder inside the empty member did not help because the cleanup
collapsed it again. The spec is now preserved byte for byte. A custom
resource that already lost empty members this way is repaired by the next
apply, and a placeholder value added to work around the old behavior can be
removed.

- **Paused Deployments settle instead of polling forever.** A `Deployment` with
`spec.paused: true` never converges its replica counts — the controller stops
reconciling by design — so `Status()` reported `InProgress` until the operation
Expand Down Expand Up @@ -52,21 +75,23 @@ formae agent.
stayed empty during a rollout. Provisioner messages now pass through on
`InProgress` (e.g. `replicas: 2/3 ready`) and are blanked only on terminal
`Success`, where a lingering message is just noise.
- Drop the removed `--watch` flag from the example commands in the README,
CONTRIBUTING, the helm/flux/crossplane/bookstore/custom-resource docs, and
the example file headers. `formae apply`/`destroy` are submit-then-poll.

### Added

- `K8S::Core::Secret` adopts formae's first-class map-shaped secret types. A
Secret's value can now be referenced one key at a time with
`secret.res.secretValue.at("key")`, resolved live at the plugin-call
boundary so a consumer such as a target credential picks it up without an
agent restart. The decoded value is hashed at rest and excluded from drift
detection. Requires formae 0.89.0 or later; `minFormaeVersion` is raised to
0.89.0.
- `examples/rollout-safety/` — one folder per case (paused Deployment,
`OnDelete` StatefulSet, partitioned StatefulSet, HPA coexistence), each with
`create.pkl`/`update.pkl` and the old-vs-new plugin behavior in the header.

## [0.1.10]

### Changed

- Drop the removed `--watch` flag from the example commands in the README,
CONTRIBUTING, the helm/flux/crossplane/bookstore/custom-resource docs, and
the example file headers. `formae apply`/`destroy` are submit-then-poll.

## [0.1.9]

### Changed
Expand Down
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ require (
github.com/aws/aws-sdk-go-v2/config v1.32.31
github.com/oracle/oci-go-sdk/v65 v65.122.0
github.com/ovh/go-ovh v1.9.0
github.com/platform-engineering-labs/formae/pkg/model v0.1.26
github.com/platform-engineering-labs/formae/pkg/model v0.1.27
github.com/platform-engineering-labs/formae/pkg/plugin v0.4.1
github.com/platform-engineering-labs/formae/pkg/plugin-conformance-tests v0.2.6
github.com/stretchr/testify v1.11.1
Expand Down
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -167,8 +167,8 @@ github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c h1:+mdjkGKdHQG3305AYmd
github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c/go.mod h1:7rwL4CYBLnjLxUqIJNnCWiEdr3bn6IUYi15bNlnbCCU=
github.com/platform-engineering-labs/formae/pkg/api/model v0.1.1 h1:ZMTgKwSomy2cVcl/+NivSqopbWeHbmYeQ+BxoYq8bVY=
github.com/platform-engineering-labs/formae/pkg/api/model v0.1.1/go.mod h1:0ncHFCsGA6b0w1kBm6m+QwJ823qAY2vL47GvoR0BTyU=
github.com/platform-engineering-labs/formae/pkg/model v0.1.26 h1:80p843bmz9sLTtUFveMGWYeGcpRjvceyVxUgPvlApqs=
github.com/platform-engineering-labs/formae/pkg/model v0.1.26/go.mod h1:1dmsFwoaJZkHevBsAIZr068CWzG7de1VNz7PFWvM3Z8=
github.com/platform-engineering-labs/formae/pkg/model v0.1.27 h1:9EhTn19+iajQecS4/spXmtOEzoolw2/tO7TUiEoc/1o=
github.com/platform-engineering-labs/formae/pkg/model v0.1.27/go.mod h1:1dmsFwoaJZkHevBsAIZr068CWzG7de1VNz7PFWvM3Z8=
github.com/platform-engineering-labs/formae/pkg/plugin v0.4.1 h1:dg8TBQVJR8DVF28bvmzAG4Ms1Id0yIr6Kd8c3UTO3iw=
github.com/platform-engineering-labs/formae/pkg/plugin v0.4.1/go.mod h1:ZFXMfeZljHVDWQTSeJ4dj2EG3iTjdrxQjm7DpvOYUXk=
github.com/platform-engineering-labs/formae/pkg/plugin-conformance-tests v0.2.6 h1:xslGa2DOFslxD0jxwI/XaUxdetpqSn++rfpqbW/uLbE=
Expand Down
6 changes: 5 additions & 1 deletion schema/pkl-main/custom/CustomResource.pkl
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,11 @@ open class CustomResource extends k8s.Resource {
/// is intentionally untyped (escape-hatch model — no per-field validation).
/// `Any` (not just `Dynamic`) so it accepts both hand-authored Dynamic
/// literals and typed objects produced by Helm chart rendering.
@k8s.FieldHint {}
/// Atomic: formae has no schema knowledge inside an `Any` body, so
/// whole-value replacement is the only update it can stand behind.
/// preserveEmptyValues: empty members are configuration here (an empty
/// object selects a variant by key presence), never rendering noise.
@k8s.FieldHint { updateMethod = "Atomic"; preserveEmptyValues = true }
spec: Any?

/// Composite identity, byte-identical to prov.CustomResourceID in Go:
Expand Down
6 changes: 5 additions & 1 deletion schema/pkl/v1.21/custom/CustomResource.pkl
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,11 @@ open class CustomResource extends k8s.Resource {
/// is intentionally untyped (escape-hatch model — no per-field validation).
/// `Any` (not just `Dynamic`) so it accepts both hand-authored Dynamic
/// literals and typed objects produced by Helm chart rendering.
@k8s.FieldHint {}
/// Atomic: formae has no schema knowledge inside an `Any` body, so
/// whole-value replacement is the only update it can stand behind.
/// preserveEmptyValues: empty members are configuration here (an empty
/// object selects a variant by key presence), never rendering noise.
@k8s.FieldHint { updateMethod = "Atomic"; preserveEmptyValues = true }
spec: Any?

/// Composite identity, byte-identical to prov.CustomResourceID in Go:
Expand Down
6 changes: 5 additions & 1 deletion schema/pkl/v1.22/custom/CustomResource.pkl
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,11 @@ open class CustomResource extends k8s.Resource {
/// is intentionally untyped (escape-hatch model — no per-field validation).
/// `Any` (not just `Dynamic`) so it accepts both hand-authored Dynamic
/// literals and typed objects produced by Helm chart rendering.
@k8s.FieldHint {}
/// Atomic: formae has no schema knowledge inside an `Any` body, so
/// whole-value replacement is the only update it can stand behind.
/// preserveEmptyValues: empty members are configuration here (an empty
/// object selects a variant by key presence), never rendering noise.
@k8s.FieldHint { updateMethod = "Atomic"; preserveEmptyValues = true }
spec: Any?

/// Composite identity, byte-identical to prov.CustomResourceID in Go:
Expand Down
6 changes: 5 additions & 1 deletion schema/pkl/v1.23/custom/CustomResource.pkl
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,11 @@ open class CustomResource extends k8s.Resource {
/// is intentionally untyped (escape-hatch model — no per-field validation).
/// `Any` (not just `Dynamic`) so it accepts both hand-authored Dynamic
/// literals and typed objects produced by Helm chart rendering.
@k8s.FieldHint {}
/// Atomic: formae has no schema knowledge inside an `Any` body, so
/// whole-value replacement is the only update it can stand behind.
/// preserveEmptyValues: empty members are configuration here (an empty
/// object selects a variant by key presence), never rendering noise.
@k8s.FieldHint { updateMethod = "Atomic"; preserveEmptyValues = true }
spec: Any?

/// Composite identity, byte-identical to prov.CustomResourceID in Go:
Expand Down
6 changes: 5 additions & 1 deletion schema/pkl/v1.24/custom/CustomResource.pkl
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,11 @@ open class CustomResource extends k8s.Resource {
/// is intentionally untyped (escape-hatch model — no per-field validation).
/// `Any` (not just `Dynamic`) so it accepts both hand-authored Dynamic
/// literals and typed objects produced by Helm chart rendering.
@k8s.FieldHint {}
/// Atomic: formae has no schema knowledge inside an `Any` body, so
/// whole-value replacement is the only update it can stand behind.
/// preserveEmptyValues: empty members are configuration here (an empty
/// object selects a variant by key presence), never rendering noise.
@k8s.FieldHint { updateMethod = "Atomic"; preserveEmptyValues = true }
spec: Any?

/// Composite identity, byte-identical to prov.CustomResourceID in Go:
Expand Down
6 changes: 5 additions & 1 deletion schema/pkl/v1.25/custom/CustomResource.pkl
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,11 @@ open class CustomResource extends k8s.Resource {
/// is intentionally untyped (escape-hatch model — no per-field validation).
/// `Any` (not just `Dynamic`) so it accepts both hand-authored Dynamic
/// literals and typed objects produced by Helm chart rendering.
@k8s.FieldHint {}
/// Atomic: formae has no schema knowledge inside an `Any` body, so
/// whole-value replacement is the only update it can stand behind.
/// preserveEmptyValues: empty members are configuration here (an empty
/// object selects a variant by key presence), never rendering noise.
@k8s.FieldHint { updateMethod = "Atomic"; preserveEmptyValues = true }
spec: Any?

/// Composite identity, byte-identical to prov.CustomResourceID in Go:
Expand Down
6 changes: 5 additions & 1 deletion schema/pkl/v1.26/custom/CustomResource.pkl
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,11 @@ open class CustomResource extends k8s.Resource {
/// is intentionally untyped (escape-hatch model — no per-field validation).
/// `Any` (not just `Dynamic`) so it accepts both hand-authored Dynamic
/// literals and typed objects produced by Helm chart rendering.
@k8s.FieldHint {}
/// Atomic: formae has no schema knowledge inside an `Any` body, so
/// whole-value replacement is the only update it can stand behind.
/// preserveEmptyValues: empty members are configuration here (an empty
/// object selects a variant by key presence), never rendering noise.
@k8s.FieldHint { updateMethod = "Atomic"; preserveEmptyValues = true }
spec: Any?

/// Composite identity, byte-identical to prov.CustomResourceID in Go:
Expand Down
6 changes: 5 additions & 1 deletion schema/pkl/v1.27/custom/CustomResource.pkl
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,11 @@ open class CustomResource extends k8s.Resource {
/// is intentionally untyped (escape-hatch model — no per-field validation).
/// `Any` (not just `Dynamic`) so it accepts both hand-authored Dynamic
/// literals and typed objects produced by Helm chart rendering.
@k8s.FieldHint {}
/// Atomic: formae has no schema knowledge inside an `Any` body, so
/// whole-value replacement is the only update it can stand behind.
/// preserveEmptyValues: empty members are configuration here (an empty
/// object selects a variant by key presence), never rendering noise.
@k8s.FieldHint { updateMethod = "Atomic"; preserveEmptyValues = true }
spec: Any?

/// Composite identity, byte-identical to prov.CustomResourceID in Go:
Expand Down
6 changes: 5 additions & 1 deletion schema/pkl/v1.28/custom/CustomResource.pkl
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,11 @@ open class CustomResource extends k8s.Resource {
/// is intentionally untyped (escape-hatch model — no per-field validation).
/// `Any` (not just `Dynamic`) so it accepts both hand-authored Dynamic
/// literals and typed objects produced by Helm chart rendering.
@k8s.FieldHint {}
/// Atomic: formae has no schema knowledge inside an `Any` body, so
/// whole-value replacement is the only update it can stand behind.
/// preserveEmptyValues: empty members are configuration here (an empty
/// object selects a variant by key presence), never rendering noise.
@k8s.FieldHint { updateMethod = "Atomic"; preserveEmptyValues = true }
spec: Any?

/// Composite identity, byte-identical to prov.CustomResourceID in Go:
Expand Down
6 changes: 5 additions & 1 deletion schema/pkl/v1.29/custom/CustomResource.pkl
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,11 @@ open class CustomResource extends k8s.Resource {
/// is intentionally untyped (escape-hatch model — no per-field validation).
/// `Any` (not just `Dynamic`) so it accepts both hand-authored Dynamic
/// literals and typed objects produced by Helm chart rendering.
@k8s.FieldHint {}
/// Atomic: formae has no schema knowledge inside an `Any` body, so
/// whole-value replacement is the only update it can stand behind.
/// preserveEmptyValues: empty members are configuration here (an empty
/// object selects a variant by key presence), never rendering noise.
@k8s.FieldHint { updateMethod = "Atomic"; preserveEmptyValues = true }
spec: Any?

/// Composite identity, byte-identical to prov.CustomResourceID in Go:
Expand Down
6 changes: 5 additions & 1 deletion schema/pkl/v1.30/custom/CustomResource.pkl
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,11 @@ open class CustomResource extends k8s.Resource {
/// is intentionally untyped (escape-hatch model — no per-field validation).
/// `Any` (not just `Dynamic`) so it accepts both hand-authored Dynamic
/// literals and typed objects produced by Helm chart rendering.
@k8s.FieldHint {}
/// Atomic: formae has no schema knowledge inside an `Any` body, so
/// whole-value replacement is the only update it can stand behind.
/// preserveEmptyValues: empty members are configuration here (an empty
/// object selects a variant by key presence), never rendering noise.
@k8s.FieldHint { updateMethod = "Atomic"; preserveEmptyValues = true }
spec: Any?

/// Composite identity, byte-identical to prov.CustomResourceID in Go:
Expand Down
6 changes: 5 additions & 1 deletion schema/pkl/v1.31/custom/CustomResource.pkl
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,11 @@ open class CustomResource extends k8s.Resource {
/// is intentionally untyped (escape-hatch model — no per-field validation).
/// `Any` (not just `Dynamic`) so it accepts both hand-authored Dynamic
/// literals and typed objects produced by Helm chart rendering.
@k8s.FieldHint {}
/// Atomic: formae has no schema knowledge inside an `Any` body, so
/// whole-value replacement is the only update it can stand behind.
/// preserveEmptyValues: empty members are configuration here (an empty
/// object selects a variant by key presence), never rendering noise.
@k8s.FieldHint { updateMethod = "Atomic"; preserveEmptyValues = true }
spec: Any?

/// Composite identity, byte-identical to prov.CustomResourceID in Go:
Expand Down
6 changes: 5 additions & 1 deletion schema/pkl/v1.32/custom/CustomResource.pkl
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,11 @@ open class CustomResource extends k8s.Resource {
/// is intentionally untyped (escape-hatch model — no per-field validation).
/// `Any` (not just `Dynamic`) so it accepts both hand-authored Dynamic
/// literals and typed objects produced by Helm chart rendering.
@k8s.FieldHint {}
/// Atomic: formae has no schema knowledge inside an `Any` body, so
/// whole-value replacement is the only update it can stand behind.
/// preserveEmptyValues: empty members are configuration here (an empty
/// object selects a variant by key presence), never rendering noise.
@k8s.FieldHint { updateMethod = "Atomic"; preserveEmptyValues = true }
spec: Any?

/// Composite identity, byte-identical to prov.CustomResourceID in Go:
Expand Down
6 changes: 5 additions & 1 deletion schema/pkl/v1.33/custom/CustomResource.pkl
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,11 @@ open class CustomResource extends k8s.Resource {
/// is intentionally untyped (escape-hatch model — no per-field validation).
/// `Any` (not just `Dynamic`) so it accepts both hand-authored Dynamic
/// literals and typed objects produced by Helm chart rendering.
@k8s.FieldHint {}
/// Atomic: formae has no schema knowledge inside an `Any` body, so
/// whole-value replacement is the only update it can stand behind.
/// preserveEmptyValues: empty members are configuration here (an empty
/// object selects a variant by key presence), never rendering noise.
@k8s.FieldHint { updateMethod = "Atomic"; preserveEmptyValues = true }
spec: Any?

/// Composite identity, byte-identical to prov.CustomResourceID in Go:
Expand Down
6 changes: 5 additions & 1 deletion schema/pkl/v1.34/custom/CustomResource.pkl
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,11 @@ open class CustomResource extends k8s.Resource {
/// is intentionally untyped (escape-hatch model — no per-field validation).
/// `Any` (not just `Dynamic`) so it accepts both hand-authored Dynamic
/// literals and typed objects produced by Helm chart rendering.
@k8s.FieldHint {}
/// Atomic: formae has no schema knowledge inside an `Any` body, so
/// whole-value replacement is the only update it can stand behind.
/// preserveEmptyValues: empty members are configuration here (an empty
/// object selects a variant by key presence), never rendering noise.
@k8s.FieldHint { updateMethod = "Atomic"; preserveEmptyValues = true }
spec: Any?

/// Composite identity, byte-identical to prov.CustomResourceID in Go:
Expand Down
6 changes: 5 additions & 1 deletion schema/pkl/v1.35/custom/CustomResource.pkl
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,11 @@ open class CustomResource extends k8s.Resource {
/// is intentionally untyped (escape-hatch model — no per-field validation).
/// `Any` (not just `Dynamic`) so it accepts both hand-authored Dynamic
/// literals and typed objects produced by Helm chart rendering.
@k8s.FieldHint {}
/// Atomic: formae has no schema knowledge inside an `Any` body, so
/// whole-value replacement is the only update it can stand behind.
/// preserveEmptyValues: empty members are configuration here (an empty
/// object selects a variant by key presence), never rendering noise.
@k8s.FieldHint { updateMethod = "Atomic"; preserveEmptyValues = true }
spec: Any?

/// Composite identity, byte-identical to prov.CustomResourceID in Go:
Expand Down
6 changes: 5 additions & 1 deletion schema/pkl/v1.36/custom/CustomResource.pkl
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,11 @@ open class CustomResource extends k8s.Resource {
/// is intentionally untyped (escape-hatch model — no per-field validation).
/// `Any` (not just `Dynamic`) so it accepts both hand-authored Dynamic
/// literals and typed objects produced by Helm chart rendering.
@k8s.FieldHint {}
/// Atomic: formae has no schema knowledge inside an `Any` body, so
/// whole-value replacement is the only update it can stand behind.
/// preserveEmptyValues: empty members are configuration here (an empty
/// object selects a variant by key presence), never rendering noise.
@k8s.FieldHint { updateMethod = "Atomic"; preserveEmptyValues = true }
spec: Any?

/// Composite identity, byte-identical to prov.CustomResourceID in Go:
Expand Down