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{}