logging: Add support for `state migrate` message types (#210)
diff --git a/logging_state_migrate.go b/logging_state_migrate.go new file mode 100644 index 0000000..00be0a4 --- /dev/null +++ b/logging_state_migrate.go
@@ -0,0 +1,134 @@ +// Copyright IBM Corp. 2019, 2026 +// SPDX-License-Identifier: MPL-2.0 + +package tfjson + +const ( + // Provider installation messages + ProviderInstallationStart LogMessageType = "provider_installation_start" + StateStoreProviderInstallationStart LogMessageType = "state_store_provider_installation_start" + ProviderQueryUsePreviousVersion LogMessageType = "provider_query_use_previous_version" + ProviderQueryUsePreviousConstraints LogMessageType = "provider_query_use_constraints" + ProviderQueryUseLatest LogMessageType = "provider_query_use_latest" + ProviderVersionAlreadyInstalled LogMessageType = "provider_version_already_installed" + ProviderVersionFoundInCacheDir LogMessageType = "provider_version_found_in_cache_dir" + ProviderVersionInstallationStart LogMessageType = "provider_version_installation_start" + ProviderVersionInstallationComplete LogMessageType = "provider_version_installation_complete" + BuiltInProviderAvailable LogMessageType = "built_in_provider_available" + ThirdPartyProvidersInstalled LogMessageType = "third_party_providers_installed" + + // Dependency lock file messages + ProviderLockfileCreated LogMessageType = "provider_lockfile_created" + ProviderLockfileUpdated LogMessageType = "provider_lockfile_updated" + + // Provider trust-related messages + ProviderInteractiveApproval LogMessageType = "provider_interactive_approval" + ProviderInteractiveRejection LogMessageType = "provider_interactive_rejection" + ProviderAutomaticApproval LogMessageType = "provider_automatic_approval" + + // State migration-related messages + LogMigrationStart LogMessageType = "migration_start" + LogMigrationComplete LogMessageType = "migration_complete" + LogMigrationErrored LogMessageType = "migration_errored" + LogMigrationFinalized LogMessageType = "migration_finalized" + LogMigrationSourceInitializationStart LogMessageType = "migration_source_initialization_start" + LogMigrationSourceInitializationComplete LogMessageType = "migration_source_initialization_complete" + LogMigrationDestinationInitializationStart LogMessageType = "migration_destination_initialization_start" + LogMigrationDestinationInitializationComplete LogMessageType = "migration_destination_initialization_complete" +) + +type ProviderInstallationStartMessage struct { + baseLogMessage +} + +type StateStoreProviderInstallationStartMessage struct { + baseLogMessage +} + +type ProviderQueryUsePreviousVersionMessage struct { + baseLogMessage +} + +type ProviderQueryUsePreviousConstraintsMessage struct { + baseLogMessage +} + +type ProviderQueryUseLatestMessage struct { + baseLogMessage +} + +type ProviderVersionAlreadyInstalledMessage struct { + baseLogMessage +} + +type ProviderVersionFoundInCacheDirMessage struct { + baseLogMessage +} + +type ProviderVersionInstallationStartMessage struct { + baseLogMessage +} + +type ProviderVersionInstallationCompleteMessage struct { + baseLogMessage +} + +type BuiltInProviderAvailableMessage struct { + baseLogMessage +} + +type ThirdPartyProvidersInstalledMessage struct { + baseLogMessage +} + +type ProviderLockfileCreatedMessage struct { + baseLogMessage +} + +type ProviderLockfileUpdatedMessage struct { + baseLogMessage +} + +type ProviderInteractiveApprovalMessage struct { + baseLogMessage +} + +type ProviderInteractiveRejectionMessage struct { + baseLogMessage +} + +type ProviderAutomaticApprovalMessage struct { + baseLogMessage +} + +type MigrationStartMessage struct { + baseLogMessage +} + +type MigrationCompleteMessage struct { + baseLogMessage +} + +type MigrationErroredMessage struct { + baseLogMessage +} + +type MigrationFinalizedMessage struct { + baseLogMessage +} + +type MigrationSourceInitializationStartMessage struct { + baseLogMessage +} + +type MigrationSourceInitializationCompleteMessage struct { + baseLogMessage +} + +type MigrationDestinationInitializationStartMessage struct { + baseLogMessage +} + +type MigrationDestinationInitializationCompleteMessage struct { + baseLogMessage +}
diff --git a/logging_test.go b/logging_test.go index 7f9f6d5..91abc52 100644 --- a/logging_test.go +++ b/logging_test.go
@@ -346,3 +346,255 @@ } } } + +func TestLogging_stateMigrate(t *testing.T) { + testCases := []struct { + rawMessage string + expectedMessage LogMsg + }{ + // provider installation + { + `{"@level":"info","@message":"Installing providers...","@module":"terraform.ui","@timestamp":"2026-08-26T16:20:04.692816Z","type":"provider_installation_start"}`, + ProviderInstallationStartMessage{ + baseLogMessage: baseLogMessage{ + Lvl: Info, + Msg: "Installing providers...", + Time: time.Date(2026, 8, 26, 16, 20, 04, 692816000, time.UTC), + }, + }, + }, + { + `{"@level":"info","@message":"Installing provider hashicorp/test v1.2.3 for state store test_foo...","@module":"terraform.ui","@timestamp":"2026-08-26T16:20:04.692816Z","type":"state_store_provider_installation_start"}`, + StateStoreProviderInstallationStartMessage{ + baseLogMessage: baseLogMessage{ + Lvl: Info, + Msg: "Installing provider hashicorp/test v1.2.3 for state store test_foo...", + Time: time.Date(2026, 8, 26, 16, 20, 04, 692816000, time.UTC), + }, + }, + }, + { + `{"@level":"info","@message":"hashicorp/test: Reusing version 1.0.0 from the dependency lock file","@module":"terraform.ui","@timestamp":"2026-08-26T16:20:04.692816Z","type":"provider_query_use_previous_version"}`, + ProviderQueryUsePreviousVersionMessage{ + baseLogMessage: baseLogMessage{ + Lvl: Info, + Msg: "hashicorp/test: Reusing version 1.0.0 from the dependency lock file", + Time: time.Date(2026, 8, 26, 16, 20, 04, 692816000, time.UTC), + }, + }, + }, + { + `{"@level":"info","@message":"Finding matching versions for provider: hashicorp/test, version_constraint: \"1.0.0\"","@module":"terraform.ui","@timestamp":"2026-08-26T16:20:04.692816Z","type":"provider_query_use_constraints"}`, + ProviderQueryUsePreviousConstraintsMessage{ + baseLogMessage: baseLogMessage{ + Lvl: Info, + Msg: "Finding matching versions for provider: hashicorp/test, version_constraint: \"1.0.0\"", + Time: time.Date(2026, 8, 26, 16, 20, 04, 692816000, time.UTC), + }, + }, + }, + { + `{"@level":"info","@message":"hashicorp/test: Finding latest version...","@module":"terraform.ui","@timestamp":"2026-08-26T16:20:04.692816Z","type":"provider_query_use_latest"}`, + ProviderQueryUseLatestMessage{ + baseLogMessage: baseLogMessage{ + Lvl: Info, + Msg: "hashicorp/test: Finding latest version...", + Time: time.Date(2026, 8, 26, 16, 20, 04, 692816000, time.UTC), + }, + }, + }, + { + `{"@level":"info","@message":"hashicorp/test v1.0.0: Using previously-installed provider version","@module":"terraform.ui","@timestamp":"2026-08-26T16:20:04.692816Z","type":"provider_version_already_installed"}`, + ProviderVersionAlreadyInstalledMessage{ + baseLogMessage: baseLogMessage{ + Lvl: Info, + Msg: "hashicorp/test v1.0.0: Using previously-installed provider version", + Time: time.Date(2026, 8, 26, 16, 20, 04, 692816000, time.UTC), + }, + }, + }, + { + `{"@level":"info","@message":"hashicorp/test v1.0.0: Using from the shared cache directory","@module":"terraform.ui","@timestamp":"2026-08-26T16:20:04.692816Z","type":"provider_version_found_in_cache_dir"}`, + ProviderVersionFoundInCacheDirMessage{ + baseLogMessage: baseLogMessage{ + Lvl: Info, + Msg: "hashicorp/test v1.0.0: Using from the shared cache directory", + Time: time.Date(2026, 8, 26, 16, 20, 04, 692816000, time.UTC), + }, + }, + }, + { + `{"@level":"info","@message":"Installing provider version: hashicorp/test v1.2.3...","@module":"terraform.ui","@timestamp":"2026-08-26T16:20:04.692816Z","type":"provider_version_installation_start"}`, + ProviderVersionInstallationStartMessage{ + baseLogMessage: baseLogMessage{ + Lvl: Info, + Msg: "Installing provider version: hashicorp/test v1.2.3...", + Time: time.Date(2026, 8, 26, 16, 20, 04, 692816000, time.UTC), + }, + }, + }, + { + `{"@level":"info","@message":"Installed provider version: hashicorp/test v1.2.3 (verified checksum)","@module":"terraform.ui","@timestamp":"2026-08-26T16:20:04.692816Z","type":"provider_version_installation_complete"}`, + ProviderVersionInstallationCompleteMessage{ + baseLogMessage: baseLogMessage{ + Lvl: Info, + Msg: "Installed provider version: hashicorp/test v1.2.3 (verified checksum)", + Time: time.Date(2026, 8, 26, 16, 20, 04, 692816000, time.UTC), + }, + }, + }, + { + `{"@level":"info","@message":"hashicorp/test is built in to Terraform","@module":"terraform.ui","@timestamp":"2026-08-26T16:20:04.692816Z","type":"built_in_provider_available"}`, + BuiltInProviderAvailableMessage{ + baseLogMessage: baseLogMessage{ + Lvl: Info, + Msg: "hashicorp/test is built in to Terraform", + Time: time.Date(2026, 8, 26, 16, 20, 04, 692816000, time.UTC), + }, + }, + }, + { + `{"@level":"info","@message":"Partner and community providers are signed by their developers.","@module":"terraform.ui","@timestamp":"2026-08-26T16:20:04.692816Z","type":"third_party_providers_installed"}`, + ThirdPartyProvidersInstalledMessage{ + baseLogMessage: baseLogMessage{ + Lvl: Info, + Msg: "Partner and community providers are signed by their developers.", + Time: time.Date(2026, 8, 26, 16, 20, 04, 692816000, time.UTC), + }, + }, + }, + // dependency lock file + { + `{"@level":"info","@message":"Terraform has created a lock file .terraform.lock.hcl to record the provider selections it made above.","@module":"terraform.ui","@timestamp":"2026-08-26T16:20:04.692816Z","type":"provider_lockfile_created"}`, + ProviderLockfileCreatedMessage{ + baseLogMessage: baseLogMessage{ + Lvl: Info, + Msg: "Terraform has created a lock file .terraform.lock.hcl to record the provider selections it made above.", + Time: time.Date(2026, 8, 26, 16, 20, 04, 692816000, time.UTC), + }, + }, + }, + { + `{"@level":"info","@message":"Terraform has made some changes to the provider dependency selections recorded in the .terraform.lock.hcl file.","@module":"terraform.ui","@timestamp":"2026-08-26T16:20:04.692816Z","type":"provider_lockfile_updated"}`, + ProviderLockfileUpdatedMessage{ + baseLogMessage: baseLogMessage{ + Lvl: Info, + Msg: "Terraform has made some changes to the provider dependency selections recorded in the .terraform.lock.hcl file.", + Time: time.Date(2026, 8, 26, 16, 20, 04, 692816000, time.UTC), + }, + }, + }, + // provider trust + { + `{"@level":"info","@message":"The state store provider was approved by the user.","@module":"terraform.ui","@timestamp":"2026-08-26T16:20:04.692816Z","type":"provider_interactive_approval"}`, + ProviderInteractiveApprovalMessage{ + baseLogMessage: baseLogMessage{ + Lvl: Info, + Msg: "The state store provider was approved by the user.", + Time: time.Date(2026, 8, 26, 16, 20, 04, 692816000, time.UTC), + }, + }, + }, + { + `{"@level":"info","@message":"The state store provider was rejected by the user.","@module":"terraform.ui","@timestamp":"2026-08-26T16:20:04.692816Z","type":"provider_interactive_rejection"}`, + ProviderInteractiveRejectionMessage{ + baseLogMessage: baseLogMessage{ + Lvl: Info, + Msg: "The state store provider was rejected by the user.", + Time: time.Date(2026, 8, 26, 16, 20, 04, 692816000, time.UTC), + }, + }, + }, + { + `{"@level":"info","@message":"The state store provider was approved automatically.","@module":"terraform.ui","@timestamp":"2026-08-26T16:20:04.692816Z","type":"provider_automatic_approval"}`, + ProviderAutomaticApprovalMessage{ + baseLogMessage: baseLogMessage{ + Lvl: Info, + Msg: "The state store provider was approved automatically.", + Time: time.Date(2026, 8, 26, 16, 20, 04, 692816000, time.UTC), + }, + }, + }, + // state migration + { + `{"@level":"info","@message":"Initializing source...","@module":"terraform.ui","@timestamp":"2026-08-26T16:20:04.692816Z","type":"migration_source_initialization_start"}`, + MigrationSourceInitializationStartMessage{ + baseLogMessage: baseLogMessage{ + Lvl: Info, + Msg: "Initializing source...", + Time: time.Date(2026, 8, 26, 16, 20, 04, 692816000, time.UTC), + }, + }, + }, + { + `{"@level":"info","@message":"Initialized source.","@module":"terraform.ui","@timestamp":"2026-08-26T16:20:04.692816Z","type":"migration_source_initialization_complete"}`, + MigrationSourceInitializationCompleteMessage{ + baseLogMessage: baseLogMessage{ + Lvl: Info, + Msg: "Initialized source.", + Time: time.Date(2026, 8, 26, 16, 20, 04, 692816000, time.UTC), + }, + }, + }, + { + `{"@level":"info","@message":"Initializing destination...","@module":"terraform.ui","@timestamp":"2026-08-26T16:20:04.692816Z","type":"migration_destination_initialization_start"}`, + MigrationDestinationInitializationStartMessage{ + baseLogMessage: baseLogMessage{ + Lvl: Info, + Msg: "Initializing destination...", + Time: time.Date(2026, 8, 26, 16, 20, 04, 692816000, time.UTC), + }, + }, + }, + { + `{"@level":"info","@message":"Initialized destination.","@module":"terraform.ui","@timestamp":"2026-08-26T16:20:04.692816Z","type":"migration_destination_initialization_complete"}`, + MigrationDestinationInitializationCompleteMessage{ + baseLogMessage: baseLogMessage{ + Lvl: Info, + Msg: "Initialized destination.", + Time: time.Date(2026, 8, 26, 16, 20, 04, 692816000, time.UTC), + }, + }, + }, + { + `{"@level":"info","@message":"Migrating state from backend \"local\" to backend \"local\"...","@module":"terraform.ui","@timestamp":"2026-08-26T16:20:04.692816Z","type":"migration_start"}`, + MigrationStartMessage{ + baseLogMessage: baseLogMessage{ + Lvl: Info, + Msg: "Migrating state from backend \"local\" to backend \"local\"...", + Time: time.Date(2026, 8, 26, 16, 20, 04, 692816000, time.UTC), + }, + }, + }, + { + `{"@level":"info","@message":"The migration process has copied state from the backend \"local\" to the backend \"local\"","@module":"terraform.ui","@timestamp":"2026-08-26T16:20:04.692816Z","type":"migration_complete"}`, + MigrationCompleteMessage{ + baseLogMessage: baseLogMessage{ + Lvl: Info, + Msg: "The migration process has copied state from the backend \"local\" to the backend \"local\"", + Time: time.Date(2026, 8, 26, 16, 20, 04, 692816000, time.UTC), + }, + }, + }, + { + `{"@level":"info","@message":"Finished migrating state from backend \"local\" to backend \"local\".","@module":"terraform.ui","@timestamp":"2026-08-26T16:20:04.692816Z","type":"migration_finalized"}`, + MigrationFinalizedMessage{ + baseLogMessage: baseLogMessage{ + Lvl: Info, + Msg: "Finished migrating state from backend \"local\" to backend \"local\".", + Time: time.Date(2026, 8, 26, 16, 20, 04, 692816000, 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) + } + } +}
diff --git a/logging_types.go b/logging_types.go index da149ba..3e905f6 100644 --- a/logging_types.go +++ b/logging_types.go
@@ -30,6 +30,36 @@ ListStartMessage{}, ListResourceFoundMessage{}, ListCompleteMessage{}, + + // state migrate + // provider installation + ProviderInstallationStartMessage{}, + StateStoreProviderInstallationStartMessage{}, + ProviderQueryUsePreviousVersionMessage{}, + ProviderQueryUsePreviousConstraintsMessage{}, + ProviderQueryUseLatestMessage{}, + ProviderVersionAlreadyInstalledMessage{}, + ProviderVersionFoundInCacheDirMessage{}, + ProviderVersionInstallationStartMessage{}, + ProviderVersionInstallationCompleteMessage{}, + BuiltInProviderAvailableMessage{}, + ThirdPartyProvidersInstalledMessage{}, + // dependency lock file + ProviderLockfileCreatedMessage{}, + ProviderLockfileUpdatedMessage{}, + // Provider trust-related message + ProviderInteractiveApprovalMessage{}, + ProviderInteractiveRejectionMessage{}, + ProviderAutomaticApprovalMessage{}, + // state migration + MigrationStartMessage{}, + MigrationCompleteMessage{}, + MigrationErroredMessage{}, + MigrationFinalizedMessage{}, + MigrationSourceInitializationStartMessage{}, + MigrationSourceInitializationCompleteMessage{}, + MigrationDestinationInitializationStartMessage{}, + MigrationDestinationInitializationCompleteMessage{}, } func unmarshalByType(t LogMessageType, b []byte) (LogMsg, error) { @@ -66,6 +96,84 @@ case MessageListComplete: v := ListCompleteMessage{} return v, d.Decode(&v) + + // state migrate + // provider installation + case ProviderInstallationStart: + v := ProviderInstallationStartMessage{} + return v, d.Decode(&v) + case StateStoreProviderInstallationStart: + v := StateStoreProviderInstallationStartMessage{} + return v, d.Decode(&v) + case ProviderQueryUsePreviousVersion: + v := ProviderQueryUsePreviousVersionMessage{} + return v, d.Decode(&v) + case ProviderQueryUsePreviousConstraints: + v := ProviderQueryUsePreviousConstraintsMessage{} + return v, d.Decode(&v) + case ProviderQueryUseLatest: + v := ProviderQueryUseLatestMessage{} + return v, d.Decode(&v) + case ProviderVersionAlreadyInstalled: + v := ProviderVersionAlreadyInstalledMessage{} + return v, d.Decode(&v) + case ProviderVersionFoundInCacheDir: + v := ProviderVersionFoundInCacheDirMessage{} + return v, d.Decode(&v) + case ProviderVersionInstallationStart: + v := ProviderVersionInstallationStartMessage{} + return v, d.Decode(&v) + case ProviderVersionInstallationComplete: + v := ProviderVersionInstallationCompleteMessage{} + return v, d.Decode(&v) + case BuiltInProviderAvailable: + v := BuiltInProviderAvailableMessage{} + return v, d.Decode(&v) + case ThirdPartyProvidersInstalled: + v := ThirdPartyProvidersInstalledMessage{} + return v, d.Decode(&v) + // dependency lock file + case ProviderLockfileCreated: + v := ProviderLockfileCreatedMessage{} + return v, d.Decode(&v) + case ProviderLockfileUpdated: + v := ProviderLockfileUpdatedMessage{} + return v, d.Decode(&v) + // provider trust + case ProviderInteractiveApproval: + v := ProviderInteractiveApprovalMessage{} + return v, d.Decode(&v) + case ProviderInteractiveRejection: + v := ProviderInteractiveRejectionMessage{} + return v, d.Decode(&v) + case ProviderAutomaticApproval: + v := ProviderAutomaticApprovalMessage{} + return v, d.Decode(&v) + // state migration + case LogMigrationStart: + v := MigrationStartMessage{} + return v, d.Decode(&v) + case LogMigrationComplete: + v := MigrationCompleteMessage{} + return v, d.Decode(&v) + case LogMigrationErrored: + v := MigrationErroredMessage{} + return v, d.Decode(&v) + case LogMigrationFinalized: + v := MigrationFinalizedMessage{} + return v, d.Decode(&v) + case LogMigrationSourceInitializationStart: + v := MigrationSourceInitializationStartMessage{} + return v, d.Decode(&v) + case LogMigrationSourceInitializationComplete: + v := MigrationSourceInitializationCompleteMessage{} + return v, d.Decode(&v) + case LogMigrationDestinationInitializationStart: + v := MigrationDestinationInitializationStartMessage{} + return v, d.Decode(&v) + case LogMigrationDestinationInitializationComplete: + v := MigrationDestinationInitializationCompleteMessage{} + return v, d.Decode(&v) } v := UnknownLogMessage{}