github.com/jaredpalmer/terraform@v1.1.0-alpha20210908.0.20210911170307-88705c943a03/internal/plans/planfile/tfplan_test.go (about)

     1  package planfile
     2  
     3  import (
     4  	"bytes"
     5  	"testing"
     6  
     7  	"github.com/go-test/deep"
     8  	"github.com/zclconf/go-cty/cty"
     9  
    10  	"github.com/hashicorp/terraform/internal/addrs"
    11  	"github.com/hashicorp/terraform/internal/lang/marks"
    12  	"github.com/hashicorp/terraform/internal/plans"
    13  )
    14  
    15  func TestTFPlanRoundTrip(t *testing.T) {
    16  	objTy := cty.Object(map[string]cty.Type{
    17  		"id": cty.String,
    18  	})
    19  
    20  	plan := &plans.Plan{
    21  		VariableValues: map[string]plans.DynamicValue{
    22  			"foo": mustNewDynamicValueStr("foo value"),
    23  		},
    24  		Changes: &plans.Changes{
    25  			Outputs: []*plans.OutputChangeSrc{
    26  				{
    27  					Addr: addrs.OutputValue{Name: "bar"}.Absolute(addrs.RootModuleInstance),
    28  					ChangeSrc: plans.ChangeSrc{
    29  						Action: plans.Create,
    30  						After:  mustDynamicOutputValue("bar value"),
    31  					},
    32  					Sensitive: false,
    33  				},
    34  				{
    35  					Addr: addrs.OutputValue{Name: "baz"}.Absolute(addrs.RootModuleInstance),
    36  					ChangeSrc: plans.ChangeSrc{
    37  						Action: plans.NoOp,
    38  						Before: mustDynamicOutputValue("baz value"),
    39  						After:  mustDynamicOutputValue("baz value"),
    40  					},
    41  					Sensitive: false,
    42  				},
    43  				{
    44  					Addr: addrs.OutputValue{Name: "secret"}.Absolute(addrs.RootModuleInstance),
    45  					ChangeSrc: plans.ChangeSrc{
    46  						Action: plans.Update,
    47  						Before: mustDynamicOutputValue("old secret value"),
    48  						After:  mustDynamicOutputValue("new secret value"),
    49  					},
    50  					Sensitive: true,
    51  				},
    52  			},
    53  			Resources: []*plans.ResourceInstanceChangeSrc{
    54  				{
    55  					Addr: addrs.Resource{
    56  						Mode: addrs.ManagedResourceMode,
    57  						Type: "test_thing",
    58  						Name: "woot",
    59  					}.Instance(addrs.IntKey(0)).Absolute(addrs.RootModuleInstance),
    60  					PrevRunAddr: addrs.Resource{
    61  						Mode: addrs.ManagedResourceMode,
    62  						Type: "test_thing",
    63  						Name: "woot",
    64  					}.Instance(addrs.NoKey).Absolute(addrs.RootModuleInstance),
    65  					ProviderAddr: addrs.AbsProviderConfig{
    66  						Provider: addrs.NewDefaultProvider("test"),
    67  						Module:   addrs.RootModule,
    68  					},
    69  					ChangeSrc: plans.ChangeSrc{
    70  						Action: plans.DeleteThenCreate,
    71  						Before: mustNewDynamicValue(cty.ObjectVal(map[string]cty.Value{
    72  							"id": cty.StringVal("foo-bar-baz"),
    73  							"boop": cty.ListVal([]cty.Value{
    74  								cty.StringVal("beep"),
    75  							}),
    76  						}), objTy),
    77  						After: mustNewDynamicValue(cty.ObjectVal(map[string]cty.Value{
    78  							"id": cty.UnknownVal(cty.String),
    79  							"boop": cty.ListVal([]cty.Value{
    80  								cty.StringVal("beep"),
    81  								cty.StringVal("honk"),
    82  							}),
    83  						}), objTy),
    84  						AfterValMarks: []cty.PathValueMarks{
    85  							{
    86  								Path:  cty.GetAttrPath("boop").IndexInt(1),
    87  								Marks: cty.NewValueMarks(marks.Sensitive),
    88  							},
    89  						},
    90  					},
    91  					RequiredReplace: cty.NewPathSet(
    92  						cty.GetAttrPath("boop"),
    93  					),
    94  					ActionReason: plans.ResourceInstanceReplaceBecauseCannotUpdate,
    95  				},
    96  				{
    97  					Addr: addrs.Resource{
    98  						Mode: addrs.ManagedResourceMode,
    99  						Type: "test_thing",
   100  						Name: "woot",
   101  					}.Instance(addrs.IntKey(1)).Absolute(addrs.RootModuleInstance),
   102  					PrevRunAddr: addrs.Resource{
   103  						Mode: addrs.ManagedResourceMode,
   104  						Type: "test_thing",
   105  						Name: "woot",
   106  					}.Instance(addrs.IntKey(1)).Absolute(addrs.RootModuleInstance),
   107  					DeposedKey: "foodface",
   108  					ProviderAddr: addrs.AbsProviderConfig{
   109  						Provider: addrs.NewDefaultProvider("test"),
   110  						Module:   addrs.RootModule,
   111  					},
   112  					ChangeSrc: plans.ChangeSrc{
   113  						Action: plans.Delete,
   114  						Before: mustNewDynamicValue(cty.ObjectVal(map[string]cty.Value{
   115  							"id": cty.StringVal("bar-baz-foo"),
   116  						}), objTy),
   117  					},
   118  				},
   119  			},
   120  		},
   121  		TargetAddrs: []addrs.Targetable{
   122  			addrs.Resource{
   123  				Mode: addrs.ManagedResourceMode,
   124  				Type: "test_thing",
   125  				Name: "woot",
   126  			}.Absolute(addrs.RootModuleInstance),
   127  		},
   128  		ProviderSHA256s: map[string][]byte{
   129  			"test": []byte{
   130  				0xba, 0x5e, 0x1e, 0x55, 0xb0, 0x1d, 0xfa, 0xce,
   131  				0xef, 0xfe, 0xc7, 0xed, 0x1a, 0xbe, 0x11, 0xed,
   132  				0x5c, 0xa1, 0xab, 0x1e, 0xda, 0x7a, 0xba, 0x5e,
   133  				0x70, 0x7a, 0x11, 0xed, 0xb0, 0x07, 0xab, 0x1e,
   134  			},
   135  		},
   136  		Backend: plans.Backend{
   137  			Type: "local",
   138  			Config: mustNewDynamicValue(
   139  				cty.ObjectVal(map[string]cty.Value{
   140  					"foo": cty.StringVal("bar"),
   141  				}),
   142  				cty.Object(map[string]cty.Type{
   143  					"foo": cty.String,
   144  				}),
   145  			),
   146  			Workspace: "default",
   147  		},
   148  	}
   149  
   150  	var buf bytes.Buffer
   151  	err := writeTfplan(plan, &buf)
   152  	if err != nil {
   153  		t.Fatal(err)
   154  	}
   155  
   156  	newPlan, err := readTfplan(&buf)
   157  	if err != nil {
   158  		t.Fatal(err)
   159  	}
   160  
   161  	{
   162  		oldDepth := deep.MaxDepth
   163  		oldCompare := deep.CompareUnexportedFields
   164  		deep.MaxDepth = 20
   165  		deep.CompareUnexportedFields = true
   166  		defer func() {
   167  			deep.MaxDepth = oldDepth
   168  			deep.CompareUnexportedFields = oldCompare
   169  		}()
   170  	}
   171  	for _, problem := range deep.Equal(newPlan, plan) {
   172  		t.Error(problem)
   173  	}
   174  }
   175  
   176  func mustDynamicOutputValue(val string) plans.DynamicValue {
   177  	ret, err := plans.NewDynamicValue(cty.StringVal(val), cty.DynamicPseudoType)
   178  	if err != nil {
   179  		panic(err)
   180  	}
   181  	return ret
   182  }
   183  
   184  func mustNewDynamicValue(val cty.Value, ty cty.Type) plans.DynamicValue {
   185  	ret, err := plans.NewDynamicValue(val, ty)
   186  	if err != nil {
   187  		panic(err)
   188  	}
   189  	return ret
   190  }
   191  
   192  func mustNewDynamicValueStr(val string) plans.DynamicValue {
   193  	realVal := cty.StringVal(val)
   194  	ret, err := plans.NewDynamicValue(realVal, cty.String)
   195  	if err != nil {
   196  		panic(err)
   197  	}
   198  	return ret
   199  }
   200  
   201  // TestTFPlanRoundTripDestroy ensures that encoding and decoding null values for
   202  // destroy doesn't leave us with any nil values.
   203  func TestTFPlanRoundTripDestroy(t *testing.T) {
   204  	objTy := cty.Object(map[string]cty.Type{
   205  		"id": cty.String,
   206  	})
   207  
   208  	plan := &plans.Plan{
   209  		Changes: &plans.Changes{
   210  			Outputs: []*plans.OutputChangeSrc{
   211  				{
   212  					Addr: addrs.OutputValue{Name: "bar"}.Absolute(addrs.RootModuleInstance),
   213  					ChangeSrc: plans.ChangeSrc{
   214  						Action: plans.Delete,
   215  						Before: mustDynamicOutputValue("output"),
   216  						After:  mustNewDynamicValue(cty.NullVal(cty.String), cty.String),
   217  					},
   218  				},
   219  			},
   220  			Resources: []*plans.ResourceInstanceChangeSrc{
   221  				{
   222  					Addr: addrs.Resource{
   223  						Mode: addrs.ManagedResourceMode,
   224  						Type: "test_thing",
   225  						Name: "woot",
   226  					}.Instance(addrs.IntKey(0)).Absolute(addrs.RootModuleInstance),
   227  					PrevRunAddr: addrs.Resource{
   228  						Mode: addrs.ManagedResourceMode,
   229  						Type: "test_thing",
   230  						Name: "woot",
   231  					}.Instance(addrs.IntKey(0)).Absolute(addrs.RootModuleInstance),
   232  					ProviderAddr: addrs.AbsProviderConfig{
   233  						Provider: addrs.NewDefaultProvider("test"),
   234  						Module:   addrs.RootModule,
   235  					},
   236  					ChangeSrc: plans.ChangeSrc{
   237  						Action: plans.Delete,
   238  						Before: mustNewDynamicValue(cty.ObjectVal(map[string]cty.Value{
   239  							"id": cty.StringVal("foo-bar-baz"),
   240  						}), objTy),
   241  						After: mustNewDynamicValue(cty.NullVal(objTy), objTy),
   242  					},
   243  				},
   244  			},
   245  		},
   246  		TargetAddrs: []addrs.Targetable{
   247  			addrs.Resource{
   248  				Mode: addrs.ManagedResourceMode,
   249  				Type: "test_thing",
   250  				Name: "woot",
   251  			}.Absolute(addrs.RootModuleInstance),
   252  		},
   253  		Backend: plans.Backend{
   254  			Type: "local",
   255  			Config: mustNewDynamicValue(
   256  				cty.ObjectVal(map[string]cty.Value{
   257  					"foo": cty.StringVal("bar"),
   258  				}),
   259  				cty.Object(map[string]cty.Type{
   260  					"foo": cty.String,
   261  				}),
   262  			),
   263  			Workspace: "default",
   264  		},
   265  	}
   266  
   267  	var buf bytes.Buffer
   268  	err := writeTfplan(plan, &buf)
   269  	if err != nil {
   270  		t.Fatal(err)
   271  	}
   272  
   273  	newPlan, err := readTfplan(&buf)
   274  	if err != nil {
   275  		t.Fatal(err)
   276  	}
   277  
   278  	for _, rics := range newPlan.Changes.Resources {
   279  		ric, err := rics.Decode(objTy)
   280  		if err != nil {
   281  			t.Fatal(err)
   282  		}
   283  
   284  		if ric.After == cty.NilVal {
   285  			t.Fatalf("unexpected nil After value: %#v\n", ric)
   286  		}
   287  	}
   288  	for _, ocs := range newPlan.Changes.Outputs {
   289  		oc, err := ocs.Decode()
   290  		if err != nil {
   291  			t.Fatal(err)
   292  		}
   293  
   294  		if oc.After == cty.NilVal {
   295  			t.Fatalf("unexpected nil After value: %#v\n", ocs)
   296  		}
   297  	}
   298  }