fix: add UseStateForUnknown plan modifiers to prevent phantom diffs#83
fix: add UseStateForUnknown plan modifiers to prevent phantom diffs#83adamtaylor13 wants to merge 5 commits into
Conversation
|
This is my first stab at contributing to OSS mostly through AI. I know this is a hotly debated subject so I want to be very upfront—Opus 4.7 was driving, but I tested these changes on my own infrastructure after building the changes locally. I did confirm it worked as I expected it to. EDIT: The consquence of the above statement is that I don't fully understand the implications of some of these changes, such as swapping the log stream from datasource/schema to resource/schema. So please be aware—I verified the behavior appears correct to me, but I can't actually tell if it's correct based on my limited understanding of how terraform providers work under the hood. |
|
Note to self, if I want to pull up the previous convo w/ Claude: |
|
Do I need to fix the tests in CI to proceed here? |
|
@jakemalachowski I looked at the latest failing test and from what I can tell it's not related to the actual code, it appears to be related to the terraform CLI missing..? Do I need to do anything here? |
@adamtaylor13 Apologies for that, the CI tests are fixed now (#85). If you incorporate the latest you should be good to go. |
Add UseStateForUnknown() plan modifiers to computed attributes that were causing phantom diffs on every terraform plan. This fixes the issue where attributes like connection_info, num_instances, max_shutdown_delay_seconds, and others would show as changing even when no actual changes occurred. Changes: - postgres: connection_info and nested attributes - postgres: disk_size_gb - services: slug, num_instances, max_shutdown_delay_seconds, root_directory - services: previews, KeyValueConnectionInfo, RedisConnectionInfo - logstreamoverride: convert to resource/schema, add plan modifiers - notificationoverride: convert to resource/schema, add plan modifiers Fixes render-oss#59, render-oss#60, and related phantom diff issues. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
tuckerchapin
left a comment
There was a problem hiding this comment.
Thanks for opening this and your patience in getting it reviewed. It looks like when this was forked, our CI was borked. That's now fixed. If you could pull in that fix, take a look at the feedback, and confirm that the cassette tests are passing/updated accordingly, that would be great!
- there are a few places where I think we'd prefer a
Defaultover relying on unknowns from state - there's also logstreamoverride and notificationoverride where the
UseStateForUknownwill likely break the removal semantics, so we should be intentional about doing so and I don't think doing so at the top/parent level is wise
| var NumInstances = schema.Int64Attribute{ | ||
| Optional: true, | ||
| Computed: true, | ||
| Description: "Number of replicas of the service to run. Defaults to 1 on service creation and current instance count on update. If you want to manage the service's instance count outside Terraform, leave num_instances unset.", | ||
| Validators: []validator.Int64{ | ||
| int64validator.Between(1, 100), | ||
| }, | ||
| PlanModifiers: []planmodifier.Int64{ | ||
| int64planmodifier.UseStateForUnknown(), | ||
| }, | ||
| } |
There was a problem hiding this comment.
If you want to manage the service's instance count outside Terraform, leave num_instances unset.
Because of the intent in this description, I don't think this is the right move here. Or at least worth calling out. With UseStateForUnknown I believe users would have to tf state rm, rather than just "...leave num_instances_unset".
If this is the aim, then we should update the description accordingly.
There was a problem hiding this comment.
@tuckerchapin My understanding is that the biggest edge-case here is if someone has already set this value, and then wishes to remove it later, can you confirm? In that case, would be it appropriate to simply update the description to explain that "handing control back to Render" involves removing the local state entry for this field? I shipped an update to the description in 1198b46. Let me know if that addresses the concern here.
EDIT: Oops... I think that commit included some other changes. Standby...
EDIT 2: Commit reference should be correct now.
Drop parent-level UseStateForUnknown on log_stream_override. With it, removing the block from config preserved the prior value in state, so the provider issued a PUT instead of clearing the override — breaking both the documented behavior and the cassette tests for background workers, web services, cron jobs, redis, keyvalue, postgres, and private services. The inner endpoint UseStateForUnknown is kept so users who set log_stream_override without specifying endpoint do not see a phantom diff on subsequent plans. Addresses inline review comment on logstreamoverride.go:43. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
With UseStateForUnknown, removing root_directory from config left the prior value in the plan but the API returned an empty string, producing "Provider produced inconsistent result after apply" for the static_site resource. root_directory is Optional+Computed; dropping the modifier returns to the pre-PR behavior where plan transitions to unknown when removed from config, matching what the API actually does. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
UseStateForUnknown on num_instances means simply unsetting the field in config no longer hands control back to Render — the last-known value stays in state. Update the description (and regenerated docs) to spell out that users must `terraform state rm` the attribute to release it back to Render's management. Addresses inline review comment on services.go:187. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The Render API rejects PATCH requests that include max_shutdown_delay_seconds for services with a disk attached (see render community thread #36752), but the same API still defaults the value to 30 on creation for those services. With UseStateForUnknown, the provider preserves that 30 in plan and re-sends it on every PATCH — which the API then rejects on any update to a disk-backed service. Drop the modifier and accept the phantom diff. The cleaner fix (detect disk-backed services in the request builder, or add a plan-time validator rejecting both fields together) is tracked separately and out of scope for this PR. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Summary
Adds
UseStateForUnknown()plan modifiers to computed attributes that were causing phantom diffs on everyterraform plan.The Problem
Before this fix, every
terraform planwould show changes like this, even when nothing actually changed:The root cause:
connection_infoon postgres resources lacked plan modifiers, so Terraform marked it as unknown on every plan. Any resource interpolatingconnection_info.internal_connection_stringinto an env var would inherit this "unknown" status.After This Fix
Changes
postgres/resource/schema.go - Add plan modifiers to
connection_info:types/resource/services.go - Add plan modifiers to service attributes:
types/resource/logstreamoverride.go and notificationoverride.go - Convert from
datasource/schematoresource/schemato support plan modifiers:Testing
go install~/.terraformrcwith dev_overridesterraform planagainst existing infrastructure with postgres + web services + background workersRelated Issues
num_instancesanddigestcause plan noise #60 (num_instances and digest plan noise)max_shutdown_delay_secondscauses plan noise #59 (max_shutdown_delay_seconds plan noise) is not resolved here. As far as I can tell, both schema-level fixes (UseStateForUnknownandDefault) break disk-backed services. The Render API rejects this field for services with an attached disk (disks disable zero-downtime deploys, so graceful shutdown doesn't apply). A proper fix needs request-builder logic to skip the field for disk-backed services plus a plan-time validator. I have tracked this as a follow-up PR.