From 8dc4c56b2e69eeb67679a2533924ad67fce5d1cd Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Tue, 27 Jan 2015 14:28:33 -0800 Subject: [PATCH] terraform: tainted transformer --- .../transform-tainted-basic/main.tf | 1 + terraform/transform_tainted.go | 57 +++++++++++++++++++ terraform/transform_tainted_test.go | 49 ++++++++++++++++ 3 files changed, 107 insertions(+) create mode 100644 terraform/test-fixtures/transform-tainted-basic/main.tf create mode 100644 terraform/transform_tainted.go create mode 100644 terraform/transform_tainted_test.go diff --git a/terraform/test-fixtures/transform-tainted-basic/main.tf b/terraform/test-fixtures/transform-tainted-basic/main.tf new file mode 100644 index 000000000..64cbf6236 --- /dev/null +++ b/terraform/test-fixtures/transform-tainted-basic/main.tf @@ -0,0 +1 @@ +resource "aws_instance" "web" {} diff --git a/terraform/transform_tainted.go b/terraform/transform_tainted.go new file mode 100644 index 000000000..ff3c67cf8 --- /dev/null +++ b/terraform/transform_tainted.go @@ -0,0 +1,57 @@ +package terraform + +import ( + "fmt" +) + +// TraintedTransformer is a GraphTransformer that adds tainted resources +// to the graph. +type TaintedTransformer struct { + // State is the global state. We'll automatically find the correct + // ModuleState based on the Graph.Path that is being transformed. + State *State +} + +func (t *TaintedTransformer) Transform(g *Graph) error { + state := t.State.ModuleByPath(g.Path) + if state == nil { + // If there is no state for our module there can't be any tainted + // resources, since they live in the state. + return nil + } + + // Go through all the resources in our state to look for tainted resources + for k, rs := range state.Resources { + // If we have no tainted resources, then move on + if len(rs.Tainted) == 0 { + continue + } + + for i, _ := range rs.Tainted { + g.Add(&graphNodeTaintedResource{ + Index: i, + ResourceName: k, + }) + } + } + + return nil +} + +// graphNodeTaintedResource is the graph vertex representing a tainted resource. +type graphNodeTaintedResource struct { + Index int + ResourceName string +} + +func (n *graphNodeTaintedResource) DependableName() []string { + return []string{n.dependableName()} +} + +func (n *graphNodeTaintedResource) Name() string { + return fmt.Sprintf("%s (tainted #%d)", n.ResourceName, n.Index+1) +} + +func (n *graphNodeTaintedResource) dependableName() string { + return n.ResourceName +} diff --git a/terraform/transform_tainted_test.go b/terraform/transform_tainted_test.go new file mode 100644 index 000000000..e5baa26ad --- /dev/null +++ b/terraform/transform_tainted_test.go @@ -0,0 +1,49 @@ +package terraform + +import ( + "strings" + "testing" +) + +func TestTaintedTransformer(t *testing.T) { + mod := testModule(t, "transform-tainted-basic") + state := &State{ + Modules: []*ModuleState{ + &ModuleState{ + Path: RootModulePath, + Resources: map[string]*ResourceState{ + "aws_instance.web": &ResourceState{ + Type: "aws_instance", + Tainted: []*InstanceState{ + &InstanceState{ID: "foo"}, + }, + }, + }, + }, + }, + } + + g := Graph{Path: RootModulePath} + { + tf := &ConfigTransformer{Module: mod} + if err := tf.Transform(&g); err != nil { + t.Fatalf("err: %s", err) + } + } + + transform := &TaintedTransformer{State: state} + if err := transform.Transform(&g); err != nil { + t.Fatalf("err: %s", err) + } + + actual := strings.TrimSpace(g.String()) + expected := strings.TrimSpace(testTransformTaintedBasicStr) + if actual != expected { + t.Fatalf("bad:\n\n%s", actual) + } +} + +const testTransformTaintedBasicStr = ` +aws_instance.web +aws_instance.web (tainted #1) +`