Fix running tofu show and tofu state show with state files that reference Terraform registry providers. (#1141)

Signed-off-by: Jakub Martin <kubam@spacelift.io>
This commit is contained in:
Kuba Martin 2024-01-18 13:58:33 +01:00 committed by GitHub
parent 3bc81f29a9
commit 9f95a840b0
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 85 additions and 8 deletions

View File

@ -22,6 +22,7 @@ import (
"github.com/opentofu/opentofu/internal/states/statemgr"
"github.com/opentofu/opentofu/internal/tfdiags"
"github.com/opentofu/opentofu/internal/tofu"
"github.com/opentofu/opentofu/internal/tofumigrate"
)
// Many of the methods we get data from can emit special error types if they're
@ -108,7 +109,7 @@ func (c *ShowCommand) Synopsis() string {
}
func (c *ShowCommand) show(path string) (*plans.Plan, *cloudplan.RemotePlanJSON, *statefile.File, *configs.Config, *tofu.Schemas, tfdiags.Diagnostics) {
var diags, showDiags tfdiags.Diagnostics
var diags, showDiags, migrateDiags tfdiags.Diagnostics
var plan *plans.Plan
var jsonPlan *cloudplan.RemotePlanJSON
var stateFile *statefile.File
@ -136,6 +137,14 @@ func (c *ShowCommand) show(path string) (*plans.Plan, *cloudplan.RemotePlanJSON,
}
}
if stateFile != nil {
stateFile.State, migrateDiags = tofumigrate.MigrateStateProviderAddresses(config, stateFile.State)
diags = diags.Append(migrateDiags)
if migrateDiags.HasErrors() {
return plan, jsonPlan, stateFile, config, schemas, diags
}
}
// Get schemas, if possible
if config != nil || stateFile != nil {
schemas, diags = c.MaybeGetSchemas(stateFile.State, config)

View File

@ -18,6 +18,7 @@ import (
"github.com/opentofu/opentofu/internal/command/jsonstate"
"github.com/opentofu/opentofu/internal/states"
"github.com/opentofu/opentofu/internal/states/statefile"
"github.com/opentofu/opentofu/internal/tofumigrate"
)
// StateShowCommand is a Command implementation that shows a single resource.
@ -124,6 +125,13 @@ func (c *StateShowCommand) Run(args []string) int {
c.Streams.Eprintln(errStateNotFound)
return 1
}
migratedState, migrateDiags := tofumigrate.MigrateStateProviderAddresses(lr.Config, state)
diags = diags.Append(migrateDiags)
if migrateDiags.HasErrors() {
c.View.Diagnostics(diags)
return 1
}
state = migratedState
is := state.ResourceInstance(addr)
if !is.HasCurrent() {

View File

@ -3,9 +3,11 @@ package tofumigrate
import (
"os"
"github.com/hashicorp/hcl/v2"
tfaddr "github.com/opentofu/registry-address"
"github.com/opentofu/opentofu/internal/configs"
"github.com/opentofu/opentofu/internal/getproviders"
"github.com/opentofu/opentofu/internal/states"
"github.com/opentofu/opentofu/internal/tfdiags"
)
@ -37,14 +39,23 @@ func MigrateStateProviderAddresses(config *configs.Config, state *states.State)
return state, nil
}
if state == nil {
return nil, nil
}
var diags tfdiags.Diagnostics
stateCopy := state.DeepCopy()
providers, hclDiags := config.ProviderRequirements()
diags = diags.Append(hclDiags)
if hclDiags.HasErrors() {
return nil, diags
providers := getproviders.Requirements{}
// config could be nil when we're e.g. showing a statefile without the configuration present
if config != nil {
var hclDiags hcl.Diagnostics
providers, hclDiags = config.ProviderRequirements()
diags = diags.Append(hclDiags)
if hclDiags.HasErrors() {
return nil, diags
}
}
for _, module := range stateCopy.Modules {

View File

@ -4,7 +4,10 @@ import (
"reflect"
"testing"
"github.com/hashicorp/hcl/v2"
"github.com/opentofu/opentofu/internal/addrs"
"github.com/opentofu/opentofu/internal/configs"
"github.com/opentofu/opentofu/internal/configs/configload"
"github.com/opentofu/opentofu/internal/states"
)
@ -163,12 +166,58 @@ func TestMigrateStateProviderAddresses(t *testing.T) {
)
}),
},
{
name: "if there is no code, migrate",
args: args{
configDir: "",
state: states.BuildState(func(s *states.SyncState) {
s.SetResourceInstanceCurrent(
mustParseInstAddr("random_id.example"),
&states.ResourceInstanceObjectSrc{
Status: states.ObjectReady,
AttrsJSON: []byte(`{}`),
},
makeRootProviderAddr("registry.terraform.io/hashicorp/random"),
)
s.SetResourceInstanceCurrent(
mustParseInstAddr("aws_instance.example"),
&states.ResourceInstanceObjectSrc{
Status: states.ObjectReady,
AttrsJSON: []byte(`{}`),
},
makeRootProviderAddr("registry.terraform.io/hashicorp/aws"),
)
}),
},
want: states.BuildState(func(s *states.SyncState) {
s.SetResourceInstanceCurrent(
mustParseInstAddr("random_id.example"),
&states.ResourceInstanceObjectSrc{
Status: states.ObjectReady,
AttrsJSON: []byte(`{}`),
},
makeRootProviderAddr("registry.opentofu.org/hashicorp/random"),
)
s.SetResourceInstanceCurrent(
mustParseInstAddr("aws_instance.example"),
&states.ResourceInstanceObjectSrc{
Status: states.ObjectReady,
AttrsJSON: []byte(`{}`),
},
makeRootProviderAddr("registry.opentofu.org/hashicorp/aws"),
)
}),
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
cfg, hclDiags := loader.LoadConfig(tt.args.configDir)
if hclDiags.HasErrors() {
t.Fatalf("invalid configuration: %s", hclDiags.Error())
var cfg *configs.Config
if tt.args.configDir != "" {
var hclDiags hcl.Diagnostics
cfg, hclDiags = loader.LoadConfig(tt.args.configDir)
if hclDiags.HasErrors() {
t.Fatalf("invalid configuration: %s", hclDiags.Error())
}
}
got, err := MigrateStateProviderAddresses(cfg, tt.args.state)