Skip to content

Commit

Permalink
fix: terraform apply partial state updates (#539)
Browse files Browse the repository at this point in the history
Fixes #527
  • Loading branch information
leg100 authored Jul 26, 2023
1 parent 6b9e6b1 commit d25e7e4
Showing 1 changed file with 31 additions and 3 deletions.
34 changes: 31 additions & 3 deletions internal/agent/steps.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ import (
"fmt"
"io"
"io/fs"
"os"
"path/filepath"
"strings"

"github.com/fatih/color"
Expand Down Expand Up @@ -63,7 +65,6 @@ func buildSteps(env *environment, run *run.Run) (steps []step) {
steps = append(steps, bldr.downloadPlanFile)
steps = append(steps, bldr.terraformInit)
steps = append(steps, bldr.terraformApply)
steps = append(steps, bldr.uploadState)
}

return
Expand Down Expand Up @@ -128,7 +129,7 @@ func (b *stepsBuilder) deleteBackendConfig(ctx context.Context) error {
return nil
}

// downloadState downloads current state to disk. If there is no state yet
// downloadState downloads current state to disk. If there is no state yet then
// nothing will be downloaded and no error will be reported.
func (b *stepsBuilder) downloadState(ctx context.Context) error {
statefile, err := b.DownloadCurrentState(ctx, b.WorkspaceID)
Expand Down Expand Up @@ -174,7 +175,34 @@ func (b *stepsBuilder) terraformPlan(ctx context.Context) error {
return b.executeTerraform(args)
}

func (b *stepsBuilder) terraformApply(ctx context.Context) error {
func (b *stepsBuilder) terraformApply(ctx context.Context) (err error) {
// prior to running an apply, capture info about local state file
// so we can detect changes...
statePath := filepath.Join(b.workdir.String(), localStateFilename)
stateInfoBefore, _ := os.Stat(statePath)
// ...and after the apply finishes, determine if there were changes, and if
// so, create a new state version. We do this even if the apply failed
// because since terraform v1.5, an apply can persist partial updates:
//
// https://github.com/hashicorp/terraform/pull/32680
defer func() {
stateInfoAfter, _ := os.Stat(statePath)
if stateInfoAfter == nil {
// no state file found
return
}
if stateInfoBefore != nil && stateInfoAfter.ModTime().Equal(stateInfoBefore.ModTime()) {
// no change to state file
return
}
// either there was no state file before and there is one now, or the
// state file modification time has changed. In either case we upload
// the new state.
if stateErr := b.uploadState(ctx); stateErr != nil {
err = errors.Join(err, stateErr)
}
}()

args := []string{"apply"}
if b.IsDestroy {
args = append(args, "-destroy")
Expand Down

0 comments on commit d25e7e4

Please sign in to comment.