Skip to content

Commit

Permalink
Merge pull request #370 from hashicorp/brandonc/current-state-version…
Browse files Browse the repository at this point in the history
…-outputs

Adds StateVersionOutputs ReadCurrent
  • Loading branch information
brandonc committed Apr 4, 2022
2 parents 17bef98 + e97759b commit 14c362e
Show file tree
Hide file tree
Showing 7 changed files with 94 additions and 20 deletions.
8 changes: 7 additions & 1 deletion CHANGELOG.md
@@ -1,3 +1,9 @@
# v1.2.0 (Unreleased)

## Enhancements

* Adds support for reading current state version outputs to StateVersionOutputs, which can be useful for reading outputs when users don't have the necessary permissions to read the entire state.

# v1.1.0

## Enhancements
Expand Down Expand Up @@ -27,4 +33,4 @@
* API Coverage documentation by @laurenolivia [#334](https://github.com/hashicorp/go-tfe/pull/334)

## Bug Fixes
* Fixed invalid memory address error when `AdminSMTPSettingsUpdateOptions.Auth` field is empty and accessed by @uturunku1 [#335](https://github.com/hashicorp/go-tfe/pull/335)
* Fixed invalid memory address error when `AdminSMTPSettingsUpdateOptions.Auth` field is empty and accessed by @uturunku1 [#335](https://github.com/hashicorp/go-tfe/pull/335)
2 changes: 2 additions & 0 deletions errors.go
Expand Up @@ -126,6 +126,8 @@ var (

ErrInvalidStateVerID = errors.New("invalid value for state version ID")

ErrInvalidOutputID = errors.New("invalid value for state version output ID")

ErrInvalidAccessTeamID = errors.New("invalid value for team access ID")

ErrInvalidTeamID = errors.New("invalid value for team ID")
Expand Down
15 changes: 15 additions & 0 deletions mocks/state_version_output_mocks.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

24 changes: 23 additions & 1 deletion state_version_output.go
Expand Up @@ -16,6 +16,7 @@ var _ StateVersionOutputs = (*stateVersionOutputs)(nil)
// TFE API docs: https://www.terraform.io/docs/cloud/api/state-version-outputs.html
type StateVersionOutputs interface {
Read(ctx context.Context, outputID string) (*StateVersionOutput, error)
ReadCurrent(ctx context.Context, workspaceID string) (*StateVersionOutputsList, error)
}

// stateVersionOutputs implements StateVersionOutputs.
Expand All @@ -32,10 +33,31 @@ type StateVersionOutput struct {
Value interface{} `jsonapi:"attr,value"`
}

// ReadCurrent reads the current state version outputs for the specified workspace
func (s *stateVersionOutputs) ReadCurrent(ctx context.Context, workspaceID string) (*StateVersionOutputsList, error) {
if !validStringID(&workspaceID) {
return nil, ErrInvalidWorkspaceID
}

u := fmt.Sprintf("workspaces/%s/current-state-version-outputs", url.QueryEscape(workspaceID))
req, err := s.client.newRequest("GET", u, nil)
if err != nil {
return nil, err
}

so := &StateVersionOutputsList{}
err = s.client.do(ctx, req, so)
if err != nil {
return nil, err
}

return so, nil
}

// Read a State Version Output
func (s *stateVersionOutputs) Read(ctx context.Context, outputID string) (*StateVersionOutput, error) {
if !validStringID(&outputID) {
return nil, ErrInvalidRunID
return nil, ErrInvalidOutputID
}

u := fmt.Sprintf("state-version-outputs/%s", url.QueryEscape(outputID))
Expand Down
50 changes: 39 additions & 11 deletions state_version_output_integration_test.go
Expand Up @@ -12,7 +12,7 @@ import (
"github.com/stretchr/testify/require"
)

const waitForStateVersionOutputs = 700 * time.Millisecond
const waitForStateVersionOutputs = 1000 * time.Millisecond

func TestStateVersionOutputsRead(t *testing.T) {
client := testClient(t)
Expand All @@ -38,19 +38,47 @@ func TestStateVersionOutputsRead(t *testing.T) {

output := sv.Outputs[0]

t.Run("when a state output exists", func(t *testing.T) {
so, err := client.StateVersionOutputs.Read(ctx, output.ID)
require.NoError(t, err)
t.Run("Read by ID", func(t *testing.T) {
t.Run("when a state output exists", func(t *testing.T) {
so, err := client.StateVersionOutputs.Read(ctx, output.ID)
require.NoError(t, err)

assert.Equal(t, so.ID, output.ID)
assert.Equal(t, so.Name, output.Name)
assert.Equal(t, so.Value, output.Value)
assert.Equal(t, so.ID, output.ID)
assert.Equal(t, so.Name, output.Name)
assert.Equal(t, so.Value, output.Value)
})

t.Run("when a state output does not exist", func(t *testing.T) {
so, err := client.StateVersionOutputs.Read(ctx, "wsout-J2zM24JPAAAAAAAA")
assert.Nil(t, so)
assert.Equal(t, ErrResourceNotFound, err)
})
})

t.Run("when a state output does not exist", func(t *testing.T) {
so, err := client.StateVersionOutputs.Read(ctx, "wsout-J2zM24JPAAAAAAAA")
assert.Nil(t, so)
assert.Equal(t, ErrResourceNotFound, err)
t.Run("Read current workspace outputs", func(t *testing.T) {
so, err := client.StateVersionOutputs.ReadCurrent(ctx, wTest1.ID)

assert.Nil(t, err)
assert.NotNil(t, so)

assert.Greater(t, len(so.Items), 0, "workspace state version outputs were empty")
})

t.Run("Sensitive secrets are null", func(t *testing.T) {
so, err := client.StateVersionOutputs.ReadCurrent(ctx, wTest1.ID)
assert.Nil(t, err)
assert.NotNil(t, so)

var found *StateVersionOutput = nil
for _, s := range so.Items {
if s.Name == "test_output_string" {
found = s
break
}
}

assert.NotNil(t, found)
assert.True(t, found.Sensitive)
assert.Nil(t, found.Value)
})
}
3 changes: 2 additions & 1 deletion test-fixtures/state-version/terraform.tfstate
Expand Up @@ -15,7 +15,8 @@
},
"test_output_string": {
"value": "9023256633839603543",
"type": "string"
"type": "string",
"sensitive": true
},
"test_output_tuple_number": {
"value": [
Expand Down
12 changes: 6 additions & 6 deletions workspace_integration_test.go
Expand Up @@ -356,20 +356,20 @@ func TestWorkspacesReadWithOptions(t *testing.T) {

assert.Len(t, w.Outputs, len(svOutputs.Items))

wsOutputs := map[string]interface{}{}
wsOutputsSensitive := map[string]bool{}
wsOutputsTypes := map[string]string{}
for _, op := range w.Outputs {
wsOutputs[op.Name] = op.Value
wsOutputsSensitive[op.Name] = op.Sensitive
wsOutputsTypes[op.Name] = op.Type
}
for _, svop := range svOutputs.Items {
val, ok := wsOutputs[svop.Name]
valSensitive, ok := wsOutputsSensitive[svop.Name]
assert.True(t, ok)
assert.Equal(t, svop.Value, val)
assert.Equal(t, svop.Sensitive, valSensitive)

val, ok = wsOutputsTypes[svop.Name]
valType, ok := wsOutputsTypes[svop.Name]
assert.True(t, ok)
assert.Equal(t, svop.Type, val)
assert.Equal(t, svop.Type, valType)
}
})
}
Expand Down

0 comments on commit 14c362e

Please sign in to comment.