| package addrs |
| |
| import ( |
| "fmt" |
| |
| "github.com/hashicorp/hcl/v2" |
| "github.com/hashicorp/hcl/v2/hclsyntax" |
| "github.com/hashicorp/terraform/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. Since output |
| // values cannot be represented from the module where they are defined, |
| // OutputValue is not Referenceable, while ModuleCallOutput is. |
| type OutputValue struct { |
| Name string |
| } |
| |
| func (v OutputValue) String() string { |
| return "output." + v.Name |
| } |
| |
| // 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) Check(t CheckType, i int) Check { |
| return Check{ |
| 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 == 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() {} |