Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix partial address representation in plan for deferred actions #34966

Draft
wants to merge 1 commit into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 1 addition & 1 deletion go.mod
Expand Up @@ -34,7 +34,7 @@ require (
github.com/hashicorp/go-uuid v1.0.3
github.com/hashicorp/go-version v1.6.0
github.com/hashicorp/hcl v1.0.0
github.com/hashicorp/hcl/v2 v2.20.0
github.com/hashicorp/hcl/v2 v2.20.2-0.20240408171805-5fdb81b0624a
github.com/hashicorp/jsonapi v1.3.1
github.com/hashicorp/terraform-registry-address v0.2.3
github.com/hashicorp/terraform-svchost v0.1.1
Expand Down
8 changes: 2 additions & 6 deletions go.sum
Expand Up @@ -279,8 +279,6 @@ github.com/antchfx/xpath v1.1.10/go.mod h1:Yee4kTMuNiPYJ7nSNorELQMr1J33uOpXDMByN
github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY=
github.com/apparentlymart/go-cidr v1.1.0 h1:2mAhrMoF+nhXqxTzSZMUzDHkLjmIHC+Zzn4tdgBZjnU=
github.com/apparentlymart/go-cidr v1.1.0/go.mod h1:EBcsNrHc3zQeuaeCeCtQruQm+n9/YjEn/vI25Lg7Gwc=
github.com/apparentlymart/go-dump v0.0.0-20180507223929-23540a00eaa3 h1:ZSTrOEhiM5J5RFxEaFvMZVEAM1KvT1YzbEOwB2EAGjA=
github.com/apparentlymart/go-dump v0.0.0-20180507223929-23540a00eaa3/go.mod h1:oL81AME2rN47vu18xqj1S1jPIPuN7afo62yKTNn3XMM=
github.com/apparentlymart/go-shquot v0.0.1 h1:MGV8lwxF4zw75lN7e0MGs7o6AFYn7L6AZaExUpLh0Mo=
github.com/apparentlymart/go-shquot v0.0.1/go.mod h1:lw58XsE5IgUXZ9h0cxnypdx31p9mPFIVEQ9P3c7MlrU=
github.com/apparentlymart/go-textseg/v15 v15.0.0 h1:uYvfpb3DyLSCGWnctWKGj857c6ew1u1fNQOlOtuGxQY=
Expand Down Expand Up @@ -705,8 +703,8 @@ github.com/hashicorp/golang-lru v0.5.1 h1:0hERBMJE1eitiLkihrMvRVBYAkpHzc/J3QdDN+
github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
github.com/hashicorp/hcl/v2 v2.20.0 h1:l++cRs/5jQOiKVvqXZm/P1ZEfVXJmvLS9WSVxkaeTb4=
github.com/hashicorp/hcl/v2 v2.20.0/go.mod h1:WmcD/Ym72MDOOx5F62Ly+leloeu6H7m0pG7VBiU6pQk=
github.com/hashicorp/hcl/v2 v2.20.2-0.20240408171805-5fdb81b0624a h1:pIZ61qH0Iq0v+GPTsnWlx46HDgvDQBysVjtCRX0ojpw=
github.com/hashicorp/hcl/v2 v2.20.2-0.20240408171805-5fdb81b0624a/go.mod h1:TZDqQ4kNKCbh1iJp99FdPiUaVDDUPivbqxZulxDYqL4=
github.com/hashicorp/jsonapi v1.3.1 h1:GtPvnmcWgYwCuDGvYT5VZBHcUyFdq9lSyCzDjn1DdPo=
github.com/hashicorp/jsonapi v1.3.1/go.mod h1:kWfdn49yCjQvbpnvY1dxxAuAFzISwrrMDQOcu6NsFoM=
github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64=
Expand Down Expand Up @@ -961,8 +959,6 @@ github.com/satori/go.uuid v1.2.0 h1:0uYX9dsZ2yD7q2RtLRtPSdGDWzjeM3TbMJP9utgA0ww=
github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0=
github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529 h1:nn5Wsu0esKSJiIVhscUtVbo7ada43DJhG55ua/hjS5I=
github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc=
github.com/sergi/go-diff v1.2.0 h1:XU+rvMAioB0UC3q1MFrIQy4Vo5/4VsRDQQXHsEya6xQ=
github.com/sergi/go-diff v1.2.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM=
github.com/shopspring/decimal v1.2.0/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o=
github.com/shopspring/decimal v1.3.1 h1:2Usl1nmF/WZucqkFZhnfFYxxxu8LG21F6nPQBE5gKV8=
github.com/shopspring/decimal v1.3.1/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o=
Expand Down
4 changes: 2 additions & 2 deletions internal/addrs/checkable.go
Expand Up @@ -100,7 +100,7 @@ func ParseCheckableStr(kind CheckableKind, src string) (Checkable, tfdiags.Diagn
return nil, diags
}

path, remain, diags := parseModuleInstancePrefix(traversal)
path, remain, diags := parseModuleInstancePrefix(traversal, false)
if diags.HasErrors() {
return nil, diags
}
Expand Down Expand Up @@ -157,7 +157,7 @@ func ParseCheckableStr(kind CheckableKind, src string) (Checkable, tfdiags.Diagn
// might be a resource whose type is literally "output".
switch kind {
case CheckableResource:
riAddr, moreDiags := parseResourceInstanceUnderModule(path, remain)
riAddr, moreDiags := parseResourceInstanceUnderModule(path, false, remain)
diags = diags.Append(moreDiags)
if diags.HasErrors() {
return nil, diags
Expand Down
18 changes: 17 additions & 1 deletion internal/addrs/instance_key.go
Expand Up @@ -56,7 +56,23 @@ var NoKey InstanceKey
// WildcardKey represents the "unknown" value of an InstanceKey. This is used
// within the deferral logic to express absolute module and resource addresses
// that are not known at the time of planning.
var WildcardKey InstanceKey = StringKey("*")
var WildcardKey InstanceKey = &wildcardKey{}

// wildcardKey is a special kind of InstanceKey that represents the "unknown"
// value of an InstanceKey. This is used within the deferral logic to express
// absolute module and resource addresses that are not known at the time of
// planning.
type wildcardKey struct{}

func (w *wildcardKey) instanceKeySigil() {}

func (w *wildcardKey) String() string {
return "[*]"
}

func (w *wildcardKey) Value() cty.Value {
return cty.DynamicVal
}

// IntKey is the InstanceKey representation representing integer indices, as
// used when the "count" argument is specified or if for_each is used with
Expand Down
13 changes: 10 additions & 3 deletions internal/addrs/module_instance.go
Expand Up @@ -30,7 +30,7 @@ var (
)

func ParseModuleInstance(traversal hcl.Traversal) (ModuleInstance, tfdiags.Diagnostics) {
mi, remain, diags := parseModuleInstancePrefix(traversal)
mi, remain, diags := parseModuleInstancePrefix(traversal, false)
if len(remain) != 0 {
if len(remain) == len(traversal) {
diags = diags.Append(&hcl.Diagnostic{
Expand Down Expand Up @@ -80,7 +80,7 @@ func ParseModuleInstanceStr(str string) (ModuleInstance, tfdiags.Diagnostics) {
return addr, diags
}

func parseModuleInstancePrefix(traversal hcl.Traversal) (ModuleInstance, hcl.Traversal, tfdiags.Diagnostics) {
func parseModuleInstancePrefix(traversal hcl.Traversal, allowPartial bool) (ModuleInstance, hcl.Traversal, tfdiags.Diagnostics) {
remain := traversal
var mi ModuleInstance
var diags tfdiags.Diagnostics
Expand Down Expand Up @@ -141,7 +141,8 @@ LOOP:
}

if len(remain) > 0 {
if idx, ok := remain[0].(hcl.TraverseIndex); ok {
switch idx := remain[0].(type) {
case hcl.TraverseIndex:
remain = remain[1:]

switch idx.Key.Type() {
Expand Down Expand Up @@ -169,6 +170,12 @@ LOOP:
Subject: idx.SourceRange().Ptr(),
})
}

case hcl.TraverseSplat:
if allowPartial {
remain = remain[1:]
step.InstanceKey = WildcardKey
}
}
}

Expand Down
5 changes: 3 additions & 2 deletions internal/addrs/move_endpoint.go
Expand Up @@ -7,6 +7,7 @@ import (
"fmt"

"github.com/hashicorp/hcl/v2"

"github.com/hashicorp/terraform/internal/tfdiags"
)

Expand Down Expand Up @@ -126,7 +127,7 @@ func (e *MoveEndpoint) ConfigMoveable(baseModule Module) ConfigMoveable {
// it with the address of the module where it was declared in order to get
// an absolute address relative to the root module.
func ParseMoveEndpoint(traversal hcl.Traversal) (*MoveEndpoint, tfdiags.Diagnostics) {
path, remain, diags := parseModuleInstancePrefix(traversal)
path, remain, diags := parseModuleInstancePrefix(traversal, false)
if diags.HasErrors() {
return nil, diags
}
Expand All @@ -140,7 +141,7 @@ func ParseMoveEndpoint(traversal hcl.Traversal) (*MoveEndpoint, tfdiags.Diagnost
}, diags
}

riAddr, moreDiags := parseResourceInstanceUnderModule(path, remain)
riAddr, moreDiags := parseResourceInstanceUnderModule(path, false, remain)
diags = diags.Append(moreDiags)
if diags.HasErrors() {
return nil, diags
Expand Down
2 changes: 1 addition & 1 deletion internal/addrs/output_value.go
Expand Up @@ -126,7 +126,7 @@ type absOutputValueUniqueKey string
func (k absOutputValueUniqueKey) uniqueKeySigil() {}

func ParseAbsOutputValue(traversal hcl.Traversal) (AbsOutputValue, tfdiags.Diagnostics) {
path, remain, diags := parseModuleInstancePrefix(traversal)
path, remain, diags := parseModuleInstancePrefix(traversal, false)
if diags.HasErrors() {
return AbsOutputValue{}, diags
}
Expand Down
98 changes: 89 additions & 9 deletions internal/addrs/parse_target.go
Expand Up @@ -6,9 +6,9 @@ package addrs
import (
"fmt"

"github.com/hashicorp/hcl/v2"
"github.com/hashicorp/hcl/v2/hclsyntax"

"github.com/hashicorp/hcl/v2"
"github.com/hashicorp/terraform/internal/tfdiags"
)

Expand All @@ -28,7 +28,19 @@ type Target struct {
// If error diagnostics are returned then the Target value is invalid and
// must not be used.
func ParseTarget(traversal hcl.Traversal) (*Target, tfdiags.Diagnostics) {
path, remain, diags := parseModuleInstancePrefix(traversal)
return parseTarget(traversal, false)
}

// ParsePartialTarget is like ParseTarget, but it allows the given traversal
// to support the [*] wildcard syntax for resource instances. These indicate
// a "partial" resource address that refers to all potential instances of a
// resource or module.
func ParsePartialTarget(traversal hcl.Traversal) (*Target, tfdiags.Diagnostics) {
return parseTarget(traversal, true)
}

func parseTarget(traversal hcl.Traversal, allowPartial bool) (*Target, tfdiags.Diagnostics) {
path, remain, diags := parseModuleInstancePrefix(traversal, allowPartial)
if diags.HasErrors() {
return nil, diags
}
Expand All @@ -42,7 +54,7 @@ func ParseTarget(traversal hcl.Traversal) (*Target, tfdiags.Diagnostics) {
}, diags
}

riAddr, moreDiags := parseResourceInstanceUnderModule(path, remain)
riAddr, moreDiags := parseResourceInstanceUnderModule(path, allowPartial, remain)
diags = diags.Append(moreDiags)
if diags.HasErrors() {
return nil, diags
Expand Down Expand Up @@ -150,7 +162,7 @@ func parseConfigResourceUnderModule(moduleAddr Module, remain hcl.Traversal) (Co
}, diags
}

func parseResourceInstanceUnderModule(moduleAddr ModuleInstance, remain hcl.Traversal) (AbsResourceInstance, tfdiags.Diagnostics) {
func parseResourceInstanceUnderModule(moduleAddr ModuleInstance, allowPartial bool, remain hcl.Traversal) (AbsResourceInstance, tfdiags.Diagnostics) {
// Note that this helper is used as part of both ParseTarget and
// ParseMoveEndpoint, so its error messages should be generic
// enough to suit both situations.
Expand Down Expand Up @@ -219,7 +231,8 @@ func parseResourceInstanceUnderModule(moduleAddr ModuleInstance, remain hcl.Trav
case 0:
return moduleAddr.ResourceInstance(mode, typeName, name, NoKey), diags
case 1:
if tt, ok := remain[0].(hcl.TraverseIndex); ok {
switch tt := remain[0].(type) {
case hcl.TraverseIndex:
key, err := ParseInstanceKey(tt.Key)
if err != nil {
diags = diags.Append(&hcl.Diagnostic{
Expand All @@ -232,7 +245,20 @@ func parseResourceInstanceUnderModule(moduleAddr ModuleInstance, remain hcl.Trav
}

return moduleAddr.ResourceInstance(mode, typeName, name, key), diags
} else {
case hcl.TraverseSplat:
if allowPartial {
return moduleAddr.ResourceInstance(mode, typeName, name, WildcardKey), diags
}

// Otherwise, return an error.
diags = diags.Append(&hcl.Diagnostic{
Severity: hcl.DiagError,
Summary: "Invalid address",
Detail: "Resource instance key must be given in square brackets.",
Subject: remain[0].SourceRange().Ptr(),
})
return AbsResourceInstance{}, diags
default:
diags = diags.Append(&hcl.Diagnostic{
Severity: hcl.DiagError,
Summary: "Invalid address",
Expand Down Expand Up @@ -369,11 +395,39 @@ func ParseAbsResourceStr(str string) (AbsResource, tfdiags.Diagnostics) {
// If error diagnostics are returned then the AbsResource value is invalid and
// must not be used.
func ParseAbsResourceInstance(traversal hcl.Traversal) (AbsResourceInstance, tfdiags.Diagnostics) {
addr, diags := ParseTarget(traversal)
target, diags := ParseTarget(traversal)
if diags.HasErrors() {
return AbsResourceInstance{}, diags
}

addr, validateDiags := validateResourceFromTarget(target, traversal.SourceRange().Ptr())
diags = diags.Append(validateDiags)
return addr, diags
}

// ParsePartialResourceInstance attempts to interpret the given traversal as a
// partial absolute resource instance address, using the same syntax as expected
// by ParsePartialTarget.
//
// If no error diagnostics are returned, the returned target includes the
// address that was extracted and the source range it was extracted from.
//
// If error diagnostics are returned then the AbsResource value is invalid and
// must not be used.
func ParsePartialResourceInstance(traversal hcl.Traversal) (AbsResourceInstance, tfdiags.Diagnostics) {
target, diags := ParsePartialTarget(traversal)
if diags.HasErrors() {
return AbsResourceInstance{}, diags
}

addr, validateDiags := validateResourceFromTarget(target, traversal.SourceRange().Ptr())
diags = diags.Append(validateDiags)
return addr, diags
}

func validateResourceFromTarget(addr *Target, src *hcl.Range) (AbsResourceInstance, tfdiags.Diagnostics) {
var diags tfdiags.Diagnostics

switch tt := addr.Subject.(type) {

case AbsResource:
Expand All @@ -387,7 +441,7 @@ func ParseAbsResourceInstance(traversal hcl.Traversal) (AbsResourceInstance, tfd
Severity: hcl.DiagError,
Summary: "Invalid address",
Detail: "A resource instance address is required here. The module path must be followed by a resource instance specification.",
Subject: traversal.SourceRange().Ptr(),
Subject: src,
})
return AbsResourceInstance{}, diags

Expand All @@ -396,7 +450,7 @@ func ParseAbsResourceInstance(traversal hcl.Traversal) (AbsResourceInstance, tfd
Severity: hcl.DiagError,
Summary: "Invalid address",
Detail: "A resource address is required here.",
Subject: traversal.SourceRange().Ptr(),
Subject: src,
})
return AbsResourceInstance{}, diags

Expand Down Expand Up @@ -429,6 +483,32 @@ func ParseAbsResourceInstanceStr(str string) (AbsResourceInstance, tfdiags.Diagn
return addr, diags
}

// ParsePartialResourceInstanceStr is a helper wrapper around
// ParsePartialResourceInstance that takes a string and parses it with the HCL
// native syntax traversal parser before interpreting it.
//
// Error diagnostics are returned if either the parsing fails or the analysis
// of the traversal fails. There is no way for the caller to distinguish the
// two kinds of diagnostics programmatically. If error diagnostics are returned
// the returned address may be incomplete.
//
// Since this function has no context about the source of the given string,
// any returned diagnostics will not have meaningful source location
// information.
func ParsePartialResourceInstanceStr(str string) (AbsResourceInstance, tfdiags.Diagnostics) {
var diags tfdiags.Diagnostics

traversal, parseDiags := hclsyntax.ParseTraversalPartial([]byte(str), "", hcl.Pos{Line: 1, Column: 1})
diags = diags.Append(parseDiags)
if parseDiags.HasErrors() {
return AbsResourceInstance{}, diags
}

addr, addrDiags := ParsePartialResourceInstance(traversal)
diags = diags.Append(addrDiags)
return addr, diags
}

// ModuleAddr returns the module address portion of the subject of
// the recieving target.
//
Expand Down
7 changes: 4 additions & 3 deletions internal/addrs/provider_config.go
Expand Up @@ -7,9 +7,10 @@ import (
"fmt"
"strings"

"github.com/hashicorp/terraform/internal/tfdiags"
"github.com/zclconf/go-cty/cty"

"github.com/hashicorp/terraform/internal/tfdiags"

"github.com/hashicorp/hcl/v2"
"github.com/hashicorp/hcl/v2/hclsyntax"
)
Expand Down Expand Up @@ -121,7 +122,7 @@ var _ UniqueKeyer = AbsProviderConfig{}
// This type of address is typically not used prominently in the UI, except in
// error messages that refer to provider configurations.
func ParseAbsProviderConfig(traversal hcl.Traversal) (AbsProviderConfig, tfdiags.Diagnostics) {
modInst, remain, diags := parseModuleInstancePrefix(traversal)
modInst, remain, diags := parseModuleInstancePrefix(traversal, false)
var ret AbsProviderConfig

// Providers cannot resolve within module instances, so verify that there
Expand Down Expand Up @@ -260,7 +261,7 @@ func ParseLegacyAbsProviderConfigStr(str string) (AbsProviderConfig, tfdiags.Dia
//
// We will not use this address form for any new file formats.
func ParseLegacyAbsProviderConfig(traversal hcl.Traversal) (AbsProviderConfig, tfdiags.Diagnostics) {
modInst, remain, diags := parseModuleInstancePrefix(traversal)
modInst, remain, diags := parseModuleInstancePrefix(traversal, false)
var ret AbsProviderConfig

// Providers cannot resolve within module instances, so verify that there
Expand Down
2 changes: 1 addition & 1 deletion internal/backend/remote-state/azure/go.mod
Expand Up @@ -40,7 +40,7 @@ require (
github.com/hashicorp/go-retryablehttp v0.7.5 // indirect
github.com/hashicorp/go-slug v0.14.0 // indirect
github.com/hashicorp/go-version v1.6.0 // indirect
github.com/hashicorp/hcl/v2 v2.20.0 // indirect
github.com/hashicorp/hcl/v2 v2.20.2-0.20240408171805-5fdb81b0624a // indirect
github.com/hashicorp/terraform-registry-address v0.2.3 // indirect
github.com/hashicorp/terraform-svchost v0.1.1 // indirect
github.com/kr/pretty v0.3.0 // indirect
Expand Down
1 change: 1 addition & 0 deletions internal/backend/remote-state/azure/go.sum
Expand Up @@ -243,6 +243,7 @@ github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ
github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
github.com/hashicorp/hcl/v2 v2.20.0 h1:l++cRs/5jQOiKVvqXZm/P1ZEfVXJmvLS9WSVxkaeTb4=
github.com/hashicorp/hcl/v2 v2.20.0/go.mod h1:WmcD/Ym72MDOOx5F62Ly+leloeu6H7m0pG7VBiU6pQk=
github.com/hashicorp/hcl/v2 v2.20.2-0.20240408171805-5fdb81b0624a/go.mod h1:TZDqQ4kNKCbh1iJp99FdPiUaVDDUPivbqxZulxDYqL4=
github.com/hashicorp/terraform-registry-address v0.2.3 h1:2TAiKJ1A3MAkZlH1YI/aTVcLZRu7JseiXNRHbOAyoTI=
github.com/hashicorp/terraform-registry-address v0.2.3/go.mod h1:lFHA76T8jfQteVfT7caREqguFrW3c4MFSPhZB7HHgUM=
github.com/hashicorp/terraform-svchost v0.1.1 h1:EZZimZ1GxdqFRinZ1tpJwVxxt49xc/S52uzrw4x0jKQ=
Expand Down
2 changes: 1 addition & 1 deletion internal/backend/remote-state/consul/go.mod
Expand Up @@ -28,7 +28,7 @@ require (
github.com/hashicorp/go-uuid v1.0.3 // indirect
github.com/hashicorp/go-version v1.6.0 // indirect
github.com/hashicorp/golang-lru v0.5.1 // indirect
github.com/hashicorp/hcl/v2 v2.20.0 // indirect
github.com/hashicorp/hcl/v2 v2.20.2-0.20240408171805-5fdb81b0624a // indirect
github.com/hashicorp/serf v0.9.6 // indirect
github.com/hashicorp/terraform-registry-address v0.2.3 // indirect
github.com/hashicorp/terraform-svchost v0.1.1 // indirect
Expand Down
1 change: 1 addition & 0 deletions internal/backend/remote-state/consul/go.sum
Expand Up @@ -217,6 +217,7 @@ github.com/hashicorp/golang-lru v0.5.1 h1:0hERBMJE1eitiLkihrMvRVBYAkpHzc/J3QdDN+
github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
github.com/hashicorp/hcl/v2 v2.20.0 h1:l++cRs/5jQOiKVvqXZm/P1ZEfVXJmvLS9WSVxkaeTb4=
github.com/hashicorp/hcl/v2 v2.20.0/go.mod h1:WmcD/Ym72MDOOx5F62Ly+leloeu6H7m0pG7VBiU6pQk=
github.com/hashicorp/hcl/v2 v2.20.2-0.20240408171805-5fdb81b0624a/go.mod h1:TZDqQ4kNKCbh1iJp99FdPiUaVDDUPivbqxZulxDYqL4=
github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64=
github.com/hashicorp/mdns v1.0.4/go.mod h1:mtBihi+LeNXGtG8L9dX59gAEa12BDtBQSp4v/YAJqrc=
github.com/hashicorp/memberlist v0.3.0 h1:8+567mCcFDnS5ADl7lrpxPMWiFCElyUEeW0gtj34fMA=
Expand Down