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