mirror of
https://github.com/opentofu/opentofu.git
synced 2024-12-28 01:41:48 -06:00
239 lines
6.2 KiB
Go
239 lines
6.2 KiB
Go
// Copyright (c) HashiCorp, Inc.
|
|
// SPDX-License-Identifier: MPL-2.0
|
|
|
|
package addrs
|
|
|
|
import (
|
|
"fmt"
|
|
|
|
"github.com/hashicorp/hcl/v2"
|
|
"github.com/hashicorp/hcl/v2/hclsyntax"
|
|
|
|
"github.com/opentofu/opentofu/internal/tfdiags"
|
|
)
|
|
|
|
// OutputValue is the address of an output value, in the context of the module
|
|
// that is defining it.
|
|
//
|
|
// This is related to but separate from ModuleCallOutput, which represents
|
|
// a module output from the perspective of its parent module. Outputs are
|
|
// referencable from the testing scope, in general tofu operation users
|
|
// will be referencing ModuleCallOutput.
|
|
type OutputValue struct {
|
|
referenceable
|
|
Name string
|
|
}
|
|
|
|
func (v OutputValue) String() string {
|
|
return "output." + v.Name
|
|
}
|
|
|
|
func (v OutputValue) Equal(o OutputValue) bool {
|
|
return v.Name == o.Name
|
|
}
|
|
|
|
func (v OutputValue) UniqueKey() UniqueKey {
|
|
return v // An OutputValue is its own UniqueKey
|
|
}
|
|
|
|
func (v OutputValue) uniqueKeySigil() {}
|
|
|
|
// Absolute converts the receiver into an absolute address within the given
|
|
// module instance.
|
|
func (v OutputValue) Absolute(m ModuleInstance) AbsOutputValue {
|
|
return AbsOutputValue{
|
|
Module: m,
|
|
OutputValue: v,
|
|
}
|
|
}
|
|
|
|
// InModule converts the receiver into a config address within the given
|
|
// module.
|
|
func (v OutputValue) InModule(m Module) ConfigOutputValue {
|
|
return ConfigOutputValue{
|
|
Module: m,
|
|
OutputValue: v,
|
|
}
|
|
}
|
|
|
|
// AbsOutputValue is the absolute address of an output value within a module instance.
|
|
//
|
|
// This represents an output globally within the namespace of a particular
|
|
// configuration. It is related to but separate from ModuleCallOutput, which
|
|
// represents a module output from the perspective of its parent module.
|
|
type AbsOutputValue struct {
|
|
Module ModuleInstance
|
|
OutputValue OutputValue
|
|
}
|
|
|
|
// OutputValue returns the absolute address of an output value of the given
|
|
// name within the receiving module instance.
|
|
func (m ModuleInstance) OutputValue(name string) AbsOutputValue {
|
|
return AbsOutputValue{
|
|
Module: m,
|
|
OutputValue: OutputValue{
|
|
Name: name,
|
|
},
|
|
}
|
|
}
|
|
|
|
func (v AbsOutputValue) CheckRule(t CheckRuleType, i int) CheckRule {
|
|
return CheckRule{
|
|
Container: v,
|
|
Type: t,
|
|
Index: i,
|
|
}
|
|
}
|
|
|
|
func (v AbsOutputValue) String() string {
|
|
if v.Module.IsRoot() {
|
|
return v.OutputValue.String()
|
|
}
|
|
return fmt.Sprintf("%s.%s", v.Module.String(), v.OutputValue.String())
|
|
}
|
|
|
|
func (v AbsOutputValue) Equal(o AbsOutputValue) bool {
|
|
return v.OutputValue.Equal(o.OutputValue) && v.Module.Equal(o.Module)
|
|
}
|
|
|
|
func (v AbsOutputValue) ConfigOutputValue() ConfigOutputValue {
|
|
return ConfigOutputValue{
|
|
Module: v.Module.Module(),
|
|
OutputValue: v.OutputValue,
|
|
}
|
|
}
|
|
|
|
func (v AbsOutputValue) checkableSigil() {
|
|
// Output values are checkable
|
|
}
|
|
|
|
func (v AbsOutputValue) ConfigCheckable() ConfigCheckable {
|
|
// Output values are declared by "output" blocks in the configuration,
|
|
// represented as ConfigOutputValue.
|
|
return v.ConfigOutputValue()
|
|
}
|
|
|
|
func (v AbsOutputValue) CheckableKind() CheckableKind {
|
|
return CheckableOutputValue
|
|
}
|
|
|
|
func (v AbsOutputValue) UniqueKey() UniqueKey {
|
|
return absOutputValueUniqueKey(v.String())
|
|
}
|
|
|
|
type absOutputValueUniqueKey string
|
|
|
|
func (k absOutputValueUniqueKey) uniqueKeySigil() {}
|
|
|
|
func ParseAbsOutputValue(traversal hcl.Traversal) (AbsOutputValue, tfdiags.Diagnostics) {
|
|
path, remain, diags := parseModuleInstancePrefix(traversal)
|
|
if diags.HasErrors() {
|
|
return AbsOutputValue{}, diags
|
|
}
|
|
|
|
if len(remain) != 2 {
|
|
diags = diags.Append(&hcl.Diagnostic{
|
|
Severity: hcl.DiagError,
|
|
Summary: "Invalid address",
|
|
Detail: "An output name is required.",
|
|
Subject: traversal.SourceRange().Ptr(),
|
|
})
|
|
return AbsOutputValue{}, diags
|
|
}
|
|
|
|
if remain.RootName() != "output" {
|
|
diags = diags.Append(&hcl.Diagnostic{
|
|
Severity: hcl.DiagError,
|
|
Summary: "Invalid address",
|
|
Detail: "Output address must start with \"output.\".",
|
|
Subject: remain[0].SourceRange().Ptr(),
|
|
})
|
|
return AbsOutputValue{}, diags
|
|
}
|
|
|
|
var name string
|
|
switch tt := remain[1].(type) {
|
|
case hcl.TraverseAttr:
|
|
name = tt.Name
|
|
default:
|
|
diags = diags.Append(&hcl.Diagnostic{
|
|
Severity: hcl.DiagError,
|
|
Summary: "Invalid address",
|
|
Detail: "An output name is required.",
|
|
Subject: remain[1].SourceRange().Ptr(),
|
|
})
|
|
return AbsOutputValue{}, diags
|
|
}
|
|
|
|
return AbsOutputValue{
|
|
Module: path,
|
|
OutputValue: OutputValue{
|
|
Name: name,
|
|
},
|
|
}, diags
|
|
}
|
|
|
|
func ParseAbsOutputValueStr(str string) (AbsOutputValue, tfdiags.Diagnostics) {
|
|
var diags tfdiags.Diagnostics
|
|
|
|
traversal, parseDiags := hclsyntax.ParseTraversalAbs([]byte(str), "", hcl.Pos{Line: 1, Column: 1})
|
|
diags = diags.Append(parseDiags)
|
|
if parseDiags.HasErrors() {
|
|
return AbsOutputValue{}, diags
|
|
}
|
|
|
|
addr, addrDiags := ParseAbsOutputValue(traversal)
|
|
diags = diags.Append(addrDiags)
|
|
return addr, diags
|
|
}
|
|
|
|
// ModuleCallOutput converts an AbsModuleOutput into a ModuleCallOutput,
|
|
// returning also the module instance that the ModuleCallOutput is relative
|
|
// to.
|
|
//
|
|
// The root module does not have a call, and so this method cannot be used
|
|
// with outputs in the root module, and will panic in that case.
|
|
func (v AbsOutputValue) ModuleCallOutput() (ModuleInstance, ModuleCallInstanceOutput) {
|
|
if v.Module.IsRoot() {
|
|
panic("ReferenceFromCall used with root module output")
|
|
}
|
|
|
|
caller, call := v.Module.CallInstance()
|
|
return caller, ModuleCallInstanceOutput{
|
|
Call: call,
|
|
Name: v.OutputValue.Name,
|
|
}
|
|
}
|
|
|
|
// ConfigOutputValue represents a particular "output" block in the
|
|
// configuration, which might have many AbsOutputValue addresses associated
|
|
// with it at runtime if it belongs to a module that was called using
|
|
// "count" or "for_each".
|
|
type ConfigOutputValue struct {
|
|
Module Module
|
|
OutputValue OutputValue
|
|
}
|
|
|
|
func (v ConfigOutputValue) String() string {
|
|
if v.Module.IsRoot() {
|
|
return v.OutputValue.String()
|
|
}
|
|
return fmt.Sprintf("%s.%s", v.Module.String(), v.OutputValue.String())
|
|
}
|
|
|
|
func (v ConfigOutputValue) configCheckableSigil() {
|
|
// ConfigOutputValue is the ConfigCheckable for AbsOutputValue.
|
|
}
|
|
|
|
func (v ConfigOutputValue) CheckableKind() CheckableKind {
|
|
return CheckableOutputValue
|
|
}
|
|
|
|
func (v ConfigOutputValue) UniqueKey() UniqueKey {
|
|
return configOutputValueUniqueKey(v.String())
|
|
}
|
|
|
|
type configOutputValueUniqueKey string
|
|
|
|
func (k configOutputValueUniqueKey) uniqueKeySigil() {}
|