mirror of
https://github.com/opentffoundation/opentf.git
synced 2025-12-25 10:00:44 -05:00
cloud: when saving state, create a pending state version then upload
Create a pending state version followed by a separate state upload When this version of the endpoint fails (It is not yet generally available, or when using with Terraform Enterprise) Fall back to the original call with state content included in the request. This strategy will reduce the amount of save failures due to network latency and gateway timeouts.
This commit is contained in:
@@ -238,6 +238,7 @@ func (s *State) PersistState(schemas *terraform.Schemas) error {
|
||||
s.readState = s.state.DeepCopy()
|
||||
s.readLineage = s.lineage
|
||||
s.readSerial = s.serial
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -262,15 +263,13 @@ func (s *State) ShouldPersistIntermediateState(info *local.IntermediateStatePers
|
||||
return currentInterval >= wantInterval
|
||||
}
|
||||
|
||||
func (s *State) uploadState(lineage string, serial uint64, isForcePush bool, state, jsonState, jsonStateOutputs []byte) error {
|
||||
ctx := context.Background()
|
||||
|
||||
func (s *State) uploadStateFallback(ctx context.Context, lineage string, serial uint64, isForcePush bool, state, jsonState, jsonStateOutputs []byte) error {
|
||||
options := tfe.StateVersionCreateOptions{
|
||||
Lineage: tfe.String(lineage),
|
||||
Serial: tfe.Int64(int64(serial)),
|
||||
MD5: tfe.String(fmt.Sprintf("%x", md5.Sum(state))),
|
||||
State: tfe.String(base64.StdEncoding.EncodeToString(state)),
|
||||
Force: tfe.Bool(isForcePush),
|
||||
State: tfe.String(base64.StdEncoding.EncodeToString(state)),
|
||||
JSONState: tfe.String(base64.StdEncoding.EncodeToString(jsonState)),
|
||||
JSONStateOutputs: tfe.String(base64.StdEncoding.EncodeToString(jsonStateOutputs)),
|
||||
}
|
||||
@@ -282,13 +281,46 @@ func (s *State) uploadState(lineage string, serial uint64, isForcePush bool, sta
|
||||
options.Run = &tfe.Run{ID: runID}
|
||||
}
|
||||
|
||||
// Create the new state.
|
||||
_, err := s.tfeClient.StateVersions.Create(ctx, s.workspace.ID, options)
|
||||
return err
|
||||
}
|
||||
|
||||
func (s *State) uploadState(lineage string, serial uint64, isForcePush bool, state, jsonState, jsonStateOutputs []byte) error {
|
||||
ctx := context.Background()
|
||||
|
||||
options := tfe.StateVersionUploadOptions{
|
||||
StateVersionCreateOptions: tfe.StateVersionCreateOptions{
|
||||
Lineage: tfe.String(lineage),
|
||||
Serial: tfe.Int64(int64(serial)),
|
||||
MD5: tfe.String(fmt.Sprintf("%x", md5.Sum(state))),
|
||||
Force: tfe.Bool(isForcePush),
|
||||
JSONStateOutputs: tfe.String(base64.StdEncoding.EncodeToString(jsonStateOutputs)),
|
||||
},
|
||||
RawState: state,
|
||||
RawJSONState: jsonState,
|
||||
}
|
||||
|
||||
// If we have a run ID, make sure to add it to the options
|
||||
// so the state will be properly associated with the run.
|
||||
runID := os.Getenv("TFE_RUN_ID")
|
||||
if runID != "" {
|
||||
options.StateVersionCreateOptions.Run = &tfe.Run{ID: runID}
|
||||
}
|
||||
|
||||
// The server is allowed to dynamically request a different time interval
|
||||
// than we'd normally use, for example if it's currently under heavy load
|
||||
// and needs clients to backoff for a while.
|
||||
ctx = tfe.ContextWithResponseHeaderHook(ctx, s.readSnapshotIntervalHeader)
|
||||
|
||||
// Create the new state.
|
||||
_, err := s.tfeClient.StateVersions.Create(ctx, s.workspace.ID, options)
|
||||
_, err := s.tfeClient.StateVersions.Upload(ctx, s.workspace.ID, options)
|
||||
if errors.Is(err, tfe.ErrStateVersionUploadNotSupported) {
|
||||
// Create the new state with content included in the request (Terraform Enterprise v202306-1 and below)
|
||||
log.Println("[INFO] Detected that state version upload is not supported. Retrying using compatibility state upload.")
|
||||
return s.uploadStateFallback(ctx, lineage, serial, isForcePush, state, jsonState, jsonStateOutputs)
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user