remove refresh!
Delete all the code associated with the Refresh walk
This commit is contained in:
parent
915d4e4b45
commit
906d399189
|
@ -311,16 +311,6 @@ func (c *Context) Graph(typ GraphType, opts *ContextGraphOpts) (*Graph, tfdiags.
|
|||
Validate: opts.Validate,
|
||||
}).Build(addrs.RootModuleInstance)
|
||||
|
||||
case GraphTypeRefresh:
|
||||
return (&RefreshGraphBuilder{
|
||||
Config: c.config,
|
||||
State: c.state,
|
||||
Components: c.components,
|
||||
Schemas: c.schemas,
|
||||
Targets: c.targets,
|
||||
Validate: opts.Validate,
|
||||
}).Build(addrs.RootModuleInstance)
|
||||
|
||||
case GraphTypeEval:
|
||||
return (&EvalGraphBuilder{
|
||||
Config: c.config,
|
||||
|
|
|
@ -8,9 +8,7 @@ package terraform
|
|||
type GraphType byte
|
||||
|
||||
const (
|
||||
GraphTypeInvalid GraphType = 0
|
||||
GraphTypeLegacy GraphType = iota
|
||||
GraphTypeRefresh
|
||||
GraphTypeInvalid GraphType = iota
|
||||
GraphTypePlan
|
||||
GraphTypePlanDestroy
|
||||
GraphTypeApply
|
||||
|
@ -25,8 +23,6 @@ var GraphTypeMap = map[string]GraphType{
|
|||
"apply": GraphTypeApply,
|
||||
"plan": GraphTypePlan,
|
||||
"plan-destroy": GraphTypePlanDestroy,
|
||||
"refresh": GraphTypeRefresh,
|
||||
"legacy": GraphTypeLegacy,
|
||||
"validate": GraphTypeValidate,
|
||||
"eval": GraphTypeEval,
|
||||
}
|
||||
|
|
|
@ -1,190 +0,0 @@
|
|||
package terraform
|
||||
|
||||
import (
|
||||
"log"
|
||||
|
||||
"github.com/hashicorp/terraform/states"
|
||||
"github.com/hashicorp/terraform/tfdiags"
|
||||
|
||||
"github.com/hashicorp/terraform/addrs"
|
||||
"github.com/hashicorp/terraform/configs"
|
||||
"github.com/hashicorp/terraform/dag"
|
||||
)
|
||||
|
||||
// RefreshGraphBuilder implements GraphBuilder and is responsible for building
|
||||
// a graph for refreshing (updating the Terraform state).
|
||||
//
|
||||
// The primary difference between this graph and others:
|
||||
//
|
||||
// * Based on the state since it represents the only resources that
|
||||
// need to be refreshed.
|
||||
//
|
||||
// * Ignores lifecycle options since no lifecycle events occur here. This
|
||||
// simplifies the graph significantly since complex transforms such as
|
||||
// create-before-destroy can be completely ignored.
|
||||
//
|
||||
type RefreshGraphBuilder struct {
|
||||
// Config is the configuration tree.
|
||||
Config *configs.Config
|
||||
|
||||
// State is the prior state
|
||||
State *states.State
|
||||
|
||||
// Components is a factory for the plug-in components (providers and
|
||||
// provisioners) available for use.
|
||||
Components contextComponentFactory
|
||||
|
||||
// Schemas is the repository of schemas we will draw from to analyse
|
||||
// the configuration.
|
||||
Schemas *Schemas
|
||||
|
||||
// Targets are resources to target
|
||||
Targets []addrs.Targetable
|
||||
|
||||
// Validate will do structural validation of the graph.
|
||||
Validate bool
|
||||
}
|
||||
|
||||
// See GraphBuilder
|
||||
func (b *RefreshGraphBuilder) Build(path addrs.ModuleInstance) (*Graph, tfdiags.Diagnostics) {
|
||||
return (&BasicGraphBuilder{
|
||||
Steps: b.Steps(),
|
||||
Validate: b.Validate,
|
||||
Name: "RefreshGraphBuilder",
|
||||
}).Build(path)
|
||||
}
|
||||
|
||||
// See GraphBuilder
|
||||
func (b *RefreshGraphBuilder) Steps() []GraphTransformer {
|
||||
// Custom factory for creating providers.
|
||||
concreteProvider := func(a *NodeAbstractProvider) dag.Vertex {
|
||||
return &NodeApplyableProvider{
|
||||
NodeAbstractProvider: a,
|
||||
}
|
||||
}
|
||||
|
||||
concreteManagedResource := func(a *NodeAbstractResource) dag.Vertex {
|
||||
return &nodeExpandRefreshableManagedResource{
|
||||
NodeAbstractResource: a,
|
||||
}
|
||||
}
|
||||
|
||||
concreteManagedResourceInstance := func(a *NodeAbstractResourceInstance) dag.Vertex {
|
||||
return &NodeRefreshableManagedResourceInstance{
|
||||
NodeAbstractResourceInstance: a,
|
||||
}
|
||||
}
|
||||
|
||||
concreteResourceInstanceDeposed := func(a *NodeAbstractResourceInstance, key states.DeposedKey) dag.Vertex {
|
||||
// The "Plan" node type also handles refreshing behavior.
|
||||
return &NodePlanDeposedResourceInstanceObject{
|
||||
NodeAbstractResourceInstance: a,
|
||||
DeposedKey: key,
|
||||
}
|
||||
}
|
||||
|
||||
concreteDataResource := func(a *NodeAbstractResource) dag.Vertex {
|
||||
return &nodeExpandRefreshableDataResource{
|
||||
NodeAbstractResource: a,
|
||||
}
|
||||
}
|
||||
|
||||
steps := []GraphTransformer{
|
||||
// Creates all the managed resources that aren't in the state, but only if
|
||||
// we have a state already. No resources in state means there's not
|
||||
// anything to refresh.
|
||||
func() GraphTransformer {
|
||||
if b.State.HasResources() {
|
||||
return &ConfigTransformer{
|
||||
Concrete: concreteManagedResource,
|
||||
Config: b.Config,
|
||||
Unique: true,
|
||||
ModeFilter: true,
|
||||
Mode: addrs.ManagedResourceMode,
|
||||
}
|
||||
}
|
||||
log.Println("[TRACE] No managed resources in state during refresh; skipping managed resource transformer")
|
||||
return nil
|
||||
}(),
|
||||
|
||||
// Creates all the data resources that aren't in the state. This will also
|
||||
// add any orphans from scaling in as destroy nodes.
|
||||
&ConfigTransformer{
|
||||
Concrete: concreteDataResource,
|
||||
Config: b.Config,
|
||||
Unique: true,
|
||||
ModeFilter: true,
|
||||
Mode: addrs.DataResourceMode,
|
||||
},
|
||||
|
||||
// Add any fully-orphaned resources from config (ones that have been
|
||||
// removed completely, not ones that are just orphaned due to a scaled-in
|
||||
// count.
|
||||
&OrphanResourceInstanceTransformer{
|
||||
Concrete: concreteManagedResourceInstance,
|
||||
State: b.State,
|
||||
Config: b.Config,
|
||||
},
|
||||
|
||||
// We also need nodes for any deposed instance objects present in the
|
||||
// state, so we can check if they still exist. (This intentionally
|
||||
// skips creating nodes for _current_ objects, since ConfigTransformer
|
||||
// created nodes that will do that during DynamicExpand.)
|
||||
&StateTransformer{
|
||||
ConcreteDeposed: concreteResourceInstanceDeposed,
|
||||
State: b.State,
|
||||
},
|
||||
|
||||
// Attach the state
|
||||
&AttachStateTransformer{State: b.State},
|
||||
|
||||
// Attach the configuration to any resources
|
||||
&AttachResourceConfigTransformer{Config: b.Config},
|
||||
|
||||
// Add root variables
|
||||
&RootVariableTransformer{Config: b.Config},
|
||||
|
||||
// Add the local values
|
||||
&LocalTransformer{Config: b.Config},
|
||||
|
||||
// Add the outputs
|
||||
&OutputTransformer{Config: b.Config},
|
||||
|
||||
// Add module variables
|
||||
&ModuleVariableTransformer{Config: b.Config},
|
||||
|
||||
TransformProviders(b.Components.ResourceProviders(), concreteProvider, b.Config),
|
||||
|
||||
// Must attach schemas before ReferenceTransformer so that we can
|
||||
// analyze the configuration to find references.
|
||||
&AttachSchemaTransformer{Schemas: b.Schemas, Config: b.Config},
|
||||
|
||||
// Create expansion nodes for all of the module calls. This must
|
||||
// come after all other transformers that create nodes representing
|
||||
// objects that can belong to modules.
|
||||
&ModuleExpansionTransformer{Config: b.Config},
|
||||
|
||||
// Connect so that the references are ready for targeting. We'll
|
||||
// have to connect again later for providers and so on.
|
||||
&ReferenceTransformer{},
|
||||
&AttachDependenciesTransformer{},
|
||||
&attachDataResourceDependenciesTransformer{},
|
||||
|
||||
// Target
|
||||
&TargetsTransformer{
|
||||
Targets: b.Targets,
|
||||
},
|
||||
|
||||
// Close opened plugin connections
|
||||
&CloseProviderTransformer{},
|
||||
|
||||
// Close root module
|
||||
&CloseRootModuleTransformer{},
|
||||
|
||||
// Perform the transitive reduction to make our graph a bit
|
||||
// more sane if possible (it usually is possible).
|
||||
&TransitiveReductionTransformer{},
|
||||
}
|
||||
|
||||
return steps
|
||||
}
|
|
@ -1,79 +0,0 @@
|
|||
package terraform
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/google/go-cmp/cmp"
|
||||
"github.com/hashicorp/terraform/addrs"
|
||||
"github.com/hashicorp/terraform/states"
|
||||
)
|
||||
|
||||
func TestRefreshGraphBuilder_configOrphans(t *testing.T) {
|
||||
|
||||
m := testModule(t, "refresh-config-orphan")
|
||||
|
||||
state := states.NewState()
|
||||
root := state.EnsureModule(addrs.RootModuleInstance)
|
||||
deposedKey := states.DeposedKey("00000001")
|
||||
testSetResourceInstanceDeposed(root, "test_object.foo[0]", `{"id":"foo"}`, `provider["registry.terraform.io/hashicorp/test"]`, deposedKey)
|
||||
testSetResourceInstanceDeposed(root, "test_object.foo[1]", `{"id":"bar"}`, `provider["registry.terraform.io/hashicorp/test"]`, deposedKey)
|
||||
testSetResourceInstanceDeposed(root, "test_object.foo[2]", `{"id":"baz"}`, `provider["registry.terraform.io/hashicorp/test"]`, deposedKey)
|
||||
|
||||
// NOTE: Real-world data resources don't get deposed
|
||||
testSetResourceInstanceDeposed(root, "data.test_object.foo[0]", `{"id":"foo"}`, `provider["registry.terraform.io/hashicorp/test"]`, deposedKey)
|
||||
testSetResourceInstanceDeposed(root, "data.test_object.foo[1]", `{"id":"bar"}`, `provider["registry.terraform.io/hashicorp/test"]`, deposedKey)
|
||||
testSetResourceInstanceDeposed(root, "data.test_object.foo[2]", `{"id":"baz"}`, `provider["registry.terraform.io/hashicorp/test"]`, deposedKey)
|
||||
|
||||
b := &RefreshGraphBuilder{
|
||||
Config: m,
|
||||
State: state,
|
||||
Components: simpleMockComponentFactory(),
|
||||
Schemas: simpleTestSchemas(),
|
||||
}
|
||||
g, err := b.Build(addrs.RootModuleInstance)
|
||||
if err != nil {
|
||||
t.Fatalf("Error building graph: %s", err)
|
||||
}
|
||||
|
||||
actual := strings.TrimSpace(g.StringWithNodeTypes())
|
||||
expected := strings.TrimSpace(`
|
||||
data.test_object.foo[0] - *terraform.NodeRefreshableManagedResourceInstance
|
||||
provider["registry.terraform.io/hashicorp/test"] - *terraform.NodeApplyableProvider
|
||||
data.test_object.foo[0] (deposed 00000001) - *terraform.NodePlanDeposedResourceInstanceObject
|
||||
provider["registry.terraform.io/hashicorp/test"] - *terraform.NodeApplyableProvider
|
||||
data.test_object.foo[1] - *terraform.NodeRefreshableManagedResourceInstance
|
||||
provider["registry.terraform.io/hashicorp/test"] - *terraform.NodeApplyableProvider
|
||||
data.test_object.foo[1] (deposed 00000001) - *terraform.NodePlanDeposedResourceInstanceObject
|
||||
provider["registry.terraform.io/hashicorp/test"] - *terraform.NodeApplyableProvider
|
||||
data.test_object.foo[2] - *terraform.NodeRefreshableManagedResourceInstance
|
||||
provider["registry.terraform.io/hashicorp/test"] - *terraform.NodeApplyableProvider
|
||||
data.test_object.foo[2] (deposed 00000001) - *terraform.NodePlanDeposedResourceInstanceObject
|
||||
provider["registry.terraform.io/hashicorp/test"] - *terraform.NodeApplyableProvider
|
||||
provider["registry.terraform.io/hashicorp/test"] - *terraform.NodeApplyableProvider
|
||||
provider["registry.terraform.io/hashicorp/test"] (close) - *terraform.graphNodeCloseProvider
|
||||
data.test_object.foo[0] - *terraform.NodeRefreshableManagedResourceInstance
|
||||
data.test_object.foo[0] (deposed 00000001) - *terraform.NodePlanDeposedResourceInstanceObject
|
||||
data.test_object.foo[1] - *terraform.NodeRefreshableManagedResourceInstance
|
||||
data.test_object.foo[1] (deposed 00000001) - *terraform.NodePlanDeposedResourceInstanceObject
|
||||
data.test_object.foo[2] - *terraform.NodeRefreshableManagedResourceInstance
|
||||
data.test_object.foo[2] (deposed 00000001) - *terraform.NodePlanDeposedResourceInstanceObject
|
||||
test_object.foo (expand) - *terraform.nodeExpandRefreshableManagedResource
|
||||
test_object.foo[0] (deposed 00000001) - *terraform.NodePlanDeposedResourceInstanceObject
|
||||
test_object.foo[1] (deposed 00000001) - *terraform.NodePlanDeposedResourceInstanceObject
|
||||
test_object.foo[2] (deposed 00000001) - *terraform.NodePlanDeposedResourceInstanceObject
|
||||
root - *terraform.nodeCloseModule
|
||||
provider["registry.terraform.io/hashicorp/test"] (close) - *terraform.graphNodeCloseProvider
|
||||
test_object.foo (expand) - *terraform.nodeExpandRefreshableManagedResource
|
||||
provider["registry.terraform.io/hashicorp/test"] - *terraform.NodeApplyableProvider
|
||||
test_object.foo[0] (deposed 00000001) - *terraform.NodePlanDeposedResourceInstanceObject
|
||||
provider["registry.terraform.io/hashicorp/test"] - *terraform.NodeApplyableProvider
|
||||
test_object.foo[1] (deposed 00000001) - *terraform.NodePlanDeposedResourceInstanceObject
|
||||
provider["registry.terraform.io/hashicorp/test"] - *terraform.NodeApplyableProvider
|
||||
test_object.foo[2] (deposed 00000001) - *terraform.NodePlanDeposedResourceInstanceObject
|
||||
provider["registry.terraform.io/hashicorp/test"] - *terraform.NodeApplyableProvider
|
||||
`)
|
||||
if expected != actual {
|
||||
t.Fatalf("wrong result\n\ngot:\n%s\n\nwant:\n%s\ndiff:\n%s", actual, expected, cmp.Diff(expected, actual))
|
||||
}
|
||||
}
|
|
@ -10,7 +10,6 @@ const (
|
|||
walkApply
|
||||
walkPlan
|
||||
walkPlanDestroy
|
||||
walkRefresh
|
||||
walkValidate
|
||||
walkDestroy
|
||||
walkImport
|
||||
|
|
|
@ -9,18 +9,16 @@ func _() {
|
|||
// Re-run the stringer command to generate them again.
|
||||
var x [1]struct{}
|
||||
_ = x[GraphTypeInvalid-0]
|
||||
_ = x[GraphTypeLegacy-1]
|
||||
_ = x[GraphTypeRefresh-2]
|
||||
_ = x[GraphTypePlan-3]
|
||||
_ = x[GraphTypePlanDestroy-4]
|
||||
_ = x[GraphTypeApply-5]
|
||||
_ = x[GraphTypeValidate-6]
|
||||
_ = x[GraphTypeEval-7]
|
||||
_ = x[GraphTypePlan-1]
|
||||
_ = x[GraphTypePlanDestroy-2]
|
||||
_ = x[GraphTypeApply-3]
|
||||
_ = x[GraphTypeValidate-4]
|
||||
_ = x[GraphTypeEval-5]
|
||||
}
|
||||
|
||||
const _GraphType_name = "GraphTypeInvalidGraphTypeLegacyGraphTypeRefreshGraphTypePlanGraphTypePlanDestroyGraphTypeApplyGraphTypeValidateGraphTypeEval"
|
||||
const _GraphType_name = "GraphTypeInvalidGraphTypePlanGraphTypePlanDestroyGraphTypeApplyGraphTypeValidateGraphTypeEval"
|
||||
|
||||
var _GraphType_index = [...]uint8{0, 16, 31, 47, 60, 80, 94, 111, 124}
|
||||
var _GraphType_index = [...]uint8{0, 16, 29, 49, 63, 80, 93}
|
||||
|
||||
func (i GraphType) String() string {
|
||||
if i >= GraphType(len(_GraphType_index)-1) {
|
||||
|
|
|
@ -1,283 +0,0 @@
|
|||
package terraform
|
||||
|
||||
import (
|
||||
"github.com/hashicorp/terraform/addrs"
|
||||
"github.com/hashicorp/terraform/dag"
|
||||
"github.com/hashicorp/terraform/plans"
|
||||
"github.com/hashicorp/terraform/states"
|
||||
"github.com/hashicorp/terraform/tfdiags"
|
||||
)
|
||||
|
||||
type nodeExpandRefreshableDataResource struct {
|
||||
*NodeAbstractResource
|
||||
}
|
||||
|
||||
var (
|
||||
_ GraphNodeDynamicExpandable = (*nodeExpandRefreshableDataResource)(nil)
|
||||
_ GraphNodeReferenceable = (*nodeExpandRefreshableDataResource)(nil)
|
||||
_ GraphNodeReferencer = (*nodeExpandRefreshableDataResource)(nil)
|
||||
_ GraphNodeConfigResource = (*nodeExpandRefreshableDataResource)(nil)
|
||||
_ GraphNodeAttachResourceConfig = (*nodeExpandRefreshableDataResource)(nil)
|
||||
)
|
||||
|
||||
func (n *nodeExpandRefreshableDataResource) Name() string {
|
||||
return n.NodeAbstractResource.Name() + " (expand)"
|
||||
}
|
||||
|
||||
func (n *nodeExpandRefreshableDataResource) References() []*addrs.Reference {
|
||||
return (&NodeRefreshableManagedResource{NodeAbstractResource: n.NodeAbstractResource}).References()
|
||||
}
|
||||
|
||||
func (n *nodeExpandRefreshableDataResource) DynamicExpand(ctx EvalContext) (*Graph, error) {
|
||||
var g Graph
|
||||
|
||||
expander := ctx.InstanceExpander()
|
||||
for _, module := range expander.ExpandModule(n.Addr.Module) {
|
||||
g.Add(&NodeRefreshableDataResource{
|
||||
NodeAbstractResource: n.NodeAbstractResource,
|
||||
Addr: n.Addr.Resource.Absolute(module),
|
||||
})
|
||||
}
|
||||
|
||||
return &g, nil
|
||||
}
|
||||
|
||||
// NodeRefreshableDataResource represents a resource that is "refreshable".
|
||||
type NodeRefreshableDataResource struct {
|
||||
*NodeAbstractResource
|
||||
|
||||
Addr addrs.AbsResource
|
||||
}
|
||||
|
||||
var (
|
||||
_ GraphNodeModuleInstance = (*NodeRefreshableDataResource)(nil)
|
||||
_ GraphNodeDynamicExpandable = (*NodeRefreshableDataResource)(nil)
|
||||
_ GraphNodeReferenceable = (*NodeRefreshableDataResource)(nil)
|
||||
_ GraphNodeReferencer = (*NodeRefreshableDataResource)(nil)
|
||||
_ GraphNodeConfigResource = (*NodeRefreshableDataResource)(nil)
|
||||
_ GraphNodeAttachResourceConfig = (*NodeRefreshableDataResource)(nil)
|
||||
_ GraphNodeAttachProviderMetaConfigs = (*NodeAbstractResource)(nil)
|
||||
)
|
||||
|
||||
func (n *NodeRefreshableDataResource) Path() addrs.ModuleInstance {
|
||||
return n.Addr.Module
|
||||
}
|
||||
|
||||
// GraphNodeDynamicExpandable
|
||||
func (n *NodeRefreshableDataResource) DynamicExpand(ctx EvalContext) (*Graph, error) {
|
||||
var diags tfdiags.Diagnostics
|
||||
|
||||
expander := ctx.InstanceExpander()
|
||||
|
||||
switch {
|
||||
case n.Config.Count != nil:
|
||||
count, countDiags := evaluateCountExpressionValue(n.Config.Count, ctx)
|
||||
diags = diags.Append(countDiags)
|
||||
if countDiags.HasErrors() {
|
||||
return nil, diags.Err()
|
||||
}
|
||||
if !count.IsKnown() {
|
||||
// If the count isn't known yet, we'll skip refreshing and try expansion
|
||||
// again during the plan walk.
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
c, _ := count.AsBigFloat().Int64()
|
||||
expander.SetResourceCount(n.Addr.Module, n.Addr.Resource, int(c))
|
||||
|
||||
case n.Config.ForEach != nil:
|
||||
forEachVal, forEachDiags := evaluateForEachExpressionValue(n.Config.ForEach, ctx)
|
||||
diags = diags.Append(forEachDiags)
|
||||
if forEachDiags.HasErrors() {
|
||||
return nil, diags.Err()
|
||||
}
|
||||
if !forEachVal.IsKnown() {
|
||||
// If the for_each isn't known yet, we'll skip refreshing and try expansion
|
||||
// again during the plan walk.
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
expander.SetResourceForEach(n.Addr.Module, n.Addr.Resource, forEachVal.AsValueMap())
|
||||
|
||||
default:
|
||||
expander.SetResourceSingle(n.Addr.Module, n.Addr.Resource)
|
||||
}
|
||||
|
||||
// Next we need to potentially rename an instance address in the state
|
||||
// if we're transitioning whether "count" is set at all.
|
||||
fixResourceCountSetTransition(ctx, n.ResourceAddr(), n.Config.Count != nil)
|
||||
|
||||
instanceAddrs := expander.ExpandResource(n.Addr)
|
||||
|
||||
// Our graph transformers require access to the full state, so we'll
|
||||
// temporarily lock it while we work on this.
|
||||
state := ctx.State().Lock()
|
||||
defer ctx.State().Unlock()
|
||||
|
||||
// The concrete resource factory we'll use
|
||||
concreteResource := func(a *NodeAbstractResourceInstance) dag.Vertex {
|
||||
// Add the config and state since we don't do that via transforms
|
||||
a.Config = n.Config
|
||||
a.ResolvedProvider = n.ResolvedProvider
|
||||
a.ProviderMetas = n.ProviderMetas
|
||||
a.dependsOn = n.dependsOn
|
||||
a.forceDependsOn = n.forceDependsOn
|
||||
a.Targets = n.Targets
|
||||
|
||||
return &NodeRefreshableDataResourceInstance{
|
||||
NodeAbstractResourceInstance: a,
|
||||
}
|
||||
}
|
||||
|
||||
// We also need a destroyable resource for orphans that are a result of a
|
||||
// scaled-in count.
|
||||
concreteResourceDestroyable := func(a *NodeAbstractResourceInstance) dag.Vertex {
|
||||
// Add the config and provider since we don't do that via transforms
|
||||
a.Config = n.Config
|
||||
a.ResolvedProvider = n.ResolvedProvider
|
||||
|
||||
return &NodeDestroyableDataResourceInstance{
|
||||
NodeAbstractResourceInstance: a,
|
||||
}
|
||||
}
|
||||
|
||||
// Start creating the steps
|
||||
steps := []GraphTransformer{
|
||||
// Expand the count.
|
||||
&ResourceCountTransformer{
|
||||
Concrete: concreteResource,
|
||||
Schema: n.Schema,
|
||||
Addr: n.ResourceAddr(),
|
||||
InstanceAddrs: instanceAddrs,
|
||||
},
|
||||
|
||||
// Add the count orphans. As these are orphaned refresh nodes, we add them
|
||||
// directly as NodeDestroyableDataResource.
|
||||
&OrphanResourceInstanceCountTransformer{
|
||||
Concrete: concreteResourceDestroyable,
|
||||
Addr: n.Addr,
|
||||
InstanceAddrs: instanceAddrs,
|
||||
State: state,
|
||||
},
|
||||
|
||||
// Attach the state
|
||||
&AttachStateTransformer{State: state},
|
||||
|
||||
// Targeting
|
||||
&TargetsTransformer{Targets: n.Targets},
|
||||
|
||||
// Connect references so ordering is correct
|
||||
&ReferenceTransformer{},
|
||||
|
||||
// Make sure there is a single root
|
||||
&RootTransformer{},
|
||||
}
|
||||
|
||||
// Build the graph
|
||||
b := &BasicGraphBuilder{
|
||||
Steps: steps,
|
||||
Validate: true,
|
||||
Name: "NodeRefreshableDataResource",
|
||||
}
|
||||
|
||||
graph, diags := b.Build(nil)
|
||||
return graph, diags.ErrWithWarnings()
|
||||
}
|
||||
|
||||
// NodeRefreshableDataResourceInstance represents a single resource instance
|
||||
// that is refreshable.
|
||||
type NodeRefreshableDataResourceInstance struct {
|
||||
*NodeAbstractResourceInstance
|
||||
}
|
||||
|
||||
// GraphNodeExecutable
|
||||
func (n *NodeRefreshableDataResourceInstance) Execute(ctx EvalContext, op walkOperation) error {
|
||||
addr := n.ResourceInstanceAddr()
|
||||
|
||||
// These variables are the state for the eval sequence below, and are
|
||||
// updated through pointers.
|
||||
var change *plans.ResourceInstanceChange
|
||||
var state *states.ResourceInstanceObject
|
||||
|
||||
provider, providerSchema, err := GetProvider(ctx, n.ResolvedProvider)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// EvalReadState
|
||||
readStateReq := &EvalReadState{
|
||||
Addr: addr.Resource,
|
||||
Provider: &provider,
|
||||
ProviderSchema: &providerSchema,
|
||||
Output: &state,
|
||||
}
|
||||
_, err = readStateReq.Eval(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// EvalReadDataRefresh will _attempt_ to read the data source, but
|
||||
// may generate an incomplete planned object if the configuration
|
||||
// includes values that won't be known until apply.
|
||||
readDataRefreshReq := &evalReadDataRefresh{
|
||||
evalReadData{
|
||||
Addr: addr.Resource,
|
||||
Config: n.Config,
|
||||
Provider: &provider,
|
||||
ProviderAddr: n.ResolvedProvider,
|
||||
ProviderMetas: n.ProviderMetas,
|
||||
ProviderSchema: &providerSchema,
|
||||
OutputChange: &change,
|
||||
State: &state,
|
||||
dependsOn: n.dependsOn,
|
||||
forceDependsOn: n.forceDependsOn,
|
||||
},
|
||||
}
|
||||
_, err = readDataRefreshReq.Eval(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if change == nil {
|
||||
// EvalWriteState
|
||||
writeStateRequest := EvalWriteState{
|
||||
Addr: addr.Resource,
|
||||
ProviderAddr: n.ResolvedProvider,
|
||||
State: &state,
|
||||
ProviderSchema: &providerSchema,
|
||||
}
|
||||
_, err := writeStateRequest.Eval(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = UpdateStateHook(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
} else {
|
||||
// EvalWriteDiff
|
||||
writeDiffReq := &EvalWriteDiff{
|
||||
Addr: addr.Resource,
|
||||
Change: &change,
|
||||
ProviderSchema: &providerSchema,
|
||||
}
|
||||
_, err = writeDiffReq.Eval(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// EvalWriteState
|
||||
writeStateRequest := EvalWriteState{
|
||||
Addr: addr.Resource,
|
||||
ProviderAddr: n.ResolvedProvider,
|
||||
State: &state,
|
||||
ProviderSchema: &providerSchema,
|
||||
}
|
||||
_, err := writeStateRequest.Eval(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return err
|
||||
}
|
|
@ -1,178 +0,0 @@
|
|||
package terraform
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/zclconf/go-cty/cty"
|
||||
|
||||
"github.com/hashicorp/terraform/addrs"
|
||||
"github.com/hashicorp/terraform/instances"
|
||||
)
|
||||
|
||||
func TestNodeRefreshableDataResourceDynamicExpand_scaleOut(t *testing.T) {
|
||||
m := testModule(t, "refresh-data-scale-inout")
|
||||
|
||||
state := MustShimLegacyState(&State{
|
||||
Modules: []*ModuleState{
|
||||
&ModuleState{
|
||||
Path: rootModulePath,
|
||||
Resources: map[string]*ResourceState{
|
||||
"data.aws_instance.foo.0": &ResourceState{
|
||||
Type: "aws_instance",
|
||||
Deposed: []*InstanceState{
|
||||
&InstanceState{
|
||||
ID: "foo",
|
||||
},
|
||||
},
|
||||
},
|
||||
"data.aws_instance.foo.1": &ResourceState{
|
||||
Type: "aws_instance",
|
||||
Deposed: []*InstanceState{
|
||||
&InstanceState{
|
||||
ID: "bar",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
addr := addrs.RootModule.Resource(addrs.DataResourceMode, "aws_instance", "foo")
|
||||
n := &NodeRefreshableDataResource{
|
||||
NodeAbstractResource: &NodeAbstractResource{
|
||||
Addr: addr,
|
||||
Config: m.Module.DataResources["data.aws_instance.foo"],
|
||||
},
|
||||
Addr: addr.Absolute(addrs.RootModuleInstance),
|
||||
}
|
||||
|
||||
g, err := n.DynamicExpand(&MockEvalContext{
|
||||
PathPath: addrs.RootModuleInstance,
|
||||
StateState: state.SyncWrapper(),
|
||||
InstanceExpanderExpander: instances.NewExpander(),
|
||||
|
||||
// DynamicExpand will call EvaluateExpr to evaluate the "count"
|
||||
// expression, which is just a literal number 3 in the fixture config
|
||||
// and so we'll just hard-code this here too.
|
||||
EvaluateExprResult: cty.NumberIntVal(3),
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("error on DynamicExpand: %s", err)
|
||||
}
|
||||
|
||||
actual := g.StringWithNodeTypes()
|
||||
expected := `data.aws_instance.foo[0] - *terraform.NodeRefreshableDataResourceInstance
|
||||
data.aws_instance.foo[1] - *terraform.NodeRefreshableDataResourceInstance
|
||||
data.aws_instance.foo[2] - *terraform.NodeRefreshableDataResourceInstance
|
||||
root - terraform.graphNodeRoot
|
||||
data.aws_instance.foo[0] - *terraform.NodeRefreshableDataResourceInstance
|
||||
data.aws_instance.foo[1] - *terraform.NodeRefreshableDataResourceInstance
|
||||
data.aws_instance.foo[2] - *terraform.NodeRefreshableDataResourceInstance
|
||||
`
|
||||
if expected != actual {
|
||||
t.Fatalf("Expected:\n%s\nGot:\n%s", expected, actual)
|
||||
}
|
||||
}
|
||||
|
||||
func TestNodeRefreshableDataResourceDynamicExpand_scaleIn(t *testing.T) {
|
||||
m := testModule(t, "refresh-data-scale-inout")
|
||||
|
||||
state := MustShimLegacyState(&State{
|
||||
Modules: []*ModuleState{
|
||||
&ModuleState{
|
||||
Path: rootModulePath,
|
||||
Resources: map[string]*ResourceState{
|
||||
"data.aws_instance.foo.0": &ResourceState{
|
||||
Type: "aws_instance",
|
||||
Deposed: []*InstanceState{
|
||||
&InstanceState{
|
||||
ID: "foo",
|
||||
},
|
||||
},
|
||||
},
|
||||
"data.aws_instance.foo.1": &ResourceState{
|
||||
Type: "aws_instance",
|
||||
Deposed: []*InstanceState{
|
||||
&InstanceState{
|
||||
ID: "bar",
|
||||
},
|
||||
},
|
||||
},
|
||||
"data.aws_instance.foo.2": &ResourceState{
|
||||
Type: "aws_instance",
|
||||
Deposed: []*InstanceState{
|
||||
&InstanceState{
|
||||
ID: "baz",
|
||||
},
|
||||
},
|
||||
},
|
||||
"data.aws_instance.foo.3": &ResourceState{
|
||||
Type: "aws_instance",
|
||||
Deposed: []*InstanceState{
|
||||
&InstanceState{
|
||||
ID: "qux",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
addr := addrs.RootModule.Resource(addrs.DataResourceMode, "aws_instance", "foo")
|
||||
n := &NodeRefreshableDataResource{
|
||||
NodeAbstractResource: &NodeAbstractResource{
|
||||
Addr: addr,
|
||||
Config: m.Module.DataResources["data.aws_instance.foo"],
|
||||
ResolvedProvider: addrs.AbsProviderConfig{
|
||||
Provider: addrs.NewDefaultProvider("aws"),
|
||||
Module: addrs.RootModule,
|
||||
},
|
||||
},
|
||||
Addr: addr.Absolute(addrs.RootModuleInstance),
|
||||
}
|
||||
|
||||
g, err := n.DynamicExpand(&MockEvalContext{
|
||||
PathPath: addrs.RootModuleInstance,
|
||||
StateState: state.SyncWrapper(),
|
||||
InstanceExpanderExpander: instances.NewExpander(),
|
||||
|
||||
// DynamicExpand will call EvaluateExpr to evaluate the "count"
|
||||
// expression, which is just a literal number 3 in the fixture config
|
||||
// and so we'll just hard-code this here too.
|
||||
EvaluateExprResult: cty.NumberIntVal(3),
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("error on DynamicExpand: %s", err)
|
||||
}
|
||||
actual := g.StringWithNodeTypes()
|
||||
expected := `data.aws_instance.foo[0] - *terraform.NodeRefreshableDataResourceInstance
|
||||
data.aws_instance.foo[1] - *terraform.NodeRefreshableDataResourceInstance
|
||||
data.aws_instance.foo[2] - *terraform.NodeRefreshableDataResourceInstance
|
||||
data.aws_instance.foo[3] - *terraform.NodeDestroyableDataResourceInstance
|
||||
root - terraform.graphNodeRoot
|
||||
data.aws_instance.foo[0] - *terraform.NodeRefreshableDataResourceInstance
|
||||
data.aws_instance.foo[1] - *terraform.NodeRefreshableDataResourceInstance
|
||||
data.aws_instance.foo[2] - *terraform.NodeRefreshableDataResourceInstance
|
||||
data.aws_instance.foo[3] - *terraform.NodeDestroyableDataResourceInstance
|
||||
`
|
||||
if expected != actual {
|
||||
t.Fatalf("Expected:\n%s\nGot:\n%s", expected, actual)
|
||||
}
|
||||
|
||||
var destroyableDataResource *NodeDestroyableDataResourceInstance
|
||||
for _, v := range g.Vertices() {
|
||||
if r, ok := v.(*NodeDestroyableDataResourceInstance); ok {
|
||||
destroyableDataResource = r
|
||||
}
|
||||
}
|
||||
|
||||
if destroyableDataResource == nil {
|
||||
t.Fatal("failed to find a destroyableDataResource")
|
||||
}
|
||||
|
||||
if destroyableDataResource.ResolvedProvider.Provider.Type == "" {
|
||||
t.Fatal("NodeDestroyableDataResourceInstance missing provider config")
|
||||
}
|
||||
}
|
|
@ -153,7 +153,7 @@ func (n *nodeModuleVariable) Execute(ctx EvalContext, op walkOperation) error {
|
|||
var err error
|
||||
|
||||
switch op {
|
||||
case walkRefresh, walkPlan, walkApply, walkDestroy, walkImport:
|
||||
case walkPlan, walkApply, walkDestroy, walkImport:
|
||||
vals, err = n.EvalModuleCallArgument(ctx, false)
|
||||
if err != nil {
|
||||
return err
|
||||
|
|
|
@ -200,7 +200,7 @@ func (n *NodeApplyableOutput) References() []*addrs.Reference {
|
|||
func (n *NodeApplyableOutput) Execute(ctx EvalContext, op walkOperation) error {
|
||||
switch op {
|
||||
// Everything except walkImport
|
||||
case walkEval, walkRefresh, walkPlan, walkApply, walkValidate, walkDestroy, walkPlanDestroy:
|
||||
case walkEval, walkPlan, walkApply, walkValidate, walkDestroy, walkPlanDestroy:
|
||||
// This has to run before we have a state lock, since evaluation also
|
||||
// reads the state
|
||||
val, diags := ctx.EvaluateExpr(n.Config.Expr, cty.DynamicPseudoType, nil)
|
||||
|
|
|
@ -33,7 +33,7 @@ func (n *NodeApplyableProvider) Execute(ctx EvalContext, op walkOperation) error
|
|||
switch op {
|
||||
case walkValidate:
|
||||
return n.ValidateProvider(ctx, provider)
|
||||
case walkRefresh, walkPlan, walkApply, walkDestroy:
|
||||
case walkPlan, walkApply, walkDestroy:
|
||||
return n.ConfigureProvider(ctx, provider, false)
|
||||
case walkImport:
|
||||
return n.ConfigureProvider(ctx, provider, true)
|
||||
|
|
|
@ -73,46 +73,6 @@ func (n *NodePlanDeposedResourceInstanceObject) EvalTree() EvalNode {
|
|||
|
||||
seq := &EvalSequence{Nodes: make([]EvalNode, 0, 5)}
|
||||
|
||||
// During the refresh walk we will ensure that our record of the deposed
|
||||
// object is up-to-date. If it was already deleted outside of Terraform
|
||||
// then this will remove it from state and thus avoid us planning a
|
||||
// destroy for it during the subsequent plan walk.
|
||||
seq.Nodes = append(seq.Nodes, &EvalOpFilter{
|
||||
Ops: []walkOperation{walkRefresh},
|
||||
Node: &EvalSequence{
|
||||
Nodes: []EvalNode{
|
||||
&EvalGetProvider{
|
||||
Addr: n.ResolvedProvider,
|
||||
Output: &provider,
|
||||
Schema: &providerSchema,
|
||||
},
|
||||
&EvalReadStateDeposed{
|
||||
Addr: addr.Resource,
|
||||
Provider: &provider,
|
||||
ProviderSchema: &providerSchema,
|
||||
Key: n.DeposedKey,
|
||||
Output: &state,
|
||||
},
|
||||
&EvalRefresh{
|
||||
Addr: addr.Resource,
|
||||
ProviderAddr: n.ResolvedProvider,
|
||||
Provider: &provider,
|
||||
ProviderMetas: n.ProviderMetas,
|
||||
ProviderSchema: &providerSchema,
|
||||
State: &state,
|
||||
Output: &state,
|
||||
},
|
||||
&EvalWriteStateDeposed{
|
||||
Addr: addr.Resource,
|
||||
Key: n.DeposedKey,
|
||||
ProviderAddr: n.ResolvedProvider,
|
||||
ProviderSchema: &providerSchema,
|
||||
State: &state,
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
// During the plan walk we always produce a planned destroy change, because
|
||||
// destroying is the only supported action for deposed objects.
|
||||
var change *plans.ResourceInstanceChange
|
||||
|
|
|
@ -1,381 +0,0 @@
|
|||
package terraform
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
|
||||
"github.com/hashicorp/terraform/plans"
|
||||
"github.com/hashicorp/terraform/providers"
|
||||
|
||||
"github.com/hashicorp/terraform/states"
|
||||
|
||||
"github.com/hashicorp/terraform/addrs"
|
||||
"github.com/hashicorp/terraform/dag"
|
||||
"github.com/hashicorp/terraform/tfdiags"
|
||||
)
|
||||
|
||||
// nodeExpandRefreshableResource handles the first layer of resource
|
||||
// expansion durin refresh. We need this extra layer so DynamicExpand is called
|
||||
// twice for the resource, the first to expand the Resource for each module
|
||||
// instance, and the second to expand each ResourceInstance for the expanded
|
||||
// Resources.
|
||||
type nodeExpandRefreshableManagedResource struct {
|
||||
*NodeAbstractResource
|
||||
|
||||
// We attach dependencies to the Resource during refresh, since the
|
||||
// instances are instantiated during DynamicExpand.
|
||||
Dependencies []addrs.ConfigResource
|
||||
}
|
||||
|
||||
var (
|
||||
_ GraphNodeDynamicExpandable = (*nodeExpandRefreshableManagedResource)(nil)
|
||||
_ GraphNodeReferenceable = (*nodeExpandRefreshableManagedResource)(nil)
|
||||
_ GraphNodeReferencer = (*nodeExpandRefreshableManagedResource)(nil)
|
||||
_ GraphNodeConfigResource = (*nodeExpandRefreshableManagedResource)(nil)
|
||||
_ GraphNodeAttachResourceConfig = (*nodeExpandRefreshableManagedResource)(nil)
|
||||
_ GraphNodeAttachDependencies = (*nodeExpandRefreshableManagedResource)(nil)
|
||||
)
|
||||
|
||||
func (n *nodeExpandRefreshableManagedResource) Name() string {
|
||||
return n.NodeAbstractResource.Name() + " (expand)"
|
||||
}
|
||||
|
||||
// GraphNodeAttachDependencies
|
||||
func (n *nodeExpandRefreshableManagedResource) AttachDependencies(deps []addrs.ConfigResource) {
|
||||
n.Dependencies = deps
|
||||
}
|
||||
|
||||
func (n *nodeExpandRefreshableManagedResource) References() []*addrs.Reference {
|
||||
return (&NodeRefreshableManagedResource{NodeAbstractResource: n.NodeAbstractResource}).References()
|
||||
}
|
||||
|
||||
func (n *nodeExpandRefreshableManagedResource) DynamicExpand(ctx EvalContext) (*Graph, error) {
|
||||
var g Graph
|
||||
|
||||
expander := ctx.InstanceExpander()
|
||||
for _, module := range expander.ExpandModule(n.Addr.Module) {
|
||||
g.Add(&NodeRefreshableManagedResource{
|
||||
NodeAbstractResource: n.NodeAbstractResource,
|
||||
Addr: n.Addr.Resource.Absolute(module),
|
||||
Dependencies: n.Dependencies,
|
||||
})
|
||||
}
|
||||
|
||||
return &g, nil
|
||||
}
|
||||
|
||||
// NodeRefreshableManagedResource represents a resource that is expandable into
|
||||
// NodeRefreshableManagedResourceInstance. Resource count orphans are also added.
|
||||
type NodeRefreshableManagedResource struct {
|
||||
*NodeAbstractResource
|
||||
|
||||
Addr addrs.AbsResource
|
||||
|
||||
// We attach dependencies to the Resource during refresh, since the
|
||||
// instances are instantiated during DynamicExpand.
|
||||
Dependencies []addrs.ConfigResource
|
||||
}
|
||||
|
||||
var (
|
||||
_ GraphNodeModuleInstance = (*NodeRefreshableManagedResource)(nil)
|
||||
_ GraphNodeDynamicExpandable = (*NodeRefreshableManagedResource)(nil)
|
||||
_ GraphNodeReferenceable = (*NodeRefreshableManagedResource)(nil)
|
||||
_ GraphNodeReferencer = (*NodeRefreshableManagedResource)(nil)
|
||||
_ GraphNodeConfigResource = (*NodeRefreshableManagedResource)(nil)
|
||||
_ GraphNodeAttachResourceConfig = (*NodeRefreshableManagedResource)(nil)
|
||||
)
|
||||
|
||||
func (n *NodeRefreshableManagedResource) Path() addrs.ModuleInstance {
|
||||
return n.Addr.Module
|
||||
}
|
||||
|
||||
// GraphNodeDynamicExpandable
|
||||
func (n *NodeRefreshableManagedResource) DynamicExpand(ctx EvalContext) (*Graph, error) {
|
||||
var diags tfdiags.Diagnostics
|
||||
|
||||
expander := ctx.InstanceExpander()
|
||||
// Inform our instance expander about our expansion results, and then use
|
||||
// it to calculate the instance addresses we'll expand for.
|
||||
switch {
|
||||
case n.Config.Count != nil:
|
||||
count, countDiags := evaluateCountExpression(n.Config.Count, ctx)
|
||||
diags = diags.Append(countDiags)
|
||||
if countDiags.HasErrors() {
|
||||
return nil, diags.Err()
|
||||
}
|
||||
|
||||
expander.SetResourceCount(n.Addr.Module, n.Addr.Resource, count)
|
||||
|
||||
case n.Config.ForEach != nil:
|
||||
forEachMap, forEachDiags := evaluateForEachExpression(n.Config.ForEach, ctx)
|
||||
if forEachDiags.HasErrors() {
|
||||
return nil, diags.Err()
|
||||
}
|
||||
|
||||
expander.SetResourceForEach(n.Addr.Module, n.Addr.Resource, forEachMap)
|
||||
|
||||
default:
|
||||
expander.SetResourceSingle(n.Addr.Module, n.Addr.Resource)
|
||||
}
|
||||
|
||||
// Next we need to potentially rename an instance address in the state
|
||||
// if we're transitioning whether "count" is set at all.
|
||||
fixResourceCountSetTransition(ctx, n.Addr.Config(), n.Config.Count != nil)
|
||||
instanceAddrs := expander.ExpandResource(n.Addr)
|
||||
|
||||
// Our graph transformers require access to the full state, so we'll
|
||||
// temporarily lock it while we work on this.
|
||||
state := ctx.State().Lock()
|
||||
defer ctx.State().Unlock()
|
||||
|
||||
// The concrete resource factory we'll use
|
||||
concreteResource := func(a *NodeAbstractResourceInstance) dag.Vertex {
|
||||
// Add the config and state since we don't do that via transforms
|
||||
a.Config = n.Config
|
||||
a.ResolvedProvider = n.ResolvedProvider
|
||||
a.Dependencies = n.Dependencies
|
||||
a.ProviderMetas = n.ProviderMetas
|
||||
|
||||
return &NodeRefreshableManagedResourceInstance{
|
||||
NodeAbstractResourceInstance: a,
|
||||
}
|
||||
}
|
||||
|
||||
// Start creating the steps
|
||||
steps := []GraphTransformer{
|
||||
// Expand the count.
|
||||
&ResourceCountTransformer{
|
||||
Concrete: concreteResource,
|
||||
Schema: n.Schema,
|
||||
Addr: n.Addr.Config(),
|
||||
InstanceAddrs: instanceAddrs,
|
||||
},
|
||||
|
||||
// Add the count orphans to make sure these resources are accounted for
|
||||
// during a scale in.
|
||||
&OrphanResourceInstanceCountTransformer{
|
||||
Concrete: concreteResource,
|
||||
Addr: n.Addr,
|
||||
InstanceAddrs: instanceAddrs,
|
||||
State: state,
|
||||
},
|
||||
|
||||
// Attach the state
|
||||
&AttachStateTransformer{State: state},
|
||||
|
||||
// Targeting
|
||||
&TargetsTransformer{Targets: n.Targets},
|
||||
|
||||
// Connect references so ordering is correct
|
||||
&ReferenceTransformer{},
|
||||
|
||||
// Make sure there is a single root
|
||||
&RootTransformer{},
|
||||
}
|
||||
|
||||
// Build the graph
|
||||
b := &BasicGraphBuilder{
|
||||
Steps: steps,
|
||||
Validate: true,
|
||||
Name: "NodeRefreshableManagedResource",
|
||||
}
|
||||
|
||||
graph, diags := b.Build(nil)
|
||||
return graph, diags.ErrWithWarnings()
|
||||
}
|
||||
|
||||
// NodeRefreshableManagedResourceInstance represents a resource that is "applyable":
|
||||
// it is ready to be applied and is represented by a diff.
|
||||
type NodeRefreshableManagedResourceInstance struct {
|
||||
*NodeAbstractResourceInstance
|
||||
}
|
||||
|
||||
var (
|
||||
_ GraphNodeModuleInstance = (*NodeRefreshableManagedResourceInstance)(nil)
|
||||
_ GraphNodeReferenceable = (*NodeRefreshableManagedResourceInstance)(nil)
|
||||
_ GraphNodeReferencer = (*NodeRefreshableManagedResourceInstance)(nil)
|
||||
_ GraphNodeDestroyer = (*NodeRefreshableManagedResourceInstance)(nil)
|
||||
_ GraphNodeConfigResource = (*NodeRefreshableManagedResourceInstance)(nil)
|
||||
_ GraphNodeResourceInstance = (*NodeRefreshableManagedResourceInstance)(nil)
|
||||
_ GraphNodeAttachResourceConfig = (*NodeRefreshableManagedResourceInstance)(nil)
|
||||
_ GraphNodeAttachResourceState = (*NodeRefreshableManagedResourceInstance)(nil)
|
||||
_ GraphNodeExecutable = (*NodeRefreshableManagedResourceInstance)(nil)
|
||||
)
|
||||
|
||||
// GraphNodeDestroyer
|
||||
func (n *NodeRefreshableManagedResourceInstance) DestroyAddr() *addrs.AbsResourceInstance {
|
||||
addr := n.ResourceInstanceAddr()
|
||||
return &addr
|
||||
}
|
||||
|
||||
// GraphNodeEvalable
|
||||
func (n *NodeRefreshableManagedResourceInstance) Execute(ctx EvalContext, op walkOperation) error {
|
||||
addr := n.ResourceInstanceAddr()
|
||||
|
||||
// Eval info is different depending on what kind of resource this is
|
||||
switch addr.Resource.Resource.Mode {
|
||||
case addrs.ManagedResourceMode:
|
||||
if n.instanceState == nil {
|
||||
log.Printf("[TRACE] NodeRefreshableManagedResourceInstance: %s has no existing state to refresh", addr)
|
||||
_, err := n.evalTreeManagedResourceNoState().Eval(ctx)
|
||||
return err
|
||||
}
|
||||
log.Printf("[TRACE] NodeRefreshableManagedResourceInstance: %s will be refreshed", addr)
|
||||
_, err := n.evalTreeManagedResource().Eval(ctx)
|
||||
return err
|
||||
|
||||
case addrs.DataResourceMode:
|
||||
// Get the data source node. If we don't have a configuration
|
||||
// then it is an orphan so we destroy it (remove it from the state).
|
||||
var dn GraphNodeExecutable
|
||||
if n.Config != nil {
|
||||
dn = &NodeRefreshableDataResourceInstance{
|
||||
NodeAbstractResourceInstance: n.NodeAbstractResourceInstance,
|
||||
}
|
||||
} else {
|
||||
dn = &NodeDestroyableDataResourceInstance{
|
||||
NodeAbstractResourceInstance: n.NodeAbstractResourceInstance,
|
||||
}
|
||||
}
|
||||
|
||||
return dn.Execute(ctx, op)
|
||||
default:
|
||||
panic(fmt.Errorf("unsupported resource mode %s", addr.Resource.Resource.Mode))
|
||||
}
|
||||
}
|
||||
|
||||
func (n *NodeRefreshableManagedResourceInstance) evalTreeManagedResource() EvalNode {
|
||||
addr := n.ResourceInstanceAddr()
|
||||
|
||||
// Declare a bunch of variables that are used for state during
|
||||
// evaluation. Most of this are written to by-address below.
|
||||
var provider providers.Interface
|
||||
var providerSchema *ProviderSchema
|
||||
var state *states.ResourceInstanceObject
|
||||
|
||||
// This happened during initial development. All known cases were
|
||||
// fixed and tested but as a sanity check let's assert here.
|
||||
if n.instanceState == nil {
|
||||
err := fmt.Errorf(
|
||||
"No resource state attached for addr: %s\n\n"+
|
||||
"This is a bug. Please report this to Terraform with your configuration\n"+
|
||||
"and state attached. Please be careful to scrub any sensitive information.",
|
||||
addr)
|
||||
return &EvalReturnError{Error: &err}
|
||||
}
|
||||
|
||||
return &EvalSequence{
|
||||
Nodes: []EvalNode{
|
||||
&EvalGetProvider{
|
||||
Addr: n.ResolvedProvider,
|
||||
Output: &provider,
|
||||
Schema: &providerSchema,
|
||||
},
|
||||
|
||||
&EvalReadState{
|
||||
Addr: addr.Resource,
|
||||
Provider: &provider,
|
||||
ProviderSchema: &providerSchema,
|
||||
|
||||
Output: &state,
|
||||
},
|
||||
|
||||
&EvalRefreshDependencies{
|
||||
State: &state,
|
||||
Dependencies: &n.Dependencies,
|
||||
},
|
||||
|
||||
&EvalRefresh{
|
||||
Addr: addr.Resource,
|
||||
ProviderAddr: n.ResolvedProvider,
|
||||
Provider: &provider,
|
||||
ProviderMetas: n.ProviderMetas,
|
||||
ProviderSchema: &providerSchema,
|
||||
State: &state,
|
||||
Output: &state,
|
||||
},
|
||||
|
||||
&EvalWriteState{
|
||||
Addr: addr.Resource,
|
||||
ProviderAddr: n.ResolvedProvider,
|
||||
ProviderSchema: &providerSchema,
|
||||
State: &state,
|
||||
Dependencies: &n.Dependencies,
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// evalTreeManagedResourceNoState produces an EvalSequence for refresh resource
|
||||
// nodes that don't have state attached. An example of where this functionality
|
||||
// is useful is when a resource that already exists in state is being scaled
|
||||
// out, ie: has its resource count increased. In this case, the scaled out node
|
||||
// needs to be available to other nodes (namely data sources) that may depend
|
||||
// on it for proper interpolation, or confusing "index out of range" errors can
|
||||
// occur.
|
||||
//
|
||||
// The steps in this sequence are very similar to the steps carried out in
|
||||
// plan, but nothing is done with the diff after it is created - it is dropped,
|
||||
// and its changes are not counted in the UI.
|
||||
func (n *NodeRefreshableManagedResourceInstance) evalTreeManagedResourceNoState() EvalNode {
|
||||
addr := n.ResourceInstanceAddr()
|
||||
|
||||
// Declare a bunch of variables that are used for state during
|
||||
// evaluation. Most of this are written to by-address below.
|
||||
var provider providers.Interface
|
||||
var providerSchema *ProviderSchema
|
||||
var change *plans.ResourceInstanceChange
|
||||
var state *states.ResourceInstanceObject
|
||||
|
||||
return &EvalSequence{
|
||||
Nodes: []EvalNode{
|
||||
&EvalGetProvider{
|
||||
Addr: n.ResolvedProvider,
|
||||
Output: &provider,
|
||||
Schema: &providerSchema,
|
||||
},
|
||||
|
||||
&EvalReadState{
|
||||
Addr: addr.Resource,
|
||||
Provider: &provider,
|
||||
ProviderSchema: &providerSchema,
|
||||
|
||||
Output: &state,
|
||||
},
|
||||
|
||||
&EvalDiff{
|
||||
Addr: addr.Resource,
|
||||
Config: n.Config,
|
||||
Provider: &provider,
|
||||
ProviderAddr: n.ResolvedProvider,
|
||||
ProviderSchema: &providerSchema,
|
||||
State: &state,
|
||||
OutputChange: &change,
|
||||
OutputState: &state,
|
||||
Stub: true,
|
||||
},
|
||||
|
||||
&EvalWriteState{
|
||||
Addr: addr.Resource,
|
||||
ProviderAddr: n.ResolvedProvider,
|
||||
ProviderSchema: &providerSchema,
|
||||
State: &state,
|
||||
Dependencies: &n.Dependencies,
|
||||
},
|
||||
|
||||
// We must also save the planned change, so that expressions in
|
||||
// other nodes, such as provider configurations and data resources,
|
||||
// can work with the planned new value.
|
||||
//
|
||||
// This depends on the fact that Context.Refresh creates a
|
||||
// temporary new empty changeset for the duration of its graph
|
||||
// walk, and so this recorded change will be discarded immediately
|
||||
// after the refresh walk completes.
|
||||
&EvalWriteDiff{
|
||||
Addr: addr.Resource,
|
||||
Change: &change,
|
||||
ProviderSchema: &providerSchema,
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
|
@ -1,159 +0,0 @@
|
|||
package terraform
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/zclconf/go-cty/cty"
|
||||
|
||||
"github.com/hashicorp/terraform/addrs"
|
||||
"github.com/hashicorp/terraform/instances"
|
||||
)
|
||||
|
||||
func TestNodeRefreshableManagedResourceDynamicExpand_scaleOut(t *testing.T) {
|
||||
m := testModule(t, "refresh-resource-scale-inout")
|
||||
|
||||
state := MustShimLegacyState(&State{
|
||||
Modules: []*ModuleState{
|
||||
&ModuleState{
|
||||
Path: rootModulePath,
|
||||
Resources: map[string]*ResourceState{
|
||||
"aws_instance.foo.0": &ResourceState{
|
||||
Type: "aws_instance",
|
||||
Deposed: []*InstanceState{
|
||||
&InstanceState{
|
||||
ID: "foo",
|
||||
},
|
||||
},
|
||||
},
|
||||
"aws_instance.foo.1": &ResourceState{
|
||||
Type: "aws_instance",
|
||||
Deposed: []*InstanceState{
|
||||
&InstanceState{
|
||||
ID: "bar",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}).SyncWrapper()
|
||||
|
||||
cfgAddr := addrs.RootModule.Resource(addrs.ManagedResourceMode, "aws_instance", "foo")
|
||||
n := &NodeRefreshableManagedResource{
|
||||
NodeAbstractResource: &NodeAbstractResource{
|
||||
Addr: cfgAddr,
|
||||
Config: m.Module.ManagedResources["aws_instance.foo"],
|
||||
},
|
||||
Addr: cfgAddr.Absolute(addrs.RootModuleInstance),
|
||||
}
|
||||
|
||||
g, err := n.DynamicExpand(&MockEvalContext{
|
||||
PathPath: addrs.RootModuleInstance,
|
||||
StateState: state,
|
||||
InstanceExpanderExpander: instances.NewExpander(),
|
||||
|
||||
// DynamicExpand will call EvaluateExpr to evaluate the "count"
|
||||
// expression, which is just a literal number 3 in the fixture config
|
||||
// and so we'll just hard-code this here too.
|
||||
EvaluateExprResult: cty.NumberIntVal(3),
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("error attempting DynamicExpand: %s", err)
|
||||
}
|
||||
|
||||
actual := g.StringWithNodeTypes()
|
||||
expected := `aws_instance.foo[0] - *terraform.NodeRefreshableManagedResourceInstance
|
||||
aws_instance.foo[1] - *terraform.NodeRefreshableManagedResourceInstance
|
||||
aws_instance.foo[2] - *terraform.NodeRefreshableManagedResourceInstance
|
||||
root - terraform.graphNodeRoot
|
||||
aws_instance.foo[0] - *terraform.NodeRefreshableManagedResourceInstance
|
||||
aws_instance.foo[1] - *terraform.NodeRefreshableManagedResourceInstance
|
||||
aws_instance.foo[2] - *terraform.NodeRefreshableManagedResourceInstance
|
||||
`
|
||||
if expected != actual {
|
||||
t.Fatalf("Expected:\n%s\nGot:\n%s", expected, actual)
|
||||
}
|
||||
}
|
||||
|
||||
func TestNodeRefreshableManagedResourceDynamicExpand_scaleIn(t *testing.T) {
|
||||
m := testModule(t, "refresh-resource-scale-inout")
|
||||
|
||||
state := MustShimLegacyState(&State{
|
||||
Modules: []*ModuleState{
|
||||
&ModuleState{
|
||||
Path: rootModulePath,
|
||||
Resources: map[string]*ResourceState{
|
||||
"aws_instance.foo.0": &ResourceState{
|
||||
Type: "aws_instance",
|
||||
Deposed: []*InstanceState{
|
||||
&InstanceState{
|
||||
ID: "foo",
|
||||
},
|
||||
},
|
||||
},
|
||||
"aws_instance.foo.1": &ResourceState{
|
||||
Type: "aws_instance",
|
||||
Deposed: []*InstanceState{
|
||||
&InstanceState{
|
||||
ID: "bar",
|
||||
},
|
||||
},
|
||||
},
|
||||
"aws_instance.foo.2": &ResourceState{
|
||||
Type: "aws_instance",
|
||||
Deposed: []*InstanceState{
|
||||
&InstanceState{
|
||||
ID: "baz",
|
||||
},
|
||||
},
|
||||
},
|
||||
"aws_instance.foo.3": &ResourceState{
|
||||
Type: "aws_instance",
|
||||
Deposed: []*InstanceState{
|
||||
&InstanceState{
|
||||
ID: "qux",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}).SyncWrapper()
|
||||
|
||||
cfgAddr := addrs.RootModule.Resource(addrs.ManagedResourceMode, "aws_instance", "foo")
|
||||
n := &NodeRefreshableManagedResource{
|
||||
NodeAbstractResource: &NodeAbstractResource{
|
||||
Addr: cfgAddr,
|
||||
Config: m.Module.ManagedResources["aws_instance.foo"],
|
||||
},
|
||||
Addr: cfgAddr.Absolute(addrs.RootModuleInstance),
|
||||
}
|
||||
|
||||
g, err := n.DynamicExpand(&MockEvalContext{
|
||||
PathPath: addrs.RootModuleInstance,
|
||||
StateState: state,
|
||||
InstanceExpanderExpander: instances.NewExpander(),
|
||||
|
||||
// DynamicExpand will call EvaluateExpr to evaluate the "count"
|
||||
// expression, which is just a literal number 3 in the fixture config
|
||||
// and so we'll just hard-code this here too.
|
||||
EvaluateExprResult: cty.NumberIntVal(3),
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("error attempting DynamicExpand: %s", err)
|
||||
}
|
||||
actual := g.StringWithNodeTypes()
|
||||
expected := `aws_instance.foo[0] - *terraform.NodeRefreshableManagedResourceInstance
|
||||
aws_instance.foo[1] - *terraform.NodeRefreshableManagedResourceInstance
|
||||
aws_instance.foo[2] - *terraform.NodeRefreshableManagedResourceInstance
|
||||
aws_instance.foo[3] - *terraform.NodeRefreshableManagedResourceInstance
|
||||
root - terraform.graphNodeRoot
|
||||
aws_instance.foo[0] - *terraform.NodeRefreshableManagedResourceInstance
|
||||
aws_instance.foo[1] - *terraform.NodeRefreshableManagedResourceInstance
|
||||
aws_instance.foo[2] - *terraform.NodeRefreshableManagedResourceInstance
|
||||
aws_instance.foo[3] - *terraform.NodeRefreshableManagedResourceInstance
|
||||
`
|
||||
if expected != actual {
|
||||
t.Fatalf("Expected:\n%s\nGot:\n%s", expected, actual)
|
||||
}
|
||||
}
|
|
@ -12,16 +12,15 @@ func _() {
|
|||
_ = x[walkApply-1]
|
||||
_ = x[walkPlan-2]
|
||||
_ = x[walkPlanDestroy-3]
|
||||
_ = x[walkRefresh-4]
|
||||
_ = x[walkValidate-5]
|
||||
_ = x[walkDestroy-6]
|
||||
_ = x[walkImport-7]
|
||||
_ = x[walkEval-8]
|
||||
_ = x[walkValidate-4]
|
||||
_ = x[walkDestroy-5]
|
||||
_ = x[walkImport-6]
|
||||
_ = x[walkEval-7]
|
||||
}
|
||||
|
||||
const _walkOperation_name = "walkInvalidwalkApplywalkPlanwalkPlanDestroywalkRefreshwalkValidatewalkDestroywalkImportwalkEval"
|
||||
const _walkOperation_name = "walkInvalidwalkApplywalkPlanwalkPlanDestroywalkValidatewalkDestroywalkImportwalkEval"
|
||||
|
||||
var _walkOperation_index = [...]uint8{0, 11, 20, 28, 43, 54, 66, 77, 87, 95}
|
||||
var _walkOperation_index = [...]uint8{0, 11, 20, 28, 43, 55, 66, 76, 84}
|
||||
|
||||
func (i walkOperation) String() string {
|
||||
if i >= walkOperation(len(_walkOperation_index)-1) {
|
||||
|
|
Loading…
Reference in New Issue