Skip to content

fix(crud): read generated outputs in the apply that creates them - #47

Merged
Aleksei Sviridkin (lexfrei) merged 4 commits into
masterfrom
fix/wait-for-generated-outputs
Sep 21, 2026
Merged

Aleksei Sviridkin (lexfrei) merged 4 commits into
masterfrom
fix/wait-for-generated-outputs

Conversation

@lexfrei

Copy link
Copy Markdown
Collaborator

Summary

Bucket credentials, cluster kubeconfig, postgres endpoints and VM addresses live in Secrets, Services and KubeVirt status that controllers create after the object itself. The provider read them once, right after the write, so they landed in state as null and only appeared on the next refresh. A bucket's S3 credentials were readable one apply late.

With wait_for_ready set, the apply now re-reads them until they show up, inside the same wait_timeout budget as the readiness wait. Default behaviour is unchanged.

Second fix in the same path: when the readiness wait timed out, the created object never reached state, so the next apply got AlreadyExists and someone had to import or delete it by hand.

Changes

  • outputsReader got outputsPending, so a model says when its outputs are still missing, and create/update poll until they appear or the deadline passes. Outputs that never appear leave a warning instead of failing an apply whose object exists. A couple of failing reads in a row are retried, the cluster is converging at that point.
  • A readiness timeout now writes the object into state together with the error, and wait_timeout is parsed before the write, so a malformed duration no longer creates anything.
  • Schema descriptions name the Secrets and Services the charts actually create: bucket-<name>-<user>, kubernetes-<name>-admin-kubeconfig, postgres-<name>-rw/-ro. The shared wait attributes moved out of the tenant resource file, which is why every resource's docs said "until the tenant's Ready condition".
  • golangci-lint 2.13 renamed exhaustruct and wsl to exhaustruct_v5 and wsl_v5 and kept the old names as deprecated aliases. With default: all both spellings are on, so both have to be listed in disable. Without that the lint job fails on any PR.

Testing

  • Unit tests pass locally (make test)
  • Linters pass locally (make lint)
  • Generated docs are up to date (make docs produces no diff)
  • Acceptance tests pass against a live cluster (make testacc), if applicable

No cluster at hand for make testacc. The new paths are covered by unit tests against a fake dynamic client, and I checked each one by breaking the code it guards and watching it fail.

Documentation

  • Examples added or updated under examples/ (bucket, kubernetes, postgres and vminstance show wait_for_ready = true and say why)
  • Registry docs regenerated (make docs)
  • CHANGELOG updated (v1.6.2)

Checklist

  • Commit messages follow semantic format (type(scope): description)
  • No secrets or credentials in code or state
  • Breaking changes documented (if any)

No breaking changes. With wait_for_ready unset the behaviour is the same as before.

Additional Notes

Update polls as well. A plan that changes the object marks Computed attributes with a null config value as unknown, prior state does not matter, so a grown credentials map is a legal result. Checked in terraform-plugin-framework v1.19.0, MarkComputedNilsAsUnknown.

VM addresses are waited for under Always, RerunOnFailure and Once, the strategies that start a guest themselves. A guest that exits before its address is read leaves no VirtualMachineInstance, and the wait then runs to the timeout and warns.

Left out on purpose: client.WaitForReady gives up on the first failing Get and calls every failure a timeout, and CI does not pin the golangci-lint version.

golangci-lint v2.13 renamed exhaustruct and wsl to exhaustruct_v5 and
wsl_v5 while keeping the originals as deprecated aliases. With
`default: all` both spellings are enabled, so disabling only the old
names silently turned the two linters back on and the run failed on
code that was never meant to satisfy them.

Assisted-by: LLM
Signed-off-by: Aleksei Sviridkin <f@lex.la>
Credentials, kubeconfigs, endpoints, and VM addresses live in Secrets,
Services, and KubeVirt status that controllers materialise after the
object itself exists. Create read them once, immediately after the
write, so the attributes landed in state as null and only appeared on
the next refresh: a bucket's S3 credentials were readable one apply
late.

The outputsReader contract gained outputsPending, so a model says when
its outputs are still missing, and the apply re-reads them until they
appear.
The polling shares the wait_timeout budget with the readiness wait and
runs only when wait_for_ready is set, so the default stays
non-blocking. Outputs that never materialise leave a warning instead
of failing an apply whose object was created successfully.

Update polls as well, which is what surfaces a bucket user's credentials
when the user is added to an existing bucket. A plan that changes the
object marks Computed attributes with a null config value unknown, so
outputs that grew during the apply are a legal result.

A read that keeps failing is still reported, but a single failing one is
retried: on a converging cluster an apiserver hiccup should not fail an
apply whose object already exists.

The credentials, kubeconfig, and endpoints attributes also name the
Secrets and Services the charts actually create, which carry a release
prefix, and the shared wait attributes move out of the tenant resource
file they had no business living in.

Assisted-by: LLM
Signed-off-by: Aleksei Sviridkin <f@lex.la>
A create with wait_for_ready that outlasts wait_timeout returned before
the state was written, so the object existed in Cozystack while
Terraform knew nothing about it: the next apply tried to create it again
and got AlreadyExists, leaving the practitioner to import or delete it
by hand.

The readiness wait now reports the last observation along with the
timeout error, so the object is recorded and the error is still raised.
Only a failed write reports nothing to record.

Assisted-by: LLM
Signed-off-by: Aleksei Sviridkin <f@lex.la>
@coderabbitai

coderabbitai Bot commented Sep 21, 2026

Copy link
Copy Markdown

Important

  • 🔍 Trigger review

This repository does not receive automatic reviews because it has fewer than 10 stars.

⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Advanced

Run ID: 804adf92-cc1d-4a1d-8b7b-893eb3daf3fb


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

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