github.com/hashicorp/terraform-plugin-sdk@v1.17.2/terraform/node_output.go (about)

     1  package terraform
     2  
     3  import (
     4  	"fmt"
     5  
     6  	"github.com/hashicorp/terraform-plugin-sdk/internal/addrs"
     7  	"github.com/hashicorp/terraform-plugin-sdk/internal/configs"
     8  	"github.com/hashicorp/terraform-plugin-sdk/internal/dag"
     9  	"github.com/hashicorp/terraform-plugin-sdk/internal/lang"
    10  )
    11  
    12  // NodeApplyableOutput represents an output that is "applyable":
    13  // it is ready to be applied.
    14  type NodeApplyableOutput struct {
    15  	Addr   addrs.AbsOutputValue
    16  	Config *configs.Output // Config is the output in the config
    17  }
    18  
    19  var (
    20  	_ GraphNodeSubPath          = (*NodeApplyableOutput)(nil)
    21  	_ RemovableIfNotTargeted    = (*NodeApplyableOutput)(nil)
    22  	_ GraphNodeTargetDownstream = (*NodeApplyableOutput)(nil)
    23  	_ GraphNodeReferenceable    = (*NodeApplyableOutput)(nil)
    24  	_ GraphNodeReferencer       = (*NodeApplyableOutput)(nil)
    25  	_ GraphNodeReferenceOutside = (*NodeApplyableOutput)(nil)
    26  	_ GraphNodeEvalable         = (*NodeApplyableOutput)(nil)
    27  	_ dag.GraphNodeDotter       = (*NodeApplyableOutput)(nil)
    28  )
    29  
    30  func (n *NodeApplyableOutput) Name() string {
    31  	return n.Addr.String()
    32  }
    33  
    34  // GraphNodeSubPath
    35  func (n *NodeApplyableOutput) Path() addrs.ModuleInstance {
    36  	return n.Addr.Module
    37  }
    38  
    39  // RemovableIfNotTargeted
    40  func (n *NodeApplyableOutput) RemoveIfNotTargeted() bool {
    41  	// We need to add this so that this node will be removed if
    42  	// it isn't targeted or a dependency of a target.
    43  	return true
    44  }
    45  
    46  // GraphNodeTargetDownstream
    47  func (n *NodeApplyableOutput) TargetDownstream(targetedDeps, untargetedDeps *dag.Set) bool {
    48  	// If any of the direct dependencies of an output are targeted then
    49  	// the output must always be targeted as well, so its value will always
    50  	// be up-to-date at the completion of an apply walk.
    51  	return true
    52  }
    53  
    54  func referenceOutsideForOutput(addr addrs.AbsOutputValue) (selfPath, referencePath addrs.ModuleInstance) {
    55  
    56  	// Output values have their expressions resolved in the context of the
    57  	// module where they are defined.
    58  	referencePath = addr.Module
    59  
    60  	// ...but they are referenced in the context of their calling module.
    61  	selfPath = addr.Module.Parent()
    62  
    63  	return // uses named return values
    64  
    65  }
    66  
    67  // GraphNodeReferenceOutside implementation
    68  func (n *NodeApplyableOutput) ReferenceOutside() (selfPath, referencePath addrs.ModuleInstance) {
    69  	return referenceOutsideForOutput(n.Addr)
    70  }
    71  
    72  func referenceableAddrsForOutput(addr addrs.AbsOutputValue) []addrs.Referenceable {
    73  	// An output in the root module can't be referenced at all.
    74  	if addr.Module.IsRoot() {
    75  		return nil
    76  	}
    77  
    78  	// Otherwise, we can be referenced via a reference to our output name
    79  	// on the parent module's call, or via a reference to the entire call.
    80  	// e.g. module.foo.bar or just module.foo .
    81  	// Note that our ReferenceOutside method causes these addresses to be
    82  	// relative to the calling module, not the module where the output
    83  	// was declared.
    84  	_, outp := addr.ModuleCallOutput()
    85  	_, call := addr.Module.CallInstance()
    86  	return []addrs.Referenceable{outp, call}
    87  
    88  }
    89  
    90  // GraphNodeReferenceable
    91  func (n *NodeApplyableOutput) ReferenceableAddrs() []addrs.Referenceable {
    92  	return referenceableAddrsForOutput(n.Addr)
    93  }
    94  
    95  func referencesForOutput(c *configs.Output) []*addrs.Reference {
    96  	impRefs, _ := lang.ReferencesInExpr(c.Expr)
    97  	expRefs, _ := lang.References(c.DependsOn)
    98  	l := len(impRefs) + len(expRefs)
    99  	if l == 0 {
   100  		return nil
   101  	}
   102  	refs := make([]*addrs.Reference, 0, l)
   103  	refs = append(refs, impRefs...)
   104  	refs = append(refs, expRefs...)
   105  	return refs
   106  
   107  }
   108  
   109  // GraphNodeReferencer
   110  func (n *NodeApplyableOutput) References() []*addrs.Reference {
   111  	return appendResourceDestroyReferences(referencesForOutput(n.Config))
   112  }
   113  
   114  // GraphNodeEvalable
   115  func (n *NodeApplyableOutput) EvalTree() EvalNode {
   116  	return &EvalSequence{
   117  		Nodes: []EvalNode{
   118  			&EvalOpFilter{
   119  				Ops: []walkOperation{walkRefresh, walkPlan, walkApply, walkValidate, walkDestroy, walkPlanDestroy},
   120  				Node: &EvalWriteOutput{
   121  					Addr:      n.Addr.OutputValue,
   122  					Sensitive: n.Config.Sensitive,
   123  					Expr:      n.Config.Expr,
   124  				},
   125  			},
   126  		},
   127  	}
   128  }
   129  
   130  // dag.GraphNodeDotter impl.
   131  func (n *NodeApplyableOutput) DotNode(name string, opts *dag.DotOpts) *dag.DotNode {
   132  	return &dag.DotNode{
   133  		Name: name,
   134  		Attrs: map[string]string{
   135  			"label": n.Name(),
   136  			"shape": "note",
   137  		},
   138  	}
   139  }
   140  
   141  // NodeDestroyableOutput represents an output that is "destroybale":
   142  // its application will remove the output from the state.
   143  type NodeDestroyableOutput struct {
   144  	Addr   addrs.AbsOutputValue
   145  	Config *configs.Output // Config is the output in the config
   146  }
   147  
   148  var (
   149  	_ GraphNodeSubPath          = (*NodeDestroyableOutput)(nil)
   150  	_ RemovableIfNotTargeted    = (*NodeDestroyableOutput)(nil)
   151  	_ GraphNodeTargetDownstream = (*NodeDestroyableOutput)(nil)
   152  	_ GraphNodeReferencer       = (*NodeDestroyableOutput)(nil)
   153  	_ GraphNodeEvalable         = (*NodeDestroyableOutput)(nil)
   154  	_ dag.GraphNodeDotter       = (*NodeDestroyableOutput)(nil)
   155  )
   156  
   157  func (n *NodeDestroyableOutput) Name() string {
   158  	return fmt.Sprintf("%s (destroy)", n.Addr.String())
   159  }
   160  
   161  // GraphNodeSubPath
   162  func (n *NodeDestroyableOutput) Path() addrs.ModuleInstance {
   163  	return n.Addr.Module
   164  }
   165  
   166  // RemovableIfNotTargeted
   167  func (n *NodeDestroyableOutput) RemoveIfNotTargeted() bool {
   168  	// We need to add this so that this node will be removed if
   169  	// it isn't targeted or a dependency of a target.
   170  	return true
   171  }
   172  
   173  // This will keep the destroy node in the graph if its corresponding output
   174  // node is also in the destroy graph.
   175  func (n *NodeDestroyableOutput) TargetDownstream(targetedDeps, untargetedDeps *dag.Set) bool {
   176  	return true
   177  }
   178  
   179  // GraphNodeReferencer
   180  func (n *NodeDestroyableOutput) References() []*addrs.Reference {
   181  	return referencesForOutput(n.Config)
   182  }
   183  
   184  // GraphNodeEvalable
   185  func (n *NodeDestroyableOutput) EvalTree() EvalNode {
   186  	return &EvalDeleteOutput{
   187  		Addr: n.Addr.OutputValue,
   188  	}
   189  }
   190  
   191  // dag.GraphNodeDotter impl.
   192  func (n *NodeDestroyableOutput) DotNode(name string, opts *dag.DotOpts) *dag.DotNode {
   193  	return &dag.DotNode{
   194  		Name: name,
   195  		Attrs: map[string]string{
   196  			"label": n.Name(),
   197  			"shape": "note",
   198  		},
   199  	}
   200  }