terraform: save/restore dependency info for orphans

This commit is contained in:
Armon Dadgar 2014-09-17 17:01:45 -07:00
parent 31653fcbcb
commit c14a17f39b
2 changed files with 83 additions and 1 deletions

View File

@ -131,6 +131,10 @@ func (c *Context) Apply() (*State, error) {
err = g.Walk(c.applyWalkFn())
log.Printf("[INFO] Apply walk complete")
// Encode the dependencies, this pushes the logical dependencies
// into the state so that we can recover it later.
EncodeDependencies(g)
// Prune the state so that we have as clean a state as possible
c.state.prune()

View File

@ -170,6 +170,45 @@ func Graph(opts *GraphOpts) (*depgraph.Graph, error) {
return g, nil
}
// EncodeDependencies is used to walk the graph and encode the
// logical dependency information into the resource state. This
// allows the dependency tree to be recovered from the state file
// such that orphaned resources will still be destroyed in the
// proper order.
func EncodeDependencies(g *depgraph.Graph) {
for _, n := range g.Nouns {
// Ignore any non-resource nodes
rn, ok := n.Meta.(*GraphNodeResource)
if !ok {
continue
}
// Skip only if the resource has state
rs := rn.Resource
state := rs.State
if state == nil {
continue
}
// Update the dependencies
var inject []string
for _, dep := range n.Deps {
switch target := dep.Target.Meta.(type) {
case *GraphNodeResource:
inject = append(inject, target.Resource.Id)
// TODO: case *GraphNodeResourceMeta?
}
}
// Inject any of the missing depedencies
for _, dep := range inject {
if !strSliceContains(state.Dependencies, dep) {
state.Dependencies = append(state.Dependencies, dep)
}
}
}
}
// configGraph turns a configuration structure into a dependency graph.
func graphAddConfigResources(
g *depgraph.Graph, c *config.Config, s *State) {
@ -513,6 +552,7 @@ func graphAddOrphans(g *depgraph.Graph, c *config.Config, s *State) {
if mod == nil {
return
}
var nlist []*depgraph.Noun
for _, k := range mod.Orphans(c) {
rs := mod.Resources[k]
noun := &depgraph.Noun{
@ -528,7 +568,45 @@ func graphAddOrphans(g *depgraph.Graph, c *config.Config, s *State) {
},
},
}
g.Nouns = append(g.Nouns, noun)
// Append it to the list so we handle it later
nlist = append(nlist, noun)
}
// Add the nouns to the graph
g.Nouns = append(g.Nouns, nlist...)
// Handle the orphan dependencies after adding them
// to the graph because there may be depedencies between the
// orphans that otherwise cannot be handled
for _, n := range nlist {
rn := n.Meta.(*GraphNodeResource)
// If we have no dependencies, then just continue
deps := rn.Resource.State.Dependencies
if len(deps) == 0 {
continue
}
for _, n2 := range nlist {
rn2 := n2.Meta.(*GraphNodeResource)
// Don't ever depend on ourselves
if rn2 == rn {
continue
}
for _, depName := range rn.Resource.State.Dependencies {
if rn2.Resource.Id != depName {
continue
}
dep := &depgraph.Dependency{
Name: depName,
Source: n,
Target: n2,
}
n.Deps = append(n.Deps, dep)
}
}
}
}