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

Make IsSecret threadsafe #11189

Merged
merged 1 commit into from Oct 29, 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
@@ -0,0 +1,4 @@
changes:
- type: fix
scope: sdk/go,yaml
description: Block IsSecret until secretness is known
27 changes: 26 additions & 1 deletion sdk/go/pulumi/types.go
Expand Up @@ -519,8 +519,33 @@ func (o *OutputState) ApplyTWithContext(ctx context.Context, applier interface{}
}

// IsSecret returns a bool representing the secretness of the Output
//
// IsSecret may return an inaccurate results if the Output is unknowable (during a
// preview) or contains an error.
func IsSecret(o Output) bool {
return o.getState().secret
_, _, secret, _, _ := o.getState().await(context.Background())
// We intentionally ignore both the `known` and `error` values returned by `await`:
//
// If a value is not known, it is possible that we will return the wrong result. This
// is unavoidable. Consider the example:
//
// ```go
// bucket, _ := s3.Bucket("bucket", &s3.BucketArgs{})
// unknowable := bucket.Bucket.ApplyT(func(b string) OutputString {
// if strings.ContainsRune(b, '9') {
// return ToSecret(String(b))
// else {
// return String(b)
// }
// })
// ```
//
// Until we resolve values from the cloud, we can't know the correct value of
// `IsSecret(unknowable)`. We have the same problem for outputs with non-nil errors.
//
// This is tolerable because users will never be able to retrieve values (secret or
// otherwise) that are unknown or erred.
return secret
}

// Unsecret will unwrap a secret output as a new output with a resolved value and no secretness
Expand Down
19 changes: 19 additions & 0 deletions sdk/go/pulumi/types_test.go
Expand Up @@ -21,6 +21,7 @@ import (
"fmt"
"reflect"
"testing"
"time"

"github.com/stretchr/testify/assert"
)
Expand Down Expand Up @@ -618,7 +619,25 @@ func TestSecretApply(t *testing.T) {
break
}
}
}

// Test that secretness is properly bubbled up with all/apply that delays its execution.
func TestSecretApplyDelayed(t *testing.T) {
t.Parallel()

// We run multiple tests here to increase the likelihood of a hypothetical race
// condition triggering. As with all concurrency tests, its not a 100% guarantee.
for i := 0; i < 10 && !t.Failed(); i++ {
t.Run("", func(t *testing.T) {
t.Parallel()
s1 := String("foo").ToStringOutput().ApplyT(func(s string) StringOutput {
time.Sleep(time.Millisecond * 5)
return ToSecret(String("bar")).(StringOutput)
})
// assert that s1 is secret.
assert.True(t, IsSecret(s1))
})
}
}

func TestNil(t *testing.T) {
Expand Down