mirror of
https://github.com/opentofu/opentofu.git
synced 2025-02-25 18:45:20 -06:00
Allow known references in import blocks (#1105)
Signed-off-by: Christian Mesh <christianmesh1@gmail.com>
This commit is contained in:
parent
5071fdc233
commit
c996be8d7d
@ -18,6 +18,7 @@ ENHANCEMENTS:
|
||||
|
||||
BUG FIXES:
|
||||
* `tofu test` resources cleanup at the end of tests changed to use simple reverse run block order. ([#1043](https://github.com/opentofu/opentofu/pull/1043))
|
||||
* Fix access to known references when using a import block for module resources ([#1105](https://github.com/opentofu/opentofu/pull/1105))
|
||||
|
||||
## Previous Releases
|
||||
|
||||
|
@ -4631,6 +4631,39 @@ func TestContext2Plan_importIdVariable(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestContext2Plan_importIdReference(t *testing.T) {
|
||||
p := testProvider("aws")
|
||||
m := testModule(t, "import-id-reference")
|
||||
ctx := testContext2(t, &ContextOpts{
|
||||
Providers: map[addrs.Provider]providers.Factory{
|
||||
addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p),
|
||||
},
|
||||
})
|
||||
|
||||
p.ImportResourceStateResponse = &providers.ImportResourceStateResponse{
|
||||
ImportedResources: []providers.ImportedResource{
|
||||
{
|
||||
TypeName: "aws_instance",
|
||||
State: cty.ObjectVal(map[string]cty.Value{
|
||||
"id": cty.StringVal("foo"),
|
||||
}),
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
_, diags := ctx.Plan(m, states.NewState(), &PlanOpts{
|
||||
SetVariables: InputValues{
|
||||
"the_id": &InputValue{
|
||||
// let var take its default value
|
||||
Value: cty.NilVal,
|
||||
},
|
||||
},
|
||||
})
|
||||
if diags.HasErrors() {
|
||||
t.Fatalf("unexpected errors: %s", diags.Err())
|
||||
}
|
||||
}
|
||||
|
||||
func TestContext2Plan_importIdFunc(t *testing.T) {
|
||||
p := testProvider("aws")
|
||||
m := testModule(t, "import-id-func")
|
||||
|
@ -4,6 +4,7 @@ import (
|
||||
"fmt"
|
||||
|
||||
"github.com/hashicorp/hcl/v2"
|
||||
"github.com/opentofu/opentofu/internal/addrs"
|
||||
"github.com/opentofu/opentofu/internal/lang/marks"
|
||||
"github.com/opentofu/opentofu/internal/tfdiags"
|
||||
"github.com/zclconf/go-cty/cty"
|
||||
@ -22,6 +23,10 @@ func evaluateImportIdExpression(expr hcl.Expression, ctx EvalContext) (string, t
|
||||
})
|
||||
}
|
||||
|
||||
// The import expression is declared within the root module
|
||||
// We need to explicitly use that context
|
||||
ctx = ctx.WithPath(addrs.RootModuleInstance)
|
||||
|
||||
importIdVal, evalDiags := ctx.EvaluateExpr(expr, cty.String, nil)
|
||||
diags = diags.Append(evalDiags)
|
||||
|
||||
|
@ -111,6 +111,7 @@ var (
|
||||
_ GraphNodeModuleInstance = (*NodeAbstractResourceInstance)(nil)
|
||||
_ GraphNodeReferenceable = (*NodeAbstractResourceInstance)(nil)
|
||||
_ GraphNodeReferencer = (*NodeAbstractResourceInstance)(nil)
|
||||
_ GraphNodeRootReferencer = (*NodeAbstractResourceInstance)(nil)
|
||||
_ GraphNodeProviderConsumer = (*NodeAbstractResourceInstance)(nil)
|
||||
_ GraphNodeProvisionerConsumer = (*NodeAbstractResourceInstance)(nil)
|
||||
_ GraphNodeConfigResource = (*NodeAbstractResourceInstance)(nil)
|
||||
@ -205,12 +206,18 @@ func (n *NodeAbstractResource) References() []*addrs.Reference {
|
||||
}
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
func (n *NodeAbstractResource) RootReferences() []*addrs.Reference {
|
||||
var root []*addrs.Reference
|
||||
|
||||
for _, importTarget := range n.importTargets {
|
||||
refs, _ := lang.ReferencesInExpr(addrs.ParseRef, importTarget.ID)
|
||||
result = append(result, refs...)
|
||||
root = append(root, refs...)
|
||||
}
|
||||
|
||||
return result
|
||||
return root
|
||||
}
|
||||
|
||||
func (n *NodeAbstractResource) DependsOn() []*addrs.Reference {
|
||||
|
13
internal/tofu/testdata/import-id-reference/main.tf
vendored
Normal file
13
internal/tofu/testdata/import-id-reference/main.tf
vendored
Normal file
@ -0,0 +1,13 @@
|
||||
variable "the_id" {
|
||||
default = "123"
|
||||
}
|
||||
|
||||
module "refmod" {
|
||||
source = "./mod"
|
||||
}
|
||||
|
||||
import {
|
||||
to = module.refmod.aws_instance.foo
|
||||
id = var.the_id
|
||||
}
|
||||
|
2
internal/tofu/testdata/import-id-reference/mod/mod.tf
vendored
Normal file
2
internal/tofu/testdata/import-id-reference/mod/mod.tf
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
resource "aws_instance" "foo" {
|
||||
}
|
@ -42,6 +42,14 @@ type GraphNodeReferencer interface {
|
||||
References() []*addrs.Reference
|
||||
}
|
||||
|
||||
// GraphNodeRootReferencer is implemented by nodes that reference the root
|
||||
// module, for example module imports
|
||||
type GraphNodeRootReferencer interface {
|
||||
GraphNodeReferencer
|
||||
|
||||
RootReferences() []*addrs.Reference
|
||||
}
|
||||
|
||||
type GraphNodeAttachDependencies interface {
|
||||
GraphNodeConfigResource
|
||||
AttachDependencies([]addrs.ConfigResource)
|
||||
@ -295,40 +303,55 @@ func (m ReferenceMap) References(v dag.Vertex) []dag.Vertex {
|
||||
|
||||
var matches []dag.Vertex
|
||||
|
||||
for _, ref := range rn.References() {
|
||||
subject := ref.Subject
|
||||
|
||||
key := m.referenceMapKey(v, subject)
|
||||
if _, exists := m[key]; !exists {
|
||||
// If what we were looking for was a ResourceInstance then we
|
||||
// might be in a resource-oriented graph rather than an
|
||||
// instance-oriented graph, and so we'll see if we have the
|
||||
// resource itself instead.
|
||||
switch ri := subject.(type) {
|
||||
case addrs.ResourceInstance:
|
||||
subject = ri.ContainingResource()
|
||||
case addrs.ResourceInstancePhase:
|
||||
subject = ri.ContainingResource()
|
||||
case addrs.ModuleCallInstanceOutput:
|
||||
subject = ri.ModuleCallOutput()
|
||||
case addrs.ModuleCallInstance:
|
||||
subject = ri.Call
|
||||
default:
|
||||
log.Printf("[INFO] ReferenceTransformer: reference not found: %q", subject)
|
||||
continue
|
||||
}
|
||||
key = m.referenceMapKey(v, subject)
|
||||
}
|
||||
vertices := m[key]
|
||||
for _, rv := range vertices {
|
||||
// don't include self-references
|
||||
if rv == v {
|
||||
continue
|
||||
}
|
||||
matches = append(matches, rv)
|
||||
if rrn, ok := rn.(GraphNodeRootReferencer); ok {
|
||||
for _, ref := range rrn.RootReferences() {
|
||||
matches = append(matches, m.addReference(addrs.RootModule, v, ref)...)
|
||||
}
|
||||
}
|
||||
|
||||
for _, ref := range rn.References() {
|
||||
matches = append(matches, m.addReference(vertexReferencePath(v), v, ref)...)
|
||||
}
|
||||
|
||||
return matches
|
||||
}
|
||||
|
||||
// addReferences returns the set of vertices that the given reference requires
|
||||
// within a given module. It additionally excludes the current vertex.
|
||||
func (m ReferenceMap) addReference(path addrs.Module, current dag.Vertex, ref *addrs.Reference) []dag.Vertex {
|
||||
var matches []dag.Vertex
|
||||
|
||||
subject := ref.Subject
|
||||
|
||||
key := m.mapKey(path, subject)
|
||||
if _, exists := m[key]; !exists {
|
||||
// If what we were looking for was a ResourceInstance then we
|
||||
// might be in a resource-oriented graph rather than an
|
||||
// instance-oriented graph, and so we'll see if we have the
|
||||
// resource itself instead.
|
||||
switch ri := subject.(type) {
|
||||
case addrs.ResourceInstance:
|
||||
subject = ri.ContainingResource()
|
||||
case addrs.ResourceInstancePhase:
|
||||
subject = ri.ContainingResource()
|
||||
case addrs.ModuleCallInstanceOutput:
|
||||
subject = ri.ModuleCallOutput()
|
||||
case addrs.ModuleCallInstance:
|
||||
subject = ri.Call
|
||||
default:
|
||||
log.Printf("[INFO] ReferenceTransformer: reference not found: %q", subject)
|
||||
return nil
|
||||
}
|
||||
key = m.mapKey(path, subject)
|
||||
}
|
||||
vertices := m[key]
|
||||
for _, rv := range vertices {
|
||||
// don't include self-references
|
||||
if rv == current {
|
||||
continue
|
||||
}
|
||||
matches = append(matches, rv)
|
||||
}
|
||||
return matches
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user