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