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

Use plan graph for importing resources #31283

Merged
merged 7 commits into from Jun 23, 2022
Merged
Show file tree
Hide file tree
Changes from 6 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
18 changes: 1 addition & 17 deletions internal/command/import.go
Expand Up @@ -45,7 +45,6 @@ func (c *ImportCommand) Run(args []string) int {
cmdFlags.StringVar(&configPath, "config", pwd, "path")
cmdFlags.BoolVar(&c.Meta.stateLock, "lock", true, "lock state")
cmdFlags.DurationVar(&c.Meta.stateLockTimeout, "lock-timeout", 0, "lock timeout")
cmdFlags.BoolVar(&c.Meta.allowMissingConfig, "allow-missing-config", false, "allow missing config")
cmdFlags.Usage = func() { c.Ui.Error(c.Help()) }
if err := cmdFlags.Parse(args); err != nil {
return 1
Expand Down Expand Up @@ -135,7 +134,7 @@ func (c *ImportCommand) Run(args []string) int {
break
}
}
if !c.Meta.allowMissingConfig && rc == nil {
if rc == nil {
modulePath := addr.Module.String()
if modulePath == "" {
modulePath = "the root module"
Expand Down Expand Up @@ -262,10 +261,6 @@ func (c *ImportCommand) Run(args []string) int {

c.Ui.Output(c.Colorize().Color("[reset][green]\n" + importCommandSuccessMsg))

if c.Meta.allowMissingConfig && rc == nil {
c.Ui.Output(c.Colorize().Color("[reset][yellow]\n" + importCommandAllowMissingResourceMsg))
}

c.showDiagnostics(diags)
if diags.HasErrors() {
return 1
Expand Down Expand Up @@ -310,8 +305,6 @@ Options:
If no config files are present, they must be provided
via the input prompts or env vars.

-allow-missing-config Allow import when no resource configuration block exists.

-input=false Disable interactive input prompts.

-lock=false Don't hold a state lock during the operation. This is
Expand Down Expand Up @@ -361,12 +354,3 @@ const importCommandSuccessMsg = `Import successful!
The resources that were imported are shown above. These resources are now in
your Terraform state and will henceforth be managed by Terraform.
`

const importCommandAllowMissingResourceMsg = `Import does not generate resource configuration, you must create a resource
configuration block that matches the current or desired state manually.

If there is no matching resource configuration block for the imported
resource, Terraform will delete the resource on the next "terraform apply".
It is recommended that you run "terraform plan" to verify that the
configuration is correct and complete.
`
57 changes: 0 additions & 57 deletions internal/command/import_test.go
Expand Up @@ -644,63 +644,6 @@ func TestImport_providerConfigWithVarFile(t *testing.T) {
testStateOutput(t, statePath, testImportStr)
}

func TestImport_allowMissingResourceConfig(t *testing.T) {
defer testChdir(t, testFixturePath("import-missing-resource-config"))()

statePath := testTempFile(t)

p := testProvider()
ui := new(cli.MockUi)
view, _ := testView(t)
c := &ImportCommand{
Meta: Meta{
testingOverrides: metaOverridesForProvider(p),
Ui: ui,
View: view,
},
}

p.ImportResourceStateFn = nil
p.ImportResourceStateResponse = &providers.ImportResourceStateResponse{
ImportedResources: []providers.ImportedResource{
{
TypeName: "test_instance",
State: cty.ObjectVal(map[string]cty.Value{
"id": cty.StringVal("yay"),
}),
},
},
}
p.GetProviderSchemaResponse = &providers.GetProviderSchemaResponse{
ResourceTypes: map[string]providers.Schema{
"test_instance": {
Block: &configschema.Block{
Attributes: map[string]*configschema.Attribute{
"id": {Type: cty.String, Optional: true, Computed: true},
},
},
},
},
}

args := []string{
"-state", statePath,
"-allow-missing-config",
"test_instance.foo",
"bar",
}

if code := c.Run(args); code != 0 {
t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String())
}

if !p.ImportResourceStateCalled {
t.Fatal("ImportResourceState should be called")
}

testStateOutput(t, statePath, testImportStr)
}

func TestImport_emptyConfig(t *testing.T) {
defer testChdir(t, testFixturePath("empty"))()

Expand Down
3 changes: 0 additions & 3 deletions internal/command/meta.go
Expand Up @@ -213,9 +213,6 @@ type Meta struct {
migrateState bool
compactWarnings bool

// Used with the import command to allow import of state when no matching config exists.
allowMissingConfig bool

// Used with commands which write state to allow users to write remote
// state even if the remote and local Terraform versions don't match.
ignoreRemoteVersion bool
Expand Down
3 changes: 1 addition & 2 deletions internal/command/meta_backend_migrate_test.go
@@ -1,7 +1,6 @@
package command

import (
"fmt"
"testing"
)

Expand Down Expand Up @@ -37,7 +36,7 @@ func TestBackendMigrate_promptMultiStatePattern(t *testing.T) {
},
}
for name, tc := range cases {
fmt.Println("Test: ", name)
t.Log("Test: ", name)
m := testMetaBackend(t, nil)
input := map[string]string{}
cleanup := testInputMap(t, input)
Expand Down
5 changes: 4 additions & 1 deletion internal/terraform/context_import.go
Expand Up @@ -56,11 +56,14 @@ func (c *Context) Import(config *configs.Config, prevRunState *states.State, opt
variables := opts.SetVariables

// Initialize our graph builder
builder := &ImportGraphBuilder{
builder := &PlanGraphBuilder{
ImportTargets: opts.Targets,
Config: config,
State: state,
RootVariableValues: variables,
Plugins: c.plugins,
skipRefresh: true,
Operation: walkImport,
}

// Build the graph
Expand Down
37 changes: 22 additions & 15 deletions internal/terraform/context_import_test.go
Expand Up @@ -52,9 +52,20 @@ func TestContextImport_basic(t *testing.T) {
}
}

// import 1 of count instances in the configuration
func TestContextImport_countIndex(t *testing.T) {
p := testProvider("aws")
m := testModule(t, "import-provider")
m := testModuleInline(t, map[string]string{
"main.tf": `
provider "aws" {
foo = "bar"
}

resource "aws_instance" "foo" {
count = 2
}
`})

ctx := testContext2(t, &ContextOpts{
Providers: map[addrs.Provider]providers.Factory{
addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p),
Expand Down Expand Up @@ -779,7 +790,7 @@ func TestContextImport_multiStateSame(t *testing.T) {
}
}

func TestContextImport_noConfigModuleImport(t *testing.T) {
func TestContextImport_nestedModuleImport(t *testing.T) {
p := testProvider("aws")
m := testModuleInline(t, map[string]string{
"main.tf": `
Expand All @@ -797,6 +808,9 @@ module "b" {
source = "./b"
y = module.a[each.key].y
}

resource "test_resource" "test" {
}
`,
"a/main.tf": `
output "y" {
Expand All @@ -810,6 +824,7 @@ variable "y" {

resource "test_resource" "unused" {
value = var.y
// missing required, but should not error
}
`,
})
Expand All @@ -823,7 +838,8 @@ resource "test_resource" "unused" {
ResourceTypes: map[string]*configschema.Block{
"test_resource": {
Attributes: map[string]*configschema.Attribute{
"id": {Type: cty.String, Computed: true},
"id": {Type: cty.String, Computed: true},
"required": {Type: cty.String, Required: true},
},
},
},
Expand All @@ -834,17 +850,8 @@ resource "test_resource" "unused" {
{
TypeName: "test_resource",
State: cty.ObjectVal(map[string]cty.Value{
"id": cty.StringVal("test"),
}),
},
},
}
p.ImportResourceStateResponse = &providers.ImportResourceStateResponse{
ImportedResources: []providers.ImportedResource{
{
TypeName: "test_resource",
State: cty.ObjectVal(map[string]cty.Value{
"id": cty.StringVal("test"),
"id": cty.StringVal("test"),
"required": cty.StringVal("value"),
}),
},
},
Expand All @@ -871,7 +878,7 @@ resource "test_resource" "unused" {
}

ri := state.ResourceInstance(mustResourceInstanceAddr("test_resource.test"))
expected := `{"id":"test"}`
expected := `{"id":"test","required":"value"}`
if ri == nil || ri.Current == nil {
t.Fatal("no state is recorded for resource instance test_resource.test")
}
Expand Down
5 changes: 4 additions & 1 deletion internal/terraform/context_plan.go
Expand Up @@ -560,6 +560,7 @@ func (c *Context) planGraph(config *configs.Config, prevRunState *states.State,
Targets: opts.Targets,
ForceReplace: opts.ForceReplace,
skipRefresh: opts.SkipRefresh,
Operation: walkPlan,
}).Build(addrs.RootModuleInstance)
return graph, walkPlan, diags
case plans.RefreshOnlyMode:
Expand All @@ -571,16 +572,18 @@ func (c *Context) planGraph(config *configs.Config, prevRunState *states.State,
Targets: opts.Targets,
skipRefresh: opts.SkipRefresh,
skipPlanChanges: true, // this activates "refresh only" mode.
Operation: walkPlan,
}).Build(addrs.RootModuleInstance)
return graph, walkPlan, diags
case plans.DestroyMode:
graph, diags := DestroyPlanGraphBuilder(&PlanGraphBuilder{
graph, diags := (&PlanGraphBuilder{
Config: config,
State: prevRunState,
RootVariableValues: opts.SetVariables,
Plugins: c.plugins,
Targets: opts.Targets,
skipRefresh: opts.SkipRefresh,
Operation: walkPlanDestroy,
}).Build(addrs.RootModuleInstance)
return graph, walkPlanDestroy, diags
default:
Expand Down
3 changes: 2 additions & 1 deletion internal/terraform/context_validate.go
Expand Up @@ -55,11 +55,12 @@ func (c *Context) Validate(config *configs.Config) tfdiags.Diagnostics {
}
}

graph, moreDiags := ValidateGraphBuilder(&PlanGraphBuilder{
graph, moreDiags := (&PlanGraphBuilder{
Config: config,
Plugins: c.plugins,
State: states.NewState(),
RootVariableValues: varValues,
Operation: walkValidate,
}).Build(addrs.RootModuleInstance)
diags = diags.Append(moreDiags)
if moreDiags.HasErrors() {
Expand Down
2 changes: 1 addition & 1 deletion internal/terraform/context_walk.go
Expand Up @@ -73,7 +73,7 @@ func (c *Context) graphWalker(operation walkOperation, opts *graphWalkOpts) *Con
refreshState = states.NewState().SyncWrapper()
prevRunState = states.NewState().SyncWrapper()

case walkPlan, walkPlanDestroy:
case walkPlan, walkPlanDestroy, walkImport:
state = inputState.DeepCopy().SyncWrapper()
refreshState = inputState.DeepCopy().SyncWrapper()
prevRunState = inputState.DeepCopy().SyncWrapper()
Expand Down
17 changes: 0 additions & 17 deletions internal/terraform/graph_builder_destroy_plan.go

This file was deleted.