github.com/muratcelep/terraform@v1.1.0-beta2-not-internal-4/not-internal/terraform/transform_destroy_edge_test.go (about) 1 package terraform 2 3 import ( 4 "fmt" 5 "strings" 6 "testing" 7 8 "github.com/muratcelep/terraform/not-internal/addrs" 9 "github.com/muratcelep/terraform/not-internal/states" 10 ) 11 12 func TestDestroyEdgeTransformer_basic(t *testing.T) { 13 g := Graph{Path: addrs.RootModuleInstance} 14 g.Add(testDestroyNode("test_object.A")) 15 g.Add(testDestroyNode("test_object.B")) 16 17 state := states.NewState() 18 root := state.EnsureModule(addrs.RootModuleInstance) 19 root.SetResourceInstanceCurrent( 20 mustResourceInstanceAddr("test_object.A").Resource, 21 &states.ResourceInstanceObjectSrc{ 22 Status: states.ObjectReady, 23 AttrsJSON: []byte(`{"id":"A"}`), 24 }, 25 mustProviderConfig(`provider["registry.terraform.io/hashicorp/test"]`), 26 ) 27 root.SetResourceInstanceCurrent( 28 mustResourceInstanceAddr("test_object.B").Resource, 29 &states.ResourceInstanceObjectSrc{ 30 Status: states.ObjectReady, 31 AttrsJSON: []byte(`{"id":"B","test_string":"x"}`), 32 Dependencies: []addrs.ConfigResource{mustConfigResourceAddr("test_object.A")}, 33 }, 34 mustProviderConfig(`provider["registry.terraform.io/hashicorp/test"]`), 35 ) 36 if err := (&AttachStateTransformer{State: state}).Transform(&g); err != nil { 37 t.Fatal(err) 38 } 39 40 tf := &DestroyEdgeTransformer{ 41 Config: testModule(t, "transform-destroy-edge-basic"), 42 } 43 if err := tf.Transform(&g); err != nil { 44 t.Fatalf("err: %s", err) 45 } 46 47 actual := strings.TrimSpace(g.String()) 48 expected := strings.TrimSpace(testTransformDestroyEdgeBasicStr) 49 if actual != expected { 50 t.Fatalf("wrong result\n\ngot:\n%s\n\nwant:\n%s", actual, expected) 51 } 52 } 53 54 func TestDestroyEdgeTransformer_multi(t *testing.T) { 55 g := Graph{Path: addrs.RootModuleInstance} 56 g.Add(testDestroyNode("test_object.A")) 57 g.Add(testDestroyNode("test_object.B")) 58 g.Add(testDestroyNode("test_object.C")) 59 60 state := states.NewState() 61 root := state.EnsureModule(addrs.RootModuleInstance) 62 root.SetResourceInstanceCurrent( 63 mustResourceInstanceAddr("test_object.A").Resource, 64 &states.ResourceInstanceObjectSrc{ 65 Status: states.ObjectReady, 66 AttrsJSON: []byte(`{"id":"A"}`), 67 }, 68 mustProviderConfig(`provider["registry.terraform.io/hashicorp/test"]`), 69 ) 70 root.SetResourceInstanceCurrent( 71 mustResourceInstanceAddr("test_object.B").Resource, 72 &states.ResourceInstanceObjectSrc{ 73 Status: states.ObjectReady, 74 AttrsJSON: []byte(`{"id":"B","test_string":"x"}`), 75 Dependencies: []addrs.ConfigResource{mustConfigResourceAddr("test_object.A")}, 76 }, 77 mustProviderConfig(`provider["registry.terraform.io/hashicorp/test"]`), 78 ) 79 root.SetResourceInstanceCurrent( 80 mustResourceInstanceAddr("test_object.C").Resource, 81 &states.ResourceInstanceObjectSrc{ 82 Status: states.ObjectReady, 83 AttrsJSON: []byte(`{"id":"C","test_string":"x"}`), 84 Dependencies: []addrs.ConfigResource{ 85 mustConfigResourceAddr("test_object.A"), 86 mustConfigResourceAddr("test_object.B"), 87 }, 88 }, 89 mustProviderConfig(`provider["registry.terraform.io/hashicorp/test"]`), 90 ) 91 92 if err := (&AttachStateTransformer{State: state}).Transform(&g); err != nil { 93 t.Fatal(err) 94 } 95 96 tf := &DestroyEdgeTransformer{ 97 Config: testModule(t, "transform-destroy-edge-multi"), 98 } 99 if err := tf.Transform(&g); err != nil { 100 t.Fatalf("err: %s", err) 101 } 102 103 actual := strings.TrimSpace(g.String()) 104 expected := strings.TrimSpace(testTransformDestroyEdgeMultiStr) 105 if actual != expected { 106 t.Fatalf("wrong result\n\ngot:\n%s\n\nwant:\n%s", actual, expected) 107 } 108 } 109 110 func TestDestroyEdgeTransformer_selfRef(t *testing.T) { 111 g := Graph{Path: addrs.RootModuleInstance} 112 g.Add(testDestroyNode("test_object.A")) 113 tf := &DestroyEdgeTransformer{ 114 Config: testModule(t, "transform-destroy-edge-self-ref"), 115 } 116 if err := tf.Transform(&g); err != nil { 117 t.Fatalf("err: %s", err) 118 } 119 120 actual := strings.TrimSpace(g.String()) 121 expected := strings.TrimSpace(testTransformDestroyEdgeSelfRefStr) 122 if actual != expected { 123 t.Fatalf("wrong result\n\ngot:\n%s\n\nwant:\n%s", actual, expected) 124 } 125 } 126 127 func TestDestroyEdgeTransformer_module(t *testing.T) { 128 g := Graph{Path: addrs.RootModuleInstance} 129 g.Add(testDestroyNode("module.child.test_object.b")) 130 g.Add(testDestroyNode("test_object.a")) 131 state := states.NewState() 132 root := state.EnsureModule(addrs.RootModuleInstance) 133 child := state.EnsureModule(addrs.RootModuleInstance.Child("child", addrs.NoKey)) 134 root.SetResourceInstanceCurrent( 135 mustResourceInstanceAddr("test_object.a").Resource, 136 &states.ResourceInstanceObjectSrc{ 137 Status: states.ObjectReady, 138 AttrsJSON: []byte(`{"id":"a"}`), 139 Dependencies: []addrs.ConfigResource{mustConfigResourceAddr("module.child.test_object.b")}, 140 }, 141 mustProviderConfig(`provider["registry.terraform.io/hashicorp/test"]`), 142 ) 143 child.SetResourceInstanceCurrent( 144 mustResourceInstanceAddr("test_object.b").Resource, 145 &states.ResourceInstanceObjectSrc{ 146 Status: states.ObjectReady, 147 AttrsJSON: []byte(`{"id":"b","test_string":"x"}`), 148 }, 149 mustProviderConfig(`provider["registry.terraform.io/hashicorp/test"]`), 150 ) 151 152 if err := (&AttachStateTransformer{State: state}).Transform(&g); err != nil { 153 t.Fatal(err) 154 } 155 156 tf := &DestroyEdgeTransformer{ 157 Config: testModule(t, "transform-destroy-edge-module"), 158 } 159 if err := tf.Transform(&g); err != nil { 160 t.Fatalf("err: %s", err) 161 } 162 163 actual := strings.TrimSpace(g.String()) 164 expected := strings.TrimSpace(testTransformDestroyEdgeModuleStr) 165 if actual != expected { 166 t.Fatalf("wrong result\n\ngot:\n%s\n\nwant:\n%s", actual, expected) 167 } 168 } 169 170 func TestDestroyEdgeTransformer_moduleOnly(t *testing.T) { 171 g := Graph{Path: addrs.RootModuleInstance} 172 173 state := states.NewState() 174 for moduleIdx := 0; moduleIdx < 2; moduleIdx++ { 175 g.Add(testDestroyNode(fmt.Sprintf("module.child[%d].test_object.a", moduleIdx))) 176 g.Add(testDestroyNode(fmt.Sprintf("module.child[%d].test_object.b", moduleIdx))) 177 g.Add(testDestroyNode(fmt.Sprintf("module.child[%d].test_object.c", moduleIdx))) 178 179 child := state.EnsureModule(addrs.RootModuleInstance.Child("child", addrs.IntKey(moduleIdx))) 180 child.SetResourceInstanceCurrent( 181 mustResourceInstanceAddr("test_object.a").Resource, 182 &states.ResourceInstanceObjectSrc{ 183 Status: states.ObjectReady, 184 AttrsJSON: []byte(`{"id":"a"}`), 185 }, 186 mustProviderConfig(`provider["registry.terraform.io/hashicorp/test"]`), 187 ) 188 child.SetResourceInstanceCurrent( 189 mustResourceInstanceAddr("test_object.b").Resource, 190 &states.ResourceInstanceObjectSrc{ 191 Status: states.ObjectReady, 192 AttrsJSON: []byte(`{"id":"b","test_string":"x"}`), 193 Dependencies: []addrs.ConfigResource{ 194 mustConfigResourceAddr("module.child.test_object.a"), 195 }, 196 }, 197 mustProviderConfig(`provider["registry.terraform.io/hashicorp/test"]`), 198 ) 199 child.SetResourceInstanceCurrent( 200 mustResourceInstanceAddr("test_object.c").Resource, 201 &states.ResourceInstanceObjectSrc{ 202 Status: states.ObjectReady, 203 AttrsJSON: []byte(`{"id":"c","test_string":"x"}`), 204 Dependencies: []addrs.ConfigResource{ 205 mustConfigResourceAddr("module.child.test_object.a"), 206 mustConfigResourceAddr("module.child.test_object.b"), 207 }, 208 }, 209 mustProviderConfig(`provider["registry.terraform.io/hashicorp/test"]`), 210 ) 211 } 212 213 if err := (&AttachStateTransformer{State: state}).Transform(&g); err != nil { 214 t.Fatal(err) 215 } 216 217 tf := &DestroyEdgeTransformer{ 218 Config: testModule(t, "transform-destroy-edge-module-only"), 219 } 220 if err := tf.Transform(&g); err != nil { 221 t.Fatalf("err: %s", err) 222 } 223 224 // The analyses done in the destroy edge transformer are between 225 // not-yet-expanded objects, which is conservative and so it will generate 226 // edges that aren't strictly necessary. As a special case we filter out 227 // any edges that are between resources instances that are in different 228 // instances of the same module, because those edges are never needed 229 // (one instance of a module cannot depend on another instance of the 230 // same module) and including them can, in complex cases, cause cycles due 231 // to unnecessary interactions between destroyed and created module 232 // instances in the same plan. 233 // 234 // Therefore below we expect to see the dependencies within each instance 235 // of module.child reflected, but we should not see any dependencies 236 // _between_ instances of module.child. 237 238 actual := strings.TrimSpace(g.String()) 239 expected := strings.TrimSpace(` 240 module.child[0].test_object.a (destroy) 241 module.child[0].test_object.b (destroy) 242 module.child[0].test_object.c (destroy) 243 module.child[0].test_object.b (destroy) 244 module.child[0].test_object.c (destroy) 245 module.child[0].test_object.c (destroy) 246 module.child[1].test_object.a (destroy) 247 module.child[1].test_object.b (destroy) 248 module.child[1].test_object.c (destroy) 249 module.child[1].test_object.b (destroy) 250 module.child[1].test_object.c (destroy) 251 module.child[1].test_object.c (destroy) 252 `) 253 if actual != expected { 254 t.Fatalf("wrong result\n\ngot:\n%s\n\nwant:\n%s", actual, expected) 255 } 256 } 257 258 func TestDestroyEdgeTransformer_destroyThenUpdate(t *testing.T) { 259 g := Graph{Path: addrs.RootModuleInstance} 260 g.Add(testUpdateNode("test_object.A")) 261 g.Add(testDestroyNode("test_object.B")) 262 263 state := states.NewState() 264 root := state.EnsureModule(addrs.RootModuleInstance) 265 root.SetResourceInstanceCurrent( 266 mustResourceInstanceAddr("test_object.A").Resource, 267 &states.ResourceInstanceObjectSrc{ 268 Status: states.ObjectReady, 269 AttrsJSON: []byte(`{"id":"A","test_string":"old"}`), 270 }, 271 mustProviderConfig(`provider["registry.terraform.io/hashicorp/test"]`), 272 ) 273 root.SetResourceInstanceCurrent( 274 mustResourceInstanceAddr("test_object.B").Resource, 275 &states.ResourceInstanceObjectSrc{ 276 Status: states.ObjectReady, 277 AttrsJSON: []byte(`{"id":"B","test_string":"x"}`), 278 Dependencies: []addrs.ConfigResource{mustConfigResourceAddr("test_object.A")}, 279 }, 280 mustProviderConfig(`provider["registry.terraform.io/hashicorp/test"]`), 281 ) 282 283 if err := (&AttachStateTransformer{State: state}).Transform(&g); err != nil { 284 t.Fatal(err) 285 } 286 287 m := testModuleInline(t, map[string]string{ 288 "main.tf": ` 289 resource "test_instance" "a" { 290 test_string = "udpated" 291 } 292 `, 293 }) 294 tf := &DestroyEdgeTransformer{ 295 Config: m, 296 } 297 if err := tf.Transform(&g); err != nil { 298 t.Fatalf("err: %s", err) 299 } 300 301 expected := strings.TrimSpace(` 302 test_object.A 303 test_object.B (destroy) 304 test_object.B (destroy) 305 `) 306 actual := strings.TrimSpace(g.String()) 307 308 if actual != expected { 309 t.Fatalf("wrong result\n\ngot:\n%s\n\nwant:\n%s", actual, expected) 310 } 311 } 312 313 func testDestroyNode(addrString string) GraphNodeDestroyer { 314 instAddr := mustResourceInstanceAddr(addrString) 315 inst := NewNodeAbstractResourceInstance(instAddr) 316 return &NodeDestroyResourceInstance{NodeAbstractResourceInstance: inst} 317 } 318 319 func testUpdateNode(addrString string) GraphNodeCreator { 320 instAddr := mustResourceInstanceAddr(addrString) 321 inst := NewNodeAbstractResourceInstance(instAddr) 322 return &NodeApplyableResourceInstance{NodeAbstractResourceInstance: inst} 323 } 324 325 const testTransformDestroyEdgeBasicStr = ` 326 test_object.A (destroy) 327 test_object.B (destroy) 328 test_object.B (destroy) 329 ` 330 331 const testTransformDestroyEdgeMultiStr = ` 332 test_object.A (destroy) 333 test_object.B (destroy) 334 test_object.C (destroy) 335 test_object.B (destroy) 336 test_object.C (destroy) 337 test_object.C (destroy) 338 ` 339 340 const testTransformDestroyEdgeSelfRefStr = ` 341 test_object.A (destroy) 342 ` 343 344 const testTransformDestroyEdgeModuleStr = ` 345 module.child.test_object.b (destroy) 346 test_object.a (destroy) 347 test_object.a (destroy) 348 `