Add support for secrets in Go SDK (#3938)
This commit is contained in:
parent
0aa208a306
commit
930adc0504
|
@ -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.
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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))
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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))
|
||||
}
|
||||
|
|
|
@ -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))
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
})
|
||||
}
|
||||
|
|
|
@ -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())
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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"])
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
166
sdk/go/pulumi/templates/config-config.go.template
Normal file
166
sdk/go/pulumi/templates/config-config.go.template
Normal 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}}
|
75
sdk/go/pulumi/templates/config-get.go.template
Normal file
75
sdk/go/pulumi/templates/config-get.go.template
Normal 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}}
|
75
sdk/go/pulumi/templates/config-require.go.template
Normal file
75
sdk/go/pulumi/templates/config-require.go.template
Normal 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}}
|
93
sdk/go/pulumi/templates/config-try.go.template
Normal file
93
sdk/go/pulumi/templates/config-try.go.template
Normal 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}}
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -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
|
@ -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
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1266,6 +1266,10 @@ func TestStackReferenceGo(t *testing.T) {
|
|||
Dir: "step1",
|
||||
Additive: true,
|
||||
},
|
||||
{
|
||||
Dir: "step2",
|
||||
Additive: true,
|
||||
},
|
||||
},
|
||||
}
|
||||
integration.ProgramTest(t, opts)
|
||||
|
|
|
@ -37,6 +37,7 @@ func main() {
|
|||
results <- v
|
||||
return v, nil
|
||||
})
|
||||
ctx.Export("val2", pulumi.ToSecret(val))
|
||||
|
||||
select {
|
||||
case err = <-errChan:
|
||||
|
|
59
tests/integration/stack_reference/go/step2/main.go
Normal file
59
tests/integration/stack_reference/go/step2/main.go
Normal 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
|
||||
})
|
||||
}
|
Loading…
Reference in a new issue