github.com/terramate-io/tf@v0.0.0-20230830114523-fce866b4dfcd/addrs/output_value.go (about)

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