terraform: add ID to diff implicitly

This commit is contained in:
Mitchell Hashimoto 2014-07-08 16:58:31 -07:00
parent 2fd5b36550
commit 251790f05a
8 changed files with 112 additions and 26 deletions

View File

@ -4,9 +4,9 @@ import (
"fmt" "fmt"
"log" "log"
"github.com/hashicorp/terraform/flatmap"
"github.com/hashicorp/terraform/helper/diff" "github.com/hashicorp/terraform/helper/diff"
"github.com/hashicorp/terraform/terraform" "github.com/hashicorp/terraform/terraform"
"github.com/hashicorp/terraform/flatmap"
"github.com/mitchellh/goamz/ec2" "github.com/mitchellh/goamz/ec2"
) )

View File

@ -532,6 +532,26 @@ func (c *Context) planWalkFn(result *Plan) depgraph.WalkFunc {
} }
} }
if diff == nil {
diff = new(ResourceDiff)
}
if diff.RequiresNew() && r.State.ID != "" {
// This will also require a destroy
diff.Destroy = true
}
if diff.RequiresNew() || r.State.ID == "" {
// Add diff to compute new ID
diff.init()
diff.Attributes["id"] = &ResourceAttrDiff{
Old: r.State.Attributes["id"],
NewComputed: true,
RequiresNew: true,
Type: DiffAttrOutput,
}
}
l.Lock() l.Lock()
if !diff.Empty() { if !diff.Empty() {
result.Diff.Resources[r.Id] = diff result.Diff.Resources[r.Id] = diff
@ -752,7 +772,10 @@ func (c *Context) genericWalkFn(cb genericWalkFunc) depgraph.WalkFunc {
}() }()
// Call the callack // Call the callack
log.Printf("[INFO] Walking: %s", rn.Resource.Id) log.Printf(
"[INFO] Walking: %s (Graph node: %s)",
rn.Resource.Id,
n.Name)
if err := cb(rn.Resource); err != nil { if err := cb(rn.Resource); err != nil {
log.Printf("[ERROR] Error walking '%s': %s", rn.Resource.Id, err) log.Printf("[ERROR] Error walking '%s': %s", rn.Resource.Id, err)
return err return err

View File

@ -180,8 +180,6 @@ func TestContextApply_Minimal(t *testing.T) {
t.Fatalf("err: %s", err) t.Fatalf("err: %s", err)
} }
ctx.variables = map[string]string{"value": "1"}
state, err := ctx.Apply() state, err := ctx.Apply()
if err != nil { if err != nil {
t.Fatalf("err: %s", err) t.Fatalf("err: %s", err)
@ -242,7 +240,9 @@ func TestContextApply_cancel(t *testing.T) {
// Start the Apply in a goroutine // Start the Apply in a goroutine
stateCh := make(chan *State) stateCh := make(chan *State)
go func() { go func() {
println("START")
state, err := ctx.Apply() state, err := ctx.Apply()
println("STOP")
if err != nil { if err != nil {
panic(err) panic(err)
} }
@ -714,6 +714,29 @@ func TestContextPlan(t *testing.T) {
} }
} }
func TestContextPlan_minimal(t *testing.T) {
c := testConfig(t, "plan-empty")
p := testProvider("aws")
p.DiffFn = testDiffFn
ctx := testContext(t, &ContextOpts{
Config: c,
Providers: map[string]ResourceProviderFactory{
"aws": testProviderFuncFixed(p),
},
})
plan, err := ctx.Plan(nil)
if err != nil {
t.Fatalf("err: %s", err)
}
actual := strings.TrimSpace(plan.String())
expected := strings.TrimSpace(testTerraformPlanEmptyStr)
if actual != expected {
t.Fatalf("bad:\n%s", actual)
}
}
func TestContextPlan_nil(t *testing.T) { func TestContextPlan_nil(t *testing.T) {
c := testConfig(t, "plan-nil") c := testConfig(t, "plan-nil")
p := testProvider("aws") p := testProvider("aws")
@ -723,6 +746,14 @@ func TestContextPlan_nil(t *testing.T) {
Providers: map[string]ResourceProviderFactory{ Providers: map[string]ResourceProviderFactory{
"aws": testProviderFuncFixed(p), "aws": testProviderFuncFixed(p),
}, },
State: &State{
Resources: map[string]*ResourceState{
"aws_instance.foo": &ResourceState{
ID: "bar",
Type: "aws_instance",
},
},
},
}) })
plan, err := ctx.Plan(nil) plan, err := ctx.Plan(nil)

View File

@ -115,6 +115,10 @@ func (d *Diff) String() string {
keyLen := 0 keyLen := 0
keys := make([]string, 0, len(rdiff.Attributes)) keys := make([]string, 0, len(rdiff.Attributes))
for key, _ := range rdiff.Attributes { for key, _ := range rdiff.Attributes {
if key == "id" {
continue
}
keys = append(keys, key) keys = append(keys, key)
if len(key) > keyLen { if len(key) > keyLen {
keyLen = len(key) keyLen = len(key)
@ -152,6 +156,8 @@ func (d *Diff) String() string {
type ResourceDiff struct { type ResourceDiff struct {
Attributes map[string]*ResourceAttrDiff Attributes map[string]*ResourceAttrDiff
Destroy bool Destroy bool
once sync.Once
} }
// ResourceAttrDiff is the diff of a single attribute of a resource. // ResourceAttrDiff is the diff of a single attribute of a resource.
@ -177,6 +183,14 @@ const (
DiffAttrOutput DiffAttrOutput
) )
func (d *ResourceDiff) init() {
d.once.Do(func() {
if d.Attributes == nil {
d.Attributes = make(map[string]*ResourceAttrDiff)
}
})
}
// Empty returns true if this diff encapsulates no changes. // Empty returns true if this diff encapsulates no changes.
func (d *ResourceDiff) Empty() bool { func (d *ResourceDiff) Empty() bool {
if d == nil { if d == nil {

View File

@ -273,7 +273,7 @@ func graphAddDiff(g *depgraph.Graph, d *Diff) error {
continue continue
} }
if rd.Destroy || rd.RequiresNew() { if rd.Destroy {
// If we're destroying, we create a new destroy node with // If we're destroying, we create a new destroy node with
// the proper dependencies. Perform a dirty copy operation. // the proper dependencies. Perform a dirty copy operation.
newNode := new(GraphNodeResource) newNode := new(GraphNodeResource)

View File

@ -129,6 +129,10 @@ func (c *ResourceConfig) IsSet(k string) bool {
} }
func (c *ResourceConfig) interpolate(ctx *Context) error { func (c *ResourceConfig) interpolate(ctx *Context) error {
if c == nil {
return nil
}
if ctx != nil { if ctx != nil {
if err := ctx.computeVars(c.raw); err != nil { if err := ctx.computeVars(c.raw); err != nil {
return err return err

View File

@ -110,10 +110,8 @@ aws_instance.foo:
const testTerraformApplyMinimalStr = ` const testTerraformApplyMinimalStr = `
aws_instance.bar: aws_instance.bar:
ID = foo ID = foo
type = aws_instance
aws_instance.foo: aws_instance.foo:
ID = foo ID = foo
type = aws_instance
` `
const testTerraformApplyDestroyStr = ` const testTerraformApplyDestroyStr = `
@ -216,10 +214,10 @@ aws_instance.foo:
const testTerraformPlanStr = ` const testTerraformPlanStr = `
DIFF: DIFF:
UPDATE: aws_instance.bar CREATE: aws_instance.bar
foo: "" => "2" foo: "" => "2"
type: "" => "aws_instance" type: "" => "aws_instance"
UPDATE: aws_instance.foo CREATE: aws_instance.foo
num: "" => "2" num: "" => "2"
type: "" => "aws_instance" type: "" => "aws_instance"
@ -231,10 +229,10 @@ STATE:
const testTerraformPlanComputedStr = ` const testTerraformPlanComputedStr = `
DIFF: DIFF:
UPDATE: aws_instance.bar CREATE: aws_instance.bar
foo: "" => "<computed>" foo: "" => "<computed>"
type: "" => "aws_instance" type: "" => "aws_instance"
UPDATE: aws_instance.foo CREATE: aws_instance.foo
foo: "" => "<computed>" foo: "" => "<computed>"
num: "" => "2" num: "" => "2"
type: "" => "aws_instance" type: "" => "aws_instance"
@ -247,10 +245,10 @@ STATE:
const testTerraformPlanComputedIdStr = ` const testTerraformPlanComputedIdStr = `
DIFF: DIFF:
UPDATE: aws_instance.bar CREATE: aws_instance.bar
foo: "" => "<computed>" foo: "" => "<computed>"
type: "" => "aws_instance" type: "" => "aws_instance"
UPDATE: aws_instance.foo CREATE: aws_instance.foo
foo: "" => "<computed>" foo: "" => "<computed>"
num: "" => "2" num: "" => "2"
type: "" => "aws_instance" type: "" => "aws_instance"
@ -263,22 +261,22 @@ STATE:
const testTerraformPlanCountStr = ` const testTerraformPlanCountStr = `
DIFF: DIFF:
UPDATE: aws_instance.bar CREATE: aws_instance.bar
foo: "" => "foo,foo,foo,foo,foo" foo: "" => "foo,foo,foo,foo,foo"
type: "" => "aws_instance" type: "" => "aws_instance"
UPDATE: aws_instance.foo.0 CREATE: aws_instance.foo.0
foo: "" => "foo" foo: "" => "foo"
type: "" => "aws_instance" type: "" => "aws_instance"
UPDATE: aws_instance.foo.1 CREATE: aws_instance.foo.1
foo: "" => "foo" foo: "" => "foo"
type: "" => "aws_instance" type: "" => "aws_instance"
UPDATE: aws_instance.foo.2 CREATE: aws_instance.foo.2
foo: "" => "foo" foo: "" => "foo"
type: "" => "aws_instance" type: "" => "aws_instance"
UPDATE: aws_instance.foo.3 CREATE: aws_instance.foo.3
foo: "" => "foo" foo: "" => "foo"
type: "" => "aws_instance" type: "" => "aws_instance"
UPDATE: aws_instance.foo.4 CREATE: aws_instance.foo.4
foo: "" => "foo" foo: "" => "foo"
type: "" => "aws_instance" type: "" => "aws_instance"
@ -290,7 +288,7 @@ STATE:
const testTerraformPlanCountDecreaseStr = ` const testTerraformPlanCountDecreaseStr = `
DIFF: DIFF:
UPDATE: aws_instance.bar CREATE: aws_instance.bar
foo: "" => "bar" foo: "" => "bar"
type: "" => "aws_instance" type: "" => "aws_instance"
DESTROY: aws_instance.foo.1 DESTROY: aws_instance.foo.1
@ -311,13 +309,13 @@ aws_instance.foo.2:
const testTerraformPlanCountIncreaseStr = ` const testTerraformPlanCountIncreaseStr = `
DIFF: DIFF:
UPDATE: aws_instance.bar CREATE: aws_instance.bar
foo: "" => "bar" foo: "" => "bar"
type: "" => "aws_instance" type: "" => "aws_instance"
UPDATE: aws_instance.foo.1 CREATE: aws_instance.foo.1
foo: "" => "foo" foo: "" => "foo"
type: "" => "aws_instance" type: "" => "aws_instance"
UPDATE: aws_instance.foo.2 CREATE: aws_instance.foo.2
foo: "" => "foo" foo: "" => "foo"
type: "" => "aws_instance" type: "" => "aws_instance"
@ -343,11 +341,22 @@ aws_instance.two:
ID = baz ID = baz
` `
const testTerraformPlanEmptyStr = `
DIFF:
CREATE: aws_instance.bar
CREATE: aws_instance.foo
STATE:
<no state>
`
const testTerraformPlanOrphanStr = ` const testTerraformPlanOrphanStr = `
DIFF: DIFF:
DESTROY: aws_instance.baz DESTROY: aws_instance.baz
UPDATE: aws_instance.foo CREATE: aws_instance.foo
num: "" => "2" num: "" => "2"
type: "" => "aws_instance" type: "" => "aws_instance"
@ -360,7 +369,7 @@ aws_instance.baz:
const testTerraformPlanStateStr = ` const testTerraformPlanStateStr = `
DIFF: DIFF:
UPDATE: aws_instance.bar CREATE: aws_instance.bar
foo: "" => "2" foo: "" => "2"
type: "" => "aws_instance" type: "" => "aws_instance"
UPDATE: aws_instance.foo UPDATE: aws_instance.foo

View File

@ -0,0 +1,5 @@
resource "aws_instance" "foo" {
}
resource "aws_instance" "bar" {
}