terraform: partial state should be saved on apply error

This commit is contained in:
Mitchell Hashimoto 2014-07-17 15:32:19 -07:00
parent f78f97647a
commit ff36378c4e
3 changed files with 19 additions and 13 deletions

View File

@ -440,6 +440,8 @@ func (c *Context) releaseRun(ch chan<- struct{}) {
func (c *Context) applyWalkFn() depgraph.WalkFunc { func (c *Context) applyWalkFn() depgraph.WalkFunc {
cb := func(r *Resource) error { cb := func(r *Resource) error {
var err error
diff := r.Diff diff := r.Diff
if diff.Empty() { if diff.Empty() {
log.Printf("[DEBUG] %s: Diff is empty. Will not apply.", r.Id) log.Printf("[DEBUG] %s: Diff is empty. Will not apply.", r.Id)
@ -452,7 +454,6 @@ func (c *Context) applyWalkFn() depgraph.WalkFunc {
return err return err
} }
var err error
diff, err = r.Provider.Diff(r.State, r.Config) diff, err = r.Provider.Diff(r.State, r.Config)
if err != nil { if err != nil {
return err return err
@ -486,9 +487,11 @@ func (c *Context) applyWalkFn() depgraph.WalkFunc {
// With the completed diff, apply! // With the completed diff, apply!
log.Printf("[DEBUG] %s: Executing Apply", r.Id) log.Printf("[DEBUG] %s: Executing Apply", r.Id)
rs, err := r.Provider.Apply(r.State, diff) rs, applyerr := r.Provider.Apply(r.State, diff)
if err != nil {
return err var errs []error
if applyerr != nil {
errs = append(errs, applyerr)
} }
// Make sure the result is instantiated // Make sure the result is instantiated
@ -508,7 +511,6 @@ func (c *Context) applyWalkFn() depgraph.WalkFunc {
rs.Attributes["id"] = rs.ID rs.Attributes["id"] = rs.ID
} }
var errs []error
for ak, av := range rs.Attributes { for ak, av := range rs.Attributes {
// If the value is the unknown variable value, then it is an error. // If the value is the unknown variable value, then it is an error.
// In this case we record the error and remove it from the state // In this case we record the error and remove it from the state
@ -522,7 +524,10 @@ func (c *Context) applyWalkFn() depgraph.WalkFunc {
// Invoke any provisioners we have defined. This is only done // Invoke any provisioners we have defined. This is only done
// if the resource was created, as updates or deletes do not // if the resource was created, as updates or deletes do not
// invoke provisioners. // invoke provisioners.
if r.State.ID == "" && len(r.Provisioners) > 0 { //
// Additionally, we need to be careful to not run this if there
// was an error during the provider apply.
if applyerr == nil && r.State.ID == "" && len(r.Provisioners) > 0 {
rs, err = c.applyProvisioners(r, rs) rs, err = c.applyProvisioners(r, rs)
if err != nil { if err != nil {
errs = append(errs, err) errs = append(errs, err)

View File

@ -738,7 +738,10 @@ func TestContextApply_error(t *testing.T) {
p.ApplyFn = func(*ResourceState, *ResourceDiff) (*ResourceState, error) { p.ApplyFn = func(*ResourceState, *ResourceDiff) (*ResourceState, error) {
if errored { if errored {
return nil, fmt.Errorf("error") state := &ResourceState{
ID: "bar",
}
return state, fmt.Errorf("error")
} }
errored = true errored = true
@ -768,10 +771,6 @@ func TestContextApply_error(t *testing.T) {
t.Fatal("should have error") t.Fatal("should have error")
} }
if len(state.Resources) != 1 {
t.Fatalf("bad: %#v", state.Resources)
}
actual := strings.TrimSpace(state.String()) actual := strings.TrimSpace(state.String())
expected := strings.TrimSpace(testTerraformApplyErrorStr) expected := strings.TrimSpace(testTerraformApplyErrorStr)
if actual != expected { if actual != expected {
@ -800,9 +799,9 @@ func TestContextApply_errorPartial(t *testing.T) {
State: s, State: s,
}) })
p.ApplyFn = func(*ResourceState, *ResourceDiff) (*ResourceState, error) { p.ApplyFn = func(s *ResourceState, d *ResourceDiff) (*ResourceState, error) {
if errored { if errored {
return nil, fmt.Errorf("error") return s, fmt.Errorf("error")
} }
errored = true errored = true

View File

@ -135,6 +135,8 @@ const testTerraformApplyDestroyStr = `
` `
const testTerraformApplyErrorStr = ` const testTerraformApplyErrorStr = `
aws_instance.bar:
ID = bar
aws_instance.foo: aws_instance.foo:
ID = foo ID = foo
num = 2 num = 2