github.com/kanishk98/terraform@v1.3.0-dev.0.20220917174235-661ca8088a6a/internal/terraform/transform_destroy_cbd_test.go (about)

     1  package terraform
     2  
     3  import (
     4  	"regexp"
     5  	"strings"
     6  	"testing"
     7  
     8  	"github.com/hashicorp/terraform/internal/addrs"
     9  	"github.com/hashicorp/terraform/internal/plans"
    10  	"github.com/hashicorp/terraform/internal/states"
    11  )
    12  
    13  func cbdTestGraph(t *testing.T, mod string, changes *plans.Changes, state *states.State) *Graph {
    14  	module := testModule(t, mod)
    15  
    16  	applyBuilder := &ApplyGraphBuilder{
    17  		Config:  module,
    18  		Changes: changes,
    19  		Plugins: simpleMockPluginLibrary(),
    20  		State:   state,
    21  	}
    22  	g, err := (&BasicGraphBuilder{
    23  		Steps: cbdTestSteps(applyBuilder.Steps()),
    24  		Name:  "ApplyGraphBuilder",
    25  	}).Build(addrs.RootModuleInstance)
    26  	if err != nil {
    27  		t.Fatalf("err: %s", err)
    28  	}
    29  
    30  	return filterInstances(g)
    31  }
    32  
    33  // override the apply graph builder to halt the process after CBD
    34  func cbdTestSteps(steps []GraphTransformer) []GraphTransformer {
    35  	found := false
    36  	var i int
    37  	var t GraphTransformer
    38  	for i, t = range steps {
    39  		if _, ok := t.(*CBDEdgeTransformer); ok {
    40  			found = true
    41  			break
    42  		}
    43  	}
    44  
    45  	if !found {
    46  		panic("CBDEdgeTransformer not found")
    47  	}
    48  
    49  	// re-add the root node so we have a valid graph for a walk, then reduce
    50  	// the graph for less output
    51  	steps = append(steps[:i+1], &CloseRootModuleTransformer{})
    52  	steps = append(steps, &TransitiveReductionTransformer{})
    53  
    54  	return steps
    55  }
    56  
    57  // remove extra nodes for easier test comparisons
    58  func filterInstances(g *Graph) *Graph {
    59  	for _, v := range g.Vertices() {
    60  		if _, ok := v.(GraphNodeResourceInstance); !ok {
    61  			g.Remove(v)
    62  		}
    63  
    64  	}
    65  	return g
    66  }
    67  
    68  func TestCBDEdgeTransformer(t *testing.T) {
    69  	changes := &plans.Changes{
    70  		Resources: []*plans.ResourceInstanceChangeSrc{
    71  			{
    72  				Addr: mustResourceInstanceAddr("test_object.A"),
    73  				ChangeSrc: plans.ChangeSrc{
    74  					Action: plans.CreateThenDelete,
    75  				},
    76  			},
    77  			{
    78  				Addr: mustResourceInstanceAddr("test_object.B"),
    79  				ChangeSrc: plans.ChangeSrc{
    80  					Action: plans.Update,
    81  				},
    82  			},
    83  		},
    84  	}
    85  
    86  	state := states.NewState()
    87  	root := state.EnsureModule(addrs.RootModuleInstance)
    88  	root.SetResourceInstanceCurrent(
    89  		mustResourceInstanceAddr("test_object.A").Resource,
    90  		&states.ResourceInstanceObjectSrc{
    91  			Status:    states.ObjectReady,
    92  			AttrsJSON: []byte(`{"id":"A"}`),
    93  		},
    94  		mustProviderConfig(`provider["registry.terraform.io/hashicorp/test"]`),
    95  	)
    96  	root.SetResourceInstanceCurrent(
    97  		mustResourceInstanceAddr("test_object.B").Resource,
    98  		&states.ResourceInstanceObjectSrc{
    99  			Status:       states.ObjectReady,
   100  			AttrsJSON:    []byte(`{"id":"B","test_list":["x"]}`),
   101  			Dependencies: []addrs.ConfigResource{mustConfigResourceAddr("test_object.A")},
   102  		},
   103  		mustProviderConfig(`provider["registry.terraform.io/hashicorp/test"]`),
   104  	)
   105  
   106  	g := cbdTestGraph(t, "transform-destroy-cbd-edge-basic", changes, state)
   107  	g = filterInstances(g)
   108  
   109  	actual := strings.TrimSpace(g.String())
   110  	expected := regexp.MustCompile(strings.TrimSpace(`
   111  (?m)test_object.A
   112  test_object.A \(destroy deposed \w+\)
   113    test_object.B
   114  test_object.B
   115    test_object.A
   116  `))
   117  
   118  	if !expected.MatchString(actual) {
   119  		t.Fatalf("wrong result\n\ngot:\n%s\n\nwant:\n%s", actual, expected)
   120  	}
   121  }
   122  
   123  func TestCBDEdgeTransformerMulti(t *testing.T) {
   124  	changes := &plans.Changes{
   125  		Resources: []*plans.ResourceInstanceChangeSrc{
   126  			{
   127  				Addr: mustResourceInstanceAddr("test_object.A"),
   128  				ChangeSrc: plans.ChangeSrc{
   129  					Action: plans.CreateThenDelete,
   130  				},
   131  			},
   132  			{
   133  				Addr: mustResourceInstanceAddr("test_object.B"),
   134  				ChangeSrc: plans.ChangeSrc{
   135  					Action: plans.CreateThenDelete,
   136  				},
   137  			},
   138  			{
   139  				Addr: mustResourceInstanceAddr("test_object.C"),
   140  				ChangeSrc: plans.ChangeSrc{
   141  					Action: plans.Update,
   142  				},
   143  			},
   144  		},
   145  	}
   146  
   147  	state := states.NewState()
   148  	root := state.EnsureModule(addrs.RootModuleInstance)
   149  	root.SetResourceInstanceCurrent(
   150  		mustResourceInstanceAddr("test_object.A").Resource,
   151  		&states.ResourceInstanceObjectSrc{
   152  			Status:    states.ObjectReady,
   153  			AttrsJSON: []byte(`{"id":"A"}`),
   154  		},
   155  		mustProviderConfig(`provider["registry.terraform.io/hashicorp/test"]`),
   156  	)
   157  	root.SetResourceInstanceCurrent(
   158  		mustResourceInstanceAddr("test_object.B").Resource,
   159  		&states.ResourceInstanceObjectSrc{
   160  			Status:    states.ObjectReady,
   161  			AttrsJSON: []byte(`{"id":"B"}`),
   162  		},
   163  		mustProviderConfig(`provider["registry.terraform.io/hashicorp/test"]`),
   164  	)
   165  	root.SetResourceInstanceCurrent(
   166  		mustResourceInstanceAddr("test_object.C").Resource,
   167  		&states.ResourceInstanceObjectSrc{
   168  			Status:    states.ObjectReady,
   169  			AttrsJSON: []byte(`{"id":"C","test_list":["x"]}`),
   170  			Dependencies: []addrs.ConfigResource{
   171  				mustConfigResourceAddr("test_object.A"),
   172  				mustConfigResourceAddr("test_object.B"),
   173  			},
   174  		},
   175  		mustProviderConfig(`provider["registry.terraform.io/hashicorp/test"]`),
   176  	)
   177  
   178  	g := cbdTestGraph(t, "transform-destroy-cbd-edge-multi", changes, state)
   179  	g = filterInstances(g)
   180  
   181  	actual := strings.TrimSpace(g.String())
   182  	expected := regexp.MustCompile(strings.TrimSpace(`
   183  (?m)test_object.A
   184  test_object.A \(destroy deposed \w+\)
   185    test_object.C
   186  test_object.B
   187  test_object.B \(destroy deposed \w+\)
   188    test_object.C
   189  test_object.C
   190    test_object.A
   191    test_object.B
   192  `))
   193  
   194  	if !expected.MatchString(actual) {
   195  		t.Fatalf("wrong result\n\ngot:\n%s\n\nwant:\n%s", actual, expected)
   196  	}
   197  }
   198  
   199  func TestCBDEdgeTransformer_depNonCBDCount(t *testing.T) {
   200  	changes := &plans.Changes{
   201  		Resources: []*plans.ResourceInstanceChangeSrc{
   202  			{
   203  				Addr: mustResourceInstanceAddr("test_object.A"),
   204  				ChangeSrc: plans.ChangeSrc{
   205  					Action: plans.CreateThenDelete,
   206  				},
   207  			},
   208  			{
   209  				Addr: mustResourceInstanceAddr("test_object.B[0]"),
   210  				ChangeSrc: plans.ChangeSrc{
   211  					Action: plans.Update,
   212  				},
   213  			},
   214  			{
   215  				Addr: mustResourceInstanceAddr("test_object.B[1]"),
   216  				ChangeSrc: plans.ChangeSrc{
   217  					Action: plans.Update,
   218  				},
   219  			},
   220  		},
   221  	}
   222  
   223  	state := states.NewState()
   224  	root := state.EnsureModule(addrs.RootModuleInstance)
   225  	root.SetResourceInstanceCurrent(
   226  		mustResourceInstanceAddr("test_object.A").Resource,
   227  		&states.ResourceInstanceObjectSrc{
   228  			Status:    states.ObjectReady,
   229  			AttrsJSON: []byte(`{"id":"A"}`),
   230  		},
   231  		mustProviderConfig(`provider["registry.terraform.io/hashicorp/test"]`),
   232  	)
   233  	root.SetResourceInstanceCurrent(
   234  		mustResourceInstanceAddr("test_object.B[0]").Resource,
   235  		&states.ResourceInstanceObjectSrc{
   236  			Status:       states.ObjectReady,
   237  			AttrsJSON:    []byte(`{"id":"B","test_list":["x"]}`),
   238  			Dependencies: []addrs.ConfigResource{mustConfigResourceAddr("test_object.A")},
   239  		},
   240  		mustProviderConfig(`provider["registry.terraform.io/hashicorp/test"]`),
   241  	)
   242  	root.SetResourceInstanceCurrent(
   243  		mustResourceInstanceAddr("test_object.B[1]").Resource,
   244  		&states.ResourceInstanceObjectSrc{
   245  			Status:       states.ObjectReady,
   246  			AttrsJSON:    []byte(`{"id":"B","test_list":["x"]}`),
   247  			Dependencies: []addrs.ConfigResource{mustConfigResourceAddr("test_object.A")},
   248  		},
   249  		mustProviderConfig(`provider["registry.terraform.io/hashicorp/test"]`),
   250  	)
   251  
   252  	g := cbdTestGraph(t, "transform-cbd-destroy-edge-count", changes, state)
   253  
   254  	actual := strings.TrimSpace(g.String())
   255  	expected := regexp.MustCompile(strings.TrimSpace(`
   256  (?m)test_object.A
   257  test_object.A \(destroy deposed \w+\)
   258    test_object.B\[0\]
   259    test_object.B\[1\]
   260  test_object.B\[0\]
   261    test_object.A
   262  test_object.B\[1\]
   263    test_object.A`))
   264  
   265  	if !expected.MatchString(actual) {
   266  		t.Fatalf("wrong result\n\ngot:\n%s\n\nwant:\n%s", actual, expected)
   267  	}
   268  }
   269  
   270  func TestCBDEdgeTransformer_depNonCBDCountBoth(t *testing.T) {
   271  	changes := &plans.Changes{
   272  		Resources: []*plans.ResourceInstanceChangeSrc{
   273  			{
   274  				Addr: mustResourceInstanceAddr("test_object.A[0]"),
   275  				ChangeSrc: plans.ChangeSrc{
   276  					Action: plans.CreateThenDelete,
   277  				},
   278  			},
   279  			{
   280  				Addr: mustResourceInstanceAddr("test_object.A[1]"),
   281  				ChangeSrc: plans.ChangeSrc{
   282  					Action: plans.CreateThenDelete,
   283  				},
   284  			},
   285  			{
   286  				Addr: mustResourceInstanceAddr("test_object.B[0]"),
   287  				ChangeSrc: plans.ChangeSrc{
   288  					Action: plans.Update,
   289  				},
   290  			},
   291  			{
   292  				Addr: mustResourceInstanceAddr("test_object.B[1]"),
   293  				ChangeSrc: plans.ChangeSrc{
   294  					Action: plans.Update,
   295  				},
   296  			},
   297  		},
   298  	}
   299  
   300  	state := states.NewState()
   301  	root := state.EnsureModule(addrs.RootModuleInstance)
   302  	root.SetResourceInstanceCurrent(
   303  		mustResourceInstanceAddr("test_object.A[0]").Resource,
   304  		&states.ResourceInstanceObjectSrc{
   305  			Status:    states.ObjectReady,
   306  			AttrsJSON: []byte(`{"id":"A"}`),
   307  		},
   308  		mustProviderConfig(`provider["registry.terraform.io/hashicorp/test"]`),
   309  	)
   310  	root.SetResourceInstanceCurrent(
   311  		mustResourceInstanceAddr("test_object.A[1]").Resource,
   312  		&states.ResourceInstanceObjectSrc{
   313  			Status:    states.ObjectReady,
   314  			AttrsJSON: []byte(`{"id":"A"}`),
   315  		},
   316  		mustProviderConfig(`provider["registry.terraform.io/hashicorp/test"]`),
   317  	)
   318  	root.SetResourceInstanceCurrent(
   319  		mustResourceInstanceAddr("test_object.B[0]").Resource,
   320  		&states.ResourceInstanceObjectSrc{
   321  			Status:       states.ObjectReady,
   322  			AttrsJSON:    []byte(`{"id":"B","test_list":["x"]}`),
   323  			Dependencies: []addrs.ConfigResource{mustConfigResourceAddr("test_object.A")},
   324  		},
   325  		mustProviderConfig(`provider["registry.terraform.io/hashicorp/test"]`),
   326  	)
   327  	root.SetResourceInstanceCurrent(
   328  		mustResourceInstanceAddr("test_object.B[1]").Resource,
   329  		&states.ResourceInstanceObjectSrc{
   330  			Status:       states.ObjectReady,
   331  			AttrsJSON:    []byte(`{"id":"B","test_list":["x"]}`),
   332  			Dependencies: []addrs.ConfigResource{mustConfigResourceAddr("test_object.A")},
   333  		},
   334  		mustProviderConfig(`provider["registry.terraform.io/hashicorp/test"]`),
   335  	)
   336  
   337  	g := cbdTestGraph(t, "transform-cbd-destroy-edge-both-count", changes, state)
   338  
   339  	actual := strings.TrimSpace(g.String())
   340  	expected := regexp.MustCompile(strings.TrimSpace(`
   341  test_object.A\[0\]
   342  test_object.A\[0\] \(destroy deposed \w+\)
   343    test_object.B\[0\]
   344    test_object.B\[1\]
   345  test_object.A\[1\]
   346  test_object.A\[1\] \(destroy deposed \w+\)
   347    test_object.B\[0\]
   348    test_object.B\[1\]
   349  test_object.B\[0\]
   350    test_object.A\[0\]
   351    test_object.A\[1\]
   352  test_object.B\[1\]
   353    test_object.A\[0\]
   354    test_object.A\[1\]
   355  `))
   356  
   357  	if !expected.MatchString(actual) {
   358  		t.Fatalf("wrong result\n\ngot:\n%s\n\nwant:\n%s", actual, expected)
   359  	}
   360  }