[CNSL-2431] remove plan noise from metadata-only cluster updates - #362
[CNSL-2431] remove plan noise from metadata-only cluster updates#362devlaneyr wants to merge 1 commit into
Conversation
Terraform marks every Computed attribute the config leaves unset as "known after apply", so changing a single label on a cockroach_cluster planned a dozen unrelated attributes as pending changes. A prior commit e66893f added UseStateForUnknown to some of them. That modifier copies prior state into the plan unconditionally, so it only fits attributes that never change on an update. The rest do change on some updates, and stayed noisy on all of them. Whether those attributes can change depends on what else the update is doing, which a per-attribute modifier can't see. This adds a resource-level check over the whole plan: when every difference between plan and state is confined to metadata that doesn't start a cluster operation, the remaining unknowns are filled in from prior state. Otherwise the plan is left alone. Only unknowns are ever replaced, so a plan can get quieter but never hide a change.
|
I reproduced by using the main.tf file: which I performed terraform apply on. I then modified the labels to:
which on main still yields noisy output from terraform plan as follows (I've redacted potentially sensitive fields): After the updates in this commit the same terraform plan yields: Nothing is marked known after apply anymore, so the plan shows only the label change. |
| "labels": {}, | ||
| "delete_protection": {}, | ||
| "parent_id": {}, | ||
| "backup_config": {}, |
There was a problem hiding this comment.
There are some weird rules governing backup_config. It can only be updated once without a support ticket. See here: https://github.com/cockroachdb/terraform-provider-cockroach/blob/3440b333e20a638ac142fe5a2dc1ee73040c2229/docs/guides/updating-backup-retention.md
I'm wondering if this might cause a problem if you're doing something like using state for unknown and expecting the update to work. It's worth testing at least.
| // Only resolve unknowns for a cluster in the CREATED state. Anything else | ||
| // isn't settled, and the apply would return different values than the plan | ||
| // promised, failing with "inconsistent result after apply". |
There was a problem hiding this comment.
I'm slightly surprised by this. Here are the states:
message ClusterState {
enum Type {
UNSPECIFIED = 0;
CREATING = 1;
CREATED = 2;
CREATION_FAILED = 3;
DELETED = 4;
// An exclusive operation is being performed on this cluster.
// Other operations should not proceed if they did not set a cluster into the LOCKED state.
LOCKED = 5;
}
It seems plausible we wouldn't allow updates while in CREATING, CREATION_FAILED or DELETED. I'm unclear about LOCKED. I think we might still allow certain metadata updates in which case this would be fine. This might be worth digging into a bit more.
| if opStatus := state.OperationStatus.ValueString(); IsKnown(state.OperationStatus) && | ||
| opStatus != "" && opStatus != string(client.CLUSTERSTATUSTYPE_UNSPECIFIED) { | ||
| return | ||
| } |
There was a problem hiding this comment.
Here again, it seems like we're not allowing updates during long running operations but I think that might actually be fine? Although sometimes terraform might be polling for a long running operation to finish such as during an update.
| // Can't prove the update is inert, so leave the plan alone. | ||
| tflog.Trace(ctx, "skipping metadata-only unknown resolution", map[string]any{"error": err.Error()}) |
There was a problem hiding this comment.
I agree with the Trace level logging here but it's probably worth a comment about why we would log this error at trace rather than error.
| } | ||
| } | ||
|
|
||
| resolved, err := tftypes.Transform(planRaw, func( |
There was a problem hiding this comment.
It's probably worth a comment above here about what this is doing. It's pretty incoherent (at least to me)
There was a problem hiding this comment.
It also might be worth trying to tuck some of this logic that's not specific to the cluster_resource into a separate file or package to keep it out of this file. the cluster_resource file is already large.
| if !configIsNullAt(configRaw, attrPath) { | ||
| return val, nil | ||
| } | ||
| stateValI, _, err := tftypes.WalkAttributePath(stateRaw, attrPath) |
There was a problem hiding this comment.
what does this var name mean? Why the "I" at the end? Normally i would expect that to be short for "index" but I see we're casting it to the state value below.
| `delete_protection`, `dedicated.memory_gib`, `dedicated.disk_iops`, and the | ||
| `regions` block's `ui_dns`, `private_endpoint_dns`, and `s3_vpc_endpoint_id` are | ||
| no longer reported as "known after apply" on updates that don't change them. | ||
| - A `cockroach_cluster` update that changes only `labels`, `delete_protection`, |
There was a problem hiding this comment.
Changelog entries should be placed on top like a stack.
Terraform marks every Computed attribute the config leaves unset as "known after apply", so changing a single label on a cockroach_cluster planned a dozen unrelated attributes as pending changes.
A prior commit e66893f added UseStateForUnknown to some of them. That modifier copies prior state into the plan unconditionally, so it only fits attributes that never change on an update. The rest do change on some updates, and stayed noisy on all of them.
Whether those attributes can change depends on what else the update is doing, which a per-attribute modifier can't see. This adds a resource-level check over the whole plan: when every difference between plan and state is confined to metadata that doesn't start a cluster operation, the remaining unknowns are filled in from prior state. Otherwise the plan is left alone. Only unknowns are ever replaced, so a plan can get quieter but never hide a change.
Commit checklist
make generate)