Add support for secrets in Go SDK (#3938)

This commit is contained in:
Evan Boyle 2020-02-25 17:45:36 -08:00 committed by GitHub
parent 0aa208a306
commit 930adc0504
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
26 changed files with 2729 additions and 1173 deletions

View file

@ -7,6 +7,9 @@ CHANGELOG
- Avoid panic when displaying failed stack policies.
[#3960](https://github.com/pulumi/pulumi/pull/3960)
- Add support for secrets in the Go SDK.
[3938](https://github.com/pulumi/pulumi/pull/3938)
## 1.11.0 (2020-02-19)
- Allow oversize protocol buffers for Python SDK.

View file

@ -226,11 +226,29 @@ func TestAssetReject(t *testing.T) {
func TestUnsupportedSecret(t *testing.T) {
rawProp := resource.NewObjectProperty(resource.NewPropertyMapFromMap(map[string]interface{}{
resource.SigKey: resource.SecretSig,
"value": "foo",
}))
prop, err := MarshalPropertyValue(rawProp, MarshalOptions{})
assert.Nil(t, err)
_, err = UnmarshalPropertyValue(prop, MarshalOptions{})
assert.Error(t, err)
val, err := UnmarshalPropertyValue(prop, MarshalOptions{})
assert.Nil(t, err)
assert.True(t, val.IsString())
assert.False(t, val.IsSecret())
assert.Equal(t, "foo", val.StringValue())
}
func TestSupportedSecret(t *testing.T) {
rawProp := resource.NewObjectProperty(resource.NewPropertyMapFromMap(map[string]interface{}{
resource.SigKey: resource.SecretSig,
"value": "foo",
}))
prop, err := MarshalPropertyValue(rawProp, MarshalOptions{KeepSecrets: true})
assert.Nil(t, err)
val, err := UnmarshalPropertyValue(prop, MarshalOptions{KeepSecrets: true})
assert.Nil(t, err)
assert.False(t, val.IsString())
assert.True(t, val.IsSecret())
assert.Equal(t, "foo", val.SecretValue().Element.StringValue())
}
func TestUnknownSig(t *testing.T) {

View file

@ -45,17 +45,23 @@ func (c *Config) Get(key string) string {
return Get(c.ctx, c.fullKey(key))
}
// GetObject loads an optional configuration value into the specified output by its key,
// or returns an error if unable to do so.
func (c *Config) GetObject(key string, output interface{}) error {
return GetObject(c.ctx, c.fullKey(key), output)
}
// GetBool loads an optional bool configuration value by its key, or returns false if it doesn't exist.
func (c *Config) GetBool(key string) bool {
return GetBool(c.ctx, c.fullKey(key))
}
// GetFloat32 loads an optional float32 configuration value by its key, or returns 0.0 if it doesn't exist.
// GetFloat32 loads an optional float32 configuration value by its key, or returns 0 if it doesn't exist.
func (c *Config) GetFloat32(key string) float32 {
return GetFloat32(c.ctx, c.fullKey(key))
}
// GetFloat64 loads an optional float64 configuration value by its key, or returns 0.0 if it doesn't exist.
// GetFloat64 loads an optional float64 configuration value by its key, or returns 0 if it doesn't exist.
func (c *Config) GetFloat64(key string) float64 {
return GetFloat64(c.ctx, c.fullKey(key))
}
@ -65,11 +71,6 @@ func (c *Config) GetInt(key string) int {
return GetInt(c.ctx, c.fullKey(key))
}
// GetInt8 loads an optional int8 configuration value by its key, or returns 0 if it doesn't exist.
func (c *Config) GetInt8(key string) int8 {
return GetInt8(c.ctx, c.fullKey(key))
}
// GetInt16 loads an optional int16 configuration value by its key, or returns 0 if it doesn't exist.
func (c *Config) GetInt16(key string) int16 {
return GetInt16(c.ctx, c.fullKey(key))
@ -85,10 +86,9 @@ func (c *Config) GetInt64(key string) int64 {
return GetInt64(c.ctx, c.fullKey(key))
}
// GetObject loads an optional configuration value into the specified output by its key,
// or returns an error if unable to do so.
func (c *Config) GetObject(key string, output interface{}) error {
return GetObject(c.ctx, c.fullKey(key), output)
// GetInt8 loads an optional int8 configuration value by its key, or returns 0 if it doesn't exist.
func (c *Config) GetInt8(key string) int8 {
return GetInt8(c.ctx, c.fullKey(key))
}
// GetUint loads an optional uint configuration value by its key, or returns 0 if it doesn't exist.
@ -96,11 +96,6 @@ func (c *Config) GetUint(key string) uint {
return GetUint(c.ctx, c.fullKey(key))
}
// GetUint8 loads an optional uint8 configuration value by its key, or returns 0 if it doesn't exist.
func (c *Config) GetUint8(key string) uint8 {
return GetUint8(c.ctx, c.fullKey(key))
}
// GetUint16 loads an optional uint16 configuration value by its key, or returns 0 if it doesn't exist.
func (c *Config) GetUint16(key string) uint16 {
return GetUint16(c.ctx, c.fullKey(key))
@ -116,11 +111,22 @@ func (c *Config) GetUint64(key string) uint64 {
return GetUint64(c.ctx, c.fullKey(key))
}
// GetUint8 loads an optional uint8 configuration value by its key, or returns 0 if it doesn't exist.
func (c *Config) GetUint8(key string) uint8 {
return GetUint8(c.ctx, c.fullKey(key))
}
// Require loads a configuration value by its key, or panics if it doesn't exist.
func (c *Config) Require(key string) string {
return Require(c.ctx, c.fullKey(key))
}
// RequireObject loads a required configuration value into the specified output by its key,
// or panics if unable to do so.
func (c *Config) RequireObject(key string, output interface{}) {
RequireObject(c.ctx, c.fullKey(key), output)
}
// RequireBool loads a bool configuration value by its key, or panics if it doesn't exist.
func (c *Config) RequireBool(key string) bool {
return RequireBool(c.ctx, c.fullKey(key))
@ -141,11 +147,6 @@ func (c *Config) RequireInt(key string) int {
return RequireInt(c.ctx, c.fullKey(key))
}
// RequireInt8 loads a int8 configuration value by its key, or panics if it doesn't exist.
func (c *Config) RequireInt8(key string) int8 {
return RequireInt8(c.ctx, c.fullKey(key))
}
// RequireInt16 loads a int16 configuration value by its key, or panics if it doesn't exist.
func (c *Config) RequireInt16(key string) int16 {
return RequireInt16(c.ctx, c.fullKey(key))
@ -161,10 +162,9 @@ func (c *Config) RequireInt64(key string) int64 {
return RequireInt64(c.ctx, c.fullKey(key))
}
// RequireObject loads a required configuration value into the specified output by its key,
// or panics if unable to do so.
func (c *Config) RequireObject(key string, output interface{}) {
RequireObject(c.ctx, c.fullKey(key), output)
// RequireInt8 loads a int8 configuration value by its key, or panics if it doesn't exist.
func (c *Config) RequireInt8(key string) int8 {
return RequireInt8(c.ctx, c.fullKey(key))
}
// RequireUint loads a uint configuration value by its key, or panics if it doesn't exist.
@ -172,11 +172,6 @@ func (c *Config) RequireUint(key string) uint {
return RequireUint(c.ctx, c.fullKey(key))
}
// RequireUint8 loads a uint8 configuration value by its key, or panics if it doesn't exist.
func (c *Config) RequireUint8(key string) uint8 {
return RequireUint8(c.ctx, c.fullKey(key))
}
// RequireUint16 loads a uint16 configuration value by its key, or panics if it doesn't exist.
func (c *Config) RequireUint16(key string) uint16 {
return RequireUint16(c.ctx, c.fullKey(key))
@ -192,11 +187,22 @@ func (c *Config) RequireUint64(key string) uint64 {
return RequireUint64(c.ctx, c.fullKey(key))
}
// RequireUint8 loads a uint8 configuration value by its key, or panics if it doesn't exist.
func (c *Config) RequireUint8(key string) uint8 {
return RequireUint8(c.ctx, c.fullKey(key))
}
// Try loads a configuration value by its key, returning a non-nil error if it doesn't exist.
func (c *Config) Try(key string) (string, error) {
return Try(c.ctx, c.fullKey(key))
}
// TryObject loads an optional configuration value into the specified output by its key,
// or returns an error if unable to do so.
func (c *Config) TryObject(key string, output interface{}) error {
return TryObject(c.ctx, c.fullKey(key), output)
}
// TryBool loads an optional bool configuration value by its key, or returns an error if it doesn't exist.
func (c *Config) TryBool(key string) (bool, error) {
return TryBool(c.ctx, c.fullKey(key))
@ -217,11 +223,6 @@ func (c *Config) TryInt(key string) (int, error) {
return TryInt(c.ctx, c.fullKey(key))
}
// TryInt8 loads an optional int8 configuration value by its key, or returns an error if it doesn't exist.
func (c *Config) TryInt8(key string) (int8, error) {
return TryInt8(c.ctx, c.fullKey(key))
}
// TryInt16 loads an optional int16 configuration value by its key, or returns an error if it doesn't exist.
func (c *Config) TryInt16(key string) (int16, error) {
return TryInt16(c.ctx, c.fullKey(key))
@ -237,10 +238,9 @@ func (c *Config) TryInt64(key string) (int64, error) {
return TryInt64(c.ctx, c.fullKey(key))
}
// TryObject loads an optional configuration value into the specified output by its key,
// or returns an error if unable to do so.
func (c *Config) TryObject(key string, output interface{}) error {
return TryObject(c.ctx, c.fullKey(key), output)
// TryInt8 loads an optional int8 configuration value by its key, or returns an error if it doesn't exist.
func (c *Config) TryInt8(key string) (int8, error) {
return TryInt8(c.ctx, c.fullKey(key))
}
// TryUint loads an optional uint configuration value by its key, or returns an error if it doesn't exist.
@ -248,11 +248,6 @@ func (c *Config) TryUint(key string) (uint, error) {
return TryUint(c.ctx, c.fullKey(key))
}
// TryUint8 loads an optional uint8 configuration value by its key, or returns an error if it doesn't exist.
func (c *Config) TryUint8(key string) (uint8, error) {
return TryUint8(c.ctx, c.fullKey(key))
}
// TryUint16 loads an optional uint16 configuration value by its key, or returns an error if it doesn't exist.
func (c *Config) TryUint16(key string) (uint16, error) {
return TryUint16(c.ctx, c.fullKey(key))
@ -267,3 +262,276 @@ func (c *Config) TryUint32(key string) (uint32, error) {
func (c *Config) TryUint64(key string) (uint64, error) {
return TryUint64(c.ctx, c.fullKey(key))
}
// TryUint8 loads an optional uint8 configuration value by its key, or returns an error if it doesn't exist.
func (c *Config) TryUint8(key string) (uint8, error) {
return TryUint8(c.ctx, c.fullKey(key))
}
// GetSecret loads an optional configuration value by its key
// or "" if it doesn't exist, and returns it wrapped in a secret Output.
func (c *Config) GetSecret(key string) pulumi.Output {
return GetSecret(c.ctx, c.fullKey(key))
}
// GetSecretObject loads an optional configuration value into the specified output by its key,
// returning it wrapped in a secret Output or an error if unable to do so.
func (c *Config) GetSecretObject(key string, output interface{}) (pulumi.Output, error) {
return GetSecretObject(c.ctx, c.fullKey(key), output)
}
// GetSecretBool loads an optional bool configuration value by its key
// or false if it doesn't exist, and returns it wrapped in a secret Output.
func (c *Config) GetSecretBool(key string) pulumi.Output {
return GetSecretBool(c.ctx, c.fullKey(key))
}
// GetSecretFloat32 loads an optional float32 configuration value by its key
// or 0 if it doesn't exist, and returns it wrapped in a secret Output.
func (c *Config) GetSecretFloat32(key string) pulumi.Output {
return GetSecretFloat32(c.ctx, c.fullKey(key))
}
// GetSecretFloat64 loads an optional float64 configuration value by its key
// or 0 if it doesn't exist, and returns it wrapped in a secret Output.
func (c *Config) GetSecretFloat64(key string) pulumi.Output {
return GetSecretFloat64(c.ctx, c.fullKey(key))
}
// GetSecretInt loads an optional int configuration value by its key
// or 0 if it doesn't exist, and returns it wrapped in a secret Output.
func (c *Config) GetSecretInt(key string) pulumi.Output {
return GetSecretInt(c.ctx, c.fullKey(key))
}
// GetSecretInt16 loads an optional int16 configuration value by its key
// or 0 if it doesn't exist, and returns it wrapped in a secret Output.
func (c *Config) GetSecretInt16(key string) pulumi.Output {
return GetSecretInt16(c.ctx, c.fullKey(key))
}
// GetSecretInt32 loads an optional int32 configuration value by its key
// or 0 if it doesn't exist, and returns it wrapped in a secret Output.
func (c *Config) GetSecretInt32(key string) pulumi.Output {
return GetSecretInt32(c.ctx, c.fullKey(key))
}
// GetSecretInt64 loads an optional int64 configuration value by its key
// or 0 if it doesn't exist, and returns it wrapped in a secret Output.
func (c *Config) GetSecretInt64(key string) pulumi.Output {
return GetSecretInt64(c.ctx, c.fullKey(key))
}
// GetSecretInt8 loads an optional int8 configuration value by its key
// or 0 if it doesn't exist, and returns it wrapped in a secret Output.
func (c *Config) GetSecretInt8(key string) pulumi.Output {
return GetSecretInt8(c.ctx, c.fullKey(key))
}
// GetSecretUint loads an optional uint configuration value by its key
// or 0 if it doesn't exist, and returns it wrapped in a secret Output.
func (c *Config) GetSecretUint(key string) pulumi.Output {
return GetSecretUint(c.ctx, c.fullKey(key))
}
// GetSecretUint16 loads an optional uint16 configuration value by its key
// or 0 if it doesn't exist, and returns it wrapped in a secret Output.
func (c *Config) GetSecretUint16(key string) pulumi.Output {
return GetSecretUint16(c.ctx, c.fullKey(key))
}
// GetSecretUint32 loads an optional uint32 configuration value by its key
// or 0 if it doesn't exist, and returns it wrapped in a secret Output.
func (c *Config) GetSecretUint32(key string) pulumi.Output {
return GetSecretUint32(c.ctx, c.fullKey(key))
}
// GetSecretUint64 loads an optional uint64 configuration value by its key
// or 0 if it doesn't exist, and returns it wrapped in a secret Output.
func (c *Config) GetSecretUint64(key string) pulumi.Output {
return GetSecretUint64(c.ctx, c.fullKey(key))
}
// GetSecretUint8 loads an optional uint8 configuration value by its key
// or 0 if it doesn't exist, and returns it wrapped in a secret Output.
func (c *Config) GetSecretUint8(key string) pulumi.Output {
return GetSecretUint8(c.ctx, c.fullKey(key))
}
// RequireSecret loads a configuration value by its key
// and returns it wrapped in a secret output, or panics if it doesn't exist.
func (c *Config) RequireSecret(key string) pulumi.Output {
return RequireSecret(c.ctx, c.fullKey(key))
}
// RequireSecretObject loads a required configuration value into the specified output by its key
// and returns it wrapped in a secret Output, or panics if unable to do so.
func (c *Config) RequireSecretObject(key string, output interface{}) pulumi.Output {
return RequireSecretObject(c.ctx, c.fullKey(key), output)
}
// RequireSecretBool loads a bool configuration value by its key
// and returns is wrapped in a secret Output, or panics if it doesn't exist.
func (c *Config) RequireSecretBool(key string) pulumi.Output {
return RequireSecretBool(c.ctx, c.fullKey(key))
}
// RequireSecretFloat32 loads a float32 configuration value by its key
// and returns is wrapped in a secret Output, or panics if it doesn't exist.
func (c *Config) RequireSecretFloat32(key string) pulumi.Output {
return RequireSecretFloat32(c.ctx, c.fullKey(key))
}
// RequireSecretFloat64 loads a float64 configuration value by its key
// and returns is wrapped in a secret Output, or panics if it doesn't exist.
func (c *Config) RequireSecretFloat64(key string) pulumi.Output {
return RequireSecretFloat64(c.ctx, c.fullKey(key))
}
// RequireSecretInt loads a int configuration value by its key
// and returns is wrapped in a secret Output, or panics if it doesn't exist.
func (c *Config) RequireSecretInt(key string) pulumi.Output {
return RequireSecretInt(c.ctx, c.fullKey(key))
}
// RequireSecretInt16 loads a int16 configuration value by its key
// and returns is wrapped in a secret Output, or panics if it doesn't exist.
func (c *Config) RequireSecretInt16(key string) pulumi.Output {
return RequireSecretInt16(c.ctx, c.fullKey(key))
}
// RequireSecretInt32 loads a int32 configuration value by its key
// and returns is wrapped in a secret Output, or panics if it doesn't exist.
func (c *Config) RequireSecretInt32(key string) pulumi.Output {
return RequireSecretInt32(c.ctx, c.fullKey(key))
}
// RequireSecretInt64 loads a int64 configuration value by its key
// and returns is wrapped in a secret Output, or panics if it doesn't exist.
func (c *Config) RequireSecretInt64(key string) pulumi.Output {
return RequireSecretInt64(c.ctx, c.fullKey(key))
}
// RequireSecretInt8 loads a int8 configuration value by its key
// and returns is wrapped in a secret Output, or panics if it doesn't exist.
func (c *Config) RequireSecretInt8(key string) pulumi.Output {
return RequireSecretInt8(c.ctx, c.fullKey(key))
}
// RequireSecretUint loads a uint configuration value by its key
// and returns is wrapped in a secret Output, or panics if it doesn't exist.
func (c *Config) RequireSecretUint(key string) pulumi.Output {
return RequireSecretUint(c.ctx, c.fullKey(key))
}
// RequireSecretUint16 loads a uint16 configuration value by its key
// and returns is wrapped in a secret Output, or panics if it doesn't exist.
func (c *Config) RequireSecretUint16(key string) pulumi.Output {
return RequireSecretUint16(c.ctx, c.fullKey(key))
}
// RequireSecretUint32 loads a uint32 configuration value by its key
// and returns is wrapped in a secret Output, or panics if it doesn't exist.
func (c *Config) RequireSecretUint32(key string) pulumi.Output {
return RequireSecretUint32(c.ctx, c.fullKey(key))
}
// RequireSecretUint64 loads a uint64 configuration value by its key
// and returns is wrapped in a secret Output, or panics if it doesn't exist.
func (c *Config) RequireSecretUint64(key string) pulumi.Output {
return RequireSecretUint64(c.ctx, c.fullKey(key))
}
// RequireSecretUint8 loads a uint8 configuration value by its key
// and returns is wrapped in a secret Output, or panics if it doesn't exist.
func (c *Config) RequireSecretUint8(key string) pulumi.Output {
return RequireSecretUint8(c.ctx, c.fullKey(key))
}
// TrySecret loads a configuration value by its key, returning a non-nil error if it doesn't exist.
func (c *Config) TrySecret(key string) (pulumi.Output, error) {
return TrySecret(c.ctx, c.fullKey(key))
}
// TrySecretObject loads a configuration value by its key, returning a non-nil error if it doesn't exist.
func (c *Config) TrySecretObject(key string, output interface{}) (pulumi.Output, error) {
return TrySecretObject(c.ctx, c.fullKey(key), output)
}
// TrySecretBool loads an optional bool configuration value by its key into a secret Output,
// or returns an error if it doesn't exist.
func (c *Config) TrySecretBool(key string) (pulumi.Output, error) {
return TrySecretBool(c.ctx, c.fullKey(key))
}
// TrySecretFloat32 loads an optional float32 configuration value by its key into a secret Output,
// or returns an error if it doesn't exist.
func (c *Config) TrySecretFloat32(key string) (pulumi.Output, error) {
return TrySecretFloat32(c.ctx, c.fullKey(key))
}
// TrySecretFloat64 loads an optional float64 configuration value by its key into a secret Output,
// or returns an error if it doesn't exist.
func (c *Config) TrySecretFloat64(key string) (pulumi.Output, error) {
return TrySecretFloat64(c.ctx, c.fullKey(key))
}
// TrySecretInt loads an optional int configuration value by its key into a secret Output,
// or returns an error if it doesn't exist.
func (c *Config) TrySecretInt(key string) (pulumi.Output, error) {
return TrySecretInt(c.ctx, c.fullKey(key))
}
// TrySecretInt16 loads an optional int16 configuration value by its key into a secret Output,
// or returns an error if it doesn't exist.
func (c *Config) TrySecretInt16(key string) (pulumi.Output, error) {
return TrySecretInt16(c.ctx, c.fullKey(key))
}
// TrySecretInt32 loads an optional int32 configuration value by its key into a secret Output,
// or returns an error if it doesn't exist.
func (c *Config) TrySecretInt32(key string) (pulumi.Output, error) {
return TrySecretInt32(c.ctx, c.fullKey(key))
}
// TrySecretInt64 loads an optional int64 configuration value by its key into a secret Output,
// or returns an error if it doesn't exist.
func (c *Config) TrySecretInt64(key string) (pulumi.Output, error) {
return TrySecretInt64(c.ctx, c.fullKey(key))
}
// TrySecretInt8 loads an optional int8 configuration value by its key into a secret Output,
// or returns an error if it doesn't exist.
func (c *Config) TrySecretInt8(key string) (pulumi.Output, error) {
return TrySecretInt8(c.ctx, c.fullKey(key))
}
// TrySecretUint loads an optional uint configuration value by its key into a secret Output,
// or returns an error if it doesn't exist.
func (c *Config) TrySecretUint(key string) (pulumi.Output, error) {
return TrySecretUint(c.ctx, c.fullKey(key))
}
// TrySecretUint16 loads an optional uint16 configuration value by its key into a secret Output,
// or returns an error if it doesn't exist.
func (c *Config) TrySecretUint16(key string) (pulumi.Output, error) {
return TrySecretUint16(c.ctx, c.fullKey(key))
}
// TrySecretUint32 loads an optional uint32 configuration value by its key into a secret Output,
// or returns an error if it doesn't exist.
func (c *Config) TrySecretUint32(key string) (pulumi.Output, error) {
return TrySecretUint32(c.ctx, c.fullKey(key))
}
// TrySecretUint64 loads an optional uint64 configuration value by its key into a secret Output,
// or returns an error if it doesn't exist.
func (c *Config) TrySecretUint64(key string) (pulumi.Output, error) {
return TrySecretUint64(c.ctx, c.fullKey(key))
}
// TrySecretUint8 loads an optional uint8 configuration value by its key into a secret Output,
// or returns an error if it doesn't exist.
func (c *Config) TrySecretUint8(key string) (pulumi.Output, error) {
return TrySecretUint8(c.ctx, c.fullKey(key))
}

View file

@ -16,8 +16,10 @@ package config
import (
"context"
"reflect"
"testing"
"github.com/pkg/errors"
"github.com/stretchr/testify/assert"
"github.com/pulumi/pulumi/sdk/go/pulumi"
@ -149,3 +151,164 @@ func TestConfig(t *testing.T) {
_, err = cfg.Try("missing")
assert.NotNil(t, err)
}
func TestSecretConfig(t *testing.T) {
ctx, err := pulumi.NewContext(context.Background(), pulumi.RunInfo{
Config: map[string]string{
"testpkg:sss": "a string value",
"testpkg:bbb": "true",
"testpkg:intint": "42",
"testpkg:fpfpfp": "99.963",
"testpkg:obj": `
{
"foo": {
"a": "1",
"b": "2"
},
"bar": "abc"
}
`,
"testpkg:malobj": "not_a_struct",
},
})
assert.Nil(t, err)
cfg := New(ctx, "testpkg")
fooMap := make(map[string]string)
fooMap["a"] = "1"
fooMap["b"] = "2"
expectedTestStruct := TestStruct{
Foo: fooMap,
Bar: "abc",
}
s1, err := cfg.TrySecret("sss")
s2 := cfg.RequireSecret("sss")
s3 := cfg.GetSecret("sss")
assert.Nil(t, err)
errChan := make(chan error)
result := make(chan string)
pulumi.All(s1, s2, s3).ApplyT(func(v []interface{}) ([]interface{}, error) {
for _, val := range v {
if val == "a string value" {
result <- val.(string)
} else {
errChan <- errors.Errorf("Invalid result: %v", val)
}
}
return v, nil
})
for i := 0; i < 3; i++ {
select {
case err = <-errChan:
assert.Nil(t, err)
break
case r := <-result:
assert.Equal(t, "a string value", r)
break
}
}
errChan = make(chan error)
objResult := make(chan TestStruct)
testStruct4 := TestStruct{}
testStruct5 := TestStruct{}
testStruct6 := TestStruct{}
s1, err = cfg.TrySecretObject("obj", &testStruct4)
assert.Nil(t, err)
s2 = cfg.RequireSecretObject("obj", &testStruct5)
s3, err = cfg.GetSecretObject("obj", &testStruct6)
assert.Nil(t, err)
pulumi.All(s1, s2, s3).ApplyT(func(v []interface{}) ([]interface{}, error) {
for _, val := range v {
ts := val.(*TestStruct)
if reflect.DeepEqual(expectedTestStruct, *ts) {
objResult <- *ts
} else {
errChan <- errors.Errorf("Invalid result: %v", val)
}
}
return v, nil
})
for i := 0; i < 3; i++ {
select {
case err = <-errChan:
assert.Nil(t, err)
break
case o := <-objResult:
assert.Equal(t, expectedTestStruct, o)
break
}
}
s1, err = cfg.TrySecretBool("bbb")
s2 = cfg.RequireSecretBool("bbb")
s3 = cfg.GetSecretBool("bbb")
assert.Nil(t, err)
errChan = make(chan error)
resultBool := make(chan bool)
pulumi.All(s1, s2, s3).ApplyT(func(v []interface{}) ([]interface{}, error) {
for _, val := range v {
if val == true {
resultBool <- val.(bool)
} else {
errChan <- errors.Errorf("Invalid result: %v", val)
}
}
return v, nil
})
for i := 0; i < 3; i++ {
select {
case err = <-errChan:
assert.Nil(t, err)
break
case r := <-resultBool:
assert.Equal(t, true, r)
break
}
}
s1, err = cfg.TrySecretInt("intint")
s2 = cfg.RequireSecretInt("intint")
s3 = cfg.GetSecretInt("intint")
assert.Nil(t, err)
errChan = make(chan error)
resultInt := make(chan int)
pulumi.All(s1, s2, s3).ApplyT(func(v []interface{}) ([]interface{}, error) {
for _, val := range v {
if val == 42 {
resultInt <- val.(int)
} else {
errChan <- errors.Errorf("Invalid result: %v", val)
}
}
return v, nil
})
for i := 0; i < 3; i++ {
select {
case err = <-errChan:
assert.Nil(t, err)
break
case r := <-resultInt:
assert.Equal(t, 42, r)
break
}
}
}

View file

@ -28,6 +28,15 @@ func Get(ctx *pulumi.Context, key string) string {
return v
}
// GetObject attempts to load an optional configuration value by its key into the specified output variable.
func GetObject(ctx *pulumi.Context, key string, output interface{}) error {
if v, ok := ctx.GetConfig(key); ok {
return json.Unmarshal([]byte(v), output)
}
return nil
}
// GetBool loads an optional configuration value by its key, as a bool, or returns false if it doesn't exist.
func GetBool(ctx *pulumi.Context, key string) bool {
if v, ok := ctx.GetConfig(key); ok {
@ -36,7 +45,7 @@ func GetBool(ctx *pulumi.Context, key string) bool {
return false
}
// GetFloat32 loads an optional configuration value by its key, as a float32, or returns 0.0 if it doesn't exist.
// GetFloat32 loads an optional configuration value by its key, as a float32, or returns 0 if it doesn't exist.
func GetFloat32(ctx *pulumi.Context, key string) float32 {
if v, ok := ctx.GetConfig(key); ok {
return cast.ToFloat32(v)
@ -44,7 +53,7 @@ func GetFloat32(ctx *pulumi.Context, key string) float32 {
return 0
}
// GetFloat64 loads an optional configuration value by its key, as a float64, or returns 0.0 if it doesn't exist.
// GetFloat64 loads an optional configuration value by its key, as a float64, or returns 0 if it doesn't exist.
func GetFloat64(ctx *pulumi.Context, key string) float64 {
if v, ok := ctx.GetConfig(key); ok {
return cast.ToFloat64(v)
@ -60,14 +69,6 @@ func GetInt(ctx *pulumi.Context, key string) int {
return 0
}
// GetInt8 loads an optional configuration value by its key, as a int8, or returns 0 if it doesn't exist.
func GetInt8(ctx *pulumi.Context, key string) int8 {
if v, ok := ctx.GetConfig(key); ok {
return cast.ToInt8(v)
}
return 0
}
// GetInt16 loads an optional configuration value by its key, as a int16, or returns 0 if it doesn't exist.
func GetInt16(ctx *pulumi.Context, key string) int16 {
if v, ok := ctx.GetConfig(key); ok {
@ -92,15 +93,12 @@ func GetInt64(ctx *pulumi.Context, key string) int64 {
return 0
}
// GetObject attempts to load an optional configuration value by its key into the specified output variable.
func GetObject(ctx *pulumi.Context, key string, output interface{}) error {
// GetInt8 loads an optional configuration value by its key, as a int8, or returns 0 if it doesn't exist.
func GetInt8(ctx *pulumi.Context, key string) int8 {
if v, ok := ctx.GetConfig(key); ok {
if err := json.Unmarshal([]byte(v), output); err != nil {
return err
}
return cast.ToInt8(v)
}
return nil
return 0
}
// GetUint loads an optional configuration value by its key, as a uint, or returns 0 if it doesn't exist.
@ -111,14 +109,6 @@ func GetUint(ctx *pulumi.Context, key string) uint {
return 0
}
// GetUint8 loads an optional configuration value by its key, as a uint8, or returns 0 if it doesn't exist.
func GetUint8(ctx *pulumi.Context, key string) uint8 {
if v, ok := ctx.GetConfig(key); ok {
return cast.ToUint8(v)
}
return 0
}
// GetUint16 loads an optional configuration value by its key, as a uint16, or returns 0 if it doesn't exist.
func GetUint16(ctx *pulumi.Context, key string) uint16 {
if v, ok := ctx.GetConfig(key); ok {
@ -142,3 +132,104 @@ func GetUint64(ctx *pulumi.Context, key string) uint64 {
}
return 0
}
// GetUint8 loads an optional configuration value by its key, as a uint8, or returns 0 if it doesn't exist.
func GetUint8(ctx *pulumi.Context, key string) uint8 {
if v, ok := ctx.GetConfig(key); ok {
return cast.ToUint8(v)
}
return 0
}
// GetSecret loads an optional configuration value by its key, or "" if it does not exist, into a secret Output.
func GetSecret(ctx *pulumi.Context, key string) pulumi.Output {
v, _ := ctx.GetConfig(key)
return pulumi.ToSecret(pulumi.String(v))
}
// GetSecretObject attempts to load an optional configuration value by its key into the specified output variable.
func GetSecretObject(ctx *pulumi.Context, key string, output interface{}) (pulumi.Output, error) {
if err := GetObject(ctx, key, output); err != nil {
return nil, err
}
return pulumi.ToSecret(output), nil
}
// GetSecretBool loads an optional bool configuration value by its key,
// or false if it does not exist, into a secret Output.
func GetSecretBool(ctx *pulumi.Context, key string) pulumi.Output {
return pulumi.ToSecret(GetBool(ctx, key))
}
// GetSecretFloat32 loads an optional float32 configuration value by its key,
// or false if it does not exist, into a secret Output.
func GetSecretFloat32(ctx *pulumi.Context, key string) pulumi.Output {
return pulumi.ToSecret(GetFloat32(ctx, key))
}
// GetSecretFloat64 loads an optional float64 configuration value by its key,
// or false if it does not exist, into a secret Output.
func GetSecretFloat64(ctx *pulumi.Context, key string) pulumi.Output {
return pulumi.ToSecret(GetFloat64(ctx, key))
}
// GetSecretInt loads an optional int configuration value by its key,
// or false if it does not exist, into a secret Output.
func GetSecretInt(ctx *pulumi.Context, key string) pulumi.Output {
return pulumi.ToSecret(GetInt(ctx, key))
}
// GetSecretInt16 loads an optional int16 configuration value by its key,
// or false if it does not exist, into a secret Output.
func GetSecretInt16(ctx *pulumi.Context, key string) pulumi.Output {
return pulumi.ToSecret(GetInt16(ctx, key))
}
// GetSecretInt32 loads an optional int32 configuration value by its key,
// or false if it does not exist, into a secret Output.
func GetSecretInt32(ctx *pulumi.Context, key string) pulumi.Output {
return pulumi.ToSecret(GetInt32(ctx, key))
}
// GetSecretInt64 loads an optional int64 configuration value by its key,
// or false if it does not exist, into a secret Output.
func GetSecretInt64(ctx *pulumi.Context, key string) pulumi.Output {
return pulumi.ToSecret(GetInt64(ctx, key))
}
// GetSecretInt8 loads an optional int8 configuration value by its key,
// or false if it does not exist, into a secret Output.
func GetSecretInt8(ctx *pulumi.Context, key string) pulumi.Output {
return pulumi.ToSecret(GetInt8(ctx, key))
}
// GetSecretUint loads an optional uint configuration value by its key,
// or false if it does not exist, into a secret Output.
func GetSecretUint(ctx *pulumi.Context, key string) pulumi.Output {
return pulumi.ToSecret(GetUint(ctx, key))
}
// GetSecretUint16 loads an optional uint16 configuration value by its key,
// or false if it does not exist, into a secret Output.
func GetSecretUint16(ctx *pulumi.Context, key string) pulumi.Output {
return pulumi.ToSecret(GetUint16(ctx, key))
}
// GetSecretUint32 loads an optional uint32 configuration value by its key,
// or false if it does not exist, into a secret Output.
func GetSecretUint32(ctx *pulumi.Context, key string) pulumi.Output {
return pulumi.ToSecret(GetUint32(ctx, key))
}
// GetSecretUint64 loads an optional uint64 configuration value by its key,
// or false if it does not exist, into a secret Output.
func GetSecretUint64(ctx *pulumi.Context, key string) pulumi.Output {
return pulumi.ToSecret(GetUint64(ctx, key))
}
// GetSecretUint8 loads an optional uint8 configuration value by its key,
// or false if it does not exist, into a secret Output.
func GetSecretUint8(ctx *pulumi.Context, key string) pulumi.Output {
return pulumi.ToSecret(GetUint8(ctx, key))
}

View file

@ -32,6 +32,15 @@ func Require(ctx *pulumi.Context, key string) string {
return v
}
// RequireObject loads an optional configuration value by its key into the output variable,
// or panics if unable to do so.
func RequireObject(ctx *pulumi.Context, key string, output interface{}) {
v := Require(ctx, key)
if err := json.Unmarshal([]byte(v), output); err != nil {
contract.Failf("unable to unmarshall required configuration variable '%s'; %s", key, err.Error())
}
}
// RequireBool loads an optional configuration value by its key, as a bool, or panics if it doesn't exist.
func RequireBool(ctx *pulumi.Context, key string) bool {
v := Require(ctx, key)
@ -56,12 +65,6 @@ func RequireInt(ctx *pulumi.Context, key string) int {
return cast.ToInt(v)
}
// RequireInt8 loads an optional configuration value by its key, as a int8, or panics if it doesn't exist.
func RequireInt8(ctx *pulumi.Context, key string) int8 {
v := Require(ctx, key)
return cast.ToInt8(v)
}
// RequireInt16 loads an optional configuration value by its key, as a int16, or panics if it doesn't exist.
func RequireInt16(ctx *pulumi.Context, key string) int16 {
v := Require(ctx, key)
@ -80,13 +83,10 @@ func RequireInt64(ctx *pulumi.Context, key string) int64 {
return cast.ToInt64(v)
}
// RequireObject loads an optional configuration value by its key into the output variable,
// or panics if unable to do so.
func RequireObject(ctx *pulumi.Context, key string, output interface{}) {
// RequireInt8 loads an optional configuration value by its key, as a int8, or panics if it doesn't exist.
func RequireInt8(ctx *pulumi.Context, key string) int8 {
v := Require(ctx, key)
if err := json.Unmarshal([]byte(v), output); err != nil {
contract.Failf("unable to unmarshall required configuration variable '%s'; %s", key, err.Error())
}
return cast.ToInt8(v)
}
// RequireUint loads an optional configuration value by its key, as a uint, or panics if it doesn't exist.
@ -95,12 +95,6 @@ func RequireUint(ctx *pulumi.Context, key string) uint {
return cast.ToUint(v)
}
// RequireUint8 loads an optional configuration value by its key, as a uint8, or panics if it doesn't exist.
func RequireUint8(ctx *pulumi.Context, key string) uint8 {
v := Require(ctx, key)
return cast.ToUint8(v)
}
// RequireUint16 loads an optional configuration value by its key, as a uint16, or panics if it doesn't exist.
func RequireUint16(ctx *pulumi.Context, key string) uint16 {
v := Require(ctx, key)
@ -118,3 +112,100 @@ func RequireUint64(ctx *pulumi.Context, key string) uint64 {
v := Require(ctx, key)
return cast.ToUint64(v)
}
// RequireUint8 loads an optional configuration value by its key, as a uint8, or panics if it doesn't exist.
func RequireUint8(ctx *pulumi.Context, key string) uint8 {
v := Require(ctx, key)
return cast.ToUint8(v)
}
// RequireSecret loads a configuration value by its key returning it wrapped in a secret Output,
// or panics if it doesn't exist.
func RequireSecret(ctx *pulumi.Context, key string) pulumi.Output {
return pulumi.ToSecret(Require(ctx, key))
}
// RequireSecretObject loads an optional configuration value by its key into the output variable,
// returning it wrapped in a secret Output, or panics if unable to do so.
func RequireSecretObject(ctx *pulumi.Context, key string, output interface{}) pulumi.Output {
RequireObject(ctx, key, output)
return pulumi.ToSecret(output)
}
// RequireSecretBool loads an optional configuration value by its key,
// as a bool wrapped in a secret Output, or panics if it doesn't exist.
func RequireSecretBool(ctx *pulumi.Context, key string) pulumi.Output {
return pulumi.ToSecret(RequireBool(ctx, key))
}
// RequireSecretFloat32 loads an optional configuration value by its key,
// as a float32 wrapped in a secret Output, or panics if it doesn't exist.
func RequireSecretFloat32(ctx *pulumi.Context, key string) pulumi.Output {
return pulumi.ToSecret(RequireFloat32(ctx, key))
}
// RequireSecretFloat64 loads an optional configuration value by its key,
// as a float64 wrapped in a secret Output, or panics if it doesn't exist.
func RequireSecretFloat64(ctx *pulumi.Context, key string) pulumi.Output {
return pulumi.ToSecret(RequireFloat64(ctx, key))
}
// RequireSecretInt loads an optional configuration value by its key,
// as a int wrapped in a secret Output, or panics if it doesn't exist.
func RequireSecretInt(ctx *pulumi.Context, key string) pulumi.Output {
return pulumi.ToSecret(RequireInt(ctx, key))
}
// RequireSecretInt16 loads an optional configuration value by its key,
// as a int16 wrapped in a secret Output, or panics if it doesn't exist.
func RequireSecretInt16(ctx *pulumi.Context, key string) pulumi.Output {
return pulumi.ToSecret(RequireInt16(ctx, key))
}
// RequireSecretInt32 loads an optional configuration value by its key,
// as a int32 wrapped in a secret Output, or panics if it doesn't exist.
func RequireSecretInt32(ctx *pulumi.Context, key string) pulumi.Output {
return pulumi.ToSecret(RequireInt32(ctx, key))
}
// RequireSecretInt64 loads an optional configuration value by its key,
// as a int64 wrapped in a secret Output, or panics if it doesn't exist.
func RequireSecretInt64(ctx *pulumi.Context, key string) pulumi.Output {
return pulumi.ToSecret(RequireInt64(ctx, key))
}
// RequireSecretInt8 loads an optional configuration value by its key,
// as a int8 wrapped in a secret Output, or panics if it doesn't exist.
func RequireSecretInt8(ctx *pulumi.Context, key string) pulumi.Output {
return pulumi.ToSecret(RequireInt8(ctx, key))
}
// RequireSecretUint loads an optional configuration value by its key,
// as a uint wrapped in a secret Output, or panics if it doesn't exist.
func RequireSecretUint(ctx *pulumi.Context, key string) pulumi.Output {
return pulumi.ToSecret(RequireUint(ctx, key))
}
// RequireSecretUint16 loads an optional configuration value by its key,
// as a uint16 wrapped in a secret Output, or panics if it doesn't exist.
func RequireSecretUint16(ctx *pulumi.Context, key string) pulumi.Output {
return pulumi.ToSecret(RequireUint16(ctx, key))
}
// RequireSecretUint32 loads an optional configuration value by its key,
// as a uint32 wrapped in a secret Output, or panics if it doesn't exist.
func RequireSecretUint32(ctx *pulumi.Context, key string) pulumi.Output {
return pulumi.ToSecret(RequireUint32(ctx, key))
}
// RequireSecretUint64 loads an optional configuration value by its key,
// as a uint64 wrapped in a secret Output, or panics if it doesn't exist.
func RequireSecretUint64(ctx *pulumi.Context, key string) pulumi.Output {
return pulumi.ToSecret(RequireUint64(ctx, key))
}
// RequireSecretUint8 loads an optional configuration value by its key,
// as a uint8 wrapped in a secret Output, or panics if it doesn't exist.
func RequireSecretUint8(ctx *pulumi.Context, key string) pulumi.Output {
return pulumi.ToSecret(RequireUint8(ctx, key))
}

View file

@ -33,6 +33,16 @@ func Try(ctx *pulumi.Context, key string) (string, error) {
return v, nil
}
// TryObject loads an optional configuration value by its key into the output variable,
// or returns an error if unable to do so.
func TryObject(ctx *pulumi.Context, key string, output interface{}) error {
v, err := Try(ctx, key)
if err != nil {
return err
}
return json.Unmarshal([]byte(v), output)
}
// TryBool loads an optional configuration value by its key, as a bool, or returns an error if it doesn't exist.
func TryBool(ctx *pulumi.Context, key string) (bool, error) {
v, err := Try(ctx, key)
@ -69,15 +79,6 @@ func TryInt(ctx *pulumi.Context, key string) (int, error) {
return cast.ToInt(v), nil
}
// TryInt8 loads an optional configuration value by its key, as a int8, or returns an error if it doesn't exist.
func TryInt8(ctx *pulumi.Context, key string) (int8, error) {
v, err := Try(ctx, key)
if err != nil {
return 0, err
}
return cast.ToInt8(v), nil
}
// TryInt16 loads an optional configuration value by its key, as a int16, or returns an error if it doesn't exist.
func TryInt16(ctx *pulumi.Context, key string) (int16, error) {
v, err := Try(ctx, key)
@ -105,14 +106,13 @@ func TryInt64(ctx *pulumi.Context, key string) (int64, error) {
return cast.ToInt64(v), nil
}
// TryObject loads an optional configuration value by its key into the output variable,
// or returns an error if unable to do so.
func TryObject(ctx *pulumi.Context, key string, output interface{}) error {
// TryInt8 loads an optional configuration value by its key, as a int8, or returns an error if it doesn't exist.
func TryInt8(ctx *pulumi.Context, key string) (int8, error) {
v, err := Try(ctx, key)
if err != nil {
return err
return 0, err
}
return json.Unmarshal([]byte(v), output)
return cast.ToInt8(v), nil
}
// TryUint loads an optional configuration value by its key, as a uint, or returns an error if it doesn't exist.
@ -124,15 +124,6 @@ func TryUint(ctx *pulumi.Context, key string) (uint, error) {
return cast.ToUint(v), nil
}
// TryUint8 loads an optional configuration value by its key, as a uint8, or returns an error if it doesn't exist.
func TryUint8(ctx *pulumi.Context, key string) (uint8, error) {
v, err := Try(ctx, key)
if err != nil {
return 0, err
}
return cast.ToUint8(v), nil
}
// TryUint16 loads an optional configuration value by its key, as a uint16, or returns an error if it doesn't exist.
func TryUint16(ctx *pulumi.Context, key string) (uint16, error) {
v, err := Try(ctx, key)
@ -159,3 +150,162 @@ func TryUint64(ctx *pulumi.Context, key string) (uint64, error) {
}
return cast.ToUint64(v), nil
}
// TryUint8 loads an optional configuration value by its key, as a uint8, or returns an error if it doesn't exist.
func TryUint8(ctx *pulumi.Context, key string) (uint8, error) {
v, err := Try(ctx, key)
if err != nil {
return 0, err
}
return cast.ToUint8(v), nil
}
// TrySecret loads a configuration value by its key, returning a non-nil error if it doesn't exist.
func TrySecret(ctx *pulumi.Context, key string) (pulumi.Output, error) {
v, err := Try(ctx, key)
if err != nil {
return nil, err
}
return pulumi.ToSecret(pulumi.String(v)), nil
}
// TrySecretObject loads a configuration value by its key into the output variable,
// or returns an error if unable to do so.
func TrySecretObject(ctx *pulumi.Context, key string, output interface{}) (pulumi.Output, error) {
err := TryObject(ctx, key, output)
if err != nil {
return nil, err
}
return pulumi.ToSecret(output), nil
}
// TrySecretBool loads an optional configuration value by its key, as a bool,
// or returns an error if it doesn't exist.
func TrySecretBool(ctx *pulumi.Context, key string) (pulumi.Output, error) {
v, err := TryBool(ctx, key)
if err != nil {
return nil, err
}
return pulumi.ToSecret(pulumi.Bool(v)), nil
}
// TrySecretFloat32 loads an optional configuration value by its key, as a float32,
// or returns an error if it doesn't exist.
func TrySecretFloat32(ctx *pulumi.Context, key string) (pulumi.Output, error) {
v, err := TryFloat32(ctx, key)
if err != nil {
return nil, err
}
return pulumi.ToSecret(pulumi.Float32(v)), nil
}
// TrySecretFloat64 loads an optional configuration value by its key, as a float64,
// or returns an error if it doesn't exist.
func TrySecretFloat64(ctx *pulumi.Context, key string) (pulumi.Output, error) {
v, err := TryFloat64(ctx, key)
if err != nil {
return nil, err
}
return pulumi.ToSecret(pulumi.Float64(v)), nil
}
// TrySecretInt loads an optional configuration value by its key, as a int,
// or returns an error if it doesn't exist.
func TrySecretInt(ctx *pulumi.Context, key string) (pulumi.Output, error) {
v, err := TryInt(ctx, key)
if err != nil {
return nil, err
}
return pulumi.ToSecret(pulumi.Int(v)), nil
}
// TrySecretInt16 loads an optional configuration value by its key, as a int16,
// or returns an error if it doesn't exist.
func TrySecretInt16(ctx *pulumi.Context, key string) (pulumi.Output, error) {
v, err := TryInt16(ctx, key)
if err != nil {
return nil, err
}
return pulumi.ToSecret(pulumi.Int16(v)), nil
}
// TrySecretInt32 loads an optional configuration value by its key, as a int32,
// or returns an error if it doesn't exist.
func TrySecretInt32(ctx *pulumi.Context, key string) (pulumi.Output, error) {
v, err := TryInt32(ctx, key)
if err != nil {
return nil, err
}
return pulumi.ToSecret(pulumi.Int32(v)), nil
}
// TrySecretInt64 loads an optional configuration value by its key, as a int64,
// or returns an error if it doesn't exist.
func TrySecretInt64(ctx *pulumi.Context, key string) (pulumi.Output, error) {
v, err := TryInt64(ctx, key)
if err != nil {
return nil, err
}
return pulumi.ToSecret(pulumi.Int64(v)), nil
}
// TrySecretInt8 loads an optional configuration value by its key, as a int8,
// or returns an error if it doesn't exist.
func TrySecretInt8(ctx *pulumi.Context, key string) (pulumi.Output, error) {
v, err := TryInt8(ctx, key)
if err != nil {
return nil, err
}
return pulumi.ToSecret(pulumi.Int8(v)), nil
}
// TrySecretUint loads an optional configuration value by its key, as a uint,
// or returns an error if it doesn't exist.
func TrySecretUint(ctx *pulumi.Context, key string) (pulumi.Output, error) {
v, err := TryUint(ctx, key)
if err != nil {
return nil, err
}
return pulumi.ToSecret(pulumi.Uint(v)), nil
}
// TrySecretUint16 loads an optional configuration value by its key, as a uint16,
// or returns an error if it doesn't exist.
func TrySecretUint16(ctx *pulumi.Context, key string) (pulumi.Output, error) {
v, err := TryUint16(ctx, key)
if err != nil {
return nil, err
}
return pulumi.ToSecret(pulumi.Uint16(v)), nil
}
// TrySecretUint32 loads an optional configuration value by its key, as a uint32,
// or returns an error if it doesn't exist.
func TrySecretUint32(ctx *pulumi.Context, key string) (pulumi.Output, error) {
v, err := TryUint32(ctx, key)
if err != nil {
return nil, err
}
return pulumi.ToSecret(pulumi.Uint32(v)), nil
}
// TrySecretUint64 loads an optional configuration value by its key, as a uint64,
// or returns an error if it doesn't exist.
func TrySecretUint64(ctx *pulumi.Context, key string) (pulumi.Output, error) {
v, err := TryUint64(ctx, key)
if err != nil {
return nil, err
}
return pulumi.ToSecret(pulumi.Uint64(v)), nil
}
// TrySecretUint8 loads an optional configuration value by its key, as a uint8,
// or returns an error if it doesn't exist.
func TrySecretUint8(ctx *pulumi.Context, key string) (pulumi.Output, error) {
v, err := TryUint8(ctx, key)
if err != nil {
return nil, err
}
return pulumi.ToSecret(pulumi.Uint8(v)), nil
}

View file

@ -172,7 +172,7 @@ func (ctx *Context) Invoke(tok string, args interface{}, result interface{}, opt
rpcArgs, err := plugin.MarshalProperties(
resolvedArgsMap,
plugin.MarshalOptions{KeepUnknowns: false})
plugin.MarshalOptions{KeepUnknowns: false, KeepSecrets: true})
if err != nil {
return errors.Wrap(err, "marshaling arguments")
}
@ -212,9 +212,14 @@ func (ctx *Context) Invoke(tok string, args interface{}, result interface{}, opt
return err
}
if err = unmarshalOutput(resource.NewObjectProperty(outProps), resultV.Elem()); err != nil {
// fail if there are secrets returned from the invoke
hasSecret, err := unmarshalOutput(resource.NewObjectProperty(outProps), resultV.Elem())
if err != nil {
return err
}
if hasSecret {
return errors.Errorf("Unexpected secret result returned to invoke call.")
}
logging.V(9).Infof("Invoke(%s, ...): success: w/ %d outs (err=%v)", tok, len(outProps), err)
return nil
}
@ -300,7 +305,7 @@ func (ctx *Context) ReadResource(
ctx.endRPC(err)
}()
idToRead, known, err := id.ToIDOutput().awaitID(context.TODO())
idToRead, known, _, err := id.ToIDOutput().awaitID(context.TODO())
if !known || err != nil {
return
}
@ -313,13 +318,15 @@ func (ctx *Context) ReadResource(
logging.V(9).Infof("ReadResource(%s, %s): Goroutine spawned, RPC call being made", t, name)
resp, err := ctx.monitor.ReadResource(ctx.ctx, &pulumirpc.ReadResourceRequest{
Type: t,
Name: name,
Parent: inputs.parent,
Properties: inputs.rpcProps,
Provider: inputs.provider,
Id: string(idToRead),
Aliases: inputs.aliases,
Type: t,
Name: name,
Parent: inputs.parent,
Properties: inputs.rpcProps,
Provider: inputs.provider,
Id: string(idToRead),
Aliases: inputs.aliases,
AcceptSecrets: true,
AdditionalSecretOutputs: inputs.additionalSecretOutputs,
})
if err != nil {
logging.V(9).Infof("ReadResource(%s, %s): error: %v", t, name, err)
@ -429,20 +436,22 @@ func (ctx *Context) RegisterResource(
logging.V(9).Infof("RegisterResource(%s, %s): Goroutine spawned, RPC call being made", t, name)
resp, err := ctx.monitor.RegisterResource(ctx.ctx, &pulumirpc.RegisterResourceRequest{
Type: t,
Name: name,
Parent: inputs.parent,
Object: inputs.rpcProps,
Custom: custom,
Protect: inputs.protect,
Dependencies: inputs.deps,
Provider: inputs.provider,
PropertyDependencies: inputs.rpcPropertyDeps,
DeleteBeforeReplace: inputs.deleteBeforeReplace,
ImportId: inputs.importID,
CustomTimeouts: inputs.customTimeouts,
IgnoreChanges: inputs.ignoreChanges,
Aliases: inputs.aliases,
Type: t,
Name: name,
Parent: inputs.parent,
Object: inputs.rpcProps,
Custom: custom,
Protect: inputs.protect,
Dependencies: inputs.deps,
Provider: inputs.provider,
PropertyDependencies: inputs.rpcPropertyDeps,
DeleteBeforeReplace: inputs.deleteBeforeReplace,
ImportId: inputs.importID,
CustomTimeouts: inputs.customTimeouts,
IgnoreChanges: inputs.ignoreChanges,
Aliases: inputs.aliases,
AcceptSecrets: true,
AdditionalSecretOutputs: inputs.additionalSecretOutputs,
})
if err != nil {
logging.V(9).Infof("RegisterResource(%s, %s): error: %v", t, name, err)
@ -656,28 +665,30 @@ func (state *resourceState) resolve(dryrun bool, err error, inputs *resourceInpu
// Allocate storage for the unmarshalled output.
dest := reflect.New(output.ElementType()).Elem()
if err = unmarshalOutput(v, dest); err != nil {
secret, err := unmarshalOutput(v, dest)
if err != nil {
output.reject(err)
} else {
output.resolve(dest.Interface(), known)
output.resolve(dest.Interface(), known, secret)
}
}
}
// resourceInputs reflects all of the inputs necessary to perform core resource RPC operations.
type resourceInputs struct {
parent string
deps []string
protect bool
provider string
resolvedProps resource.PropertyMap
rpcProps *structpb.Struct
rpcPropertyDeps map[string]*pulumirpc.RegisterResourceRequest_PropertyDependencies
deleteBeforeReplace bool
importID string
customTimeouts *pulumirpc.RegisterResourceRequest_CustomTimeouts
ignoreChanges []string
aliases []string
parent string
deps []string
protect bool
provider string
resolvedProps resource.PropertyMap
rpcProps *structpb.Struct
rpcPropertyDeps map[string]*pulumirpc.RegisterResourceRequest_PropertyDependencies
deleteBeforeReplace bool
importID string
customTimeouts *pulumirpc.RegisterResourceRequest_CustomTimeouts
ignoreChanges []string
aliases []string
additionalSecretOutputs []string
}
// prepareResourceInputs prepares the inputs for a resource operation, shared between read and register.
@ -689,7 +700,7 @@ func (ctx *Context) prepareResourceInputs(props Input, t string,
// Get the parent and dependency URNs from the options, in addition to the protection bit. If there wasn't an
// explicit parent, and a root stack resource exists, we will automatically parent to that.
parent, optDeps, protect, provider, deleteBeforeReplace,
importID, ignoreChanges, err := ctx.getOpts(t, providers, opts)
importID, ignoreChanges, additionalSecretOutputs, err := ctx.getOpts(t, providers, opts)
if err != nil {
return nil, errors.Wrap(err, "resolving options")
}
@ -704,7 +715,7 @@ func (ctx *Context) prepareResourceInputs(props Input, t string,
keepUnknowns := ctx.DryRun()
rpcProps, err := plugin.MarshalProperties(
resolvedProps,
plugin.MarshalOptions{KeepUnknowns: keepUnknowns})
plugin.MarshalOptions{KeepUnknowns: keepUnknowns, KeepSecrets: true})
if err != nil {
return nil, errors.Wrap(err, "marshaling properties")
}
@ -741,7 +752,7 @@ func (ctx *Context) prepareResourceInputs(props Input, t string,
// Await alias URNs
aliases := make([]string, len(resource.aliases))
for i, alias := range resource.aliases {
urn, _, err := alias.awaitURN(context.Background())
urn, _, _, err := alias.awaitURN(context.Background())
if err != nil {
return nil, errors.Wrap(err, "error waiting for alias URN to resolve")
}
@ -749,18 +760,19 @@ func (ctx *Context) prepareResourceInputs(props Input, t string,
}
return &resourceInputs{
parent: string(parent),
deps: deps,
protect: protect,
provider: provider,
resolvedProps: resolvedProps,
rpcProps: rpcProps,
rpcPropertyDeps: rpcPropertyDeps,
deleteBeforeReplace: deleteBeforeReplace,
importID: string(importID),
customTimeouts: getTimeouts(opts.CustomTimeouts),
ignoreChanges: ignoreChanges,
aliases: aliases,
parent: string(parent),
deps: deps,
protect: protect,
provider: provider,
resolvedProps: resolvedProps,
rpcProps: rpcProps,
rpcPropertyDeps: rpcPropertyDeps,
deleteBeforeReplace: deleteBeforeReplace,
importID: string(importID),
customTimeouts: getTimeouts(opts.CustomTimeouts),
ignoreChanges: ignoreChanges,
aliases: aliases,
additionalSecretOutputs: additionalSecretOutputs,
}, nil
}
@ -777,22 +789,22 @@ func getTimeouts(custom *CustomTimeouts) *pulumirpc.RegisterResourceRequest_Cust
// getOpts returns a set of resource options from an array of them. This includes the parent URN, any dependency URNs,
// a boolean indicating whether the resource is to be protected, and the URN and ID of the resource's provider, if any.
func (ctx *Context) getOpts(t string, providers map[string]ProviderResource, opts *resourceOptions) (
URN, []URN, bool, string, bool, ID, []string, error) {
URN, []URN, bool, string, bool, ID, []string, []string, error) {
var importID ID
if opts.Import != nil {
id, _, err := opts.Import.ToIDOutput().awaitID(context.TODO())
id, _, _, err := opts.Import.ToIDOutput().awaitID(context.TODO())
if err != nil {
return "", nil, false, "", false, "", nil, err
return "", nil, false, "", false, "", nil, nil, err
}
importID = id
}
var parentURN URN
if opts.Parent != nil {
urn, _, err := opts.Parent.URN().awaitURN(context.TODO())
urn, _, _, err := opts.Parent.URN().awaitURN(context.TODO())
if err != nil {
return "", nil, false, "", false, "", nil, err
return "", nil, false, "", false, "", nil, nil, err
}
parentURN = urn
}
@ -801,9 +813,9 @@ func (ctx *Context) getOpts(t string, providers map[string]ProviderResource, opt
if opts.DependsOn != nil {
depURNs = make([]URN, len(opts.DependsOn))
for i, r := range opts.DependsOn {
urn, _, err := r.URN().awaitURN(context.TODO())
urn, _, _, err := r.URN().awaitURN(context.TODO())
if err != nil {
return "", nil, false, "", false, "", nil, err
return "", nil, false, "", false, "", nil, nil, err
}
depURNs[i] = urn
}
@ -819,20 +831,21 @@ func (ctx *Context) getOpts(t string, providers map[string]ProviderResource, opt
if provider != nil {
pr, err := ctx.resolveProviderReference(provider)
if err != nil {
return "", nil, false, "", false, "", nil, err
return "", nil, false, "", false, "", nil, nil, err
}
providerRef = pr
}
return parentURN, depURNs, opts.Protect, providerRef, opts.DeleteBeforeReplace, importID, opts.IgnoreChanges, nil
return parentURN, depURNs, opts.Protect, providerRef, opts.DeleteBeforeReplace,
importID, opts.IgnoreChanges, opts.AdditionalSecretOutputs, nil
}
func (ctx *Context) resolveProviderReference(provider ProviderResource) (string, error) {
urn, _, err := provider.URN().awaitURN(context.TODO())
urn, _, _, err := provider.URN().awaitURN(context.TODO())
if err != nil {
return "", err
}
id, known, err := provider.ID().awaitID(context.TODO())
id, known, _, err := provider.ID().awaitID(context.TODO())
if err != nil {
return "", err
}
@ -906,7 +919,7 @@ func (ctx *Context) RegisterResourceOutputs(resource Resource, outs Map) error {
ctx.endRPC(err)
}()
urn, _, err := resource.URN().awaitURN(context.TODO())
urn, _, _, err := resource.URN().awaitURN(context.TODO())
if err != nil {
return
}
@ -919,7 +932,7 @@ func (ctx *Context) RegisterResourceOutputs(resource Resource, outs Map) error {
keepUnknowns := ctx.DryRun()
outsMarshalled, err := plugin.MarshalProperties(
outsResolved.ObjectValue(),
plugin.MarshalOptions{KeepUnknowns: keepUnknowns})
plugin.MarshalOptions{KeepUnknowns: keepUnknowns, KeepSecrets: true})
if err != nil {
return
}

View file

@ -22,19 +22,22 @@ import (
"log"
"os"
"os/exec"
"path/filepath"
"strings"
"text/template"
"unicode"
)
type builtin struct {
Name string
Type string
inputType string
implements []string
Implements []*builtin
elementType string
Example string
Name string
Type string
inputType string
implements []string
Implements []*builtin
elementType string
Example string
GenerateConfig bool
DefaultConfig string
}
func (b builtin) DefineInputType() bool {
@ -116,23 +119,23 @@ var builtins = makeBuiltins([]*builtin{
{Name: "Archive", Type: "Archive", inputType: "*archive", implements: []string{"AssetOrArchive"}, Example: "NewFileArchive(\"foo.zip\")"},
{Name: "Asset", Type: "Asset", inputType: "*asset", implements: []string{"AssetOrArchive"}, Example: "NewFileAsset(\"foo.txt\")"},
{Name: "AssetOrArchive", Type: "AssetOrArchive", Example: "NewFileArchive(\"foo.zip\")"},
{Name: "Bool", Type: "bool", Example: "Bool(true)"},
{Name: "Float32", Type: "float32", Example: "Float32(1.3)"},
{Name: "Float64", Type: "float64", Example: "Float64(999.9)"},
{Name: "Bool", Type: "bool", Example: "Bool(true)", GenerateConfig: true, DefaultConfig: "false"},
{Name: "Float32", Type: "float32", Example: "Float32(1.3)", GenerateConfig: true, DefaultConfig: "0"},
{Name: "Float64", Type: "float64", Example: "Float64(999.9)", GenerateConfig: true, DefaultConfig: "0"},
{Name: "ID", Type: "ID", inputType: "ID", implements: []string{"String"}, Example: "ID(\"foo\")"},
{Name: "Input", Type: "interface{}", Example: "String(\"any\")"},
{Name: "Int", Type: "int", Example: "Int(42)"},
{Name: "Int16", Type: "int16", Example: "Int16(33)"},
{Name: "Int32", Type: "int32", Example: "Int32(24)"},
{Name: "Int64", Type: "int64", Example: "Int64(15)"},
{Name: "Int8", Type: "int8", Example: "Int8(6)"},
{Name: "Int", Type: "int", Example: "Int(42)", GenerateConfig: true, DefaultConfig: "0"},
{Name: "Int16", Type: "int16", Example: "Int16(33)", GenerateConfig: true, DefaultConfig: "0"},
{Name: "Int32", Type: "int32", Example: "Int32(24)", GenerateConfig: true, DefaultConfig: "0"},
{Name: "Int64", Type: "int64", Example: "Int64(15)", GenerateConfig: true, DefaultConfig: "0"},
{Name: "Int8", Type: "int8", Example: "Int8(6)", GenerateConfig: true, DefaultConfig: "0"},
{Name: "String", Type: "string", Example: "String(\"foo\")"},
{Name: "URN", Type: "URN", inputType: "URN", implements: []string{"String"}, Example: "URN(\"foo\")"},
{Name: "Uint", Type: "uint", Example: "Uint(42)"},
{Name: "Uint16", Type: "uint16", Example: "Uint16(33)"},
{Name: "Uint32", Type: "uint32", Example: "Uint32(24)"},
{Name: "Uint64", Type: "uint64", Example: "Uint64(15)"},
{Name: "Uint8", Type: "uint8", Example: "Uint8(6)"},
{Name: "Uint", Type: "uint", Example: "Uint(42)", GenerateConfig: true, DefaultConfig: "0"},
{Name: "Uint16", Type: "uint16", Example: "Uint16(33)", GenerateConfig: true, DefaultConfig: "0"},
{Name: "Uint32", Type: "uint32", Example: "Uint32(24)", GenerateConfig: true, DefaultConfig: "0"},
{Name: "Uint64", Type: "uint64", Example: "Uint64(15)", GenerateConfig: true, DefaultConfig: "0"},
{Name: "Uint8", Type: "uint8", Example: "Uint8(6)", GenerateConfig: true, DefaultConfig: "0"},
})
func unexported(s string) string {
@ -199,17 +202,32 @@ func main() {
"Builtins": builtins,
}
for _, t := range templates.Templates() {
filename := strings.TrimRight(t.Name(), ".template")
f, err := os.Create(filename)
// template with Name = "foo-bar.go.template" will be written to ./foo/bar.go
// "bar.go.template" ./bar.go
// "fizz-buzz-bar.go.template" ./fizz/buzz/bar.go
pwd, err := os.Getwd()
if err != nil {
log.Fatalf("failed to create %v: %v", filename, err)
log.Fatalf("code generation failed: %v", err.Error())
}
parts := strings.Split(t.Name(), "-")
filename := strings.TrimRight(parts[len(parts)-1], ".template")
parts[len(parts)-1] = filename
paths := append([]string{pwd}, parts...)
fullname := filepath.Join(paths...)
f, err := os.Create(fullname)
if err != nil {
log.Fatalf("failed to create %v: %v", fullname, err)
}
if err := t.Execute(f, data); err != nil {
log.Fatalf("failed to execute %v: %v", t.Name(), err)
}
f.Close()
gofmt := exec.Command("gofmt", "-s", "-w", filename)
gofmt := exec.Command("gofmt", "-s", "-w", fullname)
stderr, err := gofmt.StderrPipe()
if err != nil {
log.Fatalf("failed to pipe stderr from gofmt: %v", err)
@ -218,7 +236,7 @@ func main() {
io.Copy(os.Stderr, stderr)
}()
if err := gofmt.Run(); err != nil {
log.Fatalf("failed to gofmt %v: %v", filename, err)
log.Fatalf("failed to gofmt %v: %v", fullname, err)
}
}
}

View file

@ -15,14 +15,16 @@ func testPrintf(t *testing.T, ins ...interface{}) {
// Fprintf
buf := &bytes.Buffer{}
out := Output(Fprintf(buf, f, ins...))
_, known, err := await(out)
_, known, secret, err := await(out)
assert.True(t, known)
assert.False(t, secret)
assert.Nil(t, err)
assert.Equal(t, expected, buf.String())
// Sprintf
out = Sprintf(f, ins...)
v, known, err := await(out)
v, known, secret, err := await(out)
assert.False(t, secret)
assert.True(t, known)
assert.Nil(t, err)
assert.Equal(t, expected, v)

View file

@ -157,6 +157,8 @@ type resourceOptions struct {
IgnoreChanges []string
// Aliases is an optional list of identifiers used to find and use existing resources.
Aliases []Alias
// AdditionalSecretOutputs is an optional list of output properties to mark as secret.
AdditionalSecretOutputs []string
}
type invokeOptions struct {
@ -293,3 +295,10 @@ func Aliases(o []Alias) ResourceOption {
ro.Aliases = o
})
}
// AdditionalSecretOutputs specifies a list of output properties to mark as secret.
func AdditionalSecretOutputs(o []string) ResourceOption {
return resourceOption(func(ro *resourceOptions) {
ro.AdditionalSecretOutputs = o
})
}

View file

@ -103,7 +103,7 @@ func marshalInputs(props Input) (resource.PropertyMap, map[string][]URN, []URN,
var deps []URN
pdepset := map[URN]bool{}
for _, dep := range resourceDeps {
depURN, _, err := dep.URN().awaitURN(context.TODO())
depURN, _, _, err := dep.URN().awaitURN(context.TODO())
if err != nil {
return nil, nil, nil, err
}
@ -136,11 +136,28 @@ const cannotAwaitFmt = "cannot marshal Output value of type %T; please use Apply
// marshalInput marshals an input value, returning its raw serializable value along with any dependencies.
func marshalInput(v interface{}, destType reflect.Type, await bool) (resource.PropertyValue, []Resource, error) {
val, deps, secret, err := marshalInputAndDetermineSecret(v, destType, await)
if err != nil {
return val, deps, err
}
if secret {
return resource.MakeSecret(val), deps, nil
}
return val, deps, nil
}
// marshalInputAndDetermineSecret marshals an input value with information about secret status
func marshalInputAndDetermineSecret(v interface{},
destType reflect.Type,
await bool) (resource.PropertyValue, []Resource, bool, error) {
for {
valueType := reflect.TypeOf(v)
// If this is an Input, make sure it is of the proper type and await it if it is an output/
var deps []Resource
secret := false
if input, ok := v.(Input); ok {
valueType = input.ElementType()
@ -152,25 +169,26 @@ func marshalInput(v interface{}, destType reflect.Type, await bool) (resource.Pr
input, valueType = newOutput, destType
} else if !valueType.AssignableTo(destType) {
err := errors.Errorf("cannot marshal an input of type %T as a value of type %v", input, destType)
return resource.PropertyValue{}, nil, err
return resource.PropertyValue{}, nil, false, err
}
}
// If the input is an Output, await its value. The returned value is fully resolved.
if output, ok := input.(Output); ok {
if !await {
return resource.PropertyValue{}, nil, errors.Errorf(cannotAwaitFmt, output)
return resource.PropertyValue{}, nil, false, errors.Errorf(cannotAwaitFmt, output)
}
// Await the output.
ov, known, err := output.await(context.TODO())
ov, known, outputSecret, err := output.await(context.TODO())
if err != nil {
return resource.PropertyValue{}, nil, err
return resource.PropertyValue{}, nil, false, err
}
secret = outputSecret
// If the value is unknown, return the appropriate sentinel.
if !known {
return resource.MakeComputed(resource.NewStringProperty("")), output.dependencies(), nil
return resource.MakeComputed(resource.NewStringProperty("")), output.dependencies(), secret, nil
}
v, deps = ov, output.dependencies()
@ -179,7 +197,7 @@ func marshalInput(v interface{}, destType reflect.Type, await bool) (resource.Pr
// If v is nil, just return that.
if v == nil {
return resource.PropertyValue{}, nil, nil
return resource.PropertyValue{}, nil, secret, nil
}
// Look for some well known types.
@ -189,7 +207,7 @@ func marshalInput(v interface{}, destType reflect.Type, await bool) (resource.Pr
Path: v.Path(),
Text: v.Text(),
URI: v.URI(),
}), deps, nil
}), deps, secret, nil
case *archive:
var assets map[string]interface{}
if as := v.Assets(); as != nil {
@ -197,7 +215,7 @@ func marshalInput(v interface{}, destType reflect.Type, await bool) (resource.Pr
for k, a := range as {
aa, _, err := marshalInput(a, anyType, await)
if err != nil {
return resource.PropertyValue{}, nil, err
return resource.PropertyValue{}, nil, false, err
}
assets[k] = aa.V
}
@ -206,16 +224,16 @@ func marshalInput(v interface{}, destType reflect.Type, await bool) (resource.Pr
Assets: assets,
Path: v.Path(),
URI: v.URI(),
}), deps, nil
}), deps, secret, nil
case CustomResource:
deps = append(deps, v)
// Resources aren't serializable; instead, serialize a reference to ID, tracking as a dependency.
e, d, err := marshalInput(v.ID(), idType, await)
if err != nil {
return resource.PropertyValue{}, nil, err
return resource.PropertyValue{}, nil, false, err
}
return e, append(deps, d...), nil
return e, append(deps, d...), secret, nil
}
contract.Assertf(valueType.AssignableTo(destType) || valueType.ConvertibleTo(destType),
@ -232,25 +250,25 @@ func marshalInput(v interface{}, destType reflect.Type, await bool) (resource.Pr
rv := reflect.ValueOf(v)
switch rv.Type().Kind() {
case reflect.Bool:
return resource.NewBoolProperty(rv.Bool()), deps, nil
return resource.NewBoolProperty(rv.Bool()), deps, secret, nil
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
return resource.NewNumberProperty(float64(rv.Int())), deps, nil
return resource.NewNumberProperty(float64(rv.Int())), deps, secret, nil
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
return resource.NewNumberProperty(float64(rv.Uint())), deps, nil
return resource.NewNumberProperty(float64(rv.Uint())), deps, secret, nil
case reflect.Float32, reflect.Float64:
return resource.NewNumberProperty(rv.Float()), deps, nil
return resource.NewNumberProperty(rv.Float()), deps, secret, nil
case reflect.Ptr, reflect.Interface:
// Dereference non-nil pointers and interfaces.
if rv.IsNil() {
return resource.PropertyValue{}, deps, nil
return resource.PropertyValue{}, deps, secret, nil
}
v, destType = rv.Elem().Interface(), destType.Elem()
continue
case reflect.String:
return resource.NewStringProperty(rv.String()), deps, nil
return resource.NewStringProperty(rv.String()), deps, secret, nil
case reflect.Array, reflect.Slice:
if rv.IsNil() {
return resource.PropertyValue{}, deps, nil
return resource.PropertyValue{}, deps, secret, nil
}
destElem := destType.Elem()
@ -261,22 +279,22 @@ func marshalInput(v interface{}, destType reflect.Type, await bool) (resource.Pr
elem := rv.Index(i)
e, d, err := marshalInput(elem.Interface(), destElem, await)
if err != nil {
return resource.PropertyValue{}, nil, err
return resource.PropertyValue{}, nil, false, err
}
if !e.IsNull() {
arr = append(arr, e)
}
deps = append(deps, d...)
}
return resource.NewArrayProperty(arr), deps, nil
return resource.NewArrayProperty(arr), deps, secret, nil
case reflect.Map:
if rv.Type().Key().Kind() != reflect.String {
return resource.PropertyValue{}, nil,
return resource.PropertyValue{}, nil, false,
errors.Errorf("expected map keys to be strings; got %v", rv.Type().Key())
}
if rv.IsNil() {
return resource.PropertyValue{}, deps, nil
return resource.PropertyValue{}, deps, secret, nil
}
destElem := destType.Elem()
@ -287,14 +305,14 @@ func marshalInput(v interface{}, destType reflect.Type, await bool) (resource.Pr
value := rv.MapIndex(key)
mv, d, err := marshalInput(value.Interface(), destElem, await)
if err != nil {
return resource.PropertyValue{}, nil, err
return resource.PropertyValue{}, nil, false, err
}
if !mv.IsNull() {
obj[resource.PropertyKey(key.String())] = mv
}
deps = append(deps, d...)
}
return resource.NewObjectProperty(obj), deps, nil
return resource.NewObjectProperty(obj), deps, secret, nil
case reflect.Struct:
obj := resource.PropertyMap{}
typ := rv.Type()
@ -308,7 +326,7 @@ func marshalInput(v interface{}, destType reflect.Type, await bool) (resource.Pr
fv, d, err := marshalInput(rv.Field(i).Interface(), destField.Type, await)
if err != nil {
return resource.PropertyValue{}, nil, err
return resource.PropertyValue{}, nil, false, err
}
if !fv.IsNull() {
@ -316,82 +334,93 @@ func marshalInput(v interface{}, destType reflect.Type, await bool) (resource.Pr
}
deps = append(deps, d...)
}
return resource.NewObjectProperty(obj), deps, nil
return resource.NewObjectProperty(obj), deps, secret, nil
}
return resource.PropertyValue{}, nil, errors.Errorf("unrecognized input property type: %v (%T)", v, v)
return resource.PropertyValue{}, nil, false, errors.Errorf("unrecognized input property type: %v (%T)", v, v)
}
}
func unmarshalPropertyValue(v resource.PropertyValue) (interface{}, error) {
func unmarshalPropertyValue(v resource.PropertyValue) (interface{}, bool, error) {
switch {
case v.IsComputed() || v.IsOutput():
return nil, nil
return nil, false, nil
case v.IsSecret():
return nil, errors.New("this version of the Pulumi SDK does not support first-class secrets")
sv, _, err := unmarshalPropertyValue(v.SecretValue().Element)
if err != nil {
return nil, false, err
}
return sv, true, nil
case v.IsArray():
arr := v.ArrayValue()
rv := make([]interface{}, len(arr))
secret := false
for i, e := range arr {
ev, err := unmarshalPropertyValue(e)
ev, esecret, err := unmarshalPropertyValue(e)
secret = secret || esecret
if err != nil {
return nil, err
return nil, false, err
}
rv[i] = ev
}
return rv, nil
return rv, secret, nil
case v.IsObject():
m := make(map[string]interface{})
secret := false
for k, e := range v.ObjectValue() {
ev, err := unmarshalPropertyValue(e)
ev, esecret, err := unmarshalPropertyValue(e)
secret = secret || esecret
if err != nil {
return nil, err
return nil, false, err
}
m[string(k)] = ev
}
return m, nil
return m, secret, nil
case v.IsAsset():
asset := v.AssetValue()
switch {
case asset.IsPath():
return NewFileAsset(asset.Path), nil
return NewFileAsset(asset.Path), false, nil
case asset.IsText():
return NewStringAsset(asset.Text), nil
return NewStringAsset(asset.Text), false, nil
case asset.IsURI():
return NewRemoteAsset(asset.URI), nil
return NewRemoteAsset(asset.URI), false, nil
}
return nil, errors.New("expected asset to be one of File, String, or Remote; got none")
return nil, false, errors.New("expected asset to be one of File, String, or Remote; got none")
case v.IsArchive():
archive := v.ArchiveValue()
secret := false
switch {
case archive.IsAssets():
as := make(map[string]interface{})
for k, v := range archive.Assets {
a, err := unmarshalPropertyValue(resource.NewPropertyValue(v))
a, asecret, err := unmarshalPropertyValue(resource.NewPropertyValue(v))
secret = secret || asecret
if err != nil {
return nil, err
return nil, false, err
}
as[k] = a
}
return NewAssetArchive(as), nil
return NewAssetArchive(as), secret, nil
case archive.IsPath():
return NewFileArchive(archive.Path), nil
return NewFileArchive(archive.Path), secret, nil
case archive.IsURI():
return NewRemoteArchive(archive.URI), nil
return NewRemoteArchive(archive.URI), secret, nil
default:
}
return nil, errors.New("expected asset to be one of File, String, or Remote; got none")
return nil, false, errors.New("expected asset to be one of File, String, or Remote; got none")
default:
return v.V, nil
return v.V, false, nil
}
}
// unmarshalOutput unmarshals a single output variable into its runtime representation.
func unmarshalOutput(v resource.PropertyValue, dest reflect.Value) error {
// returning a bool that indicates secretness
func unmarshalOutput(v resource.PropertyValue, dest reflect.Value) (bool, error) {
contract.Assert(dest.CanSet())
// Check for nils and unknowns. The destination will be left with the zero value.
if v.IsNull() || v.IsComputed() || v.IsOutput() {
return nil
return false, nil
}
// Allocate storage as necessary.
@ -405,91 +434,100 @@ func unmarshalOutput(v resource.PropertyValue, dest reflect.Value) error {
switch {
case v.IsAsset():
if !assetType.AssignableTo(dest.Type()) {
return errors.Errorf("expected a %s, got an asset", dest.Type())
return false, errors.Errorf("expected a %s, got an asset", dest.Type())
}
asset, err := unmarshalPropertyValue(v)
asset, secret, err := unmarshalPropertyValue(v)
if err != nil {
return err
return false, err
}
dest.Set(reflect.ValueOf(asset))
return nil
return secret, nil
case v.IsArchive():
if !archiveType.AssignableTo(dest.Type()) {
return errors.Errorf("expected a %s, got an archive", dest.Type())
return false, errors.Errorf("expected a %s, got an archive", dest.Type())
}
archive, err := unmarshalPropertyValue(v)
archive, secret, err := unmarshalPropertyValue(v)
if err != nil {
return err
return false, err
}
dest.Set(reflect.ValueOf(archive))
return nil
return secret, nil
case v.IsSecret():
return errors.New("this version of the Pulumi SDK does not support first-class secrets")
if _, err := unmarshalOutput(v.SecretValue().Element, dest); err != nil {
return false, err
}
return true, nil
}
// Unmarshal based on the desired type.
switch dest.Kind() {
case reflect.Bool:
if !v.IsBool() {
return errors.Errorf("expected a %v, got a %s", dest.Type(), v.TypeString())
return false, errors.Errorf("expected a %v, got a %s", dest.Type(), v.TypeString())
}
dest.SetBool(v.BoolValue())
return nil
return false, nil
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
if !v.IsNumber() {
return errors.Errorf("expected an %v, got a %s", dest.Type(), v.TypeString())
return false, errors.Errorf("expected an %v, got a %s", dest.Type(), v.TypeString())
}
dest.SetInt(int64(v.NumberValue()))
return nil
return false, nil
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
if !v.IsNumber() {
return errors.Errorf("expected an %v, got a %s", dest.Type(), v.TypeString())
return false, errors.Errorf("expected an %v, got a %s", dest.Type(), v.TypeString())
}
dest.SetUint(uint64(v.NumberValue()))
return nil
return false, nil
case reflect.Float32, reflect.Float64:
if !v.IsNumber() {
return errors.Errorf("expected an %v, got a %s", dest.Type(), v.TypeString())
return false, errors.Errorf("expected an %v, got a %s", dest.Type(), v.TypeString())
}
dest.SetFloat(v.NumberValue())
return nil
return false, nil
case reflect.String:
if !v.IsString() {
return errors.Errorf("expected a %v, got a %s", dest.Type(), v.TypeString())
return false, errors.Errorf("expected a %v, got a %s", dest.Type(), v.TypeString())
}
dest.SetString(v.StringValue())
return nil
return false, nil
case reflect.Slice:
if !v.IsArray() {
return errors.Errorf("expected a %v, got a %s", dest.Type(), v.TypeString())
return false, errors.Errorf("expected a %v, got a %s", dest.Type(), v.TypeString())
}
arr := v.ArrayValue()
slice := reflect.MakeSlice(dest.Type(), len(arr), len(arr))
secret := false
for i, e := range arr {
if err := unmarshalOutput(e, slice.Index(i)); err != nil {
return err
isecret, err := unmarshalOutput(e, slice.Index(i))
if err != nil {
return false, err
}
secret = secret || isecret
}
dest.Set(slice)
return nil
return secret, nil
case reflect.Map:
if !v.IsObject() {
return errors.Errorf("expected a %v, got a %s", dest.Type(), v.TypeString())
return false, errors.Errorf("expected a %v, got a %s", dest.Type(), v.TypeString())
}
keyType, elemType := dest.Type().Key(), dest.Type().Elem()
if keyType.Kind() != reflect.String {
return errors.Errorf("map keys must be assignable from type string")
return false, errors.Errorf("map keys must be assignable from type string")
}
result := reflect.MakeMap(dest.Type())
secret := false
for k, e := range v.ObjectValue() {
elem := reflect.New(elemType).Elem()
if err := unmarshalOutput(e, elem); err != nil {
return err
esecret, err := unmarshalOutput(e, elem)
if err != nil {
return false, err
}
secret = secret || esecret
key := reflect.New(keyType).Elem()
key.SetString(string(k))
@ -497,26 +535,27 @@ func unmarshalOutput(v resource.PropertyValue, dest reflect.Value) error {
result.SetMapIndex(key, elem)
}
dest.Set(result)
return nil
return secret, nil
case reflect.Interface:
if !anyType.Implements(dest.Type()) {
return errors.Errorf("cannot unmarshal into non-empty interface type %v", dest.Type())
return false, errors.Errorf("cannot unmarshal into non-empty interface type %v", dest.Type())
}
// If we're unmarshaling into the empty interface type, use the property type as the type of the result.
result, err := unmarshalPropertyValue(v)
result, secret, err := unmarshalPropertyValue(v)
if err != nil {
return err
return false, err
}
dest.Set(reflect.ValueOf(result))
return nil
return secret, nil
case reflect.Struct:
if !v.IsObject() {
return errors.Errorf("expected a %v, got a %s", dest.Type(), v.TypeString())
return false, errors.Errorf("expected a %v, got a %s", dest.Type(), v.TypeString())
}
obj := v.ObjectValue()
typ := dest.Type()
secret := false
for i := 0; i < typ.NumField(); i++ {
fieldV := dest.Field(i)
if !fieldV.CanSet() {
@ -533,12 +572,14 @@ func unmarshalOutput(v resource.PropertyValue, dest reflect.Value) error {
continue
}
if err := unmarshalOutput(e, fieldV); err != nil {
return err
osecret, err := unmarshalOutput(e, fieldV)
secret = secret || osecret
if err != nil {
return false, err
}
}
return nil
return secret, nil
default:
return errors.Errorf("cannot unmarshal into type %v", dest.Type())
return false, errors.Errorf("cannot unmarshal into type %v", dest.Type())
}
}

View file

@ -70,7 +70,7 @@ func TestMarshalRoundtrip(t *testing.T) {
out, resolve, _ := NewOutput()
resolve("outputty")
out2 := newOutputState(reflect.TypeOf(""))
out2.fulfill(nil, false, nil)
out2.fulfill(nil, false, false, nil)
inputs := testInputs{
S: String("a string"),
A: Bool(true),
@ -100,32 +100,35 @@ func TestMarshalRoundtrip(t *testing.T) {
resolved, pdeps, deps, err := marshalInputs(inputs)
assert.Nil(t, err)
if !assert.Nil(t, err) {
assert.Equal(t, reflect.TypeOf(inputs).NumField(), len(pdeps))
if assert.Nil(t, err) {
assert.Equal(t, reflect.TypeOf(inputs).NumField(), len(resolved))
assert.Equal(t, 0, len(deps))
assert.Equal(t, 0, len(pdeps))
// Now just unmarshal and ensure the resulting map matches.
resV, err := unmarshalPropertyValue(resource.NewObjectProperty(resolved))
if !assert.Nil(t, err) {
if !assert.NotNil(t, resV) {
resV, secret, err := unmarshalPropertyValue(resource.NewObjectProperty(resolved))
assert.False(t, secret)
if assert.Nil(t, err) {
if assert.NotNil(t, resV) {
res := resV.(map[string]interface{})
assert.Equal(t, "a string", res["s"])
assert.Equal(t, true, res["a"])
assert.Equal(t, 42, res["b"])
assert.Equal(t, 42.0, res["b"])
assert.Equal(t, "put a lime in the coconut", res["cStringAsset"].(Asset).Text())
assert.Equal(t, "foo.txt", res["cFileAsset"].(Asset).Path())
assert.Equal(t, "https://pulumi.com/fake/txt", res["cRemoteAsset"].(Asset).URI())
ar := res["dAssetArchive"].(Archive).Assets()
assert.Equal(t, 2, len(ar))
assert.Equal(t, "bar.txt", ar["subAsset"].(Asset).Path())
assert.Equal(t, "bar.zip", ar["subrchive"].(Archive).Path())
assert.Equal(t, "bar.zip", ar["subArchive"].(Archive).Path())
assert.Equal(t, "foo.zip", res["dFileArchive"].(Archive).Path())
assert.Equal(t, "https://pulumi.com/fake/archive.zip", res["dRemoteArchive"].(Archive).URI())
assert.Equal(t, "outputty", res["e"])
aa := res["fArray"].([]interface{})
assert.Equal(t, 4, len(aa))
assert.Equal(t, 0, aa[0])
assert.Equal(t, 1.3, aa[1])
assert.Equal(t, 0.0, aa[0])
assert.Less(t, 1.3-aa[1].(float64), 0.00001)
assert.Greater(t, 1.3-aa[1].(float64), 0.0)
assert.Equal(t, "x", aa[2])
assert.Equal(t, false, aa[3])
am := res["fMap"].(map[string]interface{})
@ -133,9 +136,9 @@ func TestMarshalRoundtrip(t *testing.T) {
assert.Equal(t, "y", am["x"])
assert.Equal(t, 999.9, am["y"])
assert.Equal(t, false, am["z"])
assert.Equal(t, rpcTokenUnknownValue, res["g"])
assert.Equal(t, nil, res["g"])
assert.Equal(t, "foo", res["h"])
assert.Equal(t, rpcTokenUnknownValue, res["i"])
assert.Equal(t, nil, res["i"])
}
}
}
@ -347,8 +350,9 @@ func TestResourceState(t *testing.T) {
}, pdeps)
assert.Equal(t, []URN{"foo"}, deps)
res, err := unmarshalPropertyValue(resource.NewObjectProperty(resolved))
res, secret, err := unmarshalPropertyValue(resource.NewObjectProperty(resolved))
assert.Nil(t, err)
assert.False(t, secret)
assert.Equal(t, map[string]interface{}{
"urn": "foo",
"id": "bar",
@ -378,13 +382,97 @@ func TestResourceState(t *testing.T) {
}, res)
}
func TestUnmarshalUnsupportedSecret(t *testing.T) {
func TestUnmarshalSecret(t *testing.T) {
secret := resource.MakeSecret(resource.NewPropertyValue("foo"))
_, err := unmarshalPropertyValue(secret)
assert.Error(t, err)
_, isSecret, err := unmarshalPropertyValue(secret)
assert.Nil(t, err)
assert.True(t, isSecret)
var sv string
err = unmarshalOutput(secret, reflect.ValueOf(&sv).Elem())
assert.Error(t, err)
isSecret, err = unmarshalOutput(secret, reflect.ValueOf(&sv).Elem())
assert.Nil(t, err)
assert.Equal(t, "foo", sv)
assert.True(t, isSecret)
}
// TestMarshalRoundtripNestedSecret ensures that marshaling a complex structure to and from
// its on-the-wire gRPC format succeeds including a nested secret property.
func TestMarshalRoundtripNestedSecret(t *testing.T) {
// Create interesting inputs.
out, resolve, _ := NewOutput()
resolve("outputty")
out2 := newOutputState(reflect.TypeOf(""))
out2.fulfill(nil, false, true, nil)
inputs := testInputs{
S: String("a string"),
A: Bool(true),
B: Int(42),
StringAsset: NewStringAsset("put a lime in the coconut"),
FileAsset: NewFileAsset("foo.txt"),
RemoteAsset: NewRemoteAsset("https://pulumi.com/fake/txt"),
AssetArchive: NewAssetArchive(map[string]interface{}{
"subAsset": NewFileAsset("bar.txt"),
"subArchive": NewFileArchive("bar.zip"),
}),
FileArchive: NewFileArchive("foo.zip"),
RemoteArchive: NewRemoteArchive("https://pulumi.com/fake/archive.zip"),
E: out,
Array: Array{Int(0), Float32(1.3), String("x"), Bool(false)},
Map: Map{
"x": String("y"),
"y": Float64(999.9),
"z": Bool(false),
},
G: StringOutput{out2},
H: URN("foo"),
I: StringOutput{},
}
// Marshal those inputs.
resolved, pdeps, deps, err := marshalInputs(inputs)
assert.Nil(t, err)
if assert.Nil(t, err) {
assert.Equal(t, reflect.TypeOf(inputs).NumField(), len(resolved))
assert.Equal(t, 0, len(deps))
assert.Equal(t, 0, len(pdeps))
// Now just unmarshal and ensure the resulting map matches.
resV, secret, err := unmarshalPropertyValue(resource.NewObjectProperty(resolved))
assert.True(t, secret)
if assert.Nil(t, err) {
if assert.NotNil(t, resV) {
res := resV.(map[string]interface{})
assert.Equal(t, "a string", res["s"])
assert.Equal(t, true, res["a"])
assert.Equal(t, 42.0, res["b"])
assert.Equal(t, "put a lime in the coconut", res["cStringAsset"].(Asset).Text())
assert.Equal(t, "foo.txt", res["cFileAsset"].(Asset).Path())
assert.Equal(t, "https://pulumi.com/fake/txt", res["cRemoteAsset"].(Asset).URI())
ar := res["dAssetArchive"].(Archive).Assets()
assert.Equal(t, 2, len(ar))
assert.Equal(t, "bar.txt", ar["subAsset"].(Asset).Path())
assert.Equal(t, "bar.zip", ar["subArchive"].(Archive).Path())
assert.Equal(t, "foo.zip", res["dFileArchive"].(Archive).Path())
assert.Equal(t, "https://pulumi.com/fake/archive.zip", res["dRemoteArchive"].(Archive).URI())
assert.Equal(t, "outputty", res["e"])
aa := res["fArray"].([]interface{})
assert.Equal(t, 4, len(aa))
assert.Equal(t, 0.0, aa[0])
assert.Less(t, 1.3-aa[1].(float64), 0.00001)
assert.Greater(t, 1.3-aa[1].(float64), 0.0)
assert.Equal(t, "x", aa[2])
assert.Equal(t, false, aa[3])
am := res["fMap"].(map[string]interface{})
assert.Equal(t, 3, len(am))
assert.Equal(t, "y", am["x"])
assert.Equal(t, 999.9, am["y"])
assert.Equal(t, false, am["z"])
assert.Equal(t, nil, res["g"])
assert.Equal(t, "foo", res["h"])
assert.Equal(t, nil, res["i"])
}
}
}
}

View file

@ -94,19 +94,22 @@ func TestRegisterResource(t *testing.T) {
}, &res)
assert.NoError(t, err)
id, known, err := await(res.ID())
id, known, secret, err := await(res.ID())
assert.NoError(t, err)
assert.True(t, known)
assert.False(t, secret)
assert.Equal(t, ID("someID"), id)
urn, known, err := await(res.URN())
urn, known, secret, err := await(res.URN())
assert.NoError(t, err)
assert.True(t, known)
assert.False(t, secret)
assert.NotEqual(t, "", urn)
foo, known, err := await(res.Foo)
foo, known, secret, err := await(res.Foo)
assert.NoError(t, err)
assert.True(t, known)
assert.False(t, secret)
assert.Equal(t, "qux", foo)
return nil
@ -138,19 +141,22 @@ func TestReadResource(t *testing.T) {
}, &res)
assert.NoError(t, err)
id, known, err := await(res.ID())
id, known, secret, err := await(res.ID())
assert.NoError(t, err)
assert.True(t, known)
assert.False(t, secret)
assert.Equal(t, ID("someID"), id)
urn, known, err := await(res.URN())
urn, known, secret, err := await(res.URN())
assert.NoError(t, err)
assert.True(t, known)
assert.False(t, secret)
assert.NotEqual(t, "", urn)
foo, known, err := await(res.Foo)
foo, known, secret, err := await(res.Foo)
assert.NoError(t, err)
assert.True(t, known)
assert.False(t, secret)
assert.Equal(t, "qux", foo)
return nil

View file

@ -36,21 +36,21 @@ func TestStackReference(t *testing.T) {
resName = "stack"
ref0, err := NewStackReference(ctx, resName, nil)
assert.NoError(t, err)
_, _, err = await(ref0.ID())
_, _, _, err = await(ref0.ID())
assert.NoError(t, err)
resName = "stack2"
ref1, err := NewStackReference(ctx, resName, &StackReferenceArgs{Name: String("stack")})
assert.NoError(t, err)
outs0, _, err := await(ref0.Outputs)
outs0, _, _, err := await(ref0.Outputs)
assert.NoError(t, err)
assert.Equal(t, outputs, outs0)
zed0, _, err := await(ref0.GetOutput(String("zed")))
zed0, _, _, err := await(ref0.GetOutput(String("zed")))
assert.NoError(t, err)
assert.Equal(t, outputs["zed"], zed0)
outs1, _, err := await(ref1.Outputs)
outs1, _, _, err := await(ref1.Outputs)
assert.NoError(t, err)
assert.Equal(t, outputs, outs1)
zed1, _, err := await(ref1.GetOutput(String("zed")))
zed1, _, _, err := await(ref1.GetOutput(String("zed")))
assert.NoError(t, err)
assert.Equal(t, outputs["zed"], zed1)
return nil

View file

@ -0,0 +1,166 @@
// Copyright 2016-2018, Pulumi Corporation.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package config
import (
"github.com/pulumi/pulumi/sdk/go/pulumi"
)
// Config is a struct that permits access to config as a "bag" with a package name. This avoids needing to access
// config with the fully qualified name all of the time (e.g., a bag whose namespace is "p" automatically translates
// attempted reads of keys "k" into "p:k"). This is optional but can save on some boilerplate when accessing config.
type Config struct {
ctx *pulumi.Context
namespace string
}
// New creates a new config bag with the given context and namespace.
func New(ctx *pulumi.Context, namespace string) *Config {
if namespace == "" {
namespace = ctx.Project()
}
return &Config{ctx: ctx, namespace: namespace}
}
// fullKey turns a simple configuration key into a fully resolved one, by prepending the bag's name.
func (c *Config) fullKey(key string) string {
return c.namespace + ":" + key
}
// Get loads an optional configuration value by its key, or returns "" if it doesn't exist.
func (c *Config) Get(key string) string {
return Get(c.ctx, c.fullKey(key))
}
// GetObject loads an optional configuration value into the specified output by its key,
// or returns an error if unable to do so.
func (c *Config) GetObject(key string, output interface{}) error {
return GetObject(c.ctx, c.fullKey(key), output)
}
{{range .Builtins}}
{{if .GenerateConfig}}
// Get{{.Name}} loads an optional {{.Type}} configuration value by its key, or returns {{.DefaultConfig}} if it doesn't exist.
func (c *Config) Get{{.Name}}(key string) {{.Type}} {
return Get{{.Name}}(c.ctx, c.fullKey(key))
}
{{end}}
{{end}}
// Require loads a configuration value by its key, or panics if it doesn't exist.
func (c *Config) Require(key string) string {
return Require(c.ctx, c.fullKey(key))
}
// RequireObject loads a required configuration value into the specified output by its key,
// or panics if unable to do so.
func (c *Config) RequireObject(key string, output interface{}) {
RequireObject(c.ctx, c.fullKey(key), output)
}
{{range .Builtins}}
{{if .GenerateConfig}}
// Require{{.Name}} loads a {{.Type}} configuration value by its key, or panics if it doesn't exist.
func (c *Config) Require{{.Name}}(key string) {{.Type}} {
return Require{{.Name}}(c.ctx, c.fullKey(key))
}
{{end}}
{{end}}
// Try loads a configuration value by its key, returning a non-nil error if it doesn't exist.
func (c *Config) Try(key string) (string, error) {
return Try(c.ctx, c.fullKey(key))
}
// TryObject loads an optional configuration value into the specified output by its key,
// or returns an error if unable to do so.
func (c *Config) TryObject(key string, output interface{}) error {
return TryObject(c.ctx, c.fullKey(key), output)
}
{{range .Builtins}}
{{if .GenerateConfig}}
// Try{{.Name}} loads an optional {{.Type}} configuration value by its key, or returns an error if it doesn't exist.
func (c *Config) Try{{.Name}}(key string) ({{.Type}}, error) {
return Try{{.Name}}(c.ctx, c.fullKey(key))
}
{{end}}
{{end}}
// GetSecret loads an optional configuration value by its key
// or "" if it doesn't exist, and returns it wrapped in a secret Output.
func (c *Config) GetSecret(key string) pulumi.Output {
return GetSecret(c.ctx, c.fullKey(key))
}
// GetSecretObject loads an optional configuration value into the specified output by its key,
// returning it wrapped in a secret Output or an error if unable to do so.
func (c *Config) GetSecretObject(key string, output interface{}) (pulumi.Output, error) {
return GetSecretObject(c.ctx, c.fullKey(key), output)
}
{{range .Builtins}}
{{if .GenerateConfig}}
// GetSecret{{.Name}} loads an optional {{.Type}} configuration value by its key
// or {{.DefaultConfig}} if it doesn't exist, and returns it wrapped in a secret Output.
func (c *Config) GetSecret{{.Name}}(key string) pulumi.Output {
return GetSecret{{.Name}}(c.ctx, c.fullKey(key))
}
{{end}}
{{end}}
// RequireSecret loads a configuration value by its key
// and returns it wrapped in a secret output, or panics if it doesn't exist.
func (c *Config) RequireSecret(key string) pulumi.Output {
return RequireSecret(c.ctx, c.fullKey(key))
}
// RequireSecretObject loads a required configuration value into the specified output by its key
// and returns it wrapped in a secret Output, or panics if unable to do so.
func (c *Config) RequireSecretObject(key string, output interface{}) pulumi.Output {
return RequireSecretObject(c.ctx, c.fullKey(key), output)
}
{{range .Builtins}}
{{if .GenerateConfig}}
// RequireSecret{{.Name}} loads a {{.Type}} configuration value by its key
// and returns is wrapped in a secret Output, or panics if it doesn't exist.
func (c *Config) RequireSecret{{.Name}}(key string) pulumi.Output {
return RequireSecret{{.Name}}(c.ctx, c.fullKey(key))
}
{{end}}
{{end}}
// TrySecret loads a configuration value by its key, returning a non-nil error if it doesn't exist.
func (c *Config) TrySecret(key string) (pulumi.Output, error) {
return TrySecret(c.ctx, c.fullKey(key))
}
// TrySecretObject loads a configuration value by its key, returning a non-nil error if it doesn't exist.
func (c *Config) TrySecretObject(key string, output interface{}) (pulumi.Output, error) {
return TrySecretObject(c.ctx, c.fullKey(key), output)
}
{{range .Builtins}}
{{if .GenerateConfig}}
// TrySecret{{.Name}} loads an optional {{.Type}} configuration value by its key into a secret Output,
// or returns an error if it doesn't exist.
func (c *Config) TrySecret{{.Name}}(key string) (pulumi.Output, error) {
return TrySecret{{.Name}}(c.ctx, c.fullKey(key))
}
{{end}}
{{end}}

View file

@ -0,0 +1,75 @@
// Copyright 2016-2018, Pulumi Corporation.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package config
import (
"encoding/json"
"github.com/spf13/cast"
"github.com/pulumi/pulumi/sdk/go/pulumi"
)
// Get loads an optional configuration value by its key, or returns "" if it doesn't exist.
func Get(ctx *pulumi.Context, key string) string {
v, _ := ctx.GetConfig(key)
return v
}
// GetObject attempts to load an optional configuration value by its key into the specified output variable.
func GetObject(ctx *pulumi.Context, key string, output interface{}) error {
if v, ok := ctx.GetConfig(key); ok {
return json.Unmarshal([]byte(v), output)
}
return nil
}
{{range .Builtins}}
{{if .GenerateConfig}}
// Get{{.Name}} loads an optional configuration value by its key, as a {{.Type}}, or returns {{.DefaultConfig}} if it doesn't exist.
func Get{{.Name}}(ctx *pulumi.Context, key string) {{.Type}} {
if v, ok := ctx.GetConfig(key); ok {
return cast.To{{.Name}}(v)
}
return {{.DefaultConfig}}
}
{{end}}
{{end}}
// GetSecret loads an optional configuration value by its key, or "" if it does not exist, into a secret Output.
func GetSecret(ctx *pulumi.Context, key string) pulumi.Output {
v, _ := ctx.GetConfig(key)
return pulumi.ToSecret(pulumi.String(v))
}
// GetSecretObject attempts to load an optional configuration value by its key into the specified output variable.
func GetSecretObject(ctx *pulumi.Context, key string, output interface{}) (pulumi.Output, error) {
if err := GetObject(ctx, key, output); err != nil {
return nil, err
}
return pulumi.ToSecret(output), nil
}
{{range .Builtins}}
{{if .GenerateConfig}}
// GetSecret{{.Name}} loads an optional {{.Type}} configuration value by its key,
// or false if it does not exist, into a secret Output.
func GetSecret{{.Name}}(ctx *pulumi.Context, key string) pulumi.Output {
return pulumi.ToSecret(Get{{.Name}}(ctx, key))
}
{{end}}
{{end}}

View file

@ -0,0 +1,75 @@
// Copyright 2016-2018, Pulumi Corporation.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package config
import (
"encoding/json"
"github.com/spf13/cast"
"github.com/pulumi/pulumi/pkg/util/contract"
"github.com/pulumi/pulumi/sdk/go/pulumi"
)
// Require loads a configuration value by its key, or panics if it doesn't exist.
func Require(ctx *pulumi.Context, key string) string {
v, ok := ctx.GetConfig(key)
if !ok {
contract.Failf("missing required configuration variable '%s'; run `pulumi config` to set", key)
}
return v
}
// RequireObject loads an optional configuration value by its key into the output variable,
// or panics if unable to do so.
func RequireObject(ctx *pulumi.Context, key string, output interface{}) {
v := Require(ctx, key)
if err := json.Unmarshal([]byte(v), output); err != nil {
contract.Failf("unable to unmarshall required configuration variable '%s'; %s", key, err.Error())
}
}
{{range .Builtins}}
{{if .GenerateConfig}}
// Require{{.Name}} loads an optional configuration value by its key, as a {{.Type}}, or panics if it doesn't exist.
func Require{{.Name}}(ctx *pulumi.Context, key string) {{.Type}} {
v := Require(ctx, key)
return cast.To{{.Name}}(v)
}
{{end}}
{{end}}
// RequireSecret loads a configuration value by its key returning it wrapped in a secret Output,
// or panics if it doesn't exist.
func RequireSecret(ctx *pulumi.Context, key string) pulumi.Output {
return pulumi.ToSecret(Require(ctx, key))
}
// RequireSecretObject loads an optional configuration value by its key into the output variable,
// returning it wrapped in a secret Output, or panics if unable to do so.
func RequireSecretObject(ctx *pulumi.Context, key string, output interface{}) pulumi.Output {
RequireObject(ctx, key, output)
return pulumi.ToSecret(output)
}
{{range .Builtins}}
{{if .GenerateConfig}}
// RequireSecret{{.Name}} loads an optional configuration value by its key,
// as a {{.Type}} wrapped in a secret Output, or panics if it doesn't exist.
func RequireSecret{{.Name}}(ctx *pulumi.Context, key string) pulumi.Output {
return pulumi.ToSecret(Require{{.Name}}(ctx, key))
}
{{end}}
{{end}}

View file

@ -0,0 +1,93 @@
// Copyright 2016-2018, Pulumi Corporation.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package config
import (
"encoding/json"
"github.com/pkg/errors"
"github.com/spf13/cast"
"github.com/pulumi/pulumi/sdk/go/pulumi"
)
// Try loads a configuration value by its key, returning a non-nil error if it doesn't exist.
func Try(ctx *pulumi.Context, key string) (string, error) {
v, ok := ctx.GetConfig(key)
if !ok {
return "",
errors.Errorf("missing required configuration variable '%s'; run `pulumi config` to set", key)
}
return v, nil
}
// TryObject loads an optional configuration value by its key into the output variable,
// or returns an error if unable to do so.
func TryObject(ctx *pulumi.Context, key string, output interface{}) error {
v, err := Try(ctx, key)
if err != nil {
return err
}
return json.Unmarshal([]byte(v), output)
}
{{range .Builtins}}
{{if .GenerateConfig}}
// Try{{.Name}} loads an optional configuration value by its key, as a {{.Type}}, or returns an error if it doesn't exist.
func Try{{.Name}}(ctx *pulumi.Context, key string) ({{.Type}}, error) {
v, err := Try(ctx, key)
if err != nil {
return {{.DefaultConfig}}, err
}
return cast.To{{.Name}}(v), nil
}
{{end}}
{{end}}
// TrySecret loads a configuration value by its key, returning a non-nil error if it doesn't exist.
func TrySecret(ctx *pulumi.Context, key string) (pulumi.Output, error) {
v, err := Try(ctx, key)
if err != nil {
return nil, err
}
return pulumi.ToSecret(pulumi.String(v)), nil
}
// TrySecretObject loads a configuration value by its key into the output variable,
// or returns an error if unable to do so.
func TrySecretObject(ctx *pulumi.Context, key string, output interface{}) (pulumi.Output, error) {
err := TryObject(ctx, key, output)
if err != nil {
return nil, err
}
return pulumi.ToSecret(output), nil
}
{{range .Builtins}}
{{if .GenerateConfig}}
// TrySecret{{.Name}} loads an optional configuration value by its key, as a {{.Type}},
// or returns an error if it doesn't exist.
func TrySecret{{.Name}}(ctx *pulumi.Context, key string) (pulumi.Output, error) {
v, err := Try{{.Name}}(ctx, key)
if err != nil {
return nil, err
}
return pulumi.ToSecret(pulumi.{{.Name}}(v)), nil
}
{{end}}
{{end}}

View file

@ -30,13 +30,13 @@ func TestOutputApply(t *testing.T) {
// Test that resolved outputs lead to applies being run.
{
out := newIntOutput()
go func() { out.resolve(42, true) }()
go func() { out.resolve(42, true, false) }()
var ranApp bool
app := out.ApplyT(func(v int) (interface{}, error) {
ranApp = true
return v + 1, nil
})
v, known, err := await(app)
v, known, _, err := await(app)
assert.True(t, ranApp)
assert.Nil(t, err)
assert.True(t, known)
@ -45,13 +45,13 @@ func TestOutputApply(t *testing.T) {
// Test that resolved, but unknown outputs, skip the running of applies.
{
out := newIntOutput()
go func() { out.resolve(42, false) }()
go func() { out.resolve(42, false, false) }()
var ranApp bool
app := out.ApplyT(func(v int) (interface{}, error) {
ranApp = true
return v + 1, nil
})
_, known, err := await(app)
_, known, _, err := await(app)
assert.False(t, ranApp)
assert.Nil(t, err)
assert.False(t, known)
@ -65,7 +65,7 @@ func TestOutputApply(t *testing.T) {
ranApp = true
return v + 1, nil
})
v, _, err := await(app)
v, _, _, err := await(app)
assert.False(t, ranApp)
assert.NotNil(t, err)
assert.Nil(t, v)
@ -73,7 +73,7 @@ func TestOutputApply(t *testing.T) {
// Test that an an apply that returns an output returns the resolution of that output, not the output itself.
{
out := newIntOutput()
go func() { out.resolve(42, true) }()
go func() { out.resolve(42, true, false) }()
var ranApp bool
app := out.ApplyT(func(v int) (interface{}, error) {
other, resolveOther, _ := NewOutput()
@ -81,7 +81,7 @@ func TestOutputApply(t *testing.T) {
ranApp = true
return other, nil
})
v, known, err := await(app)
v, known, _, err := await(app)
assert.True(t, ranApp)
assert.Nil(t, err)
assert.True(t, known)
@ -93,7 +93,7 @@ func TestOutputApply(t *testing.T) {
ranApp = true
return other, nil
})
v, known, err = await(app)
v, known, _, err = await(app)
assert.True(t, ranApp)
assert.Nil(t, err)
assert.True(t, known)
@ -102,7 +102,7 @@ func TestOutputApply(t *testing.T) {
// Test that an an apply that reject an output returns the rejection of that output, not the output itself.
{
out := newIntOutput()
go func() { out.resolve(42, true) }()
go func() { out.resolve(42, true, false) }()
var ranApp bool
app := out.ApplyT(func(v int) (interface{}, error) {
other, _, rejectOther := NewOutput()
@ -110,7 +110,7 @@ func TestOutputApply(t *testing.T) {
ranApp = true
return other, nil
})
v, _, err := await(app)
v, _, _, err := await(app)
assert.True(t, ranApp)
assert.NotNil(t, err)
assert.Nil(t, v)
@ -121,7 +121,7 @@ func TestOutputApply(t *testing.T) {
ranApp = true
return other, nil
})
v, _, err = await(app)
v, _, _, err = await(app)
assert.True(t, ranApp)
assert.NotNil(t, err)
assert.Nil(t, v)
@ -129,17 +129,17 @@ func TestOutputApply(t *testing.T) {
// Test builtin applies.
{
out := newIntOutput()
go func() { out.resolve(42, true) }()
go func() { out.resolve(42, true, false) }()
{{range .Builtins}}
t.Run("Apply{{.Name}}", func(t *testing.T) {
o2 := out.Apply{{.Name}}(func(v int) {{.ElementType}} { return *new({{.ElementType}}) })
_, known, err := await(o2)
_, known, _, err := await(o2)
assert.True(t, known)
assert.NoError(t, err)
o2 = out.Apply{{.Name}}WithContext(context.Background(), func(_ context.Context, v int) {{.ElementType}} { return *new({{.ElementType}}) })
_, known, err = await(o2)
_, known, _, err = await(o2)
assert.True(t, known)
assert.NoError(t, err)
})
@ -148,7 +148,7 @@ func TestOutputApply(t *testing.T) {
// Test that applies return appropriate concrete implementations of Output based on the callback type
{
out := newIntOutput()
go func() { out.resolve(42, true) }()
go func() { out.resolve(42, true, false) }()
{{range .Builtins}}
t.Run("ApplyT::{{.Name}}Output", func(t *testing.T) {
@ -165,10 +165,10 @@ func TestOutputApply(t *testing.T) {
}
out := newIntOutput()
go func() { out.resolve(42, true) }()
go func() { out.resolve(42, true, false) }()
out2 := StringOutput{newOutputState(reflect.TypeOf(""))}
go func() { out2.resolve("hello", true) }()
go func() { out2.resolve("hello", true, false) }()
res := out.
ApplyT(func(v int) myStructType {
@ -230,7 +230,7 @@ func TestOutputApply(t *testing.T) {
_, ok := res.(StringArrayOutput)
assert.True(t, ok)
v, known, err := await(res)
v, known, _, err := await(res)
assert.Nil(t, err)
assert.True(t, known)
assert.Equal(t, []string{"qux", "zed"}, v)
@ -238,7 +238,7 @@ func TestOutputApply(t *testing.T) {
_, ok = res2.(StringArrayOutput)
assert.True(t, ok)
v, known, err = await(res2)
v, known, _, err = await(res2)
assert.Nil(t, err)
assert.True(t, known)
assert.Equal(t, []string{"foo", "bar"}, v)
@ -246,7 +246,7 @@ func TestOutputApply(t *testing.T) {
_, ok = res3.(StringOutput)
assert.True(t, ok)
v, known, err = await(res3)
v, known, _, err = await(res3)
assert.Nil(t, err)
assert.True(t, known)
assert.Equal(t, "foo,bar,qux,zed", v)
@ -254,12 +254,12 @@ func TestOutputApply(t *testing.T) {
_, ok = res4.(AnyOutput)
assert.True(t, ok)
v, known, err = await(res4)
v, known, _, err = await(res4)
assert.Nil(t, err)
assert.True(t, known)
assert.Equal(t, &myStructType{foo: 42, bar: "hello"}, v)
v, known, err = await(res5)
v, known, _, err = await(res5)
assert.Nil(t, err)
assert.True(t, known)
assert.Equal(t, "foo,bar,qux,zed;42;hello", v)
@ -273,7 +273,7 @@ func TestToOutput{{.Name}}(t *testing.T) {
_, ok := out.({{.Name}}Input)
assert.True(t, ok)
_, known, err := await(out)
_, known, _, err := await(out)
assert.True(t, known)
assert.NoError(t, err)
@ -281,7 +281,7 @@ func TestToOutput{{.Name}}(t *testing.T) {
_, ok = out.({{.Name}}Input)
assert.True(t, ok)
_, known, err = await(out)
_, known, _, err = await(out)
assert.True(t, known)
assert.NoError(t, err)
}
@ -294,25 +294,25 @@ func TestTo{{.Name}}Output(t *testing.T) {
out := in.To{{.Name}}Output()
_, known, err := await(out)
_, known, _, err := await(out)
assert.True(t, known)
assert.NoError(t, err)
out = out.To{{.Name}}Output()
_, known, err = await(out)
_, known, _, err = await(out)
assert.True(t, known)
assert.NoError(t, err)
out = in.To{{.Name}}OutputWithContext(context.Background())
_, known, err = await(out)
_, known, _, err = await(out)
assert.True(t, known)
assert.NoError(t, err)
out = out.To{{.Name}}OutputWithContext(context.Background())
_, known, err = await(out)
_, known, _, err = await(out)
assert.True(t, known)
assert.NoError(t, err)
}
@ -322,56 +322,56 @@ func TestTo{{.Name}}Output(t *testing.T) {
func TestBuiltinConversions(t *testing.T) {
archiveIn := NewFileArchive("foo.zip")
assetOrArchiveOut := archiveIn.ToAssetOrArchiveOutput()
archiveV, known, err := await(assetOrArchiveOut)
archiveV, known, _, err := await(assetOrArchiveOut)
assert.True(t, known)
assert.NoError(t, err)
assert.Equal(t, archiveIn, archiveV)
archiveOut := archiveIn.ToArchiveOutput()
assetOrArchiveOut = archiveOut.ToAssetOrArchiveOutput()
archiveV, known, err = await(assetOrArchiveOut)
archiveV, known, _, err = await(assetOrArchiveOut)
assert.True(t, known)
assert.NoError(t, err)
assert.Equal(t, archiveIn, archiveV)
assetIn := NewFileAsset("foo.zip")
assetOrArchiveOut = assetIn.ToAssetOrArchiveOutput()
assetV, known, err := await(assetOrArchiveOut)
assetV, known, _, err := await(assetOrArchiveOut)
assert.True(t, known)
assert.NoError(t, err)
assert.Equal(t, assetIn, assetV)
assetOut := assetIn.ToAssetOutput()
assetOrArchiveOut = assetOut.ToAssetOrArchiveOutput()
assetV, known, err = await(assetOrArchiveOut)
assetV, known, _, err = await(assetOrArchiveOut)
assert.True(t, known)
assert.NoError(t, err)
assert.Equal(t, assetIn, assetV)
idIn := ID("foo")
stringOut := idIn.ToStringOutput()
stringV, known, err := await(stringOut)
stringV, known, _, err := await(stringOut)
assert.True(t, known)
assert.NoError(t, err)
assert.Equal(t, string(idIn), stringV)
idOut := idIn.ToIDOutput()
stringOut = idOut.ToStringOutput()
stringV, known, err = await(stringOut)
stringV, known, _, err = await(stringOut)
assert.True(t, known)
assert.NoError(t, err)
assert.Equal(t, string(idIn), stringV)
urnIn := URN("foo")
stringOut = urnIn.ToStringOutput()
stringV, known, err = await(stringOut)
stringV, known, _, err = await(stringOut)
assert.True(t, known)
assert.NoError(t, err)
assert.Equal(t, string(urnIn), stringV)
urnOut := urnIn.ToURNOutput()
stringOut = urnOut.ToStringOutput()
stringV, known, err = await(stringOut)
stringV, known, _, err = await(stringOut)
assert.True(t, known)
assert.NoError(t, err)
assert.Equal(t, string(urnIn), stringV)
@ -383,11 +383,11 @@ func TestBuiltinConversions(t *testing.T) {
func Test{{.Name}}Elem(t *testing.T) {
out := ({{.Example}}).To{{.Name}}Output()
av, known, err := await(out)
av, known, _, err := await(out)
assert.True(t, known)
assert.NoError(t, err)
iv, known, err := await(out.Elem())
iv, known, _, err := await(out.Elem())
assert.True(t, known)
assert.NoError(t, err)
@ -403,11 +403,11 @@ func Test{{.Name}}Elem(t *testing.T) {
func Test{{.Name}}Index(t *testing.T) {
out := ({{.Example}}).To{{.Name}}Output()
av, known, err := await(out)
av, known, _, err := await(out)
assert.True(t, known)
assert.NoError(t, err)
iv, known, err := await(out.Index(Int(0)))
iv, known, _, err := await(out.Index(Int(0)))
assert.True(t, known)
assert.NoError(t, err)
@ -422,11 +422,11 @@ func Test{{.Name}}Index(t *testing.T) {
func Test{{.Name}}Index(t *testing.T) {
out := ({{.Example}}).To{{.Name}}Output()
av, known, err := await(out)
av, known, _, err := await(out)
assert.True(t, known)
assert.NoError(t, err)
iv, known, err := await(out.MapIndex(String("baz")))
iv, known, _, err := await(out.MapIndex(String("baz")))
assert.True(t, known)
assert.NoError(t, err)

View file

@ -38,12 +38,13 @@ type Output interface {
getState() *OutputState
dependencies() []Resource
fulfillValue(value reflect.Value, known bool, err error)
resolveValue(value reflect.Value, known bool)
fulfill(value interface{}, known bool, err error)
resolve(value interface{}, known bool)
fulfillValue(value reflect.Value, known, secret bool, err error)
resolveValue(value reflect.Value, known, secret bool)
fulfill(value interface{}, known, secret bool, err error)
resolve(value interface{}, known, secret bool)
reject(err error)
await(ctx context.Context) (interface{}, bool, error)
await(ctx context.Context) (interface{}, bool, bool, error)
isSecret() bool
}
var outputType = reflect.TypeOf((*Output)(nil)).Elem()
@ -74,9 +75,10 @@ type OutputState struct {
state uint32 // one of output{Pending,Resolved,Rejected}
value interface{} // the value of this output if it is resolved.
err error // the error associated with this output if it is rejected.
known bool // true if this output's value is known.
value interface{} // the value of this output if it is resolved.
err error // the error associated with this output if it is rejected.
known bool // true if this output's value is known.
secret bool // true if this output's value is secret
element reflect.Type // the element type of this output.
deps []Resource // the dependencies associated with this output property.
@ -96,11 +98,11 @@ func (o *OutputState) dependencies() []Resource {
return o.deps
}
func (o *OutputState) fulfill(value interface{}, known bool, err error) {
o.fulfillValue(reflect.ValueOf(value), known, err)
func (o *OutputState) fulfill(value interface{}, known, secret bool, err error) {
o.fulfillValue(reflect.ValueOf(value), known, secret, err)
}
func (o *OutputState) fulfillValue(value reflect.Value, known bool, err error) {
func (o *OutputState) fulfillValue(value reflect.Value, known, secret bool, err error) {
if o == nil {
return
}
@ -116,45 +118,45 @@ func (o *OutputState) fulfillValue(value reflect.Value, known bool, err error) {
}
if err != nil {
o.state, o.err, o.known = outputRejected, err, true
o.state, o.err, o.known, o.secret = outputRejected, err, true, secret
} else {
if value.IsValid() {
reflect.ValueOf(&o.value).Elem().Set(value)
}
o.state, o.known = outputResolved, known
o.state, o.known, o.secret = outputResolved, known, secret
}
}
func (o *OutputState) resolve(value interface{}, known bool) {
o.fulfill(value, known, nil)
func (o *OutputState) resolve(value interface{}, known, secret bool) {
o.fulfill(value, known, secret, nil)
}
func (o *OutputState) resolveValue(value reflect.Value, known bool) {
o.fulfillValue(value, known, nil)
func (o *OutputState) resolveValue(value reflect.Value, known, secret bool) {
o.fulfillValue(value, known, secret, nil)
}
func (o *OutputState) reject(err error) {
o.fulfill(nil, true, err)
o.fulfill(nil, true, false, err)
}
func (o *OutputState) await(ctx context.Context) (interface{}, bool, error) {
func (o *OutputState) await(ctx context.Context) (interface{}, bool, bool, error) {
for {
if o == nil {
// If the state is nil, treat its value as resolved and unknown.
return nil, false, nil
return nil, false, false, nil
}
o.mutex.Lock()
for o.state == outputPending {
if ctx.Err() != nil {
return nil, true, ctx.Err()
return nil, true, false, ctx.Err()
}
o.cond.Wait()
}
o.mutex.Unlock()
if !o.known || o.err != nil {
return nil, o.known, o.err
return nil, o.known, o.secret, o.err
}
// If the result is an Output, await it in turn.
@ -163,7 +165,7 @@ func (o *OutputState) await(ctx context.Context) (interface{}, bool, error) {
// the element type of the outer output. We should reconsider this.
ov, ok := o.value.(Output)
if !ok {
return o.value, true, nil
return o.value, true, o.secret, nil
}
o = ov.getState()
}
@ -220,7 +222,7 @@ func NewOutput() (Output, func(interface{}), func(error)) {
out := newOutputState(anyType)
resolve := func(v interface{}) {
out.resolve(v, true)
out.resolve(v, true, false)
}
reject := func(err error) {
out.reject(err)
@ -375,9 +377,9 @@ func (o *OutputState) ApplyTWithContext(ctx context.Context, applier interface{}
result := newOutput(resultType, o.dependencies()...)
go func() {
v, known, err := o.await(ctx)
v, known, secret, err := o.await(ctx)
if err != nil || !known {
result.fulfill(nil, known, err)
result.fulfill(nil, known, secret, err)
return
}
@ -389,11 +391,31 @@ func (o *OutputState) ApplyTWithContext(ctx context.Context, applier interface{}
}
// Fulfill the result.
result.fulfillValue(results[0], true, nil)
result.fulfillValue(results[0], true, secret, nil)
}()
return result
}
// isSecret returns a bool representing the secretness of the Output
func (o *OutputState) isSecret() bool {
return o.getState().secret
}
// 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 {
return ToSecretWithContext(context.Background(), input)
}
// 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)
// set immediate secretness ahead of resolution/fufillment
o.getState().secret = true
return o
}
// All returns an ArrayOutput that will resolve when all of the provided inputs will resolve. Each element of the
// array will contain the resolved value of the corresponding output. The output will be rejected if any of the inputs
// is rejected.
@ -489,11 +511,11 @@ func callToOutputMethod(ctx context.Context, input reflect.Value, resolvedType r
return toOutputMethod.Call([]reflect.Value{reflect.ValueOf(ctx)})[0].Interface().(Output), true
}
func awaitInputs(ctx context.Context, v, resolved reflect.Value) (bool, error) {
func awaitInputs(ctx context.Context, v, resolved reflect.Value) (bool, bool, error) {
contract.Assert(v.IsValid())
if !resolved.CanSet() {
return true, nil
return true, false, nil
}
// If the value is an Input with of a different element type, turn it into an Output of the appropriate type and
@ -503,7 +525,7 @@ func awaitInputs(ctx context.Context, v, resolved reflect.Value) (bool, error) {
input, isNonNil := v.Interface().(Input)
if !isNonNil {
// A nil input is already fully-resolved.
return true, nil
return true, false, nil
}
valueType = input.ElementType()
@ -529,22 +551,22 @@ func awaitInputs(ctx context.Context, v, resolved reflect.Value) (bool, error) {
// If the input is an Output, await its value. The returned value is fully resolved.
if output, ok := input.(Output); ok {
e, known, err := output.await(ctx)
e, known, secret, err := output.await(ctx)
if err != nil || !known {
return known, err
return known, secret, err
}
if !assignInput {
resolved.Set(reflect.ValueOf(e))
} else {
resolved.Set(reflect.ValueOf(input))
}
return true, nil
return true, secret, nil
}
// Check for types that are already fully-resolved.
if v, ok := getResolvedValue(input); ok {
resolved.Set(v)
return true, nil
return true, false, nil
}
v, isInput = reflect.ValueOf(input), true
@ -572,7 +594,7 @@ func awaitInputs(ctx context.Context, v, resolved reflect.Value) (bool, error) {
resolved = reflect.New(valueType).Elem()
}
known, err := true, error(nil)
known, secret, err := true, false, error(nil)
switch v.Kind() {
case reflect.Interface:
if !v.IsNil() {
@ -589,8 +611,9 @@ func awaitInputs(ctx context.Context, v, resolved reflect.Value) (bool, error) {
numFields := typ.NumField()
for i := 0; i < numFields; i++ {
_, field := getMappedField(resolved, i)
fknown, ferr := awaitInputs(ctx, v.Field(i), field)
fknown, fsecret, ferr := awaitInputs(ctx, v.Field(i), field)
known = known && fknown
secret = secret || fsecret
if err == nil {
err = ferr
}
@ -598,8 +621,9 @@ func awaitInputs(ctx context.Context, v, resolved reflect.Value) (bool, error) {
case reflect.Array:
l := v.Len()
for i := 0; i < l; i++ {
eknown, eerr := awaitInputs(ctx, v.Index(i), resolved.Index(i))
eknown, esecret, eerr := awaitInputs(ctx, v.Index(i), resolved.Index(i))
known = known && eknown
secret = secret || esecret
if err == nil {
err = eerr
}
@ -608,8 +632,9 @@ func awaitInputs(ctx context.Context, v, resolved reflect.Value) (bool, error) {
l := v.Len()
resolved.Set(reflect.MakeSlice(resolved.Type(), l, l))
for i := 0; i < l; i++ {
eknown, eerr := awaitInputs(ctx, v.Index(i), resolved.Index(i))
eknown, esecret, eerr := awaitInputs(ctx, v.Index(i), resolved.Index(i))
known = known && eknown
secret = secret || esecret
if err == nil {
err = eerr
}
@ -620,13 +645,13 @@ func awaitInputs(ctx context.Context, v, resolved reflect.Value) (bool, error) {
iter := v.MapRange()
for iter.Next() {
kv := reflect.New(resolvedKeyType).Elem()
kknown, kerr := awaitInputs(ctx, iter.Key(), kv)
kknown, ksecret, kerr := awaitInputs(ctx, iter.Key(), kv)
if err == nil {
err = kerr
}
vv := reflect.New(resolvedValueType).Elem()
vknown, verr := awaitInputs(ctx, iter.Value(), vv)
vknown, vsecret, verr := awaitInputs(ctx, iter.Value(), vv)
if err == nil {
err = verr
}
@ -636,6 +661,7 @@ func awaitInputs(ctx context.Context, v, resolved reflect.Value) (bool, error) {
}
known = known && kknown && vknown
secret = secret || ksecret || vsecret
}
default:
if isInput {
@ -643,7 +669,7 @@ func awaitInputs(ctx context.Context, v, resolved reflect.Value) (bool, error) {
}
resolved.Set(v)
}
return known, err
return known, secret, err
}
// ToOutput returns an Output that will resolve when all Inputs contained in the given value have resolved.
@ -654,6 +680,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)
}
func toOutputWithContext(ctx context.Context, v interface{}, forceSecret bool) Output {
resolvedType := reflect.TypeOf(v)
if input, ok := v.(Input); ok {
resolvedType = input.ElementType()
@ -668,13 +698,14 @@ func ToOutputWithContext(ctx context.Context, v interface{}) Output {
go func() {
element := reflect.New(resolvedType).Elem()
known, err := awaitInputs(ctx, reflect.ValueOf(v), element)
known, secret, err := awaitInputs(ctx, reflect.ValueOf(v), element)
secret = secret || forceSecret
if err != nil || !known {
result.fulfill(nil, known, err)
result.fulfill(nil, known, secret, err)
return
}
result.resolveValue(element, true)
result.resolveValue(element, true, secret)
}()
return result
}
@ -754,8 +785,8 @@ func AnyWithContext(ctx context.Context, v interface{}) AnyOutput {
out := newOutput(anyOutputType, gatherDependencies(v)...)
go func() {
var result interface{}
known, err := awaitInputs(ctx, reflect.ValueOf(v), reflect.ValueOf(&result).Elem())
out.fulfill(result, known, err)
known, secret, err := awaitInputs(ctx, reflect.ValueOf(v), reflect.ValueOf(&result).Elem())
out.fulfill(result, known, secret, err)
}()
return out.(AnyOutput)
}
@ -782,12 +813,12 @@ func (o IDOutput) ToStringPtrOutputWithContext(ctx context.Context) StringPtrOut
return o.ToStringOutputWithContext(ctx).ToStringPtrOutputWithContext(ctx)
}
func (o IDOutput) awaitID(ctx context.Context) (ID, bool, error) {
id, known, err := o.await(ctx)
func (o IDOutput) awaitID(ctx context.Context) (ID, bool, bool, error) {
id, known, secret, err := o.await(ctx)
if !known || err != nil {
return "", known, err
return "", known, false, err
}
return ID(convert(id, stringType).(string)), true, nil
return ID(convert(id, stringType).(string)), true, secret, nil
}
func (in URN) ToStringPtrOutput() StringPtrOutput {
@ -806,12 +837,12 @@ func (o URNOutput) ToStringPtrOutputWithContext(ctx context.Context) StringPtrOu
return o.ToStringOutputWithContext(ctx).ToStringPtrOutputWithContext(ctx)
}
func (o URNOutput) awaitURN(ctx context.Context) (URN, bool, error) {
id, known, err := o.await(ctx)
func (o URNOutput) awaitURN(ctx context.Context) (URN, bool, bool, error) {
id, known, secret, err := o.await(ctx)
if !known || err != nil {
return "", known, err
return "", known, secret, err
}
return URN(convert(id, stringType).(string)), true, nil
return URN(convert(id, stringType).(string)), true, secret, nil
}
func convert(v interface{}, to reflect.Type) interface{} {

File diff suppressed because it is too large Load diff

View file

@ -24,12 +24,12 @@ import (
"github.com/stretchr/testify/assert"
)
func await(out Output) (interface{}, bool, error) {
func await(out Output) (interface{}, bool, bool, error) {
return out.await(context.Background())
}
func assertApplied(t *testing.T, out Output) {
_, known, err := await(out)
_, known, _, err := await(out)
assert.True(t, known)
assert.Nil(t, err)
}
@ -45,9 +45,10 @@ func TestBasicOutputs(t *testing.T) {
go func() {
resolve(42)
}()
v, known, err := await(out)
v, known, secret, err := await(out)
assert.Nil(t, err)
assert.True(t, known)
assert.False(t, secret)
assert.NotNil(t, v)
assert.Equal(t, 42, v.(int))
}
@ -56,7 +57,7 @@ func TestBasicOutputs(t *testing.T) {
go func() {
reject(errors.New("boom"))
}()
v, _, err := await(out)
v, _, _, err := await(out)
assert.NotNil(t, err)
assert.Nil(t, v)
}
@ -65,7 +66,7 @@ func TestBasicOutputs(t *testing.T) {
func TestArrayOutputs(t *testing.T) {
out := ArrayOutput{newOutputState(reflect.TypeOf([]interface{}{}))}
go func() {
out.resolve([]interface{}{nil, 0, "x"}, true)
out.resolve([]interface{}{nil, 0, "x"}, true, false)
}()
{
assertApplied(t, out.ApplyT(func(arr []interface{}) (interface{}, error) {
@ -83,7 +84,7 @@ func TestArrayOutputs(t *testing.T) {
func TestBoolOutputs(t *testing.T) {
out := BoolOutput{newOutputState(reflect.TypeOf(false))}
go func() {
out.resolve(true, true)
out.resolve(true, true, false)
}()
{
assertApplied(t, out.ApplyT(func(v bool) (interface{}, error) {
@ -100,7 +101,7 @@ func TestMapOutputs(t *testing.T) {
"x": 1,
"y": false,
"z": "abc",
}, true)
}, true, false)
}()
{
assertApplied(t, out.ApplyT(func(v map[string]interface{}) (interface{}, error) {
@ -116,7 +117,7 @@ func TestMapOutputs(t *testing.T) {
func TestNumberOutputs(t *testing.T) {
out := Float64Output{newOutputState(reflect.TypeOf(float64(0)))}
go func() {
out.resolve(42.345, true)
out.resolve(42.345, true, false)
}()
{
assertApplied(t, out.ApplyT(func(v float64) (interface{}, error) {
@ -129,7 +130,7 @@ func TestNumberOutputs(t *testing.T) {
func TestStringOutputs(t *testing.T) {
out := StringOutput{newOutputState(reflect.TypeOf(""))}
go func() {
out.resolve("a stringy output", true)
out.resolve("a stringy output", true, false)
}()
{
assertApplied(t, out.ApplyT(func(v string) (interface{}, error) {
@ -161,7 +162,7 @@ func TestResolveOutputToOutput(t *testing.T) {
resolve(other)
go func() { rejectOther(errors.New("boom")) }()
}()
v, _, err := await(out)
v, _, _, err := await(out)
assert.NotNil(t, err)
assert.Nil(t, v)
}
@ -173,8 +174,9 @@ func TestToOutputStruct(t *testing.T) {
_, ok := out.(nestedTypeOutput)
assert.True(t, ok)
v, known, err := await(out)
v, known, secret, err := await(out)
assert.True(t, known)
assert.False(t, secret)
assert.NoError(t, err)
assert.Equal(t, nestedType{Foo: "bar", Bar: 42}, v)
@ -182,8 +184,10 @@ func TestToOutputStruct(t *testing.T) {
_, ok = out.(nestedTypeOutput)
assert.True(t, ok)
v, known, err = await(out)
v, known, secret, err = await(out)
assert.True(t, known)
assert.False(t, secret)
assert.NoError(t, err)
assert.Equal(t, nestedType{Foo: "bar", Bar: 42}, v)
@ -191,8 +195,9 @@ func TestToOutputStruct(t *testing.T) {
_, ok = out.(nestedTypeOutput)
assert.True(t, ok)
v, known, err = await(out)
v, known, secret, err = await(out)
assert.True(t, known)
assert.False(t, secret)
assert.NoError(t, err)
assert.Equal(t, nestedType{Foo: "bar", Bar: 42}, v)
}
@ -219,8 +224,9 @@ func TestToOutputConvert(t *testing.T) {
_, ok := out.(nestedTypeOutput)
assert.True(t, ok)
v, known, err := await(out)
v, known, secret, err := await(out)
assert.True(t, known)
assert.False(t, secret)
assert.NoError(t, err)
assert.Equal(t, nestedType{Foo: "bar", Bar: 1}, v)
}
@ -241,8 +247,9 @@ func TestToOutputAny(t *testing.T) {
_, ok := out.(AnyOutput)
assert.True(t, ok)
v, known, err := await(out)
v, known, secret, err := await(out)
assert.True(t, known)
assert.False(t, secret)
assert.NoError(t, err)
argsV := v.(*args)
@ -291,8 +298,9 @@ func TestToOutputInputAny(t *testing.T) {
_, ok := out.(AnyOutput)
assert.True(t, ok)
v, known, err := await(out)
v, known, secret, err := await(out)
assert.True(t, known)
assert.False(t, secret)
assert.NoError(t, err)
assert.Equal(t, &args{
@ -301,3 +309,86 @@ func TestToOutputInputAny(t *testing.T) {
A: map[string]interface{}{"world": true},
}, v)
}
// 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())
errChan := make(chan error)
resultChan := make(chan string)
secretChan := make(chan bool)
s.ApplyT(func(v interface{}) (string, error) {
// assert secretness after the output resolves
secretChan <- s.isSecret()
val := v.(string)
if val == "foo" {
// validate the value
resultChan <- val
} else {
errChan <- errors.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.True(t, isSecret)
break
}
}
}
// Test that secretness is properly bubbled up with all/apply.
func TestSecretApply(t *testing.T) {
s1 := ToSecret(String("foo"))
// assert that secret is immediately secret
assert.True(t, s1.isSecret())
s2 := StringInput(String("bar"))
errChan := make(chan error)
resultChan := make(chan string)
secretChan := make(chan bool)
s := All(s1, s2).ApplyT(func(v interface{}) (string, error) {
val := v.([]interface{})
return val[0].(string) + val[1].(string), nil
})
s.ApplyT(func(v interface{}) (string, error) {
// assert secretness after the output resolves
secretChan <- s.isSecret()
val := v.(string)
if val == "foobar" {
// validate the value
resultChan <- val
} else {
errChan <- errors.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, "foobar", r)
break
case isSecret := <-secretChan:
assert.True(t, isSecret)
break
}
}
}

View file

@ -1266,6 +1266,10 @@ func TestStackReferenceGo(t *testing.T) {
Dir: "step1",
Additive: true,
},
{
Dir: "step2",
Additive: true,
},
},
}
integration.ProgramTest(t, opts)

View file

@ -37,6 +37,7 @@ func main() {
results <- v
return v, nil
})
ctx.Export("val2", pulumi.ToSecret(val))
select {
case err = <-errChan:

View file

@ -0,0 +1,59 @@
// Copyright 2016-2020, Pulumi Corporation. All rights reserved.
package main
import (
"fmt"
"github.com/pkg/errors"
"github.com/pulumi/pulumi/sdk/go/pulumi"
"github.com/pulumi/pulumi/sdk/go/pulumi/config"
)
// Tests that the stack export that included secrets in step1 is read into a secret output.
func main() {
pulumi.Run(func(ctx *pulumi.Context) error {
cfg := config.New(ctx, ctx.Project())
org := cfg.Require("org")
slug := fmt.Sprintf("%v/%v/%v", org, ctx.Project(), ctx.Stack())
stackRef, err := pulumi.NewStackReference(ctx, slug, nil)
if err != nil {
return errors.Wrap(err, "Error reading stack reference.")
}
val := pulumi.StringArrayOutput(stackRef.GetOutput(pulumi.String("val2")))
errChan := make(chan error)
results := make(chan []string)
secret := make(chan bool)
_ = val.ApplyStringArray(func(v []string) ([]string, error) {
if len(v) != 2 || v[0] != "a" || v[1] != "b" {
errChan <- errors.Errorf("Invalid result")
return nil, errors.Errorf("Invalid result")
}
results <- v
return v, nil
})
for i := 0; i < 2; i++ {
select {
case s := <-secret:
if !s {
return errors.Errorf("Error, stack export should be marked as secret!!!")
}
break
case err = <-errChan:
return err
case <-results:
return nil
}
}
return nil
})
}