opentofu/internal/terraform/context_import.go
Martin Atkins 343279110a core: Graph walk loads plugin schemas opportunistically
Previously our graph walker expected to recieve a data structure
containing schemas for all of the provider and provisioner plugins used in
the configuration and state. That made sense back when
terraform.NewContext was responsible for loading all of the schemas before
taking any other action, but it no longer has that responsiblity.

Instead, we'll now make sure that the "contextPlugins" object reaches all
of the locations where we need schema -- many of which already had access
to that object anyway -- and then load the needed schemas just in time.

The contextPlugins object memoizes schema lookups, so we can safely call
it many times with the same provider address or provisioner type name and
know that it'll still only load each distinct plugin once per Context
object.

As of this commit, the Context.Schemas method is now a public interface
only and not used by logic in the "terraform" package at all. However,
that does leave us in a rather tenuous situation of relying on the fact
that all practical users of terraform.Context end up calling "Schemas" at
some point in order to verify that we have all of the expected versions
of plugins. That's a non-obvious implicit dependency, and so in subsequent
commits we'll gradually move all responsibility for verifying plugin
versions into the caller of terraform.NewContext, which'll heal a
long-standing architectural wart whereby the caller is responsible for
installing and locating the plugin executables but not for verifying that
what's installed is conforming to the current configuration and dependency
lock file.
2021-09-10 14:56:49 -07:00

86 lines
2.5 KiB
Go

package terraform
import (
"log"
"github.com/hashicorp/terraform/internal/addrs"
"github.com/hashicorp/terraform/internal/configs"
"github.com/hashicorp/terraform/internal/states"
"github.com/hashicorp/terraform/internal/tfdiags"
)
// ImportOpts are used as the configuration for Import.
type ImportOpts struct {
// Targets are the targets to import
Targets []*ImportTarget
// SetVariables are the variables set outside of the configuration,
// such as on the command line, in variables files, etc.
SetVariables InputValues
}
// ImportTarget is a single resource to import.
type ImportTarget struct {
// Addr is the address for the resource instance that the new object should
// be imported into.
Addr addrs.AbsResourceInstance
// ID is the ID of the resource to import. This is resource-specific.
ID string
// ProviderAddr is the address of the provider that should handle the import.
ProviderAddr addrs.AbsProviderConfig
}
// Import takes already-created external resources and brings them
// under Terraform management. Import requires the exact type, name, and ID
// of the resources to import.
//
// This operation is idempotent. If the requested resource is already
// imported, no changes are made to the state.
//
// Further, this operation also gracefully handles partial state. If during
// an import there is a failure, all previously imported resources remain
// imported.
func (c *Context) Import(config *configs.Config, prevRunState *states.State, opts *ImportOpts) (*states.State, tfdiags.Diagnostics) {
var diags tfdiags.Diagnostics
// Hold a lock since we can modify our own state here
defer c.acquireRun("import")()
// Don't modify our caller's state
state := prevRunState.DeepCopy()
log.Printf("[DEBUG] Building and walking import graph")
// Initialize our graph builder
builder := &ImportGraphBuilder{
ImportTargets: opts.Targets,
Config: config,
Plugins: c.plugins,
}
// Build the graph
graph, graphDiags := builder.Build(addrs.RootModuleInstance)
diags = diags.Append(graphDiags)
if graphDiags.HasErrors() {
return state, diags
}
variables := mergeDefaultInputVariableValues(opts.SetVariables, config.Module.Variables)
// Walk it
walker, walkDiags := c.walk(graph, walkImport, &graphWalkOpts{
Config: config,
InputState: state,
RootVariableValues: variables,
})
diags = diags.Append(walkDiags)
if walkDiags.HasErrors() {
return state, diags
}
newState := walker.State.Close()
return newState, diags
}