2024-02-15 04:45:47 -05:00
// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: BUSL-1.1
package stackruntime
import (
"context"
2024-06-21 08:05:26 -04:00
"fmt"
2024-02-15 04:45:47 -05:00
"path"
2024-07-17 02:44:39 -04:00
"path/filepath"
2024-02-15 04:45:47 -05:00
"sort"
2024-09-16 05:36:36 -04:00
"strconv"
2024-06-21 08:05:26 -04:00
"strings"
2024-02-15 04:45:47 -05:00
"testing"
"time"
"github.com/google/go-cmp/cmp"
2024-08-21 15:30:01 -04:00
"github.com/google/go-cmp/cmp/cmpopts"
2024-03-01 20:16:14 -05:00
"github.com/hashicorp/hcl/v2"
2024-02-15 04:45:47 -05:00
"github.com/zclconf/go-cty-debug/ctydebug"
"github.com/zclconf/go-cty/cty"
"github.com/hashicorp/terraform/internal/addrs"
terraformProvider "github.com/hashicorp/terraform/internal/builtin/providers/terraform"
2024-07-01 08:23:17 -04:00
"github.com/hashicorp/terraform/internal/collections"
2024-06-27 10:08:08 -04:00
"github.com/hashicorp/terraform/internal/depsfile"
"github.com/hashicorp/terraform/internal/getproviders/providerreqs"
2025-05-02 16:55:06 -04:00
"github.com/hashicorp/terraform/internal/lang"
2024-02-16 04:46:50 -05:00
"github.com/hashicorp/terraform/internal/lang/marks"
2024-07-23 11:45:07 -04:00
"github.com/hashicorp/terraform/internal/plans"
2024-02-15 04:45:47 -05:00
"github.com/hashicorp/terraform/internal/providers"
"github.com/hashicorp/terraform/internal/stacks/stackaddrs"
"github.com/hashicorp/terraform/internal/stacks/stackplan"
2024-07-01 08:23:17 -04:00
"github.com/hashicorp/terraform/internal/stacks/stackruntime/hooks"
"github.com/hashicorp/terraform/internal/stacks/stackruntime/internal/stackeval"
2024-02-16 04:46:50 -05:00
stacks_testing_provider "github.com/hashicorp/terraform/internal/stacks/stackruntime/testing"
2024-02-15 04:45:47 -05:00
"github.com/hashicorp/terraform/internal/stacks/stackstate"
"github.com/hashicorp/terraform/internal/states"
"github.com/hashicorp/terraform/internal/tfdiags"
2024-08-22 03:28:39 -04:00
"github.com/hashicorp/terraform/version"
2024-02-15 04:45:47 -05:00
)
2024-08-21 15:30:01 -04:00
var changesCmpOpts = cmp . Options {
ctydebug . CmpOptions ,
2025-05-16 04:10:47 -04:00
collections . CmpOptions ,
2024-08-21 15:30:01 -04:00
cmpopts . IgnoreUnexported ( addrs . InputVariable { } ) ,
cmpopts . IgnoreUnexported ( states . ResourceInstanceObjectSrc { } ) ,
}
2024-09-16 05:36:36 -04:00
// TestApply uses a generic framework for running apply integration tests
// against Stacks. Generally, new tests should be added into this function
// rather than copying the large amount of duplicate code from the other
// tests in this file.
//
// If you are editing other tests in this file, please consider moving them
// into this test function so they can reuse the shared setup and boilerplate
// code managing the boring parts of the test.
func TestApply ( t * testing . T ) {
fakePlanTimestamp , err := time . Parse ( time . RFC3339 , "2021-01-01T00:00:00Z" )
if err != nil {
t . Fatal ( err )
}
tcs := map [ string ] struct {
path string
2025-10-31 04:52:48 -04:00
skip bool
2024-09-16 05:36:36 -04:00
state * stackstate . State
store * stacks_testing_provider . ResourceStore
cycles [ ] TestCycle
} {
2025-06-18 11:02:27 -04:00
"built-in provider used not present in required" : {
path : "with-built-in-provider" ,
cycles : [ ] TestCycle {
{ } , // plan, apply -> no diags
} ,
} ,
"built-in provider used and explicitly defined in required providers" : {
path : "with-built-in-provider-explicitly-defined" ,
cycles : [ ] TestCycle {
{ } , // plan, apply -> no diags
} ,
} ,
2024-09-16 05:45:19 -04:00
"creating inputs and outputs" : {
2024-09-16 05:36:36 -04:00
path : "component-input-output" ,
cycles : [ ] TestCycle {
{
planInputs : map [ string ] cty . Value {
"value" : cty . StringVal ( "foo" ) ,
} ,
wantPlannedChanges : [ ] stackplan . PlannedChange {
& stackplan . PlannedChangeApplyable {
Applyable : true ,
} ,
& stackplan . PlannedChangeHeader {
TerraformVersion : version . SemVer ,
} ,
& stackplan . PlannedChangeOutputValue {
Addr : mustStackOutputValue ( "value" ) ,
Action : plans . Create ,
Before : cty . NullVal ( cty . DynamicPseudoType ) ,
After : cty . StringVal ( "foo" ) ,
} ,
& stackplan . PlannedChangePlannedTimestamp {
PlannedTimestamp : fakePlanTimestamp ,
} ,
& stackplan . PlannedChangeRootInputValue {
2024-09-16 05:45:19 -04:00
Addr : mustStackInputVariable ( "value" ) ,
Action : plans . Create ,
Before : cty . NullVal ( cty . DynamicPseudoType ) ,
After : cty . StringVal ( "foo" ) ,
2024-09-16 05:36:36 -04:00
} ,
} ,
wantAppliedChanges : [ ] stackstate . AppliedChange {
& stackstate . AppliedChangeOutputValue {
Addr : mustStackOutputValue ( "value" ) ,
Value : cty . StringVal ( "foo" ) ,
} ,
2024-09-16 05:45:19 -04:00
& stackstate . AppliedChangeInputVariable {
Addr : mustStackInputVariable ( "value" ) ,
Value : cty . StringVal ( "foo" ) ,
} ,
2024-09-16 05:36:36 -04:00
} ,
} ,
} ,
} ,
2024-09-16 05:45:19 -04:00
"updating inputs and outputs" : {
2024-09-16 05:36:36 -04:00
path : "component-input-output" ,
cycles : [ ] TestCycle {
{
planInputs : map [ string ] cty . Value {
"value" : cty . StringVal ( "foo" ) ,
} ,
} ,
{
planInputs : map [ string ] cty . Value {
"value" : cty . StringVal ( "bar" ) ,
} ,
wantPlannedChanges : [ ] stackplan . PlannedChange {
& stackplan . PlannedChangeApplyable {
Applyable : true ,
} ,
& stackplan . PlannedChangeHeader {
TerraformVersion : version . SemVer ,
} ,
& stackplan . PlannedChangeOutputValue {
Addr : mustStackOutputValue ( "value" ) ,
Action : plans . Update ,
Before : cty . StringVal ( "foo" ) ,
After : cty . StringVal ( "bar" ) ,
} ,
& stackplan . PlannedChangePlannedTimestamp {
PlannedTimestamp : fakePlanTimestamp ,
} ,
& stackplan . PlannedChangeRootInputValue {
2024-09-16 05:45:19 -04:00
Addr : mustStackInputVariable ( "value" ) ,
Action : plans . Update ,
Before : cty . StringVal ( "foo" ) ,
After : cty . StringVal ( "bar" ) ,
2024-09-16 05:36:36 -04:00
} ,
} ,
wantAppliedChanges : [ ] stackstate . AppliedChange {
& stackstate . AppliedChangeOutputValue {
Addr : mustStackOutputValue ( "value" ) ,
Value : cty . StringVal ( "bar" ) ,
} ,
2024-09-16 05:45:19 -04:00
& stackstate . AppliedChangeInputVariable {
Addr : mustStackInputVariable ( "value" ) ,
Value : cty . StringVal ( "bar" ) ,
} ,
2024-09-16 05:36:36 -04:00
} ,
} ,
} ,
} ,
2024-09-16 05:53:21 -04:00
"updating inputs and outputs (noop)" : {
path : "component-input-output" ,
cycles : [ ] TestCycle {
{
planInputs : map [ string ] cty . Value {
"value" : cty . StringVal ( "foo" ) ,
} ,
} ,
{
planInputs : map [ string ] cty . Value {
"value" : cty . StringVal ( "foo" ) ,
} ,
wantPlannedChanges : [ ] stackplan . PlannedChange {
& stackplan . PlannedChangeApplyable {
Applyable : true ,
} ,
& stackplan . PlannedChangeHeader {
TerraformVersion : version . SemVer ,
} ,
& stackplan . PlannedChangeOutputValue {
Addr : mustStackOutputValue ( "value" ) ,
Action : plans . NoOp ,
Before : cty . StringVal ( "foo" ) ,
After : cty . StringVal ( "foo" ) ,
} ,
& stackplan . PlannedChangePlannedTimestamp {
PlannedTimestamp : fakePlanTimestamp ,
} ,
& stackplan . PlannedChangeRootInputValue {
Addr : mustStackInputVariable ( "value" ) ,
Action : plans . NoOp ,
Before : cty . StringVal ( "foo" ) ,
After : cty . StringVal ( "foo" ) ,
} ,
} ,
wantAppliedChanges : [ ] stackstate . AppliedChange {
& stackstate . AppliedChangeOutputValue {
Addr : mustStackOutputValue ( "value" ) ,
Value : cty . StringVal ( "foo" ) ,
} ,
& stackstate . AppliedChangeInputVariable {
Addr : mustStackInputVariable ( "value" ) ,
Value : cty . StringVal ( "foo" ) ,
} ,
} ,
} ,
} ,
} ,
2024-09-16 05:45:19 -04:00
"deleting inputs and outputs" : {
2024-09-16 05:36:36 -04:00
path : "component-input-output" ,
state : stackstate . NewStateBuilder ( ) .
2024-09-16 05:45:19 -04:00
AddInput ( "removed" , cty . StringVal ( "bar" ) ) .
2024-09-16 05:36:36 -04:00
AddOutput ( "removed" , cty . StringVal ( "bar" ) ) .
Build ( ) ,
cycles : [ ] TestCycle {
{
planInputs : map [ string ] cty . Value {
"value" : cty . StringVal ( "foo" ) ,
} ,
wantPlannedChanges : [ ] stackplan . PlannedChange {
& stackplan . PlannedChangeApplyable {
Applyable : true ,
} ,
& stackplan . PlannedChangeHeader {
TerraformVersion : version . SemVer ,
} ,
& stackplan . PlannedChangeOutputValue {
Addr : mustStackOutputValue ( "removed" ) ,
Action : plans . Delete ,
Before : cty . StringVal ( "bar" ) ,
After : cty . NullVal ( cty . DynamicPseudoType ) ,
} ,
& stackplan . PlannedChangeOutputValue {
Addr : mustStackOutputValue ( "value" ) ,
Action : plans . Create ,
Before : cty . NullVal ( cty . DynamicPseudoType ) ,
After : cty . StringVal ( "foo" ) ,
} ,
& stackplan . PlannedChangePlannedTimestamp {
PlannedTimestamp : fakePlanTimestamp ,
} ,
& stackplan . PlannedChangeRootInputValue {
2024-09-16 05:45:19 -04:00
Addr : mustStackInputVariable ( "removed" ) ,
Action : plans . Delete ,
Before : cty . StringVal ( "bar" ) ,
After : cty . NullVal ( cty . DynamicPseudoType ) ,
} ,
& stackplan . PlannedChangeRootInputValue {
Addr : mustStackInputVariable ( "value" ) ,
Action : plans . Create ,
Before : cty . NullVal ( cty . DynamicPseudoType ) ,
After : cty . StringVal ( "foo" ) ,
2024-09-16 05:36:36 -04:00
} ,
} ,
wantAppliedChanges : [ ] stackstate . AppliedChange {
& stackstate . AppliedChangeOutputValue {
Addr : mustStackOutputValue ( "removed" ) ,
} ,
& stackstate . AppliedChangeOutputValue {
Addr : mustStackOutputValue ( "value" ) ,
Value : cty . StringVal ( "foo" ) ,
} ,
2024-09-16 05:45:19 -04:00
& stackstate . AppliedChangeInputVariable {
2024-10-08 10:46:31 -04:00
Addr : mustStackInputVariable ( "removed" ) ,
Value : cty . NilVal , // destroyed
2024-09-16 05:45:19 -04:00
} ,
& stackstate . AppliedChangeInputVariable {
Addr : mustStackInputVariable ( "value" ) ,
Value : cty . StringVal ( "foo" ) ,
} ,
2024-09-16 05:36:36 -04:00
} ,
} ,
} ,
} ,
2024-09-18 04:41:36 -04:00
"checkable objects" : {
path : "checkable-objects" ,
2024-09-17 04:55:41 -04:00
cycles : [ ] TestCycle {
{
2024-09-18 04:41:36 -04:00
planInputs : map [ string ] cty . Value {
"foo" : cty . StringVal ( "bar" ) ,
2024-09-17 04:55:41 -04:00
} ,
2024-09-18 04:41:36 -04:00
wantPlannedDiags : initDiags ( func ( diags tfdiags . Diagnostics ) tfdiags . Diagnostics {
return diags . Append ( & hcl . Diagnostic {
Severity : hcl . DiagWarning ,
Summary : "Check block assertion failed" ,
Detail : ` value must be 'baz' ` ,
Subject : & hcl . Range {
Filename : mainBundleSourceAddrStr ( "checkable-objects/checkable-objects.tf" ) ,
Start : hcl . Pos { Line : 41 , Column : 21 , Byte : 716 } ,
End : hcl . Pos { Line : 41 , Column : 57 , Byte : 752 } ,
} ,
} )
} ) ,
2024-09-17 04:55:41 -04:00
wantAppliedChanges : [ ] stackstate . AppliedChange {
& stackstate . AppliedChangeComponentInstance {
2024-09-18 04:41:36 -04:00
ComponentAddr : mustAbsComponent ( "component.single" ) ,
ComponentInstanceAddr : mustAbsComponentInstance ( "component.single" ) ,
2024-09-17 04:55:41 -04:00
OutputValues : map [ addrs . OutputValue ] cty . Value {
2024-09-18 04:41:36 -04:00
addrs . OutputValue { Name : "foo" } : cty . StringVal ( "bar" ) ,
2024-09-17 04:55:41 -04:00
} ,
InputVariables : map [ addrs . InputVariable ] cty . Value {
2024-09-18 04:41:36 -04:00
mustInputVariable ( "foo" ) : cty . StringVal ( "bar" ) ,
2024-09-17 04:55:41 -04:00
} ,
} ,
& stackstate . AppliedChangeResourceInstanceObject {
2024-09-18 04:41:36 -04:00
ResourceInstanceObjectAddr : mustAbsResourceInstanceObject ( "component.single.testing_resource.main" ) ,
2024-09-17 04:55:41 -04:00
NewStateSrc : & states . ResourceInstanceObjectSrc {
AttrsJSON : mustMarshalJSONAttrs ( map [ string ] interface { } {
2024-09-18 04:41:36 -04:00
"id" : "test" ,
2024-09-17 04:55:41 -04:00
"value" : "bar" ,
} ) ,
2024-09-18 04:41:36 -04:00
Status : states . ObjectReady ,
Dependencies : make ( [ ] addrs . ConfigResource , 0 ) ,
2024-09-17 04:55:41 -04:00
} ,
ProviderConfigAddr : addrs . AbsProviderConfig {
2024-09-18 04:41:36 -04:00
Provider : addrs . NewDefaultProvider ( "testing" ) ,
2024-09-17 04:55:41 -04:00
} ,
Schema : stacks_testing_provider . TestingResourceSchema ,
} ,
2024-09-18 04:41:36 -04:00
} ,
wantAppliedDiags : initDiags ( func ( diags tfdiags . Diagnostics ) tfdiags . Diagnostics {
return diags . Append ( & hcl . Diagnostic {
Severity : hcl . DiagWarning ,
Summary : "Check block assertion failed" ,
Detail : ` value must be 'baz' ` ,
Subject : & hcl . Range {
Filename : mainBundleSourceAddrStr ( "checkable-objects/checkable-objects.tf" ) ,
Start : hcl . Pos { Line : 41 , Column : 21 , Byte : 716 } ,
End : hcl . Pos { Line : 41 , Column : 57 , Byte : 752 } ,
2024-09-17 04:55:41 -04:00
} ,
2024-09-18 04:41:36 -04:00
} )
} ) ,
} ,
{
planMode : plans . DestroyMode ,
planInputs : map [ string ] cty . Value {
"foo" : cty . StringVal ( "bar" ) ,
} ,
wantAppliedChanges : [ ] stackstate . AppliedChange {
& stackstate . AppliedChangeComponentInstanceRemoved {
ComponentAddr : mustAbsComponent ( "component.single" ) ,
ComponentInstanceAddr : mustAbsComponentInstance ( "component.single" ) ,
2024-09-17 04:55:41 -04:00
} ,
& stackstate . AppliedChangeResourceInstanceObject {
2024-09-18 04:41:36 -04:00
ResourceInstanceObjectAddr : mustAbsResourceInstanceObject ( "component.single.testing_resource.main" ) ,
ProviderConfigAddr : mustDefaultRootProvider ( "testing" ) ,
2024-09-17 04:55:41 -04:00
} ,
} ,
} ,
} ,
} ,
2024-09-18 04:41:36 -04:00
"removed component" : {
path : filepath . Join ( "with-single-input" , "removed-component" ) ,
2024-09-17 04:55:41 -04:00
state : stackstate . NewStateBuilder ( ) .
2024-09-18 04:41:36 -04:00
AddComponentInstance ( stackstate . NewComponentInstanceBuilder ( mustAbsComponentInstance ( "component.self" ) ) .
AddInputVariable ( "id" , cty . StringVal ( "removed" ) ) .
AddInputVariable ( "input" , cty . StringVal ( "removed" ) ) ) .
2024-09-17 04:55:41 -04:00
AddResourceInstance ( stackstate . NewResourceInstanceBuilder ( ) .
2024-09-18 04:41:36 -04:00
SetAddr ( mustAbsResourceInstanceObject ( "component.self.testing_resource.data" ) ) .
2024-09-17 04:55:41 -04:00
SetProviderAddr ( mustDefaultRootProvider ( "testing" ) ) .
SetResourceInstanceObjectSrc ( states . ResourceInstanceObjectSrc {
Status : states . ObjectReady ,
2024-09-18 04:41:36 -04:00
AttrsJSON : mustMarshalJSONAttrs ( map [ string ] any {
"id" : "removed" ,
"value" : "removed" ,
} ) ,
2024-09-17 04:55:41 -04:00
} ) ) .
2024-09-18 04:41:36 -04:00
Build ( ) ,
store : stacks_testing_provider . NewResourceStoreBuilder ( ) .
AddResource ( "removed" , cty . ObjectVal ( map [ string ] cty . Value {
"id" : cty . StringVal ( "removed" ) ,
"value" : cty . StringVal ( "removed" ) ,
} ) ) .
Build ( ) ,
cycles : [ ] TestCycle {
{
planInputs : map [ string ] cty . Value { } ,
wantPlannedChanges : [ ] stackplan . PlannedChange {
& stackplan . PlannedChangeApplyable {
Applyable : true ,
} ,
& stackplan . PlannedChangeComponentInstance {
Addr : mustAbsComponentInstance ( "component.self" ) ,
PlanComplete : true ,
PlanApplyable : true ,
Mode : plans . DestroyMode ,
Action : plans . Delete ,
PlannedInputValues : map [ string ] plans . DynamicValue {
"id" : mustPlanDynamicValueDynamicType ( cty . StringVal ( "removed" ) ) ,
"input" : mustPlanDynamicValueDynamicType ( cty . StringVal ( "removed" ) ) ,
} ,
PlannedInputValueMarks : map [ string ] [ ] cty . PathValueMarks {
"input" : nil ,
"id" : nil ,
} ,
PlannedOutputValues : make ( map [ string ] cty . Value ) ,
PlannedCheckResults : & states . CheckResults { } ,
PlanTimestamp : fakePlanTimestamp ,
} ,
& stackplan . PlannedChangeResourceInstancePlanned {
ResourceInstanceObjectAddr : mustAbsResourceInstanceObject ( "component.self.testing_resource.data" ) ,
ChangeSrc : & plans . ResourceInstanceChangeSrc {
Addr : mustAbsResourceInstance ( "testing_resource.data" ) ,
PrevRunAddr : mustAbsResourceInstance ( "testing_resource.data" ) ,
ChangeSrc : plans . ChangeSrc {
Action : plans . Delete ,
Before : mustPlanDynamicValue ( cty . ObjectVal ( map [ string ] cty . Value {
"id" : cty . StringVal ( "removed" ) ,
"value" : cty . StringVal ( "removed" ) ,
} ) ) ,
After : mustPlanDynamicValue ( cty . NullVal ( cty . Object ( map [ string ] cty . Type {
"id" : cty . String ,
"value" : cty . String ,
} ) ) ) ,
} ,
ProviderAddr : mustDefaultRootProvider ( "testing" ) ,
} ,
PriorStateSrc : & states . ResourceInstanceObjectSrc {
AttrsJSON : mustMarshalJSONAttrs ( map [ string ] any {
"id" : "removed" ,
"value" : "removed" ,
} ) ,
Dependencies : make ( [ ] addrs . ConfigResource , 0 ) ,
Status : states . ObjectReady ,
} ,
ProviderConfigAddr : mustDefaultRootProvider ( "testing" ) ,
Schema : stacks_testing_provider . TestingResourceSchema ,
} ,
& stackplan . PlannedChangeHeader {
TerraformVersion : version . SemVer ,
} ,
& stackplan . PlannedChangePlannedTimestamp {
PlannedTimestamp : fakePlanTimestamp ,
} ,
} ,
wantAppliedChanges : [ ] stackstate . AppliedChange {
& stackstate . AppliedChangeComponentInstanceRemoved {
ComponentAddr : mustAbsComponent ( "component.self" ) ,
ComponentInstanceAddr : mustAbsComponentInstance ( "component.self" ) ,
} ,
& stackstate . AppliedChangeResourceInstanceObject {
ResourceInstanceObjectAddr : mustAbsResourceInstanceObject ( "component.self.testing_resource.data" ) ,
ProviderConfigAddr : mustDefaultRootProvider ( "testing" ) ,
NewStateSrc : nil ,
2025-03-11 15:58:44 -04:00
Schema : providers . Schema { } ,
2024-09-18 04:41:36 -04:00
} ,
} ,
} ,
} ,
} ,
"removed component instance" : {
path : filepath . Join ( "with-single-input" , "removed-component-instance" ) ,
state : stackstate . NewStateBuilder ( ) .
AddComponentInstance ( stackstate . NewComponentInstanceBuilder ( mustAbsComponentInstance ( "component.self[\"removed\"]" ) ) .
AddInputVariable ( "id" , cty . StringVal ( "removed" ) ) .
AddInputVariable ( "input" , cty . StringVal ( "removed" ) ) ) .
2024-09-17 04:55:41 -04:00
AddResourceInstance ( stackstate . NewResourceInstanceBuilder ( ) .
2024-09-18 04:41:36 -04:00
SetAddr ( mustAbsResourceInstanceObject ( "component.self[\"removed\"].testing_resource.data" ) ) .
2024-09-17 04:55:41 -04:00
SetProviderAddr ( mustDefaultRootProvider ( "testing" ) ) .
SetResourceInstanceObjectSrc ( states . ResourceInstanceObjectSrc {
Status : states . ObjectReady ,
2024-09-18 04:41:36 -04:00
AttrsJSON : mustMarshalJSONAttrs ( map [ string ] any {
"id" : "removed" ,
"value" : "removed" ,
} ) ,
2024-09-17 04:55:41 -04:00
} ) ) .
Build ( ) ,
store : stacks_testing_provider . NewResourceStoreBuilder ( ) .
2024-09-18 04:41:36 -04:00
AddResource ( "removed" , cty . ObjectVal ( map [ string ] cty . Value {
"id" : cty . StringVal ( "removed" ) ,
"value" : cty . StringVal ( "removed" ) ,
2024-09-17 04:55:41 -04:00
} ) ) .
Build ( ) ,
cycles : [ ] TestCycle {
{
2024-09-18 04:41:36 -04:00
planInputs : map [ string ] cty . Value {
"input" : cty . SetVal ( [ ] cty . Value {
cty . StringVal ( "added" ) ,
} ) ,
"removed" : cty . SetVal ( [ ] cty . Value {
cty . StringVal ( "removed" ) ,
} ) ,
2024-09-17 04:55:41 -04:00
} ,
2024-09-18 04:41:36 -04:00
wantPlannedChanges : [ ] stackplan . PlannedChange {
& stackplan . PlannedChangeApplyable {
Applyable : true ,
} ,
// we're expecting the new component to be created
& stackplan . PlannedChangeComponentInstance {
Addr : mustAbsComponentInstance ( "component.self[\"added\"]" ) ,
PlanComplete : true ,
PlanApplyable : true ,
Action : plans . Create ,
PlannedInputValues : map [ string ] plans . DynamicValue {
"id" : mustPlanDynamicValueDynamicType ( cty . StringVal ( "added" ) ) ,
"input" : mustPlanDynamicValueDynamicType ( cty . StringVal ( "added" ) ) ,
2024-09-17 04:55:41 -04:00
} ,
2024-09-18 04:41:36 -04:00
PlannedInputValueMarks : map [ string ] [ ] cty . PathValueMarks {
"input" : nil ,
"id" : nil ,
} ,
PlannedOutputValues : make ( map [ string ] cty . Value ) ,
PlannedCheckResults : & states . CheckResults { } ,
PlanTimestamp : fakePlanTimestamp ,
2024-09-17 04:55:41 -04:00
} ,
2024-09-18 04:41:36 -04:00
& stackplan . PlannedChangeResourceInstancePlanned {
ResourceInstanceObjectAddr : mustAbsResourceInstanceObject ( "component.self[\"added\"].testing_resource.data" ) ,
ChangeSrc : & plans . ResourceInstanceChangeSrc {
Addr : mustAbsResourceInstance ( "testing_resource.data" ) ,
PrevRunAddr : mustAbsResourceInstance ( "testing_resource.data" ) ,
ChangeSrc : plans . ChangeSrc {
Action : plans . Create ,
Before : mustPlanDynamicValue ( cty . NullVal ( cty . Object ( map [ string ] cty . Type {
"id" : cty . String ,
"value" : cty . String ,
} ) ) ) ,
After : mustPlanDynamicValue ( cty . ObjectVal ( map [ string ] cty . Value {
"id" : cty . StringVal ( "added" ) ,
"value" : cty . StringVal ( "added" ) ,
} ) ) ,
} ,
ProviderAddr : mustDefaultRootProvider ( "testing" ) ,
} ,
ProviderConfigAddr : mustDefaultRootProvider ( "testing" ) ,
Schema : stacks_testing_provider . TestingResourceSchema ,
} ,
& stackplan . PlannedChangeComponentInstance {
Addr : mustAbsComponentInstance ( "component.self[\"removed\"]" ) ,
PlanComplete : true ,
PlanApplyable : true ,
Mode : plans . DestroyMode ,
Action : plans . Delete ,
PlannedInputValues : map [ string ] plans . DynamicValue {
"id" : mustPlanDynamicValueDynamicType ( cty . StringVal ( "removed" ) ) ,
"input" : mustPlanDynamicValueDynamicType ( cty . StringVal ( "removed" ) ) ,
} ,
PlannedInputValueMarks : map [ string ] [ ] cty . PathValueMarks {
"input" : nil ,
"id" : nil ,
} ,
PlannedOutputValues : make ( map [ string ] cty . Value ) ,
PlannedCheckResults : & states . CheckResults { } ,
PlanTimestamp : fakePlanTimestamp ,
} ,
& stackplan . PlannedChangeResourceInstancePlanned {
ResourceInstanceObjectAddr : mustAbsResourceInstanceObject ( "component.self[\"removed\"].testing_resource.data" ) ,
ChangeSrc : & plans . ResourceInstanceChangeSrc {
Addr : mustAbsResourceInstance ( "testing_resource.data" ) ,
PrevRunAddr : mustAbsResourceInstance ( "testing_resource.data" ) ,
ChangeSrc : plans . ChangeSrc {
Action : plans . Delete ,
Before : mustPlanDynamicValue ( cty . ObjectVal ( map [ string ] cty . Value {
"id" : cty . StringVal ( "removed" ) ,
"value" : cty . StringVal ( "removed" ) ,
} ) ) ,
After : mustPlanDynamicValue ( cty . NullVal ( cty . Object ( map [ string ] cty . Type {
"id" : cty . String ,
"value" : cty . String ,
} ) ) ) ,
} ,
ProviderAddr : mustDefaultRootProvider ( "testing" ) ,
} ,
PriorStateSrc : & states . ResourceInstanceObjectSrc {
AttrsJSON : mustMarshalJSONAttrs ( map [ string ] any {
"id" : "removed" ,
"value" : "removed" ,
} ) ,
Dependencies : make ( [ ] addrs . ConfigResource , 0 ) ,
Status : states . ObjectReady ,
2024-09-17 04:55:41 -04:00
} ,
2024-09-18 04:41:36 -04:00
ProviderConfigAddr : mustDefaultRootProvider ( "testing" ) ,
Schema : stacks_testing_provider . TestingResourceSchema ,
} ,
& stackplan . PlannedChangeHeader {
TerraformVersion : version . SemVer ,
} ,
& stackplan . PlannedChangePlannedTimestamp {
PlannedTimestamp : fakePlanTimestamp ,
} ,
& stackplan . PlannedChangeRootInputValue {
Addr : stackaddrs . InputVariable { Name : "input" } ,
Action : plans . Create ,
Before : cty . NullVal ( cty . DynamicPseudoType ) ,
After : cty . SetVal ( [ ] cty . Value {
cty . StringVal ( "added" ) ,
} ) ,
} ,
& stackplan . PlannedChangeRootInputValue {
Addr : stackaddrs . InputVariable { Name : "removed" } ,
Action : plans . Create ,
Before : cty . NullVal ( cty . DynamicPseudoType ) ,
After : cty . SetVal ( [ ] cty . Value {
cty . StringVal ( "removed" ) ,
} ) ,
2024-09-17 04:55:41 -04:00
} ,
2024-09-18 04:41:36 -04:00
} ,
wantAppliedChanges : [ ] stackstate . AppliedChange {
2024-09-17 04:55:41 -04:00
& stackstate . AppliedChangeComponentInstance {
2024-09-18 04:41:36 -04:00
ComponentAddr : mustAbsComponent ( "component.self" ) ,
ComponentInstanceAddr : mustAbsComponentInstance ( "component.self[\"added\"]" ) ,
OutputValues : make ( map [ addrs . OutputValue ] cty . Value ) ,
2024-09-17 04:55:41 -04:00
InputVariables : map [ addrs . InputVariable ] cty . Value {
2024-09-18 04:41:36 -04:00
mustInputVariable ( "id" ) : cty . StringVal ( "added" ) ,
mustInputVariable ( "input" ) : cty . StringVal ( "added" ) ,
2024-09-17 04:55:41 -04:00
} ,
} ,
& stackstate . AppliedChangeResourceInstanceObject {
2024-09-18 04:41:36 -04:00
ResourceInstanceObjectAddr : mustAbsResourceInstanceObject ( "component.self[\"added\"].testing_resource.data" ) ,
NewStateSrc : & states . ResourceInstanceObjectSrc {
AttrsJSON : mustMarshalJSONAttrs ( map [ string ] any {
"id" : "added" ,
"value" : "added" ,
} ) ,
Status : states . ObjectReady ,
Dependencies : make ( [ ] addrs . ConfigResource , 0 ) ,
2024-09-17 04:55:41 -04:00
} ,
2024-09-18 04:41:36 -04:00
ProviderConfigAddr : mustDefaultRootProvider ( "testing" ) ,
Schema : stacks_testing_provider . TestingResourceSchema ,
} ,
& stackstate . AppliedChangeComponentInstanceRemoved {
ComponentAddr : mustAbsComponent ( "component.self" ) ,
ComponentInstanceAddr : mustAbsComponentInstance ( "component.self[\"removed\"]" ) ,
} ,
& stackstate . AppliedChangeResourceInstanceObject {
ResourceInstanceObjectAddr : mustAbsResourceInstanceObject ( "component.self[\"removed\"].testing_resource.data" ) ,
ProviderConfigAddr : mustDefaultRootProvider ( "testing" ) ,
NewStateSrc : nil ,
2025-03-11 15:58:44 -04:00
Schema : providers . Schema { } ,
2024-09-18 04:41:36 -04:00
} ,
& stackstate . AppliedChangeInputVariable {
Addr : mustStackInputVariable ( "input" ) ,
Value : cty . SetVal ( [ ] cty . Value {
cty . StringVal ( "added" ) ,
} ) ,
} ,
& stackstate . AppliedChangeInputVariable {
Addr : mustStackInputVariable ( "removed" ) ,
Value : cty . SetVal ( [ ] cty . Value {
cty . StringVal ( "removed" ) ,
} ) ,
2024-09-17 04:55:41 -04:00
} ,
} ,
} ,
} ,
2025-03-18 03:54:27 -04:00
} ,
"duplicate removed blocks" : {
path : path . Join ( "with-single-input" , "removed-component-duplicate" ) ,
state : stackstate . NewStateBuilder ( ) .
AddComponentInstance ( stackstate . NewComponentInstanceBuilder ( mustAbsComponentInstance ( "component.self[\"one\"]" ) ) .
AddInputVariable ( "id" , cty . StringVal ( "one" ) ) .
AddInputVariable ( "input" , cty . StringVal ( "one" ) ) ) .
AddResourceInstance ( stackstate . NewResourceInstanceBuilder ( ) .
SetAddr ( mustAbsResourceInstanceObject ( "component.self[\"one\"].testing_resource.data" ) ) .
SetProviderAddr ( mustDefaultRootProvider ( "testing" ) ) .
SetResourceInstanceObjectSrc ( states . ResourceInstanceObjectSrc {
Status : states . ObjectReady ,
AttrsJSON : mustMarshalJSONAttrs ( map [ string ] any {
"id" : "one" ,
"value" : "one" ,
} ) ,
} ) ) .
AddComponentInstance ( stackstate . NewComponentInstanceBuilder ( mustAbsComponentInstance ( "component.self[\"two\"]" ) ) .
AddInputVariable ( "id" , cty . StringVal ( "two" ) ) .
AddInputVariable ( "input" , cty . StringVal ( "two" ) ) ) .
AddResourceInstance ( stackstate . NewResourceInstanceBuilder ( ) .
SetAddr ( mustAbsResourceInstanceObject ( "component.self[\"two\"].testing_resource.data" ) ) .
SetProviderAddr ( mustDefaultRootProvider ( "testing" ) ) .
SetResourceInstanceObjectSrc ( states . ResourceInstanceObjectSrc {
Status : states . ObjectReady ,
AttrsJSON : mustMarshalJSONAttrs ( map [ string ] any {
"id" : "two" ,
"value" : "two" ,
} ) ,
} ) ) .
Build ( ) ,
store : stacks_testing_provider . NewResourceStoreBuilder ( ) .
AddResource ( "one" , cty . ObjectVal ( map [ string ] cty . Value {
"id" : cty . StringVal ( "one" ) ,
"value" : cty . StringVal ( "one" ) ,
} ) ) .
AddResource ( "two" , cty . ObjectVal ( map [ string ] cty . Value {
"id" : cty . StringVal ( "two" ) ,
"value" : cty . StringVal ( "two" ) ,
} ) ) .
Build ( ) ,
cycles : [ ] TestCycle {
{
planMode : plans . NormalMode ,
planInputs : map [ string ] cty . Value {
"input" : cty . SetValEmpty ( cty . String ) ,
"removed_one" : cty . SetVal ( [ ] cty . Value {
cty . StringVal ( "one" ) ,
} ) ,
"removed_two" : cty . SetVal ( [ ] cty . Value {
cty . StringVal ( "two" ) ,
} ) ,
} ,
wantPlannedChanges : [ ] stackplan . PlannedChange {
& stackplan . PlannedChangeApplyable {
Applyable : true ,
} ,
& stackplan . PlannedChangeComponentInstance {
Addr : mustAbsComponentInstance ( "component.self[\"one\"]" ) ,
PlanComplete : true ,
PlanApplyable : true ,
Mode : plans . DestroyMode ,
Action : plans . Delete ,
PlannedInputValues : map [ string ] plans . DynamicValue {
"id" : mustPlanDynamicValueDynamicType ( cty . StringVal ( "one" ) ) ,
"input" : mustPlanDynamicValueDynamicType ( cty . StringVal ( "one" ) ) ,
} ,
PlannedInputValueMarks : map [ string ] [ ] cty . PathValueMarks {
"input" : nil ,
"id" : nil ,
} ,
PlannedOutputValues : make ( map [ string ] cty . Value ) ,
PlannedCheckResults : & states . CheckResults { } ,
PlanTimestamp : fakePlanTimestamp ,
} ,
& stackplan . PlannedChangeResourceInstancePlanned {
ResourceInstanceObjectAddr : mustAbsResourceInstanceObject ( "component.self[\"one\"].testing_resource.data" ) ,
ChangeSrc : & plans . ResourceInstanceChangeSrc {
Addr : mustAbsResourceInstance ( "testing_resource.data" ) ,
PrevRunAddr : mustAbsResourceInstance ( "testing_resource.data" ) ,
ChangeSrc : plans . ChangeSrc {
Action : plans . Delete ,
Before : mustPlanDynamicValue ( cty . ObjectVal ( map [ string ] cty . Value {
"id" : cty . StringVal ( "one" ) ,
"value" : cty . StringVal ( "one" ) ,
} ) ) ,
After : mustPlanDynamicValue ( cty . NullVal ( cty . Object ( map [ string ] cty . Type {
"id" : cty . String ,
"value" : cty . String ,
} ) ) ) ,
} ,
ProviderAddr : mustDefaultRootProvider ( "testing" ) ,
} ,
PriorStateSrc : & states . ResourceInstanceObjectSrc {
AttrsJSON : mustMarshalJSONAttrs ( map [ string ] any {
"id" : "one" ,
"value" : "one" ,
} ) ,
Dependencies : make ( [ ] addrs . ConfigResource , 0 ) ,
Status : states . ObjectReady ,
} ,
ProviderConfigAddr : mustDefaultRootProvider ( "testing" ) ,
Schema : stacks_testing_provider . TestingResourceSchema ,
} ,
& stackplan . PlannedChangeComponentInstance {
Addr : mustAbsComponentInstance ( "component.self[\"two\"]" ) ,
PlanComplete : true ,
PlanApplyable : true ,
Mode : plans . DestroyMode ,
Action : plans . Delete ,
PlannedInputValues : map [ string ] plans . DynamicValue {
"id" : mustPlanDynamicValueDynamicType ( cty . StringVal ( "two" ) ) ,
"input" : mustPlanDynamicValueDynamicType ( cty . StringVal ( "two" ) ) ,
} ,
PlannedInputValueMarks : map [ string ] [ ] cty . PathValueMarks {
"input" : nil ,
"id" : nil ,
} ,
PlannedOutputValues : make ( map [ string ] cty . Value ) ,
PlannedCheckResults : & states . CheckResults { } ,
PlanTimestamp : fakePlanTimestamp ,
} ,
& stackplan . PlannedChangeResourceInstancePlanned {
ResourceInstanceObjectAddr : mustAbsResourceInstanceObject ( "component.self[\"two\"].testing_resource.data" ) ,
ChangeSrc : & plans . ResourceInstanceChangeSrc {
Addr : mustAbsResourceInstance ( "testing_resource.data" ) ,
PrevRunAddr : mustAbsResourceInstance ( "testing_resource.data" ) ,
ChangeSrc : plans . ChangeSrc {
Action : plans . Delete ,
Before : mustPlanDynamicValue ( cty . ObjectVal ( map [ string ] cty . Value {
"id" : cty . StringVal ( "two" ) ,
"value" : cty . StringVal ( "two" ) ,
} ) ) ,
After : mustPlanDynamicValue ( cty . NullVal ( cty . Object ( map [ string ] cty . Type {
"id" : cty . String ,
"value" : cty . String ,
} ) ) ) ,
} ,
ProviderAddr : mustDefaultRootProvider ( "testing" ) ,
} ,
PriorStateSrc : & states . ResourceInstanceObjectSrc {
AttrsJSON : mustMarshalJSONAttrs ( map [ string ] any {
"id" : "two" ,
"value" : "two" ,
} ) ,
Dependencies : make ( [ ] addrs . ConfigResource , 0 ) ,
Status : states . ObjectReady ,
} ,
ProviderConfigAddr : mustDefaultRootProvider ( "testing" ) ,
Schema : stacks_testing_provider . TestingResourceSchema ,
} ,
& stackplan . PlannedChangeHeader {
TerraformVersion : version . SemVer ,
} ,
& stackplan . PlannedChangePlannedTimestamp {
PlannedTimestamp : fakePlanTimestamp ,
} ,
& stackplan . PlannedChangeRootInputValue {
Addr : stackaddrs . InputVariable { Name : "input" } ,
Action : plans . Create ,
Before : cty . NullVal ( cty . DynamicPseudoType ) ,
After : cty . SetValEmpty ( cty . String ) ,
} ,
& stackplan . PlannedChangeRootInputValue {
Addr : stackaddrs . InputVariable { Name : "removed_one" } ,
Action : plans . Create ,
Before : cty . NullVal ( cty . DynamicPseudoType ) ,
After : cty . SetVal ( [ ] cty . Value {
cty . StringVal ( "one" ) ,
} ) ,
} ,
& stackplan . PlannedChangeRootInputValue {
Addr : stackaddrs . InputVariable { Name : "removed_two" } ,
Action : plans . Create ,
Before : cty . NullVal ( cty . DynamicPseudoType ) ,
After : cty . SetVal ( [ ] cty . Value {
cty . StringVal ( "two" ) ,
} ) ,
} ,
} ,
wantAppliedChanges : [ ] stackstate . AppliedChange {
& stackstate . AppliedChangeComponentInstanceRemoved {
ComponentAddr : mustAbsComponent ( "component.self" ) ,
ComponentInstanceAddr : mustAbsComponentInstance ( "component.self[\"one\"]" ) ,
} ,
& stackstate . AppliedChangeResourceInstanceObject {
ResourceInstanceObjectAddr : mustAbsResourceInstanceObject ( "component.self[\"one\"].testing_resource.data" ) ,
ProviderConfigAddr : mustDefaultRootProvider ( "testing" ) ,
NewStateSrc : nil ,
Schema : providers . Schema { } ,
} ,
& stackstate . AppliedChangeComponentInstanceRemoved {
ComponentAddr : mustAbsComponent ( "component.self" ) ,
ComponentInstanceAddr : mustAbsComponentInstance ( "component.self[\"two\"]" ) ,
} ,
& stackstate . AppliedChangeResourceInstanceObject {
ResourceInstanceObjectAddr : mustAbsResourceInstanceObject ( "component.self[\"two\"].testing_resource.data" ) ,
ProviderConfigAddr : mustDefaultRootProvider ( "testing" ) ,
NewStateSrc : nil ,
Schema : providers . Schema { } ,
} ,
& stackstate . AppliedChangeInputVariable {
Addr : mustStackInputVariable ( "input" ) ,
Value : cty . SetValEmpty ( cty . String ) ,
} ,
& stackstate . AppliedChangeInputVariable {
Addr : mustStackInputVariable ( "removed_one" ) ,
Value : cty . SetVal ( [ ] cty . Value {
cty . StringVal ( "one" ) ,
} ) ,
} ,
& stackstate . AppliedChangeInputVariable {
Addr : mustStackInputVariable ( "removed_two" ) ,
Value : cty . SetVal ( [ ] cty . Value {
cty . StringVal ( "two" ) ,
} ) ,
} ,
} ,
} ,
} ,
2024-09-17 04:55:41 -04:00
} ,
2025-03-12 05:33:18 -04:00
"removed component instance direct" : {
path : filepath . Join ( "with-single-input" , "removed-component-instance-direct" ) ,
state : stackstate . NewStateBuilder ( ) .
AddComponentInstance ( stackstate . NewComponentInstanceBuilder ( mustAbsComponentInstance ( "component.self[\"removed\"]" ) ) .
AddInputVariable ( "id" , cty . StringVal ( "removed" ) ) .
AddInputVariable ( "input" , cty . StringVal ( "removed" ) ) ) .
AddResourceInstance ( stackstate . NewResourceInstanceBuilder ( ) .
SetAddr ( mustAbsResourceInstanceObject ( "component.self[\"removed\"].testing_resource.data" ) ) .
SetProviderAddr ( mustDefaultRootProvider ( "testing" ) ) .
SetResourceInstanceObjectSrc ( states . ResourceInstanceObjectSrc {
Status : states . ObjectReady ,
AttrsJSON : mustMarshalJSONAttrs ( map [ string ] any {
"id" : "removed" ,
"value" : "removed" ,
} ) ,
} ) ) .
Build ( ) ,
store : stacks_testing_provider . NewResourceStoreBuilder ( ) .
AddResource ( "removed" , cty . ObjectVal ( map [ string ] cty . Value {
"id" : cty . StringVal ( "removed" ) ,
"value" : cty . StringVal ( "removed" ) ,
} ) ) .
Build ( ) ,
cycles : [ ] TestCycle {
{
planInputs : map [ string ] cty . Value {
"input" : cty . SetVal ( [ ] cty . Value {
cty . StringVal ( "added" ) ,
} ) ,
} ,
wantPlannedChanges : [ ] stackplan . PlannedChange {
& stackplan . PlannedChangeApplyable {
Applyable : true ,
} ,
// we're expecting the new component to be created
& stackplan . PlannedChangeComponentInstance {
Addr : mustAbsComponentInstance ( "component.self[\"added\"]" ) ,
PlanComplete : true ,
PlanApplyable : true ,
Action : plans . Create ,
PlannedInputValues : map [ string ] plans . DynamicValue {
"id" : mustPlanDynamicValueDynamicType ( cty . StringVal ( "added" ) ) ,
"input" : mustPlanDynamicValueDynamicType ( cty . StringVal ( "added" ) ) ,
} ,
PlannedInputValueMarks : map [ string ] [ ] cty . PathValueMarks {
"input" : nil ,
"id" : nil ,
} ,
PlannedOutputValues : make ( map [ string ] cty . Value ) ,
PlannedCheckResults : & states . CheckResults { } ,
PlanTimestamp : fakePlanTimestamp ,
} ,
& stackplan . PlannedChangeResourceInstancePlanned {
ResourceInstanceObjectAddr : mustAbsResourceInstanceObject ( "component.self[\"added\"].testing_resource.data" ) ,
ChangeSrc : & plans . ResourceInstanceChangeSrc {
Addr : mustAbsResourceInstance ( "testing_resource.data" ) ,
PrevRunAddr : mustAbsResourceInstance ( "testing_resource.data" ) ,
ChangeSrc : plans . ChangeSrc {
Action : plans . Create ,
Before : mustPlanDynamicValue ( cty . NullVal ( cty . Object ( map [ string ] cty . Type {
"id" : cty . String ,
"value" : cty . String ,
} ) ) ) ,
After : mustPlanDynamicValue ( cty . ObjectVal ( map [ string ] cty . Value {
"id" : cty . StringVal ( "added" ) ,
"value" : cty . StringVal ( "added" ) ,
} ) ) ,
} ,
ProviderAddr : mustDefaultRootProvider ( "testing" ) ,
} ,
ProviderConfigAddr : mustDefaultRootProvider ( "testing" ) ,
Schema : stacks_testing_provider . TestingResourceSchema ,
} ,
& stackplan . PlannedChangeComponentInstance {
Addr : mustAbsComponentInstance ( "component.self[\"removed\"]" ) ,
PlanComplete : true ,
PlanApplyable : true ,
Mode : plans . DestroyMode ,
Action : plans . Delete ,
PlannedInputValues : map [ string ] plans . DynamicValue {
"id" : mustPlanDynamicValueDynamicType ( cty . StringVal ( "removed" ) ) ,
"input" : mustPlanDynamicValueDynamicType ( cty . StringVal ( "removed" ) ) ,
} ,
PlannedInputValueMarks : map [ string ] [ ] cty . PathValueMarks {
"input" : nil ,
"id" : nil ,
} ,
PlannedOutputValues : make ( map [ string ] cty . Value ) ,
PlannedCheckResults : & states . CheckResults { } ,
PlanTimestamp : fakePlanTimestamp ,
} ,
& stackplan . PlannedChangeResourceInstancePlanned {
ResourceInstanceObjectAddr : mustAbsResourceInstanceObject ( "component.self[\"removed\"].testing_resource.data" ) ,
ChangeSrc : & plans . ResourceInstanceChangeSrc {
Addr : mustAbsResourceInstance ( "testing_resource.data" ) ,
PrevRunAddr : mustAbsResourceInstance ( "testing_resource.data" ) ,
ChangeSrc : plans . ChangeSrc {
Action : plans . Delete ,
Before : mustPlanDynamicValue ( cty . ObjectVal ( map [ string ] cty . Value {
"id" : cty . StringVal ( "removed" ) ,
"value" : cty . StringVal ( "removed" ) ,
} ) ) ,
After : mustPlanDynamicValue ( cty . NullVal ( cty . Object ( map [ string ] cty . Type {
"id" : cty . String ,
"value" : cty . String ,
} ) ) ) ,
} ,
ProviderAddr : mustDefaultRootProvider ( "testing" ) ,
} ,
PriorStateSrc : & states . ResourceInstanceObjectSrc {
AttrsJSON : mustMarshalJSONAttrs ( map [ string ] any {
"id" : "removed" ,
"value" : "removed" ,
} ) ,
Dependencies : make ( [ ] addrs . ConfigResource , 0 ) ,
Status : states . ObjectReady ,
} ,
ProviderConfigAddr : mustDefaultRootProvider ( "testing" ) ,
Schema : stacks_testing_provider . TestingResourceSchema ,
} ,
& stackplan . PlannedChangeHeader {
TerraformVersion : version . SemVer ,
} ,
& stackplan . PlannedChangePlannedTimestamp {
PlannedTimestamp : fakePlanTimestamp ,
} ,
& stackplan . PlannedChangeRootInputValue {
Addr : stackaddrs . InputVariable { Name : "input" } ,
Action : plans . Create ,
Before : cty . NullVal ( cty . DynamicPseudoType ) ,
After : cty . SetVal ( [ ] cty . Value {
cty . StringVal ( "added" ) ,
} ) ,
} ,
} ,
wantAppliedChanges : [ ] stackstate . AppliedChange {
& stackstate . AppliedChangeComponentInstance {
ComponentAddr : mustAbsComponent ( "component.self" ) ,
ComponentInstanceAddr : mustAbsComponentInstance ( "component.self[\"added\"]" ) ,
OutputValues : make ( map [ addrs . OutputValue ] cty . Value ) ,
InputVariables : map [ addrs . InputVariable ] cty . Value {
mustInputVariable ( "id" ) : cty . StringVal ( "added" ) ,
mustInputVariable ( "input" ) : cty . StringVal ( "added" ) ,
} ,
} ,
& stackstate . AppliedChangeResourceInstanceObject {
ResourceInstanceObjectAddr : mustAbsResourceInstanceObject ( "component.self[\"added\"].testing_resource.data" ) ,
NewStateSrc : & states . ResourceInstanceObjectSrc {
AttrsJSON : mustMarshalJSONAttrs ( map [ string ] any {
"id" : "added" ,
"value" : "added" ,
} ) ,
Status : states . ObjectReady ,
Dependencies : make ( [ ] addrs . ConfigResource , 0 ) ,
} ,
ProviderConfigAddr : mustDefaultRootProvider ( "testing" ) ,
Schema : stacks_testing_provider . TestingResourceSchema ,
} ,
& stackstate . AppliedChangeComponentInstanceRemoved {
ComponentAddr : mustAbsComponent ( "component.self" ) ,
ComponentInstanceAddr : mustAbsComponentInstance ( "component.self[\"removed\"]" ) ,
} ,
& stackstate . AppliedChangeResourceInstanceObject {
ResourceInstanceObjectAddr : mustAbsResourceInstanceObject ( "component.self[\"removed\"].testing_resource.data" ) ,
ProviderConfigAddr : mustDefaultRootProvider ( "testing" ) ,
NewStateSrc : nil ,
2025-03-12 05:47:07 -04:00
Schema : providers . Schema { } ,
2025-03-12 05:33:18 -04:00
} ,
& stackstate . AppliedChangeInputVariable {
Addr : mustStackInputVariable ( "input" ) ,
Value : cty . SetVal ( [ ] cty . Value {
cty . StringVal ( "added" ) ,
} ) ,
} ,
} ,
} ,
} ,
} ,
2025-04-04 09:01:37 -04:00
"removed stack instance" : {
path : filepath . Join ( "with-single-input" , "removed-stack-instance-dynamic" ) ,
state : stackstate . NewStateBuilder ( ) .
AddComponentInstance ( stackstate . NewComponentInstanceBuilder ( mustAbsComponentInstance ( "stack.simple[\"removed\"].component.self" ) ) .
AddInputVariable ( "id" , cty . StringVal ( "removed" ) ) .
AddInputVariable ( "input" , cty . StringVal ( "removed" ) ) ) .
AddResourceInstance ( stackstate . NewResourceInstanceBuilder ( ) .
SetAddr ( mustAbsResourceInstanceObject ( "stack.simple[\"removed\"].component.self.testing_resource.data" ) ) .
SetProviderAddr ( mustDefaultRootProvider ( "testing" ) ) .
SetResourceInstanceObjectSrc ( states . ResourceInstanceObjectSrc {
Status : states . ObjectReady ,
AttrsJSON : mustMarshalJSONAttrs ( map [ string ] any {
"id" : "removed" ,
"value" : "removed" ,
} ) ,
} ) ) .
Build ( ) ,
store : stacks_testing_provider . NewResourceStoreBuilder ( ) .
AddResource ( "removed" , cty . ObjectVal ( map [ string ] cty . Value {
"id" : cty . StringVal ( "removed" ) ,
"value" : cty . StringVal ( "removed" ) ,
} ) ) .
Build ( ) ,
cycles : [ ] TestCycle {
{
planInputs : map [ string ] cty . Value {
"input" : cty . MapVal ( map [ string ] cty . Value {
"added" : cty . StringVal ( "added" ) ,
} ) ,
"removed" : cty . MapVal ( map [ string ] cty . Value {
"removed" : cty . StringVal ( "removed" ) ,
} ) ,
} ,
wantAppliedChanges : [ ] stackstate . AppliedChange {
& stackstate . AppliedChangeComponentInstance {
ComponentAddr : mustAbsComponent ( "stack.simple[\"added\"].component.self" ) ,
ComponentInstanceAddr : mustAbsComponentInstance ( "stack.simple[\"added\"].component.self" ) ,
OutputValues : make ( map [ addrs . OutputValue ] cty . Value ) ,
InputVariables : map [ addrs . InputVariable ] cty . Value {
mustInputVariable ( "id" ) : cty . StringVal ( "added" ) ,
mustInputVariable ( "input" ) : cty . StringVal ( "added" ) ,
} ,
} ,
& stackstate . AppliedChangeResourceInstanceObject {
ResourceInstanceObjectAddr : mustAbsResourceInstanceObject ( "stack.simple[\"added\"].component.self.testing_resource.data" ) ,
NewStateSrc : & states . ResourceInstanceObjectSrc {
AttrsJSON : mustMarshalJSONAttrs ( map [ string ] any {
"id" : "added" ,
"value" : "added" ,
} ) ,
Status : states . ObjectReady ,
Dependencies : make ( [ ] addrs . ConfigResource , 0 ) ,
} ,
ProviderConfigAddr : mustDefaultRootProvider ( "testing" ) ,
Schema : stacks_testing_provider . TestingResourceSchema ,
} ,
& stackstate . AppliedChangeComponentInstanceRemoved {
ComponentAddr : mustAbsComponent ( "stack.simple[\"removed\"].component.self" ) ,
ComponentInstanceAddr : mustAbsComponentInstance ( "stack.simple[\"removed\"].component.self" ) ,
} ,
& stackstate . AppliedChangeResourceInstanceObject {
ResourceInstanceObjectAddr : mustAbsResourceInstanceObject ( "stack.simple[\"removed\"].component.self.testing_resource.data" ) ,
ProviderConfigAddr : mustDefaultRootProvider ( "testing" ) ,
NewStateSrc : nil ,
Schema : providers . Schema { } ,
} ,
& stackstate . AppliedChangeInputVariable {
Addr : mustStackInputVariable ( "input" ) ,
Value : cty . MapVal ( map [ string ] cty . Value {
"added" : cty . StringVal ( "added" ) ,
} ) ,
} ,
& stackstate . AppliedChangeInputVariable {
Addr : mustStackInputVariable ( "removed" ) ,
Value : cty . MapVal ( map [ string ] cty . Value {
"removed" : cty . StringVal ( "removed" ) ,
} ) ,
} ,
2025-04-24 03:22:50 -04:00
& stackstate . AppliedChangeInputVariable {
Addr : mustStackInputVariable ( "removed-direct" ) ,
Value : cty . SetValEmpty ( cty . String ) ,
} ,
2025-04-04 09:01:37 -04:00
} ,
} ,
} ,
} ,
2025-04-03 04:29:18 -04:00
"removed embedded dynamic component from stack" : {
path : filepath . Join ( "with-single-input" , "removed-component-from-stack-dynamic" ) ,
state : stackstate . NewStateBuilder ( ) .
AddComponentInstance ( stackstate . NewComponentInstanceBuilder ( mustAbsComponentInstance ( "stack.for_each.component.self[\"removed\"]" ) ) .
AddInputVariable ( "id" , cty . StringVal ( "removed" ) ) .
AddInputVariable ( "input" , cty . StringVal ( "removed" ) ) ) .
AddResourceInstance ( stackstate . NewResourceInstanceBuilder ( ) .
SetAddr ( mustAbsResourceInstanceObject ( "stack.for_each.component.self[\"removed\"].testing_resource.data" ) ) .
SetProviderAddr ( mustDefaultRootProvider ( "testing" ) ) .
SetResourceInstanceObjectSrc ( states . ResourceInstanceObjectSrc {
Status : states . ObjectReady ,
AttrsJSON : mustMarshalJSONAttrs ( map [ string ] any {
"id" : "removed" ,
"value" : "removed" ,
} ) ,
} ) ) .
Build ( ) ,
store : stacks_testing_provider . NewResourceStoreBuilder ( ) .
AddResource ( "removed" , cty . ObjectVal ( map [ string ] cty . Value {
"id" : cty . StringVal ( "removed" ) ,
"value" : cty . StringVal ( "removed" ) ,
} ) ) .
Build ( ) ,
cycles : [ ] TestCycle {
{
planInputs : map [ string ] cty . Value {
"for_each_input" : cty . MapVal ( map [ string ] cty . Value {
"added" : cty . StringVal ( "added" ) ,
} ) ,
"for_each_removed" : cty . SetVal ( [ ] cty . Value {
cty . StringVal ( "removed" ) ,
} ) ,
} ,
wantAppliedChanges : [ ] stackstate . AppliedChange {
& stackstate . AppliedChangeComponentInstance {
ComponentAddr : mustAbsComponent ( "stack.for_each.component.self" ) ,
ComponentInstanceAddr : mustAbsComponentInstance ( "stack.for_each.component.self[\"added\"]" ) ,
OutputValues : make ( map [ addrs . OutputValue ] cty . Value ) ,
InputVariables : map [ addrs . InputVariable ] cty . Value {
mustInputVariable ( "id" ) : cty . StringVal ( "added" ) ,
mustInputVariable ( "input" ) : cty . StringVal ( "added" ) ,
} ,
} ,
& stackstate . AppliedChangeResourceInstanceObject {
ResourceInstanceObjectAddr : mustAbsResourceInstanceObject ( "stack.for_each.component.self[\"added\"].testing_resource.data" ) ,
NewStateSrc : & states . ResourceInstanceObjectSrc {
AttrsJSON : mustMarshalJSONAttrs ( map [ string ] any {
"id" : "added" ,
"value" : "added" ,
} ) ,
Status : states . ObjectReady ,
Dependencies : make ( [ ] addrs . ConfigResource , 0 ) ,
} ,
ProviderConfigAddr : mustDefaultRootProvider ( "testing" ) ,
Schema : stacks_testing_provider . TestingResourceSchema ,
} ,
& stackstate . AppliedChangeComponentInstanceRemoved {
ComponentAddr : mustAbsComponent ( "stack.for_each.component.self[\"removed\"]" ) ,
ComponentInstanceAddr : mustAbsComponentInstance ( "stack.for_each.component.self[\"removed\"]" ) ,
} ,
& stackstate . AppliedChangeResourceInstanceObject {
ResourceInstanceObjectAddr : mustAbsResourceInstanceObject ( "stack.for_each.component.self[\"removed\"].testing_resource.data" ) ,
ProviderConfigAddr : mustDefaultRootProvider ( "testing" ) ,
NewStateSrc : nil ,
Schema : providers . Schema { } ,
} ,
& stackstate . AppliedChangeInputVariable {
Addr : mustStackInputVariable ( "for_each_input" ) ,
Value : cty . MapVal ( map [ string ] cty . Value {
"added" : cty . StringVal ( "added" ) ,
} ) ,
} ,
& stackstate . AppliedChangeInputVariable {
Addr : mustStackInputVariable ( "for_each_removed" ) ,
Value : cty . SetVal ( [ ] cty . Value {
cty . StringVal ( "removed" ) ,
} ) ,
} ,
& stackstate . AppliedChangeInputVariable {
Addr : mustStackInputVariable ( "simple_input" ) ,
Value : cty . MapValEmpty ( cty . String ) ,
} ,
& stackstate . AppliedChangeInputVariable {
Addr : mustStackInputVariable ( "simple_removed" ) ,
Value : cty . SetValEmpty ( cty . String ) ,
} ,
} ,
} ,
} ,
} ,
"removed embedded component relative" : {
path : filepath . Join ( "with-single-input" , "removed-component-from-stack" ) ,
state : stackstate . NewStateBuilder ( ) .
AddComponentInstance ( stackstate . NewComponentInstanceBuilder ( mustAbsComponentInstance ( "stack.nested.component.self[\"foo\"]" ) ) .
AddInputVariable ( "id" , cty . StringVal ( "removed" ) ) .
AddInputVariable ( "input" , cty . StringVal ( "removed" ) ) ) .
AddResourceInstance ( stackstate . NewResourceInstanceBuilder ( ) .
SetAddr ( mustAbsResourceInstanceObject ( "stack.nested.component.self[\"foo\"].testing_resource.data" ) ) .
SetProviderAddr ( mustDefaultRootProvider ( "testing" ) ) .
SetResourceInstanceObjectSrc ( states . ResourceInstanceObjectSrc {
Status : states . ObjectReady ,
AttrsJSON : mustMarshalJSONAttrs ( map [ string ] any {
"id" : "removed" ,
"value" : "removed" ,
} ) ,
} ) ) .
Build ( ) ,
store : stacks_testing_provider . NewResourceStoreBuilder ( ) .
AddResource ( "removed" , cty . ObjectVal ( map [ string ] cty . Value {
"id" : cty . StringVal ( "removed" ) ,
"value" : cty . StringVal ( "removed" ) ,
} ) ) .
Build ( ) ,
cycles : [ ] TestCycle {
{
wantPlannedChanges : [ ] stackplan . PlannedChange {
& stackplan . PlannedChangeApplyable {
Applyable : true ,
} ,
& stackplan . PlannedChangeHeader {
TerraformVersion : version . SemVer ,
} ,
& stackplan . PlannedChangePlannedTimestamp {
PlannedTimestamp : fakePlanTimestamp ,
} ,
& stackplan . PlannedChangeComponentInstance {
Addr : mustAbsComponentInstance ( "stack.nested.component.self[\"foo\"]" ) ,
PlanComplete : true ,
PlanApplyable : true ,
Mode : plans . DestroyMode ,
Action : plans . Delete ,
PlannedInputValues : map [ string ] plans . DynamicValue {
"id" : mustPlanDynamicValueDynamicType ( cty . StringVal ( "removed" ) ) ,
"input" : mustPlanDynamicValueDynamicType ( cty . StringVal ( "removed" ) ) ,
} ,
PlannedInputValueMarks : map [ string ] [ ] cty . PathValueMarks {
"input" : nil ,
"id" : nil ,
} ,
PlannedOutputValues : make ( map [ string ] cty . Value ) ,
PlannedCheckResults : & states . CheckResults { } ,
PlanTimestamp : fakePlanTimestamp ,
} ,
& stackplan . PlannedChangeResourceInstancePlanned {
ResourceInstanceObjectAddr : mustAbsResourceInstanceObject ( "stack.nested.component.self[\"foo\"].testing_resource.data" ) ,
ChangeSrc : & plans . ResourceInstanceChangeSrc {
Addr : mustAbsResourceInstance ( "testing_resource.data" ) ,
PrevRunAddr : mustAbsResourceInstance ( "testing_resource.data" ) ,
ChangeSrc : plans . ChangeSrc {
Action : plans . Delete ,
Before : mustPlanDynamicValue ( cty . ObjectVal ( map [ string ] cty . Value {
"id" : cty . StringVal ( "removed" ) ,
"value" : cty . StringVal ( "removed" ) ,
} ) ) ,
After : mustPlanDynamicValue ( cty . NullVal ( cty . Object ( map [ string ] cty . Type {
"id" : cty . String ,
"value" : cty . String ,
} ) ) ) ,
} ,
ProviderAddr : mustDefaultRootProvider ( "testing" ) ,
} ,
PriorStateSrc : & states . ResourceInstanceObjectSrc {
AttrsJSON : mustMarshalJSONAttrs ( map [ string ] any {
"id" : "removed" ,
"value" : "removed" ,
} ) ,
Dependencies : make ( [ ] addrs . ConfigResource , 0 ) ,
Status : states . ObjectReady ,
} ,
ProviderConfigAddr : mustDefaultRootProvider ( "testing" ) ,
Schema : stacks_testing_provider . TestingResourceSchema ,
} ,
} ,
wantAppliedChanges : [ ] stackstate . AppliedChange {
& stackstate . AppliedChangeComponentInstanceRemoved {
ComponentAddr : mustAbsComponent ( "stack.nested.component.self[\"foo\"]" ) ,
ComponentInstanceAddr : mustAbsComponentInstance ( "stack.nested.component.self[\"foo\"]" ) ,
} ,
& stackstate . AppliedChangeResourceInstanceObject {
ResourceInstanceObjectAddr : mustAbsResourceInstanceObject ( "stack.nested.component.self[\"foo\"].testing_resource.data" ) ,
ProviderConfigAddr : mustDefaultRootProvider ( "testing" ) ,
NewStateSrc : nil ,
Schema : providers . Schema { } ,
} ,
} ,
} ,
} ,
} ,
"removed embedded component local" : {
2024-09-18 04:41:36 -04:00
path : filepath . Join ( "with-single-input" , "removed-embedded-component" ) ,
state : stackstate . NewStateBuilder ( ) .
AddComponentInstance ( stackstate . NewComponentInstanceBuilder ( mustAbsComponentInstance ( "stack.a.component.self" ) ) .
AddInputVariable ( "id" , cty . StringVal ( "removed" ) ) .
AddInputVariable ( "input" , cty . StringVal ( "removed" ) ) ) .
AddResourceInstance ( stackstate . NewResourceInstanceBuilder ( ) .
SetAddr ( mustAbsResourceInstanceObject ( "stack.a.component.self.testing_resource.data" ) ) .
SetProviderAddr ( mustDefaultRootProvider ( "testing" ) ) .
SetResourceInstanceObjectSrc ( states . ResourceInstanceObjectSrc {
Status : states . ObjectReady ,
AttrsJSON : mustMarshalJSONAttrs ( map [ string ] any {
"id" : "removed" ,
"value" : "removed" ,
} ) ,
} ) ) .
Build ( ) ,
store : stacks_testing_provider . NewResourceStoreBuilder ( ) .
AddResource ( "removed" , cty . ObjectVal ( map [ string ] cty . Value {
"id" : cty . StringVal ( "removed" ) ,
"value" : cty . StringVal ( "removed" ) ,
} ) ) .
Build ( ) ,
cycles : [ ] TestCycle {
{
wantPlannedChanges : [ ] stackplan . PlannedChange {
& stackplan . PlannedChangeApplyable {
Applyable : true ,
} ,
& stackplan . PlannedChangeHeader {
TerraformVersion : version . SemVer ,
} ,
& stackplan . PlannedChangePlannedTimestamp {
PlannedTimestamp : fakePlanTimestamp ,
} ,
& stackplan . PlannedChangeComponentInstance {
Addr : mustAbsComponentInstance ( "stack.a.component.self" ) ,
PlanComplete : true ,
PlanApplyable : true ,
Mode : plans . DestroyMode ,
Action : plans . Delete ,
PlannedInputValues : map [ string ] plans . DynamicValue {
"id" : mustPlanDynamicValueDynamicType ( cty . StringVal ( "removed" ) ) ,
"input" : mustPlanDynamicValueDynamicType ( cty . StringVal ( "removed" ) ) ,
} ,
PlannedInputValueMarks : map [ string ] [ ] cty . PathValueMarks {
"input" : nil ,
"id" : nil ,
} ,
PlannedOutputValues : make ( map [ string ] cty . Value ) ,
PlannedCheckResults : & states . CheckResults { } ,
PlanTimestamp : fakePlanTimestamp ,
} ,
& stackplan . PlannedChangeResourceInstancePlanned {
ResourceInstanceObjectAddr : mustAbsResourceInstanceObject ( "stack.a.component.self.testing_resource.data" ) ,
ChangeSrc : & plans . ResourceInstanceChangeSrc {
Addr : mustAbsResourceInstance ( "testing_resource.data" ) ,
PrevRunAddr : mustAbsResourceInstance ( "testing_resource.data" ) ,
ChangeSrc : plans . ChangeSrc {
Action : plans . Delete ,
Before : mustPlanDynamicValue ( cty . ObjectVal ( map [ string ] cty . Value {
"id" : cty . StringVal ( "removed" ) ,
"value" : cty . StringVal ( "removed" ) ,
} ) ) ,
After : mustPlanDynamicValue ( cty . NullVal ( cty . Object ( map [ string ] cty . Type {
"id" : cty . String ,
"value" : cty . String ,
} ) ) ) ,
} ,
ProviderAddr : mustDefaultRootProvider ( "testing" ) ,
} ,
PriorStateSrc : & states . ResourceInstanceObjectSrc {
AttrsJSON : mustMarshalJSONAttrs ( map [ string ] any {
"id" : "removed" ,
"value" : "removed" ,
} ) ,
Dependencies : make ( [ ] addrs . ConfigResource , 0 ) ,
Status : states . ObjectReady ,
} ,
ProviderConfigAddr : mustDefaultRootProvider ( "testing" ) ,
Schema : stacks_testing_provider . TestingResourceSchema ,
} ,
} ,
wantAppliedChanges : [ ] stackstate . AppliedChange {
& stackstate . AppliedChangeComponentInstanceRemoved {
ComponentAddr : mustAbsComponent ( "stack.a.component.self" ) ,
ComponentInstanceAddr : mustAbsComponentInstance ( "stack.a.component.self" ) ,
} ,
& stackstate . AppliedChangeResourceInstanceObject {
ResourceInstanceObjectAddr : mustAbsResourceInstanceObject ( "stack.a.component.self.testing_resource.data" ) ,
ProviderConfigAddr : mustDefaultRootProvider ( "testing" ) ,
NewStateSrc : nil ,
2025-03-11 15:58:44 -04:00
Schema : providers . Schema { } ,
2024-09-18 04:41:36 -04:00
} ,
2024-09-16 05:36:36 -04:00
} ,
} ,
2024-02-15 04:45:47 -05:00
} ,
} ,
2024-09-18 04:41:36 -04:00
"forgotten component" : {
path : filepath . Join ( "with-single-input" , "forgotten-component" ) ,
state : stackstate . NewStateBuilder ( ) .
AddComponentInstance ( stackstate . NewComponentInstanceBuilder ( mustAbsComponentInstance ( "component.self" ) ) .
AddInputVariable ( "id" , cty . StringVal ( "removed" ) ) .
AddInputVariable ( "input" , cty . StringVal ( "removed" ) ) ) .
AddResourceInstance ( stackstate . NewResourceInstanceBuilder ( ) .
SetAddr ( mustAbsResourceInstanceObject ( "component.self.testing_resource.data" ) ) .
SetProviderAddr ( mustDefaultRootProvider ( "testing" ) ) .
SetResourceInstanceObjectSrc ( states . ResourceInstanceObjectSrc {
Status : states . ObjectReady ,
AttrsJSON : mustMarshalJSONAttrs ( map [ string ] any {
"id" : "removed" ,
"value" : "removed" ,
} ) ,
} ) ) .
Build ( ) ,
store : stacks_testing_provider . NewResourceStoreBuilder ( ) .
AddResource ( "removed" , cty . ObjectVal ( map [ string ] cty . Value {
"id" : cty . StringVal ( "removed" ) ,
"value" : cty . StringVal ( "removed" ) ,
} ) ) .
Build ( ) ,
cycles : [ ] TestCycle {
{
planInputs : map [ string ] cty . Value {
"destroy" : cty . BoolVal ( false ) ,
} ,
wantPlannedChanges : [ ] stackplan . PlannedChange {
& stackplan . PlannedChangeApplyable {
Applyable : true ,
} ,
& stackplan . PlannedChangeComponentInstance {
Addr : mustAbsComponentInstance ( "component.self" ) ,
PlanComplete : true ,
PlanApplyable : true ,
Mode : plans . DestroyMode ,
Action : plans . Forget ,
PlannedInputValues : map [ string ] plans . DynamicValue {
"id" : mustPlanDynamicValueDynamicType ( cty . StringVal ( "removed" ) ) ,
"input" : mustPlanDynamicValueDynamicType ( cty . StringVal ( "removed" ) ) ,
2024-02-15 04:45:47 -05:00
} ,
2024-09-18 04:41:36 -04:00
PlannedInputValueMarks : map [ string ] [ ] cty . PathValueMarks {
"input" : nil ,
"id" : nil ,
} ,
PlannedOutputValues : make ( map [ string ] cty . Value ) ,
PlannedCheckResults : & states . CheckResults { } ,
PlanTimestamp : fakePlanTimestamp ,
2024-02-15 04:45:47 -05:00
} ,
2024-09-18 04:41:36 -04:00
& stackplan . PlannedChangeResourceInstancePlanned {
ResourceInstanceObjectAddr : mustAbsResourceInstanceObject ( "component.self.testing_resource.data" ) ,
ChangeSrc : & plans . ResourceInstanceChangeSrc {
Addr : mustAbsResourceInstance ( "testing_resource.data" ) ,
PrevRunAddr : mustAbsResourceInstance ( "testing_resource.data" ) ,
ChangeSrc : plans . ChangeSrc {
Action : plans . Forget ,
Before : mustPlanDynamicValue ( cty . ObjectVal ( map [ string ] cty . Value {
"id" : cty . StringVal ( "removed" ) ,
"value" : cty . StringVal ( "removed" ) ,
} ) ) ,
After : mustPlanDynamicValue ( cty . NullVal ( cty . Object ( map [ string ] cty . Type {
"id" : cty . String ,
"value" : cty . String ,
} ) ) ) ,
2024-02-15 04:45:47 -05:00
} ,
2024-09-18 04:41:36 -04:00
ProviderAddr : mustDefaultRootProvider ( "testing" ) ,
} ,
PriorStateSrc : & states . ResourceInstanceObjectSrc {
AttrsJSON : mustMarshalJSONAttrs ( map [ string ] any {
"id" : "removed" ,
"value" : "removed" ,
} ) ,
Dependencies : make ( [ ] addrs . ConfigResource , 0 ) ,
Status : states . ObjectReady ,
2024-02-15 04:45:47 -05:00
} ,
2024-09-18 04:41:36 -04:00
ProviderConfigAddr : mustDefaultRootProvider ( "testing" ) ,
Schema : stacks_testing_provider . TestingResourceSchema ,
2024-02-15 04:45:47 -05:00
} ,
2024-09-18 04:41:36 -04:00
& stackplan . PlannedChangeHeader {
TerraformVersion : version . SemVer ,
2024-02-16 04:46:50 -05:00
} ,
2024-09-18 04:41:36 -04:00
& stackplan . PlannedChangePlannedTimestamp {
PlannedTimestamp : fakePlanTimestamp ,
2024-02-16 04:46:50 -05:00
} ,
2024-09-18 04:41:36 -04:00
} ,
wantPlannedDiags : initDiags ( func ( diags tfdiags . Diagnostics ) tfdiags . Diagnostics {
return diags . Append ( tfdiags . Sourceless (
tfdiags . Warning ,
"Some objects will no longer be managed by Terraform" ,
` If you apply this plan , Terraform will discard its tracking information for the following objects , but it will not delete them :
- testing_resource . data
2024-02-15 04:45:47 -05:00
2024-09-18 04:41:36 -04:00
After applying this plan , Terraform will no longer manage these objects . You will need to import them into Terraform to manage them again . ` ,
) )
} ) ,
wantAppliedChanges : [ ] stackstate . AppliedChange {
& stackstate . AppliedChangeComponentInstanceRemoved {
ComponentAddr : mustAbsComponent ( "component.self" ) ,
ComponentInstanceAddr : mustAbsComponentInstance ( "component.self" ) ,
} ,
& stackstate . AppliedChangeResourceInstanceObject {
ResourceInstanceObjectAddr : mustAbsResourceInstanceObject ( "component.self.testing_resource.data" ) ,
ProviderConfigAddr : mustDefaultRootProvider ( "testing" ) ,
NewStateSrc : nil ,
2025-03-11 15:58:44 -04:00
Schema : providers . Schema { } ,
2024-09-18 04:41:36 -04:00
} ,
} ,
2024-02-15 04:45:47 -05:00
} ,
} ,
} ,
2024-09-18 04:41:36 -04:00
"orphaned component" : {
path : filepath . Join ( "with-single-input" , "valid" ) ,
state : stackstate . NewStateBuilder ( ) .
AddComponentInstance ( stackstate . NewComponentInstanceBuilder ( mustAbsComponentInstance ( "component.orphan" ) ) ) .
Build ( ) ,
cycles : [ ] TestCycle {
{
planInputs : map [ string ] cty . Value {
"id" : cty . StringVal ( "foo" ) ,
"input" : cty . StringVal ( "bar" ) ,
} ,
wantPlannedChanges : [ ] stackplan . PlannedChange {
& stackplan . PlannedChangeApplyable {
Applyable : true ,
} ,
& stackplan . PlannedChangeComponentInstanceRemoved {
// The orphaned component is just silently being removed.
Addr : mustAbsComponentInstance ( "component.orphan" ) ,
} ,
& stackplan . PlannedChangeComponentInstance {
Addr : mustAbsComponentInstance ( "component.self" ) ,
PlanApplyable : true ,
PlanComplete : true ,
Action : plans . Create ,
PlannedInputValues : map [ string ] plans . DynamicValue {
"id" : mustPlanDynamicValueDynamicType ( cty . StringVal ( "foo" ) ) ,
"input" : mustPlanDynamicValueDynamicType ( cty . StringVal ( "bar" ) ) ,
2024-06-27 12:01:20 -04:00
} ,
2024-09-18 04:41:36 -04:00
PlannedInputValueMarks : map [ string ] [ ] cty . PathValueMarks {
"id" : nil ,
"input" : nil ,
} ,
PlannedOutputValues : make ( map [ string ] cty . Value ) ,
PlannedCheckResults : & states . CheckResults { } ,
PlanTimestamp : fakePlanTimestamp ,
2024-06-27 12:01:20 -04:00
} ,
2024-09-18 04:41:36 -04:00
& stackplan . PlannedChangeResourceInstancePlanned {
ResourceInstanceObjectAddr : mustAbsResourceInstanceObject ( "component.self.testing_resource.data" ) ,
ChangeSrc : & plans . ResourceInstanceChangeSrc {
Addr : mustAbsResourceInstance ( "testing_resource.data" ) ,
PrevRunAddr : mustAbsResourceInstance ( "testing_resource.data" ) ,
ProviderAddr : mustDefaultRootProvider ( "testing" ) ,
ChangeSrc : plans . ChangeSrc {
Action : plans . Create ,
Before : mustPlanDynamicValue ( cty . NullVal ( cty . DynamicPseudoType ) ) ,
After : mustPlanDynamicValue ( cty . ObjectVal ( map [ string ] cty . Value {
"id" : cty . StringVal ( "foo" ) ,
"value" : cty . StringVal ( "bar" ) ,
} ) ) ,
2024-06-27 12:01:20 -04:00
} ,
} ,
2024-09-18 04:41:36 -04:00
ProviderConfigAddr : mustDefaultRootProvider ( "testing" ) ,
Schema : stacks_testing_provider . TestingResourceSchema ,
} ,
& stackplan . PlannedChangeHeader {
TerraformVersion : version . SemVer ,
} ,
& stackplan . PlannedChangePlannedTimestamp {
PlannedTimestamp : fakePlanTimestamp ,
} ,
& stackplan . PlannedChangeRootInputValue {
Addr : stackaddrs . InputVariable {
Name : "id" ,
} ,
Action : plans . Create ,
Before : cty . NullVal ( cty . DynamicPseudoType ) ,
After : cty . StringVal ( "foo" ) ,
} ,
& stackplan . PlannedChangeRootInputValue {
Addr : stackaddrs . InputVariable {
Name : "input" ,
} ,
Action : plans . Create ,
Before : cty . NullVal ( cty . DynamicPseudoType ) ,
After : cty . StringVal ( "bar" ) ,
2024-06-27 12:01:20 -04:00
} ,
} ,
2024-09-18 04:41:36 -04:00
wantAppliedChanges : [ ] stackstate . AppliedChange {
& stackstate . AppliedChangeComponentInstanceRemoved {
// The orphaned component is just silently being removed.
ComponentAddr : mustAbsComponent ( "component.orphan" ) ,
ComponentInstanceAddr : mustAbsComponentInstance ( "component.orphan" ) ,
} ,
& stackstate . AppliedChangeComponentInstance {
ComponentAddr : mustAbsComponent ( "component.self" ) ,
ComponentInstanceAddr : mustAbsComponentInstance ( "component.self" ) ,
OutputValues : make ( map [ addrs . OutputValue ] cty . Value ) ,
InputVariables : map [ addrs . InputVariable ] cty . Value {
mustInputVariable ( "id" ) : cty . StringVal ( "foo" ) ,
mustInputVariable ( "input" ) : cty . StringVal ( "bar" ) ,
} ,
} ,
& stackstate . AppliedChangeResourceInstanceObject {
ResourceInstanceObjectAddr : mustAbsResourceInstanceObject ( "component.self.testing_resource.data" ) ,
NewStateSrc : & states . ResourceInstanceObjectSrc {
AttrsJSON : mustMarshalJSONAttrs ( map [ string ] interface { } {
"id" : "foo" ,
"value" : "bar" ,
} ) ,
Dependencies : make ( [ ] addrs . ConfigResource , 0 ) ,
Status : states . ObjectReady ,
} ,
ProviderConfigAddr : mustDefaultRootProvider ( "testing" ) ,
Schema : stacks_testing_provider . TestingResourceSchema ,
} ,
& stackstate . AppliedChangeInputVariable {
Addr : mustStackInputVariable ( "id" ) ,
Value : cty . StringVal ( "foo" ) ,
} ,
& stackstate . AppliedChangeInputVariable {
Addr : mustStackInputVariable ( "input" ) ,
Value : cty . StringVal ( "bar" ) ,
} ,
} ,
} ,
2024-06-27 12:01:20 -04:00
} ,
} ,
2024-09-18 04:41:36 -04:00
"forget with dependency" : {
path : "forget_with_dependency" ,
state : stackstate . NewStateBuilder ( ) .
AddComponentInstance ( stackstate . NewComponentInstanceBuilder ( mustAbsComponentInstance ( "component.one" ) ) .
AddDependent ( mustAbsComponent ( "component.two" ) ) .
AddInputVariable ( "value" , cty . StringVal ( "bar" ) ) .
AddOutputValue ( "id" , cty . StringVal ( "foo" ) ) ) .
AddResourceInstance ( stackstate . NewResourceInstanceBuilder ( ) .
SetAddr ( mustAbsResourceInstanceObject ( "component.one.testing_resource.resource" ) ) .
SetProviderAddr ( mustDefaultRootProvider ( "testing" ) ) .
SetResourceInstanceObjectSrc ( states . ResourceInstanceObjectSrc {
AttrsJSON : mustMarshalJSONAttrs ( map [ string ] interface { } {
"id" : "foo" ,
"value" : "bar" ,
} ) ,
Status : states . ObjectReady ,
} ) ) .
AddComponentInstance ( stackstate . NewComponentInstanceBuilder ( mustAbsComponentInstance ( "component.two" ) ) .
AddDependency ( mustAbsComponent ( "component.one" ) ) .
AddInputVariable ( "value" , cty . StringVal ( "foo" ) ) .
AddOutputValue ( "id" , cty . StringVal ( "baz" ) ) ) .
AddResourceInstance ( stackstate . NewResourceInstanceBuilder ( ) .
SetAddr ( mustAbsResourceInstanceObject ( "component.two.testing_resource.resource" ) ) .
SetProviderAddr ( mustDefaultRootProvider ( "testing" ) ) .
SetResourceInstanceObjectSrc ( states . ResourceInstanceObjectSrc {
AttrsJSON : mustMarshalJSONAttrs ( map [ string ] interface { } {
"id" : "baz" ,
"value" : "foo" ,
} ) ,
Status : states . ObjectReady ,
} ) ) .
Build ( ) ,
store : stacks_testing_provider . NewResourceStoreBuilder ( ) .
AddResource ( "foo" , cty . ObjectVal ( map [ string ] cty . Value {
"id" : cty . StringVal ( "foo" ) ,
"value" : cty . StringVal ( "bar" ) ,
} ) ) .
AddResource ( "baz" , cty . ObjectVal ( map [ string ] cty . Value {
"id" : cty . StringVal ( "baz" ) ,
"value" : cty . StringVal ( "foo" ) ,
} ) ) .
Build ( ) ,
cycles : [ ] TestCycle {
{
planMode : plans . NormalMode ,
wantPlannedDiags : tfdiags . Diagnostics {
tfdiags . Sourceless ( tfdiags . Warning , "Some objects will no longer be managed by Terraform" , ` If you apply this plan , Terraform will discard its tracking information for the following objects , but it will not delete them :
- testing_resource . resource
2024-06-27 12:01:20 -04:00
2024-09-18 04:41:36 -04:00
After applying this plan , Terraform will no longer manage these objects . You will need to import them into Terraform to manage them again . ` ) ,
} ,
wantAppliedChanges : [ ] stackstate . AppliedChange {
& stackstate . AppliedChangeComponentInstance {
ComponentAddr : mustAbsComponent ( "component.one" ) ,
ComponentInstanceAddr : mustAbsComponentInstance ( "component.one" ) ,
OutputValues : map [ addrs . OutputValue ] cty . Value {
addrs . OutputValue { Name : "id" } : cty . StringVal ( "foo" ) ,
} ,
InputVariables : map [ addrs . InputVariable ] cty . Value {
addrs . InputVariable { Name : "value" } : cty . StringVal ( "bar" ) ,
} ,
Dependents : collections . NewSet ( mustAbsComponent ( "component.two" ) ) ,
} ,
& stackstate . AppliedChangeResourceInstanceObject {
ResourceInstanceObjectAddr : mustAbsResourceInstanceObject ( "component.one.testing_resource.resource" ) ,
NewStateSrc : & states . ResourceInstanceObjectSrc {
AttrsJSON : mustMarshalJSONAttrs ( map [ string ] interface { } {
"id" : "foo" ,
"value" : "bar" ,
} ) ,
Status : states . ObjectReady ,
AttrSensitivePaths : make ( [ ] cty . Path , 0 ) ,
} ,
ProviderConfigAddr : addrs . AbsProviderConfig {
Provider : addrs . MustParseProviderSourceString ( "hashicorp/testing" ) ,
} ,
Schema : stacks_testing_provider . TestingResourceSchema ,
} ,
& stackstate . AppliedChangeComponentInstanceRemoved {
ComponentAddr : mustAbsComponent ( "component.two" ) ,
ComponentInstanceAddr : mustAbsComponentInstance ( "component.two" ) ,
} ,
& stackstate . AppliedChangeResourceInstanceObject {
ResourceInstanceObjectAddr : mustAbsResourceInstanceObject ( "component.two.testing_resource.resource" ) ,
NewStateSrc : nil , // Resource is forgotten
ProviderConfigAddr : addrs . AbsProviderConfig {
Provider : addrs . MustParseProviderSourceString ( "hashicorp/testing" ) ,
} ,
} ,
} ,
} ,
} ,
} ,
"forget with dependency on component to forget" : {
path : "forget_with_dependency_to_forget" ,
state : stackstate . NewStateBuilder ( ) .
AddComponentInstance ( stackstate . NewComponentInstanceBuilder ( mustAbsComponentInstance ( "component.one" ) ) .
AddDependent ( mustAbsComponent ( "component.two" ) ) .
AddInputVariable ( "value" , cty . StringVal ( "bar" ) ) .
AddOutputValue ( "id" , cty . StringVal ( "foo" ) ) ) .
AddResourceInstance ( stackstate . NewResourceInstanceBuilder ( ) .
SetAddr ( mustAbsResourceInstanceObject ( "component.one.testing_resource.resource" ) ) .
SetProviderAddr ( mustDefaultRootProvider ( "testing" ) ) .
SetResourceInstanceObjectSrc ( states . ResourceInstanceObjectSrc {
AttrsJSON : mustMarshalJSONAttrs ( map [ string ] interface { } {
"id" : "foo" ,
"value" : "bar" ,
} ) ,
Status : states . ObjectReady ,
} ) ) .
AddComponentInstance ( stackstate . NewComponentInstanceBuilder ( mustAbsComponentInstance ( "component.two" ) ) .
AddDependency ( mustAbsComponent ( "component.one" ) ) .
AddInputVariable ( "value" , cty . StringVal ( "foo" ) ) .
AddOutputValue ( "id" , cty . StringVal ( "baz" ) ) ) .
AddResourceInstance ( stackstate . NewResourceInstanceBuilder ( ) .
SetAddr ( mustAbsResourceInstanceObject ( "component.two.testing_resource.resource" ) ) .
SetProviderAddr ( mustDefaultRootProvider ( "testing" ) ) .
SetResourceInstanceObjectSrc ( states . ResourceInstanceObjectSrc {
AttrsJSON : mustMarshalJSONAttrs ( map [ string ] interface { } {
"id" : "baz" ,
"value" : "foo" ,
} ) ,
Status : states . ObjectReady ,
} ) ) .
Build ( ) ,
store : stacks_testing_provider . NewResourceStoreBuilder ( ) .
AddResource ( "foo" , cty . ObjectVal ( map [ string ] cty . Value {
"id" : cty . StringVal ( "foo" ) ,
"value" : cty . StringVal ( "bar" ) ,
} ) ) .
AddResource ( "baz" , cty . ObjectVal ( map [ string ] cty . Value {
"id" : cty . StringVal ( "baz" ) ,
"value" : cty . StringVal ( "foo" ) ,
} ) ) .
Build ( ) ,
cycles : [ ] TestCycle {
{
planMode : plans . NormalMode ,
wantPlannedDiags : tfdiags . Diagnostics {
tfdiags . Sourceless ( tfdiags . Warning , "Some objects will no longer be managed by Terraform" , ` If you apply this plan , Terraform will discard its tracking information for the following objects , but it will not delete them :
- testing_resource . resource
After applying this plan , Terraform will no longer manage these objects . You will need to import them into Terraform to manage them again . ` ) ,
tfdiags . Sourceless ( tfdiags . Warning , "Some objects will no longer be managed by Terraform" , ` If you apply this plan , Terraform will discard its tracking information for the following objects , but it will not delete them :
- testing_resource . resource
After applying this plan , Terraform will no longer manage these objects . You will need to import them into Terraform to manage them again . ` ) ,
} ,
wantAppliedChanges : [ ] stackstate . AppliedChange {
& stackstate . AppliedChangeComponentInstanceRemoved {
ComponentAddr : mustAbsComponent ( "component.one" ) ,
ComponentInstanceAddr : mustAbsComponentInstance ( "component.one" ) ,
} ,
& stackstate . AppliedChangeResourceInstanceObject {
ResourceInstanceObjectAddr : mustAbsResourceInstanceObject ( "component.one.testing_resource.resource" ) ,
NewStateSrc : nil , // Resource is forgotten
ProviderConfigAddr : addrs . AbsProviderConfig {
Provider : addrs . MustParseProviderSourceString ( "hashicorp/testing" ) ,
} ,
} ,
& stackstate . AppliedChangeComponentInstanceRemoved {
ComponentAddr : mustAbsComponent ( "component.two" ) ,
ComponentInstanceAddr : mustAbsComponentInstance ( "component.two" ) ,
} ,
& stackstate . AppliedChangeResourceInstanceObject {
ResourceInstanceObjectAddr : mustAbsResourceInstanceObject ( "component.two.testing_resource.resource" ) ,
NewStateSrc : nil , // Resource is forgotten
ProviderConfigAddr : addrs . AbsProviderConfig {
Provider : addrs . MustParseProviderSourceString ( "hashicorp/testing" ) ,
} ,
} ,
} ,
} ,
} ,
2024-06-27 12:01:20 -04:00
} ,
2024-09-20 02:20:32 -04:00
"removed block with provider-to-component dep" : {
path : path . Join ( "auth-provider-w-data" , "removed" ) ,
state : stackstate . NewStateBuilder ( ) .
AddComponentInstance ( stackstate . NewComponentInstanceBuilder ( mustAbsComponentInstance ( "component.load" ) ) .
AddDependent ( mustAbsComponent ( "component.create" ) ) .
AddOutputValue ( "credentials" , cty . StringVal ( "wrong" ) ) ) . // must reload the credentials
AddComponentInstance ( stackstate . NewComponentInstanceBuilder ( mustAbsComponentInstance ( "component.create" ) ) .
AddDependency ( mustAbsComponent ( "component.load" ) ) ) .
AddResourceInstance ( stackstate . NewResourceInstanceBuilder ( ) .
SetAddr ( mustAbsResourceInstanceObject ( "component.create.testing_resource.resource" ) ) .
SetResourceInstanceObjectSrc ( states . ResourceInstanceObjectSrc {
AttrsJSON : mustMarshalJSONAttrs ( map [ string ] interface { } {
"id" : "resource" ,
"value" : nil ,
} ) ,
Status : states . ObjectReady ,
} ) .
SetProviderAddr ( mustDefaultRootProvider ( "testing" ) ) ) .
Build ( ) ,
store : stacks_testing_provider . NewResourceStoreBuilder ( ) . AddResource ( "credentials" , cty . ObjectVal ( map [ string ] cty . Value {
"id" : cty . StringVal ( "credentials" ) ,
// we have the wrong value in state, so this correct value must
// be loaded for this test to work.
"value" : cty . StringVal ( "authn" ) ,
} ) ) . Build ( ) ,
cycles : [ ] TestCycle {
{
planMode : plans . NormalMode ,
wantAppliedChanges : [ ] stackstate . AppliedChange {
& stackstate . AppliedChangeComponentInstanceRemoved {
ComponentAddr : mustAbsComponent ( "component.create" ) ,
ComponentInstanceAddr : mustAbsComponentInstance ( "component.create" ) ,
} ,
& stackstate . AppliedChangeResourceInstanceObject {
ResourceInstanceObjectAddr : mustAbsResourceInstanceObject ( "component.create.testing_resource.resource" ) ,
ProviderConfigAddr : mustDefaultRootProvider ( "testing" ) ,
NewStateSrc : nil , // deleted
} ,
& stackstate . AppliedChangeComponentInstance {
ComponentAddr : mustAbsComponent ( "component.load" ) ,
ComponentInstanceAddr : mustAbsComponentInstance ( "component.load" ) ,
OutputValues : map [ addrs . OutputValue ] cty . Value {
addrs . OutputValue { Name : "credentials" } : cty . StringVal ( "authn" ) . Mark ( marks . Sensitive ) ,
} ,
InputVariables : make ( map [ addrs . InputVariable ] cty . Value ) ,
Dependents : collections . NewSet ( mustAbsComponent ( "component.create" ) ) ,
} ,
& stackstate . AppliedChangeResourceInstanceObject {
ResourceInstanceObjectAddr : mustAbsResourceInstanceObject ( "component.load.data.testing_data_source.credentials" ) ,
NewStateSrc : & states . ResourceInstanceObjectSrc {
AttrsJSON : mustMarshalJSONAttrs ( map [ string ] interface { } {
"id" : "credentials" ,
"value" : "authn" ,
} ) ,
AttrSensitivePaths : make ( [ ] cty . Path , 0 ) ,
Status : states . ObjectReady ,
} ,
ProviderConfigAddr : mustDefaultRootProvider ( "testing" ) ,
Schema : stacks_testing_provider . TestingDataSourceSchema ,
} ,
} ,
} ,
} ,
} ,
2024-10-08 10:46:31 -04:00
"ephemeral" : {
path : path . Join ( "with-single-input" , "ephemeral" ) ,
cycles : [ ] TestCycle {
{
planMode : plans . NormalMode ,
planInputs : map [ string ] cty . Value {
"input" : cty . StringVal ( "hello" ) ,
"ephemeral" : cty . StringVal ( "planning" ) ,
} ,
applyInputs : map [ string ] cty . Value {
"ephemeral" : cty . StringVal ( "applying" ) ,
} ,
wantAppliedChanges : [ ] stackstate . AppliedChange {
& stackstate . AppliedChangeComponentInstance {
ComponentAddr : mustAbsComponent ( "component.self" ) ,
ComponentInstanceAddr : mustAbsComponentInstance ( "component.self" ) ,
OutputValues : make ( map [ addrs . OutputValue ] cty . Value ) ,
InputVariables : map [ addrs . InputVariable ] cty . Value {
mustInputVariable ( "id" ) : cty . StringVal ( "2f9f3b84" ) ,
mustInputVariable ( "input" ) : cty . StringVal ( "hello" ) ,
} ,
} ,
& stackstate . AppliedChangeResourceInstanceObject {
ResourceInstanceObjectAddr : mustAbsResourceInstanceObject ( "component.self.testing_resource.data" ) ,
NewStateSrc : & states . ResourceInstanceObjectSrc {
AttrsJSON : mustMarshalJSONAttrs ( map [ string ] interface { } {
"id" : "2f9f3b84" ,
"value" : "hello" ,
} ) ,
Status : states . ObjectReady ,
Dependencies : make ( [ ] addrs . ConfigResource , 0 ) ,
} ,
ProviderConfigAddr : mustDefaultRootProvider ( "testing" ) ,
Schema : stacks_testing_provider . TestingResourceSchema ,
} ,
& stackstate . AppliedChangeInputVariable {
Addr : mustStackInputVariable ( "ephemeral" ) ,
Value : cty . NullVal ( cty . String ) , // ephemeral
} ,
& stackstate . AppliedChangeInputVariable {
Addr : mustStackInputVariable ( "input" ) ,
Value : cty . StringVal ( "hello" ) ,
} ,
} ,
} ,
} ,
} ,
"missing-ephemeral" : {
path : path . Join ( "with-single-input" , "ephemeral" ) ,
cycles : [ ] TestCycle {
{
planMode : plans . NormalMode ,
planInputs : map [ string ] cty . Value {
"input" : cty . StringVal ( "hello" ) ,
"ephemeral" : cty . StringVal ( "planning" ) ,
} ,
applyInputs : make ( map [ string ] cty . Value ) , // deliberately omitting ephemeral
wantAppliedChanges : [ ] stackstate . AppliedChange {
& stackstate . AppliedChangeComponentInstance {
ComponentAddr : mustAbsComponent ( "component.self" ) ,
ComponentInstanceAddr : mustAbsComponentInstance ( "component.self" ) ,
OutputValues : make ( map [ addrs . OutputValue ] cty . Value ) ,
InputVariables : map [ addrs . InputVariable ] cty . Value {
mustInputVariable ( "id" ) : cty . StringVal ( "2f9f3b84" ) ,
mustInputVariable ( "input" ) : cty . StringVal ( "hello" ) ,
} ,
} ,
& stackstate . AppliedChangeResourceInstanceObject {
ResourceInstanceObjectAddr : mustAbsResourceInstanceObject ( "component.self.testing_resource.data" ) ,
NewStateSrc : & states . ResourceInstanceObjectSrc {
AttrsJSON : mustMarshalJSONAttrs ( map [ string ] interface { } {
"id" : "2f9f3b84" ,
"value" : "hello" ,
} ) ,
Status : states . ObjectReady ,
Dependencies : make ( [ ] addrs . ConfigResource , 0 ) ,
} ,
ProviderConfigAddr : mustDefaultRootProvider ( "testing" ) ,
Schema : stacks_testing_provider . TestingResourceSchema ,
} ,
& stackstate . AppliedChangeInputVariable {
Addr : mustStackInputVariable ( "input" ) ,
Value : cty . StringVal ( "hello" ) ,
} ,
} ,
wantAppliedDiags : initDiags ( func ( diags tfdiags . Diagnostics ) tfdiags . Diagnostics {
return diags . Append ( & hcl . Diagnostic {
Severity : hcl . DiagError ,
Summary : "No value for required variable" ,
Detail : "The root input variable \"var.ephemeral\" is not set, and has no default value." ,
Subject : & hcl . Range {
2025-05-15 02:33:13 -04:00
Filename : "git::https://example.com/test.git//with-single-input/ephemeral/ephemeral.tfcomponent.hcl" ,
2024-10-08 10:46:31 -04:00
Start : hcl . Pos {
Line : 14 ,
Column : 1 ,
Byte : 175 ,
} ,
End : hcl . Pos {
Line : 14 ,
Column : 21 ,
Byte : 195 ,
} ,
} ,
} )
} ) ,
} ,
} ,
} ,
"ephemeral-default" : {
path : path . Join ( "with-single-input" , "ephemeral-default" ) ,
cycles : [ ] TestCycle {
{
planMode : plans . NormalMode ,
planInputs : map [ string ] cty . Value {
"input" : cty . StringVal ( "hello" ) ,
// deliberately omitting ephemeral
} ,
applyInputs : make ( map [ string ] cty . Value ) , // deliberately omitting ephemeral
wantAppliedChanges : [ ] stackstate . AppliedChange {
& stackstate . AppliedChangeComponentInstance {
ComponentAddr : mustAbsComponent ( "component.self" ) ,
ComponentInstanceAddr : mustAbsComponentInstance ( "component.self" ) ,
OutputValues : make ( map [ addrs . OutputValue ] cty . Value ) ,
InputVariables : map [ addrs . InputVariable ] cty . Value {
mustInputVariable ( "id" ) : cty . StringVal ( "2f9f3b84" ) ,
mustInputVariable ( "input" ) : cty . StringVal ( "hello" ) ,
} ,
} ,
& stackstate . AppliedChangeResourceInstanceObject {
ResourceInstanceObjectAddr : mustAbsResourceInstanceObject ( "component.self.testing_resource.data" ) ,
NewStateSrc : & states . ResourceInstanceObjectSrc {
AttrsJSON : mustMarshalJSONAttrs ( map [ string ] interface { } {
"id" : "2f9f3b84" ,
"value" : "hello" ,
} ) ,
Status : states . ObjectReady ,
Dependencies : make ( [ ] addrs . ConfigResource , 0 ) ,
} ,
ProviderConfigAddr : mustDefaultRootProvider ( "testing" ) ,
Schema : stacks_testing_provider . TestingResourceSchema ,
} ,
& stackstate . AppliedChangeInputVariable {
Addr : mustStackInputVariable ( "ephemeral" ) ,
Value : cty . NullVal ( cty . String ) , // ephemeral
} ,
& stackstate . AppliedChangeInputVariable {
Addr : mustStackInputVariable ( "input" ) ,
Value : cty . StringVal ( "hello" ) ,
} ,
} ,
} ,
} ,
} ,
2024-10-22 05:23:53 -04:00
"deferred-components" : {
path : path . Join ( "with-data-source" , "deferred-provider-for-each" ) ,
store : stacks_testing_provider . NewResourceStoreBuilder ( ) .
AddResource ( "data_known" , cty . ObjectVal ( map [ string ] cty . Value {
"id" : cty . StringVal ( "data_known" ) ,
"value" : cty . StringVal ( "known" ) ,
} ) ) .
Build ( ) ,
cycles : [ ] TestCycle {
{
planMode : plans . NormalMode ,
planInputs : map [ string ] cty . Value {
"providers" : cty . UnknownVal ( cty . Set ( cty . String ) ) ,
} ,
wantAppliedChanges : [ ] stackstate . AppliedChange {
& stackstate . AppliedChangeComponentInstance {
ComponentAddr : mustAbsComponent ( "component.const" ) ,
ComponentInstanceAddr : mustAbsComponentInstance ( "component.const" ) ,
Dependencies : collections . NewSet [ stackaddrs . AbsComponent ] ( ) ,
Dependents : collections . NewSet [ stackaddrs . AbsComponent ] ( ) ,
InputVariables : map [ addrs . InputVariable ] cty . Value {
mustInputVariable ( "id" ) : cty . StringVal ( "data_known" ) ,
mustInputVariable ( "resource" ) : cty . StringVal ( "resource_known" ) ,
} ,
OutputValues : make ( map [ addrs . OutputValue ] cty . Value ) ,
} ,
& stackstate . AppliedChangeResourceInstanceObject {
ResourceInstanceObjectAddr : mustAbsResourceInstanceObject ( "component.const.data.testing_data_source.data" ) ,
NewStateSrc : & states . ResourceInstanceObjectSrc {
AttrsJSON : mustMarshalJSONAttrs ( map [ string ] interface { } {
"id" : "data_known" ,
"value" : "known" ,
} ) ,
AttrSensitivePaths : make ( [ ] cty . Path , 0 ) ,
Status : states . ObjectReady ,
} ,
ProviderConfigAddr : mustDefaultRootProvider ( "testing" ) ,
Schema : stacks_testing_provider . TestingDataSourceSchema ,
} ,
& stackstate . AppliedChangeResourceInstanceObject {
ResourceInstanceObjectAddr : mustAbsResourceInstanceObject ( "component.const.testing_resource.data" ) ,
NewStateSrc : & states . ResourceInstanceObjectSrc {
AttrsJSON : mustMarshalJSONAttrs ( map [ string ] interface { } {
"id" : "resource_known" ,
"value" : "known" ,
} ) ,
Dependencies : [ ] addrs . ConfigResource {
mustAbsResourceInstance ( "data.testing_data_source.data" ) . ConfigResource ( ) ,
} ,
Status : states . ObjectReady ,
} ,
ProviderConfigAddr : mustDefaultRootProvider ( "testing" ) ,
Schema : stacks_testing_provider . TestingResourceSchema ,
} ,
& stackstate . AppliedChangeInputVariable {
Addr : mustStackInputVariable ( "providers" ) ,
Value : cty . UnknownVal ( cty . Set ( cty . String ) ) ,
} ,
} ,
} ,
{
planMode : plans . DestroyMode ,
planInputs : map [ string ] cty . Value {
"providers" : cty . UnknownVal ( cty . Set ( cty . String ) ) ,
} ,
wantAppliedChanges : [ ] stackstate . AppliedChange {
& stackstate . AppliedChangeComponentInstanceRemoved {
ComponentAddr : mustAbsComponent ( "component.const" ) ,
ComponentInstanceAddr : mustAbsComponentInstance ( "component.const" ) ,
} ,
& stackstate . AppliedChangeResourceInstanceObject {
ResourceInstanceObjectAddr : mustAbsResourceInstanceObject ( "component.const.data.testing_data_source.data" ) ,
ProviderConfigAddr : mustDefaultRootProvider ( "testing" ) ,
NewStateSrc : nil ,
} ,
& stackstate . AppliedChangeResourceInstanceObject {
ResourceInstanceObjectAddr : mustAbsResourceInstanceObject ( "component.const.testing_resource.data" ) ,
ProviderConfigAddr : mustDefaultRootProvider ( "testing" ) ,
NewStateSrc : nil ,
} ,
& stackstate . AppliedChangeInputVariable {
Addr : mustStackInputVariable ( "providers" ) ,
Value : cty . NilVal , // destroyed
} ,
} ,
} ,
} ,
} ,
"unknown-component-input" : {
path : path . Join ( "map-object-input" , "for-each-input" ) ,
cycles : [ ] TestCycle {
{
planMode : plans . NormalMode ,
planInputs : map [ string ] cty . Value {
"inputs" : cty . UnknownVal ( cty . Map ( cty . String ) ) ,
} ,
wantAppliedChanges : [ ] stackstate . AppliedChange {
& stackstate . AppliedChangeComponentInstance {
ComponentAddr : mustAbsComponent ( "component.main" ) ,
ComponentInstanceAddr : mustAbsComponentInstance ( "component.main" ) ,
Dependencies : collections . NewSet ( mustAbsComponent ( "component.self" ) ) ,
OutputValues : make ( map [ addrs . OutputValue ] cty . Value ) ,
2025-01-20 06:18:35 -05:00
InputVariables : map [ addrs . InputVariable ] cty . Value {
mustInputVariable ( "input" ) : cty . UnknownVal ( cty . Map ( cty . Object ( map [ string ] cty . Type {
"output" : cty . String ,
} ) ) ) ,
} ,
2024-10-22 05:23:53 -04:00
} ,
& stackstate . AppliedChangeInputVariable {
Addr : mustStackInputVariable ( "inputs" ) ,
Value : cty . UnknownVal ( cty . Map ( cty . String ) ) ,
} ,
} ,
} ,
{
planMode : plans . DestroyMode ,
planInputs : map [ string ] cty . Value {
"inputs" : cty . MapValEmpty ( cty . String ) ,
} ,
wantAppliedChanges : [ ] stackstate . AppliedChange {
& stackstate . AppliedChangeComponentInstanceRemoved {
ComponentAddr : mustAbsComponent ( "component.main" ) ,
ComponentInstanceAddr : mustAbsComponentInstance ( "component.main" ) ,
} ,
& stackstate . AppliedChangeInputVariable {
Addr : mustStackInputVariable ( "inputs" ) ,
Value : cty . NilVal , // destroyed
} ,
} ,
} ,
} ,
} ,
2025-04-02 02:39:10 -04:00
"unknown-component" : {
path : path . Join ( "with-single-input" , "removed-component-instance" ) ,
state : stackstate . NewStateBuilder ( ) .
AddComponentInstance ( stackstate . NewComponentInstanceBuilder ( mustAbsComponentInstance ( "component.self[\"main\"]" ) ) .
AddInputVariable ( "id" , cty . StringVal ( "main" ) ) .
AddInputVariable ( "input" , cty . StringVal ( "main" ) ) ) .
AddResourceInstance ( stackstate . NewResourceInstanceBuilder ( ) .
SetAddr ( mustAbsResourceInstanceObject ( "component.self[\"main\"].testing_resource.data" ) ) .
SetProviderAddr ( mustDefaultRootProvider ( "testing" ) ) .
SetResourceInstanceObjectSrc ( states . ResourceInstanceObjectSrc {
Status : states . ObjectReady ,
AttrsJSON : mustMarshalJSONAttrs ( map [ string ] any {
"id" : "main" ,
"value" : "main" ,
} ) ,
} ) ) .
Build ( ) ,
store : stacks_testing_provider . NewResourceStoreBuilder ( ) .
AddResource ( "main" , cty . ObjectVal ( map [ string ] cty . Value {
"id" : cty . StringVal ( "main" ) ,
"value" : cty . StringVal ( "main" ) ,
} ) ) .
Build ( ) ,
cycles : [ ] TestCycle {
{
planInputs : map [ string ] cty . Value {
"input" : cty . UnknownVal ( cty . Set ( cty . String ) ) ,
"removed" : cty . SetValEmpty ( cty . String ) ,
} ,
wantAppliedChanges : [ ] stackstate . AppliedChange {
& stackstate . AppliedChangeComponentInstance {
ComponentAddr : mustAbsComponent ( "component.self" ) ,
ComponentInstanceAddr : mustAbsComponentInstance ( "component.self[\"main\"]" ) ,
OutputValues : make ( map [ addrs . OutputValue ] cty . Value ) ,
InputVariables : map [ addrs . InputVariable ] cty . Value {
mustInputVariable ( "id" ) : cty . StringVal ( "main" ) ,
mustInputVariable ( "input" ) : cty . StringVal ( "main" ) ,
} ,
} ,
& stackstate . AppliedChangeInputVariable {
Addr : mustStackInputVariable ( "input" ) ,
Value : cty . UnknownVal ( cty . Set ( cty . String ) ) ,
} ,
& stackstate . AppliedChangeInputVariable {
Addr : mustStackInputVariable ( "removed" ) ,
Value : cty . SetValEmpty ( cty . String ) ,
} ,
} ,
} ,
} ,
} ,
2025-10-28 04:29:44 -04:00
"ephemeral-module-outputs" : {
path : "ephemeral-module-output" ,
2025-10-31 04:52:48 -04:00
skip : true , // TODO(issues/37822): Enable this.
2025-10-28 04:29:44 -04:00
cycles : [ ] TestCycle {
{
wantPlannedChanges : [ ] stackplan . PlannedChange {
& stackplan . PlannedChangeApplyable {
Applyable : true ,
} ,
& stackplan . PlannedChangeComponentInstance {
Addr : mustAbsComponentInstance ( "component.ephemeral_in" ) ,
PlanApplyable : false ,
PlanComplete : true ,
Action : plans . Create ,
RequiredComponents : collections . NewSet ( mustAbsComponent ( "component.ephemeral_out" ) ) ,
PlannedInputValues : make ( map [ string ] plans . DynamicValue ) ,
PlannedOutputValues : make ( map [ string ] cty . Value ) ,
PlannedCheckResults : new ( states . CheckResults ) ,
PlanTimestamp : fakePlanTimestamp ,
} ,
& stackplan . PlannedChangeComponentInstance {
Addr : mustAbsComponentInstance ( "component.ephemeral_out" ) ,
PlanApplyable : false ,
PlanComplete : true ,
Action : plans . Create ,
PlannedInputValues : make ( map [ string ] plans . DynamicValue ) ,
PlannedOutputValues : map [ string ] cty . Value {
"value" : cty . DynamicVal , // ephemeral
} ,
PlannedCheckResults : new ( states . CheckResults ) ,
PlanTimestamp : fakePlanTimestamp ,
} ,
& stackplan . PlannedChangeHeader {
TerraformVersion : version . SemVer ,
} ,
& stackplan . PlannedChangePlannedTimestamp {
PlannedTimestamp : fakePlanTimestamp ,
} ,
} ,
wantAppliedChanges : [ ] stackstate . AppliedChange {
& stackstate . AppliedChangeComponentInstance {
ComponentAddr : mustAbsComponent ( "component.ephemeral_in" ) ,
ComponentInstanceAddr : mustAbsComponentInstance ( "component.ephemeral_in" ) ,
Dependencies : collections . NewSet [ stackaddrs . AbsComponent ] (
mustAbsComponent ( "component.ephemeral_out" ) ,
) ,
OutputValues : make ( map [ addrs . OutputValue ] cty . Value ) ,
InputVariables : map [ addrs . InputVariable ] cty . Value {
mustInputVariable ( "input" ) : cty . UnknownVal ( cty . String ) , // ephemeral
} ,
} ,
& stackstate . AppliedChangeComponentInstance {
ComponentAddr : mustAbsComponent ( "component.ephemeral_out" ) ,
ComponentInstanceAddr : mustAbsComponentInstance ( "component.ephemeral_out" ) ,
Dependents : collections . NewSet [ stackaddrs . AbsComponent ] (
mustAbsComponent ( "component.ephemeral_in" ) ,
) ,
OutputValues : make ( map [ addrs . OutputValue ] cty . Value ) ,
InputVariables : make ( map [ addrs . InputVariable ] cty . Value ) ,
} ,
} ,
} ,
} ,
} ,
2024-06-27 12:01:20 -04:00
}
2024-09-18 04:41:36 -04:00
for name , tc := range tcs {
t . Run ( name , func ( t * testing . T ) {
2025-10-31 04:52:48 -04:00
if tc . skip {
t . Skip ( )
}
2024-09-18 04:41:36 -04:00
ctx := context . Background ( )
2024-06-27 12:01:20 -04:00
2024-09-18 04:41:36 -04:00
lock := depsfile . NewLocks ( )
lock . SetProvider (
addrs . NewDefaultProvider ( "testing" ) ,
providerreqs . MustParseVersion ( "0.0.0" ) ,
providerreqs . MustParseVersionConstraints ( "=0.0.0" ) ,
providerreqs . PreferredHashes ( [ ] providerreqs . Hash { } ) ,
)
store := tc . store
if store == nil {
store = stacks_testing_provider . NewResourceStore ( )
}
testContext := TestContext {
timestamp : & fakePlanTimestamp ,
config : loadMainBundleConfigForTest ( t , tc . path ) ,
providers : map [ addrs . Provider ] providers . Factory {
addrs . NewDefaultProvider ( "testing" ) : func ( ) ( providers . Interface , error ) {
2024-09-20 02:20:32 -04:00
provider := stacks_testing_provider . NewProviderWithData ( t , store )
provider . Authentication = "authn"
return provider , nil
2024-09-18 04:41:36 -04:00
} ,
} ,
dependencyLocks : * lock ,
}
state := tc . state
for ix , cycle := range tc . cycles {
t . Run ( strconv . FormatInt ( int64 ( ix ) , 10 ) , func ( t * testing . T ) {
var plan * stackplan . Plan
t . Run ( "plan" , func ( t * testing . T ) {
plan = testContext . Plan ( t , ctx , state , cycle )
} )
t . Run ( "apply" , func ( t * testing . T ) {
state = testContext . Apply ( t , ctx , plan , cycle )
} )
} )
}
} )
2024-06-27 12:01:20 -04:00
}
}
2024-09-18 04:41:36 -04:00
func TestApplyWithRemovedResource ( t * testing . T ) {
fakePlanTimestamp , err := time . Parse ( time . RFC3339 , "1994-09-05T08:50:00Z" )
2024-02-16 04:46:50 -05:00
if err != nil {
t . Fatal ( err )
}
2024-09-18 04:41:36 -04:00
ctx := context . Background ( )
cfg := loadMainBundleConfigForTest ( t , path . Join ( "empty-component" , "valid-providers" ) )
2024-06-27 10:08:08 -04:00
lock := depsfile . NewLocks ( )
2024-09-18 04:41:36 -04:00
planReq := PlanRequest {
2024-02-16 04:46:50 -05:00
Config : cfg ,
ProviderFactories : map [ addrs . Provider ] providers . Factory {
2024-09-18 04:41:36 -04:00
addrs . NewBuiltInProvider ( "terraform" ) : func ( ) ( providers . Interface , error ) {
return terraformProvider . NewProvider ( ) , nil
2024-02-16 04:46:50 -05:00
} ,
} ,
2024-06-27 10:08:08 -04:00
DependencyLocks : * lock ,
2024-02-16 04:46:50 -05:00
ForcePlanTimestamp : & fakePlanTimestamp ,
2024-09-18 04:41:36 -04:00
// PrevState specifies a state with a resource that is not present in
// the current configuration. This is a common situation when a resource
// is removed from the configuration but still exists in the state.
PrevState : stackstate . NewStateBuilder ( ) .
AddResourceInstance ( stackstate . NewResourceInstanceBuilder ( ) .
SetAddr ( stackaddrs . AbsResourceInstanceObject {
Component : stackaddrs . AbsComponentInstance {
Stack : stackaddrs . RootStackInstance ,
Item : stackaddrs . ComponentInstance {
Component : stackaddrs . Component {
Name : "self" ,
} ,
Key : addrs . NoKey ,
} ,
} ,
Item : addrs . AbsResourceInstanceObject {
ResourceInstance : addrs . AbsResourceInstance {
Module : addrs . RootModuleInstance ,
Resource : addrs . ResourceInstance {
Resource : addrs . Resource {
Mode : addrs . ManagedResourceMode ,
Type : "terraform_data" ,
Name : "main" ,
} ,
Key : addrs . NoKey ,
} ,
} ,
DeposedKey : addrs . NotDeposed ,
} ,
} ) .
SetResourceInstanceObjectSrc ( states . ResourceInstanceObjectSrc {
SchemaVersion : 0 ,
AttrsJSON : mustMarshalJSONAttrs ( map [ string ] interface { } {
"id" : "FE1D5830765C" ,
"input" : map [ string ] interface { } {
"value" : "hello" ,
"type" : "string" ,
} ,
"output" : map [ string ] interface { } {
"value" : nil ,
"type" : "string" ,
} ,
"triggers_replace" : nil ,
} ) ,
Status : states . ObjectReady ,
} ) .
SetProviderAddr ( addrs . AbsProviderConfig {
Module : addrs . RootModule ,
Provider : addrs . MustParseProviderSourceString ( "terraform.io/builtin/terraform" ) ,
} ) ) .
Build ( ) ,
2024-02-16 04:46:50 -05:00
}
2024-09-18 04:41:36 -04:00
planChangesCh := make ( chan stackplan . PlannedChange )
diagsCh := make ( chan tfdiags . Diagnostic )
planResp := PlanResponse {
PlannedChanges : planChangesCh ,
2024-02-16 04:46:50 -05:00
Diagnostics : diagsCh ,
}
2024-09-18 04:41:36 -04:00
go Plan ( ctx , & planReq , & planResp )
planChanges , diags := collectPlanOutput ( planChangesCh , diagsCh )
2024-02-16 04:46:50 -05:00
if len ( diags ) > 0 {
2024-09-18 04:41:36 -04:00
t . Fatalf ( "expected no diagnostics, go %s" , diags . ErrWithWarnings ( ) )
2024-02-16 04:46:50 -05:00
}
2024-07-12 22:20:34 -04:00
planLoader := stackplan . NewLoader ( )
2024-02-16 04:46:50 -05:00
for _ , change := range planChanges {
proto , err := change . PlannedChangeProto ( )
if err != nil {
t . Fatal ( err )
}
2024-07-12 22:20:34 -04:00
for _ , rawMsg := range proto . Raw {
err = planLoader . AddRaw ( rawMsg )
if err != nil {
t . Fatal ( err )
}
}
}
plan , err := planLoader . Plan ( )
if err != nil {
t . Fatal ( err )
2024-02-16 04:46:50 -05:00
}
applyReq := ApplyRequest {
2024-07-12 22:20:34 -04:00
Config : cfg ,
Plan : plan ,
2024-02-16 04:46:50 -05:00
ProviderFactories : map [ addrs . Provider ] providers . Factory {
2024-09-18 04:41:36 -04:00
addrs . NewBuiltInProvider ( "terraform" ) : func ( ) ( providers . Interface , error ) {
return terraformProvider . NewProvider ( ) , nil
2024-02-16 04:46:50 -05:00
} ,
} ,
}
applyChangesCh := make ( chan stackstate . AppliedChange )
diagsCh = make ( chan tfdiags . Diagnostic )
applyResp := ApplyResponse {
AppliedChanges : applyChangesCh ,
Diagnostics : diagsCh ,
}
go Apply ( ctx , & applyReq , & applyResp )
applyChanges , applyDiags := collectApplyOutput ( applyChangesCh , diagsCh )
if len ( applyDiags ) > 0 {
t . Fatalf ( "expected no diagnostics, got %s" , applyDiags . ErrWithWarnings ( ) )
}
wantChanges := [ ] stackstate . AppliedChange {
& stackstate . AppliedChangeComponentInstance {
2024-09-07 08:36:16 -04:00
ComponentAddr : mustAbsComponent ( "component.self" ) ,
ComponentInstanceAddr : mustAbsComponentInstance ( "component.self" ) ,
OutputValues : make ( map [ addrs . OutputValue ] cty . Value ) ,
2024-09-18 04:41:36 -04:00
InputVariables : make ( map [ addrs . InputVariable ] cty . Value ) ,
2024-02-16 04:46:50 -05:00
} ,
& stackstate . AppliedChangeResourceInstanceObject {
2024-09-18 04:41:36 -04:00
ResourceInstanceObjectAddr : mustAbsResourceInstanceObject ( "component.self.terraform_data.main" ) ,
NewStateSrc : nil , // Deleted, so is nil.
2024-02-16 04:46:50 -05:00
ProviderConfigAddr : addrs . AbsProviderConfig {
2024-09-18 04:41:36 -04:00
Provider : addrs . Provider {
Type : "terraform" ,
Namespace : "builtin" ,
Hostname : "terraform.io" ,
} ,
2024-02-16 04:46:50 -05:00
} ,
2024-09-16 05:45:19 -04:00
} ,
2024-03-01 20:16:14 -05:00
}
sort . SliceStable ( applyChanges , func ( i , j int ) bool {
return appliedChangeSortKey ( applyChanges [ i ] ) < appliedChangeSortKey ( applyChanges [ j ] )
} )
2024-08-21 15:30:01 -04:00
if diff := cmp . Diff ( wantChanges , applyChanges , changesCmpOpts ) ; diff != "" {
2024-03-01 20:16:14 -05:00
t . Errorf ( "wrong changes\n%s" , diff )
}
}
2024-09-18 04:41:36 -04:00
func TestApplyWithMovedResource ( t * testing . T ) {
fakePlanTimestamp , err := time . Parse ( time . RFC3339 , "1994-09-05T08:50:00Z" )
2024-03-01 20:16:14 -05:00
if err != nil {
t . Fatal ( err )
}
2024-09-18 04:41:36 -04:00
ctx := context . Background ( )
cfg := loadMainBundleConfigForTest ( t , path . Join ( "state-manipulation" , "moved" ) )
2024-07-23 11:45:07 -04:00
2024-06-27 10:08:08 -04:00
lock := depsfile . NewLocks ( )
lock . SetProvider (
addrs . NewDefaultProvider ( "testing" ) ,
providerreqs . MustParseVersion ( "0.0.0" ) ,
providerreqs . MustParseVersionConstraints ( "=0.0.0" ) ,
providerreqs . PreferredHashes ( [ ] providerreqs . Hash { } ) ,
)
2024-09-18 04:41:36 -04:00
planReq := PlanRequest {
2024-03-01 20:16:14 -05:00
Config : cfg ,
ProviderFactories : map [ addrs . Provider ] providers . Factory {
addrs . NewDefaultProvider ( "testing" ) : func ( ) ( providers . Interface , error ) {
2024-09-18 04:41:36 -04:00
return stacks_testing_provider . NewProviderWithData ( t , stacks_testing_provider . NewResourceStoreBuilder ( ) .
AddResource ( "moved" , cty . ObjectVal ( map [ string ] cty . Value {
"id" : cty . StringVal ( "moved" ) ,
"value" : cty . StringVal ( "moved" ) ,
} ) ) .
Build ( ) ) , nil
2024-03-01 20:16:14 -05:00
} ,
} ,
2024-06-27 10:08:08 -04:00
DependencyLocks : * lock ,
2024-03-01 20:16:14 -05:00
ForcePlanTimestamp : & fakePlanTimestamp ,
2024-09-18 04:41:36 -04:00
// PrevState specifies a state with a resource that is not present in
// the current configuration. This is a common situation when a resource
// is removed from the configuration but still exists in the state.
PrevState : stackstate . NewStateBuilder ( ) .
AddResourceInstance ( stackstate . NewResourceInstanceBuilder ( ) .
SetAddr ( stackaddrs . AbsResourceInstanceObject {
Component : stackaddrs . AbsComponentInstance {
Stack : stackaddrs . RootStackInstance ,
Item : stackaddrs . ComponentInstance {
Component : stackaddrs . Component {
Name : "self" ,
} ,
Key : addrs . NoKey ,
} ,
} ,
Item : addrs . AbsResourceInstanceObject {
ResourceInstance : addrs . AbsResourceInstance {
Module : addrs . RootModuleInstance ,
Resource : addrs . ResourceInstance {
Resource : addrs . Resource {
Mode : addrs . ManagedResourceMode ,
Type : "testing_resource" ,
Name : "before" ,
} ,
Key : addrs . NoKey ,
} ,
} ,
DeposedKey : addrs . NotDeposed ,
} ,
} ) .
SetResourceInstanceObjectSrc ( states . ResourceInstanceObjectSrc {
SchemaVersion : 0 ,
AttrsJSON : mustMarshalJSONAttrs ( map [ string ] interface { } {
"id" : "moved" ,
"value" : "moved" ,
} ) ,
Status : states . ObjectReady ,
} ) .
SetProviderAddr ( addrs . AbsProviderConfig {
Module : addrs . RootModule ,
Provider : addrs . MustParseProviderSourceString ( "hashicorp/testing" ) ,
} ) ) .
Build ( ) ,
2024-03-01 20:16:14 -05:00
}
2024-09-18 04:41:36 -04:00
planChangesCh := make ( chan stackplan . PlannedChange )
diagsCh := make ( chan tfdiags . Diagnostic )
planResp := PlanResponse {
PlannedChanges : planChangesCh ,
2024-03-01 20:16:14 -05:00
Diagnostics : diagsCh ,
}
2024-09-18 04:41:36 -04:00
go Plan ( ctx , & planReq , & planResp )
planChanges , diags := collectPlanOutput ( planChangesCh , diagsCh )
if len ( diags ) > 0 {
t . Fatalf ( "expected no diagnostics, go %s" , diags . ErrWithWarnings ( ) )
2024-03-01 20:16:14 -05:00
}
2024-07-12 22:20:34 -04:00
planLoader := stackplan . NewLoader ( )
2024-03-01 20:16:14 -05:00
for _ , change := range planChanges {
proto , err := change . PlannedChangeProto ( )
if err != nil {
t . Fatal ( err )
}
2024-07-12 22:20:34 -04:00
for _ , rawMsg := range proto . Raw {
err = planLoader . AddRaw ( rawMsg )
if err != nil {
t . Fatal ( err )
}
}
}
plan , err := planLoader . Plan ( )
if err != nil {
t . Fatal ( err )
2024-03-01 20:16:14 -05:00
}
applyReq := ApplyRequest {
2024-07-12 22:20:34 -04:00
Config : cfg ,
Plan : plan ,
2024-03-01 20:16:14 -05:00
ProviderFactories : map [ addrs . Provider ] providers . Factory {
addrs . NewDefaultProvider ( "testing" ) : func ( ) ( providers . Interface , error ) {
2024-09-18 04:41:36 -04:00
return stacks_testing_provider . NewProviderWithData ( t , stacks_testing_provider . NewResourceStoreBuilder ( ) .
AddResource ( "moved" , cty . ObjectVal ( map [ string ] cty . Value {
"id" : cty . StringVal ( "moved" ) ,
"value" : cty . StringVal ( "moved" ) ,
} ) ) .
Build ( ) ) , nil
2024-03-01 20:16:14 -05:00
} ,
} ,
2024-06-27 10:08:08 -04:00
DependencyLocks : * lock ,
2024-03-01 20:16:14 -05:00
}
applyChangesCh := make ( chan stackstate . AppliedChange )
diagsCh = make ( chan tfdiags . Diagnostic )
applyResp := ApplyResponse {
AppliedChanges : applyChangesCh ,
Diagnostics : diagsCh ,
}
go Apply ( ctx , & applyReq , & applyResp )
applyChanges , applyDiags := collectApplyOutput ( applyChangesCh , diagsCh )
2024-09-18 04:41:36 -04:00
if len ( applyDiags ) > 0 {
t . Fatalf ( "expected no diagnostics, got %s" , applyDiags . ErrWithWarnings ( ) )
2024-03-01 20:16:14 -05:00
}
2024-09-18 04:41:36 -04:00
expectedPreviousAddr := mustAbsResourceInstanceObject ( "component.self.testing_resource.before" )
2024-03-01 20:16:14 -05:00
wantChanges := [ ] stackstate . AppliedChange {
& stackstate . AppliedChangeComponentInstance {
2024-09-18 04:41:36 -04:00
ComponentAddr : mustAbsComponent ( "component.self" ) ,
ComponentInstanceAddr : mustAbsComponentInstance ( "component.self" ) ,
OutputValues : make ( map [ addrs . OutputValue ] cty . Value ) ,
InputVariables : make ( map [ addrs . InputVariable ] cty . Value ) ,
2024-03-01 20:16:14 -05:00
} ,
& stackstate . AppliedChangeResourceInstanceObject {
2024-09-18 04:41:36 -04:00
ResourceInstanceObjectAddr : mustAbsResourceInstanceObject ( "component.self.testing_resource.after" ) ,
PreviousResourceInstanceObjectAddr : & expectedPreviousAddr ,
2024-03-01 20:16:14 -05:00
NewStateSrc : & states . ResourceInstanceObjectSrc {
AttrsJSON : mustMarshalJSONAttrs ( map [ string ] interface { } {
2024-09-18 04:41:36 -04:00
"id" : "moved" ,
"value" : "moved" ,
2024-03-01 20:16:14 -05:00
} ) ,
2024-09-18 04:41:36 -04:00
Status : states . ObjectReady ,
AttrSensitivePaths : make ( [ ] cty . Path , 0 ) ,
2024-03-01 20:16:14 -05:00
} ,
ProviderConfigAddr : addrs . AbsProviderConfig {
2024-09-18 04:41:36 -04:00
Provider : addrs . MustParseProviderSourceString ( "hashicorp/testing" ) ,
2024-03-01 20:16:14 -05:00
} ,
Schema : stacks_testing_provider . TestingResourceSchema ,
2024-02-16 04:46:50 -05:00
} ,
}
sort . SliceStable ( applyChanges , func ( i , j int ) bool {
return appliedChangeSortKey ( applyChanges [ i ] ) < appliedChangeSortKey ( applyChanges [ j ] )
} )
2024-08-21 15:30:01 -04:00
if diff := cmp . Diff ( wantChanges , applyChanges , changesCmpOpts ) ; diff != "" {
2024-07-23 11:45:07 -04:00
t . Errorf ( "wrong changes\n%s" , diff )
}
2024-09-18 04:41:36 -04:00
}
2024-07-23 11:45:07 -04:00
2024-09-18 04:41:36 -04:00
func TestApplyWithSensitivePropagation ( t * testing . T ) {
ctx := context . Background ( )
cfg := loadMainBundleConfigForTest ( t , path . Join ( "with-single-input" , "sensitive-input" ) )
fakePlanTimestamp , err := time . Parse ( time . RFC3339 , "1991-08-25T20:57:08Z" )
2024-07-23 11:45:07 -04:00
if err != nil {
2024-09-18 04:41:36 -04:00
t . Fatal ( err )
2024-07-23 11:45:07 -04:00
}
2024-09-18 04:41:36 -04:00
changesCh := make ( chan stackplan . PlannedChange )
diagsCh := make ( chan tfdiags . Diagnostic )
lock := depsfile . NewLocks ( )
lock . SetProvider (
addrs . NewDefaultProvider ( "testing" ) ,
providerreqs . MustParseVersion ( "0.0.0" ) ,
providerreqs . MustParseVersionConstraints ( "=0.0.0" ) ,
providerreqs . PreferredHashes ( [ ] providerreqs . Hash { } ) ,
)
req := PlanRequest {
2024-07-23 11:45:07 -04:00
Config : cfg ,
ProviderFactories : map [ addrs . Provider ] providers . Factory {
addrs . NewDefaultProvider ( "testing" ) : func ( ) ( providers . Interface , error ) {
2024-09-18 04:41:36 -04:00
return stacks_testing_provider . NewProvider ( t ) , nil
2024-07-23 11:45:07 -04:00
} ,
} ,
DependencyLocks : * lock ,
ForcePlanTimestamp : & fakePlanTimestamp ,
InputValues : map [ stackaddrs . InputVariable ] ExternalInputValue {
2024-09-18 04:41:36 -04:00
stackaddrs . InputVariable { Name : "id" } : {
Value : cty . StringVal ( "bb5cf32312ec" ) ,
2024-07-23 11:45:07 -04:00
} ,
} ,
}
2024-09-18 04:41:36 -04:00
resp := PlanResponse {
2024-07-23 11:45:07 -04:00
PlannedChanges : changesCh ,
Diagnostics : diagsCh ,
}
go Plan ( ctx , & req , & resp )
2024-09-18 04:41:36 -04:00
planChanges , diags := collectPlanOutput ( changesCh , diagsCh )
if len ( diags ) > 0 {
t . Fatalf ( "expected no diagnostics, got %s" , diags . ErrWithWarnings ( ) )
2024-07-23 11:45:07 -04:00
}
2024-09-18 04:41:36 -04:00
planLoader := stackplan . NewLoader ( )
2024-07-23 11:45:07 -04:00
for _ , change := range planChanges {
proto , err := change . PlannedChangeProto ( )
if err != nil {
t . Fatal ( err )
}
for _ , rawMsg := range proto . Raw {
err = planLoader . AddRaw ( rawMsg )
if err != nil {
t . Fatal ( err )
}
}
}
2024-09-18 04:41:36 -04:00
plan , err := planLoader . Plan ( )
2024-07-23 11:45:07 -04:00
if err != nil {
t . Fatal ( err )
}
2024-09-18 04:41:36 -04:00
applyReq := ApplyRequest {
2024-07-23 11:45:07 -04:00
Config : cfg ,
Plan : plan ,
ProviderFactories : map [ addrs . Provider ] providers . Factory {
addrs . NewDefaultProvider ( "testing" ) : func ( ) ( providers . Interface , error ) {
2024-09-18 04:41:36 -04:00
return stacks_testing_provider . NewProvider ( t ) , nil
2024-07-23 11:45:07 -04:00
} ,
} ,
DependencyLocks : * lock ,
}
2024-09-18 04:41:36 -04:00
applyChangesCh := make ( chan stackstate . AppliedChange )
2024-07-23 11:45:07 -04:00
diagsCh = make ( chan tfdiags . Diagnostic )
2024-09-18 04:41:36 -04:00
applyResp := ApplyResponse {
2024-07-23 11:45:07 -04:00
AppliedChanges : applyChangesCh ,
Diagnostics : diagsCh ,
}
go Apply ( ctx , & applyReq , & applyResp )
2024-09-18 04:41:36 -04:00
applyChanges , applyDiags := collectApplyOutput ( applyChangesCh , diagsCh )
2024-07-23 11:45:07 -04:00
if len ( applyDiags ) > 0 {
t . Fatalf ( "expected no diagnostics, got %s" , applyDiags . ErrWithWarnings ( ) )
}
2024-09-18 04:41:36 -04:00
wantChanges := [ ] stackstate . AppliedChange {
2024-07-23 11:45:07 -04:00
& stackstate . AppliedChangeComponentInstance {
2024-09-18 04:41:36 -04:00
ComponentAddr : mustAbsComponent ( "component.self" ) ,
ComponentInstanceAddr : mustAbsComponentInstance ( "component.self" ) ,
Dependencies : collections . NewSet ( mustAbsComponent ( "component.sensitive" ) ) ,
2024-07-23 11:45:07 -04:00
OutputValues : make ( map [ addrs . OutputValue ] cty . Value ) ,
2024-09-07 08:36:16 -04:00
InputVariables : map [ addrs . InputVariable ] cty . Value {
2024-09-18 04:41:36 -04:00
mustInputVariable ( "id" ) : cty . StringVal ( "bb5cf32312ec" ) ,
mustInputVariable ( "input" ) : cty . StringVal ( "secret" ) . Mark ( marks . Sensitive ) ,
2024-09-07 08:36:16 -04:00
} ,
2024-07-23 11:45:07 -04:00
} ,
& stackstate . AppliedChangeResourceInstanceObject {
2024-09-18 04:41:36 -04:00
ResourceInstanceObjectAddr : mustAbsResourceInstanceObject ( "component.self.testing_resource.data" ) ,
NewStateSrc : & states . ResourceInstanceObjectSrc {
AttrsJSON : mustMarshalJSONAttrs ( map [ string ] interface { } {
"id" : "bb5cf32312ec" ,
"value" : "secret" ,
} ) ,
AttrSensitivePaths : [ ] cty . Path {
cty . GetAttrPath ( "value" ) ,
} ,
Status : states . ObjectReady ,
Dependencies : make ( [ ] addrs . ConfigResource , 0 ) ,
} ,
ProviderConfigAddr : addrs . AbsProviderConfig {
Provider : addrs . NewDefaultProvider ( "testing" ) ,
} ,
Schema : stacks_testing_provider . TestingResourceSchema ,
} ,
& stackstate . AppliedChangeComponentInstance {
ComponentAddr : mustAbsComponent ( "component.sensitive" ) ,
ComponentInstanceAddr : mustAbsComponentInstance ( "component.sensitive" ) ,
Dependents : collections . NewSet ( mustAbsComponent ( "component.self" ) ) ,
OutputValues : map [ addrs . OutputValue ] cty . Value {
addrs . OutputValue { Name : "out" } : cty . StringVal ( "secret" ) . Mark ( marks . Sensitive ) ,
} ,
InputVariables : make ( map [ addrs . InputVariable ] cty . Value ) ,
} ,
& stackstate . AppliedChangeInputVariable {
Addr : mustStackInputVariable ( "id" ) ,
Value : cty . StringVal ( "bb5cf32312ec" ) ,
2024-07-23 11:45:07 -04:00
} ,
}
sort . SliceStable ( applyChanges , func ( i , j int ) bool {
return appliedChangeSortKey ( applyChanges [ i ] ) < appliedChangeSortKey ( applyChanges [ j ] )
} )
2024-08-21 15:30:01 -04:00
if diff := cmp . Diff ( wantChanges , applyChanges , changesCmpOpts ) ; diff != "" {
2024-02-16 04:46:50 -05:00
t . Errorf ( "wrong changes\n%s" , diff )
}
}
2024-06-21 08:05:26 -04:00
func TestApplyWithForcePlanTimestamp ( t * testing . T ) {
ctx := context . Background ( )
cfg := loadMainBundleConfigForTest ( t , "with-plantimestamp" )
forcedPlanTimestamp := "1991-08-25T20:57:08Z"
fakePlanTimestamp , err := time . Parse ( time . RFC3339 , forcedPlanTimestamp )
if err != nil {
t . Fatal ( err )
}
changesCh := make ( chan stackplan . PlannedChange )
diagsCh := make ( chan tfdiags . Diagnostic )
req := PlanRequest {
Config : cfg ,
ProviderFactories : map [ addrs . Provider ] providers . Factory {
addrs . NewDefaultProvider ( "testing" ) : func ( ) ( providers . Interface , error ) {
2024-09-07 08:22:16 -04:00
return stacks_testing_provider . NewProvider ( t ) , nil
2024-06-21 08:05:26 -04:00
} ,
} ,
ForcePlanTimestamp : & fakePlanTimestamp ,
}
resp := PlanResponse {
PlannedChanges : changesCh ,
Diagnostics : diagsCh ,
}
go Plan ( ctx , & req , & resp )
planChanges , diags := collectPlanOutput ( changesCh , diagsCh )
if len ( diags ) > 0 {
t . Fatalf ( "expected no diagnostics, got %s" , diags . ErrWithWarnings ( ) )
}
// Sanity check that the plan timestamp was set correctly
output := expectOutput ( t , "plantimestamp" , planChanges )
2024-09-16 05:36:36 -04:00
plantimestampValue := output . After
2024-06-21 08:05:26 -04:00
if plantimestampValue . AsString ( ) != forcedPlanTimestamp {
t . Errorf ( "expected plantimestamp to be %q, got %q" , forcedPlanTimestamp , plantimestampValue . AsString ( ) )
}
2024-07-12 22:20:34 -04:00
planLoader := stackplan . NewLoader ( )
2024-06-21 08:05:26 -04:00
for _ , change := range planChanges {
proto , err := change . PlannedChangeProto ( )
if err != nil {
t . Fatal ( err )
}
2024-07-12 22:20:34 -04:00
for _ , rawMsg := range proto . Raw {
err = planLoader . AddRaw ( rawMsg )
if err != nil {
t . Fatal ( err )
}
}
}
plan , err := planLoader . Plan ( )
if err != nil {
t . Fatal ( err )
2024-06-21 08:05:26 -04:00
}
applyReq := ApplyRequest {
2024-07-12 22:20:34 -04:00
Config : cfg ,
Plan : plan ,
2024-06-21 08:05:26 -04:00
ProviderFactories : map [ addrs . Provider ] providers . Factory {
addrs . NewDefaultProvider ( "testing" ) : func ( ) ( providers . Interface , error ) {
2024-09-07 08:22:16 -04:00
return stacks_testing_provider . NewProvider ( t ) , nil
2024-06-21 08:05:26 -04:00
} ,
} ,
}
applyChangesCh := make ( chan stackstate . AppliedChange )
diagsCh = make ( chan tfdiags . Diagnostic )
applyResp := ApplyResponse {
AppliedChanges : applyChangesCh ,
Diagnostics : diagsCh ,
}
go Apply ( ctx , & applyReq , & applyResp )
applyChanges , applyDiags := collectApplyOutput ( applyChangesCh , diagsCh )
if len ( applyDiags ) > 0 {
t . Fatalf ( "expected no diagnostics, got %s" , applyDiags . ErrWithWarnings ( ) )
}
wantChanges := [ ] stackstate . AppliedChange {
& stackstate . AppliedChangeComponentInstance {
2024-09-07 08:36:16 -04:00
ComponentAddr : mustAbsComponent ( "component.second-self" ) ,
ComponentInstanceAddr : mustAbsComponentInstance ( "component.second-self" ) ,
2024-06-21 08:05:26 -04:00
OutputValues : map [ addrs . OutputValue ] cty . Value {
// We want to make sure the plantimestamp is set correctly
{ Name : "input" } : cty . StringVal ( forcedPlanTimestamp ) ,
// plantimestamp should also be set for the module runtime used in the components
{ Name : "out" } : cty . StringVal ( fmt . Sprintf ( "module-output-%s" , forcedPlanTimestamp ) ) ,
} ,
2024-09-07 08:36:16 -04:00
InputVariables : map [ addrs . InputVariable ] cty . Value {
mustInputVariable ( "value" ) : cty . StringVal ( forcedPlanTimestamp ) ,
} ,
2024-06-21 08:05:26 -04:00
} ,
& stackstate . AppliedChangeComponentInstance {
2024-09-07 08:36:16 -04:00
ComponentAddr : mustAbsComponent ( "component.self" ) ,
ComponentInstanceAddr : mustAbsComponentInstance ( "component.self" ) ,
2024-06-21 08:05:26 -04:00
OutputValues : map [ addrs . OutputValue ] cty . Value {
// We want to make sure the plantimestamp is set correctly
{ Name : "input" } : cty . StringVal ( forcedPlanTimestamp ) ,
// plantimestamp should also be set for the module runtime used in the components
{ Name : "out" } : cty . StringVal ( fmt . Sprintf ( "module-output-%s" , forcedPlanTimestamp ) ) ,
} ,
2024-09-07 08:36:16 -04:00
InputVariables : map [ addrs . InputVariable ] cty . Value {
mustInputVariable ( "value" ) : cty . StringVal ( forcedPlanTimestamp ) ,
} ,
2024-06-21 08:05:26 -04:00
} ,
2024-09-16 05:36:36 -04:00
& stackstate . AppliedChangeOutputValue {
Addr : stackaddrs . OutputValue { Name : "plantimestamp" } ,
Value : cty . StringVal ( forcedPlanTimestamp ) ,
} ,
2024-06-21 08:05:26 -04:00
}
sort . SliceStable ( applyChanges , func ( i , j int ) bool {
return appliedChangeSortKey ( applyChanges [ i ] ) < appliedChangeSortKey ( applyChanges [ j ] )
} )
2024-08-21 15:30:01 -04:00
if diff := cmp . Diff ( wantChanges , applyChanges , changesCmpOpts ) ; diff != "" {
2024-06-21 08:05:26 -04:00
t . Errorf ( "wrong changes\n%s" , diff )
}
}
func TestApplyWithDefaultPlanTimestamp ( t * testing . T ) {
ctx := context . Background ( )
cfg := loadMainBundleConfigForTest ( t , "with-plantimestamp" )
dayOfWritingThisTest := "2024-06-21T06:37:08Z"
dayOfWritingThisTestTime , err := time . Parse ( time . RFC3339 , dayOfWritingThisTest )
if err != nil {
t . Fatal ( err )
}
changesCh := make ( chan stackplan . PlannedChange )
diagsCh := make ( chan tfdiags . Diagnostic )
req := PlanRequest {
Config : cfg ,
ProviderFactories : map [ addrs . Provider ] providers . Factory {
addrs . NewDefaultProvider ( "testing" ) : func ( ) ( providers . Interface , error ) {
2024-09-07 08:22:16 -04:00
return stacks_testing_provider . NewProvider ( t ) , nil
2024-06-21 08:05:26 -04:00
} ,
} ,
}
resp := PlanResponse {
PlannedChanges : changesCh ,
Diagnostics : diagsCh ,
}
go Plan ( ctx , & req , & resp )
planChanges , diags := collectPlanOutput ( changesCh , diagsCh )
if len ( diags ) > 0 {
t . Fatalf ( "expected no diagnostics, got %s" , diags . ErrWithWarnings ( ) )
}
// Sanity check that the plan timestamp was set correctly
output := expectOutput ( t , "plantimestamp" , planChanges )
2024-09-16 05:36:36 -04:00
plantimestampValue := output . After
2024-06-21 08:05:26 -04:00
plantimestamp , err := time . Parse ( time . RFC3339 , plantimestampValue . AsString ( ) )
if err != nil {
t . Fatal ( err )
}
if plantimestamp . Before ( dayOfWritingThisTestTime ) {
t . Errorf ( "expected plantimestamp to be later than %q, got %q" , dayOfWritingThisTest , plantimestampValue . AsString ( ) )
}
2024-07-12 22:20:34 -04:00
planLoader := stackplan . NewLoader ( )
2024-06-21 08:05:26 -04:00
for _ , change := range planChanges {
proto , err := change . PlannedChangeProto ( )
if err != nil {
t . Fatal ( err )
}
2024-07-12 22:20:34 -04:00
for _ , rawMsg := range proto . Raw {
err = planLoader . AddRaw ( rawMsg )
if err != nil {
t . Fatal ( err )
}
}
}
plan , err := planLoader . Plan ( )
if err != nil {
t . Fatal ( err )
2024-06-21 08:05:26 -04:00
}
applyReq := ApplyRequest {
2024-07-12 22:20:34 -04:00
Config : cfg ,
Plan : plan ,
2024-06-21 08:05:26 -04:00
ProviderFactories : map [ addrs . Provider ] providers . Factory {
addrs . NewDefaultProvider ( "testing" ) : func ( ) ( providers . Interface , error ) {
2024-09-07 08:22:16 -04:00
return stacks_testing_provider . NewProvider ( t ) , nil
2024-06-21 08:05:26 -04:00
} ,
} ,
}
applyChangesCh := make ( chan stackstate . AppliedChange )
diagsCh = make ( chan tfdiags . Diagnostic )
applyResp := ApplyResponse {
AppliedChanges : applyChangesCh ,
Diagnostics : diagsCh ,
}
go Apply ( ctx , & applyReq , & applyResp )
applyChanges , applyDiags := collectApplyOutput ( applyChangesCh , diagsCh )
if len ( applyDiags ) > 0 {
t . Fatalf ( "expected no diagnostics, got %s" , applyDiags . ErrWithWarnings ( ) )
}
for _ , x := range applyChanges {
if v , ok := x . ( * stackstate . AppliedChangeComponentInstance ) ; ok {
if actualTimestampValue , ok := v . OutputValues [ addrs . OutputValue {
Name : "input" ,
} ] ; ok {
actualTimestamp , err := time . Parse ( time . RFC3339 , actualTimestampValue . AsString ( ) )
if err != nil {
t . Fatalf ( "Could not parse component output value: %q" , err )
}
if actualTimestamp . Before ( dayOfWritingThisTestTime ) {
t . Error ( "Timestamp is before day of writing this test, that should be incorrect." )
}
}
if actualTimestampValue , ok := v . OutputValues [ addrs . OutputValue {
Name : "out" ,
} ] ; ok {
actualTimestamp , err := time . Parse ( time . RFC3339 , strings . ReplaceAll ( actualTimestampValue . AsString ( ) , "module-output-" , "" ) )
if err != nil {
t . Fatalf ( "Could not parse component output value: %q" , err )
}
if actualTimestamp . Before ( dayOfWritingThisTestTime ) {
t . Error ( "Timestamp is before day of writing this test, that should be incorrect." )
}
}
}
}
}
2024-07-17 02:44:39 -04:00
func TestApplyWithFailedComponent ( t * testing . T ) {
ctx := context . Background ( )
cfg := loadMainBundleConfigForTest ( t , filepath . Join ( "with-single-input" , "failed-parent" ) )
fakePlanTimestamp , err := time . Parse ( time . RFC3339 , "1991-08-25T20:57:08Z" )
if err != nil {
t . Fatal ( err )
}
changesCh := make ( chan stackplan . PlannedChange )
diagsCh := make ( chan tfdiags . Diagnostic )
lock := depsfile . NewLocks ( )
lock . SetProvider (
addrs . NewDefaultProvider ( "testing" ) ,
providerreqs . MustParseVersion ( "0.0.0" ) ,
providerreqs . MustParseVersionConstraints ( "=0.0.0" ) ,
providerreqs . PreferredHashes ( [ ] providerreqs . Hash { } ) ,
)
req := PlanRequest {
Config : cfg ,
ProviderFactories : map [ addrs . Provider ] providers . Factory {
addrs . NewDefaultProvider ( "testing" ) : func ( ) ( providers . Interface , error ) {
2024-09-07 08:22:16 -04:00
return stacks_testing_provider . NewProvider ( t ) , nil
2024-07-17 02:44:39 -04:00
} ,
} ,
DependencyLocks : * lock ,
ForcePlanTimestamp : & fakePlanTimestamp ,
}
resp := PlanResponse {
PlannedChanges : changesCh ,
Diagnostics : diagsCh ,
}
go Plan ( ctx , & req , & resp )
planChanges , diags := collectPlanOutput ( changesCh , diagsCh )
if len ( diags ) > 0 {
t . Fatalf ( "expected no diagnostics, got %s" , diags . ErrWithWarnings ( ) )
}
2024-07-12 22:20:34 -04:00
planLoader := stackplan . NewLoader ( )
2024-07-17 02:44:39 -04:00
for _ , change := range planChanges {
proto , err := change . PlannedChangeProto ( )
if err != nil {
t . Fatal ( err )
}
2024-07-12 22:20:34 -04:00
for _ , rawMsg := range proto . Raw {
err = planLoader . AddRaw ( rawMsg )
if err != nil {
t . Fatal ( err )
}
}
}
plan , err := planLoader . Plan ( )
if err != nil {
t . Fatal ( err )
2024-07-17 02:44:39 -04:00
}
applyReq := ApplyRequest {
2024-07-12 22:20:34 -04:00
Config : cfg ,
Plan : plan ,
2024-07-17 02:44:39 -04:00
ProviderFactories : map [ addrs . Provider ] providers . Factory {
addrs . NewDefaultProvider ( "testing" ) : func ( ) ( providers . Interface , error ) {
2024-09-07 08:22:16 -04:00
return stacks_testing_provider . NewProvider ( t ) , nil
2024-07-17 02:44:39 -04:00
} ,
} ,
DependencyLocks : * lock ,
}
applyChangesCh := make ( chan stackstate . AppliedChange )
diagsCh = make ( chan tfdiags . Diagnostic )
applyResp := ApplyResponse {
AppliedChanges : applyChangesCh ,
Diagnostics : diagsCh ,
}
go Apply ( ctx , & applyReq , & applyResp )
applyChanges , applyDiags := collectApplyOutput ( applyChangesCh , diagsCh )
expectDiagnosticsForTest ( t , applyDiags ,
// This is the expected failure, from our testing_failed_resource.
2024-09-05 05:29:15 -04:00
expectDiagnostic ( tfdiags . Error , "failedResource error" , "failed during apply" ) )
2024-07-17 02:44:39 -04:00
wantChanges := [ ] stackstate . AppliedChange {
& stackstate . AppliedChangeComponentInstance {
ComponentAddr : mustAbsComponent ( "component.parent" ) ,
ComponentInstanceAddr : mustAbsComponentInstance ( "component.parent" ) ,
2024-09-05 06:10:24 -04:00
Dependents : collections . NewSet ( mustAbsComponent ( "component.self" ) ) ,
2024-07-17 02:44:39 -04:00
OutputValues : make ( map [ addrs . OutputValue ] cty . Value ) ,
2024-09-07 08:36:16 -04:00
InputVariables : map [ addrs . InputVariable ] cty . Value {
mustInputVariable ( "input" ) : cty . StringVal ( "Hello, world!" ) ,
mustInputVariable ( "id" ) : cty . NullVal ( cty . String ) ,
mustInputVariable ( "fail_plan" ) : cty . NullVal ( cty . Bool ) ,
mustInputVariable ( "fail_apply" ) : cty . BoolVal ( true ) ,
} ,
2024-07-17 02:44:39 -04:00
} ,
& stackstate . AppliedChangeResourceInstanceObject {
ResourceInstanceObjectAddr : mustAbsResourceInstanceObject ( "component.parent.testing_failed_resource.data" ) ,
ProviderConfigAddr : mustDefaultRootProvider ( "testing" ) ,
} ,
& stackstate . AppliedChangeComponentInstance {
ComponentAddr : mustAbsComponent ( "component.self" ) ,
ComponentInstanceAddr : mustAbsComponentInstance ( "component.self" ) ,
2024-09-05 06:10:24 -04:00
Dependencies : collections . NewSet ( mustAbsComponent ( "component.parent" ) ) ,
2024-07-17 02:44:39 -04:00
OutputValues : make ( map [ addrs . OutputValue ] cty . Value ) ,
2025-01-20 06:18:35 -05:00
InputVariables : map [ addrs . InputVariable ] cty . Value {
mustInputVariable ( "id" ) : cty . NullVal ( cty . String ) ,
mustInputVariable ( "input" ) : cty . UnknownVal ( cty . String ) ,
} ,
2024-07-17 02:44:39 -04:00
} ,
}
sort . SliceStable ( applyChanges , func ( i , j int ) bool {
return appliedChangeSortKey ( applyChanges [ i ] ) < appliedChangeSortKey ( applyChanges [ j ] )
} )
2024-08-21 15:30:01 -04:00
if diff := cmp . Diff ( wantChanges , applyChanges , changesCmpOpts ) ; diff != "" {
2024-07-17 02:44:39 -04:00
t . Errorf ( "wrong changes\n%s" , diff )
}
}
func TestApplyWithFailedProviderLinkedComponent ( t * testing . T ) {
ctx := context . Background ( )
cfg := loadMainBundleConfigForTest ( t , filepath . Join ( "with-single-input" , "failed-component-to-provider" ) )
fakePlanTimestamp , err := time . Parse ( time . RFC3339 , "1991-08-25T20:57:08Z" )
if err != nil {
t . Fatal ( err )
}
changesCh := make ( chan stackplan . PlannedChange )
diagsCh := make ( chan tfdiags . Diagnostic )
lock := depsfile . NewLocks ( )
lock . SetProvider (
addrs . NewDefaultProvider ( "testing" ) ,
providerreqs . MustParseVersion ( "0.0.0" ) ,
providerreqs . MustParseVersionConstraints ( "=0.0.0" ) ,
providerreqs . PreferredHashes ( [ ] providerreqs . Hash { } ) ,
)
req := PlanRequest {
Config : cfg ,
ProviderFactories : map [ addrs . Provider ] providers . Factory {
addrs . NewDefaultProvider ( "testing" ) : func ( ) ( providers . Interface , error ) {
2024-09-07 08:22:16 -04:00
return stacks_testing_provider . NewProvider ( t ) , nil
2024-07-17 02:44:39 -04:00
} ,
} ,
DependencyLocks : * lock ,
ForcePlanTimestamp : & fakePlanTimestamp ,
}
resp := PlanResponse {
PlannedChanges : changesCh ,
Diagnostics : diagsCh ,
}
go Plan ( ctx , & req , & resp )
planChanges , diags := collectPlanOutput ( changesCh , diagsCh )
if len ( diags ) > 0 {
t . Fatalf ( "expected no diagnostics, got %s" , diags . ErrWithWarnings ( ) )
}
2024-07-12 22:20:34 -04:00
planLoader := stackplan . NewLoader ( )
2024-07-17 02:44:39 -04:00
for _ , change := range planChanges {
proto , err := change . PlannedChangeProto ( )
if err != nil {
t . Fatal ( err )
}
2024-07-12 22:20:34 -04:00
for _ , rawMsg := range proto . Raw {
err = planLoader . AddRaw ( rawMsg )
if err != nil {
t . Fatal ( err )
}
}
}
plan , err := planLoader . Plan ( )
if err != nil {
t . Fatal ( err )
2024-07-17 02:44:39 -04:00
}
applyReq := ApplyRequest {
2024-07-12 22:20:34 -04:00
Config : cfg ,
Plan : plan ,
2024-07-17 02:44:39 -04:00
ProviderFactories : map [ addrs . Provider ] providers . Factory {
addrs . NewDefaultProvider ( "testing" ) : func ( ) ( providers . Interface , error ) {
2024-09-07 08:22:16 -04:00
return stacks_testing_provider . NewProvider ( t ) , nil
2024-07-17 02:44:39 -04:00
} ,
} ,
DependencyLocks : * lock ,
}
applyChangesCh := make ( chan stackstate . AppliedChange )
diagsCh = make ( chan tfdiags . Diagnostic )
applyResp := ApplyResponse {
AppliedChanges : applyChangesCh ,
Diagnostics : diagsCh ,
}
go Apply ( ctx , & applyReq , & applyResp )
applyChanges , applyDiags := collectApplyOutput ( applyChangesCh , diagsCh )
expectDiagnosticsForTest ( t , applyDiags ,
// This is the expected failure, from our testing_failed_resource.
2024-09-05 05:29:15 -04:00
expectDiagnostic ( tfdiags . Error , "failedResource error" , "failed during apply" ) )
2024-07-17 02:44:39 -04:00
wantChanges := [ ] stackstate . AppliedChange {
& stackstate . AppliedChangeComponentInstance {
ComponentAddr : mustAbsComponent ( "component.parent" ) ,
ComponentInstanceAddr : mustAbsComponentInstance ( "component.parent" ) ,
2024-09-05 06:10:24 -04:00
Dependents : collections . NewSet ( mustAbsComponent ( "component.self" ) ) ,
2024-07-17 02:44:39 -04:00
OutputValues : make ( map [ addrs . OutputValue ] cty . Value ) ,
2024-09-07 08:36:16 -04:00
InputVariables : map [ addrs . InputVariable ] cty . Value {
mustInputVariable ( "input" ) : cty . NullVal ( cty . String ) ,
mustInputVariable ( "id" ) : cty . NullVal ( cty . String ) ,
mustInputVariable ( "fail_plan" ) : cty . NullVal ( cty . Bool ) ,
mustInputVariable ( "fail_apply" ) : cty . BoolVal ( true ) ,
} ,
2024-07-17 02:44:39 -04:00
} ,
& stackstate . AppliedChangeResourceInstanceObject {
ResourceInstanceObjectAddr : mustAbsResourceInstanceObject ( "component.parent.testing_failed_resource.data" ) ,
ProviderConfigAddr : mustDefaultRootProvider ( "testing" ) ,
} ,
& stackstate . AppliedChangeComponentInstance {
ComponentAddr : mustAbsComponent ( "component.self" ) ,
ComponentInstanceAddr : mustAbsComponentInstance ( "component.self" ) ,
2024-09-05 06:10:24 -04:00
Dependencies : collections . NewSet ( mustAbsComponent ( "component.parent" ) ) ,
2024-07-17 02:44:39 -04:00
OutputValues : make ( map [ addrs . OutputValue ] cty . Value ) ,
2024-09-07 08:36:16 -04:00
InputVariables : map [ addrs . InputVariable ] cty . Value {
mustInputVariable ( "id" ) : cty . NullVal ( cty . String ) ,
mustInputVariable ( "input" ) : cty . StringVal ( "Hello, world!" ) ,
} ,
2024-07-17 02:44:39 -04:00
} ,
}
sort . SliceStable ( applyChanges , func ( i , j int ) bool {
return appliedChangeSortKey ( applyChanges [ i ] ) < appliedChangeSortKey ( applyChanges [ j ] )
} )
2024-08-21 15:30:01 -04:00
if diff := cmp . Diff ( wantChanges , applyChanges , changesCmpOpts ) ; diff != "" {
2024-07-17 02:44:39 -04:00
t . Errorf ( "wrong changes\n%s" , diff )
}
}
2024-07-01 08:23:17 -04:00
func TestApplyWithStateManipulation ( t * testing . T ) {
fakePlanTimestamp , err := time . Parse ( time . RFC3339 , "1991-08-25T20:57:08Z" )
if err != nil {
t . Fatal ( err )
}
2024-07-03 13:25:27 -04:00
lock := depsfile . NewLocks ( )
lock . SetProvider (
addrs . NewDefaultProvider ( "testing" ) ,
providerreqs . MustParseVersion ( "0.0.0" ) ,
providerreqs . MustParseVersionConstraints ( "=0.0.0" ) ,
providerreqs . PreferredHashes ( [ ] providerreqs . Hash { } ) ,
)
2024-07-01 08:23:17 -04:00
tcs := map [ string ] struct {
2024-08-19 05:45:08 -04:00
state * stackstate . State
store * stacks_testing_provider . ResourceStore
inputs map [ string ] cty . Value
changes [ ] stackstate . AppliedChange
counts collections . Map [ stackaddrs . AbsComponentInstance , * hooks . ComponentInstanceChange ]
planDiags [ ] expectedDiagnostic
applyDiags [ ] expectedDiagnostic
2024-07-01 08:23:17 -04:00
} {
"moved" : {
state : stackstate . NewStateBuilder ( ) .
AddResourceInstance ( stackstate . NewResourceInstanceBuilder ( ) .
SetAddr ( mustAbsResourceInstanceObject ( "component.self.testing_resource.before" ) ) .
SetProviderAddr ( mustDefaultRootProvider ( "testing" ) ) .
SetResourceInstanceObjectSrc ( states . ResourceInstanceObjectSrc {
Status : states . ObjectReady ,
AttrsJSON : mustMarshalJSONAttrs ( map [ string ] any {
"id" : "moved" ,
"value" : "moved" ,
} ) ,
} ) ) .
Build ( ) ,
store : stacks_testing_provider . NewResourceStoreBuilder ( ) .
AddResource ( "moved" , cty . ObjectVal ( map [ string ] cty . Value {
"id" : cty . StringVal ( "moved" ) ,
"value" : cty . StringVal ( "moved" ) ,
} ) ) .
Build ( ) ,
changes : [ ] stackstate . AppliedChange {
& stackstate . AppliedChangeComponentInstance {
ComponentAddr : mustAbsComponent ( "component.self" ) ,
ComponentInstanceAddr : mustAbsComponentInstance ( "component.self" ) ,
OutputValues : make ( map [ addrs . OutputValue ] cty . Value ) ,
2024-09-07 08:36:16 -04:00
InputVariables : make ( map [ addrs . InputVariable ] cty . Value ) ,
2024-07-01 08:23:17 -04:00
} ,
& stackstate . AppliedChangeResourceInstanceObject {
ResourceInstanceObjectAddr : mustAbsResourceInstanceObject ( "component.self.testing_resource.after" ) ,
NewStateSrc : & states . ResourceInstanceObjectSrc {
AttrsJSON : mustMarshalJSONAttrs ( map [ string ] interface { } {
"id" : "moved" ,
"value" : "moved" ,
} ) ,
Status : states . ObjectReady ,
AttrSensitivePaths : make ( [ ] cty . Path , 0 ) ,
} ,
ProviderConfigAddr : mustDefaultRootProvider ( "testing" ) ,
PreviousResourceInstanceObjectAddr : mustAbsResourceInstanceObjectPtr ( "component.self.testing_resource.before" ) ,
Schema : stacks_testing_provider . TestingResourceSchema ,
} ,
} ,
counts : collections . NewMap [ stackaddrs . AbsComponentInstance , * hooks . ComponentInstanceChange ] (
collections . MapElem [ stackaddrs . AbsComponentInstance , * hooks . ComponentInstanceChange ] {
K : mustAbsComponentInstance ( "component.self" ) ,
V : & hooks . ComponentInstanceChange {
Addr : mustAbsComponentInstance ( "component.self" ) ,
Move : 1 ,
} ,
} ) ,
} ,
2024-08-19 05:45:08 -04:00
"moved-failed-dep" : {
state : stackstate . NewStateBuilder ( ) .
AddResourceInstance ( stackstate . NewResourceInstanceBuilder ( ) .
SetAddr ( mustAbsResourceInstanceObject ( "component.self.testing_resource.before" ) ) .
SetProviderAddr ( mustDefaultRootProvider ( "testing" ) ) .
SetResourceInstanceObjectSrc ( states . ResourceInstanceObjectSrc {
Status : states . ObjectReady ,
AttrsJSON : mustMarshalJSONAttrs ( map [ string ] any {
"id" : "moved" ,
"value" : "moved" ,
} ) ,
} ) ) .
Build ( ) ,
store : stacks_testing_provider . NewResourceStoreBuilder ( ) .
AddResource ( "moved" , cty . ObjectVal ( map [ string ] cty . Value {
"id" : cty . StringVal ( "moved" ) ,
"value" : cty . StringVal ( "moved" ) ,
} ) ) .
Build ( ) ,
changes : [ ] stackstate . AppliedChange {
& stackstate . AppliedChangeComponentInstance {
ComponentAddr : mustAbsComponent ( "component.self" ) ,
ComponentInstanceAddr : mustAbsComponentInstance ( "component.self" ) ,
OutputValues : make ( map [ addrs . OutputValue ] cty . Value ) ,
2024-09-07 08:36:16 -04:00
InputVariables : make ( map [ addrs . InputVariable ] cty . Value ) ,
2024-08-19 05:45:08 -04:00
} ,
& stackstate . AppliedChangeResourceInstanceObject {
ResourceInstanceObjectAddr : mustAbsResourceInstanceObject ( "component.self.testing_failed_resource.resource" ) ,
ProviderConfigAddr : mustDefaultRootProvider ( "testing" ) ,
} ,
& stackstate . AppliedChangeResourceInstanceObject {
ResourceInstanceObjectAddr : mustAbsResourceInstanceObject ( "component.self.testing_resource.after" ) ,
NewStateSrc : & states . ResourceInstanceObjectSrc {
AttrsJSON : mustMarshalJSONAttrs ( map [ string ] interface { } {
"id" : "moved" ,
"value" : "moved" ,
} ) ,
Status : states . ObjectReady ,
AttrSensitivePaths : make ( [ ] cty . Path , 0 ) ,
Dependencies : [ ] addrs . ConfigResource {
{
Resource : addrs . Resource {
Mode : addrs . ManagedResourceMode ,
Type : "testing_failed_resource" ,
Name : "resource" ,
} ,
} ,
} ,
} ,
ProviderConfigAddr : mustDefaultRootProvider ( "testing" ) ,
PreviousResourceInstanceObjectAddr : mustAbsResourceInstanceObjectPtr ( "component.self.testing_resource.before" ) ,
Schema : stacks_testing_provider . TestingResourceSchema ,
} ,
} ,
counts : collections . NewMap [ stackaddrs . AbsComponentInstance , * hooks . ComponentInstanceChange ] (
collections . MapElem [ stackaddrs . AbsComponentInstance , * hooks . ComponentInstanceChange ] {
K : mustAbsComponentInstance ( "component.self" ) ,
V : & hooks . ComponentInstanceChange {
Addr : mustAbsComponentInstance ( "component.self" ) ,
Move : 1 ,
} ,
} ) ,
applyDiags : [ ] expectedDiagnostic {
// This error comes from the testing_failed_resource
2024-09-05 05:29:15 -04:00
expectDiagnostic ( tfdiags . Error , "failedResource error" , "failed during apply" ) ,
2024-08-19 05:45:08 -04:00
} ,
} ,
2024-07-01 08:23:17 -04:00
"import" : {
state : stackstate . NewStateBuilder ( ) . Build ( ) , // We start with an empty state for this.
store : stacks_testing_provider . NewResourceStoreBuilder ( ) .
AddResource ( "imported" , cty . ObjectVal ( map [ string ] cty . Value {
"id" : cty . StringVal ( "imported" ) ,
"value" : cty . StringVal ( "imported" ) ,
} ) ) .
Build ( ) ,
inputs : map [ string ] cty . Value {
"id" : cty . StringVal ( "imported" ) ,
} ,
changes : [ ] stackstate . AppliedChange {
& stackstate . AppliedChangeComponentInstance {
ComponentAddr : mustAbsComponent ( "component.self" ) ,
ComponentInstanceAddr : mustAbsComponentInstance ( "component.self" ) ,
OutputValues : make ( map [ addrs . OutputValue ] cty . Value ) ,
2024-09-07 08:36:16 -04:00
InputVariables : map [ addrs . InputVariable ] cty . Value {
mustInputVariable ( "id" ) : cty . StringVal ( "imported" ) ,
} ,
2024-07-01 08:23:17 -04:00
} ,
& stackstate . AppliedChangeResourceInstanceObject {
ResourceInstanceObjectAddr : mustAbsResourceInstanceObject ( "component.self.testing_resource.data" ) ,
NewStateSrc : & states . ResourceInstanceObjectSrc {
AttrsJSON : mustMarshalJSONAttrs ( map [ string ] interface { } {
"id" : "imported" ,
"value" : "imported" ,
} ) ,
Status : states . ObjectReady ,
AttrSensitivePaths : make ( [ ] cty . Path , 0 ) ,
} ,
ProviderConfigAddr : mustDefaultRootProvider ( "testing" ) ,
Schema : stacks_testing_provider . TestingResourceSchema ,
} ,
2024-09-16 05:45:19 -04:00
& stackstate . AppliedChangeInputVariable {
Addr : mustStackInputVariable ( "id" ) ,
Value : cty . StringVal ( "imported" ) ,
} ,
2024-07-01 08:23:17 -04:00
} ,
counts : collections . NewMap [ stackaddrs . AbsComponentInstance , * hooks . ComponentInstanceChange ] (
collections . MapElem [ stackaddrs . AbsComponentInstance , * hooks . ComponentInstanceChange ] {
K : mustAbsComponentInstance ( "component.self" ) ,
V : & hooks . ComponentInstanceChange {
Addr : mustAbsComponentInstance ( "component.self" ) ,
Import : 1 ,
} ,
} ) ,
} ,
2024-08-19 05:45:08 -04:00
"import-failed-dep" : {
state : stackstate . NewStateBuilder ( ) . Build ( ) , // We start with an empty state for this.
store : stacks_testing_provider . NewResourceStoreBuilder ( ) .
AddResource ( "imported" , cty . ObjectVal ( map [ string ] cty . Value {
"id" : cty . StringVal ( "imported" ) ,
"value" : cty . StringVal ( "imported" ) ,
} ) ) .
Build ( ) ,
inputs : map [ string ] cty . Value {
"id" : cty . StringVal ( "imported" ) ,
} ,
changes : [ ] stackstate . AppliedChange {
& stackstate . AppliedChangeComponentInstance {
ComponentAddr : mustAbsComponent ( "component.self" ) ,
ComponentInstanceAddr : mustAbsComponentInstance ( "component.self" ) ,
OutputValues : make ( map [ addrs . OutputValue ] cty . Value ) ,
2024-09-07 08:36:16 -04:00
InputVariables : map [ addrs . InputVariable ] cty . Value {
mustInputVariable ( "id" ) : cty . StringVal ( "imported" ) ,
} ,
2024-08-19 05:45:08 -04:00
} ,
& stackstate . AppliedChangeResourceInstanceObject {
ResourceInstanceObjectAddr : mustAbsResourceInstanceObject ( "component.self.testing_failed_resource.resource" ) ,
ProviderConfigAddr : mustDefaultRootProvider ( "testing" ) ,
} ,
& stackstate . AppliedChangeResourceInstanceObject {
ResourceInstanceObjectAddr : mustAbsResourceInstanceObject ( "component.self.testing_resource.data" ) ,
NewStateSrc : & states . ResourceInstanceObjectSrc {
AttrsJSON : mustMarshalJSONAttrs ( map [ string ] interface { } {
"id" : "imported" ,
"value" : "imported" ,
} ) ,
Status : states . ObjectReady ,
AttrSensitivePaths : make ( [ ] cty . Path , 0 ) ,
Dependencies : [ ] addrs . ConfigResource {
{
Resource : addrs . Resource {
Mode : addrs . ManagedResourceMode ,
Type : "testing_failed_resource" ,
Name : "resource" ,
} ,
} ,
} ,
} ,
ProviderConfigAddr : mustDefaultRootProvider ( "testing" ) ,
Schema : stacks_testing_provider . TestingResourceSchema ,
} ,
2024-09-16 05:45:19 -04:00
& stackstate . AppliedChangeInputVariable {
Addr : mustStackInputVariable ( "id" ) ,
Value : cty . StringVal ( "imported" ) ,
} ,
2024-08-19 05:45:08 -04:00
} ,
counts : collections . NewMap [ stackaddrs . AbsComponentInstance , * hooks . ComponentInstanceChange ] (
collections . MapElem [ stackaddrs . AbsComponentInstance , * hooks . ComponentInstanceChange ] {
K : mustAbsComponentInstance ( "component.self" ) ,
V : & hooks . ComponentInstanceChange {
Addr : mustAbsComponentInstance ( "component.self" ) ,
Import : 1 ,
} ,
} ) ,
applyDiags : [ ] expectedDiagnostic {
// This error comes from the testing_failed_resource
2024-09-05 05:29:15 -04:00
expectDiagnostic ( tfdiags . Error , "failedResource error" , "failed during apply" ) ,
2024-08-19 05:45:08 -04:00
} ,
} ,
2024-07-01 08:23:17 -04:00
"removed" : {
state : stackstate . NewStateBuilder ( ) .
AddResourceInstance ( stackstate . NewResourceInstanceBuilder ( ) .
SetAddr ( mustAbsResourceInstanceObject ( "component.self.testing_resource.resource" ) ) .
SetProviderAddr ( mustDefaultRootProvider ( "testing" ) ) .
SetResourceInstanceObjectSrc ( states . ResourceInstanceObjectSrc {
Status : states . ObjectReady ,
AttrsJSON : mustMarshalJSONAttrs ( map [ string ] any {
"id" : "removed" ,
"value" : "removed" ,
} ) ,
} ) ) .
Build ( ) ,
store : stacks_testing_provider . NewResourceStoreBuilder ( ) .
AddResource ( "removed" , cty . ObjectVal ( map [ string ] cty . Value {
"id" : cty . StringVal ( "removed" ) ,
"value" : cty . StringVal ( "removed" ) ,
} ) ) .
Build ( ) ,
changes : [ ] stackstate . AppliedChange {
& stackstate . AppliedChangeComponentInstance {
ComponentAddr : mustAbsComponent ( "component.self" ) ,
ComponentInstanceAddr : mustAbsComponentInstance ( "component.self" ) ,
OutputValues : make ( map [ addrs . OutputValue ] cty . Value ) ,
2024-09-07 08:36:16 -04:00
InputVariables : make ( map [ addrs . InputVariable ] cty . Value ) ,
2024-07-01 08:23:17 -04:00
} ,
& stackstate . AppliedChangeResourceInstanceObject {
ResourceInstanceObjectAddr : mustAbsResourceInstanceObject ( "component.self.testing_resource.resource" ) ,
NewStateSrc : nil , // Deleted, so is nil.
ProviderConfigAddr : mustDefaultRootProvider ( "testing" ) ,
} ,
} ,
counts : collections . NewMap [ stackaddrs . AbsComponentInstance , * hooks . ComponentInstanceChange ] (
collections . MapElem [ stackaddrs . AbsComponentInstance , * hooks . ComponentInstanceChange ] {
K : mustAbsComponentInstance ( "component.self" ) ,
V : & hooks . ComponentInstanceChange {
Addr : mustAbsComponentInstance ( "component.self" ) ,
Forget : 1 ,
} ,
} ) ,
2024-08-19 05:45:08 -04:00
planDiags : [ ] expectedDiagnostic {
expectDiagnostic ( tfdiags . Warning , "Some objects will no longer be managed by Terraform" , "If you apply this plan, Terraform will discard its tracking information for the following objects, but it will not delete them:\n - testing_resource.resource\n\nAfter applying this plan, Terraform will no longer manage these objects. You will need to import them into Terraform to manage them again." ) ,
} ,
} ,
"removed-failed-dep" : {
state : stackstate . NewStateBuilder ( ) .
AddResourceInstance ( stackstate . NewResourceInstanceBuilder ( ) .
SetAddr ( mustAbsResourceInstanceObject ( "component.self.testing_resource.resource" ) ) .
SetProviderAddr ( mustDefaultRootProvider ( "testing" ) ) .
SetResourceInstanceObjectSrc ( states . ResourceInstanceObjectSrc {
Status : states . ObjectReady ,
AttrsJSON : mustMarshalJSONAttrs ( map [ string ] any {
"id" : "removed" ,
"value" : "removed" ,
} ) ,
} ) ) .
Build ( ) ,
store : stacks_testing_provider . NewResourceStoreBuilder ( ) .
AddResource ( "removed" , cty . ObjectVal ( map [ string ] cty . Value {
"id" : cty . StringVal ( "removed" ) ,
"value" : cty . StringVal ( "removed" ) ,
} ) ) .
Build ( ) ,
changes : [ ] stackstate . AppliedChange {
& stackstate . AppliedChangeComponentInstance {
ComponentAddr : mustAbsComponent ( "component.self" ) ,
ComponentInstanceAddr : mustAbsComponentInstance ( "component.self" ) ,
OutputValues : make ( map [ addrs . OutputValue ] cty . Value ) ,
2024-09-07 08:36:16 -04:00
InputVariables : make ( map [ addrs . InputVariable ] cty . Value ) ,
2024-08-19 05:45:08 -04:00
} ,
& stackstate . AppliedChangeResourceInstanceObject {
ResourceInstanceObjectAddr : mustAbsResourceInstanceObject ( "component.self.testing_failed_resource.resource" ) ,
ProviderConfigAddr : mustDefaultRootProvider ( "testing" ) ,
} ,
& stackstate . AppliedChangeResourceInstanceObject {
ResourceInstanceObjectAddr : mustAbsResourceInstanceObject ( "component.self.testing_resource.resource" ) ,
NewStateSrc : nil , // Deleted, so is nil.
ProviderConfigAddr : mustDefaultRootProvider ( "testing" ) ,
} ,
} ,
counts : collections . NewMap [ stackaddrs . AbsComponentInstance , * hooks . ComponentInstanceChange ] (
collections . MapElem [ stackaddrs . AbsComponentInstance , * hooks . ComponentInstanceChange ] {
K : mustAbsComponentInstance ( "component.self" ) ,
V : & hooks . ComponentInstanceChange {
Addr : mustAbsComponentInstance ( "component.self" ) ,
Forget : 1 ,
} ,
} ) ,
planDiags : [ ] expectedDiagnostic {
expectDiagnostic ( tfdiags . Warning , "Some objects will no longer be managed by Terraform" , "If you apply this plan, Terraform will discard its tracking information for the following objects, but it will not delete them:\n - testing_resource.resource\n\nAfter applying this plan, Terraform will no longer manage these objects. You will need to import them into Terraform to manage them again." ) ,
} ,
applyDiags : [ ] expectedDiagnostic {
// This error comes from the testing_failed_resource
2024-09-05 05:29:15 -04:00
expectDiagnostic ( tfdiags . Error , "failedResource error" , "failed during apply" ) ,
2024-08-19 05:45:08 -04:00
} ,
2024-07-01 08:23:17 -04:00
} ,
2024-07-11 08:28:07 -04:00
"deferred" : {
store : stacks_testing_provider . NewResourceStoreBuilder ( ) .
AddResource ( "self" , cty . ObjectVal ( map [ string ] cty . Value {
"id" : cty . StringVal ( "deferred" ) ,
"value" : cty . UnknownVal ( cty . String ) ,
} ) ) .
Build ( ) ,
changes : [ ] stackstate . AppliedChange {
& stackstate . AppliedChangeComponentInstance {
ComponentAddr : mustAbsComponent ( "component.deferred" ) ,
ComponentInstanceAddr : mustAbsComponentInstance ( "component.deferred" ) ,
OutputValues : make ( map [ addrs . OutputValue ] cty . Value ) ,
2024-09-07 08:36:16 -04:00
InputVariables : make ( map [ addrs . InputVariable ] cty . Value ) ,
2024-07-11 08:28:07 -04:00
} ,
& stackstate . AppliedChangeComponentInstance {
ComponentAddr : mustAbsComponent ( "component.ok" ) ,
ComponentInstanceAddr : mustAbsComponentInstance ( "component.ok" ) ,
OutputValues : make ( map [ addrs . OutputValue ] cty . Value ) ,
2024-09-07 08:36:16 -04:00
InputVariables : make ( map [ addrs . InputVariable ] cty . Value ) ,
2024-07-11 08:28:07 -04:00
} ,
& stackstate . AppliedChangeResourceInstanceObject {
ResourceInstanceObjectAddr : mustAbsResourceInstanceObject ( "component.ok.testing_resource.self" ) ,
NewStateSrc : & states . ResourceInstanceObjectSrc {
AttrsJSON : mustMarshalJSONAttrs ( map [ string ] interface { } {
"id" : "ok" ,
"value" : "ok" ,
} ) ,
Status : states . ObjectReady ,
AttrSensitivePaths : nil ,
Dependencies : [ ] addrs . ConfigResource { } ,
} ,
ProviderConfigAddr : mustDefaultRootProvider ( "testing" ) ,
PreviousResourceInstanceObjectAddr : nil ,
Schema : stacks_testing_provider . TestingResourceSchema ,
} ,
} ,
counts : collections . NewMap [ stackaddrs . AbsComponentInstance , * hooks . ComponentInstanceChange ] (
collections . MapElem [ stackaddrs . AbsComponentInstance , * hooks . ComponentInstanceChange ] {
K : mustAbsComponentInstance ( "component.ok" ) ,
V : & hooks . ComponentInstanceChange {
Addr : mustAbsComponentInstance ( "component.ok" ) ,
Add : 1 ,
Defer : 0 ,
} ,
} ,
collections . MapElem [ stackaddrs . AbsComponentInstance , * hooks . ComponentInstanceChange ] {
K : mustAbsComponentInstance ( "component.deferred" ) ,
V : & hooks . ComponentInstanceChange {
Addr : mustAbsComponentInstance ( "component.deferred" ) ,
Defer : 1 ,
} ,
} ,
) ,
} ,
2024-07-01 08:23:17 -04:00
}
for name , tc := range tcs {
t . Run ( name , func ( t * testing . T ) {
ctx := context . Background ( )
cfg := loadMainBundleConfigForTest ( t , path . Join ( "state-manipulation" , name ) )
inputs := make ( map [ stackaddrs . InputVariable ] ExternalInputValue , len ( tc . inputs ) )
for name , input := range tc . inputs {
inputs [ stackaddrs . InputVariable { Name : name } ] = ExternalInputValue {
Value : input ,
}
}
providers := map [ addrs . Provider ] providers . Factory {
addrs . NewDefaultProvider ( "testing" ) : func ( ) ( providers . Interface , error ) {
2024-09-07 08:22:16 -04:00
return stacks_testing_provider . NewProviderWithData ( t , tc . store ) , nil
2024-07-01 08:23:17 -04:00
} ,
}
planChangeCh := make ( chan stackplan . PlannedChange )
diagsCh := make ( chan tfdiags . Diagnostic )
planReq := PlanRequest {
Config : cfg ,
ProviderFactories : providers ,
InputValues : inputs ,
ForcePlanTimestamp : & fakePlanTimestamp ,
PrevState : tc . state ,
2024-07-03 13:25:27 -04:00
DependencyLocks : * lock ,
2024-07-01 08:23:17 -04:00
}
planResp := PlanResponse {
PlannedChanges : planChangeCh ,
Diagnostics : diagsCh ,
}
go Plan ( ctx , & planReq , & planResp )
planChanges , diags := collectPlanOutput ( planChangeCh , diagsCh )
2024-08-19 05:45:08 -04:00
sort . SliceStable ( diags , diagnosticSortFunc ( diags ) )
expectDiagnosticsForTest ( t , diags , tc . planDiags ... )
2024-07-01 08:23:17 -04:00
// Check the counts during the apply for this test.
gotCounts := collections . NewMap [ stackaddrs . AbsComponentInstance , * hooks . ComponentInstanceChange ] ( )
ctx = ContextWithHooks ( ctx , & stackeval . Hooks {
ReportComponentInstanceApplied : func ( ctx context . Context , span any , change * hooks . ComponentInstanceChange ) any {
gotCounts . Put ( change . Addr , change )
return span
} ,
} )
2024-07-12 22:20:34 -04:00
planLoader := stackplan . NewLoader ( )
for _ , change := range planChanges {
proto , err := change . PlannedChangeProto ( )
if err != nil {
t . Fatal ( err )
}
for _ , rawMsg := range proto . Raw {
err = planLoader . AddRaw ( rawMsg )
if err != nil {
t . Fatal ( err )
}
}
}
plan , err := planLoader . Plan ( )
if err != nil {
t . Fatal ( err )
}
2024-07-01 08:23:17 -04:00
applyReq := ApplyRequest {
Config : cfg ,
2024-07-12 22:20:34 -04:00
Plan : plan ,
2024-07-01 08:23:17 -04:00
ProviderFactories : providers ,
2024-07-03 13:25:27 -04:00
DependencyLocks : * lock ,
2024-07-01 08:23:17 -04:00
}
2024-07-12 22:20:34 -04:00
applyChangesCh := make ( chan stackstate . AppliedChange )
diagsCh = make ( chan tfdiags . Diagnostic )
2024-07-01 08:23:17 -04:00
applyResp := ApplyResponse {
AppliedChanges : applyChangesCh ,
Diagnostics : diagsCh ,
}
go Apply ( ctx , & applyReq , & applyResp )
applyChanges , diags := collectApplyOutput ( applyChangesCh , diagsCh )
2024-08-19 05:45:08 -04:00
sort . SliceStable ( diags , diagnosticSortFunc ( diags ) )
expectDiagnosticsForTest ( t , diags , tc . applyDiags ... )
2024-07-01 08:23:17 -04:00
sort . SliceStable ( applyChanges , func ( i , j int ) bool {
return appliedChangeSortKey ( applyChanges [ i ] ) < appliedChangeSortKey ( applyChanges [ j ] )
} )
2024-08-21 15:30:01 -04:00
if diff := cmp . Diff ( tc . changes , applyChanges , changesCmpOpts ) ; diff != "" {
2024-07-01 08:23:17 -04:00
t . Errorf ( "wrong changes\n%s" , diff )
}
wantCounts := tc . counts
2024-10-03 15:02:12 -04:00
for key , elem := range wantCounts . All ( ) {
2024-07-01 08:23:17 -04:00
// First, make sure everything we wanted is present.
2024-10-03 15:02:12 -04:00
if ! gotCounts . HasKey ( key ) {
t . Errorf ( "wrong counts: wanted %s but didn't get it" , key )
2024-07-01 08:23:17 -04:00
}
// And that the values actually match.
2024-10-03 15:02:12 -04:00
got , want := gotCounts . Get ( key ) , elem
2024-07-01 08:23:17 -04:00
if diff := cmp . Diff ( want , got ) ; diff != "" {
t . Errorf ( "wrong counts for %s: %s" , want . Addr , diff )
}
}
2024-10-03 15:02:12 -04:00
for key := range gotCounts . All ( ) {
2024-07-01 08:23:17 -04:00
// Then, make sure we didn't get anything we didn't want.
2024-10-03 15:02:12 -04:00
if ! wantCounts . HasKey ( key ) {
t . Errorf ( "wrong counts: got %s but didn't want it" , key )
2024-07-01 08:23:17 -04:00
}
}
} )
}
}
2024-08-05 04:08:35 -04:00
func TestApplyWithChangedInputValues ( t * testing . T ) {
ctx := context . Background ( )
cfg := loadMainBundleConfigForTest ( t , filepath . Join ( "with-single-input" , "valid" ) )
fakePlanTimestamp , err := time . Parse ( time . RFC3339 , "1991-08-25T20:57:08Z" )
if err != nil {
t . Fatal ( err )
}
changesCh := make ( chan stackplan . PlannedChange )
diagsCh := make ( chan tfdiags . Diagnostic )
lock := depsfile . NewLocks ( )
lock . SetProvider (
addrs . NewDefaultProvider ( "testing" ) ,
providerreqs . MustParseVersion ( "0.0.0" ) ,
providerreqs . MustParseVersionConstraints ( "=0.0.0" ) ,
providerreqs . PreferredHashes ( [ ] providerreqs . Hash { } ) ,
)
req := PlanRequest {
Config : cfg ,
ProviderFactories : map [ addrs . Provider ] providers . Factory {
addrs . NewDefaultProvider ( "testing" ) : func ( ) ( providers . Interface , error ) {
2024-09-07 08:22:16 -04:00
return stacks_testing_provider . NewProvider ( t ) , nil
2024-08-05 04:08:35 -04:00
} ,
} ,
DependencyLocks : * lock ,
ForcePlanTimestamp : & fakePlanTimestamp ,
InputValues : map [ stackaddrs . InputVariable ] ExternalInputValue {
stackaddrs . InputVariable { Name : "input" } : {
Value : cty . StringVal ( "hello" ) ,
} ,
} ,
}
resp := PlanResponse {
PlannedChanges : changesCh ,
Diagnostics : diagsCh ,
}
go Plan ( ctx , & req , & resp )
planChanges , diags := collectPlanOutput ( changesCh , diagsCh )
if len ( diags ) > 0 {
t . Fatalf ( "expected no diagnostics, got %s" , diags . ErrWithWarnings ( ) )
}
planLoader := stackplan . NewLoader ( )
for _ , change := range planChanges {
proto , err := change . PlannedChangeProto ( )
if err != nil {
t . Fatal ( err )
}
for _ , rawMsg := range proto . Raw {
err = planLoader . AddRaw ( rawMsg )
if err != nil {
t . Fatal ( err )
}
}
}
plan , err := planLoader . Plan ( )
if err != nil {
t . Fatal ( err )
}
applyReq := ApplyRequest {
Config : cfg ,
Plan : plan ,
ProviderFactories : map [ addrs . Provider ] providers . Factory {
addrs . NewDefaultProvider ( "testing" ) : func ( ) ( providers . Interface , error ) {
2024-09-07 08:22:16 -04:00
return stacks_testing_provider . NewProvider ( t ) , nil
2024-08-05 04:08:35 -04:00
} ,
} ,
DependencyLocks : * lock ,
InputValues : map [ stackaddrs . InputVariable ] ExternalInputValue {
// This time we're deliberately changing the values we're giving
// to the apply operation. We expect this to fail earlier than
// the previous test.
stackaddrs . InputVariable { Name : "input" } : {
Value : cty . StringVal ( "world" ) ,
} ,
} ,
}
applyChangesCh := make ( chan stackstate . AppliedChange )
diagsCh = make ( chan tfdiags . Diagnostic )
applyResp := ApplyResponse {
AppliedChanges : applyChangesCh ,
Diagnostics : diagsCh ,
}
go Apply ( ctx , & applyReq , & applyResp )
applyChanges , applyDiags := collectApplyOutput ( applyChangesCh , diagsCh )
2024-08-12 08:54:32 -04:00
sort . SliceStable ( applyDiags , diagnosticSortFunc ( applyDiags ) )
expectDiagnosticsForTest ( t , applyDiags ,
expectDiagnostic (
tfdiags . Error ,
"Inconsistent value for input variable during apply" ,
"The value for non-ephemeral input variable \"input\" was set to a different value during apply than was set during plan. Only ephemeral input variables can change between the plan and apply phases." ) ,
2025-07-09 07:05:18 -04:00
expectDiagnostic ( tfdiags . Error , "Invalid inputs for component" , "Input variable \"input\" could not be evaluated, additional diagnostics elsewhere should provide mode detail." ) ,
2024-08-14 03:47:13 -04:00
)
2024-08-05 04:08:35 -04:00
wantChanges := [ ] stackstate . AppliedChange {
& stackstate . AppliedChangeComponentInstance {
ComponentAddr : mustAbsComponent ( "component.self" ) ,
ComponentInstanceAddr : mustAbsComponentInstance ( "component.self" ) ,
OutputValues : make ( map [ addrs . OutputValue ] cty . Value ) ,
2024-09-07 08:36:16 -04:00
InputVariables : make ( map [ addrs . InputVariable ] cty . Value ) ,
2024-08-05 04:08:35 -04:00
} ,
2024-09-16 05:45:19 -04:00
& stackstate . AppliedChangeInputVariable {
Addr : mustStackInputVariable ( "id" ) ,
Value : cty . NullVal ( cty . String ) ,
} ,
2024-08-05 04:08:35 -04:00
// no resources should have been created because the input variable was
// invalid.
}
sort . SliceStable ( applyChanges , func ( i , j int ) bool {
return appliedChangeSortKey ( applyChanges [ i ] ) < appliedChangeSortKey ( applyChanges [ j ] )
} )
2024-08-21 15:30:01 -04:00
if diff := cmp . Diff ( wantChanges , applyChanges , changesCmpOpts ) ; diff != "" {
2024-08-05 04:08:35 -04:00
t . Errorf ( "wrong changes\n%s" , diff )
}
}
func TestApplyAutomaticInputConversion ( t * testing . T ) {
ctx := context . Background ( )
cfg := loadMainBundleConfigForTest ( t , filepath . Join ( "with-single-input" , "for-each-component" ) )
fakePlanTimestamp , err := time . Parse ( time . RFC3339 , "1991-08-25T20:57:08Z" )
if err != nil {
t . Fatal ( err )
}
changesCh := make ( chan stackplan . PlannedChange )
diagsCh := make ( chan tfdiags . Diagnostic )
lock := depsfile . NewLocks ( )
lock . SetProvider (
addrs . NewDefaultProvider ( "testing" ) ,
providerreqs . MustParseVersion ( "0.0.0" ) ,
providerreqs . MustParseVersionConstraints ( "=0.0.0" ) ,
providerreqs . PreferredHashes ( [ ] providerreqs . Hash { } ) ,
)
req := PlanRequest {
Config : cfg ,
ProviderFactories : map [ addrs . Provider ] providers . Factory {
addrs . NewDefaultProvider ( "testing" ) : func ( ) ( providers . Interface , error ) {
2024-09-07 08:22:16 -04:00
return stacks_testing_provider . NewProvider ( t ) , nil
2024-08-05 04:08:35 -04:00
} ,
} ,
DependencyLocks : * lock ,
ForcePlanTimestamp : & fakePlanTimestamp ,
InputValues : map [ stackaddrs . InputVariable ] ExternalInputValue {
stackaddrs . InputVariable { Name : "input" } : {
// The stack expects a map of strings, but we're giving it
// an object. Terraform should automatically convert this to
// the expected type.
Value : cty . ObjectVal ( map [ string ] cty . Value {
"hello" : cty . StringVal ( "hello" ) ,
"world" : cty . StringVal ( "world" ) ,
} ) ,
} ,
} ,
}
resp := PlanResponse {
PlannedChanges : changesCh ,
Diagnostics : diagsCh ,
}
go Plan ( ctx , & req , & resp )
planChanges , planDiags := collectPlanOutput ( changesCh , diagsCh )
if len ( planDiags ) > 0 {
t . Fatalf ( "expected no diagnostics, got %s" , planDiags . ErrWithWarnings ( ) )
}
planLoader := stackplan . NewLoader ( )
for _ , change := range planChanges {
proto , err := change . PlannedChangeProto ( )
if err != nil {
t . Fatal ( err )
}
for _ , rawMsg := range proto . Raw {
err = planLoader . AddRaw ( rawMsg )
if err != nil {
t . Fatal ( err )
}
}
}
plan , err := planLoader . Plan ( )
if err != nil {
t . Fatal ( err )
}
applyReq := ApplyRequest {
Config : cfg ,
Plan : plan ,
ProviderFactories : map [ addrs . Provider ] providers . Factory {
addrs . NewDefaultProvider ( "testing" ) : func ( ) ( providers . Interface , error ) {
2024-09-07 08:22:16 -04:00
return stacks_testing_provider . NewProvider ( t ) , nil
2024-08-05 04:08:35 -04:00
} ,
} ,
DependencyLocks : * lock ,
InputValues : map [ stackaddrs . InputVariable ] ExternalInputValue {
stackaddrs . InputVariable { Name : "input" } : {
// The stack expects a map of strings, but we're giving it
// an object. Terraform should automatically convert this to
// the expected type.
Value : cty . ObjectVal ( map [ string ] cty . Value {
"hello" : cty . StringVal ( "hello" ) ,
"world" : cty . StringVal ( "world" ) ,
} ) ,
} ,
} ,
}
applyChangesCh := make ( chan stackstate . AppliedChange )
diagsCh = make ( chan tfdiags . Diagnostic )
applyResp := ApplyResponse {
AppliedChanges : applyChangesCh ,
Diagnostics : diagsCh ,
}
go Apply ( ctx , & applyReq , & applyResp )
applyChanges , applyDiags := collectApplyOutput ( applyChangesCh , diagsCh )
if len ( applyDiags ) > 0 {
t . Fatalf ( "expected no diagnostics, got %s" , applyDiags . ErrWithWarnings ( ) )
}
sort . SliceStable ( applyChanges , func ( i , j int ) bool {
return appliedChangeSortKey ( applyChanges [ i ] ) < appliedChangeSortKey ( applyChanges [ j ] )
} )
wantChanges := [ ] stackstate . AppliedChange {
& stackstate . AppliedChangeComponentInstance {
ComponentAddr : mustAbsComponent ( "component.self" ) ,
ComponentInstanceAddr : mustAbsComponentInstance ( "component.self[\"hello\"]" ) ,
OutputValues : make ( map [ addrs . OutputValue ] cty . Value ) ,
2024-09-07 08:36:16 -04:00
InputVariables : map [ addrs . InputVariable ] cty . Value {
mustInputVariable ( "id" ) : cty . StringVal ( "hello" ) ,
mustInputVariable ( "input" ) : cty . StringVal ( "hello" ) ,
} ,
2024-08-05 04:08:35 -04:00
} ,
& stackstate . AppliedChangeResourceInstanceObject {
ResourceInstanceObjectAddr : mustAbsResourceInstanceObject ( "component.self[\"hello\"].testing_resource.data" ) ,
NewStateSrc : & states . ResourceInstanceObjectSrc {
AttrsJSON : mustMarshalJSONAttrs ( map [ string ] interface { } {
"id" : "hello" ,
"value" : "hello" ,
} ) ,
Status : states . ObjectReady ,
Dependencies : make ( [ ] addrs . ConfigResource , 0 ) ,
} ,
ProviderConfigAddr : mustDefaultRootProvider ( "testing" ) ,
Schema : stacks_testing_provider . TestingResourceSchema ,
} ,
& stackstate . AppliedChangeComponentInstance {
ComponentAddr : mustAbsComponent ( "component.self" ) ,
ComponentInstanceAddr : mustAbsComponentInstance ( "component.self[\"world\"]" ) ,
OutputValues : make ( map [ addrs . OutputValue ] cty . Value ) ,
2024-09-07 08:36:16 -04:00
InputVariables : map [ addrs . InputVariable ] cty . Value {
mustInputVariable ( "id" ) : cty . StringVal ( "world" ) ,
mustInputVariable ( "input" ) : cty . StringVal ( "world" ) ,
} ,
2024-08-05 04:08:35 -04:00
} ,
& stackstate . AppliedChangeResourceInstanceObject {
ResourceInstanceObjectAddr : mustAbsResourceInstanceObject ( "component.self[\"world\"].testing_resource.data" ) ,
NewStateSrc : & states . ResourceInstanceObjectSrc {
AttrsJSON : mustMarshalJSONAttrs ( map [ string ] interface { } {
"id" : "world" ,
"value" : "world" ,
} ) ,
Status : states . ObjectReady ,
Dependencies : make ( [ ] addrs . ConfigResource , 0 ) ,
} ,
ProviderConfigAddr : mustDefaultRootProvider ( "testing" ) ,
Schema : stacks_testing_provider . TestingResourceSchema ,
} ,
2024-09-16 05:45:19 -04:00
& stackstate . AppliedChangeInputVariable {
Addr : mustStackInputVariable ( "input" ) ,
Value : cty . MapVal ( map [ string ] cty . Value {
"hello" : cty . StringVal ( "hello" ) ,
"world" : cty . StringVal ( "world" ) ,
} ) ,
} ,
2024-08-05 04:08:35 -04:00
}
2024-08-21 15:30:01 -04:00
if diff := cmp . Diff ( wantChanges , applyChanges , changesCmpOpts ) ; diff != "" {
2024-08-05 04:08:35 -04:00
t . Errorf ( "wrong changes\n%s" , diff )
}
}
2024-08-12 09:02:36 -04:00
func TestApply_DependsOnComponentWithNoInstances ( t * testing . T ) {
ctx := context . Background ( )
cfg := loadMainBundleConfigForTest ( t , path . Join ( "with-single-input" , "depends-on" ) )
fakePlanTimestamp , err := time . Parse ( time . RFC3339 , "1991-08-25T20:57:08Z" )
if err != nil {
t . Fatal ( err )
}
lock := depsfile . NewLocks ( )
lock . SetProvider (
addrs . NewDefaultProvider ( "testing" ) ,
providerreqs . MustParseVersion ( "0.0.0" ) ,
providerreqs . MustParseVersionConstraints ( "=0.0.0" ) ,
providerreqs . PreferredHashes ( [ ] providerreqs . Hash { } ) ,
)
changesCh := make ( chan stackplan . PlannedChange )
diagsCh := make ( chan tfdiags . Diagnostic )
planRequest := PlanRequest {
Config : cfg ,
ProviderFactories : map [ addrs . Provider ] providers . Factory {
addrs . NewDefaultProvider ( "testing" ) : func ( ) ( providers . Interface , error ) {
2024-09-07 08:22:16 -04:00
return stacks_testing_provider . NewProvider ( t ) , nil
2024-08-12 09:02:36 -04:00
} ,
} ,
DependencyLocks : * lock ,
ForcePlanTimestamp : & fakePlanTimestamp ,
InputValues : map [ stackaddrs . InputVariable ] ExternalInputValue {
{ Name : "input" } : {
Value : cty . StringVal ( "hello, world!" ) ,
} ,
} ,
}
planResponse := PlanResponse {
PlannedChanges : changesCh ,
Diagnostics : diagsCh ,
}
go Plan ( ctx , & planRequest , & planResponse )
planChanges , planDiags := collectPlanOutput ( changesCh , diagsCh )
reportDiagnosticsForTest ( t , planDiags )
if len ( planDiags ) != 0 {
t . FailNow ( )
}
planLoader := stackplan . NewLoader ( )
for _ , change := range planChanges {
proto , err := change . PlannedChangeProto ( )
if err != nil {
t . Fatal ( err )
}
for _ , rawMsg := range proto . Raw {
err = planLoader . AddRaw ( rawMsg )
if err != nil {
t . Fatal ( err )
}
}
}
plan , err := planLoader . Plan ( )
if err != nil {
t . Fatal ( err )
}
applyReq := ApplyRequest {
Config : cfg ,
Plan : plan ,
ProviderFactories : map [ addrs . Provider ] providers . Factory {
addrs . NewDefaultProvider ( "testing" ) : func ( ) ( providers . Interface , error ) {
2024-09-07 08:22:16 -04:00
return stacks_testing_provider . NewProvider ( t ) , nil
2024-08-12 09:02:36 -04:00
} ,
} ,
DependencyLocks : * lock ,
}
applyChangesCh := make ( chan stackstate . AppliedChange )
diagsCh = make ( chan tfdiags . Diagnostic )
applyResp := ApplyResponse {
AppliedChanges : applyChangesCh ,
Diagnostics : diagsCh ,
}
go Apply ( ctx , & applyReq , & applyResp )
_ , applyDiags := collectApplyOutput ( applyChangesCh , diagsCh )
reportDiagnosticsForTest ( t , applyDiags )
if len ( applyDiags ) != 0 {
t . FailNow ( )
}
// don't care about the changes - just want to make sure that depends_on
// reference to a component with zero instances doesn't break anything
}
2024-08-22 03:20:47 -04:00
func TestApply_WithProviderFunctions ( t * testing . T ) {
ctx := context . Background ( )
cfg := loadMainBundleConfigForTest ( t , filepath . Join ( "with-provider-functions" ) )
fakePlanTimestamp , err := time . Parse ( time . RFC3339 , "1991-08-25T20:57:08Z" )
if err != nil {
t . Fatal ( err )
}
lock := depsfile . NewLocks ( )
lock . SetProvider (
addrs . NewDefaultProvider ( "testing" ) ,
providerreqs . MustParseVersion ( "0.0.0" ) ,
providerreqs . MustParseVersionConstraints ( "=0.0.0" ) ,
providerreqs . PreferredHashes ( [ ] providerreqs . Hash { } ) ,
)
changesCh := make ( chan stackplan . PlannedChange )
diagsCh := make ( chan tfdiags . Diagnostic )
planRequest := PlanRequest {
Config : cfg ,
ProviderFactories : map [ addrs . Provider ] providers . Factory {
addrs . NewDefaultProvider ( "testing" ) : func ( ) ( providers . Interface , error ) {
2024-09-07 08:22:16 -04:00
return stacks_testing_provider . NewProvider ( t ) , nil
2024-08-22 03:20:47 -04:00
} ,
} ,
DependencyLocks : * lock ,
ForcePlanTimestamp : & fakePlanTimestamp ,
InputValues : map [ stackaddrs . InputVariable ] ExternalInputValue {
{ Name : "input" } : {
Value : cty . StringVal ( "hello, world!" ) ,
} ,
} ,
}
planResponse := PlanResponse {
PlannedChanges : changesCh ,
Diagnostics : diagsCh ,
}
go Plan ( ctx , & planRequest , & planResponse )
planChanges , planDiags := collectPlanOutput ( changesCh , diagsCh )
reportDiagnosticsForTest ( t , planDiags )
if len ( planDiags ) != 0 {
t . FailNow ( )
}
2024-08-22 03:28:39 -04:00
sort . SliceStable ( planChanges , func ( i , j int ) bool {
return plannedChangeSortKey ( planChanges [ i ] ) < plannedChangeSortKey ( planChanges [ j ] )
} )
wantPlanChanges := [ ] stackplan . PlannedChange {
& stackplan . PlannedChangeApplyable {
Applyable : true ,
} ,
& stackplan . PlannedChangeComponentInstance {
Addr : mustAbsComponentInstance ( "component.self" ) ,
PlanApplyable : true ,
PlanComplete : true ,
Action : plans . Create ,
RequiredComponents : collections . NewSet [ stackaddrs . AbsComponent ] ( ) ,
PlannedInputValues : map [ string ] plans . DynamicValue {
"id" : mustPlanDynamicValueDynamicType ( cty . StringVal ( "2f9f3b84" ) ) ,
"input" : mustPlanDynamicValueDynamicType ( cty . StringVal ( "hello, world!" ) ) ,
} ,
PlannedInputValueMarks : map [ string ] [ ] cty . PathValueMarks {
"id" : nil ,
"input" : nil ,
} ,
PlannedOutputValues : map [ string ] cty . Value {
"value" : cty . StringVal ( "hello, world!" ) ,
} ,
PlannedCheckResults : & states . CheckResults { } ,
2025-05-08 11:42:05 -04:00
PlannedProviderFunctionResults : [ ] lang . FunctionResultHash {
2024-08-22 03:28:39 -04:00
{
Key : providerFunctionHashArgs ( mustDefaultRootProvider ( "testing" ) . Provider , "echo" , cty . StringVal ( "hello, world!" ) ) ,
Result : providerFunctionHashResult ( cty . StringVal ( "hello, world!" ) ) ,
} ,
} ,
PlanTimestamp : fakePlanTimestamp ,
} ,
& stackplan . PlannedChangeResourceInstancePlanned {
ResourceInstanceObjectAddr : mustAbsResourceInstanceObject ( "component.self.testing_resource.data" ) ,
ChangeSrc : & plans . ResourceInstanceChangeSrc {
Addr : mustAbsResourceInstance ( "testing_resource.data" ) ,
PrevRunAddr : mustAbsResourceInstance ( "testing_resource.data" ) ,
ProviderAddr : mustDefaultRootProvider ( "testing" ) ,
ChangeSrc : plans . ChangeSrc {
Action : plans . Create ,
Before : mustPlanDynamicValue ( cty . NullVal ( cty . Object ( map [ string ] cty . Type {
"id" : cty . String ,
"value" : cty . String ,
} ) ) ) ,
After : mustPlanDynamicValue ( cty . ObjectVal ( map [ string ] cty . Value {
"id" : cty . StringVal ( "2f9f3b84" ) ,
"value" : cty . StringVal ( "hello, world!" ) ,
} ) ) ,
} ,
} ,
ProviderConfigAddr : mustDefaultRootProvider ( "testing" ) ,
Schema : stacks_testing_provider . TestingResourceSchema ,
} ,
& stackplan . PlannedChangeProviderFunctionResults {
2025-05-08 11:42:05 -04:00
Results : [ ] lang . FunctionResultHash {
2024-08-22 03:28:39 -04:00
{
Key : providerFunctionHashArgs ( mustDefaultRootProvider ( "testing" ) . Provider , "echo" , cty . StringVal ( "hello, world!" ) ) ,
Result : providerFunctionHashResult ( cty . StringVal ( "hello, world!" ) ) ,
} ,
} ,
} ,
& stackplan . PlannedChangeHeader {
TerraformVersion : version . SemVer ,
} ,
& stackplan . PlannedChangeOutputValue {
2024-09-16 05:36:36 -04:00
Addr : stackaddrs . OutputValue { Name : "value" } ,
Action : plans . Create ,
Before : cty . NullVal ( cty . DynamicPseudoType ) ,
After : cty . StringVal ( "hello, world!" ) ,
2024-08-22 03:28:39 -04:00
} ,
& stackplan . PlannedChangePlannedTimestamp {
PlannedTimestamp : fakePlanTimestamp ,
} ,
& stackplan . PlannedChangeRootInputValue {
2024-09-16 05:45:19 -04:00
Addr : stackaddrs . InputVariable { Name : "input" } ,
Action : plans . Create ,
Before : cty . NullVal ( cty . DynamicPseudoType ) ,
After : cty . StringVal ( "hello, world!" ) ,
2024-08-22 03:28:39 -04:00
} ,
}
2025-05-16 04:10:47 -04:00
if diff := cmp . Diff ( wantPlanChanges , planChanges , changesCmpOpts ) ; diff != "" {
2024-08-22 03:28:39 -04:00
t . Errorf ( "wrong changes\n%s" , diff )
}
2024-08-22 03:20:47 -04:00
planLoader := stackplan . NewLoader ( )
for _ , change := range planChanges {
proto , err := change . PlannedChangeProto ( )
if err != nil {
t . Fatal ( err )
}
for _ , rawMsg := range proto . Raw {
err = planLoader . AddRaw ( rawMsg )
if err != nil {
t . Fatal ( err )
}
}
}
plan , err := planLoader . Plan ( )
if err != nil {
t . Fatal ( err )
}
2024-08-22 03:28:39 -04:00
// just verify the plan is correctly loading the provider function results
// as well
2025-05-07 14:06:41 -04:00
if len ( plan . FunctionResults ) == 0 {
2024-08-22 03:28:39 -04:00
t . Errorf ( "expected provider function results, got none" )
2025-04-16 08:05:51 -04:00
if len ( plan . GetComponent ( mustAbsComponentInstance ( "component.self" ) ) . PlannedFunctionResults ) == 0 {
2024-08-22 03:28:39 -04:00
t . Errorf ( "expected component function results, got none" )
}
}
2024-08-22 03:20:47 -04:00
applyReq := ApplyRequest {
Config : cfg ,
Plan : plan ,
ProviderFactories : map [ addrs . Provider ] providers . Factory {
addrs . NewDefaultProvider ( "testing" ) : func ( ) ( providers . Interface , error ) {
2024-09-07 08:22:16 -04:00
return stacks_testing_provider . NewProvider ( t ) , nil
2024-08-22 03:20:47 -04:00
} ,
} ,
DependencyLocks : * lock ,
}
applyChangesCh := make ( chan stackstate . AppliedChange )
diagsCh = make ( chan tfdiags . Diagnostic )
applyResp := ApplyResponse {
AppliedChanges : applyChangesCh ,
Diagnostics : diagsCh ,
}
go Apply ( ctx , & applyReq , & applyResp )
applyChanges , applyDiags := collectApplyOutput ( applyChangesCh , diagsCh )
reportDiagnosticsForTest ( t , applyDiags )
if len ( applyDiags ) != 0 {
t . FailNow ( )
}
sort . SliceStable ( applyChanges , func ( i , j int ) bool {
return appliedChangeSortKey ( applyChanges [ i ] ) < appliedChangeSortKey ( applyChanges [ j ] )
} )
2024-08-22 03:28:39 -04:00
wantApplyChanges := [ ] stackstate . AppliedChange {
2024-08-22 03:20:47 -04:00
& stackstate . AppliedChangeComponentInstance {
ComponentAddr : mustAbsComponent ( "component.self" ) ,
ComponentInstanceAddr : mustAbsComponentInstance ( "component.self" ) ,
OutputValues : map [ addrs . OutputValue ] cty . Value {
{ Name : "value" } : cty . StringVal ( "hello, world!" ) ,
} ,
2024-09-07 08:36:16 -04:00
InputVariables : map [ addrs . InputVariable ] cty . Value {
mustInputVariable ( "id" ) : cty . StringVal ( "2f9f3b84" ) ,
mustInputVariable ( "input" ) : cty . StringVal ( "hello, world!" ) ,
} ,
2024-08-22 03:20:47 -04:00
} ,
& stackstate . AppliedChangeResourceInstanceObject {
ResourceInstanceObjectAddr : mustAbsResourceInstanceObject ( "component.self.testing_resource.data" ) ,
NewStateSrc : & states . ResourceInstanceObjectSrc {
AttrsJSON : mustMarshalJSONAttrs ( map [ string ] interface { } {
"id" : "2f9f3b84" ,
"value" : "hello, world!" ,
} ) ,
Status : states . ObjectReady ,
Dependencies : make ( [ ] addrs . ConfigResource , 0 ) ,
} ,
ProviderConfigAddr : mustDefaultRootProvider ( "testing" ) ,
Schema : stacks_testing_provider . TestingResourceSchema ,
} ,
2024-09-16 05:36:36 -04:00
& stackstate . AppliedChangeOutputValue {
Addr : stackaddrs . OutputValue { Name : "value" } ,
Value : cty . StringVal ( "hello, world!" ) ,
} ,
2024-09-16 05:45:19 -04:00
& stackstate . AppliedChangeInputVariable {
Addr : mustStackInputVariable ( "input" ) ,
Value : cty . StringVal ( "hello, world!" ) ,
} ,
2024-08-22 03:20:47 -04:00
}
2024-08-22 09:40:47 -04:00
if diff := cmp . Diff ( wantApplyChanges , applyChanges , changesCmpOpts ) ; diff != "" {
2024-08-22 03:20:47 -04:00
t . Errorf ( "wrong changes\n%s" , diff )
}
}
2024-08-28 03:23:41 -04:00
func TestApplyFailedDependencyWithResourceInState ( t * testing . T ) {
ctx := context . Background ( )
cfg := loadMainBundleConfigForTest ( t , "failed-dependency" )
fakePlanTimestamp , err := time . Parse ( time . RFC3339 , "2021-01-01T00:00:00Z" )
if err != nil {
t . Fatal ( err )
}
lock := depsfile . NewLocks ( )
lock . SetProvider (
addrs . NewDefaultProvider ( "testing" ) ,
providerreqs . MustParseVersion ( "0.0.0" ) ,
providerreqs . MustParseVersionConstraints ( "=0.0.0" ) ,
providerreqs . PreferredHashes ( [ ] providerreqs . Hash { } ) ,
)
store := stacks_testing_provider . NewResourceStoreBuilder ( ) .
AddResource ( "resource" , cty . ObjectVal ( map [ string ] cty . Value {
"id" : cty . StringVal ( "resource" ) ,
"value" : cty . NullVal ( cty . String ) ,
} ) ) .
Build ( )
planReq := PlanRequest {
PlanMode : plans . NormalMode ,
Config : cfg ,
ProviderFactories : map [ addrs . Provider ] providers . Factory {
addrs . NewDefaultProvider ( "testing" ) : func ( ) ( providers . Interface , error ) {
2024-09-07 08:22:16 -04:00
return stacks_testing_provider . NewProviderWithData ( t , store ) , nil
2024-08-28 03:23:41 -04:00
} ,
} ,
DependencyLocks : * lock ,
ForcePlanTimestamp : & fakePlanTimestamp ,
InputValues : map [ stackaddrs . InputVariable ] ExternalInputValue {
stackaddrs . InputVariable { Name : "fail_apply" } : {
Value : cty . True ,
} ,
} ,
// We have a resource in the state from a previous run. We shouldn't
// emit any state changes to this resource as a result of the dependency
// failing.
PrevState : stackstate . NewStateBuilder ( ) .
AddResourceInstance ( stackstate . NewResourceInstanceBuilder ( ) .
SetAddr ( mustAbsResourceInstanceObject ( "component.self.testing_resource.data" ) ) .
SetProviderAddr ( mustDefaultRootProvider ( "testing" ) ) .
SetResourceInstanceObjectSrc ( states . ResourceInstanceObjectSrc {
SchemaVersion : 0 ,
AttrsJSON : mustMarshalJSONAttrs ( map [ string ] interface { } {
"id" : "resource" ,
"value" : nil ,
} ) ,
Status : states . ObjectReady ,
} ) ) .
Build ( ) ,
}
planChangesCh := make ( chan stackplan . PlannedChange )
planDiagsCh := make ( chan tfdiags . Diagnostic )
planResp := PlanResponse {
PlannedChanges : planChangesCh ,
Diagnostics : planDiagsCh ,
}
go Plan ( ctx , & planReq , & planResp )
planChanges , planDiags := collectPlanOutput ( planChangesCh , planDiagsCh )
if len ( planDiags ) > 0 {
t . Fatalf ( "unexpected diagnostics during planning: %s" , planDiags )
}
planLoader := stackplan . NewLoader ( )
for _ , change := range planChanges {
proto , err := change . PlannedChangeProto ( )
if err != nil {
t . Fatal ( err )
}
for _ , rawMsg := range proto . Raw {
err = planLoader . AddRaw ( rawMsg )
if err != nil {
t . Fatal ( err )
}
}
}
plan , err := planLoader . Plan ( )
if err != nil {
t . Fatal ( err )
}
applyReq := ApplyRequest {
Config : cfg ,
Plan : plan ,
ProviderFactories : map [ addrs . Provider ] providers . Factory {
addrs . NewDefaultProvider ( "testing" ) : func ( ) ( providers . Interface , error ) {
2024-09-07 08:22:16 -04:00
return stacks_testing_provider . NewProviderWithData ( t , store ) , nil
2024-08-28 03:23:41 -04:00
} ,
} ,
DependencyLocks : * lock ,
}
applyChangesCh := make ( chan stackstate . AppliedChange )
applyDiagsCh := make ( chan tfdiags . Diagnostic )
applyResp := ApplyResponse {
AppliedChanges : applyChangesCh ,
Diagnostics : applyDiagsCh ,
}
go Apply ( ctx , & applyReq , & applyResp )
applyChanges , applyDiags := collectApplyOutput ( applyChangesCh , applyDiagsCh )
2024-09-05 05:29:15 -04:00
expectDiagnosticsForTest ( t , applyDiags , expectDiagnostic ( tfdiags . Error , "failedResource error" , "failed during apply" ) )
2024-08-28 03:23:41 -04:00
wantChanges := [ ] stackstate . AppliedChange {
& stackstate . AppliedChangeComponentInstance {
ComponentAddr : mustAbsComponent ( "component.self" ) ,
ComponentInstanceAddr : mustAbsComponentInstance ( "component.self" ) ,
OutputValues : make ( map [ addrs . OutputValue ] cty . Value ) ,
2024-09-07 08:36:16 -04:00
InputVariables : map [ addrs . InputVariable ] cty . Value {
mustInputVariable ( "resource_id" ) : cty . StringVal ( "resource" ) ,
mustInputVariable ( "failed_id" ) : cty . StringVal ( "failed" ) ,
mustInputVariable ( "fail_apply" ) : cty . True ,
2024-09-18 04:41:36 -04:00
mustInputVariable ( "fail_plan" ) : cty . False ,
mustInputVariable ( "input" ) : cty . NullVal ( cty . String ) ,
2024-09-11 04:58:09 -04:00
} ,
2024-09-18 04:41:36 -04:00
} ,
& stackstate . AppliedChangeResourceInstanceObject {
// This has no state as the apply operation failed and it wasn't
// in the state before.
ResourceInstanceObjectAddr : mustAbsResourceInstanceObject ( "component.self.testing_failed_resource.data" ) ,
ProviderConfigAddr : mustDefaultRootProvider ( "testing" ) ,
} ,
& stackstate . AppliedChangeResourceInstanceObject {
// This emits the state from the previous run, as it was not
// changed during this run.
ResourceInstanceObjectAddr : mustAbsResourceInstanceObject ( "component.self.testing_resource.data" ) ,
ProviderConfigAddr : mustDefaultRootProvider ( "testing" ) ,
NewStateSrc : & states . ResourceInstanceObjectSrc {
AttrsJSON : mustMarshalJSONAttrs ( map [ string ] interface { } {
"id" : "resource" ,
"value" : nil ,
} ) ,
AttrSensitivePaths : make ( [ ] cty . Path , 0 ) ,
Status : states . ObjectReady ,
Dependencies : [ ] addrs . ConfigResource { mustAbsResourceInstance ( "testing_failed_resource.data" ) . ConfigResource ( ) } ,
2024-09-07 08:36:16 -04:00
} ,
2024-09-18 04:41:36 -04:00
Schema : stacks_testing_provider . TestingResourceSchema ,
} ,
& stackstate . AppliedChangeInputVariable {
Addr : mustStackInputVariable ( "fail_apply" ) ,
Value : cty . True ,
} ,
& stackstate . AppliedChangeInputVariable {
Addr : mustStackInputVariable ( "fail_plan" ) ,
Value : cty . False ,
2024-09-07 08:36:16 -04:00
} ,
}
2024-09-18 04:41:36 -04:00
sort . SliceStable ( applyChanges , func ( i , j int ) bool {
return appliedChangeSortKey ( applyChanges [ i ] ) < appliedChangeSortKey ( applyChanges [ j ] )
} )
2024-09-07 08:36:16 -04:00
2024-09-18 04:41:36 -04:00
if diff := cmp . Diff ( wantChanges , applyChanges , changesCmpOpts ) ; diff != "" {
t . Errorf ( "wrong changes\n%s" , diff )
}
2024-09-07 08:36:16 -04:00
2024-09-18 04:41:36 -04:00
}
2024-09-07 08:36:16 -04:00
2024-09-18 04:41:36 -04:00
func TestApplyManuallyRemovedResource ( t * testing . T ) {
2024-09-07 08:36:16 -04:00
2024-09-18 04:41:36 -04:00
ctx := context . Background ( )
cfg := loadMainBundleConfigForTest ( t , filepath . Join ( "with-single-input" , "valid" ) )
2024-09-07 08:36:16 -04:00
2024-09-18 04:41:36 -04:00
fakePlanTimestamp , err := time . Parse ( time . RFC3339 , "2021-01-01T00:00:00Z" )
if err != nil {
t . Fatal ( err )
}
2024-09-07 08:36:16 -04:00
2024-09-18 04:41:36 -04:00
lock := depsfile . NewLocks ( )
lock . SetProvider (
addrs . NewDefaultProvider ( "testing" ) ,
providerreqs . MustParseVersion ( "0.0.0" ) ,
providerreqs . MustParseVersionConstraints ( "=0.0.0" ) ,
providerreqs . PreferredHashes ( [ ] providerreqs . Hash { } ) ,
)
2024-09-07 08:36:16 -04:00
2024-09-18 04:41:36 -04:00
planReq := PlanRequest {
PlanMode : plans . NormalMode ,
2024-09-07 08:36:16 -04:00
2024-09-18 04:41:36 -04:00
Config : cfg ,
ProviderFactories : map [ addrs . Provider ] providers . Factory {
addrs . NewDefaultProvider ( "testing" ) : func ( ) ( providers . Interface , error ) {
return stacks_testing_provider . NewProvider ( t ) , nil
} ,
} ,
DependencyLocks : * lock ,
ForcePlanTimestamp : & fakePlanTimestamp ,
InputValues : map [ stackaddrs . InputVariable ] ExternalInputValue {
stackaddrs . InputVariable { Name : "id" } : {
Value : cty . StringVal ( "foo" ) ,
} ,
stackaddrs . InputVariable { Name : "input" } : {
Value : cty . StringVal ( "hello" ) ,
} ,
} ,
// We have in the previous state a resource that is not in our
// underlying data store. This simulates the case where someone went
// in and manually deleted a resource that Terraform is managing.
//
// Some providers will return an error in this case, but some will
// not. We need to ensure that we handle the second case gracefully.
PrevState : stackstate . NewStateBuilder ( ) .
AddResourceInstance ( stackstate . NewResourceInstanceBuilder ( ) .
SetAddr ( mustAbsResourceInstanceObject ( "component.self.testing_resource.missing" ) ) .
SetProviderAddr ( mustDefaultRootProvider ( "testing" ) ) .
SetResourceInstanceObjectSrc ( states . ResourceInstanceObjectSrc {
SchemaVersion : 0 ,
AttrsJSON : mustMarshalJSONAttrs ( map [ string ] interface { } {
"id" : "e84b59f2" ,
"value" : "hello" ,
} ) ,
Status : states . ObjectReady ,
} ) ) .
Build ( ) ,
}
planChangesCh := make ( chan stackplan . PlannedChange )
planDiagsCh := make ( chan tfdiags . Diagnostic )
planResp := PlanResponse {
PlannedChanges : planChangesCh ,
Diagnostics : planDiagsCh ,
}
go Plan ( ctx , & planReq , & planResp )
planChanges , planDiags := collectPlanOutput ( planChangesCh , planDiagsCh )
if len ( planDiags ) > 0 {
t . Fatalf ( "unexpected diagnostics during planning: %s" , planDiags )
}
planLoader := stackplan . NewLoader ( )
for _ , change := range planChanges {
proto , err := change . PlannedChangeProto ( )
if err != nil {
t . Fatal ( err )
}
for _ , rawMsg := range proto . Raw {
err = planLoader . AddRaw ( rawMsg )
2024-09-07 08:36:16 -04:00
if err != nil {
t . Fatal ( err )
}
2024-09-18 04:41:36 -04:00
}
}
plan , err := planLoader . Plan ( )
if err != nil {
t . Fatal ( err )
}
2024-09-07 08:36:16 -04:00
2024-09-18 04:41:36 -04:00
applyReq := ApplyRequest {
Config : cfg ,
Plan : plan ,
ProviderFactories : map [ addrs . Provider ] providers . Factory {
addrs . NewDefaultProvider ( "testing" ) : func ( ) ( providers . Interface , error ) {
return stacks_testing_provider . NewProvider ( t ) , nil
} ,
} ,
DependencyLocks : * lock ,
}
2024-09-07 08:36:16 -04:00
2024-09-18 04:41:36 -04:00
applyChangesCh := make ( chan stackstate . AppliedChange )
applyDiagsCh := make ( chan tfdiags . Diagnostic )
applyResp := ApplyResponse {
AppliedChanges : applyChangesCh ,
Diagnostics : applyDiagsCh ,
}
2024-09-07 08:36:16 -04:00
2024-09-18 04:41:36 -04:00
go Apply ( ctx , & applyReq , & applyResp )
applyChanges , applyDiags := collectApplyOutput ( applyChangesCh , applyDiagsCh )
if len ( applyDiags ) > 0 {
t . Fatalf ( "unexpected diagnostics during apply: %s" , applyDiags )
}
2024-09-07 08:36:16 -04:00
2024-09-18 04:41:36 -04:00
wantChanges := [ ] stackstate . AppliedChange {
& stackstate . AppliedChangeComponentInstance {
ComponentAddr : mustAbsComponent ( "component.self" ) ,
ComponentInstanceAddr : mustAbsComponentInstance ( "component.self" ) ,
OutputValues : make ( map [ addrs . OutputValue ] cty . Value ) ,
InputVariables : map [ addrs . InputVariable ] cty . Value {
mustInputVariable ( "id" ) : cty . StringVal ( "foo" ) ,
mustInputVariable ( "input" ) : cty . StringVal ( "hello" ) ,
} ,
} ,
// The resource in our configuration has been updated, so that is
// present as normal.
& stackstate . AppliedChangeResourceInstanceObject {
ResourceInstanceObjectAddr : mustAbsResourceInstanceObject ( "component.self.testing_resource.data" ) ,
ProviderConfigAddr : mustDefaultRootProvider ( "testing" ) ,
NewStateSrc : & states . ResourceInstanceObjectSrc {
AttrsJSON : mustMarshalJSONAttrs ( map [ string ] interface { } {
"id" : "foo" ,
"value" : "hello" ,
} ) ,
Status : states . ObjectReady ,
Dependencies : make ( [ ] addrs . ConfigResource , 0 ) ,
} ,
Schema : stacks_testing_provider . TestingResourceSchema ,
} ,
// The resource that was in state but not in the configuration should
// be removed from state.
& stackstate . AppliedChangeResourceInstanceObject {
ResourceInstanceObjectAddr : mustAbsResourceInstanceObject ( "component.self.testing_resource.missing" ) ,
ProviderConfigAddr : mustDefaultRootProvider ( "testing" ) ,
NewStateSrc : nil , // We should be removing this from the state file.
2025-03-11 15:58:44 -04:00
Schema : providers . Schema { } ,
2024-09-18 04:41:36 -04:00
} ,
& stackstate . AppliedChangeInputVariable {
Addr : mustStackInputVariable ( "id" ) ,
Value : cty . StringVal ( "foo" ) ,
} ,
& stackstate . AppliedChangeInputVariable {
Addr : mustStackInputVariable ( "input" ) ,
Value : cty . StringVal ( "hello" ) ,
} ,
2024-09-07 08:36:16 -04:00
}
2024-09-18 04:41:36 -04:00
sort . SliceStable ( applyChanges , func ( i , j int ) bool {
return appliedChangeSortKey ( applyChanges [ i ] ) < appliedChangeSortKey ( applyChanges [ j ] )
} )
if diff := cmp . Diff ( wantChanges , applyChanges , changesCmpOpts ) ; diff != "" {
t . Errorf ( "wrong changes\n%s" , diff )
}
2024-09-07 08:36:16 -04:00
}
2024-02-15 04:45:47 -05:00
func collectApplyOutput ( changesCh <- chan stackstate . AppliedChange , diagsCh <- chan tfdiags . Diagnostic ) ( [ ] stackstate . AppliedChange , tfdiags . Diagnostics ) {
var changes [ ] stackstate . AppliedChange
var diags tfdiags . Diagnostics
for {
select {
case change , ok := <- changesCh :
if ! ok {
// The plan operation is complete but we might still have
// some buffered diagnostics to consume.
if diagsCh != nil {
for diag := range diagsCh {
diags = append ( diags , diag )
}
}
return changes , diags
}
changes = append ( changes , change )
case diag , ok := <- diagsCh :
if ! ok {
// no more diagnostics to read
diagsCh = nil
continue
}
diags = append ( diags , diag )
}
}
}