package terraform import ( "fmt" "log" "strings" "github.com/hashicorp/terraform/tfdiags" "github.com/hashicorp/terraform/addrs" ) // GraphBuilder is an interface that can be implemented and used with // Terraform to build the graph that Terraform walks. type GraphBuilder interface { // Build builds the graph for the given module path. It is up to // the interface implementation whether this build should expand // the graph or not. Build(addrs.ModuleInstance) (*Graph, tfdiags.Diagnostics) } // BasicGraphBuilder is a GraphBuilder that builds a graph out of a // series of transforms and (optionally) validates the graph is a valid // structure. type BasicGraphBuilder struct { Steps []GraphTransformer Validate bool // Optional name to add to the graph debug log Name string } func (b *BasicGraphBuilder) Build(path addrs.ModuleInstance) (*Graph, tfdiags.Diagnostics) { var diags tfdiags.Diagnostics g := &Graph{Path: path} var lastStepStr string for _, step := range b.Steps { if step == nil { continue } log.Printf("[TRACE] Executing graph transform %T", step) stepName := fmt.Sprintf("%T", step) dot := strings.LastIndex(stepName, ".") if dot >= 0 { stepName = stepName[dot+1:] } debugOp := g.DebugOperation(stepName, "") err := step.Transform(g) errMsg := "" if err != nil { errMsg = err.Error() } debugOp.End(errMsg) if thisStepStr := g.StringWithNodeTypes(); thisStepStr != lastStepStr { log.Printf("[TRACE] Completed graph transform %T with new graph:\n%s------", step, thisStepStr) lastStepStr = thisStepStr } else { log.Printf("[TRACE] Completed graph transform %T (no changes)", step) } if err != nil { if nf, isNF := err.(tfdiags.NonFatalError); isNF { diags = diags.Append(nf.Diagnostics) } else { diags = diags.Append(err) return g, diags } } } // Validate the graph structure if b.Validate { if err := g.Validate(); err != nil { log.Printf("[ERROR] Graph validation failed. Graph:\n\n%s", g.String()) diags = diags.Append(err) return nil, diags } } return g, diags }