github.com/opentofu/opentofu@v1.7.1/internal/tofu/transform_check_starter.go (about) 1 // Copyright (c) The OpenTofu Authors 2 // SPDX-License-Identifier: MPL-2.0 3 // Copyright (c) 2023 HashiCorp, Inc. 4 // SPDX-License-Identifier: MPL-2.0 5 6 package tofu 7 8 import ( 9 "github.com/opentofu/opentofu/internal/addrs" 10 "github.com/opentofu/opentofu/internal/configs" 11 "github.com/opentofu/opentofu/internal/dag" 12 ) 13 14 var _ GraphTransformer = (*checkStartTransformer)(nil) 15 16 // checkStartTransformer checks if the configuration has any data blocks nested 17 // within check blocks, and if it does then it introduces a nodeCheckStart 18 // vertex that ensures all resources have been applied before it starts loading 19 // the nested data sources. 20 type checkStartTransformer struct { 21 // Config for the entire module. 22 Config *configs.Config 23 24 // Operation is the current operation this node will be part of. 25 Operation walkOperation 26 } 27 28 func (s *checkStartTransformer) Transform(graph *Graph) error { 29 if s.Operation != walkApply && s.Operation != walkPlan { 30 // We only actually execute the checks during plan apply operations 31 // so if we are doing something else we can just skip this and 32 // leave the graph alone. 33 return nil 34 } 35 36 var resources []dag.Vertex 37 var nested []dag.Vertex 38 39 // We're going to step through all the vertices and pull out the relevant 40 // resources and data sources. 41 for _, vertex := range graph.Vertices() { 42 if node, isResource := vertex.(GraphNodeCreator); isResource { 43 addr := node.CreateAddr() 44 45 if addr.Resource.Resource.Mode == addrs.ManagedResourceMode { 46 // This is a resource, so we want to make sure it executes 47 // before any nested data sources. 48 49 // We can reduce the number of additional edges we write into 50 // the graph by only including "leaf" resources, that is 51 // resources that aren't referenced by other resources. If a 52 // resource is referenced by another resource then we know that 53 // it will execute before that resource so we only need to worry 54 // about the referencing resource. 55 56 leafResource := true 57 for _, other := range graph.UpEdges(vertex) { 58 if otherResource, isResource := other.(GraphNodeCreator); isResource { 59 otherAddr := otherResource.CreateAddr() 60 if otherAddr.Resource.Resource.Mode == addrs.ManagedResourceMode { 61 // Then this resource is being referenced so skip 62 // it. 63 leafResource = false 64 break 65 } 66 } 67 } 68 69 if leafResource { 70 resources = append(resources, vertex) 71 } 72 73 // We've handled the resource so move to the next vertex. 74 continue 75 } 76 77 // Now, we know we are processing a data block. 78 79 config := s.Config 80 if !addr.Module.IsRoot() { 81 config = s.Config.Descendent(addr.Module.Module()) 82 } 83 if config == nil { 84 // might have been deleted, so it won't be subject to any checks 85 // anyway. 86 continue 87 } 88 89 resource := config.Module.ResourceByAddr(addr.Resource.Resource) 90 if resource == nil { 91 // might have been deleted, so it won't be subject to any checks 92 // anyway. 93 continue 94 } 95 96 if _, ok := resource.Container.(*configs.Check); ok { 97 // Then this is a data source within a check block, so let's 98 // make a note of it. 99 nested = append(nested, vertex) 100 } 101 102 // Otherwise, it's just a normal data source. From a check block we 103 // don't really care when OpenTofu is loading non-nested data 104 // sources so we'll just forget about it and move on. 105 } 106 } 107 108 if len(nested) > 0 { 109 110 // We don't need to do any of this if we don't have any nested data 111 // sources, so we check that first. 112 // 113 // Otherwise we introduce a vertex that can act as a pauser between 114 // our nested data sources and leaf resources. 115 116 check := &nodeCheckStart{} 117 graph.Add(check) 118 119 // Finally, connect everything up so it all executes in order. 120 121 for _, vertex := range nested { 122 graph.Connect(dag.BasicEdge(vertex, check)) 123 } 124 125 for _, vertex := range resources { 126 graph.Connect(dag.BasicEdge(check, vertex)) 127 } 128 } 129 130 return nil 131 }