From 19291996eddbb3e4e8aa9a4ed5e69f417bfae74f Mon Sep 17 00:00:00 2001 From: Danny Kopping Date: Wed, 21 Aug 2024 23:13:32 +0200 Subject: [PATCH] Include action in state hash Signed-off-by: Danny Kopping --- .../multiple-resource-actions.txtar | 46 +++++++++++++++++++ provisioner/terraform/timings.go | 2 +- .../terraform/timings_internal_test.go | 16 +++++-- 3 files changed, 59 insertions(+), 5 deletions(-) create mode 100644 provisioner/terraform/testdata/timings-aggregation/multiple-resource-actions.txtar diff --git a/provisioner/terraform/testdata/timings-aggregation/multiple-resource-actions.txtar b/provisioner/terraform/testdata/timings-aggregation/multiple-resource-actions.txtar new file mode 100644 index 0000000000000..e3ace3f27ff15 --- /dev/null +++ b/provisioner/terraform/testdata/timings-aggregation/multiple-resource-actions.txtar @@ -0,0 +1,46 @@ +A resource can transition through multiple states through actions like 'delete' and 'create'. +Previously we were not including the action in the 'hashByState' function, leading to missed timings. +See 'docker_container.workspace[0]' below. + +-- apply -- +{"@level":"info","@message":"Terraform 1.9.2","@module":"terraform.ui","@timestamp":"2024-08-21T22:59:39.724076+02:00","terraform":"1.9.2","type":"version","ui":"1.2"} +{"@level":"info","@message":"data.coder_parameter.memory_size: Refreshing...","@module":"terraform.ui","@timestamp":"2024-08-21T22:59:40.003696+02:00","hook":{"resource":{"addr":"data.coder_parameter.memory_size","module":"","resource":"data.coder_parameter.memory_size","implied_provider":"coder","resource_type":"coder_parameter","resource_name":"memory_size","resource_key":null},"action":"read"},"type":"apply_start"} +{"@level":"info","@message":"data.coder_workspace.me: Refreshing...","@module":"terraform.ui","@timestamp":"2024-08-21T22:59:40.003703+02:00","hook":{"resource":{"addr":"data.coder_workspace.me","module":"","resource":"data.coder_workspace.me","implied_provider":"coder","resource_type":"coder_workspace","resource_name":"me","resource_key":null},"action":"read"},"type":"apply_start"} +{"@level":"info","@message":"data.coder_provisioner.me: Refreshing...","@module":"terraform.ui","@timestamp":"2024-08-21T22:59:40.003711+02:00","hook":{"resource":{"addr":"data.coder_provisioner.me","module":"","resource":"data.coder_provisioner.me","implied_provider":"coder","resource_type":"coder_provisioner","resource_name":"me","resource_key":null},"action":"read"},"type":"apply_start"} +{"@level":"info","@message":"data.http.latest_commit: Refreshing...","@module":"terraform.ui","@timestamp":"2024-08-21T22:59:40.003786+02:00","hook":{"resource":{"addr":"data.http.latest_commit","module":"","resource":"data.http.latest_commit","implied_provider":"http","resource_type":"http","resource_name":"latest_commit","resource_key":null},"action":"read"},"type":"apply_start"} +{"@level":"info","@message":"data.coder_provisioner.me: Refresh complete after 0s [id=6c107654-0d6d-400f-bd54-5dd3eb7c0ecd]","@module":"terraform.ui","@timestamp":"2024-08-21T22:59:40.004366+02:00","hook":{"resource":{"addr":"data.coder_provisioner.me","module":"","resource":"data.coder_provisioner.me","implied_provider":"coder","resource_type":"coder_provisioner","resource_name":"me","resource_key":null},"action":"read","id_key":"id","id_value":"6c107654-0d6d-400f-bd54-5dd3eb7c0ecd","elapsed_seconds":0},"type":"apply_complete"} +{"@level":"info","@message":"data.coder_workspace.me: Refresh complete after 0s [id=5509156c-f08e-4524-8eb5-51ff595226fb]","@module":"terraform.ui","@timestamp":"2024-08-21T22:59:40.004689+02:00","hook":{"resource":{"addr":"data.coder_workspace.me","module":"","resource":"data.coder_workspace.me","implied_provider":"coder","resource_type":"coder_workspace","resource_name":"me","resource_key":null},"action":"read","id_key":"id","id_value":"5509156c-f08e-4524-8eb5-51ff595226fb","elapsed_seconds":0},"type":"apply_complete"} +{"@level":"info","@message":"data.coder_parameter.memory_size: Refresh complete after 0s [id=1be91971-33dd-4eb8-a1a3-0ba3a38a8dde]","@module":"terraform.ui","@timestamp":"2024-08-21T22:59:40.004938+02:00","hook":{"resource":{"addr":"data.coder_parameter.memory_size","module":"","resource":"data.coder_parameter.memory_size","implied_provider":"coder","resource_type":"coder_parameter","resource_name":"memory_size","resource_key":null},"action":"read","id_key":"id","id_value":"1be91971-33dd-4eb8-a1a3-0ba3a38a8dde","elapsed_seconds":0},"type":"apply_complete"} +{"@level":"info","@message":"coder_agent.main: Refreshing state... [id=9a62f453-6303-4d10-99d4-9001f73683c2]","@module":"terraform.ui","@timestamp":"2024-08-21T22:59:40.007139+02:00","hook":{"resource":{"addr":"coder_agent.main","module":"","resource":"coder_agent.main","implied_provider":"coder","resource_type":"coder_agent","resource_name":"main","resource_key":null},"id_key":"id","id_value":"9a62f453-6303-4d10-99d4-9001f73683c2"},"type":"refresh_start"} +{"@level":"info","@message":"docker_image.main: Refreshing state... [id=sha256:443d199e8bfcce69c2aa494b36b5f8b04c3b183277cd19190e9589fd8552d618nginx:latest]","@module":"terraform.ui","@timestamp":"2024-08-21T22:59:40.008559+02:00","hook":{"resource":{"addr":"docker_image.main","module":"","resource":"docker_image.main","implied_provider":"docker","resource_type":"docker_image","resource_name":"main","resource_key":null},"id_key":"id","id_value":"sha256:443d199e8bfcce69c2aa494b36b5f8b04c3b183277cd19190e9589fd8552d618nginx:latest"},"type":"refresh_start"} +{"@level":"info","@message":"coder_agent.main: Refresh complete [id=9a62f453-6303-4d10-99d4-9001f73683c2]","@module":"terraform.ui","@timestamp":"2024-08-21T22:59:40.011774+02:00","hook":{"resource":{"addr":"coder_agent.main","module":"","resource":"coder_agent.main","implied_provider":"coder","resource_type":"coder_agent","resource_name":"main","resource_key":null},"id_key":"id","id_value":"9a62f453-6303-4d10-99d4-9001f73683c2"},"type":"refresh_complete"} +{"@level":"info","@message":"docker_volume.home_volume: Refreshing state... [id=coder-57e02f44-3b83-4f24-ac6f-65376cc5ab8e-home]","@module":"terraform.ui","@timestamp":"2024-08-21T22:59:40.011801+02:00","hook":{"resource":{"addr":"docker_volume.home_volume","module":"","resource":"docker_volume.home_volume","implied_provider":"docker","resource_type":"docker_volume","resource_name":"home_volume","resource_key":null},"id_key":"id","id_value":"coder-57e02f44-3b83-4f24-ac6f-65376cc5ab8e-home"},"type":"refresh_start"} +{"@level":"info","@message":"coder_script.startup_script: Refreshing state... [id=46d825ef-dd7e-47b6-a8e0-cda5d7695e0e]","@module":"terraform.ui","@timestamp":"2024-08-21T22:59:40.015683+02:00","hook":{"resource":{"addr":"coder_script.startup_script","module":"","resource":"coder_script.startup_script","implied_provider":"coder","resource_type":"coder_script","resource_name":"startup_script","resource_key":null},"id_key":"id","id_value":"46d825ef-dd7e-47b6-a8e0-cda5d7695e0e"},"type":"refresh_start"} +{"@level":"info","@message":"coder_script.startup_script: Refresh complete [id=46d825ef-dd7e-47b6-a8e0-cda5d7695e0e]","@module":"terraform.ui","@timestamp":"2024-08-21T22:59:40.016027+02:00","hook":{"resource":{"addr":"coder_script.startup_script","module":"","resource":"coder_script.startup_script","implied_provider":"coder","resource_type":"coder_script","resource_name":"startup_script","resource_key":null},"id_key":"id","id_value":"46d825ef-dd7e-47b6-a8e0-cda5d7695e0e"},"type":"refresh_complete"} +{"@level":"info","@message":"docker_volume.home_volume: Refresh complete [id=coder-57e02f44-3b83-4f24-ac6f-65376cc5ab8e-home]","@module":"terraform.ui","@timestamp":"2024-08-21T22:59:40.017694+02:00","hook":{"resource":{"addr":"docker_volume.home_volume","module":"","resource":"docker_volume.home_volume","implied_provider":"docker","resource_type":"docker_volume","resource_name":"home_volume","resource_key":null},"id_key":"id","id_value":"coder-57e02f44-3b83-4f24-ac6f-65376cc5ab8e-home"},"type":"refresh_complete"} +{"@level":"info","@message":"docker_image.main: Refresh complete [id=sha256:443d199e8bfcce69c2aa494b36b5f8b04c3b183277cd19190e9589fd8552d618nginx:latest]","@module":"terraform.ui","@timestamp":"2024-08-21T22:59:40.025098+02:00","hook":{"resource":{"addr":"docker_image.main","module":"","resource":"docker_image.main","implied_provider":"docker","resource_type":"docker_image","resource_name":"main","resource_key":null},"id_key":"id","id_value":"sha256:443d199e8bfcce69c2aa494b36b5f8b04c3b183277cd19190e9589fd8552d618nginx:latest"},"type":"refresh_complete"} +{"@level":"info","@message":"docker_container.workspace[0]: Refreshing state... [id=6124169bfea9b13f34ee9e730c8772e950898136cd5565f5b3343a7849573050]","@module":"terraform.ui","@timestamp":"2024-08-21T22:59:40.027874+02:00","hook":{"resource":{"addr":"docker_container.workspace[0]","module":"","resource":"docker_container.workspace[0]","implied_provider":"docker","resource_type":"docker_container","resource_name":"workspace","resource_key":0},"id_key":"id","id_value":"6124169bfea9b13f34ee9e730c8772e950898136cd5565f5b3343a7849573050"},"type":"refresh_start"} +{"@level":"info","@message":"data.http.latest_commit: Refresh complete after 0s [id=https://api.github.com/repos/coder/coder/commits/main]","@module":"terraform.ui","@timestamp":"2024-08-21T22:59:40.107958+02:00","hook":{"resource":{"addr":"data.http.latest_commit","module":"","resource":"data.http.latest_commit","implied_provider":"http","resource_type":"http","resource_name":"latest_commit","resource_key":null},"action":"read","id_key":"id","id_value":"https://api.github.com/repos/coder/coder/commits/main","elapsed_seconds":0},"type":"apply_complete"} +{"@level":"info","@message":"docker_container.workspace[0]: Refresh complete [id=6124169bfea9b13f34ee9e730c8772e950898136cd5565f5b3343a7849573050]","@module":"terraform.ui","@timestamp":"2024-08-21T22:59:40.137794+02:00","hook":{"resource":{"addr":"docker_container.workspace[0]","module":"","resource":"docker_container.workspace[0]","implied_provider":"docker","resource_type":"docker_container","resource_name":"workspace","resource_key":0},"id_key":"id","id_value":"6124169bfea9b13f34ee9e730c8772e950898136cd5565f5b3343a7849573050"},"type":"refresh_complete"} +{"@level":"info","@message":"docker_container.workspace[0]: Drift detected (update)","@module":"terraform.ui","@timestamp":"2024-08-21T22:59:40.151984+02:00","change":{"resource":{"addr":"docker_container.workspace[0]","module":"","resource":"docker_container.workspace[0]","implied_provider":"docker","resource_type":"docker_container","resource_name":"workspace","resource_key":0},"action":"update"},"type":"resource_drift"} +{"@level":"info","@message":"coder_agent.main: Drift detected (update)","@module":"terraform.ui","@timestamp":"2024-08-21T22:59:40.152016+02:00","change":{"resource":{"addr":"coder_agent.main","module":"","resource":"coder_agent.main","implied_provider":"coder","resource_type":"coder_agent","resource_name":"main","resource_key":null},"action":"update"},"type":"resource_drift"} +{"@level":"info","@message":"docker_container.workspace[0]: Plan to replace","@module":"terraform.ui","@timestamp":"2024-08-21T22:59:40.152023+02:00","change":{"resource":{"addr":"docker_container.workspace[0]","module":"","resource":"docker_container.workspace[0]","implied_provider":"docker","resource_type":"docker_container","resource_name":"workspace","resource_key":0},"action":"replace","reason":"cannot_update"},"type":"planned_change"} +{"@level":"info","@message":"Plan: 1 to add, 0 to change, 1 to destroy.","@module":"terraform.ui","@timestamp":"2024-08-21T22:59:40.152028+02:00","changes":{"add":1,"change":0,"import":0,"remove":1,"operation":"plan"},"type":"change_summary"} +{"@level":"info","@message":"docker_container.workspace[0]: Destroying... [id=6124169bfea9b13f34ee9e730c8772e950898136cd5565f5b3343a7849573050]","@module":"terraform.ui","@timestamp":"2024-08-21T22:59:40.204215+02:00","hook":{"resource":{"addr":"docker_container.workspace[0]","module":"","resource":"docker_container.workspace[0]","implied_provider":"docker","resource_type":"docker_container","resource_name":"workspace","resource_key":0},"action":"delete","id_key":"id","id_value":"6124169bfea9b13f34ee9e730c8772e950898136cd5565f5b3343a7849573050"},"type":"apply_start"} +{"@level":"info","@message":"docker_container.workspace[0]: Destruction complete after 0s","@module":"terraform.ui","@timestamp":"2024-08-21T22:59:40.250903+02:00","hook":{"resource":{"addr":"docker_container.workspace[0]","module":"","resource":"docker_container.workspace[0]","implied_provider":"docker","resource_type":"docker_container","resource_name":"workspace","resource_key":0},"action":"delete","elapsed_seconds":0},"type":"apply_complete"} +{"@level":"info","@message":"docker_container.workspace[0]: Creating...","@module":"terraform.ui","@timestamp":"2024-08-21T22:59:40.264384+02:00","hook":{"resource":{"addr":"docker_container.workspace[0]","module":"","resource":"docker_container.workspace[0]","implied_provider":"docker","resource_type":"docker_container","resource_name":"workspace","resource_key":0},"action":"create"},"type":"apply_start"} +{"@level":"info","@message":"docker_container.workspace[0]: Creation complete after 1s [id=4c8842d427970f6ce34da73085b61deaa72bdaf14d0dc56972f5eaa93c86a2f0]","@module":"terraform.ui","@timestamp":"2024-08-21T22:59:40.577054+02:00","hook":{"resource":{"addr":"docker_container.workspace[0]","module":"","resource":"docker_container.workspace[0]","implied_provider":"docker","resource_type":"docker_container","resource_name":"workspace","resource_key":0},"action":"create","id_key":"id","id_value":"4c8842d427970f6ce34da73085b61deaa72bdaf14d0dc56972f5eaa93c86a2f0","elapsed_seconds":1},"type":"apply_complete"} +{"@level":"info","@message":"Apply complete! Resources: 1 added, 0 changed, 1 destroyed.","@module":"terraform.ui","@timestamp":"2024-08-21T22:59:40.612265+02:00","changes":{"add":1,"change":0,"import":0,"remove":1,"operation":"apply"},"type":"change_summary"} +{"@level":"info","@message":"Outputs: 0","@module":"terraform.ui","@timestamp":"2024-08-21T22:59:40.612270+02:00","outputs":{},"type":"outputs"} +-- timings -- +{"start":"2024-08-21T20:59:40.003696Z", "end":"2024-08-21T20:59:40.004938Z", "action":"read", "source":"coder", "resource":"data.coder_parameter.memory_size", "stage":"apply", "state":"COMPLETED"} +{"start":"2024-08-21T20:59:40.003703Z", "end":"2024-08-21T20:59:40.004689Z", "action":"read", "source":"coder", "resource":"data.coder_workspace.me", "stage":"apply", "state":"COMPLETED"} +{"start":"2024-08-21T20:59:40.003711Z", "end":"2024-08-21T20:59:40.004366Z", "action":"read", "source":"coder", "resource":"data.coder_provisioner.me", "stage":"apply", "state":"COMPLETED"} +{"start":"2024-08-21T20:59:40.003786Z", "end":"2024-08-21T20:59:40.107958Z", "action":"read", "source":"http", "resource":"data.http.latest_commit", "stage":"apply", "state":"COMPLETED"} +{"start":"2024-08-21T20:59:40.007139Z", "end":"2024-08-21T20:59:40.011774Z", "action":"state refresh", "source":"coder", "resource":"coder_agent.main", "stage":"apply", "state":"COMPLETED"} +{"start":"2024-08-21T20:59:40.008559Z", "end":"2024-08-21T20:59:40.025098Z", "action":"state refresh", "source":"docker", "resource":"docker_image.main", "stage":"apply", "state":"COMPLETED"} +{"start":"2024-08-21T20:59:40.011801Z", "end":"2024-08-21T20:59:40.017694Z", "action":"state refresh", "source":"docker", "resource":"docker_volume.home_volume", "stage":"apply", "state":"COMPLETED"} +{"start":"2024-08-21T20:59:40.015683Z", "end":"2024-08-21T20:59:40.016027Z", "action":"state refresh", "source":"coder", "resource":"coder_script.startup_script", "stage":"apply", "state":"COMPLETED"} +{"start":"2024-08-21T20:59:40.027874Z", "end":"2024-08-21T20:59:40.137794Z", "action":"state refresh", "source":"docker", "resource":"docker_container.workspace[0]", "stage":"apply", "state":"COMPLETED"} +{"start":"2024-08-21T20:59:40.204215Z", "end":"2024-08-21T20:59:40.250903Z", "action":"delete", "source":"docker", "resource":"docker_container.workspace[0]", "stage":"apply", "state":"COMPLETED"} +{"start":"2024-08-21T20:59:40.264384Z", "end":"2024-08-21T20:59:40.577054Z", "action":"create", "source":"docker", "resource":"docker_container.workspace[0]", "stage":"apply", "state":"COMPLETED"} \ No newline at end of file diff --git a/provisioner/terraform/timings.go b/provisioner/terraform/timings.go index 0bf6d537d4304..19be9edac12de 100644 --- a/provisioner/terraform/timings.go +++ b/provisioner/terraform/timings.go @@ -200,7 +200,7 @@ func (l timingKind) Category() string { // hashState computes a hash based on a timingSpan's unique properties and state. // The combination of resource and provider names MUST be unique across entries. func (e *timingSpan) hashByState(state proto.TimingState) uint64 { - id := fmt.Sprintf("%s:%s:%s:%s", e.kind.Category(), state.String(), e.resource, e.provider) + id := fmt.Sprintf("%s:%s:%s:%s:%s", e.kind.Category(), state.String(), e.action, e.resource, e.provider) return xxhash.Sum64String(id) } diff --git a/provisioner/terraform/timings_internal_test.go b/provisioner/terraform/timings_internal_test.go index 2dd19f7afbcf8..95dc47318e2d0 100644 --- a/provisioner/terraform/timings_internal_test.go +++ b/provisioner/terraform/timings_internal_test.go @@ -30,6 +30,8 @@ var ( inputIncomplete []byte //go:embed testdata/timings-aggregation/faster-than-light.txtar inputFasterThanLight []byte + //go:embed testdata/timings-aggregation/multiple-resource-actions.txtar + multipleResourceActions []byte ) func TestAggregation(t *testing.T) { @@ -63,6 +65,10 @@ func TestAggregation(t *testing.T) { name: "faster-than-light", input: inputFasterThanLight, }, + { + name: "multiple-resource-actions", + input: multipleResourceActions, + }, } // nolint:paralleltest // Not since go v1.22. @@ -101,7 +107,10 @@ func TestAggregation(t *testing.T) { expected := terraform_internal.ParseTimingLines(t, expectedTimings.Data) terraform_internal.StableSortTimings(t, actualTimings) // To reduce flakiness. if !assert.True(t, terraform_internal.TimingsAreEqual(t, expected, actualTimings)) { - printExpectation(t, expected) + t.Log("expected:") + printTimings(t, expected) + t.Log("actual:") + printTimings(t, actualTimings) } }) } @@ -133,11 +142,10 @@ func ingestAllSpans(t *testing.T, input []byte, aggregator *timingAggregator) { require.NoError(t, scanner.Err()) } -func printExpectation(t *testing.T, actual []*proto.Timing) { +func printTimings(t *testing.T, timings []*proto.Timing) { t.Helper() - t.Log("expected:") - for _, a := range actual { + for _, a := range timings { terraform_internal.PrintTiming(t, a) } }