mirror of
https://github.com/opentofu/opentofu.git
synced 2025-02-25 18:45:20 -06:00
plannable import: write generated config to out flag (#33186)
* plannable import: write generated config to out flag * Add example command to diagnostic
This commit is contained in:
parent
2b71e9edf3
commit
d5fed58fc5
@ -295,6 +295,11 @@ type Operation struct {
|
||||
// Workspace is the name of the workspace that this operation should run
|
||||
// in, which controls which named state is used.
|
||||
Workspace string
|
||||
|
||||
// GenerateConfigOut tells the operation both that it should generate config
|
||||
// for unmatched import targets and where any generated config should be
|
||||
// written to.
|
||||
GenerateConfigOut string
|
||||
}
|
||||
|
||||
// HasConfig returns true if and only if the operation has a ConfigDir value
|
||||
|
@ -190,11 +190,12 @@ func (b *Local) localRunDirect(op *backend.Operation, run *backend.LocalRun, cor
|
||||
}
|
||||
|
||||
planOpts := &terraform.PlanOpts{
|
||||
Mode: op.PlanMode,
|
||||
Targets: op.Targets,
|
||||
ForceReplace: op.ForceReplace,
|
||||
SetVariables: variables,
|
||||
SkipRefresh: op.Type != backend.OperationTypeRefresh && !op.PlanRefresh,
|
||||
Mode: op.PlanMode,
|
||||
Targets: op.Targets,
|
||||
ForceReplace: op.ForceReplace,
|
||||
SetVariables: variables,
|
||||
SkipRefresh: op.Type != backend.OperationTypeRefresh && !op.PlanRefresh,
|
||||
GenerateConfig: len(op.GenerateConfigOut) > 0,
|
||||
}
|
||||
run.PlanOpts = planOpts
|
||||
|
||||
|
@ -9,6 +9,7 @@ import (
|
||||
"log"
|
||||
|
||||
"github.com/hashicorp/terraform/internal/backend"
|
||||
"github.com/hashicorp/terraform/internal/genconfig"
|
||||
"github.com/hashicorp/terraform/internal/logging"
|
||||
"github.com/hashicorp/terraform/internal/plans"
|
||||
"github.com/hashicorp/terraform/internal/plans/planfile"
|
||||
@ -53,6 +54,24 @@ func (b *Local) opPlan(
|
||||
return
|
||||
}
|
||||
|
||||
if len(op.GenerateConfigOut) > 0 {
|
||||
|
||||
if op.PlanMode != plans.NormalMode {
|
||||
diags = diags.Append(tfdiags.Sourceless(
|
||||
tfdiags.Error,
|
||||
"Invalid generate-config-out flag",
|
||||
"Config can only be generated during a normal plan operation, and not during a refresh-only or destroy plan."))
|
||||
op.ReportResult(runningOp, diags)
|
||||
return
|
||||
}
|
||||
|
||||
diags = diags.Append(genconfig.ValidateTargetFile(op.GenerateConfigOut))
|
||||
if diags.HasErrors() {
|
||||
op.ReportResult(runningOp, diags)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if b.ContextOpts == nil {
|
||||
b.ContextOpts = new(terraform.ContextOpts)
|
||||
}
|
||||
@ -171,6 +190,15 @@ func (b *Local) opPlan(
|
||||
op.ReportResult(runningOp, diags)
|
||||
return
|
||||
}
|
||||
|
||||
// Write out any generated config, before we render the plan.
|
||||
moreDiags = genconfig.MaybeWriteGeneratedConfig(plan, op.GenerateConfigOut)
|
||||
diags = diags.Append(moreDiags)
|
||||
if moreDiags.HasErrors() {
|
||||
op.ReportResult(runningOp, diags)
|
||||
return
|
||||
}
|
||||
|
||||
op.View.Plan(plan, schemas)
|
||||
|
||||
// If we've accumulated any diagnostics along the way then we'll show them
|
||||
|
@ -25,6 +25,11 @@ type Plan struct {
|
||||
// OutPath contains an optional path to store the plan file
|
||||
OutPath string
|
||||
|
||||
// GenerateConfigPath tells Terraform that config should be generated for
|
||||
// unmatched import target paths and which path the generated file should
|
||||
// be written to.
|
||||
GenerateConfigPath string
|
||||
|
||||
// ViewType specifies which output format to use
|
||||
ViewType ViewType
|
||||
}
|
||||
@ -44,6 +49,7 @@ func ParsePlan(args []string) (*Plan, tfdiags.Diagnostics) {
|
||||
cmdFlags.BoolVar(&plan.DetailedExitCode, "detailed-exitcode", false, "detailed-exitcode")
|
||||
cmdFlags.BoolVar(&plan.InputEnabled, "input", true, "input")
|
||||
cmdFlags.StringVar(&plan.OutPath, "out", "", "out")
|
||||
cmdFlags.StringVar(&plan.GenerateConfigPath, "generate-config-out", "", "generate-config-out")
|
||||
|
||||
var json bool
|
||||
cmdFlags.BoolVar(&json, "json", false, "json")
|
||||
|
@ -252,6 +252,24 @@ func testPlanFileNoop(t *testing.T) string {
|
||||
return testPlanFile(t, snap, state, plan)
|
||||
}
|
||||
|
||||
func testFileEquals(t *testing.T, got, want string) {
|
||||
t.Helper()
|
||||
|
||||
actual, err := os.ReadFile(got)
|
||||
if err != nil {
|
||||
t.Fatalf("error reading %s", got)
|
||||
}
|
||||
|
||||
expected, err := os.ReadFile(want)
|
||||
if err != nil {
|
||||
t.Fatalf("error reading %s", want)
|
||||
}
|
||||
|
||||
if diff := cmp.Diff(string(actual), string(expected)); len(diff) > 0 {
|
||||
t.Fatalf("got:\n%s\nwant:\n%s\ndiff:\n%s", actual, expected, diff)
|
||||
}
|
||||
}
|
||||
|
||||
func testReadPlan(t *testing.T, path string) *plans.Plan {
|
||||
t.Helper()
|
||||
|
||||
|
@ -75,7 +75,7 @@ func (c *PlanCommand) Run(rawArgs []string) int {
|
||||
}
|
||||
|
||||
// Build the operation request
|
||||
opReq, opDiags := c.OperationRequest(be, view, args.ViewType, args.Operation, args.OutPath)
|
||||
opReq, opDiags := c.OperationRequest(be, view, args.ViewType, args.Operation, args.OutPath, args.GenerateConfigPath)
|
||||
diags = diags.Append(opDiags)
|
||||
if diags.HasErrors() {
|
||||
view.Diagnostics(diags)
|
||||
@ -144,6 +144,7 @@ func (c *PlanCommand) OperationRequest(
|
||||
viewType arguments.ViewType,
|
||||
args *arguments.Operation,
|
||||
planOutPath string,
|
||||
generateConfigOut string,
|
||||
) (*backend.Operation, tfdiags.Diagnostics) {
|
||||
var diags tfdiags.Diagnostics
|
||||
|
||||
@ -154,6 +155,7 @@ func (c *PlanCommand) OperationRequest(
|
||||
opReq.Hooks = view.Hooks()
|
||||
opReq.PlanRefresh = args.Refresh
|
||||
opReq.PlanOutPath = planOutPath
|
||||
opReq.GenerateConfigOut = generateConfigOut
|
||||
opReq.Targets = args.Targets
|
||||
opReq.ForceReplace = args.ForceReplace
|
||||
opReq.Type = backend.OperationTypePlan
|
||||
|
@ -193,6 +193,47 @@ func TestPlan_noState(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestPlan_generatedConfigPath(t *testing.T) {
|
||||
td := t.TempDir()
|
||||
testCopyDir(t, testFixturePath("plan-import-config-gen"), td)
|
||||
defer testChdir(t, td)()
|
||||
|
||||
genPath := filepath.Join(td, "generated.tf")
|
||||
|
||||
p := planFixtureProvider()
|
||||
view, done := testView(t)
|
||||
|
||||
c := &PlanCommand{
|
||||
Meta: Meta{
|
||||
testingOverrides: metaOverridesForProvider(p),
|
||||
View: view,
|
||||
},
|
||||
}
|
||||
|
||||
p.ImportResourceStateResponse = &providers.ImportResourceStateResponse{
|
||||
ImportedResources: []providers.ImportedResource{
|
||||
{
|
||||
TypeName: "test_instance",
|
||||
State: cty.ObjectVal(map[string]cty.Value{
|
||||
"id": cty.StringVal("bar"),
|
||||
}),
|
||||
Private: nil,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
args := []string{
|
||||
"-generate-config-out", genPath,
|
||||
}
|
||||
code := c.Run(args)
|
||||
output := done(t)
|
||||
if code != 0 {
|
||||
t.Fatalf("bad: %d\n\n%s", code, output.Stderr())
|
||||
}
|
||||
|
||||
testFileEquals(t, genPath, filepath.Join(td, "generated.tf.expected"))
|
||||
}
|
||||
|
||||
func TestPlan_outPath(t *testing.T) {
|
||||
td := t.TempDir()
|
||||
testCopyDir(t, testFixturePath("plan"), td)
|
||||
|
8
internal/command/testdata/plan-import-config-gen/generated.tf.expected
vendored
Normal file
8
internal/command/testdata/plan-import-config-gen/generated.tf.expected
vendored
Normal file
@ -0,0 +1,8 @@
|
||||
# __generated__ by Terraform
|
||||
# Please review these resources and move them into your main configuration files.
|
||||
|
||||
# __generated__ by Terraform from "bar"
|
||||
resource "test_instance" "foo" {
|
||||
ami = null
|
||||
id = "bar"
|
||||
}
|
4
internal/command/testdata/plan-import-config-gen/main.tf
vendored
Normal file
4
internal/command/testdata/plan-import-config-gen/main.tf
vendored
Normal file
@ -0,0 +1,4 @@
|
||||
import {
|
||||
id = "bar"
|
||||
to = test_instance.foo
|
||||
}
|
81
internal/genconfig/generate_config_write.go
Normal file
81
internal/genconfig/generate_config_write.go
Normal file
@ -0,0 +1,81 @@
|
||||
package genconfig
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
|
||||
"github.com/hashicorp/terraform/internal/plans"
|
||||
"github.com/hashicorp/terraform/internal/tfdiags"
|
||||
)
|
||||
|
||||
func ValidateTargetFile(out string) tfdiags.Diagnostics {
|
||||
var diags tfdiags.Diagnostics
|
||||
if _, err := os.Stat(out); !os.IsNotExist(err) {
|
||||
diags = diags.Append(tfdiags.Sourceless(
|
||||
tfdiags.Error,
|
||||
"Target generated file already exists",
|
||||
"Terraform can only write generated config into a new file. Either choose a different target location or move all existing configuration out of the target file, delete it and try again."))
|
||||
|
||||
}
|
||||
return diags
|
||||
}
|
||||
|
||||
func MaybeWriteGeneratedConfig(plan *plans.Plan, out string) tfdiags.Diagnostics {
|
||||
if len(out) == 0 {
|
||||
// No specified out file, so don't write anything.
|
||||
return nil
|
||||
}
|
||||
|
||||
diags := ValidateTargetFile(out)
|
||||
if diags.HasErrors() {
|
||||
return diags
|
||||
}
|
||||
|
||||
var writer io.Writer
|
||||
|
||||
for _, change := range plan.Changes.Resources {
|
||||
if len(change.GeneratedConfig) > 0 {
|
||||
|
||||
if writer == nil {
|
||||
// Lazily create the generated file, in case we have no
|
||||
// generated config to create.
|
||||
var err error
|
||||
if writer, err = os.Create(out); err != nil {
|
||||
if os.IsPermission(err) {
|
||||
diags = diags.Append(tfdiags.Sourceless(
|
||||
tfdiags.Error,
|
||||
"Failed to create target generated file",
|
||||
fmt.Sprintf("Terraform did not have permission to create the generated file (%s) in the target directory. Please modify permissions over the target directory, and try again.", out)))
|
||||
return diags
|
||||
}
|
||||
|
||||
diags = diags.Append(tfdiags.Sourceless(
|
||||
tfdiags.Error,
|
||||
"Failed to create target generated file",
|
||||
fmt.Sprintf("Terraform could not create the generated file (%s) in the target directory: %v. Depending on the error message, this may be a bug in Terraform itself. If so, please report it!", out, err)))
|
||||
return diags
|
||||
}
|
||||
|
||||
header := "# __generated__ by Terraform\n# Please review these resources and move them into your main configuration files.\n"
|
||||
// Missing the header from the file, isn't the end of the world
|
||||
// so if this did return an error, then we will just ignore it.
|
||||
_, _ = writer.Write([]byte(header))
|
||||
}
|
||||
|
||||
header := "\n# __generated__ by Terraform"
|
||||
if change.Importing != nil && len(change.Importing.ID) > 0 {
|
||||
header += fmt.Sprintf(" from %q", change.Importing.ID)
|
||||
}
|
||||
header += "\n"
|
||||
if _, err := writer.Write([]byte(fmt.Sprintf("%s%s\n", header, change.GeneratedConfig))); err != nil {
|
||||
diags = diags.Append(tfdiags.Sourceless(
|
||||
tfdiags.Warning,
|
||||
"Failed to save generated config",
|
||||
fmt.Sprintf("Terraform encountered an error while writing generated config: %v. The config for %s must be created manually before applying. Depending on the error message, this may be a bug in Terraform itself. If so, please report it!", err, change.Addr.String())))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return diags
|
||||
}
|
@ -77,6 +77,10 @@ type PlanOpts struct {
|
||||
// ImportTargets is a list of target resources to import. These resources
|
||||
// will be added to the plan graph.
|
||||
ImportTargets []*ImportTarget
|
||||
|
||||
// GenerateConfig tells Terraform to generate configuration for any
|
||||
// ImportTargets that do not have configuration already.
|
||||
GenerateConfig bool
|
||||
}
|
||||
|
||||
// Plan generates an execution plan by comparing the given configuration
|
||||
@ -630,6 +634,7 @@ func (c *Context) planGraph(config *configs.Config, prevRunState *states.State,
|
||||
preDestroyRefresh: opts.PreDestroyRefresh,
|
||||
Operation: walkPlan,
|
||||
ImportTargets: opts.ImportTargets,
|
||||
GenerateConfig: opts.GenerateConfig,
|
||||
}).Build(addrs.RootModuleInstance)
|
||||
return graph, walkPlan, diags
|
||||
case plans.RefreshOnlyMode:
|
||||
|
@ -4512,7 +4512,10 @@ resource "test_object" "a" {
|
||||
},
|
||||
}
|
||||
|
||||
plan, diags := ctx.Plan(m, states.NewState(), DefaultPlanOpts)
|
||||
plan, diags := ctx.Plan(m, states.NewState(), &PlanOpts{
|
||||
Mode: plans.NormalMode,
|
||||
GenerateConfig: true,
|
||||
})
|
||||
if diags.HasErrors() {
|
||||
t.Fatalf("unexpected errors\n%s", diags.Err().Error())
|
||||
}
|
||||
@ -4569,7 +4572,10 @@ import {
|
||||
},
|
||||
}
|
||||
|
||||
plan, diags := ctx.Plan(m, states.NewState(), DefaultPlanOpts)
|
||||
plan, diags := ctx.Plan(m, states.NewState(), &PlanOpts{
|
||||
Mode: plans.NormalMode,
|
||||
GenerateConfig: true,
|
||||
})
|
||||
if diags.HasErrors() {
|
||||
t.Fatalf("unexpected errors\n%s", diags.Err().Error())
|
||||
}
|
||||
@ -4648,7 +4654,10 @@ import {
|
||||
},
|
||||
}
|
||||
|
||||
plan, diags := ctx.Plan(m, states.NewState(), DefaultPlanOpts)
|
||||
plan, diags := ctx.Plan(m, states.NewState(), &PlanOpts{
|
||||
Mode: plans.NormalMode,
|
||||
GenerateConfig: true,
|
||||
})
|
||||
if diags.HasErrors() {
|
||||
t.Fatalf("unexpected errors\n%s", diags.Err().Error())
|
||||
}
|
||||
|
@ -75,6 +75,10 @@ type PlanGraphBuilder struct {
|
||||
|
||||
// ImportTargets are the list of resources to import.
|
||||
ImportTargets []*ImportTarget
|
||||
|
||||
// GenerateConfig tells Terraform to generate config for any import targets
|
||||
// that do not already have configuration.
|
||||
GenerateConfig bool
|
||||
}
|
||||
|
||||
// See GraphBuilder
|
||||
@ -113,8 +117,7 @@ func (b *PlanGraphBuilder) Steps() []GraphTransformer {
|
||||
importTargets: b.ImportTargets,
|
||||
|
||||
// We only want to generate config during a plan operation.
|
||||
// TODO: add a dedicated flag for this?
|
||||
generateConfigForImportTargets: b.Operation == walkPlan,
|
||||
generateConfigForImportTargets: b.GenerateConfig,
|
||||
},
|
||||
|
||||
// Add dynamic values
|
||||
|
@ -78,6 +78,9 @@ type NodeAbstractResource struct {
|
||||
|
||||
// This resource may expand into instances which need to be imported.
|
||||
importTargets []*ImportTarget
|
||||
|
||||
// generateConfig tells this node that it's okay for it to generate config.
|
||||
generateConfig bool
|
||||
}
|
||||
|
||||
var (
|
||||
|
@ -651,7 +651,6 @@ func (n *NodeAbstractResourceInstance) plan(
|
||||
currentState *states.ResourceInstanceObject,
|
||||
createBeforeDestroy bool,
|
||||
forceReplace []addrs.AbsResourceInstance,
|
||||
generateConfig bool,
|
||||
) (*plans.ResourceInstanceChange, *states.ResourceInstanceObject, instances.RepetitionData, tfdiags.Diagnostics) {
|
||||
var diags tfdiags.Diagnostics
|
||||
var state *states.ResourceInstanceObject
|
||||
@ -678,7 +677,7 @@ func (n *NodeAbstractResourceInstance) plan(
|
||||
|
||||
// If we're importing and generating config, generate it now.
|
||||
var generatedHCL string
|
||||
if generateConfig {
|
||||
if n.generateConfig {
|
||||
var generatedDiags tfdiags.Diagnostics
|
||||
|
||||
if n.Config != nil {
|
||||
@ -722,6 +721,19 @@ func (n *NodeAbstractResourceInstance) plan(
|
||||
n.Config = generatedConfig
|
||||
}
|
||||
|
||||
if n.Config == nil {
|
||||
// This shouldn't happen. A node that isn't generating config should
|
||||
// have embedded config, and the rest of Terraform should enforce this.
|
||||
// If, however, we didn't do things correctly the next line will panic,
|
||||
// so let's not do that and return an error message with more context.
|
||||
|
||||
diags = diags.Append(tfdiags.Sourceless(
|
||||
tfdiags.Error,
|
||||
"Resource has no configuration",
|
||||
fmt.Sprintf("Terraform attempted to process a resource at %s that has no configuration. This is a bug in Terraform; please report it!", n.Addr.String())))
|
||||
return plan, state, keyData, diags
|
||||
}
|
||||
|
||||
config := *n.Config
|
||||
|
||||
checkRuleSeverity := tfdiags.Error
|
||||
|
@ -274,7 +274,7 @@ func (n *NodeApplyableResourceInstance) managedResourceExecute(ctx EvalContext)
|
||||
|
||||
// Make a new diff, in case we've learned new values in the state
|
||||
// during apply which we can now incorporate.
|
||||
diffApply, _, repeatData, planDiags := n.plan(ctx, diff, state, false, n.forceReplace, false)
|
||||
diffApply, _, repeatData, planDiags := n.plan(ctx, diff, state, false, n.forceReplace)
|
||||
diags = diags.Append(planDiags)
|
||||
if diags.HasErrors() {
|
||||
return diags
|
||||
|
@ -336,6 +336,7 @@ func (n *nodeExpandPlannableResource) resourceInstanceSubgraph(ctx EvalContext,
|
||||
a.dependsOn = n.dependsOn
|
||||
a.Dependencies = n.dependencies
|
||||
a.preDestroyRefresh = n.preDestroyRefresh
|
||||
a.generateConfig = n.generateConfig
|
||||
|
||||
m = &NodePlannableResourceInstance{
|
||||
NodeAbstractResourceInstance: a,
|
||||
|
@ -8,14 +8,15 @@ import (
|
||||
"log"
|
||||
"sort"
|
||||
|
||||
"github.com/hashicorp/hcl/v2"
|
||||
"github.com/zclconf/go-cty/cty"
|
||||
|
||||
"github.com/hashicorp/terraform/internal/addrs"
|
||||
"github.com/hashicorp/terraform/internal/instances"
|
||||
"github.com/hashicorp/terraform/internal/plans"
|
||||
"github.com/hashicorp/terraform/internal/providers"
|
||||
"github.com/hashicorp/terraform/internal/states"
|
||||
"github.com/hashicorp/terraform/internal/tfdiags"
|
||||
|
||||
"github.com/zclconf/go-cty/cty"
|
||||
)
|
||||
|
||||
// NodePlannableResourceInstance represents a _single_ resource
|
||||
@ -135,7 +136,6 @@ func (n *NodePlannableResourceInstance) managedResourceExecute(ctx EvalContext)
|
||||
|
||||
var change *plans.ResourceInstanceChange
|
||||
var instanceRefreshState *states.ResourceInstanceObject
|
||||
var generateConfig bool
|
||||
|
||||
checkRuleSeverity := tfdiags.Error
|
||||
if n.skipPlanChanges || n.preDestroyRefresh {
|
||||
@ -157,12 +157,32 @@ func (n *NodePlannableResourceInstance) managedResourceExecute(ctx EvalContext)
|
||||
|
||||
importing := n.importTarget.ID != ""
|
||||
|
||||
if importing && n.Config == nil && !n.generateConfig {
|
||||
// Then the user wrote an import target to a target that didn't exist.
|
||||
if n.Addr.Module.IsRoot() {
|
||||
diags = diags.Append(&hcl.Diagnostic{
|
||||
Severity: hcl.DiagError,
|
||||
Summary: "Import block target does not exist",
|
||||
Detail: "The target for the given import block does not exist. If you wish to automatically generate config for this resource, use the -generate-config-out option within terraform plan. Otherwise, make sure the target resource exists within your configuration. For example:\n\n terraform plan -generate-config-out=generated.tf",
|
||||
Subject: n.importTarget.Config.DeclRange.Ptr(),
|
||||
})
|
||||
} else {
|
||||
// You can't generate config for a resource that is inside a
|
||||
// module, so we will present a different error message for
|
||||
// this case.
|
||||
diags = diags.Append(&hcl.Diagnostic{
|
||||
Severity: hcl.DiagError,
|
||||
Summary: "Import block target does not exist",
|
||||
Detail: "The target for the given import block does not exist. The specified target is within a module, and must be defined as a resource within that module before anything can be imported.",
|
||||
Subject: n.importTarget.Config.DeclRange.Ptr(),
|
||||
})
|
||||
}
|
||||
return diags
|
||||
}
|
||||
|
||||
// If the resource is to be imported, we now ask the provider for an Import
|
||||
// and a Refresh, and save the resulting state to instanceRefreshState.
|
||||
if importing {
|
||||
if n.Config == nil || n.Config.Managed == nil {
|
||||
generateConfig = true
|
||||
}
|
||||
instanceRefreshState, diags = n.importState(ctx, addr, provider)
|
||||
} else {
|
||||
var readDiags tfdiags.Diagnostics
|
||||
@ -245,7 +265,7 @@ func (n *NodePlannableResourceInstance) managedResourceExecute(ctx EvalContext)
|
||||
}
|
||||
|
||||
change, instancePlanState, repeatData, planDiags := n.plan(
|
||||
ctx, change, instanceRefreshState, n.ForceCreateBeforeDestroy, n.forceReplace, generateConfig,
|
||||
ctx, change, instanceRefreshState, n.ForceCreateBeforeDestroy, n.forceReplace,
|
||||
)
|
||||
diags = diags.Append(planDiags)
|
||||
if diags.HasErrors() {
|
||||
|
@ -98,8 +98,13 @@ func (t *ConfigTransformer) transformSingle(g *Graph, config *configs.Config, ge
|
||||
}
|
||||
|
||||
// Take a copy of the import targets, so we can edit them as we go.
|
||||
// Only include import targets that are targeting the current module.
|
||||
var importTargets []*ImportTarget
|
||||
importTargets = append(importTargets, t.importTargets...)
|
||||
for _, target := range t.importTargets {
|
||||
if targetModule := target.Addr.Module.Module(); targetModule.Equal(config.Path) {
|
||||
importTargets = append(importTargets, target)
|
||||
}
|
||||
}
|
||||
|
||||
for _, r := range allResources {
|
||||
relAddr := r.Addr()
|
||||
@ -151,28 +156,32 @@ func (t *ConfigTransformer) transformSingle(g *Graph, config *configs.Config, ge
|
||||
g.Add(node)
|
||||
}
|
||||
|
||||
if generateConfig {
|
||||
// If any import targets were not claimed by resources, then we will
|
||||
// generate config for them.
|
||||
for _, i := range importTargets {
|
||||
if !i.Addr.Module.IsRoot() {
|
||||
// We only generate config for resources imported into the root
|
||||
// module.
|
||||
continue
|
||||
}
|
||||
|
||||
abstract := &NodeAbstractResource{
|
||||
Addr: i.Addr.ConfigResource(),
|
||||
importTargets: []*ImportTarget{i},
|
||||
}
|
||||
|
||||
var node dag.Vertex = abstract
|
||||
if f := t.Concrete; f != nil {
|
||||
node = f(abstract)
|
||||
}
|
||||
|
||||
g.Add(node)
|
||||
// If any import targets were not claimed by resources, then let's add them
|
||||
// into the graph now.
|
||||
//
|
||||
// We actually know that if any of the resources aren't claimed and
|
||||
// generateConfig is false, then we have a problem. But, we can't raise a
|
||||
// nice error message from this function.
|
||||
//
|
||||
// We'll add the nodes that we know will fail, and catch them again later
|
||||
// in the processing when we are in a position to raise a much more helpful
|
||||
// error message.
|
||||
//
|
||||
// TODO: We could actually catch and process these kind of problems earlier,
|
||||
// this is something that could be done during the Validate process.
|
||||
for _, i := range importTargets {
|
||||
abstract := &NodeAbstractResource{
|
||||
Addr: i.Addr.ConfigResource(),
|
||||
importTargets: []*ImportTarget{i},
|
||||
generateConfig: generateConfig,
|
||||
}
|
||||
|
||||
var node dag.Vertex = abstract
|
||||
if f := t.Concrete; f != nil {
|
||||
node = f(abstract)
|
||||
}
|
||||
|
||||
g.Add(node)
|
||||
}
|
||||
|
||||
return nil
|
||||
|
Loading…
Reference in New Issue
Block a user