terraform: diff is split down into modules
This commit is contained in:
parent
29603f36d2
commit
bc67e7c443
|
@ -831,7 +831,7 @@ func (c *Context) planWalkFn(result *Plan) depgraph.WalkFunc {
|
|||
|
||||
l.Lock()
|
||||
if !diff.Empty() {
|
||||
result.Diff.Resources[r.Id] = diff
|
||||
result.Diff.RootModule().Resources[r.Id] = diff
|
||||
}
|
||||
l.Unlock()
|
||||
|
||||
|
@ -872,7 +872,7 @@ func (c *Context) planDestroyWalkFn(result *Plan) depgraph.WalkFunc {
|
|||
|
||||
l.Lock()
|
||||
defer l.Unlock()
|
||||
result.Diff.Resources[r.Id] = &InstanceDiff{Destroy: true}
|
||||
result.Diff.RootModule().Resources[r.Id] = &InstanceDiff{Destroy: true}
|
||||
} else {
|
||||
log.Printf("[DEBUG] %s: Not marking for destroy, no ID", r.Id)
|
||||
}
|
||||
|
|
|
@ -1394,8 +1394,8 @@ func TestContextPlan(t *testing.T) {
|
|||
t.Fatalf("err: %s", err)
|
||||
}
|
||||
|
||||
if len(plan.Diff.Resources) < 2 {
|
||||
t.Fatalf("bad: %#v", plan.Diff.Resources)
|
||||
if len(plan.Diff.RootModule().Resources) < 2 {
|
||||
t.Fatalf("bad: %#v", plan.Diff.RootModule().Resources)
|
||||
}
|
||||
|
||||
actual := strings.TrimSpace(plan.String())
|
||||
|
@ -1458,8 +1458,8 @@ func TestContextPlan_nil(t *testing.T) {
|
|||
if err != nil {
|
||||
t.Fatalf("err: %s", err)
|
||||
}
|
||||
if len(plan.Diff.Resources) != 0 {
|
||||
t.Fatalf("bad: %#v", plan.Diff.Resources)
|
||||
if len(plan.Diff.RootModule().Resources) != 0 {
|
||||
t.Fatalf("bad: %#v", plan.Diff.RootModule().Resources)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1525,8 +1525,8 @@ func TestContextPlan_count(t *testing.T) {
|
|||
t.Fatalf("err: %s", err)
|
||||
}
|
||||
|
||||
if len(plan.Diff.Resources) < 6 {
|
||||
t.Fatalf("bad: %#v", plan.Diff.Resources)
|
||||
if len(plan.Diff.RootModule().Resources) < 6 {
|
||||
t.Fatalf("bad: %#v", plan.Diff.RootModule().Resources)
|
||||
}
|
||||
|
||||
actual := strings.TrimSpace(plan.String())
|
||||
|
@ -1715,8 +1715,8 @@ func TestContextPlan_destroy(t *testing.T) {
|
|||
t.Fatalf("err: %s", err)
|
||||
}
|
||||
|
||||
if len(plan.Diff.Resources) != 2 {
|
||||
t.Fatalf("bad: %#v", plan.Diff.Resources)
|
||||
if len(plan.Diff.RootModule().Resources) != 2 {
|
||||
t.Fatalf("bad: %#v", plan.Diff.RootModule().Resources)
|
||||
}
|
||||
|
||||
actual := strings.TrimSpace(plan.String())
|
||||
|
@ -1880,8 +1880,8 @@ func TestContextPlan_state(t *testing.T) {
|
|||
t.Fatalf("err: %s", err)
|
||||
}
|
||||
|
||||
if len(plan.Diff.Resources) < 2 {
|
||||
t.Fatalf("bad: %#v", plan.Diff.Resources)
|
||||
if len(plan.Diff.RootModule().Resources) < 2 {
|
||||
t.Fatalf("bad: %#v", plan.Diff.RootModule().Resources)
|
||||
}
|
||||
|
||||
actual := strings.TrimSpace(plan.String())
|
||||
|
|
|
@ -1,29 +1,98 @@
|
|||
package terraform
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"sort"
|
||||
"strings"
|
||||
"sync"
|
||||
)
|
||||
|
||||
// Diff tracks the differences between resources to apply.
|
||||
// Diff trackes the changes that are necessary to apply a configuration
|
||||
// to an existing infrastructure.
|
||||
type Diff struct {
|
||||
Resources map[string]*InstanceDiff
|
||||
once sync.Once
|
||||
// Modules contains all the modules that have a diff
|
||||
Modules []*ModuleDiff
|
||||
}
|
||||
|
||||
// ModuleByPath is used to lookup the module diff for the given path.
|
||||
// This should be the prefered lookup mechanism as it allows for future
|
||||
// lookup optimizations.
|
||||
func (d *Diff) ModuleByPath(path []string) *ModuleDiff {
|
||||
if d == nil {
|
||||
return nil
|
||||
}
|
||||
for _, mod := range d.Modules {
|
||||
if mod.Path == nil {
|
||||
panic("missing module path")
|
||||
}
|
||||
if reflect.DeepEqual(mod.Path, path) {
|
||||
return mod
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// RootModule returns the ModuleState for the root module
|
||||
func (d *Diff) RootModule() *ModuleDiff {
|
||||
root := d.ModuleByPath(rootModulePath)
|
||||
if root == nil {
|
||||
panic("missing root module")
|
||||
}
|
||||
return root
|
||||
}
|
||||
|
||||
func (d *Diff) String() string {
|
||||
var buf bytes.Buffer
|
||||
for _, m := range d.Modules {
|
||||
mStr := m.String()
|
||||
|
||||
// If we're the root module, we just write the output directly.
|
||||
if reflect.DeepEqual(m.Path, rootModulePath) {
|
||||
buf.WriteString(mStr + "\n")
|
||||
continue
|
||||
}
|
||||
|
||||
buf.WriteString(fmt.Sprintf("module.%s:\n", strings.Join(m.Path[1:], ".")))
|
||||
|
||||
s := bufio.NewScanner(strings.NewReader(mStr))
|
||||
for s.Scan() {
|
||||
buf.WriteString(fmt.Sprintf(" %s\n", s.Text()))
|
||||
}
|
||||
}
|
||||
|
||||
return strings.TrimSpace(buf.String())
|
||||
}
|
||||
|
||||
func (d *Diff) init() {
|
||||
d.once.Do(func() {
|
||||
if d.Modules == nil {
|
||||
rootDiff := &ModuleDiff{Path: rootModulePath}
|
||||
d.Modules = []*ModuleDiff{rootDiff}
|
||||
}
|
||||
for _, m := range d.Modules {
|
||||
m.init()
|
||||
}
|
||||
}
|
||||
|
||||
// ModuleDiff tracks the differences between resources to apply within
|
||||
// a single module.
|
||||
type ModuleDiff struct {
|
||||
Path []string
|
||||
Resources map[string]*InstanceDiff
|
||||
}
|
||||
|
||||
func (d *ModuleDiff) init() {
|
||||
if d.Resources == nil {
|
||||
d.Resources = make(map[string]*InstanceDiff)
|
||||
}
|
||||
})
|
||||
for _, r := range d.Resources {
|
||||
r.init()
|
||||
}
|
||||
}
|
||||
|
||||
// Empty returns true if the diff has no changes.
|
||||
func (d *Diff) Empty() bool {
|
||||
// Empty returns true if the diff has no changes within this module.
|
||||
func (d *ModuleDiff) Empty() bool {
|
||||
if len(d.Resources) == 0 {
|
||||
return true
|
||||
}
|
||||
|
@ -39,7 +108,7 @@ func (d *Diff) Empty() bool {
|
|||
|
||||
// String outputs the diff in a long but command-line friendly output
|
||||
// format that users can read to quickly inspect a diff.
|
||||
func (d *Diff) String() string {
|
||||
func (d *ModuleDiff) String() string {
|
||||
var buf bytes.Buffer
|
||||
|
||||
names := make([]string, 0, len(d.Resources))
|
||||
|
@ -110,8 +179,6 @@ type InstanceDiff struct {
|
|||
Attributes map[string]*ResourceAttrDiff
|
||||
Destroy bool
|
||||
DestroyTainted bool
|
||||
|
||||
once sync.Once
|
||||
}
|
||||
|
||||
// ResourceAttrDiff is the diff of a single attribute of a resource.
|
||||
|
@ -143,11 +210,9 @@ const (
|
|||
)
|
||||
|
||||
func (d *InstanceDiff) init() {
|
||||
d.once.Do(func() {
|
||||
if d.Attributes == nil {
|
||||
d.Attributes = make(map[string]*ResourceAttrDiff)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// Empty returns true if this diff encapsulates no changes.
|
||||
|
|
|
@ -5,8 +5,8 @@ import (
|
|||
"testing"
|
||||
)
|
||||
|
||||
func TestDiff_Empty(t *testing.T) {
|
||||
diff := new(Diff)
|
||||
func TestModuleDiff_Empty(t *testing.T) {
|
||||
diff := new(ModuleDiff)
|
||||
if !diff.Empty() {
|
||||
t.Fatal("should be empty")
|
||||
}
|
||||
|
@ -38,8 +38,8 @@ func TestDiff_Empty(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestDiff_String(t *testing.T) {
|
||||
diff := &Diff{
|
||||
func TestModuleDiff_String(t *testing.T) {
|
||||
diff := &ModuleDiff{
|
||||
Resources: map[string]*InstanceDiff{
|
||||
"nodeA": &InstanceDiff{
|
||||
Attributes: map[string]*ResourceAttrDiff{
|
||||
|
@ -62,7 +62,7 @@ func TestDiff_String(t *testing.T) {
|
|||
}
|
||||
|
||||
actual := strings.TrimSpace(diff.String())
|
||||
expected := strings.TrimSpace(diffStrBasic)
|
||||
expected := strings.TrimSpace(moduleDiffStrBasic)
|
||||
if actual != expected {
|
||||
t.Fatalf("bad:\n%s", actual)
|
||||
}
|
||||
|
@ -206,7 +206,7 @@ func TestResourceDiffSame(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
const diffStrBasic = `
|
||||
const moduleDiffStrBasic = `
|
||||
CREATE: nodeA
|
||||
bar: "foo" => "<computed>"
|
||||
foo: "foo" => "bar"
|
||||
|
|
|
@ -132,10 +132,14 @@ func Graph(opts *GraphOpts) (*depgraph.Graph, error) {
|
|||
|
||||
config := currentModule.Config()
|
||||
|
||||
// Get the state of the module that we're working with.
|
||||
var mod *ModuleState
|
||||
// Get the state and diff of the module that we're working with.
|
||||
var modDiff *ModuleDiff
|
||||
var modState *ModuleState
|
||||
if opts.Diff != nil {
|
||||
modDiff = opts.Diff.ModuleByPath(opts.ModulePath)
|
||||
}
|
||||
if opts.State != nil {
|
||||
mod = opts.State.ModuleByPath(opts.ModulePath)
|
||||
modState = opts.State.ModuleByPath(opts.ModulePath)
|
||||
}
|
||||
|
||||
log.Printf("[DEBUG] Creating graph...")
|
||||
|
@ -145,7 +149,7 @@ func Graph(opts *GraphOpts) (*depgraph.Graph, error) {
|
|||
// First, build the initial resource graph. This only has the resources
|
||||
// and no dependencies. This only adds resources that are in the config
|
||||
// and not "orphans" (that are in the state, but not in the config).
|
||||
graphAddConfigResources(g, config, mod)
|
||||
graphAddConfigResources(g, config, modState)
|
||||
|
||||
// Add the modules that are in the configuration.
|
||||
if err := graphAddConfigModules(g, config, opts); err != nil {
|
||||
|
@ -155,12 +159,12 @@ func Graph(opts *GraphOpts) (*depgraph.Graph, error) {
|
|||
// Add explicit dependsOn dependencies to the graph
|
||||
graphAddExplicitDeps(g)
|
||||
|
||||
if mod != nil {
|
||||
if modState != nil {
|
||||
// Next, add the state orphans if we have any
|
||||
graphAddOrphans(g, config, mod)
|
||||
graphAddOrphans(g, config, modState)
|
||||
|
||||
// Add tainted resources if we have any.
|
||||
graphAddTainted(g, mod)
|
||||
graphAddTainted(g, modState)
|
||||
}
|
||||
|
||||
// Map the provider configurations to all of the resources
|
||||
|
@ -198,8 +202,8 @@ func Graph(opts *GraphOpts) (*depgraph.Graph, error) {
|
|||
}
|
||||
|
||||
// If we have a diff, then make sure to add that in
|
||||
if opts.Diff != nil {
|
||||
if err := graphAddDiff(g, opts.Diff); err != nil {
|
||||
if modDiff != nil {
|
||||
if err := graphAddDiff(g, modDiff); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
@ -423,7 +427,7 @@ func graphAddConfigResources(
|
|||
// destroying the VPC's subnets first, whereas creating a VPC requires
|
||||
// doing it before the subnets are created. This function handles inserting
|
||||
// these nodes for you.
|
||||
func graphAddDiff(g *depgraph.Graph, d *Diff) error {
|
||||
func graphAddDiff(g *depgraph.Graph, d *ModuleDiff) error {
|
||||
var nlist []*depgraph.Noun
|
||||
for _, n := range g.Nouns {
|
||||
rn, ok := n.Meta.(*GraphNodeResource)
|
||||
|
|
|
@ -357,6 +357,9 @@ func TestGraphProvisioners(t *testing.T) {
|
|||
func TestGraphAddDiff(t *testing.T) {
|
||||
m := testModule(t, "graph-diff")
|
||||
diff := &Diff{
|
||||
Modules: []*ModuleDiff{
|
||||
&ModuleDiff{
|
||||
Path: rootModulePath,
|
||||
Resources: map[string]*InstanceDiff{
|
||||
"aws_instance.foo": &InstanceDiff{
|
||||
Attributes: map[string]*ResourceAttrDiff{
|
||||
|
@ -366,6 +369,8 @@ func TestGraphAddDiff(t *testing.T) {
|
|||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
g, err := Graph(&GraphOpts{Module: m, Diff: diff})
|
||||
|
@ -383,7 +388,7 @@ func TestGraphAddDiff(t *testing.T) {
|
|||
n := g.Noun("aws_instance.foo")
|
||||
rn := n.Meta.(*GraphNodeResource)
|
||||
|
||||
expected2 := diff.Resources["aws_instance.foo"]
|
||||
expected2 := diff.RootModule().Resources["aws_instance.foo"]
|
||||
actual2 := rn.Resource.Diff
|
||||
if !reflect.DeepEqual(actual2, expected2) {
|
||||
t.Fatalf("bad: %#v", actual2)
|
||||
|
@ -393,6 +398,9 @@ func TestGraphAddDiff(t *testing.T) {
|
|||
func TestGraphAddDiff_destroy(t *testing.T) {
|
||||
m := testModule(t, "graph-diff-destroy")
|
||||
diff := &Diff{
|
||||
Modules: []*ModuleDiff{
|
||||
&ModuleDiff{
|
||||
Path: rootModulePath,
|
||||
Resources: map[string]*InstanceDiff{
|
||||
"aws_instance.foo": &InstanceDiff{
|
||||
Destroy: true,
|
||||
|
@ -401,6 +409,8 @@ func TestGraphAddDiff_destroy(t *testing.T) {
|
|||
Destroy: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
state := &State{
|
||||
Modules: []*ModuleState{
|
||||
|
@ -463,6 +473,9 @@ func TestGraphAddDiff_destroy(t *testing.T) {
|
|||
func TestGraphAddDiff_destroy_counts(t *testing.T) {
|
||||
m := testModule(t, "graph-count")
|
||||
diff := &Diff{
|
||||
Modules: []*ModuleDiff{
|
||||
&ModuleDiff{
|
||||
Path: rootModulePath,
|
||||
Resources: map[string]*InstanceDiff{
|
||||
"aws_instance.web.0": &InstanceDiff{
|
||||
Destroy: true,
|
||||
|
@ -477,6 +490,8 @@ func TestGraphAddDiff_destroy_counts(t *testing.T) {
|
|||
Destroy: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
state := &State{
|
||||
Modules: []*ModuleState{
|
||||
|
|
|
@ -55,7 +55,7 @@ func (p *Plan) String() string {
|
|||
buf := new(bytes.Buffer)
|
||||
buf.WriteString("DIFF:\n\n")
|
||||
buf.WriteString(p.Diff.String())
|
||||
buf.WriteString("\nSTATE:\n\n")
|
||||
buf.WriteString("\n\nSTATE:\n\n")
|
||||
buf.WriteString(p.State.String())
|
||||
return buf.String()
|
||||
}
|
||||
|
|
|
@ -11,6 +11,9 @@ func TestReadWritePlan(t *testing.T) {
|
|||
plan := &Plan{
|
||||
Config: testConfig(t, "new-good"),
|
||||
Diff: &Diff{
|
||||
Modules: []*ModuleDiff{
|
||||
&ModuleDiff{
|
||||
Path: rootModulePath,
|
||||
Resources: map[string]*InstanceDiff{
|
||||
"nodeA": &InstanceDiff{
|
||||
Attributes: map[string]*ResourceAttrDiff{
|
||||
|
@ -31,6 +34,8 @@ func TestReadWritePlan(t *testing.T) {
|
|||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
State: &State{
|
||||
Modules: []*ModuleState{
|
||||
&ModuleState{
|
||||
|
|
Loading…
Reference in New Issue