2025-03-19 05:39:50 -04:00
// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: BUSL-1.1
package stackmigrate
import (
"encoding/json"
"fmt"
"path/filepath"
2025-05-16 04:10:47 -04:00
"sort"
2025-03-19 05:39:50 -04:00
"strings"
"testing"
"github.com/google/go-cmp/cmp"
"github.com/google/go-cmp/cmp/cmpopts"
"github.com/hashicorp/go-slug/sourceaddrs"
"github.com/hashicorp/go-slug/sourcebundle"
"github.com/hashicorp/hcl/v2"
2025-05-16 04:10:47 -04:00
"github.com/zclconf/go-cty-debug/ctydebug"
"github.com/zclconf/go-cty/cty"
2025-03-19 05:39:50 -04:00
"github.com/hashicorp/terraform/internal/addrs"
"github.com/hashicorp/terraform/internal/collections"
"github.com/hashicorp/terraform/internal/depsfile"
"github.com/hashicorp/terraform/internal/getproviders/providerreqs"
"github.com/hashicorp/terraform/internal/providers"
"github.com/hashicorp/terraform/internal/stacks/stackaddrs"
"github.com/hashicorp/terraform/internal/stacks/stackconfig"
stacks_testing_provider "github.com/hashicorp/terraform/internal/stacks/stackruntime/testing"
"github.com/hashicorp/terraform/internal/stacks/stackstate"
"github.com/hashicorp/terraform/internal/states"
"github.com/hashicorp/terraform/internal/tfdiags"
)
2025-05-16 04:10:47 -04:00
func TestMigrate ( t * testing . T ) {
deposedKey := states . NewDeposedKey ( )
tcs := map [ string ] struct {
path string
state func ( ss * states . SyncState )
resources map [ string ] string
modules map [ string ] string
expected [ ] stackstate . AppliedChange
expectedDiags tfdiags . Diagnostics
} {
"module" : {
path : filepath . Join ( "with-single-input" , "valid" ) ,
state : func ( ss * states . SyncState ) {
ss . SetResourceInstanceCurrent (
addrs . AbsResourceInstance {
Module : addrs . ModuleInstance {
{
Name : "child" ,
} ,
} ,
Resource : addrs . ResourceInstance {
Resource : addrs . Resource {
Mode : addrs . ManagedResourceMode ,
Type : "testing_resource" ,
Name : "data" ,
} ,
Key : addrs . NoKey ,
} ,
} ,
& states . ResourceInstanceObjectSrc {
Status : states . ObjectReady ,
AttrsJSON : mustMarshalJSONAttrs ( map [ string ] interface { } {
"id" : "foo" ,
"value" : "hello" ,
} ) ,
} ,
mustDefaultRootProvider ( "testing" ) ,
)
ss . SetResourceInstanceDeposed (
addrs . AbsResourceInstance {
Module : addrs . ModuleInstance {
{
Name : "child" ,
} ,
} ,
Resource : addrs . ResourceInstance {
Resource : addrs . Resource {
Mode : addrs . ManagedResourceMode ,
Type : "testing_resource" ,
Name : "data" ,
} ,
Key : addrs . NoKey ,
} ,
} ,
deposedKey ,
& states . ResourceInstanceObjectSrc {
Status : states . ObjectReady ,
AttrsJSON : mustMarshalJSONAttrs ( map [ string ] interface { } {
"id" : "foo" ,
"value" : "hello" ,
} ) ,
} ,
mustDefaultRootProvider ( "testing" ) ,
)
} ,
modules : map [ string ] string {
"child" : "self" ,
} ,
expected : [ ] stackstate . AppliedChange {
& stackstate . AppliedChangeComponentInstance {
ComponentAddr : mustAbsComponent ( "component.self" ) ,
ComponentInstanceAddr : mustAbsComponentInstance ( "component.self" ) ,
OutputValues : map [ addrs . OutputValue ] cty . Value { } ,
InputVariables : map [ addrs . InputVariable ] cty . Value {
{ Name : "id" } : cty . DynamicVal ,
{ Name : "input" } : cty . DynamicVal ,
} ,
2025-03-19 05:39:50 -04:00
} ,
2025-05-16 04:10:47 -04:00
& stackstate . AppliedChangeResourceInstanceObject {
ResourceInstanceObjectAddr : mustAbsResourceInstanceObject ( "component.self.testing_resource.data" ) ,
NewStateSrc : & states . ResourceInstanceObjectSrc {
AttrsJSON : mustMarshalJSONAttrs ( map [ string ] interface { } {
"id" : "foo" ,
"value" : "hello" ,
} ) ,
Status : states . ObjectReady ,
Private : nil ,
} ,
ProviderConfigAddr : mustDefaultRootProvider ( "testing" ) ,
Schema : stacks_testing_provider . TestingResourceSchema ,
} ,
& stackstate . AppliedChangeResourceInstanceObject {
ResourceInstanceObjectAddr : stackaddrs . AbsResourceInstanceObject {
Component : mustAbsResourceInstanceObject ( "component.self.testing_resource.data" ) . Component ,
Item : addrs . AbsResourceInstanceObject {
ResourceInstance : mustAbsResourceInstanceObject ( "component.self.testing_resource.data" ) . Item . ResourceInstance ,
DeposedKey : deposedKey ,
} ,
} ,
NewStateSrc : & states . ResourceInstanceObjectSrc {
AttrsJSON : mustMarshalJSONAttrs ( map [ string ] interface { } {
"id" : "foo" ,
"value" : "hello" ,
} ) ,
Status : states . ObjectReady ,
Private : nil ,
} ,
ProviderConfigAddr : mustDefaultRootProvider ( "testing" ) ,
Schema : stacks_testing_provider . TestingResourceSchema ,
2025-03-19 05:39:50 -04:00
} ,
} ,
} ,
2025-05-16 04:10:47 -04:00
"root resources" : {
path : filepath . Join ( "with-single-input" , "valid" ) ,
state : func ( ss * states . SyncState ) {
ss . SetResourceInstanceDeposed (
addrs . AbsResourceInstance {
Module : addrs . RootModuleInstance ,
Resource : addrs . ResourceInstance {
Resource : addrs . Resource {
Mode : addrs . ManagedResourceMode ,
Type : "testing_resource" ,
Name : "data" ,
} ,
Key : addrs . NoKey ,
} ,
} ,
deposedKey ,
& states . ResourceInstanceObjectSrc {
Status : states . ObjectReady ,
AttrsJSON : mustMarshalJSONAttrs ( map [ string ] interface { } {
"id" : "foo" ,
"value" : "hello" ,
} ) ,
} ,
mustDefaultRootProvider ( "testing" ) ,
)
ss . SetResourceInstanceCurrent (
addrs . AbsResourceInstance {
Module : addrs . RootModuleInstance ,
Resource : addrs . ResourceInstance {
Resource : addrs . Resource {
Mode : addrs . ManagedResourceMode ,
Type : "testing_resource" ,
Name : "data" ,
} ,
Key : addrs . NoKey ,
} ,
} ,
& states . ResourceInstanceObjectSrc {
Status : states . ObjectReady ,
AttrsJSON : mustMarshalJSONAttrs ( map [ string ] interface { } {
"id" : "foo" ,
"value" : "hello" ,
} ) ,
} ,
mustDefaultRootProvider ( "testing" ) ,
)
} ,
resources : map [ string ] string {
"testing_resource.data" : "component.self" ,
} ,
expected : [ ] stackstate . AppliedChange {
& stackstate . AppliedChangeComponentInstance {
ComponentAddr : mustAbsComponent ( "component.self" ) ,
ComponentInstanceAddr : mustAbsComponentInstance ( "component.self" ) ,
OutputValues : map [ addrs . OutputValue ] cty . Value { } ,
InputVariables : map [ addrs . InputVariable ] cty . Value {
{ Name : "id" } : cty . DynamicVal ,
{ Name : "input" } : cty . DynamicVal ,
} ,
} ,
& stackstate . AppliedChangeResourceInstanceObject {
ResourceInstanceObjectAddr : mustAbsResourceInstanceObject ( "component.self.testing_resource.data" ) ,
NewStateSrc : & states . ResourceInstanceObjectSrc {
AttrsJSON : mustMarshalJSONAttrs ( map [ string ] interface { } {
"id" : "foo" ,
"value" : "hello" ,
} ) ,
Status : states . ObjectReady ,
Private : nil ,
} ,
ProviderConfigAddr : mustDefaultRootProvider ( "testing" ) ,
Schema : stacks_testing_provider . TestingResourceSchema ,
} ,
& stackstate . AppliedChangeResourceInstanceObject {
ResourceInstanceObjectAddr : stackaddrs . AbsResourceInstanceObject {
Component : mustAbsResourceInstanceObject ( "component.self.testing_resource.data" ) . Component ,
Item : addrs . AbsResourceInstanceObject {
ResourceInstance : mustAbsResourceInstanceObject ( "component.self.testing_resource.data" ) . Item . ResourceInstance ,
DeposedKey : deposedKey ,
} ,
} ,
NewStateSrc : & states . ResourceInstanceObjectSrc {
AttrsJSON : mustMarshalJSONAttrs ( map [ string ] interface { } {
"id" : "foo" ,
"value" : "hello" ,
} ) ,
Status : states . ObjectReady ,
Private : nil ,
} ,
ProviderConfigAddr : mustDefaultRootProvider ( "testing" ) ,
Schema : stacks_testing_provider . TestingResourceSchema ,
} ,
2025-03-19 05:39:50 -04:00
} ,
} ,
2025-05-16 04:10:47 -04:00
"component_dependency" : {
path : filepath . Join ( "for-stacks-migrate" , "with-dependency" , "input-dependency" ) ,
state : func ( ss * states . SyncState ) {
ss . SetOutputValue ( addrs . AbsOutputValue {
Module : addrs . RootModuleInstance ,
OutputValue : addrs . OutputValue { Name : "output" } ,
} , cty . StringVal ( "before" ) , false )
ss . SetResourceInstanceCurrent (
addrs . AbsResourceInstance {
Module : addrs . RootModuleInstance ,
Resource : addrs . ResourceInstance {
Resource : addrs . Resource {
Mode : addrs . ManagedResourceMode ,
Type : "testing_resource" ,
Name : "data" ,
} ,
Key : addrs . NoKey ,
} ,
} ,
& states . ResourceInstanceObjectSrc {
Status : states . ObjectReady ,
AttrsJSON : mustMarshalJSONAttrs ( map [ string ] interface { } {
"id" : "foo" ,
"value" : "hello" ,
} ) ,
} ,
mustDefaultRootProvider ( "testing" ) ,
)
ss . SetResourceInstanceCurrent (
addrs . AbsResourceInstance {
Module : addrs . RootModuleInstance ,
Resource : addrs . ResourceInstance {
Resource : addrs . Resource {
Mode : addrs . ManagedResourceMode ,
Type : "testing_resource" ,
Name : "another" ,
} ,
Key : addrs . IntKey ( 0 ) ,
} ,
} ,
& states . ResourceInstanceObjectSrc {
Status : states . ObjectReady ,
AttrsJSON : mustMarshalJSONAttrs ( map [ string ] interface { } {
"id" : "foo" ,
"value" : "hello" ,
} ) ,
} ,
mustDefaultRootProvider ( "testing" ) ,
)
ss . SetResourceInstanceCurrent (
addrs . AbsResourceInstance {
Module : addrs . RootModuleInstance ,
Resource : addrs . ResourceInstance {
Resource : addrs . Resource {
Mode : addrs . ManagedResourceMode ,
Type : "testing_resource" ,
Name : "another" ,
} ,
Key : addrs . IntKey ( 1 ) ,
} ,
} ,
& states . ResourceInstanceObjectSrc {
Status : states . ObjectReady ,
AttrsJSON : mustMarshalJSONAttrs ( map [ string ] interface { } {
"id" : "foo" ,
"value" : "hello" ,
} ) ,
} ,
mustDefaultRootProvider ( "testing" ) ,
)
} ,
resources : map [ string ] string {
"testing_resource.data" : "component.parent" ,
"testing_resource.another[0]" : "component.child" ,
"testing_resource.another[1]" : "component.child" ,
} ,
modules : map [ string ] string { } ,
expected : [ ] stackstate . AppliedChange {
& stackstate . AppliedChangeComponentInstance {
ComponentAddr : mustAbsComponent ( "component.child" ) ,
ComponentInstanceAddr : mustAbsComponentInstance ( "component.child" ) ,
OutputValues : map [ addrs . OutputValue ] cty . Value {
{ Name : "id" } : cty . DynamicVal ,
} ,
InputVariables : map [ addrs . InputVariable ] cty . Value {
{ Name : "id" } : cty . DynamicVal ,
{ Name : "input" } : cty . DynamicVal ,
} ,
Dependencies : collections . NewSet ( mustAbsComponent ( "component.parent" ) ) ,
} ,
& stackstate . AppliedChangeResourceInstanceObject {
ResourceInstanceObjectAddr : mustAbsResourceInstanceObject ( "component.child.testing_resource.another[0]" ) ,
NewStateSrc : & states . ResourceInstanceObjectSrc {
AttrsJSON : mustMarshalJSONAttrs ( map [ string ] interface { } {
"id" : "foo" ,
"value" : "hello" ,
} ) ,
Status : states . ObjectReady ,
Private : nil ,
} ,
ProviderConfigAddr : mustDefaultRootProvider ( "testing" ) ,
Schema : stacks_testing_provider . TestingResourceSchema ,
} ,
& stackstate . AppliedChangeResourceInstanceObject {
ResourceInstanceObjectAddr : mustAbsResourceInstanceObject ( "component.child.testing_resource.another[1]" ) ,
NewStateSrc : & states . ResourceInstanceObjectSrc {
AttrsJSON : mustMarshalJSONAttrs ( map [ string ] interface { } {
"id" : "foo" ,
"value" : "hello" ,
} ) ,
Status : states . ObjectReady ,
Private : nil ,
} ,
ProviderConfigAddr : mustDefaultRootProvider ( "testing" ) ,
Schema : stacks_testing_provider . TestingResourceSchema ,
} ,
& stackstate . AppliedChangeComponentInstance {
ComponentAddr : mustAbsComponent ( "component.parent" ) ,
ComponentInstanceAddr : mustAbsComponentInstance ( "component.parent" ) ,
OutputValues : map [ addrs . OutputValue ] cty . Value {
{ Name : "id" } : cty . DynamicVal ,
} ,
InputVariables : map [ addrs . InputVariable ] cty . Value {
{ Name : "id" } : cty . DynamicVal ,
{ Name : "input" } : cty . DynamicVal ,
} ,
Dependents : collections . NewSet ( mustAbsComponent ( "component.child" ) ) ,
} ,
& stackstate . AppliedChangeResourceInstanceObject {
ResourceInstanceObjectAddr : mustAbsResourceInstanceObject ( "component.parent.testing_resource.data" ) ,
NewStateSrc : & states . ResourceInstanceObjectSrc {
AttrsJSON : mustMarshalJSONAttrs ( map [ string ] interface { } {
"id" : "foo" ,
"value" : "hello" ,
} ) ,
Status : states . ObjectReady ,
Private : nil ,
} ,
ProviderConfigAddr : mustDefaultRootProvider ( "testing" ) ,
Schema : stacks_testing_provider . TestingResourceSchema ,
} ,
2025-03-19 05:39:50 -04:00
} ,
} ,
2025-05-16 04:10:47 -04:00
"nested module resources" : {
path : filepath . Join ( "for-stacks-migrate" , "with-nested-module" ) ,
state : func ( ss * states . SyncState ) {
ss . SetResourceInstanceCurrent (
addrs . Resource {
Mode : addrs . ManagedResourceMode ,
Type : "testing_resource" ,
Name : "data" ,
} . Instance ( addrs . NoKey ) . Absolute ( addrs . RootModuleInstance ) ,
& states . ResourceInstanceObjectSrc {
Status : states . ObjectReady ,
AttrsJSON : mustMarshalJSONAttrs ( map [ string ] interface { } {
"id" : "foo" ,
"value" : "hello" ,
} ) ,
} ,
mustDefaultRootProvider ( "testing" ) ,
)
ss . SetResourceInstanceCurrent (
addrs . Resource {
Mode : addrs . ManagedResourceMode ,
Type : "testing_resource" ,
Name : "another" ,
} . Instance ( addrs . IntKey ( 0 ) ) . Absolute ( addrs . RootModuleInstance ) ,
& states . ResourceInstanceObjectSrc {
Status : states . ObjectReady ,
AttrsJSON : mustMarshalJSONAttrs ( map [ string ] interface { } {
"id" : "foo" ,
"value" : "hello" ,
} ) ,
} ,
mustDefaultRootProvider ( "testing" ) ,
)
ss . SetResourceInstanceCurrent (
addrs . Resource {
Mode : addrs . ManagedResourceMode ,
Type : "testing_resource" ,
Name : "another" ,
} . Instance ( addrs . IntKey ( 1 ) ) . Absolute ( addrs . RootModuleInstance ) ,
& states . ResourceInstanceObjectSrc {
Status : states . ObjectReady ,
AttrsJSON : mustMarshalJSONAttrs ( map [ string ] interface { } {
"id" : "foo" ,
"value" : "hello" ,
} ) ,
} ,
mustDefaultRootProvider ( "testing" ) ,
)
for _ , child := range [ ] string { "child_mod" , "child_mod2" } {
ss . SetResourceInstanceCurrent (
addrs . Resource {
Mode : addrs . ManagedResourceMode ,
Type : "testing_resource" ,
Name : "child_data" ,
} . Instance ( addrs . NoKey ) . Absolute ( addrs . ModuleInstance {
{
Name : child ,
} ,
} ) ,
& states . ResourceInstanceObjectSrc {
Status : states . ObjectReady ,
AttrsJSON : mustMarshalJSONAttrs ( map [ string ] interface { } {
"id" : "foo" ,
"value" : "hello" ,
} ) ,
} ,
mustDefaultRootProvider ( "testing" ) ,
)
ss . SetResourceInstanceCurrent (
addrs . Resource {
Mode : addrs . ManagedResourceMode ,
Type : "testing_resource" ,
Name : "another_child_data" ,
} . Instance ( addrs . IntKey ( 0 ) ) . Absolute ( addrs . ModuleInstance {
{
Name : child ,
} ,
} ) ,
& states . ResourceInstanceObjectSrc {
Status : states . ObjectReady ,
AttrsJSON : mustMarshalJSONAttrs ( map [ string ] interface { } {
"id" : "foo" ,
"value" : "hello" ,
} ) ,
} ,
mustDefaultRootProvider ( "testing" ) ,
)
ss . SetResourceInstanceCurrent (
addrs . Resource {
Mode : addrs . ManagedResourceMode ,
Type : "testing_resource" ,
Name : "another_child_data" ,
} . Instance ( addrs . IntKey ( 1 ) ) . Absolute ( addrs . ModuleInstance {
{
Name : child ,
} ,
} ) ,
& states . ResourceInstanceObjectSrc {
Status : states . ObjectReady ,
AttrsJSON : mustMarshalJSONAttrs ( map [ string ] interface { } {
"id" : "foo" ,
"value" : "hello" ,
} ) ,
} ,
mustDefaultRootProvider ( "testing" ) ,
)
}
2025-03-19 05:39:50 -04:00
} ,
2025-05-16 04:10:47 -04:00
resources : map [ string ] string {
"testing_resource.data" : "component.parent" ,
"testing_resource.another[0]" : "component.parent" ,
"testing_resource.another[1]" : "component.parent" ,
} ,
modules : map [ string ] string {
"child_mod" : "child" ,
"child_mod2" : "child2" ,
} ,
expected : [ ] stackstate . AppliedChange {
& stackstate . AppliedChangeComponentInstance {
ComponentAddr : mustAbsComponent ( "component.child" ) ,
ComponentInstanceAddr : mustAbsComponentInstance ( "component.child" ) ,
OutputValues : map [ addrs . OutputValue ] cty . Value {
{ Name : "id" } : cty . DynamicVal ,
} ,
InputVariables : map [ addrs . InputVariable ] cty . Value {
{ Name : "id" } : cty . DynamicVal ,
{ Name : "input" } : cty . DynamicVal ,
} ,
Dependencies : collections . NewSet ( mustAbsComponent ( "component.parent" ) ) ,
} ,
& stackstate . AppliedChangeResourceInstanceObject {
ResourceInstanceObjectAddr : mustAbsResourceInstanceObject ( "component.child.testing_resource.another_child_data[0]" ) ,
NewStateSrc : & states . ResourceInstanceObjectSrc {
AttrsJSON : mustMarshalJSONAttrs ( map [ string ] interface { } {
"id" : "foo" ,
"value" : "hello" ,
} ) ,
Status : states . ObjectReady ,
Private : nil ,
} ,
ProviderConfigAddr : mustDefaultRootProvider ( "testing" ) ,
Schema : stacks_testing_provider . TestingResourceSchema ,
} ,
& stackstate . AppliedChangeResourceInstanceObject {
ResourceInstanceObjectAddr : mustAbsResourceInstanceObject ( "component.child.testing_resource.another_child_data[1]" ) ,
NewStateSrc : & states . ResourceInstanceObjectSrc {
AttrsJSON : mustMarshalJSONAttrs ( map [ string ] interface { } {
"id" : "foo" ,
"value" : "hello" ,
} ) ,
Status : states . ObjectReady ,
Private : nil ,
} ,
ProviderConfigAddr : mustDefaultRootProvider ( "testing" ) ,
Schema : stacks_testing_provider . TestingResourceSchema ,
} ,
& stackstate . AppliedChangeResourceInstanceObject {
ResourceInstanceObjectAddr : mustAbsResourceInstanceObject ( "component.child.testing_resource.child_data" ) ,
NewStateSrc : & states . ResourceInstanceObjectSrc {
AttrsJSON : mustMarshalJSONAttrs ( map [ string ] interface { } {
"id" : "foo" ,
"value" : "hello" ,
} ) ,
Status : states . ObjectReady ,
Private : nil ,
} ,
ProviderConfigAddr : mustDefaultRootProvider ( "testing" ) ,
Schema : stacks_testing_provider . TestingResourceSchema ,
} ,
& stackstate . AppliedChangeComponentInstance {
ComponentAddr : mustAbsComponent ( "component.child2" ) ,
ComponentInstanceAddr : mustAbsComponentInstance ( "component.child2" ) ,
OutputValues : map [ addrs . OutputValue ] cty . Value {
{ Name : "id" } : cty . DynamicVal ,
} ,
InputVariables : map [ addrs . InputVariable ] cty . Value {
{ Name : "id" } : cty . DynamicVal ,
{ Name : "input" } : cty . DynamicVal ,
} ,
Dependencies : collections . NewSet ( mustAbsComponent ( "component.parent" ) ) ,
} ,
& stackstate . AppliedChangeResourceInstanceObject {
ResourceInstanceObjectAddr : mustAbsResourceInstanceObject ( "component.child2.testing_resource.another_child_data[0]" ) ,
NewStateSrc : & states . ResourceInstanceObjectSrc {
AttrsJSON : mustMarshalJSONAttrs ( map [ string ] interface { } {
"id" : "foo" ,
"value" : "hello" ,
} ) ,
Status : states . ObjectReady ,
Private : nil ,
} ,
ProviderConfigAddr : mustDefaultRootProvider ( "testing" ) ,
Schema : stacks_testing_provider . TestingResourceSchema ,
} ,
& stackstate . AppliedChangeResourceInstanceObject {
ResourceInstanceObjectAddr : mustAbsResourceInstanceObject ( "component.child2.testing_resource.another_child_data[1]" ) ,
NewStateSrc : & states . ResourceInstanceObjectSrc {
AttrsJSON : mustMarshalJSONAttrs ( map [ string ] interface { } {
"id" : "foo" ,
"value" : "hello" ,
} ) ,
Status : states . ObjectReady ,
Private : nil ,
} ,
ProviderConfigAddr : mustDefaultRootProvider ( "testing" ) ,
Schema : stacks_testing_provider . TestingResourceSchema ,
} ,
& stackstate . AppliedChangeResourceInstanceObject {
ResourceInstanceObjectAddr : mustAbsResourceInstanceObject ( "component.child2.testing_resource.child_data" ) ,
NewStateSrc : & states . ResourceInstanceObjectSrc {
AttrsJSON : mustMarshalJSONAttrs ( map [ string ] interface { } {
"id" : "foo" ,
"value" : "hello" ,
} ) ,
Status : states . ObjectReady ,
Private : nil ,
} ,
ProviderConfigAddr : mustDefaultRootProvider ( "testing" ) ,
Schema : stacks_testing_provider . TestingResourceSchema ,
} ,
& stackstate . AppliedChangeComponentInstance {
ComponentAddr : mustAbsComponent ( "component.parent" ) ,
ComponentInstanceAddr : mustAbsComponentInstance ( "component.parent" ) ,
OutputValues : map [ addrs . OutputValue ] cty . Value {
{ Name : "id" } : cty . DynamicVal ,
} ,
InputVariables : map [ addrs . InputVariable ] cty . Value {
{ Name : "id" } : cty . DynamicVal ,
{ Name : "input" } : cty . DynamicVal ,
} ,
Dependents : collections . NewSet ( mustAbsComponent ( "component.child" ) , mustAbsComponent ( "component.child2" ) ) ,
} ,
& stackstate . AppliedChangeResourceInstanceObject {
ResourceInstanceObjectAddr : mustAbsResourceInstanceObject ( "component.parent.testing_resource.another[0]" ) ,
NewStateSrc : & states . ResourceInstanceObjectSrc {
AttrsJSON : mustMarshalJSONAttrs ( map [ string ] interface { } {
"id" : "foo" ,
"value" : "hello" ,
} ) ,
Status : states . ObjectReady ,
Private : nil ,
} ,
ProviderConfigAddr : mustDefaultRootProvider ( "testing" ) ,
Schema : stacks_testing_provider . TestingResourceSchema ,
} ,
& stackstate . AppliedChangeResourceInstanceObject {
ResourceInstanceObjectAddr : mustAbsResourceInstanceObject ( "component.parent.testing_resource.another[1]" ) ,
NewStateSrc : & states . ResourceInstanceObjectSrc {
AttrsJSON : mustMarshalJSONAttrs ( map [ string ] interface { } {
"id" : "foo" ,
"value" : "hello" ,
} ) ,
Status : states . ObjectReady ,
Private : nil ,
} ,
ProviderConfigAddr : mustDefaultRootProvider ( "testing" ) ,
Schema : stacks_testing_provider . TestingResourceSchema ,
} ,
& stackstate . AppliedChangeResourceInstanceObject {
ResourceInstanceObjectAddr : mustAbsResourceInstanceObject ( "component.parent.testing_resource.data" ) ,
NewStateSrc : & states . ResourceInstanceObjectSrc {
AttrsJSON : mustMarshalJSONAttrs ( map [ string ] interface { } {
"id" : "foo" ,
"value" : "hello" ,
} ) ,
Status : states . ObjectReady ,
Private : nil ,
} ,
ProviderConfigAddr : mustDefaultRootProvider ( "testing" ) ,
Schema : stacks_testing_provider . TestingResourceSchema ,
} ,
2025-03-19 05:39:50 -04:00
} ,
} ,
2025-05-16 04:10:47 -04:00
"missing config resource" : {
path : filepath . Join ( "for-stacks-migrate" , "with-nested-module" ) ,
state : func ( ss * states . SyncState ) {
ss . SetResourceInstanceCurrent (
addrs . Resource {
Mode : addrs . ManagedResourceMode ,
Type : "testing_resource" ,
Name : "data" ,
} . Instance ( addrs . NoKey ) . Absolute ( addrs . RootModuleInstance ) ,
& states . ResourceInstanceObjectSrc {
Status : states . ObjectReady ,
AttrsJSON : mustMarshalJSONAttrs ( map [ string ] interface { } {
"id" : "foo" ,
"value" : "hello" ,
} ) ,
} ,
mustDefaultRootProvider ( "testing" ) ,
)
ss . SetResourceInstanceCurrent (
addrs . Resource {
Mode : addrs . ManagedResourceMode ,
Type : "testing_resource" ,
Name : "another" ,
} . Instance ( addrs . IntKey ( 0 ) ) . Absolute ( addrs . RootModuleInstance ) ,
& states . ResourceInstanceObjectSrc {
Status : states . ObjectReady ,
AttrsJSON : mustMarshalJSONAttrs ( map [ string ] interface { } {
"id" : "foo" ,
"value" : "hello" ,
} ) ,
} ,
mustDefaultRootProvider ( "testing" ) ,
)
ss . SetResourceInstanceCurrent (
addrs . Resource {
Mode : addrs . ManagedResourceMode ,
Type : "testing_resource" ,
Name : "another" ,
} . Instance ( addrs . IntKey ( 1 ) ) . Absolute ( addrs . RootModuleInstance ) ,
& states . ResourceInstanceObjectSrc {
Status : states . ObjectReady ,
AttrsJSON : mustMarshalJSONAttrs ( map [ string ] interface { } {
"id" : "foo" ,
"value" : "hello" ,
} ) ,
} ,
mustDefaultRootProvider ( "testing" ) ,
)
ss . SetResourceInstanceCurrent (
addrs . Resource {
Mode : addrs . ManagedResourceMode ,
Type : "testing_resource" ,
Name : "for_child" ,
} . Instance ( addrs . NoKey ) . Absolute ( addrs . RootModuleInstance ) ,
& states . ResourceInstanceObjectSrc {
Status : states . ObjectReady ,
AttrsJSON : mustMarshalJSONAttrs ( map [ string ] interface { } {
"id" : "foo" ,
"value" : "hello" ,
} ) ,
} ,
mustDefaultRootProvider ( "testing" ) ,
)
} ,
resources : map [ string ] string {
"testing_resource.data" : "component.parent" ,
"testing_resource.another[0]" : "component.parent" ,
"testing_resource.another[1]" : "component.parent" ,
"testing_resource.for_child" : "component.child" ,
} ,
expected : [ ] stackstate . AppliedChange {
& stackstate . AppliedChangeComponentInstance {
ComponentAddr : mustAbsComponent ( "component.child" ) ,
ComponentInstanceAddr : mustAbsComponentInstance ( "component.child" ) ,
OutputValues : map [ addrs . OutputValue ] cty . Value {
{ Name : "id" } : cty . DynamicVal ,
} ,
InputVariables : map [ addrs . InputVariable ] cty . Value {
{ Name : "id" } : cty . DynamicVal ,
{ Name : "input" } : cty . DynamicVal ,
} ,
Dependencies : collections . NewSet ( mustAbsComponent ( "component.parent" ) ) ,
} ,
& stackstate . AppliedChangeComponentInstance {
ComponentAddr : mustAbsComponent ( "component.parent" ) ,
ComponentInstanceAddr : mustAbsComponentInstance ( "component.parent" ) ,
OutputValues : map [ addrs . OutputValue ] cty . Value {
{ Name : "id" } : cty . DynamicVal ,
} ,
InputVariables : map [ addrs . InputVariable ] cty . Value {
{ Name : "id" } : cty . DynamicVal ,
{ Name : "input" } : cty . DynamicVal ,
} ,
Dependents : collections . NewSet ( mustAbsComponent ( "component.child" ) ) ,
} ,
& stackstate . AppliedChangeResourceInstanceObject {
ResourceInstanceObjectAddr : mustAbsResourceInstanceObject ( "component.parent.testing_resource.another[0]" ) ,
NewStateSrc : & states . ResourceInstanceObjectSrc {
AttrsJSON : mustMarshalJSONAttrs ( map [ string ] interface { } {
"id" : "foo" ,
"value" : "hello" ,
} ) ,
Status : states . ObjectReady ,
Private : nil ,
} ,
ProviderConfigAddr : mustDefaultRootProvider ( "testing" ) ,
Schema : stacks_testing_provider . TestingResourceSchema ,
} ,
& stackstate . AppliedChangeResourceInstanceObject {
ResourceInstanceObjectAddr : mustAbsResourceInstanceObject ( "component.parent.testing_resource.another[1]" ) ,
NewStateSrc : & states . ResourceInstanceObjectSrc {
AttrsJSON : mustMarshalJSONAttrs ( map [ string ] interface { } {
"id" : "foo" ,
"value" : "hello" ,
} ) ,
Status : states . ObjectReady ,
Private : nil ,
} ,
ProviderConfigAddr : mustDefaultRootProvider ( "testing" ) ,
Schema : stacks_testing_provider . TestingResourceSchema ,
} ,
& stackstate . AppliedChangeResourceInstanceObject {
ResourceInstanceObjectAddr : mustAbsResourceInstanceObject ( "component.parent.testing_resource.data" ) ,
NewStateSrc : & states . ResourceInstanceObjectSrc {
AttrsJSON : mustMarshalJSONAttrs ( map [ string ] interface { } {
"id" : "foo" ,
"value" : "hello" ,
} ) ,
Status : states . ObjectReady ,
Private : nil ,
} ,
ProviderConfigAddr : mustDefaultRootProvider ( "testing" ) ,
Schema : stacks_testing_provider . TestingResourceSchema ,
} ,
2025-03-19 05:39:50 -04:00
} ,
2025-05-16 04:10:47 -04:00
expectedDiags : tfdiags . Diagnostics { } . Append ( & hcl . Diagnostic {
Severity : hcl . DiagError ,
2025-08-25 04:10:21 -04:00
Summary : "Resource mapped to non-existent target" ,
Detail : "Could not migrate resource \"testing_resource.for_child\". Target resource \"testing_resource.for_child\" not found in component \"component.child\"." ,
2025-05-16 04:10:47 -04:00
} ) ,
} ,
"missing mapping for state resource" : {
path : filepath . Join ( "for-stacks-migrate" , "with-nested-module" ) ,
state : func ( ss * states . SyncState ) {
ss . SetResourceInstanceCurrent (
addrs . Resource {
Mode : addrs . ManagedResourceMode ,
Type : "testing_resource" ,
Name : "data" ,
} . Instance ( addrs . NoKey ) . Absolute ( addrs . RootModuleInstance ) ,
& states . ResourceInstanceObjectSrc {
Status : states . ObjectReady ,
AttrsJSON : mustMarshalJSONAttrs ( map [ string ] interface { } {
"id" : "foo" ,
"value" : "hello" ,
} ) ,
} ,
mustDefaultRootProvider ( "testing" ) ,
)
ss . SetResourceInstanceCurrent (
addrs . Resource {
Mode : addrs . ManagedResourceMode ,
Type : "testing_resource" ,
Name : "another" ,
} . Instance ( addrs . IntKey ( 0 ) ) . Absolute ( addrs . RootModuleInstance ) ,
& states . ResourceInstanceObjectSrc {
Status : states . ObjectReady ,
AttrsJSON : mustMarshalJSONAttrs ( map [ string ] interface { } {
"id" : "foo" ,
"value" : "hello" ,
} ) ,
} ,
mustDefaultRootProvider ( "testing" ) ,
)
ss . SetResourceInstanceCurrent (
addrs . Resource {
Mode : addrs . ManagedResourceMode ,
Type : "testing_resource" ,
Name : "another" ,
} . Instance ( addrs . IntKey ( 1 ) ) . Absolute ( addrs . RootModuleInstance ) ,
& states . ResourceInstanceObjectSrc {
Status : states . ObjectReady ,
AttrsJSON : mustMarshalJSONAttrs ( map [ string ] interface { } {
"id" : "foo" ,
"value" : "hello" ,
} ) ,
} ,
mustDefaultRootProvider ( "testing" ) ,
)
ss . SetResourceInstanceCurrent (
addrs . Resource {
Mode : addrs . ManagedResourceMode ,
Type : "testing_resource" ,
Name : "for_child" ,
} . Instance ( addrs . NoKey ) . Absolute ( addrs . RootModuleInstance ) ,
& states . ResourceInstanceObjectSrc {
AttrsJSON : mustMarshalJSONAttrs ( map [ string ] interface { } {
"id" : "foo" ,
"value" : "hello" ,
} ) ,
} ,
mustDefaultRootProvider ( "testing" ) ,
)
} ,
resources : map [ string ] string {
"testing_resource.data" : "component.parent" ,
"testing_resource.another[0]" : "component.parent" ,
"testing_resource.another[1]" : "component.parent" ,
} ,
modules : map [ string ] string { } ,
expected : [ ] stackstate . AppliedChange {
// this component has a dependent "child", but that other component
// is not present in the modules mapping, so it is not included here
& stackstate . AppliedChangeComponentInstance {
ComponentAddr : mustAbsComponent ( "component.parent" ) ,
ComponentInstanceAddr : mustAbsComponentInstance ( "component.parent" ) ,
OutputValues : map [ addrs . OutputValue ] cty . Value {
{ Name : "id" } : cty . DynamicVal ,
} ,
InputVariables : map [ addrs . InputVariable ] cty . Value {
{ Name : "id" } : cty . DynamicVal ,
{ Name : "input" } : cty . DynamicVal ,
} ,
} ,
& stackstate . AppliedChangeResourceInstanceObject {
ResourceInstanceObjectAddr : mustAbsResourceInstanceObject ( "component.parent.testing_resource.another[0]" ) ,
NewStateSrc : & states . ResourceInstanceObjectSrc {
AttrsJSON : mustMarshalJSONAttrs ( map [ string ] interface { } {
"id" : "foo" ,
"value" : "hello" ,
} ) ,
Status : states . ObjectReady ,
Private : nil ,
} ,
ProviderConfigAddr : mustDefaultRootProvider ( "testing" ) ,
Schema : stacks_testing_provider . TestingResourceSchema ,
} ,
& stackstate . AppliedChangeResourceInstanceObject {
ResourceInstanceObjectAddr : mustAbsResourceInstanceObject ( "component.parent.testing_resource.another[1]" ) ,
NewStateSrc : & states . ResourceInstanceObjectSrc {
AttrsJSON : mustMarshalJSONAttrs ( map [ string ] interface { } {
"id" : "foo" ,
"value" : "hello" ,
} ) ,
Status : states . ObjectReady ,
Private : nil ,
} ,
ProviderConfigAddr : mustDefaultRootProvider ( "testing" ) ,
Schema : stacks_testing_provider . TestingResourceSchema ,
} ,
& stackstate . AppliedChangeResourceInstanceObject {
ResourceInstanceObjectAddr : mustAbsResourceInstanceObject ( "component.parent.testing_resource.data" ) ,
NewStateSrc : & states . ResourceInstanceObjectSrc {
AttrsJSON : mustMarshalJSONAttrs ( map [ string ] interface { } {
"id" : "foo" ,
"value" : "hello" ,
} ) ,
Status : states . ObjectReady ,
Private : nil ,
} ,
ProviderConfigAddr : mustDefaultRootProvider ( "testing" ) ,
Schema : stacks_testing_provider . TestingResourceSchema ,
} ,
2025-03-19 05:39:50 -04:00
} ,
2025-05-16 04:10:47 -04:00
expectedDiags : tfdiags . Diagnostics { } . Append ( & hcl . Diagnostic {
Severity : hcl . DiagError ,
Summary : "Resource not found" ,
2025-08-25 04:10:21 -04:00
Detail : "Resource \"testing_resource.for_child\" exists in state, but was not included in any provided mapping." ,
2025-05-16 04:10:47 -04:00
} ) ,
} ,
"config depends on" : {
path : filepath . Join ( "for-stacks-migrate" , "with-depends-on" ) ,
state : func ( ss * states . SyncState ) {
ss . SetResourceInstanceCurrent (
addrs . Resource {
Mode : addrs . ManagedResourceMode ,
Type : "testing_resource" ,
Name : "data" ,
} . Instance ( addrs . NoKey ) . Absolute ( addrs . RootModuleInstance ) ,
& states . ResourceInstanceObjectSrc {
Status : states . ObjectReady ,
AttrsJSON : mustMarshalJSONAttrs ( map [ string ] interface { } {
"id" : "foo" ,
"value" : "depends_test" ,
} ) ,
} ,
mustDefaultRootProvider ( "testing" ) ,
)
ss . SetResourceInstanceCurrent (
addrs . Resource {
Mode : addrs . ManagedResourceMode ,
Type : "testing_resource" ,
Name : "second" ,
} . Instance ( addrs . NoKey ) . Absolute ( addrs . RootModuleInstance ) ,
& states . ResourceInstanceObjectSrc {
Status : states . ObjectReady ,
AttrsJSON : mustMarshalJSONAttrs ( map [ string ] interface { } {
"id" : "foo" ,
"value" : "depends_test" ,
} ) ,
} ,
mustDefaultRootProvider ( "testing" ) ,
)
ss . SetResourceInstanceCurrent (
addrs . Resource {
Mode : addrs . ManagedResourceMode ,
Type : "testing_resource" ,
Name : "third" ,
} . Instance ( addrs . NoKey ) . Absolute ( addrs . RootModuleInstance ) ,
& states . ResourceInstanceObjectSrc {
Status : states . ObjectReady ,
AttrsJSON : mustMarshalJSONAttrs ( map [ string ] interface { } {
"id" : "foo" ,
"value" : "depends_test" ,
} ) ,
} ,
mustDefaultRootProvider ( "testing" ) ,
)
} ,
resources : map [ string ] string {
"testing_resource.data" : "component.first" ,
"testing_resource.second" : "component.second" ,
"testing_resource.third" : "component.second" ,
} ,
modules : map [ string ] string { } ,
expected : [ ] stackstate . AppliedChange {
& stackstate . AppliedChangeComponentInstance {
ComponentAddr : mustAbsComponent ( "component.first" ) ,
ComponentInstanceAddr : mustAbsComponentInstance ( "component.first" ) ,
OutputValues : make ( map [ addrs . OutputValue ] cty . Value ) ,
InputVariables : map [ addrs . InputVariable ] cty . Value {
{ Name : "input" } : cty . DynamicVal ,
{ Name : "id" } : cty . DynamicVal ,
} ,
Dependents : collections . NewSet ( mustAbsComponent ( "component.second" ) ) ,
} ,
& stackstate . AppliedChangeResourceInstanceObject {
ResourceInstanceObjectAddr : mustAbsResourceInstanceObject ( "component.first.testing_resource.data" ) ,
NewStateSrc : & states . ResourceInstanceObjectSrc {
AttrsJSON : mustMarshalJSONAttrs ( map [ string ] interface { } {
"id" : "foo" ,
"value" : "depends_test" ,
} ) ,
Status : states . ObjectReady ,
Private : nil ,
} ,
ProviderConfigAddr : mustDefaultRootProvider ( "testing" ) ,
Schema : stacks_testing_provider . TestingResourceSchema ,
} ,
& stackstate . AppliedChangeComponentInstance {
ComponentAddr : mustAbsComponent ( "component.second" ) ,
ComponentInstanceAddr : mustAbsComponentInstance ( "component.second" ) ,
OutputValues : make ( map [ addrs . OutputValue ] cty . Value ) ,
InputVariables : map [ addrs . InputVariable ] cty . Value {
{ Name : "input" } : cty . DynamicVal ,
{ Name : "id" } : cty . DynamicVal ,
} ,
Dependencies : collections . NewSet ( mustAbsComponent ( "component.first" ) ) ,
} ,
& stackstate . AppliedChangeResourceInstanceObject {
ResourceInstanceObjectAddr : mustAbsResourceInstanceObject ( "component.second.testing_resource.second" ) ,
NewStateSrc : & states . ResourceInstanceObjectSrc {
AttrsJSON : mustMarshalJSONAttrs ( map [ string ] interface { } {
"id" : "foo" ,
"value" : "depends_test" ,
} ) ,
Status : states . ObjectReady ,
Private : nil ,
} ,
ProviderConfigAddr : mustDefaultRootProvider ( "testing" ) ,
Schema : stacks_testing_provider . TestingResourceSchema ,
} ,
& stackstate . AppliedChangeResourceInstanceObject {
ResourceInstanceObjectAddr : mustAbsResourceInstanceObject ( "component.second.testing_resource.third" ) ,
NewStateSrc : & states . ResourceInstanceObjectSrc {
AttrsJSON : mustMarshalJSONAttrs ( map [ string ] interface { } {
"id" : "foo" ,
"value" : "depends_test" ,
} ) ,
Status : states . ObjectReady ,
Private : nil ,
} ,
ProviderConfigAddr : mustDefaultRootProvider ( "testing" ) ,
Schema : stacks_testing_provider . TestingResourceSchema ,
} ,
2025-03-19 05:39:50 -04:00
} ,
2025-05-16 04:10:47 -04:00
expectedDiags : tfdiags . Diagnostics { } . Append ( ) ,
} ,
"unsupported component ref" : {
path : filepath . Join ( "for-stacks-migrate" , "with-depends-on" ) ,
state : func ( ss * states . SyncState ) {
ss . SetResourceInstanceCurrent (
addrs . Resource {
Mode : addrs . ManagedResourceMode ,
Type : "testing_resource" ,
Name : "data" ,
} . Instance ( addrs . NoKey ) . Absolute ( addrs . RootModuleInstance ) ,
& states . ResourceInstanceObjectSrc {
Status : states . ObjectReady ,
AttrsJSON : mustMarshalJSONAttrs ( map [ string ] interface { } {
"id" : "foo" ,
"value" : "depends_test" ,
} ) ,
} ,
mustDefaultRootProvider ( "testing" ) ,
)
ss . SetResourceInstanceCurrent (
addrs . Resource {
Mode : addrs . ManagedResourceMode ,
Type : "testing_resource" ,
Name : "second" ,
} . Instance ( addrs . NoKey ) . Absolute ( addrs . RootModuleInstance ) ,
& states . ResourceInstanceObjectSrc {
Status : states . ObjectReady ,
AttrsJSON : mustMarshalJSONAttrs ( map [ string ] interface { } {
"id" : "foo" ,
"value" : "depends_test" ,
} ) ,
} ,
mustDefaultRootProvider ( "testing" ) ,
)
ss . SetResourceInstanceCurrent (
addrs . Resource {
Mode : addrs . ManagedResourceMode ,
Type : "testing_resource" ,
Name : "third" ,
} . Instance ( addrs . NoKey ) . Absolute ( addrs . RootModuleInstance ) ,
& states . ResourceInstanceObjectSrc {
Status : states . ObjectReady ,
AttrsJSON : mustMarshalJSONAttrs ( map [ string ] interface { } {
"id" : "foo" ,
"value" : "depends_test" ,
} ) ,
} ,
mustDefaultRootProvider ( "testing" ) ,
)
} ,
resources : map [ string ] string {
"testing_resource.data" : "component.first" ,
"testing_resource.second" : "component.second" ,
"testing_resource.third" : "stack.embedded.component.self" ,
} ,
modules : map [ string ] string { } ,
expected : [ ] stackstate . AppliedChange {
& stackstate . AppliedChangeComponentInstance {
ComponentAddr : mustAbsComponent ( "component.first" ) ,
ComponentInstanceAddr : mustAbsComponentInstance ( "component.first" ) ,
OutputValues : make ( map [ addrs . OutputValue ] cty . Value ) ,
InputVariables : map [ addrs . InputVariable ] cty . Value {
{ Name : "input" } : cty . DynamicVal ,
{ Name : "id" } : cty . DynamicVal ,
} ,
Dependents : collections . NewSet ( mustAbsComponent ( "component.second" ) ) ,
} ,
& stackstate . AppliedChangeResourceInstanceObject {
ResourceInstanceObjectAddr : mustAbsResourceInstanceObject ( "component.first.testing_resource.data" ) ,
NewStateSrc : & states . ResourceInstanceObjectSrc {
AttrsJSON : mustMarshalJSONAttrs ( map [ string ] interface { } {
"id" : "foo" ,
"value" : "depends_test" ,
} ) ,
Status : states . ObjectReady ,
Private : nil ,
} ,
ProviderConfigAddr : mustDefaultRootProvider ( "testing" ) ,
Schema : stacks_testing_provider . TestingResourceSchema ,
} ,
& stackstate . AppliedChangeComponentInstance {
ComponentAddr : mustAbsComponent ( "component.second" ) ,
ComponentInstanceAddr : mustAbsComponentInstance ( "component.second" ) ,
OutputValues : make ( map [ addrs . OutputValue ] cty . Value ) ,
InputVariables : map [ addrs . InputVariable ] cty . Value {
{ Name : "input" } : cty . DynamicVal ,
{ Name : "id" } : cty . DynamicVal ,
} ,
Dependencies : collections . NewSet ( mustAbsComponent ( "component.first" ) ) ,
} ,
& stackstate . AppliedChangeResourceInstanceObject {
ResourceInstanceObjectAddr : mustAbsResourceInstanceObject ( "component.second.testing_resource.second" ) ,
NewStateSrc : & states . ResourceInstanceObjectSrc {
AttrsJSON : mustMarshalJSONAttrs ( map [ string ] interface { } {
"id" : "foo" ,
"value" : "depends_test" ,
} ) ,
Status : states . ObjectReady ,
Private : nil ,
} ,
ProviderConfigAddr : mustDefaultRootProvider ( "testing" ) ,
Schema : stacks_testing_provider . TestingResourceSchema ,
} ,
2025-03-19 05:39:50 -04:00
} ,
2025-05-16 04:10:47 -04:00
expectedDiags : tfdiags . Diagnostics { } . Append ( & hcl . Diagnostic {
Severity : hcl . DiagError ,
Summary : "Invalid component instance" ,
Detail : "Only root component instances are allowed, got \"stack.embedded.component.self\"" ,
} ) ,
} ,
"child module as component source" : {
path : filepath . Join ( "for-stacks-migrate" , "child-module-as-component-source" ) ,
state : func ( ss * states . SyncState ) {
ss . SetResourceInstanceCurrent (
addrs . Resource {
Mode : addrs . ManagedResourceMode ,
Type : "testing_resource" ,
Name : "root_id" ,
} . Instance ( addrs . NoKey ) . Absolute ( addrs . RootModuleInstance ) ,
& states . ResourceInstanceObjectSrc {
Status : states . ObjectReady ,
AttrsJSON : mustMarshalJSONAttrs ( map [ string ] interface { } {
"id" : "root_id" ,
"value" : "root_output" ,
} ) ,
} ,
mustDefaultRootProvider ( "testing" ) ,
)
childProv := mustDefaultRootProvider ( "testing" )
childProv . Module = addrs . Module { "child_module" }
ss . SetResourceInstanceCurrent (
addrs . Resource {
Mode : addrs . ManagedResourceMode ,
Type : "testing_resource" ,
Name : "child_data" ,
} . Instance ( addrs . NoKey ) . Absolute ( addrs . ModuleInstance {
{
Name : "child_module" ,
} ,
} ) ,
& states . ResourceInstanceObjectSrc {
Status : states . ObjectReady ,
AttrsJSON : mustMarshalJSONAttrs ( map [ string ] interface { } {
"id" : "child_data" ,
"value" : "child_output" ,
} ) ,
} ,
childProv ,
)
} ,
resources : map [ string ] string {
"testing_resource.root_id" : "component.self" ,
"testing_resource.child_data" : "component.self" , // this should just be ignored
} ,
modules : map [ string ] string {
"child_module" : "triage" ,
} ,
expected : [ ] stackstate . AppliedChange {
& stackstate . AppliedChangeComponentInstance {
ComponentAddr : mustAbsComponent ( "component.self" ) ,
ComponentInstanceAddr : mustAbsComponentInstance ( "component.self" ) ,
OutputValues : map [ addrs . OutputValue ] cty . Value { } ,
InputVariables : map [ addrs . InputVariable ] cty . Value { } ,
} ,
& stackstate . AppliedChangeResourceInstanceObject {
ResourceInstanceObjectAddr : mustAbsResourceInstanceObject ( "component.self.testing_resource.root_id" ) ,
NewStateSrc : & states . ResourceInstanceObjectSrc {
AttrsJSON : mustMarshalJSONAttrs ( map [ string ] interface { } {
"id" : "root_id" ,
"value" : "root_output" ,
} ) ,
Status : states . ObjectReady ,
Private : nil ,
} ,
ProviderConfigAddr : mustDefaultRootProvider ( "testing" ) ,
Schema : stacks_testing_provider . TestingResourceSchema ,
} ,
& stackstate . AppliedChangeComponentInstance {
ComponentAddr : mustAbsComponent ( "component.triage" ) ,
ComponentInstanceAddr : mustAbsComponentInstance ( "component.triage" ) ,
OutputValues : map [ addrs . OutputValue ] cty . Value { } ,
InputVariables : map [ addrs . InputVariable ] cty . Value {
addrs . InputVariable { Name : "input" } : cty . DynamicVal ,
} ,
} ,
& stackstate . AppliedChangeResourceInstanceObject {
ResourceInstanceObjectAddr : mustAbsResourceInstanceObject ( "component.triage.testing_resource.child_data" ) ,
NewStateSrc : & states . ResourceInstanceObjectSrc {
AttrsJSON : mustMarshalJSONAttrs ( map [ string ] interface { } {
"id" : "child_data" ,
"value" : "child_output" ,
} ) ,
Status : states . ObjectReady ,
Private : nil ,
} ,
ProviderConfigAddr : mustDefaultRootProvider ( "testing" ) ,
Schema : stacks_testing_provider . TestingResourceSchema ,
} ,
2025-03-19 05:39:50 -04:00
} ,
} ,
2025-05-16 04:10:47 -04:00
"unclaimed resources fall into modules" : {
path : filepath . Join ( "for-stacks-migrate" , "multiple-components" ) ,
state : func ( ss * states . SyncState ) {
ss . SetResourceInstanceCurrent (
addrs . Resource {
Mode : addrs . ManagedResourceMode ,
Type : "testing_resource" ,
Name : "one" ,
} . Instance ( addrs . NoKey ) . Absolute ( addrs . ModuleInstance {
{
Name : "self" ,
} ,
} ) ,
& states . ResourceInstanceObjectSrc {
Status : states . ObjectReady ,
AttrsJSON : mustMarshalJSONAttrs ( map [ string ] interface { } {
"id" : "one" ,
"value" : "one" ,
} ) ,
} ,
mustDefaultRootProvider ( "testing" ) ,
)
ss . SetResourceInstanceCurrent (
addrs . Resource {
Mode : addrs . ManagedResourceMode ,
Type : "testing_resource" ,
Name : "resource" ,
} . Instance ( addrs . NoKey ) . Absolute ( addrs . ModuleInstance {
{
Name : "self" ,
} ,
} ) ,
& states . ResourceInstanceObjectSrc {
Status : states . ObjectReady ,
AttrsJSON : mustMarshalJSONAttrs ( map [ string ] interface { } {
"id" : "two" ,
"value" : "two" ,
} ) ,
} ,
mustDefaultRootProvider ( "testing" ) ,
)
} ,
resources : map [ string ] string {
// this specific resource goes to component.one
"module.self.testing_resource.one" : "component.one.testing_resource.resource" ,
} ,
modules : map [ string ] string {
"self" : "two" , // all other resources go to component.two
} ,
expected : [ ] stackstate . AppliedChange {
& stackstate . AppliedChangeComponentInstance {
ComponentAddr : mustAbsComponent ( "component.one" ) ,
ComponentInstanceAddr : mustAbsComponentInstance ( "component.one" ) ,
OutputValues : map [ addrs . OutputValue ] cty . Value { } ,
InputVariables : map [ addrs . InputVariable ] cty . Value { } ,
} ,
& stackstate . AppliedChangeResourceInstanceObject {
ResourceInstanceObjectAddr : mustAbsResourceInstanceObject ( "component.one.testing_resource.resource" ) ,
NewStateSrc : & states . ResourceInstanceObjectSrc {
AttrsJSON : mustMarshalJSONAttrs ( map [ string ] interface { } {
"id" : "one" ,
"value" : "one" ,
} ) ,
Status : states . ObjectReady ,
Private : nil ,
} ,
ProviderConfigAddr : mustDefaultRootProvider ( "testing" ) ,
Schema : stacks_testing_provider . TestingResourceSchema ,
} ,
& stackstate . AppliedChangeComponentInstance {
ComponentAddr : mustAbsComponent ( "component.two" ) ,
ComponentInstanceAddr : mustAbsComponentInstance ( "component.two" ) ,
OutputValues : map [ addrs . OutputValue ] cty . Value { } ,
InputVariables : map [ addrs . InputVariable ] cty . Value { } ,
} ,
& stackstate . AppliedChangeResourceInstanceObject {
ResourceInstanceObjectAddr : mustAbsResourceInstanceObject ( "component.two.testing_resource.resource" ) ,
NewStateSrc : & states . ResourceInstanceObjectSrc {
AttrsJSON : mustMarshalJSONAttrs ( map [ string ] interface { } {
"id" : "two" ,
"value" : "two" ,
} ) ,
Status : states . ObjectReady ,
Private : nil ,
} ,
ProviderConfigAddr : mustDefaultRootProvider ( "testing" ) ,
Schema : stacks_testing_provider . TestingResourceSchema ,
} ,
2025-03-19 05:39:50 -04:00
} ,
} ,
2025-08-25 04:10:21 -04:00
"single component" : {
path : filepath . Join ( "for-stacks-migrate" , "single-component" ) ,
state : func ( ss * states . SyncState ) {
ss . SetResourceInstanceCurrent (
addrs . Resource {
Mode : addrs . ManagedResourceMode ,
Type : "testing_resource" ,
Name : "one" ,
} . Instance ( addrs . NoKey ) . Absolute ( addrs . RootModuleInstance ) ,
& states . ResourceInstanceObjectSrc {
Status : states . ObjectReady ,
AttrsJSON : mustMarshalJSONAttrs ( map [ string ] interface { } {
"id" : "one" ,
"value" : "one" ,
} ) ,
} ,
mustDefaultRootProvider ( "testing" ) ,
)
ss . SetResourceInstanceCurrent (
addrs . Resource {
Mode : addrs . ManagedResourceMode ,
Type : "testing_resource" ,
Name : "two" ,
} . Instance ( addrs . NoKey ) . Absolute ( addrs . ModuleInstance {
{
Name : "two" ,
} ,
} ) ,
& states . ResourceInstanceObjectSrc {
Status : states . ObjectReady ,
AttrsJSON : mustMarshalJSONAttrs ( map [ string ] interface { } {
"id" : "two" ,
"value" : "two" ,
} ) ,
} ,
mustDefaultRootProvider ( "testing" ) ,
)
ss . SetResourceInstanceCurrent (
addrs . Resource {
Mode : addrs . ManagedResourceMode ,
Type : "testing_resource" ,
Name : "three" ,
} . Instance ( addrs . NoKey ) . Absolute ( addrs . ModuleInstance {
{
Name : "three" ,
} ,
} ) ,
& states . ResourceInstanceObjectSrc {
Status : states . ObjectReady ,
AttrsJSON : mustMarshalJSONAttrs ( map [ string ] interface { } {
"id" : "three" ,
"value" : "three" ,
} ) ,
} ,
mustDefaultRootProvider ( "testing" ) ,
)
} ,
resources : map [ string ] string {
"testing_resource.one" : "component.single.testing_resource.one" ,
} ,
modules : map [ string ] string {
"two" : "single" ,
"three" : "single" ,
} , expected : [ ] stackstate . AppliedChange {
& stackstate . AppliedChangeComponentInstance {
ComponentAddr : mustAbsComponent ( "component.single" ) ,
ComponentInstanceAddr : mustAbsComponentInstance ( "component.single" ) ,
OutputValues : map [ addrs . OutputValue ] cty . Value { } ,
InputVariables : map [ addrs . InputVariable ] cty . Value { } ,
} ,
& stackstate . AppliedChangeResourceInstanceObject {
ResourceInstanceObjectAddr : mustAbsResourceInstanceObject ( "component.single.testing_resource.one" ) ,
NewStateSrc : & states . ResourceInstanceObjectSrc {
AttrsJSON : mustMarshalJSONAttrs ( map [ string ] interface { } {
"id" : "one" ,
"value" : "one" ,
} ) ,
Status : states . ObjectReady ,
Private : nil ,
} ,
ProviderConfigAddr : mustDefaultRootProvider ( "testing" ) ,
Schema : stacks_testing_provider . TestingResourceSchema ,
} ,
& stackstate . AppliedChangeResourceInstanceObject {
ResourceInstanceObjectAddr : mustAbsResourceInstanceObject ( "component.single.testing_resource.three" ) ,
NewStateSrc : & states . ResourceInstanceObjectSrc {
AttrsJSON : mustMarshalJSONAttrs ( map [ string ] interface { } {
"id" : "three" ,
"value" : "three" ,
} ) ,
Status : states . ObjectReady ,
Private : nil ,
} ,
ProviderConfigAddr : mustDefaultRootProvider ( "testing" ) ,
Schema : stacks_testing_provider . TestingResourceSchema ,
} ,
& stackstate . AppliedChangeResourceInstanceObject {
ResourceInstanceObjectAddr : mustAbsResourceInstanceObject ( "component.single.testing_resource.two" ) ,
NewStateSrc : & states . ResourceInstanceObjectSrc {
AttrsJSON : mustMarshalJSONAttrs ( map [ string ] interface { } {
"id" : "two" ,
"value" : "two" ,
} ) ,
Status : states . ObjectReady ,
Private : nil ,
} ,
ProviderConfigAddr : mustDefaultRootProvider ( "testing" ) ,
Schema : stacks_testing_provider . TestingResourceSchema ,
} ,
} ,
} ,
2025-03-19 05:39:50 -04:00
}
2025-05-16 04:10:47 -04:00
for name , tc := range tcs {
t . Run ( name , func ( t * testing . T ) {
cfg := loadMainBundleConfigForTest ( t , tc . path )
2025-03-19 05:39:50 -04:00
2025-05-16 04:10:47 -04:00
lock := depsfile . NewLocks ( )
lock . SetProvider (
addrs . NewDefaultProvider ( "testing" ) ,
providerreqs . MustParseVersion ( "0.0.0" ) ,
providerreqs . MustParseVersionConstraints ( "=0.0.0" ) ,
providerreqs . PreferredHashes ( [ ] providerreqs . Hash { } ) ,
)
2025-03-19 05:39:50 -04:00
2025-05-16 04:10:47 -04:00
state := states . BuildState ( tc . state )
2025-03-19 05:39:50 -04:00
2025-05-16 04:10:47 -04:00
migration := Migration {
Providers : map [ addrs . Provider ] providers . Factory {
addrs . NewDefaultProvider ( "testing" ) : func ( ) ( providers . Interface , error ) {
return stacks_testing_provider . NewProvider ( t ) , nil
} ,
} ,
PreviousState : state ,
Config : cfg ,
}
2025-03-19 05:39:50 -04:00
2025-05-16 04:10:47 -04:00
var applied [ ] stackstate . AppliedChange
var gotDiags tfdiags . Diagnostics
migration . Migrate ( tc . resources , tc . modules , func ( change stackstate . AppliedChange ) {
applied = append ( applied , change )
} , func ( diagnostic tfdiags . Diagnostic ) {
gotDiags = append ( gotDiags , diagnostic )
} )
sort . SliceStable ( applied , func ( i , j int ) bool {
key := func ( change stackstate . AppliedChange ) string {
switch change := change . ( type ) {
case * stackstate . AppliedChangeComponentInstance :
return change . ComponentInstanceAddr . String ( )
case * stackstate . AppliedChangeResourceInstanceObject :
return change . ResourceInstanceObjectAddr . String ( )
default :
panic ( "unsupported change type" )
2025-03-19 05:39:50 -04:00
}
}
2025-05-16 04:10:47 -04:00
return key ( applied [ i ] ) < key ( applied [ j ] )
} )
if diff := cmp . Diff ( tc . expected , applied , cmp . Options {
ctydebug . CmpOptions ,
collections . CmpOptions ,
cmpopts . IgnoreUnexported ( addrs . InputVariable { } ) ,
cmpopts . IgnoreUnexported ( states . ResourceInstanceObjectSrc { } ) ,
} ) ; len ( diff ) > 0 {
t . Errorf ( "unexpected applied changes:\n%s" , diff )
2025-03-19 05:39:50 -04:00
}
2025-05-16 04:10:47 -04:00
tfdiags . AssertDiagnosticsMatch ( t , gotDiags , tc . expectedDiags )
} )
2025-03-19 05:39:50 -04:00
}
}
func mustMarshalJSONAttrs ( attrs map [ string ] interface { } ) [ ] byte {
jsonAttrs , err := json . Marshal ( attrs )
if err != nil {
panic ( err )
}
return jsonAttrs
}
func mustDefaultRootProvider ( provider string ) addrs . AbsProviderConfig {
return addrs . AbsProviderConfig {
Module : addrs . RootModule ,
Provider : addrs . NewDefaultProvider ( provider ) ,
}
}
func mustAbsResourceInstanceObject ( addr string ) stackaddrs . AbsResourceInstanceObject {
ret , diags := stackaddrs . ParseAbsResourceInstanceObjectStr ( addr )
if len ( diags ) > 0 {
panic ( fmt . Sprintf ( "failed to parse resource instance object address %q: %s" , addr , diags ) )
}
return ret
}
func mustAbsComponentInstance ( addr string ) stackaddrs . AbsComponentInstance {
ret , diags := stackaddrs . ParsePartialComponentInstanceStr ( addr )
if len ( diags ) > 0 {
panic ( fmt . Sprintf ( "failed to parse component instance address %q: %s" , addr , diags ) )
}
return ret
}
func mustAbsComponent ( addr string ) stackaddrs . AbsComponent {
ret , diags := stackaddrs . ParsePartialComponentInstanceStr ( addr )
if len ( diags ) > 0 {
panic ( fmt . Sprintf ( "failed to parse component instance address %q: %s" , addr , diags ) )
}
return stackaddrs . AbsComponent {
Stack : ret . Stack ,
Item : ret . Item . Component ,
}
}
// TODO: Perhaps export this from helper_test instead
func loadMainBundleConfigForTest ( t * testing . T , dirName string ) * stackconfig . Config {
t . Helper ( )
fullSourceAddr := mainBundleSourceAddrStr ( dirName )
return loadConfigForTest ( t , "../stackruntime/testdata/mainbundle" , fullSourceAddr )
}
func mainBundleSourceAddrStr ( dirName string ) string {
return "git::https://example.com/test.git//" + dirName
}
// loadConfigForTest is a test helper that tries to open bundleRoot as a
// source bundle, and then if successful tries to load the given source address
// from it as a stack configuration. If any part of the operation fails then
// it halts execution of the test and doesn't return.
func loadConfigForTest ( t * testing . T , bundleRoot string , configSourceAddr string ) * stackconfig . Config {
t . Helper ( )
sources , err := sourcebundle . OpenDir ( bundleRoot )
if err != nil {
t . Fatalf ( "cannot load source bundle: %s" , err )
}
// We force using remote source addresses here because that avoids
// us having to deal with the extra version constraints argument
// that registry sources require. Exactly what source address type
// we use isn't relevant for tests in this package, since it's
// the sourcebundle package's responsibility to make sure its
// abstraction works for all of the source types.
sourceAddr , err := sourceaddrs . ParseRemoteSource ( configSourceAddr )
if err != nil {
t . Fatalf ( "invalid config source address: %s" , err )
}
cfg , diags := stackconfig . LoadConfigDir ( sourceAddr , sources )
reportDiagnosticsForTest ( t , diags )
return cfg
}
// reportDiagnosticsForTest creates a test log entry for every diagnostic in
// the given diags, and halts the test if any of them are error diagnostics.
func reportDiagnosticsForTest ( t * testing . T , diags tfdiags . Diagnostics ) {
t . Helper ( )
for _ , diag := range diags {
var b strings . Builder
desc := diag . Description ( )
locs := diag . Source ( )
switch sev := diag . Severity ( ) ; sev {
case tfdiags . Error :
b . WriteString ( "Error: " )
case tfdiags . Warning :
b . WriteString ( "Warning: " )
default :
t . Errorf ( "unsupported diagnostic type %s" , sev )
}
b . WriteString ( desc . Summary )
if desc . Address != "" {
b . WriteString ( "\nwith " )
b . WriteString ( desc . Summary )
}
if locs . Subject != nil {
b . WriteString ( "\nat " )
b . WriteString ( locs . Subject . StartString ( ) )
}
if desc . Detail != "" {
b . WriteString ( "\n\n" )
b . WriteString ( desc . Detail )
}
t . Log ( b . String ( ) )
}
if diags . HasErrors ( ) {
t . FailNow ( )
}
}