Skip to content

Commit d25e7e4

Browse files
authored
fix: terraform apply partial state updates (#539)
Fixes #527
1 parent 6b9e6b1 commit d25e7e4

File tree

1 file changed

+31
-3
lines changed

1 file changed

+31
-3
lines changed

internal/agent/steps.go

Lines changed: 31 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ import (
77
"fmt"
88
"io"
99
"io/fs"
10+
"os"
11+
"path/filepath"
1012
"strings"
1113

1214
"github.com/fatih/color"
@@ -63,7 +65,6 @@ func buildSteps(env *environment, run *run.Run) (steps []step) {
6365
steps = append(steps, bldr.downloadPlanFile)
6466
steps = append(steps, bldr.terraformInit)
6567
steps = append(steps, bldr.terraformApply)
66-
steps = append(steps, bldr.uploadState)
6768
}
6869

6970
return
@@ -128,7 +129,7 @@ func (b *stepsBuilder) deleteBackendConfig(ctx context.Context) error {
128129
return nil
129130
}
130131

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

177-
func (b *stepsBuilder) terraformApply(ctx context.Context) error {
178+
func (b *stepsBuilder) terraformApply(ctx context.Context) (err error) {
179+
// prior to running an apply, capture info about local state file
180+
// so we can detect changes...
181+
statePath := filepath.Join(b.workdir.String(), localStateFilename)
182+
stateInfoBefore, _ := os.Stat(statePath)
183+
// ...and after the apply finishes, determine if there were changes, and if
184+
// so, create a new state version. We do this even if the apply failed
185+
// because since terraform v1.5, an apply can persist partial updates:
186+
//
187+
// https://github.com/hashicorp/terraform/pull/32680
188+
defer func() {
189+
stateInfoAfter, _ := os.Stat(statePath)
190+
if stateInfoAfter == nil {
191+
// no state file found
192+
return
193+
}
194+
if stateInfoBefore != nil && stateInfoAfter.ModTime().Equal(stateInfoBefore.ModTime()) {
195+
// no change to state file
196+
return
197+
}
198+
// either there was no state file before and there is one now, or the
199+
// state file modification time has changed. In either case we upload
200+
// the new state.
201+
if stateErr := b.uploadState(ctx); stateErr != nil {
202+
err = errors.Join(err, stateErr)
203+
}
204+
}()
205+
178206
args := []string{"apply"}
179207
if b.IsDestroy {
180208
args = append(args, "-destroy")

0 commit comments

Comments
 (0)