terraform: start apply-based graph builder, basic diff transform

This commit is contained in:
Mitchell Hashimoto 2016-09-13 10:56:37 -07:00
parent 361bc5a8df
commit 3f090df26e
No known key found for this signature in database
GPG Key ID: 744E147AA52F5B0A
4 changed files with 164 additions and 0 deletions

View File

@ -0,0 +1,44 @@
package terraform
import (
"github.com/hashicorp/terraform/config/module"
)
// ApplyGraphBuilder implements GraphBuilder and is responsible for building
// a graph for applying a Terraform diff.
//
// Because the graph is built from the diff (vs. the config or state),
// this helps ensure that the apply-time graph doesn't modify any resources
// that aren't explicitly in the diff. There are other scenarios where the
// diff can be deviated, so this is just one layer of protection.
type ApplyGraphBuilder struct {
// Config is the root module for the graph to build.
Config *module.Tree
// Diff is the diff to apply.
Diff *Diff
// Providers is the list of providers supported.
Providers []string
// Provisioners is the list of provisioners supported.
Provisioners []string
}
// See GraphBuilder
func (b *ApplyGraphBuilder) Build(path []string) (*Graph, error) {
return (&BasicGraphBuilder{
Steps: b.Steps(),
Validate: true,
}).Build(path)
}
// See GraphBuilder
func (b *ApplyGraphBuilder) Steps() []GraphTransformer {
steps := []GraphTransformer{
// Creates all the nodes represented in the diff.
&DiffTransformer{Diff: b.Diff},
}
return steps
}

View File

@ -0,0 +1,57 @@
package terraform
import (
"reflect"
"strings"
"testing"
)
func TestApplyGraphBuilder_impl(t *testing.T) {
var _ GraphBuilder = new(ApplyGraphBuilder)
}
func TestApplyGraphBuilder(t *testing.T) {
diff := &Diff{
Modules: []*ModuleDiff{
&ModuleDiff{
Path: []string{"root"},
Resources: map[string]*InstanceDiff{
// Verify noop doesn't show up in graph
"aws_instance.noop": &InstanceDiff{},
"aws_instance.create": &InstanceDiff{
Attributes: map[string]*ResourceAttrDiff{
"name": &ResourceAttrDiff{
Old: "",
New: "foo",
},
},
},
},
},
},
}
b := &ApplyGraphBuilder{
Diff: diff,
}
g, err := b.Build(RootModulePath)
if err != nil {
t.Fatalf("err: %s", err)
}
if !reflect.DeepEqual(g.Path, RootModulePath) {
t.Fatalf("bad: %#v", g.Path)
}
actual := strings.TrimSpace(g.String())
expected := strings.TrimSpace(testApplyGraphBuilderStr)
if actual != expected {
t.Fatalf("bad: %s", actual)
}
}
const testApplyGraphBuilderStr = `
aws_instance.create
`

View File

@ -0,0 +1,10 @@
package terraform
// NodeResource is a graph node for referencing a resource.
type NodeResource struct {
Addr *ResourceAddress // Addr is the address for this resource
}
func (n *NodeResource) Name() string {
return n.Addr.String()
}

View File

@ -0,0 +1,53 @@
package terraform
import (
"fmt"
)
// DiffTransformer is a GraphTransformer that adds the elements of
// the diff to the graph.
//
// This transform is used for example by the ApplyGraphBuilder to ensure
// that only resources that are being modified are represented in the graph.
type DiffTransformer struct {
Diff *Diff
}
func (t *DiffTransformer) Transform(g *Graph) error {
// If the diff is nil or empty (nil is empty) then do nothing
if t.Diff.Empty() {
return nil
}
// Go through all the modules in the diff.
for _, m := range t.Diff.Modules {
// TODO: If this is a destroy diff then add a module destroy node
// Go through all the resources in this module.
for name, inst := range m.Resources {
// TODO: Destroy diff
// If this diff has no attribute changes, then we have
// nothing to do and therefore won't add it to the graph.
if len(inst.Attributes) == 0 {
continue
}
// We have changes! This is a create or update operation.
// First grab the address so we have a unique way to
// reference this resource.
addr, err := parseResourceAddressInternal(name)
if err != nil {
return fmt.Errorf(
"Error parsing internal name, this is a bug: %q", name)
}
// Add the resource to the graph
g.Add(&NodeResource{
Addr: addr,
})
}
}
return nil
}