mirror of
https://github.com/opentffoundation/opentf.git
synced 2025-12-25 01:00:16 -05:00
Propagate generated config filename into the Terraform graph (#33255)
This commit is contained in:
@@ -190,12 +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,
|
||||
GenerateConfig: len(op.GenerateConfigOut) > 0,
|
||||
Mode: op.PlanMode,
|
||||
Targets: op.Targets,
|
||||
ForceReplace: op.ForceReplace,
|
||||
SetVariables: variables,
|
||||
SkipRefresh: op.Type != backend.OperationTypeRefresh && !op.PlanRefresh,
|
||||
GenerateConfigPath: op.GenerateConfigOut,
|
||||
}
|
||||
run.PlanOpts = planOpts
|
||||
|
||||
|
||||
@@ -78,9 +78,11 @@ type PlanOpts struct {
|
||||
// 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
|
||||
// GenerateConfig tells Terraform where to write any generated configuration
|
||||
// for any ImportTargets that do not have configuration already.
|
||||
//
|
||||
// If empty, then no config will be generated.
|
||||
GenerateConfigPath string
|
||||
}
|
||||
|
||||
// Plan generates an execution plan by comparing the given configuration
|
||||
@@ -669,7 +671,7 @@ func (c *Context) planGraph(config *configs.Config, prevRunState *states.State,
|
||||
preDestroyRefresh: opts.PreDestroyRefresh,
|
||||
Operation: walkPlan,
|
||||
ImportTargets: opts.ImportTargets,
|
||||
GenerateConfig: opts.GenerateConfig,
|
||||
GenerateConfigPath: opts.GenerateConfigPath,
|
||||
}).Build(addrs.RootModuleInstance)
|
||||
return graph, walkPlan, diags
|
||||
case plans.RefreshOnlyMode:
|
||||
|
||||
@@ -4598,8 +4598,8 @@ resource "test_object" "a" {
|
||||
}
|
||||
|
||||
plan, diags := ctx.Plan(m, states.NewState(), &PlanOpts{
|
||||
Mode: plans.NormalMode,
|
||||
GenerateConfig: true,
|
||||
Mode: plans.NormalMode,
|
||||
GenerateConfigPath: "generated.tf", // Actual value here doesn't matter, as long as it is not empty.
|
||||
})
|
||||
if diags.HasErrors() {
|
||||
t.Fatalf("unexpected errors\n%s", diags.Err().Error())
|
||||
@@ -4658,8 +4658,8 @@ import {
|
||||
}
|
||||
|
||||
plan, diags := ctx.Plan(m, states.NewState(), &PlanOpts{
|
||||
Mode: plans.NormalMode,
|
||||
GenerateConfig: true,
|
||||
Mode: plans.NormalMode,
|
||||
GenerateConfigPath: "generated.tf", // Actual value here doesn't matter, as long as it is not empty.
|
||||
})
|
||||
if diags.HasErrors() {
|
||||
t.Fatalf("unexpected errors\n%s", diags.Err().Error())
|
||||
@@ -4740,8 +4740,8 @@ import {
|
||||
}
|
||||
|
||||
plan, diags := ctx.Plan(m, states.NewState(), &PlanOpts{
|
||||
Mode: plans.NormalMode,
|
||||
GenerateConfig: true,
|
||||
Mode: plans.NormalMode,
|
||||
GenerateConfigPath: "generated.tf", // Actual value here doesn't matter, as long as it is not empty.
|
||||
})
|
||||
if diags.HasErrors() {
|
||||
t.Fatalf("unexpected errors\n%s", diags.Err().Error())
|
||||
@@ -4823,8 +4823,8 @@ import {
|
||||
}
|
||||
|
||||
plan, diags := ctx.Plan(m, states.NewState(), &PlanOpts{
|
||||
Mode: plans.NormalMode,
|
||||
GenerateConfig: true,
|
||||
Mode: plans.NormalMode,
|
||||
GenerateConfigPath: "generated.tf", // Actual value here doesn't matter, as long as it is not empty.
|
||||
})
|
||||
if !diags.HasErrors() {
|
||||
t.Fatal("expected error")
|
||||
|
||||
@@ -76,9 +76,11 @@ 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
|
||||
// GenerateConfig tells Terraform where to write and generated config for
|
||||
// any import targets that do not already have configuration.
|
||||
//
|
||||
// If empty, then config will not be generated.
|
||||
GenerateConfigPath string
|
||||
}
|
||||
|
||||
// See GraphBuilder
|
||||
@@ -117,7 +119,7 @@ func (b *PlanGraphBuilder) Steps() []GraphTransformer {
|
||||
importTargets: b.ImportTargets,
|
||||
|
||||
// We only want to generate config during a plan operation.
|
||||
generateConfigForImportTargets: b.GenerateConfig,
|
||||
generateConfigPathForImportTargets: b.GenerateConfigPath,
|
||||
},
|
||||
|
||||
// Add dynamic values
|
||||
|
||||
@@ -79,8 +79,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
|
||||
// generateConfigPath tells this node which file to write generated config
|
||||
// into. If empty, then config should not be generated.
|
||||
generateConfigPath string
|
||||
}
|
||||
|
||||
var (
|
||||
|
||||
@@ -336,7 +336,7 @@ func (n *nodeExpandPlannableResource) resourceInstanceSubgraph(ctx EvalContext,
|
||||
a.dependsOn = n.dependsOn
|
||||
a.Dependencies = n.dependencies
|
||||
a.preDestroyRefresh = n.preDestroyRefresh
|
||||
a.generateConfig = n.generateConfig
|
||||
a.generateConfigPath = n.generateConfigPath
|
||||
|
||||
m = &NodePlannableResourceInstance{
|
||||
NodeAbstractResourceInstance: a,
|
||||
|
||||
@@ -6,6 +6,7 @@ package terraform
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"path/filepath"
|
||||
"sort"
|
||||
|
||||
"github.com/hashicorp/hcl/v2"
|
||||
@@ -160,7 +161,7 @@ func (n *NodePlannableResourceInstance) managedResourceExecute(ctx EvalContext)
|
||||
|
||||
importing := n.importTarget.ID != ""
|
||||
|
||||
if importing && n.Config == nil && !n.generateConfig {
|
||||
if importing && n.Config == nil && len(n.generateConfigPath) == 0 {
|
||||
// Then the user wrote an import target to a target that didn't exist.
|
||||
if n.Addr.Module.IsRoot() {
|
||||
diags = diags.Append(&hcl.Diagnostic{
|
||||
@@ -275,7 +276,7 @@ func (n *NodePlannableResourceInstance) managedResourceExecute(ctx EvalContext)
|
||||
// If we are importing and generating a configuration, we need to
|
||||
// ensure the change is written out so the configuration can be
|
||||
// captured.
|
||||
if n.generateConfig {
|
||||
if len(n.generateConfigPath) > 0 {
|
||||
// Update our return plan
|
||||
change := &plans.ResourceInstanceChange{
|
||||
Addr: n.Addr,
|
||||
@@ -543,7 +544,7 @@ func (n *NodePlannableResourceInstance) importState(ctx EvalContext, addr addrs.
|
||||
}
|
||||
|
||||
// If we're importing and generating config, generate it now.
|
||||
if n.generateConfig {
|
||||
if len(n.generateConfigPath) > 0 {
|
||||
if n.Config != nil {
|
||||
return instanceRefreshState, diags.Append(fmt.Errorf("tried to generate config for %s, but it already exists", n.Addr))
|
||||
}
|
||||
@@ -565,7 +566,7 @@ func (n *NodePlannableResourceInstance) importState(ctx EvalContext, addr addrs.
|
||||
n.generatedConfigHCL = genconfig.WrapResourceContents(n.Addr, generatedHCLAttributes)
|
||||
|
||||
// parse the "file" as HCL to get the hcl.Body
|
||||
synthHCLFile, hclDiags := hclsyntax.ParseConfig([]byte(generatedHCLAttributes), "generated_resources.tf", hcl.Pos{Byte: 0, Line: 1, Column: 1})
|
||||
synthHCLFile, hclDiags := hclsyntax.ParseConfig([]byte(generatedHCLAttributes), filepath.Base(n.generateConfigPath), hcl.Pos{Byte: 0, Line: 1, Column: 1})
|
||||
diags = diags.Append(hclDiags)
|
||||
if hclDiags.HasErrors() {
|
||||
return instanceRefreshState, diags
|
||||
|
||||
@@ -39,14 +39,14 @@ type ConfigTransformer struct {
|
||||
// imported for them.
|
||||
importTargets []*ImportTarget
|
||||
|
||||
// generateConfigForImportTargets tells the graph to generate config for any
|
||||
// import targets that are not contained within config.
|
||||
// generateConfigPathForImportTargets tells the graph where to write any
|
||||
// generated config for import targets that are not contained within config.
|
||||
//
|
||||
// If this is false and an import target has no config, the graph will
|
||||
// If this is empty and an import target has no config, the graph will
|
||||
// simply import the state for the target and any follow-up operations will
|
||||
// try to delete the imported resource unless the config is updated
|
||||
// manually.
|
||||
generateConfigForImportTargets bool
|
||||
generateConfigPathForImportTargets string
|
||||
}
|
||||
|
||||
func (t *ConfigTransformer) Transform(g *Graph) error {
|
||||
@@ -60,23 +60,23 @@ func (t *ConfigTransformer) Transform(g *Graph) error {
|
||||
}
|
||||
|
||||
// Start the transformation process
|
||||
return t.transform(g, t.Config, t.generateConfigForImportTargets)
|
||||
return t.transform(g, t.Config, t.generateConfigPathForImportTargets)
|
||||
}
|
||||
|
||||
func (t *ConfigTransformer) transform(g *Graph, config *configs.Config, generateConfig bool) error {
|
||||
func (t *ConfigTransformer) transform(g *Graph, config *configs.Config, generateConfigPath string) error {
|
||||
// If no config, do nothing
|
||||
if config == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Add our resources
|
||||
if err := t.transformSingle(g, config, generateConfig); err != nil {
|
||||
if err := t.transformSingle(g, config, generateConfigPath); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Transform all the children without generating config.
|
||||
for _, c := range config.Children {
|
||||
if err := t.transform(g, c, false); err != nil {
|
||||
if err := t.transform(g, c, ""); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
@@ -84,7 +84,7 @@ func (t *ConfigTransformer) transform(g *Graph, config *configs.Config, generate
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t *ConfigTransformer) transformSingle(g *Graph, config *configs.Config, generateConfig bool) error {
|
||||
func (t *ConfigTransformer) transformSingle(g *Graph, config *configs.Config, generateConfigPath string) error {
|
||||
path := config.Path
|
||||
module := config.Module
|
||||
log.Printf("[TRACE] ConfigTransformer: Starting for path: %v", path)
|
||||
@@ -171,9 +171,9 @@ func (t *ConfigTransformer) transformSingle(g *Graph, config *configs.Config, ge
|
||||
// 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,
|
||||
Addr: i.Addr.ConfigResource(),
|
||||
importTargets: []*ImportTarget{i},
|
||||
generateConfigPath: generateConfigPath,
|
||||
}
|
||||
|
||||
var node dag.Vertex = abstract
|
||||
|
||||
Reference in New Issue
Block a user