github.com/opentofu/opentofu@v1.7.1/internal/addrs/output_value.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 addrs
     7  
     8  import (
     9  	"fmt"
    10  
    11  	"github.com/hashicorp/hcl/v2"
    12  	"github.com/hashicorp/hcl/v2/hclsyntax"
    13  
    14  	"github.com/opentofu/opentofu/internal/tfdiags"
    15  )
    16  
    17  // OutputValue is the address of an output value, in the context of the module
    18  // that is defining it.
    19  //
    20  // This is related to but separate from ModuleCallOutput, which represents
    21  // a module output from the perspective of its parent module. Outputs are
    22  // referencable from the testing scope, in general tofu operation users
    23  // will be referencing ModuleCallOutput.
    24  type OutputValue struct {
    25  	referenceable
    26  	Name string
    27  }
    28  
    29  func (v OutputValue) String() string {
    30  	return "output." + v.Name
    31  }
    32  
    33  func (v OutputValue) Equal(o OutputValue) bool {
    34  	return v.Name == o.Name
    35  }
    36  
    37  func (v OutputValue) UniqueKey() UniqueKey {
    38  	return v // An OutputValue is its own UniqueKey
    39  }
    40  
    41  func (v OutputValue) uniqueKeySigil() {}
    42  
    43  // Absolute converts the receiver into an absolute address within the given
    44  // module instance.
    45  func (v OutputValue) Absolute(m ModuleInstance) AbsOutputValue {
    46  	return AbsOutputValue{
    47  		Module:      m,
    48  		OutputValue: v,
    49  	}
    50  }
    51  
    52  // InModule converts the receiver into a config address within the given
    53  // module.
    54  func (v OutputValue) InModule(m Module) ConfigOutputValue {
    55  	return ConfigOutputValue{
    56  		Module:      m,
    57  		OutputValue: v,
    58  	}
    59  }
    60  
    61  // AbsOutputValue is the absolute address of an output value within a module instance.
    62  //
    63  // This represents an output globally within the namespace of a particular
    64  // configuration. It is related to but separate from ModuleCallOutput, which
    65  // represents a module output from the perspective of its parent module.
    66  type AbsOutputValue struct {
    67  	Module      ModuleInstance
    68  	OutputValue OutputValue
    69  }
    70  
    71  // OutputValue returns the absolute address of an output value of the given
    72  // name within the receiving module instance.
    73  func (m ModuleInstance) OutputValue(name string) AbsOutputValue {
    74  	return AbsOutputValue{
    75  		Module: m,
    76  		OutputValue: OutputValue{
    77  			Name: name,
    78  		},
    79  	}
    80  }
    81  
    82  func (v AbsOutputValue) CheckRule(t CheckRuleType, i int) CheckRule {
    83  	return CheckRule{
    84  		Container: v,
    85  		Type:      t,
    86  		Index:     i,
    87  	}
    88  }
    89  
    90  func (v AbsOutputValue) String() string {
    91  	if v.Module.IsRoot() {
    92  		return v.OutputValue.String()
    93  	}
    94  	return fmt.Sprintf("%s.%s", v.Module.String(), v.OutputValue.String())
    95  }
    96  
    97  func (v AbsOutputValue) Equal(o AbsOutputValue) bool {
    98  	return v.OutputValue.Equal(o.OutputValue) && v.Module.Equal(o.Module)
    99  }
   100  
   101  func (v AbsOutputValue) ConfigOutputValue() ConfigOutputValue {
   102  	return ConfigOutputValue{
   103  		Module:      v.Module.Module(),
   104  		OutputValue: v.OutputValue,
   105  	}
   106  }
   107  
   108  func (v AbsOutputValue) checkableSigil() {
   109  	// Output values are checkable
   110  }
   111  
   112  func (v AbsOutputValue) ConfigCheckable() ConfigCheckable {
   113  	// Output values are declared by "output" blocks in the configuration,
   114  	// represented as ConfigOutputValue.
   115  	return v.ConfigOutputValue()
   116  }
   117  
   118  func (v AbsOutputValue) CheckableKind() CheckableKind {
   119  	return CheckableOutputValue
   120  }
   121  
   122  func (v AbsOutputValue) UniqueKey() UniqueKey {
   123  	return absOutputValueUniqueKey(v.String())
   124  }
   125  
   126  type absOutputValueUniqueKey string
   127  
   128  func (k absOutputValueUniqueKey) uniqueKeySigil() {}
   129  
   130  func ParseAbsOutputValue(traversal hcl.Traversal) (AbsOutputValue, tfdiags.Diagnostics) {
   131  	path, remain, diags := parseModuleInstancePrefix(traversal)
   132  	if diags.HasErrors() {
   133  		return AbsOutputValue{}, diags
   134  	}
   135  
   136  	if len(remain) != 2 {
   137  		diags = diags.Append(&hcl.Diagnostic{
   138  			Severity: hcl.DiagError,
   139  			Summary:  "Invalid address",
   140  			Detail:   "An output name is required.",
   141  			Subject:  traversal.SourceRange().Ptr(),
   142  		})
   143  		return AbsOutputValue{}, diags
   144  	}
   145  
   146  	if remain.RootName() != "output" {
   147  		diags = diags.Append(&hcl.Diagnostic{
   148  			Severity: hcl.DiagError,
   149  			Summary:  "Invalid address",
   150  			Detail:   "Output address must start with \"output.\".",
   151  			Subject:  remain[0].SourceRange().Ptr(),
   152  		})
   153  		return AbsOutputValue{}, diags
   154  	}
   155  
   156  	var name string
   157  	switch tt := remain[1].(type) {
   158  	case hcl.TraverseAttr:
   159  		name = tt.Name
   160  	default:
   161  		diags = diags.Append(&hcl.Diagnostic{
   162  			Severity: hcl.DiagError,
   163  			Summary:  "Invalid address",
   164  			Detail:   "An output name is required.",
   165  			Subject:  remain[1].SourceRange().Ptr(),
   166  		})
   167  		return AbsOutputValue{}, diags
   168  	}
   169  
   170  	return AbsOutputValue{
   171  		Module: path,
   172  		OutputValue: OutputValue{
   173  			Name: name,
   174  		},
   175  	}, diags
   176  }
   177  
   178  func ParseAbsOutputValueStr(str string) (AbsOutputValue, tfdiags.Diagnostics) {
   179  	var diags tfdiags.Diagnostics
   180  
   181  	traversal, parseDiags := hclsyntax.ParseTraversalAbs([]byte(str), "", hcl.Pos{Line: 1, Column: 1})
   182  	diags = diags.Append(parseDiags)
   183  	if parseDiags.HasErrors() {
   184  		return AbsOutputValue{}, diags
   185  	}
   186  
   187  	addr, addrDiags := ParseAbsOutputValue(traversal)
   188  	diags = diags.Append(addrDiags)
   189  	return addr, diags
   190  }
   191  
   192  // ModuleCallOutput converts an AbsModuleOutput into a ModuleCallOutput,
   193  // returning also the module instance that the ModuleCallOutput is relative
   194  // to.
   195  //
   196  // The root module does not have a call, and so this method cannot be used
   197  // with outputs in the root module, and will panic in that case.
   198  func (v AbsOutputValue) ModuleCallOutput() (ModuleInstance, ModuleCallInstanceOutput) {
   199  	if v.Module.IsRoot() {
   200  		panic("ReferenceFromCall used with root module output")
   201  	}
   202  
   203  	caller, call := v.Module.CallInstance()
   204  	return caller, ModuleCallInstanceOutput{
   205  		Call: call,
   206  		Name: v.OutputValue.Name,
   207  	}
   208  }
   209  
   210  // ConfigOutputValue represents a particular "output" block in the
   211  // configuration, which might have many AbsOutputValue addresses associated
   212  // with it at runtime if it belongs to a module that was called using
   213  // "count" or "for_each".
   214  type ConfigOutputValue struct {
   215  	Module      Module
   216  	OutputValue OutputValue
   217  }
   218  
   219  func (v ConfigOutputValue) String() string {
   220  	if v.Module.IsRoot() {
   221  		return v.OutputValue.String()
   222  	}
   223  	return fmt.Sprintf("%s.%s", v.Module.String(), v.OutputValue.String())
   224  }
   225  
   226  func (v ConfigOutputValue) configCheckableSigil() {
   227  	// ConfigOutputValue is the ConfigCheckable for AbsOutputValue.
   228  }
   229  
   230  func (v ConfigOutputValue) CheckableKind() CheckableKind {
   231  	return CheckableOutputValue
   232  }
   233  
   234  func (v ConfigOutputValue) UniqueKey() UniqueKey {
   235  	return configOutputValueUniqueKey(v.String())
   236  }
   237  
   238  type configOutputValueUniqueKey string
   239  
   240  func (k configOutputValueUniqueKey) uniqueKeySigil() {}