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 }