github.com/graywolf-at-work-2/terraform-vendor@v1.4.5/internal/terraform/node_output.go (about)

     1  package terraform
     2  
     3  import (
     4  	"fmt"
     5  	"log"
     6  
     7  	"github.com/hashicorp/hcl/v2"
     8  	"github.com/zclconf/go-cty/cty"
     9  
    10  	"github.com/hashicorp/terraform/internal/addrs"
    11  	"github.com/hashicorp/terraform/internal/configs"
    12  	"github.com/hashicorp/terraform/internal/dag"
    13  	"github.com/hashicorp/terraform/internal/lang"
    14  	"github.com/hashicorp/terraform/internal/lang/marks"
    15  	"github.com/hashicorp/terraform/internal/plans"
    16  	"github.com/hashicorp/terraform/internal/states"
    17  	"github.com/hashicorp/terraform/internal/tfdiags"
    18  )
    19  
    20  // nodeExpandOutput is the placeholder for a non-root module output that has
    21  // not yet had its module path expanded.
    22  type nodeExpandOutput struct {
    23  	Addr         addrs.OutputValue
    24  	Module       addrs.Module
    25  	Config       *configs.Output
    26  	PlanDestroy  bool
    27  	ApplyDestroy bool
    28  	RefreshOnly  bool
    29  
    30  	// Planning is set to true when this node is in a graph that was produced
    31  	// by the plan graph builder, as opposed to the apply graph builder.
    32  	// This quirk is just because we share the same node type between both
    33  	// phases but in practice there are a few small differences in the actions
    34  	// we need to take between plan and apply. See method DynamicExpand for
    35  	// details.
    36  	Planning bool
    37  }
    38  
    39  var (
    40  	_ GraphNodeReferenceable     = (*nodeExpandOutput)(nil)
    41  	_ GraphNodeReferencer        = (*nodeExpandOutput)(nil)
    42  	_ GraphNodeReferenceOutside  = (*nodeExpandOutput)(nil)
    43  	_ GraphNodeDynamicExpandable = (*nodeExpandOutput)(nil)
    44  	_ graphNodeTemporaryValue    = (*nodeExpandOutput)(nil)
    45  	_ graphNodeExpandsInstances  = (*nodeExpandOutput)(nil)
    46  )
    47  
    48  func (n *nodeExpandOutput) expandsInstances() {}
    49  
    50  func (n *nodeExpandOutput) temporaryValue() bool {
    51  	// non root outputs are temporary
    52  	return !n.Module.IsRoot()
    53  }
    54  
    55  func (n *nodeExpandOutput) DynamicExpand(ctx EvalContext) (*Graph, error) {
    56  	expander := ctx.InstanceExpander()
    57  	changes := ctx.Changes()
    58  
    59  	// If this is an output value that participates in custom condition checks
    60  	// (i.e. it has preconditions or postconditions) then the check state
    61  	// wants to know the addresses of the checkable objects so that it can
    62  	// treat them as unknown status if we encounter an error before actually
    63  	// visiting the checks.
    64  	//
    65  	// We must do this only during planning, because the apply phase will start
    66  	// with all of the same checkable objects that were registered during the
    67  	// planning phase. Consumers of our JSON plan and state formats expect
    68  	// that the set of checkable objects will be consistent between the plan
    69  	// and any state snapshots created during apply, and that only the statuses
    70  	// of those objects will have changed.
    71  	var checkableAddrs addrs.Set[addrs.Checkable]
    72  	if n.Planning {
    73  		if checkState := ctx.Checks(); checkState.ConfigHasChecks(n.Addr.InModule(n.Module)) {
    74  			checkableAddrs = addrs.MakeSet[addrs.Checkable]()
    75  		}
    76  	}
    77  
    78  	var g Graph
    79  	for _, module := range expander.ExpandModule(n.Module) {
    80  		absAddr := n.Addr.Absolute(module)
    81  		if checkableAddrs != nil {
    82  			checkableAddrs.Add(absAddr)
    83  		}
    84  
    85  		// Find any recorded change for this output
    86  		var change *plans.OutputChangeSrc
    87  		var outputChanges []*plans.OutputChangeSrc
    88  		if module.IsRoot() {
    89  			outputChanges = changes.GetRootOutputChanges()
    90  		} else {
    91  			parent, call := module.Call()
    92  			outputChanges = changes.GetOutputChanges(parent, call)
    93  		}
    94  		for _, c := range outputChanges {
    95  			if c.Addr.String() == absAddr.String() {
    96  				change = c
    97  				break
    98  			}
    99  		}
   100  
   101  		var node dag.Vertex
   102  		switch {
   103  		case module.IsRoot() && (n.PlanDestroy || n.ApplyDestroy):
   104  			node = &NodeDestroyableOutput{
   105  				Addr:     absAddr,
   106  				Planning: n.Planning,
   107  			}
   108  
   109  		case n.PlanDestroy:
   110  			// nothing is done here for non-root outputs
   111  			continue
   112  
   113  		default:
   114  			node = &NodeApplyableOutput{
   115  				Addr:         absAddr,
   116  				Config:       n.Config,
   117  				Change:       change,
   118  				RefreshOnly:  n.RefreshOnly,
   119  				DestroyApply: n.ApplyDestroy,
   120  				Planning:     n.Planning,
   121  			}
   122  		}
   123  
   124  		log.Printf("[TRACE] Expanding output: adding %s as %T", absAddr.String(), node)
   125  		g.Add(node)
   126  	}
   127  	addRootNodeToGraph(&g)
   128  
   129  	if checkableAddrs != nil {
   130  		checkState := ctx.Checks()
   131  		checkState.ReportCheckableObjects(n.Addr.InModule(n.Module), checkableAddrs)
   132  	}
   133  
   134  	return &g, nil
   135  }
   136  
   137  func (n *nodeExpandOutput) Name() string {
   138  	path := n.Module.String()
   139  	addr := n.Addr.String() + " (expand)"
   140  	if path != "" {
   141  		return path + "." + addr
   142  	}
   143  	return addr
   144  }
   145  
   146  // GraphNodeModulePath
   147  func (n *nodeExpandOutput) ModulePath() addrs.Module {
   148  	return n.Module
   149  }
   150  
   151  // GraphNodeReferenceable
   152  func (n *nodeExpandOutput) ReferenceableAddrs() []addrs.Referenceable {
   153  	// An output in the root module can't be referenced at all.
   154  	if n.Module.IsRoot() {
   155  		return nil
   156  	}
   157  
   158  	// the output is referenced through the module call, and via the
   159  	// module itself.
   160  	_, call := n.Module.Call()
   161  	callOutput := addrs.ModuleCallOutput{
   162  		Call: call,
   163  		Name: n.Addr.Name,
   164  	}
   165  
   166  	// Otherwise, we can reference the output via the
   167  	// module call itself
   168  	return []addrs.Referenceable{call, callOutput}
   169  }
   170  
   171  // GraphNodeReferenceOutside implementation
   172  func (n *nodeExpandOutput) ReferenceOutside() (selfPath, referencePath addrs.Module) {
   173  	// Output values have their expressions resolved in the context of the
   174  	// module where they are defined.
   175  	referencePath = n.Module
   176  
   177  	// ...but they are referenced in the context of their calling module.
   178  	selfPath = referencePath.Parent()
   179  
   180  	return // uses named return values
   181  }
   182  
   183  // GraphNodeReferencer
   184  func (n *nodeExpandOutput) References() []*addrs.Reference {
   185  	// DestroyNodes do not reference anything.
   186  	if n.Module.IsRoot() && n.ApplyDestroy {
   187  		return nil
   188  	}
   189  
   190  	return referencesForOutput(n.Config)
   191  }
   192  
   193  // NodeApplyableOutput represents an output that is "applyable":
   194  // it is ready to be applied.
   195  type NodeApplyableOutput struct {
   196  	Addr   addrs.AbsOutputValue
   197  	Config *configs.Output // Config is the output in the config
   198  	// If this is being evaluated during apply, we may have a change recorded already
   199  	Change *plans.OutputChangeSrc
   200  
   201  	// Refresh-only mode means that any failing output preconditions are
   202  	// reported as warnings rather than errors
   203  	RefreshOnly bool
   204  
   205  	// DestroyApply indicates that we are applying a destroy plan, and do not
   206  	// need to account for conditional blocks.
   207  	DestroyApply bool
   208  
   209  	Planning bool
   210  }
   211  
   212  var (
   213  	_ GraphNodeModuleInstance   = (*NodeApplyableOutput)(nil)
   214  	_ GraphNodeReferenceable    = (*NodeApplyableOutput)(nil)
   215  	_ GraphNodeReferencer       = (*NodeApplyableOutput)(nil)
   216  	_ GraphNodeReferenceOutside = (*NodeApplyableOutput)(nil)
   217  	_ GraphNodeExecutable       = (*NodeApplyableOutput)(nil)
   218  	_ graphNodeTemporaryValue   = (*NodeApplyableOutput)(nil)
   219  	_ dag.GraphNodeDotter       = (*NodeApplyableOutput)(nil)
   220  )
   221  
   222  func (n *NodeApplyableOutput) temporaryValue() bool {
   223  	// this must always be evaluated if it is a root module output
   224  	return !n.Addr.Module.IsRoot()
   225  }
   226  
   227  func (n *NodeApplyableOutput) Name() string {
   228  	return n.Addr.String()
   229  }
   230  
   231  // GraphNodeModuleInstance
   232  func (n *NodeApplyableOutput) Path() addrs.ModuleInstance {
   233  	return n.Addr.Module
   234  }
   235  
   236  // GraphNodeModulePath
   237  func (n *NodeApplyableOutput) ModulePath() addrs.Module {
   238  	return n.Addr.Module.Module()
   239  }
   240  
   241  func referenceOutsideForOutput(addr addrs.AbsOutputValue) (selfPath, referencePath addrs.Module) {
   242  	// Output values have their expressions resolved in the context of the
   243  	// module where they are defined.
   244  	referencePath = addr.Module.Module()
   245  
   246  	// ...but they are referenced in the context of their calling module.
   247  	selfPath = addr.Module.Parent().Module()
   248  
   249  	return // uses named return values
   250  }
   251  
   252  // GraphNodeReferenceOutside implementation
   253  func (n *NodeApplyableOutput) ReferenceOutside() (selfPath, referencePath addrs.Module) {
   254  	return referenceOutsideForOutput(n.Addr)
   255  }
   256  
   257  func referenceableAddrsForOutput(addr addrs.AbsOutputValue) []addrs.Referenceable {
   258  	// An output in the root module can't be referenced at all.
   259  	if addr.Module.IsRoot() {
   260  		return nil
   261  	}
   262  
   263  	// Otherwise, we can be referenced via a reference to our output name
   264  	// on the parent module's call, or via a reference to the entire call.
   265  	// e.g. module.foo.bar or just module.foo .
   266  	// Note that our ReferenceOutside method causes these addresses to be
   267  	// relative to the calling module, not the module where the output
   268  	// was declared.
   269  	_, outp := addr.ModuleCallOutput()
   270  	_, call := addr.Module.CallInstance()
   271  
   272  	return []addrs.Referenceable{outp, call}
   273  }
   274  
   275  // GraphNodeReferenceable
   276  func (n *NodeApplyableOutput) ReferenceableAddrs() []addrs.Referenceable {
   277  	return referenceableAddrsForOutput(n.Addr)
   278  }
   279  
   280  func referencesForOutput(c *configs.Output) []*addrs.Reference {
   281  	var refs []*addrs.Reference
   282  
   283  	impRefs, _ := lang.ReferencesInExpr(c.Expr)
   284  	expRefs, _ := lang.References(c.DependsOn)
   285  
   286  	refs = append(refs, impRefs...)
   287  	refs = append(refs, expRefs...)
   288  
   289  	for _, check := range c.Preconditions {
   290  		condRefs, _ := lang.ReferencesInExpr(check.Condition)
   291  		refs = append(refs, condRefs...)
   292  		errRefs, _ := lang.ReferencesInExpr(check.ErrorMessage)
   293  		refs = append(refs, errRefs...)
   294  	}
   295  
   296  	return refs
   297  }
   298  
   299  // GraphNodeReferencer
   300  func (n *NodeApplyableOutput) References() []*addrs.Reference {
   301  	return referencesForOutput(n.Config)
   302  }
   303  
   304  // GraphNodeExecutable
   305  func (n *NodeApplyableOutput) Execute(ctx EvalContext, op walkOperation) (diags tfdiags.Diagnostics) {
   306  	state := ctx.State()
   307  	if state == nil {
   308  		return
   309  	}
   310  
   311  	changes := ctx.Changes() // may be nil, if we're not working on a changeset
   312  
   313  	val := cty.UnknownVal(cty.DynamicPseudoType)
   314  	changeRecorded := n.Change != nil
   315  	// we we have a change recorded, we don't need to re-evaluate if the value
   316  	// was known
   317  	if changeRecorded {
   318  		change, err := n.Change.Decode()
   319  		diags = diags.Append(err)
   320  		if err == nil {
   321  			val = change.After
   322  		}
   323  	}
   324  
   325  	// Checks are not evaluated during a destroy. The checks may fail, may not
   326  	// be valid, or may not have been registered at all.
   327  	if !n.DestroyApply {
   328  		checkRuleSeverity := tfdiags.Error
   329  		if n.RefreshOnly {
   330  			checkRuleSeverity = tfdiags.Warning
   331  		}
   332  		checkDiags := evalCheckRules(
   333  			addrs.OutputPrecondition,
   334  			n.Config.Preconditions,
   335  			ctx, n.Addr, EvalDataForNoInstanceKey,
   336  			checkRuleSeverity,
   337  		)
   338  		diags = diags.Append(checkDiags)
   339  		if diags.HasErrors() {
   340  			return diags // failed preconditions prevent further evaluation
   341  		}
   342  	}
   343  
   344  	// If there was no change recorded, or the recorded change was not wholly
   345  	// known, then we need to re-evaluate the output
   346  	if !changeRecorded || !val.IsWhollyKnown() {
   347  		// This has to run before we have a state lock, since evaluation also
   348  		// reads the state
   349  		var evalDiags tfdiags.Diagnostics
   350  		val, evalDiags = ctx.EvaluateExpr(n.Config.Expr, cty.DynamicPseudoType, nil)
   351  		diags = diags.Append(evalDiags)
   352  
   353  		// We'll handle errors below, after we have loaded the module.
   354  		// Outputs don't have a separate mode for validation, so validate
   355  		// depends_on expressions here too
   356  		diags = diags.Append(validateDependsOn(ctx, n.Config.DependsOn))
   357  
   358  		// For root module outputs in particular, an output value must be
   359  		// statically declared as sensitive in order to dynamically return
   360  		// a sensitive result, to help avoid accidental exposure in the state
   361  		// of a sensitive value that the user doesn't want to include there.
   362  		if n.Addr.Module.IsRoot() {
   363  			if !n.Config.Sensitive && marks.Contains(val, marks.Sensitive) {
   364  				diags = diags.Append(&hcl.Diagnostic{
   365  					Severity: hcl.DiagError,
   366  					Summary:  "Output refers to sensitive values",
   367  					Detail: `To reduce the risk of accidentally exporting sensitive data that was intended to be only internal, Terraform requires that any root module output containing sensitive data be explicitly marked as sensitive, to confirm your intent.
   368  
   369  If you do intend to export this data, annotate the output value as sensitive by adding the following argument:
   370      sensitive = true`,
   371  					Subject: n.Config.DeclRange.Ptr(),
   372  				})
   373  			}
   374  		}
   375  	}
   376  
   377  	// handling the interpolation error
   378  	if diags.HasErrors() {
   379  		if flagWarnOutputErrors {
   380  			log.Printf("[ERROR] Output interpolation %q failed: %s", n.Addr, diags.Err())
   381  			// if we're continuing, make sure the output is included, and
   382  			// marked as unknown. If the evaluator was able to find a type
   383  			// for the value in spite of the error then we'll use it.
   384  			n.setValue(state, changes, cty.UnknownVal(val.Type()))
   385  
   386  			// Keep existing warnings, while converting errors to warnings.
   387  			// This is not meant to be the normal path, so there no need to
   388  			// make the errors pretty.
   389  			var warnings tfdiags.Diagnostics
   390  			for _, d := range diags {
   391  				switch d.Severity() {
   392  				case tfdiags.Warning:
   393  					warnings = warnings.Append(d)
   394  				case tfdiags.Error:
   395  					desc := d.Description()
   396  					warnings = warnings.Append(tfdiags.SimpleWarning(fmt.Sprintf("%s:%s", desc.Summary, desc.Detail)))
   397  				}
   398  			}
   399  
   400  			return warnings
   401  		}
   402  		return diags
   403  	}
   404  	n.setValue(state, changes, val)
   405  
   406  	// If we were able to evaluate a new value, we can update that in the
   407  	// refreshed state as well.
   408  	if state = ctx.RefreshState(); state != nil && val.IsWhollyKnown() {
   409  		// we only need to update the state, do not pass in the changes again
   410  		n.setValue(state, nil, val)
   411  	}
   412  
   413  	return diags
   414  }
   415  
   416  // dag.GraphNodeDotter impl.
   417  func (n *NodeApplyableOutput) DotNode(name string, opts *dag.DotOpts) *dag.DotNode {
   418  	return &dag.DotNode{
   419  		Name: name,
   420  		Attrs: map[string]string{
   421  			"label": n.Name(),
   422  			"shape": "note",
   423  		},
   424  	}
   425  }
   426  
   427  // NodeDestroyableOutput represents an output that is "destroyable":
   428  // its application will remove the output from the state.
   429  type NodeDestroyableOutput struct {
   430  	Addr     addrs.AbsOutputValue
   431  	Planning bool
   432  }
   433  
   434  var (
   435  	_ GraphNodeExecutable = (*NodeDestroyableOutput)(nil)
   436  	_ dag.GraphNodeDotter = (*NodeDestroyableOutput)(nil)
   437  )
   438  
   439  func (n *NodeDestroyableOutput) Name() string {
   440  	return fmt.Sprintf("%s (destroy)", n.Addr.String())
   441  }
   442  
   443  // GraphNodeModulePath
   444  func (n *NodeDestroyableOutput) ModulePath() addrs.Module {
   445  	return n.Addr.Module.Module()
   446  }
   447  
   448  func (n *NodeDestroyableOutput) temporaryValue() bool {
   449  	// this must always be evaluated if it is a root module output
   450  	return !n.Addr.Module.IsRoot()
   451  }
   452  
   453  // GraphNodeExecutable
   454  func (n *NodeDestroyableOutput) Execute(ctx EvalContext, op walkOperation) tfdiags.Diagnostics {
   455  	state := ctx.State()
   456  	if state == nil {
   457  		return nil
   458  	}
   459  
   460  	// if this is a root module, try to get a before value from the state for
   461  	// the diff
   462  	sensitiveBefore := false
   463  	before := cty.NullVal(cty.DynamicPseudoType)
   464  	mod := state.Module(n.Addr.Module)
   465  	if n.Addr.Module.IsRoot() && mod != nil {
   466  		if o, ok := mod.OutputValues[n.Addr.OutputValue.Name]; ok {
   467  			sensitiveBefore = o.Sensitive
   468  			before = o.Value
   469  		} else {
   470  			// If the output was not in state, a delete change would
   471  			// be meaningless, so exit early.
   472  			return nil
   473  
   474  		}
   475  	}
   476  
   477  	changes := ctx.Changes()
   478  	if changes != nil && n.Planning {
   479  		change := &plans.OutputChange{
   480  			Addr:      n.Addr,
   481  			Sensitive: sensitiveBefore,
   482  			Change: plans.Change{
   483  				Action: plans.Delete,
   484  				Before: before,
   485  				After:  cty.NullVal(cty.DynamicPseudoType),
   486  			},
   487  		}
   488  
   489  		cs, err := change.Encode()
   490  		if err != nil {
   491  			// Should never happen, since we just constructed this right above
   492  			panic(fmt.Sprintf("planned change for %s could not be encoded: %s", n.Addr, err))
   493  		}
   494  		log.Printf("[TRACE] NodeDestroyableOutput: Saving %s change for %s in changeset", change.Action, n.Addr)
   495  
   496  		changes.RemoveOutputChange(n.Addr) // remove any existing planned change, if present
   497  		changes.AppendOutputChange(cs)     // add the new planned change
   498  	}
   499  
   500  	state.RemoveOutputValue(n.Addr)
   501  	return nil
   502  }
   503  
   504  // dag.GraphNodeDotter impl.
   505  func (n *NodeDestroyableOutput) DotNode(name string, opts *dag.DotOpts) *dag.DotNode {
   506  	return &dag.DotNode{
   507  		Name: name,
   508  		Attrs: map[string]string{
   509  			"label": n.Name(),
   510  			"shape": "note",
   511  		},
   512  	}
   513  }
   514  
   515  func (n *NodeApplyableOutput) setValue(state *states.SyncState, changes *plans.ChangesSync, val cty.Value) {
   516  	if changes != nil && n.Planning {
   517  		// if this is a root module, try to get a before value from the state for
   518  		// the diff
   519  		sensitiveBefore := false
   520  		before := cty.NullVal(cty.DynamicPseudoType)
   521  
   522  		// is this output new to our state?
   523  		newOutput := true
   524  
   525  		mod := state.Module(n.Addr.Module)
   526  		if n.Addr.Module.IsRoot() && mod != nil {
   527  			for name, o := range mod.OutputValues {
   528  				if name == n.Addr.OutputValue.Name {
   529  					before = o.Value
   530  					sensitiveBefore = o.Sensitive
   531  					newOutput = false
   532  					break
   533  				}
   534  			}
   535  		}
   536  
   537  		// We will not show the value if either the before or after are marked
   538  		// as sensitive. We can show the value again once sensitivity is
   539  		// removed from both the config and the state.
   540  		sensitiveChange := sensitiveBefore || n.Config.Sensitive
   541  
   542  		// strip any marks here just to be sure we don't panic on the True comparison
   543  		unmarkedVal, _ := val.UnmarkDeep()
   544  
   545  		action := plans.Update
   546  		switch {
   547  		case val.IsNull() && before.IsNull():
   548  			// This is separate from the NoOp case below, since we can ignore
   549  			// sensitivity here when there are only null values.
   550  			action = plans.NoOp
   551  
   552  		case newOutput:
   553  			// This output was just added to the configuration
   554  			action = plans.Create
   555  
   556  		case val.IsWhollyKnown() &&
   557  			unmarkedVal.Equals(before).True() &&
   558  			n.Config.Sensitive == sensitiveBefore:
   559  			// Sensitivity must also match to be a NoOp.
   560  			// Theoretically marks may not match here, but sensitivity is the
   561  			// only one we can act on, and the state will have been loaded
   562  			// without any marks to consider.
   563  			action = plans.NoOp
   564  		}
   565  
   566  		change := &plans.OutputChange{
   567  			Addr:      n.Addr,
   568  			Sensitive: sensitiveChange,
   569  			Change: plans.Change{
   570  				Action: action,
   571  				Before: before,
   572  				After:  val,
   573  			},
   574  		}
   575  
   576  		cs, err := change.Encode()
   577  		if err != nil {
   578  			// Should never happen, since we just constructed this right above
   579  			panic(fmt.Sprintf("planned change for %s could not be encoded: %s", n.Addr, err))
   580  		}
   581  		log.Printf("[TRACE] setValue: Saving %s change for %s in changeset", change.Action, n.Addr)
   582  		changes.AppendOutputChange(cs) // add the new planned change
   583  	}
   584  
   585  	if changes != nil && !n.Planning {
   586  		// During apply there is no longer any change to track, so we must
   587  		// ensure the state is updated and not overridden by a change.
   588  		changes.RemoveOutputChange(n.Addr)
   589  	}
   590  
   591  	// Null outputs must be saved for modules so that they can still be
   592  	// evaluated. Null root outputs are removed entirely, which is always fine
   593  	// because they can't be referenced by anything else in the configuration.
   594  	if n.Addr.Module.IsRoot() && val.IsNull() {
   595  		log.Printf("[TRACE] setValue: Removing %s from state (it is now null)", n.Addr)
   596  		state.RemoveOutputValue(n.Addr)
   597  		return
   598  	}
   599  
   600  	log.Printf("[TRACE] setValue: Saving value for %s in state", n.Addr)
   601  
   602  	// non-root outputs need to keep sensitive marks for evaluation, but are
   603  	// not serialized.
   604  	if n.Addr.Module.IsRoot() {
   605  		val, _ = val.UnmarkDeep()
   606  		val = cty.UnknownAsNull(val)
   607  	}
   608  
   609  	state.SetOutputValue(n.Addr, val, n.Config.Sensitive)
   610  }