terraform: support create-before-destroy

This commit is contained in:
Armon Dadgar 2014-09-22 14:25:54 -07:00
parent a14ea76c84
commit aef7718778
3 changed files with 144 additions and 10 deletions

View File

@ -482,6 +482,7 @@ func graphAddConfigResources(
// these nodes for you. // these nodes for you.
func graphAddDiff(g *depgraph.Graph, d *ModuleDiff) error { func graphAddDiff(g *depgraph.Graph, d *ModuleDiff) error {
var nlist []*depgraph.Noun var nlist []*depgraph.Noun
injected := make(map[*depgraph.Dependency]struct{})
for _, n := range g.Nouns { for _, n := range g.Nouns {
rn, ok := n.Meta.(*GraphNodeResource) rn, ok := n.Meta.(*GraphNodeResource)
if !ok { if !ok {
@ -530,13 +531,43 @@ func graphAddDiff(g *depgraph.Graph, d *ModuleDiff) error {
newDiff.Destroy = false newDiff.Destroy = false
rd = newDiff rd = newDiff
// Add to the new noun to our dependencies so that the destroy // The dependency ordering depends on if the CreateBeforeDestroy
// happens before the apply. // flag is enabled. If so, we must create the replacement first,
n.Deps = append(n.Deps, &depgraph.Dependency{ // and then destroy the old instance.
if rn.Config != nil && rn.Config.CreateBeforeDestroy && !rd.Empty() {
dep := &depgraph.Dependency{
Name: n.Name,
Source: newN,
Target: n,
}
// Add the old noun to the new noun dependencies so that
// the create happens before the destroy.
newN.Deps = append(newN.Deps, dep)
// Mark that this dependency has been injected so that
// we do not invert the direction below.
injected[dep] = struct{}{}
// Add a depedency from the root, since the create node
// does not depend on us
g.Root.Deps = append(g.Root.Deps, &depgraph.Dependency{
Name: newN.Name,
Source: g.Root,
Target: newN,
})
} else {
dep := &depgraph.Dependency{
Name: newN.Name, Name: newN.Name,
Source: n, Source: n,
Target: newN, Target: newN,
}) }
// Add the new noun to our dependencies so that
// the destroy happens before the apply.
n.Deps = append(n.Deps, dep)
}
} }
rn.Resource.Diff = rd rn.Resource.Diff = rd
@ -544,7 +575,6 @@ func graphAddDiff(g *depgraph.Graph, d *ModuleDiff) error {
// Go through each noun and make sure we calculate all the dependencies // Go through each noun and make sure we calculate all the dependencies
// properly. // properly.
injected := make(map[*depgraph.Dependency]struct{})
for _, n := range nlist { for _, n := range nlist {
deps := n.Deps deps := n.Deps
num := len(deps) num := len(deps)
@ -948,6 +978,7 @@ func graphAddRoot(g *depgraph.Graph) {
}) })
} }
g.Nouns = append(g.Nouns, root) g.Nouns = append(g.Nouns, root)
g.Root = root
} }
// graphAddVariableDeps inspects all the nouns and adds any dependencies // graphAddVariableDeps inspects all the nouns and adds any dependencies

View File

@ -652,6 +652,92 @@ func TestGraphAddDiff_module(t *testing.T) {
} }
} }
func TestGraphAddDiff_createBeforeDestroy(t *testing.T) {
config := testConfig(t, "graph-diff-create-before")
diff := &Diff{
Resources: map[string]*InstanceDiff{
"aws_instance.bar": &InstanceDiff{
Destroy: true,
Attributes: map[string]*ResourceAttrDiff{
"ami": &ResourceAttrDiff{
Old: "abc",
New: "xyz",
RequiresNew: true,
},
},
},
},
}
state := &State{
Modules: []*ModuleState{
&ModuleState{
Path: rootModulePath,
Resources: map[string]*ResourceState{
"aws_instance.bar": &ResourceState{
Type: "aws_instance",
Primary: &InstanceState{
ID: "bar",
Attributes: map[string]string{
"ami": "abc",
},
},
},
},
},
},
}
diffHash := checksumStruct(t, diff)
g, err := Graph(&GraphOpts{
Config: config,
Diff: diff,
State: state,
})
if err != nil {
t.Fatalf("err: %s", err)
}
actual := strings.TrimSpace(g.String())
expected := strings.TrimSpace(testTerraformGraphDiffCreateBeforeDestroyStr)
if actual != expected {
t.Fatalf("bad:\n\n%s\n\nexpected:\n\n%s", actual, expected)
}
// Verify that our original structure has not been modified
diffHash2 := checksumStruct(t, diff)
if diffHash != diffHash2 {
t.Fatal("diff has been modified")
}
}
func TestGraphInitState(t *testing.T) {
config := testConfig(t, "graph-basic")
state := &State{
Modules: []*ModuleState{
&ModuleState{
Path: rootModulePath,
Resources: map[string]*InstanceDiff{
"aws_instance.foo": &InstanceDiff{
Destroy: true,
},
},
},
},
}
g, err := Graph(&GraphOpts{Module: m, Diff: diff})
if err != nil {
t.Fatalf("err: %s", err)
}
actual := strings.TrimSpace(g.String())
expected := strings.TrimSpace(testTerraformGraphDiffModuleStr)
if actual != expected {
t.Fatalf("bad:\n\n%s", actual)
}
}
func TestGraphAddDiff_moduleDestroy(t *testing.T) { func TestGraphAddDiff_moduleDestroy(t *testing.T) {
m := testModule(t, "graph-diff-module") m := testModule(t, "graph-diff-module")
diff := &Diff{ diff := &Diff{
@ -1044,8 +1130,19 @@ aws_load_balancer.weblb
aws_load_balancer.weblb -> provider.aws aws_load_balancer.weblb -> provider.aws
provider.aws provider.aws
root root
root -> aws_load_balancer.weblb root -> aws_load_balancer.weblb`
`
const testTerraformGraphDiffCreateBeforeDestroyStr = `
root: root
aws_instance.bar
aws_instance.bar -> provider.aws
aws_instance.bar (destroy)
aws_instance.bar (destroy) -> aws_instance.bar
aws_instance.bar (destroy) -> provider.aws
provider.aws
root
root -> aws_instance.bar
root -> aws_instance.bar (destroy)`
const testTerraformGraphStateStr = ` const testTerraformGraphStateStr = `
root: root root: root

View File

@ -0,0 +1,6 @@
provider "aws" {}
resource "aws_instance" "bar" {
ami = "abc"
create_before_destroy = true
}