From 7eb671f855c3914148beb4fe39a969f2a37d43cc Mon Sep 17 00:00:00 2001 From: Sarah French Date: Mon, 17 Nov 2025 18:21:46 +0000 Subject: [PATCH 1/2] test: Add tests for parsing logs from init commands --- logging_test.go | 121 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 121 insertions(+) diff --git a/logging_test.go b/logging_test.go index e6a1119..c7310d9 100644 --- a/logging_test.go +++ b/logging_test.go @@ -165,3 +165,124 @@ func TestLogging_query(t *testing.T) { } } } + +// Includes a typical sequence of logs that happen when initializing a working directory +// +// Currently `init` creates some logs with "type":"log" and others with "type":"init_output" +// Type "init_output" logs include a specific field called "message_code" that takes a string value. +func TestLogging_init(t *testing.T) { + testCases := []struct { + rawMessage string + expectedMessage LogMsg + }{ + { + `{"@level":"info","@message":"Terraform 1.15.0-dev","@module":"terraform.ui","@timestamp":"2025-11-17T17:17:58.540604Z","terraform":"1.15.0-dev","type":"version","ui":"1.2"}`, + VersionLogMessage{ + baseLogMessage: baseLogMessage{ + Lvl: Info, + Msg: "Terraform 1.15.0-dev", + Time: time.Date(2025, 11, 17, 17, 17, 58, 540604000, time.UTC), + }, + Terraform: version.Must(version.NewSemver("1.15.0-dev")), + UI: version.Must(version.NewSemver("1.2.0")), + }, + }, + { + `{"@level":"info","@message":"Initializing provider plugins found in the configuration...","@module":"terraform.ui","@timestamp":"2025-11-17T17:18:04.314Z","message_code":"initializing_provider_plugin_from_config_message","type":"init_output"}`, + UnknownLogMessage{ + baseLogMessage: baseLogMessage{ + Lvl: Info, + Msg: "Initializing provider plugins found in the configuration...", + Time: time.Date(2025, 11, 17, 17, 18, 04, 314000000, time.UTC), + }, + }, + }, + { + `{"@level":"info","@message":"hashicorp/aws: Finding latest version...","@module":"terraform.ui","@timestamp":"2025-11-17T17:18:04.314594Z","type":"log"}`, + LogMessage{ + baseLogMessage: baseLogMessage{ + Lvl: Info, + Msg: "hashicorp/aws: Finding latest version...", + Time: time.Date(2025, 11, 17, 17, 18, 04, 314594000, time.UTC), + }, + }, + }, + { + `{"@level":"info","@message":"Installing provider version: hashicorp/aws v6.21.0...","@module":"terraform.ui","@timestamp":"2025-11-17T17:18:04.784659Z","type":"log"}`, + LogMessage{ + baseLogMessage: baseLogMessage{ + Lvl: Info, + Msg: "Installing provider version: hashicorp/aws v6.21.0...", + Time: time.Date(2025, 11, 17, 17, 18, 04, 784659000, time.UTC), + }, + }, + }, + { + `{"@level":"info","@message":"Installed provider version: hashicorp/aws v6.21.0 (signed by HashiCorp)","@module":"terraform.ui","@timestamp":"2025-11-17T17:18:26.345919Z","type":"log"}`, + LogMessage{ + baseLogMessage: baseLogMessage{ + Lvl: Info, + Msg: "Installed provider version: hashicorp/aws v6.21.0 (signed by HashiCorp)", + Time: time.Date(2025, 11, 17, 17, 18, 26, 345919000, time.UTC), + }, + }, + }, + { + `{"@level":"info","@message":"Initializing the backend...","@module":"terraform.ui","@timestamp":"2025-11-17T17:18:52.256Z","message_code":"initializing_backend_message","type":"init_output"}`, + UnknownLogMessage{ + baseLogMessage: baseLogMessage{ + Lvl: Info, + Msg: "Initializing the backend...", + Time: time.Date(2025, 11, 17, 17, 18, 52, 256000000, time.UTC), + }, + }, + }, + // At this point in an init command's output there is a log message that isn't presented in JSON format: + // /* + // Successfully configured the backend "local"! Terraform will automatically + // use this backend unless the backend configuration changes. + // */ + // + // See this GitHub issue: https://github.com/hashicorp/terraform/issues/37911 + { + `{"@level":"info","@message":"Terraform has created a lock file .terraform.lock.hcl to record the provider\nselections it made above. Include this file in your version control repository\nso that Terraform can guarantee to make the same selections by default when\nyou run \"terraform init\" in the future.","@module":"terraform.ui","@timestamp":"2025-11-17T17:19:06.698Z","message_code":"lock_info","type":"init_output"}`, + UnknownLogMessage{ + baseLogMessage: baseLogMessage{ + Lvl: Info, + Msg: "Terraform has created a lock file .terraform.lock.hcl to record the provider\nselections it made above. Include this file in your version control repository\nso that Terraform can guarantee to make the same selections by default when\nyou run \"terraform init\" in the future.", + Time: time.Date(2025, 11, 17, 17, 19, 06, 698000000, time.UTC), + }, + }, + }, + { + `{"@level":"info","@message":"Terraform has been successfully initialized!","@module":"terraform.ui","@timestamp":"2025-11-17T17:19:09.915Z","message_code":"output_init_success_message","type":"init_output"}`, + UnknownLogMessage{ + baseLogMessage: baseLogMessage{ + Lvl: Info, + Msg: "Terraform has been successfully initialized!", + Time: time.Date(2025, 11, 17, 17, 19, 9, 915000000, time.UTC), + }, + }, + }, + { + `{"@level":"info","@message":"You may now begin working with Terraform. Try running \"terraform plan\" to see\nany changes that are required for your infrastructure. All Terraform commands\nshould now work.\n\nIf you ever set or change modules or backend configuration for Terraform,\nrerun this command to reinitialize your working directory. If you forget, other\ncommands will detect it and remind you to do so if necessary.","@module":"terraform.ui","@timestamp":"2025-11-17T17:19:10.553Z","message_code":"output_init_success_cli_message","type":"init_output"}`, + UnknownLogMessage{ + baseLogMessage: baseLogMessage{ + Lvl: Info, + Msg: "You may now begin working with Terraform. Try running \"terraform plan\" to see\nany changes that are required for your infrastructure. All Terraform commands\nshould now work.\n\nIf you ever set or change modules or backend configuration for Terraform,\nrerun this command to reinitialize your working directory. If you forget, other\ncommands will detect it and remind you to do so if necessary.", + Time: time.Date(2025, 11, 17, 17, 19, 10, 553000000, time.UTC), + }, + }, + }, + } + + for _, tc := range testCases { + msg, err := UnmarshalLogMessage([]byte(tc.rawMessage)) + if err != nil { + t.Fatal(err) + } + if diff := cmp.Diff(tc.expectedMessage, msg, cmpOpts); diff != "" { + t.Fatalf("unexpected message: %s", diff) + } + } +} From 521cfb3a2472d02cc8f1a08d5642a3670ab11c82 Mon Sep 17 00:00:00 2001 From: Sarah French Date: Mon, 17 Nov 2025 18:27:51 +0000 Subject: [PATCH 2/2] feat: Add support for `init_output` type log messages created during `init` commands --- logging_init.go | 13 +++++++++++++ logging_test.go | 15 ++++++++++----- logging_types.go | 8 ++++++++ 3 files changed, 31 insertions(+), 5 deletions(-) create mode 100644 logging_init.go diff --git a/logging_init.go b/logging_init.go new file mode 100644 index 0000000..c0d6508 --- /dev/null +++ b/logging_init.go @@ -0,0 +1,13 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 +package tfjson + +const ( + InitOutput LogMessageType = "init_output" +) + +// InitOutputMessage represents messages of type "init_output" +type InitOutputMessage struct { + baseLogMessage + MessageCode string `json:"message_code"` +} diff --git a/logging_test.go b/logging_test.go index c7310d9..db7ac1c 100644 --- a/logging_test.go +++ b/logging_test.go @@ -189,12 +189,13 @@ func TestLogging_init(t *testing.T) { }, { `{"@level":"info","@message":"Initializing provider plugins found in the configuration...","@module":"terraform.ui","@timestamp":"2025-11-17T17:18:04.314Z","message_code":"initializing_provider_plugin_from_config_message","type":"init_output"}`, - UnknownLogMessage{ + InitOutputMessage{ baseLogMessage: baseLogMessage{ Lvl: Info, Msg: "Initializing provider plugins found in the configuration...", Time: time.Date(2025, 11, 17, 17, 18, 04, 314000000, time.UTC), }, + MessageCode: "initializing_provider_plugin_from_config_message", }, }, { @@ -229,12 +230,13 @@ func TestLogging_init(t *testing.T) { }, { `{"@level":"info","@message":"Initializing the backend...","@module":"terraform.ui","@timestamp":"2025-11-17T17:18:52.256Z","message_code":"initializing_backend_message","type":"init_output"}`, - UnknownLogMessage{ + InitOutputMessage{ baseLogMessage: baseLogMessage{ Lvl: Info, Msg: "Initializing the backend...", Time: time.Date(2025, 11, 17, 17, 18, 52, 256000000, time.UTC), }, + MessageCode: "initializing_backend_message", }, }, // At this point in an init command's output there is a log message that isn't presented in JSON format: @@ -246,32 +248,35 @@ func TestLogging_init(t *testing.T) { // See this GitHub issue: https://github.com/hashicorp/terraform/issues/37911 { `{"@level":"info","@message":"Terraform has created a lock file .terraform.lock.hcl to record the provider\nselections it made above. Include this file in your version control repository\nso that Terraform can guarantee to make the same selections by default when\nyou run \"terraform init\" in the future.","@module":"terraform.ui","@timestamp":"2025-11-17T17:19:06.698Z","message_code":"lock_info","type":"init_output"}`, - UnknownLogMessage{ + InitOutputMessage{ baseLogMessage: baseLogMessage{ Lvl: Info, Msg: "Terraform has created a lock file .terraform.lock.hcl to record the provider\nselections it made above. Include this file in your version control repository\nso that Terraform can guarantee to make the same selections by default when\nyou run \"terraform init\" in the future.", Time: time.Date(2025, 11, 17, 17, 19, 06, 698000000, time.UTC), }, + MessageCode: "lock_info", }, }, { `{"@level":"info","@message":"Terraform has been successfully initialized!","@module":"terraform.ui","@timestamp":"2025-11-17T17:19:09.915Z","message_code":"output_init_success_message","type":"init_output"}`, - UnknownLogMessage{ + InitOutputMessage{ baseLogMessage: baseLogMessage{ Lvl: Info, Msg: "Terraform has been successfully initialized!", Time: time.Date(2025, 11, 17, 17, 19, 9, 915000000, time.UTC), }, + MessageCode: "output_init_success_message", }, }, { `{"@level":"info","@message":"You may now begin working with Terraform. Try running \"terraform plan\" to see\nany changes that are required for your infrastructure. All Terraform commands\nshould now work.\n\nIf you ever set or change modules or backend configuration for Terraform,\nrerun this command to reinitialize your working directory. If you forget, other\ncommands will detect it and remind you to do so if necessary.","@module":"terraform.ui","@timestamp":"2025-11-17T17:19:10.553Z","message_code":"output_init_success_cli_message","type":"init_output"}`, - UnknownLogMessage{ + InitOutputMessage{ baseLogMessage: baseLogMessage{ Lvl: Info, Msg: "You may now begin working with Terraform. Try running \"terraform plan\" to see\nany changes that are required for your infrastructure. All Terraform commands\nshould now work.\n\nIf you ever set or change modules or backend configuration for Terraform,\nrerun this command to reinitialize your working directory. If you forget, other\ncommands will detect it and remind you to do so if necessary.", Time: time.Date(2025, 11, 17, 17, 19, 10, 553000000, time.UTC), }, + MessageCode: "output_init_success_cli_message", }, }, } diff --git a/logging_types.go b/logging_types.go index 3e712a0..95fdd1f 100644 --- a/logging_types.go +++ b/logging_types.go @@ -22,6 +22,9 @@ var allLogMessageTypes = []any{ DiagnosticLogMessage{}, UnknownLogMessage{}, + // init + InitOutputMessage{}, + // query ListStartMessage{}, ListResourceFoundMessage{}, @@ -42,6 +45,11 @@ func unmarshalByType(t LogMessageType, b []byte) (LogMsg, error) { v := DiagnosticLogMessage{} return v, json.Unmarshal(b, &v) + // init + case InitOutput: + v := InitOutputMessage{} + return v, json.Unmarshal(b, &v) + // query case MessageListStart: v := ListStartMessage{}