github.com/graywolf-at-work-2/terraform-vendor@v1.4.5/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/checks"
    12  	"github.com/hashicorp/terraform/internal/lang/globalref"
    13  	"github.com/hashicorp/terraform/internal/lang/marks"
    14  	"github.com/hashicorp/terraform/internal/plans"
    15  	"github.com/hashicorp/terraform/internal/states"
    16  )
    17  
    18  func TestTFPlanRoundTrip(t *testing.T) {
    19  	objTy := cty.Object(map[string]cty.Type{
    20  		"id": cty.String,
    21  	})
    22  
    23  	plan := &plans.Plan{
    24  		VariableValues: map[string]plans.DynamicValue{
    25  			"foo": mustNewDynamicValueStr("foo value"),
    26  		},
    27  		Changes: &plans.Changes{
    28  			Outputs: []*plans.OutputChangeSrc{
    29  				{
    30  					Addr: addrs.OutputValue{Name: "bar"}.Absolute(addrs.RootModuleInstance),
    31  					ChangeSrc: plans.ChangeSrc{
    32  						Action: plans.Create,
    33  						After:  mustDynamicOutputValue("bar value"),
    34  					},
    35  					Sensitive: false,
    36  				},
    37  				{
    38  					Addr: addrs.OutputValue{Name: "baz"}.Absolute(addrs.RootModuleInstance),
    39  					ChangeSrc: plans.ChangeSrc{
    40  						Action: plans.NoOp,
    41  						Before: mustDynamicOutputValue("baz value"),
    42  						After:  mustDynamicOutputValue("baz value"),
    43  					},
    44  					Sensitive: false,
    45  				},
    46  				{
    47  					Addr: addrs.OutputValue{Name: "secret"}.Absolute(addrs.RootModuleInstance),
    48  					ChangeSrc: plans.ChangeSrc{
    49  						Action: plans.Update,
    50  						Before: mustDynamicOutputValue("old secret value"),
    51  						After:  mustDynamicOutputValue("new secret value"),
    52  					},
    53  					Sensitive: true,
    54  				},
    55  			},
    56  			Resources: []*plans.ResourceInstanceChangeSrc{
    57  				{
    58  					Addr: addrs.Resource{
    59  						Mode: addrs.ManagedResourceMode,
    60  						Type: "test_thing",
    61  						Name: "woot",
    62  					}.Instance(addrs.IntKey(0)).Absolute(addrs.RootModuleInstance),
    63  					PrevRunAddr: addrs.Resource{
    64  						Mode: addrs.ManagedResourceMode,
    65  						Type: "test_thing",
    66  						Name: "woot",
    67  					}.Instance(addrs.NoKey).Absolute(addrs.RootModuleInstance),
    68  					ProviderAddr: addrs.AbsProviderConfig{
    69  						Provider: addrs.NewDefaultProvider("test"),
    70  						Module:   addrs.RootModule,
    71  					},
    72  					ChangeSrc: plans.ChangeSrc{
    73  						Action: plans.DeleteThenCreate,
    74  						Before: mustNewDynamicValue(cty.ObjectVal(map[string]cty.Value{
    75  							"id": cty.StringVal("foo-bar-baz"),
    76  							"boop": cty.ListVal([]cty.Value{
    77  								cty.StringVal("beep"),
    78  							}),
    79  						}), objTy),
    80  						After: mustNewDynamicValue(cty.ObjectVal(map[string]cty.Value{
    81  							"id": cty.UnknownVal(cty.String),
    82  							"boop": cty.ListVal([]cty.Value{
    83  								cty.StringVal("beep"),
    84  								cty.StringVal("honk"),
    85  							}),
    86  						}), objTy),
    87  						AfterValMarks: []cty.PathValueMarks{
    88  							{
    89  								Path:  cty.GetAttrPath("boop").IndexInt(1),
    90  								Marks: cty.NewValueMarks(marks.Sensitive),
    91  							},
    92  						},
    93  					},
    94  					RequiredReplace: cty.NewPathSet(
    95  						cty.GetAttrPath("boop"),
    96  					),
    97  					ActionReason: plans.ResourceInstanceReplaceBecauseCannotUpdate,
    98  				},
    99  				{
   100  					Addr: addrs.Resource{
   101  						Mode: addrs.ManagedResourceMode,
   102  						Type: "test_thing",
   103  						Name: "woot",
   104  					}.Instance(addrs.IntKey(1)).Absolute(addrs.RootModuleInstance),
   105  					PrevRunAddr: addrs.Resource{
   106  						Mode: addrs.ManagedResourceMode,
   107  						Type: "test_thing",
   108  						Name: "woot",
   109  					}.Instance(addrs.IntKey(1)).Absolute(addrs.RootModuleInstance),
   110  					DeposedKey: "foodface",
   111  					ProviderAddr: addrs.AbsProviderConfig{
   112  						Provider: addrs.NewDefaultProvider("test"),
   113  						Module:   addrs.RootModule,
   114  					},
   115  					ChangeSrc: plans.ChangeSrc{
   116  						Action: plans.Delete,
   117  						Before: mustNewDynamicValue(cty.ObjectVal(map[string]cty.Value{
   118  							"id": cty.StringVal("bar-baz-foo"),
   119  						}), objTy),
   120  					},
   121  				},
   122  			},
   123  		},
   124  		DriftedResources: []*plans.ResourceInstanceChangeSrc{
   125  			{
   126  				Addr: addrs.Resource{
   127  					Mode: addrs.ManagedResourceMode,
   128  					Type: "test_thing",
   129  					Name: "woot",
   130  				}.Instance(addrs.IntKey(0)).Absolute(addrs.RootModuleInstance),
   131  				PrevRunAddr: addrs.Resource{
   132  					Mode: addrs.ManagedResourceMode,
   133  					Type: "test_thing",
   134  					Name: "woot",
   135  				}.Instance(addrs.NoKey).Absolute(addrs.RootModuleInstance),
   136  				ProviderAddr: addrs.AbsProviderConfig{
   137  					Provider: addrs.NewDefaultProvider("test"),
   138  					Module:   addrs.RootModule,
   139  				},
   140  				ChangeSrc: plans.ChangeSrc{
   141  					Action: plans.DeleteThenCreate,
   142  					Before: mustNewDynamicValue(cty.ObjectVal(map[string]cty.Value{
   143  						"id": cty.StringVal("foo-bar-baz"),
   144  						"boop": cty.ListVal([]cty.Value{
   145  							cty.StringVal("beep"),
   146  						}),
   147  					}), objTy),
   148  					After: mustNewDynamicValue(cty.ObjectVal(map[string]cty.Value{
   149  						"id": cty.UnknownVal(cty.String),
   150  						"boop": cty.ListVal([]cty.Value{
   151  							cty.StringVal("beep"),
   152  							cty.StringVal("bonk"),
   153  						}),
   154  					}), objTy),
   155  					AfterValMarks: []cty.PathValueMarks{
   156  						{
   157  							Path:  cty.GetAttrPath("boop").IndexInt(1),
   158  							Marks: cty.NewValueMarks(marks.Sensitive),
   159  						},
   160  					},
   161  				},
   162  			},
   163  		},
   164  		RelevantAttributes: []globalref.ResourceAttr{
   165  			{
   166  				Resource: addrs.Resource{
   167  					Mode: addrs.ManagedResourceMode,
   168  					Type: "test_thing",
   169  					Name: "woot",
   170  				}.Instance(addrs.IntKey(0)).Absolute(addrs.RootModuleInstance),
   171  				Attr: cty.GetAttrPath("boop").Index(cty.NumberIntVal(1)),
   172  			},
   173  		},
   174  		Checks: &states.CheckResults{
   175  			ConfigResults: addrs.MakeMap(
   176  				addrs.MakeMapElem[addrs.ConfigCheckable](
   177  					addrs.Resource{
   178  						Mode: addrs.ManagedResourceMode,
   179  						Type: "test_thing",
   180  						Name: "woot",
   181  					}.InModule(addrs.RootModule),
   182  					&states.CheckResultAggregate{
   183  						Status: checks.StatusFail,
   184  						ObjectResults: addrs.MakeMap(
   185  							addrs.MakeMapElem[addrs.Checkable](
   186  								addrs.Resource{
   187  									Mode: addrs.ManagedResourceMode,
   188  									Type: "test_thing",
   189  									Name: "woot",
   190  								}.Instance(addrs.IntKey(0)).Absolute(addrs.RootModuleInstance),
   191  								&states.CheckResultObject{
   192  									Status:          checks.StatusFail,
   193  									FailureMessages: []string{"Oh no!"},
   194  								},
   195  							),
   196  						),
   197  					},
   198  				),
   199  			),
   200  		},
   201  		TargetAddrs: []addrs.Targetable{
   202  			addrs.Resource{
   203  				Mode: addrs.ManagedResourceMode,
   204  				Type: "test_thing",
   205  				Name: "woot",
   206  			}.Absolute(addrs.RootModuleInstance),
   207  		},
   208  		Backend: plans.Backend{
   209  			Type: "local",
   210  			Config: mustNewDynamicValue(
   211  				cty.ObjectVal(map[string]cty.Value{
   212  					"foo": cty.StringVal("bar"),
   213  				}),
   214  				cty.Object(map[string]cty.Type{
   215  					"foo": cty.String,
   216  				}),
   217  			),
   218  			Workspace: "default",
   219  		},
   220  	}
   221  
   222  	var buf bytes.Buffer
   223  	err := writeTfplan(plan, &buf)
   224  	if err != nil {
   225  		t.Fatal(err)
   226  	}
   227  
   228  	newPlan, err := readTfplan(&buf)
   229  	if err != nil {
   230  		t.Fatal(err)
   231  	}
   232  
   233  	{
   234  		oldDepth := deep.MaxDepth
   235  		oldCompare := deep.CompareUnexportedFields
   236  		deep.MaxDepth = 20
   237  		deep.CompareUnexportedFields = true
   238  		defer func() {
   239  			deep.MaxDepth = oldDepth
   240  			deep.CompareUnexportedFields = oldCompare
   241  		}()
   242  	}
   243  	for _, problem := range deep.Equal(newPlan, plan) {
   244  		t.Error(problem)
   245  	}
   246  }
   247  
   248  func mustDynamicOutputValue(val string) plans.DynamicValue {
   249  	ret, err := plans.NewDynamicValue(cty.StringVal(val), cty.DynamicPseudoType)
   250  	if err != nil {
   251  		panic(err)
   252  	}
   253  	return ret
   254  }
   255  
   256  func mustNewDynamicValue(val cty.Value, ty cty.Type) plans.DynamicValue {
   257  	ret, err := plans.NewDynamicValue(val, ty)
   258  	if err != nil {
   259  		panic(err)
   260  	}
   261  	return ret
   262  }
   263  
   264  func mustNewDynamicValueStr(val string) plans.DynamicValue {
   265  	realVal := cty.StringVal(val)
   266  	ret, err := plans.NewDynamicValue(realVal, cty.String)
   267  	if err != nil {
   268  		panic(err)
   269  	}
   270  	return ret
   271  }
   272  
   273  // TestTFPlanRoundTripDestroy ensures that encoding and decoding null values for
   274  // destroy doesn't leave us with any nil values.
   275  func TestTFPlanRoundTripDestroy(t *testing.T) {
   276  	objTy := cty.Object(map[string]cty.Type{
   277  		"id": cty.String,
   278  	})
   279  
   280  	plan := &plans.Plan{
   281  		Changes: &plans.Changes{
   282  			Outputs: []*plans.OutputChangeSrc{
   283  				{
   284  					Addr: addrs.OutputValue{Name: "bar"}.Absolute(addrs.RootModuleInstance),
   285  					ChangeSrc: plans.ChangeSrc{
   286  						Action: plans.Delete,
   287  						Before: mustDynamicOutputValue("output"),
   288  						After:  mustNewDynamicValue(cty.NullVal(cty.String), cty.String),
   289  					},
   290  				},
   291  			},
   292  			Resources: []*plans.ResourceInstanceChangeSrc{
   293  				{
   294  					Addr: addrs.Resource{
   295  						Mode: addrs.ManagedResourceMode,
   296  						Type: "test_thing",
   297  						Name: "woot",
   298  					}.Instance(addrs.IntKey(0)).Absolute(addrs.RootModuleInstance),
   299  					PrevRunAddr: addrs.Resource{
   300  						Mode: addrs.ManagedResourceMode,
   301  						Type: "test_thing",
   302  						Name: "woot",
   303  					}.Instance(addrs.IntKey(0)).Absolute(addrs.RootModuleInstance),
   304  					ProviderAddr: addrs.AbsProviderConfig{
   305  						Provider: addrs.NewDefaultProvider("test"),
   306  						Module:   addrs.RootModule,
   307  					},
   308  					ChangeSrc: plans.ChangeSrc{
   309  						Action: plans.Delete,
   310  						Before: mustNewDynamicValue(cty.ObjectVal(map[string]cty.Value{
   311  							"id": cty.StringVal("foo-bar-baz"),
   312  						}), objTy),
   313  						After: mustNewDynamicValue(cty.NullVal(objTy), objTy),
   314  					},
   315  				},
   316  			},
   317  		},
   318  		DriftedResources: []*plans.ResourceInstanceChangeSrc{},
   319  		TargetAddrs: []addrs.Targetable{
   320  			addrs.Resource{
   321  				Mode: addrs.ManagedResourceMode,
   322  				Type: "test_thing",
   323  				Name: "woot",
   324  			}.Absolute(addrs.RootModuleInstance),
   325  		},
   326  		Backend: plans.Backend{
   327  			Type: "local",
   328  			Config: mustNewDynamicValue(
   329  				cty.ObjectVal(map[string]cty.Value{
   330  					"foo": cty.StringVal("bar"),
   331  				}),
   332  				cty.Object(map[string]cty.Type{
   333  					"foo": cty.String,
   334  				}),
   335  			),
   336  			Workspace: "default",
   337  		},
   338  	}
   339  
   340  	var buf bytes.Buffer
   341  	err := writeTfplan(plan, &buf)
   342  	if err != nil {
   343  		t.Fatal(err)
   344  	}
   345  
   346  	newPlan, err := readTfplan(&buf)
   347  	if err != nil {
   348  		t.Fatal(err)
   349  	}
   350  
   351  	for _, rics := range newPlan.Changes.Resources {
   352  		ric, err := rics.Decode(objTy)
   353  		if err != nil {
   354  			t.Fatal(err)
   355  		}
   356  
   357  		if ric.After == cty.NilVal {
   358  			t.Fatalf("unexpected nil After value: %#v\n", ric)
   359  		}
   360  	}
   361  	for _, ocs := range newPlan.Changes.Outputs {
   362  		oc, err := ocs.Decode()
   363  		if err != nil {
   364  			t.Fatal(err)
   365  		}
   366  
   367  		if oc.After == cty.NilVal {
   368  			t.Fatalf("unexpected nil After value: %#v\n", ocs)
   369  		}
   370  	}
   371  }