Adding pulumi.IsSecret and pulumi.Unsecret to the Go SDK (#6085)

This commit is contained in:
Paul Stack 2021-01-15 20:49:48 +00:00 committed by GitHub
parent 655418bbbf
commit 475250f82f
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 79 additions and 12 deletions

View file

@ -6,6 +6,12 @@ CHANGELOG
- [CLI] Add the ability to log out of all Pulumi backends at once.
[#6101](https://github.com/pulumi/pulumi/pull/6101)
- [sdk/go] Added `pulumi.Unsecret` which will take an existing secret output and
create a non-secret variant with an unwrapped secret value. Also adds,
`pulumi.IsSecret` which will take an existing output and
determine if an output has a secret within the output.
[#6085](https://github.com/pulumi/pulumi/pull/6085)
## 2.17.2 (2021-01-14)
- .NET: Allow `IMock.NewResourceAsync` to return a null ID for component resources.
@ -55,7 +61,7 @@ CHANGELOG
- [sdk/dotnet] Moved urn value retrieval into if statement
for MockMonitor
[#6081](https://github.com/pulumi/pulumi/pull/6081)
- [sdk/dotnet] Added `Pulumi.Output.Unsecret` which will
take an existing secret output and
create a non-secret variant with an unwrapped secret value.

View file

@ -45,7 +45,7 @@ type Output interface {
resolve(value interface{}, known, secret bool, deps []Resource)
reject(err error)
await(ctx context.Context) (interface{}, bool, bool, []Resource, error)
isSecret() bool
IsSecret() bool
}
var outputType = reflect.TypeOf((*Output)(nil)).Elem()
@ -419,11 +419,26 @@ func (o *OutputState) ApplyTWithContext(ctx context.Context, applier interface{}
return result
}
// isSecret returns a bool representing the secretness of the Output
func (o *OutputState) isSecret() bool {
// IsSecret returns a bool representing the secretness of the Output
func (o *OutputState) IsSecret() bool {
return o.getState().secret
}
// Unsecret will unwrap a secret output as a new output with a resolved value and no secretness
func Unsecret(input Output) Output {
return UnsecretWithContext(context.Background(), input)
}
// UnsecretWithContext will unwrap a secret output as a new output with a resolved value and no secretness
func UnsecretWithContext(ctx context.Context, input Output) Output {
var x bool
x = false
o := toOutputWithContext(ctx, input, &x)
// set immediate secretness ahead of resolution/fufillment
o.getState().secret = false
return o
}
// ToSecret wraps the input in an Output marked as secret
// that will resolve when all Inputs contained in the given value have resolved.
func ToSecret(input interface{}) Output {
@ -433,7 +448,9 @@ func ToSecret(input interface{}) Output {
// ToSecretWithContext wraps the input in an Output marked as secret
// that will resolve when all Inputs contained in the given value have resolved.
func ToSecretWithContext(ctx context.Context, input interface{}) Output {
o := toOutputWithContext(ctx, input, true)
var x bool
x = true
o := toOutputWithContext(ctx, input, &x)
// set immediate secretness ahead of resolution/fufillment
o.getState().secret = true
return o
@ -738,10 +755,10 @@ func ToOutput(v interface{}) Output {
// ToOutputWithContext returns an Output that will resolve when all Outputs contained in the given value have
// resolved.
func ToOutputWithContext(ctx context.Context, v interface{}) Output {
return toOutputWithContext(ctx, v, false)
return toOutputWithContext(ctx, v, nil)
}
func toOutputWithContext(ctx context.Context, v interface{}, forceSecret bool) Output {
func toOutputWithContext(ctx context.Context, v interface{}, forceSecretVal *bool) Output {
resolvedType := reflect.TypeOf(v)
if input, ok := v.(Input); ok {
resolvedType = input.ElementType()
@ -762,7 +779,9 @@ func toOutputWithContext(ctx context.Context, v interface{}, forceSecret bool) O
element := reflect.New(resolvedType).Elem()
known, secret, deps, err := awaitInputs(ctx, reflect.ValueOf(v), element)
secret = secret || forceSecret
if forceSecretVal != nil {
secret = *forceSecretVal
}
if err != nil || !known {
result.fulfill(nil, known, secret, deps, err)
return

View file

@ -396,11 +396,53 @@ func TestToOutputInputAny(t *testing.T) {
}, v)
}
// Test that Unsecret will return an Output that has an unwrapped secret
func TestUnsecret(t *testing.T) {
s := ToSecret(String("foo"))
// assert that secret is immediately secret
assert.True(t, s.IsSecret())
unS := Unsecret(s)
// assert that we do not have a secret
assert.False(t, unS.IsSecret())
errChan := make(chan error)
resultChan := make(chan string)
secretChan := make(chan bool)
unS.ApplyT(func(v interface{}) (string, error) {
// assert secretness after the output resolves
secretChan <- unS.IsSecret()
val := v.(string)
if val == "foo" {
// validate the value
resultChan <- val
} else {
errChan <- fmt.Errorf("Invalid result: %v", val)
}
return val, nil
})
for i := 0; i < 2; i++ {
select {
case err := <-errChan:
assert.Nil(t, err)
break
case r := <-resultChan:
assert.Equal(t, "foo", r)
break
case isSecret := <-secretChan:
assert.False(t, isSecret)
break
}
}
}
// Test that SecretT sets appropriate internal state and that IsSecret appropriately reads it.
func TestSecrets(t *testing.T) {
s := ToSecret(String("foo"))
// assert that secret is immediately secret
assert.True(t, s.isSecret())
assert.True(t, s.IsSecret())
errChan := make(chan error)
resultChan := make(chan string)
@ -408,7 +450,7 @@ func TestSecrets(t *testing.T) {
s.ApplyT(func(v interface{}) (string, error) {
// assert secretness after the output resolves
secretChan <- s.isSecret()
secretChan <- s.IsSecret()
val := v.(string)
if val == "foo" {
// validate the value
@ -439,7 +481,7 @@ func TestSecrets(t *testing.T) {
func TestSecretApply(t *testing.T) {
s1 := ToSecret(String("foo"))
// assert that secret is immediately secret
assert.True(t, s1.isSecret())
assert.True(t, s1.IsSecret())
s2 := StringInput(String("bar"))
errChan := make(chan error)
@ -452,7 +494,7 @@ func TestSecretApply(t *testing.T) {
})
s.ApplyT(func(v interface{}) (string, error) {
// assert secretness after the output resolves
secretChan <- s.isSecret()
secretChan <- s.IsSecret()
val := v.(string)
if val == "foobar" {
// validate the value