internal/applying: More buildGraph work

f-possible-apply-refactoring
Martin Atkins 6 years ago
parent c19ce1af27
commit 64bdb48cf3

@ -2,6 +2,7 @@ package applying
import (
"fmt"
"log"
"github.com/hashicorp/terraform/addrs"
"github.com/hashicorp/terraform/configs"
@ -94,6 +95,7 @@ func buildGraphResourceActions(
// is not sufficient on its own.
for _, ricSrc := range plan.Changes.Resources {
instanceAddr := ricSrc.Addr
log.Printf("[TRACE] Apply: buildGraph: deciding actions for %s", instanceAddr)
resourceAddr := instanceAddr.ContainingResource()
resourceMapKey := resourceAddr.String()
resourceSchema, _ := schemas.ResourceTypeConfig(
@ -119,6 +121,7 @@ func buildGraphResourceActions(
configDeps := resources.ResourceDependencies(resourceConfig, resourceSchema, schemas.Provisioners)
deps = append(deps, configDeps...)
}
// TODO: Take into account dependencies recorded in the state too
actions[resourceMapKey] = &resourceActions{
Addr: resourceAddr,
@ -162,6 +165,7 @@ func buildGraphResourceActions(
}
rActions.SetMeta = action
g.Add(action)
log.Printf("[TRACE] Apply: buildGraph: will set metadata for %s", resourceAddr)
}
if needDestroy {
// If we have at least one delete action (where replace actions
@ -182,6 +186,7 @@ func buildGraphResourceActions(
}
rActions.Cleanup = action
g.Add(action)
log.Printf("[TRACE] Apply: buildGraph: will attempt state cleanup for %s", resourceAddr)
}
}
}
@ -211,6 +216,7 @@ func buildGraphResourceActions(
}
riActions.CreateUpdate = action
g.Add(action)
log.Printf("[TRACE] Apply: buildGraph: will %s %s", actionType, resourceAddr)
}
if needDestroy {
actionType := ric.Action
@ -231,6 +237,7 @@ func buildGraphResourceActions(
riActions.DestroyDeposed[ric.DeposedKey] = action
}
g.Add(action)
log.Printf("[TRACE] Apply: buildGraph: will %s %s", actionType, resourceAddr)
}
if ric.Action.IsReplace() {
// When we're replacing we have two nodes, which need a dependency
@ -238,8 +245,10 @@ func buildGraphResourceActions(
switch ric.Action {
case plans.CreateThenDelete:
g.Connect(dag.BasicEdge(riActions.Destroy, riActions.CreateUpdate))
log.Printf("[TRACE] Apply: buildGraph: will create a new %s before destroying the current one", resourceAddr)
case plans.DeleteThenCreate:
g.Connect(dag.BasicEdge(riActions.CreateUpdate, riActions.Destroy))
log.Printf("[TRACE] Apply: buildGraph: will destroy the current %s before creating a new one", resourceAddr)
}
}
}
@ -266,6 +275,11 @@ func buildGraphResourceActions(
for _, riActions := range rActions.Instances {
if riActions.CreateUpdate != nil {
g.Connect(dag.BasicEdge(rActions.Cleanup, riActions.CreateUpdate))
for _, deposedAction := range riActions.DestroyDeposed {
// Don't Create/Update until all deposed objects have
// been destroyed.
g.Connect(dag.BasicEdge(riActions.CreateUpdate, deposedAction))
}
}
if riActions.Destroy != nil {
g.Connect(dag.BasicEdge(rActions.Cleanup, riActions.Destroy))
@ -285,6 +299,41 @@ func buildGraphResourceActions(
}
}
// The handling of references from resource to resources is a little
// special because of how we need to model reverse dependencies for
// destroying, so we'll handle those up front here while letting the
// caller handle the more straightforward cases of references to/from
// named values, etc.
for _, rActions := range actions {
refs := findConfigReferences(rActions.Addr.Module, rActions.Dependencies)
for _, otherResourceAddr := range refs.Resources {
log.Printf("[TRACE] Apply: buildGraph: %s refers to %s", rActions.Addr, otherResourceAddr)
if rActions.Addr.Equal(otherResourceAddr) {
continue // don't create self-references
}
otherRActions := actions[otherResourceAddr.String()]
if otherRActions == nil {
continue
}
// For every combination of instances in the referer and the
// referent we'll create dependency edges between the CreateUpdate
// actions and between the Destroy actions, where present.
for _, riActions := range rActions.Instances {
for _, otherRIActions := range otherRActions.Instances {
if riActions.CreateUpdate != nil && otherRIActions.CreateUpdate != nil {
g.Connect(dag.BasicEdge(riActions.CreateUpdate, otherRIActions.CreateUpdate))
}
if riActions.Destroy != nil && otherRIActions.Destroy != nil {
// The destroy-to-destroy dependencies are inverted,
// because if A depends on B then A must be destroyed
// before B is destroyed.
g.Connect(dag.BasicEdge(otherRIActions.Destroy, riActions.Destroy))
}
}
}
}
}
return actions, nil
}
@ -337,3 +386,55 @@ func buildProviderConfigActions(
return actions, nil
}
// buildNamedValueActionsAndReferences uses the references from objects already
// in the graph to detect any additional named value actions that would be
// needed for a correct traversal and any additional dependency edges that
// are not already present in the graph. It then creates those missing actions
// and edges, iterating until no more need to be added.
func buildNamedValueActionsAndReferences(
g *dag.AcyclicGraph,
resourceActions map[string]*resourceActions,
providerConfigActions map[string]*providerConfigActions,
existingNamedValueActions map[string]*namedValueActions,
) (map[string]*namedValueActions, error) {
namedValueActions := make(map[string]*namedValueActions, len(existingNamedValueActions))
for k, v := range existingNamedValueActions {
namedValueActions[k] = v
}
more := true
for more {
more = false // will be set back to true if the work below changes anything
for _, rAction := range resourceActions {
refs := findConfigReferences(rAction.Addr.Module, rAction.Dependencies)
if changed := addMissingActionsAndEdges(
g,
resourceActions,
providerConfigActions,
namedValueActions,
refs,
func(a action) {
rAction.AllRequire(a, g)
},
); changed {
more = true
}
}
}
return namedValueActions, nil
}
func addMissingActionsAndEdges(
g *dag.AcyclicGraph,
resourceActions map[string]*resourceActions,
providerConfigActions map[string]*providerConfigActions,
namedValueActions map[string]*namedValueActions,
refs configReferences,
connectTo func(target action),
) (changed bool) {
return changed
}

@ -21,14 +21,18 @@ type configReferences struct {
Resources map[string]addrs.AbsResource
}
func findConfigReferences(moduleAddr addrs.ModuleInstance, refAddrs []addrs.Referenceable) configReferences {
ret := configReferences{
func newConfigReferences() configReferences {
return configReferences{
InputVariables: make(map[string]addrs.AbsInputVariableInstance),
LocalValues: make(map[string]addrs.AbsLocalValue),
OutputValues: make(map[string]addrs.AbsOutputValue),
AllModuleOutputs: make(map[string]addrs.ModuleInstance),
Resources: make(map[string]addrs.AbsResource),
}
}
func findConfigReferences(moduleAddr addrs.ModuleInstance, refAddrs []addrs.Referenceable) configReferences {
ret := newConfigReferences()
for _, addr := range refAddrs {
switch addr := addr.(type) {
case addrs.InputVariable:
@ -57,3 +61,38 @@ func findConfigReferences(moduleAddr addrs.ModuleInstance, refAddrs []addrs.Refe
}
return ret
}
func (cr configReferences) Merge(other configReferences) configReferences {
ret := newConfigReferences()
for k, v := range cr.InputVariables {
ret.InputVariables[k] = v
}
for k, v := range other.InputVariables {
ret.InputVariables[k] = v
}
for k, v := range cr.LocalValues {
ret.LocalValues[k] = v
}
for k, v := range other.LocalValues {
ret.LocalValues[k] = v
}
for k, v := range cr.OutputValues {
ret.OutputValues[k] = v
}
for k, v := range other.OutputValues {
ret.OutputValues[k] = v
}
for k, v := range cr.AllModuleOutputs {
ret.AllModuleOutputs[k] = v
}
for k, v := range other.AllModuleOutputs {
ret.AllModuleOutputs[k] = v
}
for k, v := range cr.Resources {
ret.Resources[k] = v
}
for k, v := range other.Resources {
ret.Resources[k] = v
}
return ret
}

Loading…
Cancel
Save