From 589a5c9ce12beec96e5dd5f4ff765a4964167ee7 Mon Sep 17 00:00:00 2001 From: Brandon Croft Date: Fri, 27 May 2022 16:44:49 -0600 Subject: [PATCH] adds ext-state attribute to StateVersion create This will be used in future versions of terraform to supply portable state details to the platform --- state_version.go | 7 + state_version_integration_test.go | 41 ++++++ test-fixtures/ext-state-version/state.json | 163 +++++++++++++++++++++ 3 files changed, 211 insertions(+) create mode 100644 test-fixtures/ext-state-version/state.json diff --git a/state_version.go b/state_version.go index 3ca13ba71..1723b05a9 100644 --- a/state_version.go +++ b/state_version.go @@ -3,6 +3,7 @@ package tfe import ( "bytes" "context" + "encoding/json" "fmt" "net/url" "time" @@ -138,6 +139,12 @@ type StateVersionCreateOptions struct { // Optional: Specifies the run to associate the state with. Run *Run `jsonapi:"relation,run,omitempty"` + + // Optional: The external, json representation of state data. + // https://www.terraform.io/internals/json-format#state-representation + // Supplying this state representation can provide more details to the platform + // about the current terraform state. + ExtState json.RawMessage `jsonapi:"attr,ext-state,omitempty"` } // List all the state versions for a given workspace. diff --git a/state_version_integration_test.go b/state_version_integration_test.go index 57bfab628..485699457 100644 --- a/state_version_integration_test.go +++ b/state_version_integration_test.go @@ -121,6 +121,11 @@ func TestStateVersionsCreate(t *testing.T) { t.Fatal(err) } + extState, err := ioutil.ReadFile("test-fixtures/ext-state-version/state.json") + if err != nil { + t.Fatal(err) + } + t.Run("with valid options", func(t *testing.T) { ctx := context.Background() _, err := client.Workspaces.Lock(ctx, wTest.ID, WorkspaceLockOptions{}) @@ -156,6 +161,42 @@ func TestStateVersionsCreate(t *testing.T) { } }) + t.Run("with external state representation", func(t *testing.T) { + ctx := context.Background() + _, err := client.Workspaces.Lock(ctx, wTest.ID, WorkspaceLockOptions{}) + if err != nil { + t.Fatal(err) + } + + sv, err := client.StateVersions.Create(ctx, wTest.ID, StateVersionCreateOptions{ + Lineage: String("741c4949-60b9-5bb1-5bf8-b14f4bb14af3"), + MD5: String(fmt.Sprintf("%x", md5.Sum(state))), + Serial: Int64(1), + State: String(base64.StdEncoding.EncodeToString(state)), + ExtState: extState, + }) + require.NoError(t, err) + + // Get a refreshed view of the configuration version. + refreshed, err := client.StateVersions.Read(ctx, sv.ID) + require.NoError(t, err) + + _, err = client.Workspaces.Unlock(ctx, wTest.ID) + if err != nil { + t.Fatal(err) + } + + for _, item := range []*StateVersion{ + sv, + refreshed, + } { + assert.NotEmpty(t, item.ID) + assert.Equal(t, int64(1), item.Serial) + assert.NotEmpty(t, item.CreatedAt) + assert.NotEmpty(t, item.DownloadURL) + } + }) + t.Run("with the force flag set", func(t *testing.T) { ctx := context.Background() _, err := client.Workspaces.Lock(ctx, wTest.ID, WorkspaceLockOptions{}) diff --git a/test-fixtures/ext-state-version/state.json b/test-fixtures/ext-state-version/state.json new file mode 100644 index 000000000..5acced8cc --- /dev/null +++ b/test-fixtures/ext-state-version/state.json @@ -0,0 +1,163 @@ +{ + "format_version": "1.0", + "terraform_version": "1.2.0", + "values": { + "outputs": { + "a-decimal": { + "sensitive": false, + "value": 1000.1, + "type": "number" + }, + "a-false-bool": { + "sensitive": false, + "value": false, + "type": "bool" + }, + "a-list": { + "sensitive": false, + "value": [ + "example", + "1001", + "1000.1" + ], + "type": [ + "list", + "string" + ] + }, + "a-long-string": { + "sensitive": false, + "value": "The private integer of the main server instance is where you want to go when you have the most fun in every Terraform instance you can see in the world that you live in except for dogs because they don't run servers in the same place that humans do.", + "type": "string" + }, + "a-object": { + "sensitive": false, + "value": { + "bar": 1000.1, + "example": 1001 + }, + "type": [ + "object", + { + "bar": "number", + "example": "number" + } + ] + }, + "a-sensitive-value": { + "sensitive": true, + "value": "hopefully you cannot see me", + "type": "string" + }, + "a-string": { + "sensitive": false, + "value": "example string", + "type": "string" + }, + "a-true-bool": { + "sensitive": false, + "value": true, + "type": "bool" + }, + "a-tuple": { + "sensitive": false, + "value": [ + 1, + "example" + ], + "type": [ + "tuple", + [ + "number", + "string" + ] + ] + }, + "an-int": { + "sensitive": false, + "value": 1001, + "type": "number" + }, + "escapes": { + "sensitive": false, + "value": "line 1\nline 2\n\\\\\\\\\n", + "type": "string" + }, + "myoutput": { + "sensitive": false, + "value": { + "nesting1": { + "nesting2": { + "nesting3": "4263891374290101092" + } + } + }, + "type": [ + "object", + { + "nesting1": [ + "object", + { + "nesting2": [ + "object", + { + "nesting3": "string" + } + ] + } + ] + } + ] + }, + "random": { + "sensitive": false, + "value": "8b3086889a9ef7a5", + "type": "string" + } + }, + "root_module": { + "resources": [ + { + "address": "null_resource.test", + "mode": "managed", + "type": "null_resource", + "name": "test", + "provider_name": "registry.terraform.io/hashicorp/null", + "schema_version": 0, + "values": { + "id": "4263891374290101092", + "triggers": { + "hello": "wat3" + } + }, + "sensitive_values": { + "triggers": {} + } + }, + { + "address": "random_id.random", + "mode": "managed", + "type": "random_id", + "name": "random", + "provider_name": "registry.terraform.io/hashicorp/random", + "schema_version": 0, + "values": { + "b64_std": "izCGiJqe96U=", + "b64_url": "izCGiJqe96U", + "byte_length": 8, + "dec": "10029664291421878181", + "hex": "8b3086889a9ef7a5", + "id": "izCGiJqe96U", + "keepers": { + "uuid": "437a1415-932b-9f74-c214-184d88215353" + }, + "prefix": null + }, + "sensitive_values": { + "keepers": {} + } + } + ] + } + } +}