github.com/muratcelep/terraform@v1.1.0-beta2-not-internal-4/not-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/muratcelep/terraform/not-internal/addrs"
    11  	"github.com/muratcelep/terraform/not-internal/lang/marks"
    12  	"github.com/muratcelep/terraform/not-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  		DriftedResources: []*plans.ResourceInstanceChangeSrc{
   122  			{
   123  				Addr: addrs.Resource{
   124  					Mode: addrs.ManagedResourceMode,
   125  					Type: "test_thing",
   126  					Name: "woot",
   127  				}.Instance(addrs.IntKey(0)).Absolute(addrs.RootModuleInstance),
   128  				PrevRunAddr: addrs.Resource{
   129  					Mode: addrs.ManagedResourceMode,
   130  					Type: "test_thing",
   131  					Name: "woot",
   132  				}.Instance(addrs.NoKey).Absolute(addrs.RootModuleInstance),
   133  				ProviderAddr: addrs.AbsProviderConfig{
   134  					Provider: addrs.NewDefaultProvider("test"),
   135  					Module:   addrs.RootModule,
   136  				},
   137  				ChangeSrc: plans.ChangeSrc{
   138  					Action: plans.DeleteThenCreate,
   139  					Before: mustNewDynamicValue(cty.ObjectVal(map[string]cty.Value{
   140  						"id": cty.StringVal("foo-bar-baz"),
   141  						"boop": cty.ListVal([]cty.Value{
   142  							cty.StringVal("beep"),
   143  						}),
   144  					}), objTy),
   145  					After: mustNewDynamicValue(cty.ObjectVal(map[string]cty.Value{
   146  						"id": cty.UnknownVal(cty.String),
   147  						"boop": cty.ListVal([]cty.Value{
   148  							cty.StringVal("beep"),
   149  							cty.StringVal("bonk"),
   150  						}),
   151  					}), objTy),
   152  					AfterValMarks: []cty.PathValueMarks{
   153  						{
   154  							Path:  cty.GetAttrPath("boop").IndexInt(1),
   155  							Marks: cty.NewValueMarks(marks.Sensitive),
   156  						},
   157  					},
   158  				},
   159  			},
   160  		},
   161  		TargetAddrs: []addrs.Targetable{
   162  			addrs.Resource{
   163  				Mode: addrs.ManagedResourceMode,
   164  				Type: "test_thing",
   165  				Name: "woot",
   166  			}.Absolute(addrs.RootModuleInstance),
   167  		},
   168  		Backend: plans.Backend{
   169  			Type: "local",
   170  			Config: mustNewDynamicValue(
   171  				cty.ObjectVal(map[string]cty.Value{
   172  					"foo": cty.StringVal("bar"),
   173  				}),
   174  				cty.Object(map[string]cty.Type{
   175  					"foo": cty.String,
   176  				}),
   177  			),
   178  			Workspace: "default",
   179  		},
   180  	}
   181  
   182  	var buf bytes.Buffer
   183  	err := writeTfplan(plan, &buf)
   184  	if err != nil {
   185  		t.Fatal(err)
   186  	}
   187  
   188  	newPlan, err := readTfplan(&buf)
   189  	if err != nil {
   190  		t.Fatal(err)
   191  	}
   192  
   193  	{
   194  		oldDepth := deep.MaxDepth
   195  		oldCompare := deep.CompareUnexportedFields
   196  		deep.MaxDepth = 20
   197  		deep.CompareUnexportedFields = true
   198  		defer func() {
   199  			deep.MaxDepth = oldDepth
   200  			deep.CompareUnexportedFields = oldCompare
   201  		}()
   202  	}
   203  	for _, problem := range deep.Equal(newPlan, plan) {
   204  		t.Error(problem)
   205  	}
   206  }
   207  
   208  func mustDynamicOutputValue(val string) plans.DynamicValue {
   209  	ret, err := plans.NewDynamicValue(cty.StringVal(val), cty.DynamicPseudoType)
   210  	if err != nil {
   211  		panic(err)
   212  	}
   213  	return ret
   214  }
   215  
   216  func mustNewDynamicValue(val cty.Value, ty cty.Type) plans.DynamicValue {
   217  	ret, err := plans.NewDynamicValue(val, ty)
   218  	if err != nil {
   219  		panic(err)
   220  	}
   221  	return ret
   222  }
   223  
   224  func mustNewDynamicValueStr(val string) plans.DynamicValue {
   225  	realVal := cty.StringVal(val)
   226  	ret, err := plans.NewDynamicValue(realVal, cty.String)
   227  	if err != nil {
   228  		panic(err)
   229  	}
   230  	return ret
   231  }
   232  
   233  // TestTFPlanRoundTripDestroy ensures that encoding and decoding null values for
   234  // destroy doesn't leave us with any nil values.
   235  func TestTFPlanRoundTripDestroy(t *testing.T) {
   236  	objTy := cty.Object(map[string]cty.Type{
   237  		"id": cty.String,
   238  	})
   239  
   240  	plan := &plans.Plan{
   241  		Changes: &plans.Changes{
   242  			Outputs: []*plans.OutputChangeSrc{
   243  				{
   244  					Addr: addrs.OutputValue{Name: "bar"}.Absolute(addrs.RootModuleInstance),
   245  					ChangeSrc: plans.ChangeSrc{
   246  						Action: plans.Delete,
   247  						Before: mustDynamicOutputValue("output"),
   248  						After:  mustNewDynamicValue(cty.NullVal(cty.String), cty.String),
   249  					},
   250  				},
   251  			},
   252  			Resources: []*plans.ResourceInstanceChangeSrc{
   253  				{
   254  					Addr: addrs.Resource{
   255  						Mode: addrs.ManagedResourceMode,
   256  						Type: "test_thing",
   257  						Name: "woot",
   258  					}.Instance(addrs.IntKey(0)).Absolute(addrs.RootModuleInstance),
   259  					PrevRunAddr: addrs.Resource{
   260  						Mode: addrs.ManagedResourceMode,
   261  						Type: "test_thing",
   262  						Name: "woot",
   263  					}.Instance(addrs.IntKey(0)).Absolute(addrs.RootModuleInstance),
   264  					ProviderAddr: addrs.AbsProviderConfig{
   265  						Provider: addrs.NewDefaultProvider("test"),
   266  						Module:   addrs.RootModule,
   267  					},
   268  					ChangeSrc: plans.ChangeSrc{
   269  						Action: plans.Delete,
   270  						Before: mustNewDynamicValue(cty.ObjectVal(map[string]cty.Value{
   271  							"id": cty.StringVal("foo-bar-baz"),
   272  						}), objTy),
   273  						After: mustNewDynamicValue(cty.NullVal(objTy), objTy),
   274  					},
   275  				},
   276  			},
   277  		},
   278  		DriftedResources: []*plans.ResourceInstanceChangeSrc{},
   279  		TargetAddrs: []addrs.Targetable{
   280  			addrs.Resource{
   281  				Mode: addrs.ManagedResourceMode,
   282  				Type: "test_thing",
   283  				Name: "woot",
   284  			}.Absolute(addrs.RootModuleInstance),
   285  		},
   286  		Backend: plans.Backend{
   287  			Type: "local",
   288  			Config: mustNewDynamicValue(
   289  				cty.ObjectVal(map[string]cty.Value{
   290  					"foo": cty.StringVal("bar"),
   291  				}),
   292  				cty.Object(map[string]cty.Type{
   293  					"foo": cty.String,
   294  				}),
   295  			),
   296  			Workspace: "default",
   297  		},
   298  	}
   299  
   300  	var buf bytes.Buffer
   301  	err := writeTfplan(plan, &buf)
   302  	if err != nil {
   303  		t.Fatal(err)
   304  	}
   305  
   306  	newPlan, err := readTfplan(&buf)
   307  	if err != nil {
   308  		t.Fatal(err)
   309  	}
   310  
   311  	for _, rics := range newPlan.Changes.Resources {
   312  		ric, err := rics.Decode(objTy)
   313  		if err != nil {
   314  			t.Fatal(err)
   315  		}
   316  
   317  		if ric.After == cty.NilVal {
   318  			t.Fatalf("unexpected nil After value: %#v\n", ric)
   319  		}
   320  	}
   321  	for _, ocs := range newPlan.Changes.Outputs {
   322  		oc, err := ocs.Decode()
   323  		if err != nil {
   324  			t.Fatal(err)
   325  		}
   326  
   327  		if oc.After == cty.NilVal {
   328  			t.Fatalf("unexpected nil After value: %#v\n", ocs)
   329  		}
   330  	}
   331  }