Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adds StateVersionOutputs ReadCurrent #370

Merged
merged 5 commits into from Apr 4, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
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)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see we have this precedence for ReadCurrent and ReadCurrentwithOptions: https://github.com/hashicorp/go-tfe/blob/main/state_version.go#L33-L36 But if this endpoint won't be adding options in the near future, then we are good.

}

// 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
Copy link
Collaborator

@uturunku1 uturunku1 Apr 1, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

geeze, were we returning the incorrect error message all this time?

}

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
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why 1000 instead of 700?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My tests didn't pass locally with the old timing.


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)
})
brandonc marked this conversation as resolved.
Show resolved Hide resolved

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) {
brandonc marked this conversation as resolved.
Show resolved Hide resolved
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