github.com/opentofu/opentofu@v1.7.1/internal/tofu/graph_builder.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  	"log"
    10  
    11  	"github.com/opentofu/opentofu/internal/addrs"
    12  	"github.com/opentofu/opentofu/internal/logging"
    13  	"github.com/opentofu/opentofu/internal/tfdiags"
    14  )
    15  
    16  // GraphBuilder is an interface that can be implemented and used with
    17  // OpenTofu to build the graph that OpenTofu walks.
    18  type GraphBuilder interface {
    19  	// Build builds the graph for the given module path. It is up to
    20  	// the interface implementation whether this build should expand
    21  	// the graph or not.
    22  	Build(addrs.ModuleInstance) (*Graph, tfdiags.Diagnostics)
    23  }
    24  
    25  // BasicGraphBuilder is a GraphBuilder that builds a graph out of a
    26  // series of transforms and (optionally) validates the graph is a valid
    27  // structure.
    28  type BasicGraphBuilder struct {
    29  	Steps []GraphTransformer
    30  	// Optional name to add to the graph debug log
    31  	Name string
    32  }
    33  
    34  func (b *BasicGraphBuilder) Build(path addrs.ModuleInstance) (*Graph, tfdiags.Diagnostics) {
    35  	var diags tfdiags.Diagnostics
    36  	g := &Graph{Path: path}
    37  
    38  	var lastStepStr string
    39  	for _, step := range b.Steps {
    40  		if step == nil {
    41  			continue
    42  		}
    43  		log.Printf("[TRACE] Executing graph transform %T", step)
    44  
    45  		err := step.Transform(g)
    46  		if thisStepStr := g.StringWithNodeTypes(); thisStepStr != lastStepStr {
    47  			log.Printf("[TRACE] Completed graph transform %T with new graph:\n%s  ------", step, logging.Indent(thisStepStr))
    48  			lastStepStr = thisStepStr
    49  		} else {
    50  			log.Printf("[TRACE] Completed graph transform %T (no changes)", step)
    51  		}
    52  
    53  		if err != nil {
    54  			if nf, isNF := err.(tfdiags.NonFatalError); isNF {
    55  				diags = diags.Append(nf.Diagnostics)
    56  			} else {
    57  				diags = diags.Append(err)
    58  				return g, diags
    59  			}
    60  		}
    61  	}
    62  
    63  	if err := g.Validate(); err != nil {
    64  		log.Printf("[ERROR] Graph validation failed. Graph:\n\n%s", g.String())
    65  		diags = diags.Append(err)
    66  		return nil, diags
    67  	}
    68  
    69  	return g, diags
    70  }