mirror of
https://github.com/opentofu/opentofu.git
synced 2024-12-23 07:33:32 -06:00
Merge pull request #33336 from hashicorp/TF-7056-uploading-state-directly-to-hosted-state-upload-url-when-available
cloud: when saving state, utilize new 'pending' state version
This commit is contained in:
commit
63124e0cb7
6
go.mod
6
go.mod
@ -40,8 +40,8 @@ require (
|
||||
github.com/hashicorp/go-hclog v1.4.0
|
||||
github.com/hashicorp/go-multierror v1.1.1
|
||||
github.com/hashicorp/go-plugin v1.4.3
|
||||
github.com/hashicorp/go-retryablehttp v0.7.2
|
||||
github.com/hashicorp/go-tfe v1.26.0
|
||||
github.com/hashicorp/go-retryablehttp v0.7.4
|
||||
github.com/hashicorp/go-tfe v1.28.0
|
||||
github.com/hashicorp/go-uuid v1.0.3
|
||||
github.com/hashicorp/go-version v1.6.0
|
||||
github.com/hashicorp/hcl v1.0.0
|
||||
@ -210,7 +210,7 @@ require (
|
||||
go.opencensus.io v0.23.0 // indirect
|
||||
golang.org/x/exp v0.0.0-20220303212507-bbda1eaf7a17 // indirect
|
||||
golang.org/x/exp/typeparams v0.0.0-20221208152030-732eee02a75a // indirect
|
||||
golang.org/x/sync v0.1.0 // indirect
|
||||
golang.org/x/sync v0.3.0 // indirect
|
||||
golang.org/x/time v0.3.0 // indirect
|
||||
golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 // indirect
|
||||
google.golang.org/appengine v1.6.7 // indirect
|
||||
|
14
go.sum
14
go.sum
@ -608,8 +608,8 @@ github.com/hashicorp/go-plugin v1.4.3 h1:DXmvivbWD5qdiBts9TpBC7BYL1Aia5sxbRgQB+v
|
||||
github.com/hashicorp/go-plugin v1.4.3/go.mod h1:5fGEH17QVwTTcR0zV7yhDPLLmFX9YSZ38b18Udy6vYQ=
|
||||
github.com/hashicorp/go-retryablehttp v0.5.4/go.mod h1:9B5zBasrRhHXnJnui7y6sL7es7NDiJgTc6Er0maI1Xs=
|
||||
github.com/hashicorp/go-retryablehttp v0.7.0/go.mod h1:vAew36LZh98gCBJNLH42IQ1ER/9wtLZZ8meHqQvEYWY=
|
||||
github.com/hashicorp/go-retryablehttp v0.7.2 h1:AcYqCvkpalPnPF2pn0KamgwamS42TqUDDYFRKq/RAd0=
|
||||
github.com/hashicorp/go-retryablehttp v0.7.2/go.mod h1:Jy/gPYAdjqffZ/yFGCFV2doI5wjtH1ewM9u8iYVjtX8=
|
||||
github.com/hashicorp/go-retryablehttp v0.7.4 h1:ZQgVdpTdAL7WpMIwLzCfbalOcSUdkDZnpUv3/+BxzFA=
|
||||
github.com/hashicorp/go-retryablehttp v0.7.4/go.mod h1:Jy/gPYAdjqffZ/yFGCFV2doI5wjtH1ewM9u8iYVjtX8=
|
||||
github.com/hashicorp/go-rootcerts v1.0.1/go.mod h1:pqUvnprVnM5bf7AOirdbb01K4ccR319Vf4pU3K5EGc8=
|
||||
github.com/hashicorp/go-rootcerts v1.0.2 h1:jzhAVGtqPKbwpyCPELlgNWhE1znq+qwJtW5Oi2viEzc=
|
||||
github.com/hashicorp/go-rootcerts v1.0.2/go.mod h1:pqUvnprVnM5bf7AOirdbb01K4ccR319Vf4pU3K5EGc8=
|
||||
@ -621,8 +621,8 @@ github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerX
|
||||
github.com/hashicorp/go-sockaddr v1.0.2 h1:ztczhD1jLxIRjVejw8gFomI1BQZOe2WoVOu0SyteCQc=
|
||||
github.com/hashicorp/go-sockaddr v1.0.2/go.mod h1:rB4wwRAUzs07qva3c5SdrY/NEtAUjGlgmH/UkBUC97A=
|
||||
github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4=
|
||||
github.com/hashicorp/go-tfe v1.26.0 h1:aacguqCENg6Z7ttfhAxdbbY2vm/jKrntl5sUUY0h6EM=
|
||||
github.com/hashicorp/go-tfe v1.26.0/go.mod h1:1Y6nsdMuJ14lYdc1VMLl/erlthvMzUsJn+WYWaAdSc4=
|
||||
github.com/hashicorp/go-tfe v1.28.0 h1:YQNfHz5UPMiOD2idad4GCjzG3R2ExPww741PBPqMOIU=
|
||||
github.com/hashicorp/go-tfe v1.28.0/go.mod h1:z0182DGE/63AKUaWblUVBIrt+xdSmsuuXg5AoxGqDF4=
|
||||
github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
|
||||
github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
|
||||
github.com/hashicorp/go-uuid v1.0.2/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
|
||||
@ -936,7 +936,7 @@ github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/
|
||||
github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals=
|
||||
github.com/stretchr/testify v1.7.4/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||
github.com/stretchr/testify v1.8.3 h1:RP3t2pwF7cMEbC1dqtB6poj3niw/9gnV4Cjg5oW5gtY=
|
||||
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
|
||||
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.194/go.mod h1:7sCQWVkxcsR38nffDW057DRGk8mUjK1Ing/EFOK8s8Y=
|
||||
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.588 h1:DYtBXB7sVc3EOW5horg8j55cLZynhsLYhHrvQ/jXKKM=
|
||||
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.588/go.mod h1:7sCQWVkxcsR38nffDW057DRGk8mUjK1Ing/EFOK8s8Y=
|
||||
@ -1175,8 +1175,8 @@ golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJ
|
||||
golang.org/x/sync v0.0.0-20220601150217-0de741cfad7f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20220929204114-8fcdb60fdcc0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o=
|
||||
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.3.0 h1:ftCYgMx6zT/asHUrPw8BLLscYtGznsLAnjq5RH9P66E=
|
||||
golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=
|
||||
golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
|
@ -9,7 +9,9 @@ import (
|
||||
"crypto/md5"
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"log"
|
||||
|
||||
tfe "github.com/hashicorp/go-tfe"
|
||||
|
||||
@ -61,32 +63,14 @@ func (r *remoteClient) Get() (*remote.Payload, error) {
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Put the remote state.
|
||||
func (r *remoteClient) Put(state []byte) error {
|
||||
ctx := context.Background()
|
||||
|
||||
// Read the raw state into a Terraform state.
|
||||
stateFile, err := statefile.Read(bytes.NewReader(state))
|
||||
if err != nil {
|
||||
return fmt.Errorf("Error reading state: %s", err)
|
||||
}
|
||||
|
||||
ov, err := jsonstate.MarshalOutputs(stateFile.State.RootModule().OutputValues)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Error reading output values: %s", err)
|
||||
}
|
||||
o, err := json.Marshal(ov)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Error converting output values to json: %s", err)
|
||||
}
|
||||
|
||||
func (r *remoteClient) uploadStateFallback(ctx context.Context, stateFile *statefile.File, state []byte, jsonStateOutputs []byte) error {
|
||||
options := tfe.StateVersionCreateOptions{
|
||||
Lineage: tfe.String(stateFile.Lineage),
|
||||
Serial: tfe.Int64(int64(stateFile.Serial)),
|
||||
MD5: tfe.String(fmt.Sprintf("%x", md5.Sum(state))),
|
||||
State: tfe.String(base64.StdEncoding.EncodeToString(state)),
|
||||
Force: tfe.Bool(r.forcePush),
|
||||
JSONStateOutputs: tfe.String(base64.StdEncoding.EncodeToString(o)),
|
||||
State: tfe.String(base64.StdEncoding.EncodeToString(state)),
|
||||
JSONStateOutputs: tfe.String(base64.StdEncoding.EncodeToString(jsonStateOutputs)),
|
||||
}
|
||||
|
||||
// If we have a run ID, make sure to add it to the options
|
||||
@ -96,10 +80,61 @@ func (r *remoteClient) Put(state []byte) error {
|
||||
}
|
||||
|
||||
// Create the new state.
|
||||
_, err = r.client.StateVersions.Create(ctx, r.workspace.ID, options)
|
||||
_, err := r.client.StateVersions.Create(ctx, r.workspace.ID, options)
|
||||
if err != nil {
|
||||
r.stateUploadErr = true
|
||||
return fmt.Errorf("Error uploading state: %v", err)
|
||||
return fmt.Errorf("error uploading state in compatibility mode: %v", err)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// Put the remote state.
|
||||
func (r *remoteClient) Put(state []byte) error {
|
||||
ctx := context.Background()
|
||||
|
||||
// Read the raw state into a Terraform state.
|
||||
stateFile, err := statefile.Read(bytes.NewReader(state))
|
||||
if err != nil {
|
||||
return fmt.Errorf("error reading state: %s", err)
|
||||
}
|
||||
|
||||
ov, err := jsonstate.MarshalOutputs(stateFile.State.RootModule().OutputValues)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error reading output values: %s", err)
|
||||
}
|
||||
o, err := json.Marshal(ov)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error converting output values to json: %s", err)
|
||||
}
|
||||
|
||||
options := tfe.StateVersionUploadOptions{
|
||||
StateVersionCreateOptions: tfe.StateVersionCreateOptions{
|
||||
Lineage: tfe.String(stateFile.Lineage),
|
||||
Serial: tfe.Int64(int64(stateFile.Serial)),
|
||||
MD5: tfe.String(fmt.Sprintf("%x", md5.Sum(state))),
|
||||
Force: tfe.Bool(r.forcePush),
|
||||
JSONStateOutputs: tfe.String(base64.StdEncoding.EncodeToString(o)),
|
||||
},
|
||||
RawState: state,
|
||||
}
|
||||
|
||||
// If we have a run ID, make sure to add it to the options
|
||||
// so the state will be properly associated with the run.
|
||||
if r.runID != "" {
|
||||
options.Run = &tfe.Run{ID: r.runID}
|
||||
}
|
||||
|
||||
// Create the new state.
|
||||
// Create the new state.
|
||||
_, err = r.client.StateVersions.Upload(ctx, r.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 r.uploadStateFallback(ctx, stateFile, state, o)
|
||||
}
|
||||
if err != nil {
|
||||
r.stateUploadErr = true
|
||||
return fmt.Errorf("error uploading state: %v", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
@ -109,7 +144,7 @@ func (r *remoteClient) Put(state []byte) error {
|
||||
func (r *remoteClient) Delete() error {
|
||||
err := r.client.Workspaces.Delete(context.Background(), r.organization, r.workspace.Name)
|
||||
if err != nil && err != tfe.ErrResourceNotFound {
|
||||
return fmt.Errorf("Error deleting workspace %s: %v", r.workspace.Name, err)
|
||||
return fmt.Errorf("error deleting workspace %s: %v", r.workspace.Name, err)
|
||||
}
|
||||
|
||||
return nil
|
||||
|
@ -41,7 +41,7 @@ func TestRemoteClient_stateLock(t *testing.T) {
|
||||
remote.TestRemoteLocks(t, s1.(*remote.State).Client, s2.(*remote.State).Client)
|
||||
}
|
||||
|
||||
func TestRemoteClient_withRunID(t *testing.T) {
|
||||
func TestRemoteClient_Put_withRunID(t *testing.T) {
|
||||
// Set the TFE_RUN_ID environment variable before creating the client!
|
||||
if err := os.Setenv("TFE_RUN_ID", cloud.GenerateID("run-")); err != nil {
|
||||
t.Fatalf("error setting env var TFE_RUN_ID: %v", err)
|
||||
|
25
internal/cloud/errored.tfstate
Normal file
25
internal/cloud/errored.tfstate
Normal file
@ -0,0 +1,25 @@
|
||||
{
|
||||
"version": 4,
|
||||
"terraform_version": "0.14.0",
|
||||
"serial": 1,
|
||||
"lineage": "30a4d634-f765-186a-f411-7dfa798a767e",
|
||||
"outputs": {},
|
||||
"resources": [
|
||||
{
|
||||
"mode": "managed",
|
||||
"type": "null_resource",
|
||||
"name": "foo",
|
||||
"provider": "provider[\"registry.terraform.io/hashicorp/null\"]",
|
||||
"instances": [
|
||||
{
|
||||
"schema_version": 0,
|
||||
"attributes": {
|
||||
"id": "yes"
|
||||
},
|
||||
"sensitive_attributes": []
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"check_results": null
|
||||
}
|
@ -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
|
||||
}
|
||||
|
||||
|
@ -298,7 +298,6 @@ func TestState_PersistState(t *testing.T) {
|
||||
// since HTTP-level concerns like headers are out of scope for the
|
||||
// mock client we typically use in other tests in this package, which
|
||||
// aim to abstract away HTTP altogether.
|
||||
var serverURL string
|
||||
|
||||
// Didn't want to repeat myself here
|
||||
for _, testCase := range []struct {
|
||||
@ -314,10 +313,9 @@ func TestState_PersistState(t *testing.T) {
|
||||
snapshotsEnabled: false,
|
||||
},
|
||||
} {
|
||||
server := testServerWithSnapshotsEnabled(t, serverURL, testCase.snapshotsEnabled)
|
||||
server := testServerWithSnapshotsEnabled(t, testCase.snapshotsEnabled)
|
||||
|
||||
defer server.Close()
|
||||
serverURL = server.URL
|
||||
cfg := &tfe.Config{
|
||||
Address: server.URL,
|
||||
BasePath: "api",
|
||||
|
@ -383,8 +383,9 @@ func testServerWithHandlers(handlers map[string]func(http.ResponseWriter, *http.
|
||||
return httptest.NewServer(mux)
|
||||
}
|
||||
|
||||
func testServerWithSnapshotsEnabled(t *testing.T, serverURL string, enabled bool) *httptest.Server {
|
||||
return httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
func testServerWithSnapshotsEnabled(t *testing.T, enabled bool) *httptest.Server {
|
||||
var serverURL string
|
||||
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
t.Log(r.Method, r.URL.String())
|
||||
|
||||
if r.URL.Path == "/state-json" {
|
||||
@ -400,6 +401,7 @@ func testServerWithSnapshotsEnabled(t *testing.T, serverURL string, enabled bool
|
||||
w.Write(respBody)
|
||||
return
|
||||
}
|
||||
|
||||
if r.URL.Path == "/api/ping" {
|
||||
t.Log("pretending to be Ping")
|
||||
w.WriteHeader(http.StatusNoContent)
|
||||
@ -409,8 +411,10 @@ func testServerWithSnapshotsEnabled(t *testing.T, serverURL string, enabled bool
|
||||
fakeBody := map[string]any{
|
||||
"data": map[string]any{
|
||||
"type": "state-versions",
|
||||
"id": GenerateID("sv-"),
|
||||
"attributes": map[string]any{
|
||||
"hosted-state-download-url": serverURL + "/state-json",
|
||||
"hosted-state-upload-url": serverURL + "/state-json",
|
||||
},
|
||||
},
|
||||
}
|
||||
@ -435,6 +439,8 @@ func testServerWithSnapshotsEnabled(t *testing.T, serverURL string, enabled bool
|
||||
w.Header().Set("x-terraform-snapshot-interval", "300")
|
||||
}
|
||||
w.WriteHeader(http.StatusOK)
|
||||
case "PUT":
|
||||
t.Log("pretending to be Archivist")
|
||||
default:
|
||||
t.Fatal("don't know what API operation this was supposed to be")
|
||||
}
|
||||
@ -442,6 +448,8 @@ func testServerWithSnapshotsEnabled(t *testing.T, serverURL string, enabled bool
|
||||
w.WriteHeader(http.StatusOK)
|
||||
w.Write(fakeBodyRaw)
|
||||
}))
|
||||
serverURL = server.URL
|
||||
return server
|
||||
}
|
||||
|
||||
// testDefaultRequestHandlers is a map of request handlers intended to be used in a request
|
||||
|
@ -1254,6 +1254,7 @@ func (m *MockStateVersions) Create(ctx context.Context, workspaceID string, opti
|
||||
sv := &tfe.StateVersion{
|
||||
ID: id,
|
||||
DownloadURL: url,
|
||||
UploadURL: fmt.Sprintf("/_archivist/upload/%s", id),
|
||||
Serial: *options.Serial,
|
||||
}
|
||||
|
||||
@ -1261,7 +1262,6 @@ func (m *MockStateVersions) Create(ctx context.Context, workspaceID string, opti
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
m.states[sv.DownloadURL] = state
|
||||
m.outputStates[sv.ID] = []byte(*options.JSONStateOutputs)
|
||||
m.stateVersions[sv.ID] = sv
|
||||
@ -1270,6 +1270,13 @@ func (m *MockStateVersions) Create(ctx context.Context, workspaceID string, opti
|
||||
return sv, nil
|
||||
}
|
||||
|
||||
func (m *MockStateVersions) Upload(ctx context.Context, workspaceID string, options tfe.StateVersionUploadOptions) (*tfe.StateVersion, error) {
|
||||
createOptions := options.StateVersionCreateOptions
|
||||
createOptions.State = tfe.String(base64.StdEncoding.EncodeToString(options.RawState))
|
||||
|
||||
return m.Create(ctx, workspaceID, createOptions)
|
||||
}
|
||||
|
||||
func (m *MockStateVersions) Read(ctx context.Context, svID string) (*tfe.StateVersion, error) {
|
||||
return m.ReadWithOptions(ctx, svID, nil)
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user