pulumi/sdk/go/pulumi/types_test.go

679 lines
16 KiB
Go
Raw Permalink Normal View History

Redesign the Go SDK resource/input/output system. (#3506) The redesign is focused around providing better static typings and improved ease-of-use for the Go SDK. Most of the redesign revolves around three pivots: - Strongly-typed inputs, especially for nested types - Struct-based resource and invoke APIs - Ease-of-use of Apply 1. Strongly-typed inputs Input is the type of a generic input value for a Pulumi resource. This type is used in conjunction with Output to provide polymorphism over strongly-typed input values. The intended pattern for nested Pulumi value types is to define an input interface and a plain, input, and output variant of the value type that implement the input interface. For example, given a nested Pulumi value type with the following shape: ``` type Nested struct { Foo int Bar string } ``` We would define the following: ``` var nestedType = reflect.TypeOf((*Nested)(nil)).Elem() type NestedInput interface { pulumi.Input ToNestedOutput() NestedOutput ToNestedOutputWithContext(context.Context) NestedOutput } type Nested struct { Foo int `pulumi:"foo"` Bar string `pulumi:"bar"` } type NestedInputValue struct { Foo pulumi.IntInput `pulumi:"foo"` Bar pulumi.StringInput `pulumi:"bar"` } func (NestedInputValue) ElementType() reflect.Type { return nestedType } func (v NestedInputValue) ToNestedOutput() NestedOutput { return pulumi.ToOutput(v).(NestedOutput) } func (v NestedInputValue) ToNestedOutputWithContext(ctx context.Context) NestedOutput { return pulumi.ToOutputWithContext(ctx, v).(NestedOutput) } type NestedOutput struct { *pulumi.OutputState } func (NestedOutput) ElementType() reflect.Type { return nestedType } func (o NestedOutput) ToNestedOutput() NestedOutput { return o } func (o NestedOutput) ToNestedOutputWithContext(ctx context.Context) NestedOutput { return o } func (o NestedOutput) Foo() pulumi.IntOutput { return o.Apply(func (v Nested) int { return v.Foo }).(pulumi.IntOutput) } func (o NestedOutput) Bar() pulumi.StringOutput { return o.Apply(func (v Nested) string { return v.Bar }).(pulumi.StringOutput) } ``` The SDK provides input and output types for primitives, arrays, and maps. 2. Struct-based APIs Instead of providing expected output properties in the input map passed to {Read,Register}Resource and returning the outputs as a map, the user now passes a pointer to a struct that implements one of the Resource interfaces and has appropriately typed and tagged fields that represent its output properties. For example, given a custom resource with an int-typed output "foo" and a string-typed output "bar", we would define the following CustomResource type: ``` type MyResource struct { pulumi.CustomResourceState Foo pulumi.IntOutput `pulumi:"foo"` Bar pulumi.StringOutput `pulumi:"bar"` } ``` And invoke RegisterResource like so: ``` var resource MyResource err := ctx.RegisterResource(tok, name, props, &resource, opts...) ``` Invoke arguments and results are also provided via structs, but use plain-old Go types for their fields: ``` type MyInvokeArgs struct { Foo int `pulumi:"foo"` } type MyInvokeResult struct { Bar string `pulumi:"bar"` } var result MyInvokeResult err := ctx.Invoke(tok, MyInvokeArgs{Foo: 42}, &result, opts...) ``` 3. Ease-of-use of Apply All `Apply` methods now accept an interface{} as the callback type. The provided callback value must have one of the following signatures: func (v T) U func (v T) (U, error) func (ctx context.Context, v T) U func (ctx context.Context, v T) (U, error) T must be assignable from the ElementType of the Output. If U is a type that has a registered Output type, the result of the Apply will be the corresponding Output type. Otherwise, the result of the Apply will be AnyOutput. Fixes https://github.com/pulumi/pulumi/issues/2149. Fixes https://github.com/pulumi/pulumi/issues/3488. Fixes https://github.com/pulumi/pulumi/issues/3487. Fixes https://github.com/pulumi/pulumi-aws/issues/248. Fixes https://github.com/pulumi/pulumi/issues/3492. Fixes https://github.com/pulumi/pulumi/issues/3491. Fixes https://github.com/pulumi/pulumi/issues/3562.
2020-01-18 16:08:37 +01:00
// 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.
// nolint: lll
package pulumi
import (
"context"
"errors"
"fmt"
Redesign the Go SDK resource/input/output system. (#3506) The redesign is focused around providing better static typings and improved ease-of-use for the Go SDK. Most of the redesign revolves around three pivots: - Strongly-typed inputs, especially for nested types - Struct-based resource and invoke APIs - Ease-of-use of Apply 1. Strongly-typed inputs Input is the type of a generic input value for a Pulumi resource. This type is used in conjunction with Output to provide polymorphism over strongly-typed input values. The intended pattern for nested Pulumi value types is to define an input interface and a plain, input, and output variant of the value type that implement the input interface. For example, given a nested Pulumi value type with the following shape: ``` type Nested struct { Foo int Bar string } ``` We would define the following: ``` var nestedType = reflect.TypeOf((*Nested)(nil)).Elem() type NestedInput interface { pulumi.Input ToNestedOutput() NestedOutput ToNestedOutputWithContext(context.Context) NestedOutput } type Nested struct { Foo int `pulumi:"foo"` Bar string `pulumi:"bar"` } type NestedInputValue struct { Foo pulumi.IntInput `pulumi:"foo"` Bar pulumi.StringInput `pulumi:"bar"` } func (NestedInputValue) ElementType() reflect.Type { return nestedType } func (v NestedInputValue) ToNestedOutput() NestedOutput { return pulumi.ToOutput(v).(NestedOutput) } func (v NestedInputValue) ToNestedOutputWithContext(ctx context.Context) NestedOutput { return pulumi.ToOutputWithContext(ctx, v).(NestedOutput) } type NestedOutput struct { *pulumi.OutputState } func (NestedOutput) ElementType() reflect.Type { return nestedType } func (o NestedOutput) ToNestedOutput() NestedOutput { return o } func (o NestedOutput) ToNestedOutputWithContext(ctx context.Context) NestedOutput { return o } func (o NestedOutput) Foo() pulumi.IntOutput { return o.Apply(func (v Nested) int { return v.Foo }).(pulumi.IntOutput) } func (o NestedOutput) Bar() pulumi.StringOutput { return o.Apply(func (v Nested) string { return v.Bar }).(pulumi.StringOutput) } ``` The SDK provides input and output types for primitives, arrays, and maps. 2. Struct-based APIs Instead of providing expected output properties in the input map passed to {Read,Register}Resource and returning the outputs as a map, the user now passes a pointer to a struct that implements one of the Resource interfaces and has appropriately typed and tagged fields that represent its output properties. For example, given a custom resource with an int-typed output "foo" and a string-typed output "bar", we would define the following CustomResource type: ``` type MyResource struct { pulumi.CustomResourceState Foo pulumi.IntOutput `pulumi:"foo"` Bar pulumi.StringOutput `pulumi:"bar"` } ``` And invoke RegisterResource like so: ``` var resource MyResource err := ctx.RegisterResource(tok, name, props, &resource, opts...) ``` Invoke arguments and results are also provided via structs, but use plain-old Go types for their fields: ``` type MyInvokeArgs struct { Foo int `pulumi:"foo"` } type MyInvokeResult struct { Bar string `pulumi:"bar"` } var result MyInvokeResult err := ctx.Invoke(tok, MyInvokeArgs{Foo: 42}, &result, opts...) ``` 3. Ease-of-use of Apply All `Apply` methods now accept an interface{} as the callback type. The provided callback value must have one of the following signatures: func (v T) U func (v T) (U, error) func (ctx context.Context, v T) U func (ctx context.Context, v T) (U, error) T must be assignable from the ElementType of the Output. If U is a type that has a registered Output type, the result of the Apply will be the corresponding Output type. Otherwise, the result of the Apply will be AnyOutput. Fixes https://github.com/pulumi/pulumi/issues/2149. Fixes https://github.com/pulumi/pulumi/issues/3488. Fixes https://github.com/pulumi/pulumi/issues/3487. Fixes https://github.com/pulumi/pulumi-aws/issues/248. Fixes https://github.com/pulumi/pulumi/issues/3492. Fixes https://github.com/pulumi/pulumi/issues/3491. Fixes https://github.com/pulumi/pulumi/issues/3562.
2020-01-18 16:08:37 +01:00
"reflect"
"testing"
"github.com/stretchr/testify/assert"
)
func await(out Output) (interface{}, bool, bool, []Resource, error) {
return out.getState().await(context.Background())
Redesign the Go SDK resource/input/output system. (#3506) The redesign is focused around providing better static typings and improved ease-of-use for the Go SDK. Most of the redesign revolves around three pivots: - Strongly-typed inputs, especially for nested types - Struct-based resource and invoke APIs - Ease-of-use of Apply 1. Strongly-typed inputs Input is the type of a generic input value for a Pulumi resource. This type is used in conjunction with Output to provide polymorphism over strongly-typed input values. The intended pattern for nested Pulumi value types is to define an input interface and a plain, input, and output variant of the value type that implement the input interface. For example, given a nested Pulumi value type with the following shape: ``` type Nested struct { Foo int Bar string } ``` We would define the following: ``` var nestedType = reflect.TypeOf((*Nested)(nil)).Elem() type NestedInput interface { pulumi.Input ToNestedOutput() NestedOutput ToNestedOutputWithContext(context.Context) NestedOutput } type Nested struct { Foo int `pulumi:"foo"` Bar string `pulumi:"bar"` } type NestedInputValue struct { Foo pulumi.IntInput `pulumi:"foo"` Bar pulumi.StringInput `pulumi:"bar"` } func (NestedInputValue) ElementType() reflect.Type { return nestedType } func (v NestedInputValue) ToNestedOutput() NestedOutput { return pulumi.ToOutput(v).(NestedOutput) } func (v NestedInputValue) ToNestedOutputWithContext(ctx context.Context) NestedOutput { return pulumi.ToOutputWithContext(ctx, v).(NestedOutput) } type NestedOutput struct { *pulumi.OutputState } func (NestedOutput) ElementType() reflect.Type { return nestedType } func (o NestedOutput) ToNestedOutput() NestedOutput { return o } func (o NestedOutput) ToNestedOutputWithContext(ctx context.Context) NestedOutput { return o } func (o NestedOutput) Foo() pulumi.IntOutput { return o.Apply(func (v Nested) int { return v.Foo }).(pulumi.IntOutput) } func (o NestedOutput) Bar() pulumi.StringOutput { return o.Apply(func (v Nested) string { return v.Bar }).(pulumi.StringOutput) } ``` The SDK provides input and output types for primitives, arrays, and maps. 2. Struct-based APIs Instead of providing expected output properties in the input map passed to {Read,Register}Resource and returning the outputs as a map, the user now passes a pointer to a struct that implements one of the Resource interfaces and has appropriately typed and tagged fields that represent its output properties. For example, given a custom resource with an int-typed output "foo" and a string-typed output "bar", we would define the following CustomResource type: ``` type MyResource struct { pulumi.CustomResourceState Foo pulumi.IntOutput `pulumi:"foo"` Bar pulumi.StringOutput `pulumi:"bar"` } ``` And invoke RegisterResource like so: ``` var resource MyResource err := ctx.RegisterResource(tok, name, props, &resource, opts...) ``` Invoke arguments and results are also provided via structs, but use plain-old Go types for their fields: ``` type MyInvokeArgs struct { Foo int `pulumi:"foo"` } type MyInvokeResult struct { Bar string `pulumi:"bar"` } var result MyInvokeResult err := ctx.Invoke(tok, MyInvokeArgs{Foo: 42}, &result, opts...) ``` 3. Ease-of-use of Apply All `Apply` methods now accept an interface{} as the callback type. The provided callback value must have one of the following signatures: func (v T) U func (v T) (U, error) func (ctx context.Context, v T) U func (ctx context.Context, v T) (U, error) T must be assignable from the ElementType of the Output. If U is a type that has a registered Output type, the result of the Apply will be the corresponding Output type. Otherwise, the result of the Apply will be AnyOutput. Fixes https://github.com/pulumi/pulumi/issues/2149. Fixes https://github.com/pulumi/pulumi/issues/3488. Fixes https://github.com/pulumi/pulumi/issues/3487. Fixes https://github.com/pulumi/pulumi-aws/issues/248. Fixes https://github.com/pulumi/pulumi/issues/3492. Fixes https://github.com/pulumi/pulumi/issues/3491. Fixes https://github.com/pulumi/pulumi/issues/3562.
2020-01-18 16:08:37 +01:00
}
func assertApplied(t *testing.T, out Output) {
_, known, _, _, err := await(out)
Redesign the Go SDK resource/input/output system. (#3506) The redesign is focused around providing better static typings and improved ease-of-use for the Go SDK. Most of the redesign revolves around three pivots: - Strongly-typed inputs, especially for nested types - Struct-based resource and invoke APIs - Ease-of-use of Apply 1. Strongly-typed inputs Input is the type of a generic input value for a Pulumi resource. This type is used in conjunction with Output to provide polymorphism over strongly-typed input values. The intended pattern for nested Pulumi value types is to define an input interface and a plain, input, and output variant of the value type that implement the input interface. For example, given a nested Pulumi value type with the following shape: ``` type Nested struct { Foo int Bar string } ``` We would define the following: ``` var nestedType = reflect.TypeOf((*Nested)(nil)).Elem() type NestedInput interface { pulumi.Input ToNestedOutput() NestedOutput ToNestedOutputWithContext(context.Context) NestedOutput } type Nested struct { Foo int `pulumi:"foo"` Bar string `pulumi:"bar"` } type NestedInputValue struct { Foo pulumi.IntInput `pulumi:"foo"` Bar pulumi.StringInput `pulumi:"bar"` } func (NestedInputValue) ElementType() reflect.Type { return nestedType } func (v NestedInputValue) ToNestedOutput() NestedOutput { return pulumi.ToOutput(v).(NestedOutput) } func (v NestedInputValue) ToNestedOutputWithContext(ctx context.Context) NestedOutput { return pulumi.ToOutputWithContext(ctx, v).(NestedOutput) } type NestedOutput struct { *pulumi.OutputState } func (NestedOutput) ElementType() reflect.Type { return nestedType } func (o NestedOutput) ToNestedOutput() NestedOutput { return o } func (o NestedOutput) ToNestedOutputWithContext(ctx context.Context) NestedOutput { return o } func (o NestedOutput) Foo() pulumi.IntOutput { return o.Apply(func (v Nested) int { return v.Foo }).(pulumi.IntOutput) } func (o NestedOutput) Bar() pulumi.StringOutput { return o.Apply(func (v Nested) string { return v.Bar }).(pulumi.StringOutput) } ``` The SDK provides input and output types for primitives, arrays, and maps. 2. Struct-based APIs Instead of providing expected output properties in the input map passed to {Read,Register}Resource and returning the outputs as a map, the user now passes a pointer to a struct that implements one of the Resource interfaces and has appropriately typed and tagged fields that represent its output properties. For example, given a custom resource with an int-typed output "foo" and a string-typed output "bar", we would define the following CustomResource type: ``` type MyResource struct { pulumi.CustomResourceState Foo pulumi.IntOutput `pulumi:"foo"` Bar pulumi.StringOutput `pulumi:"bar"` } ``` And invoke RegisterResource like so: ``` var resource MyResource err := ctx.RegisterResource(tok, name, props, &resource, opts...) ``` Invoke arguments and results are also provided via structs, but use plain-old Go types for their fields: ``` type MyInvokeArgs struct { Foo int `pulumi:"foo"` } type MyInvokeResult struct { Bar string `pulumi:"bar"` } var result MyInvokeResult err := ctx.Invoke(tok, MyInvokeArgs{Foo: 42}, &result, opts...) ``` 3. Ease-of-use of Apply All `Apply` methods now accept an interface{} as the callback type. The provided callback value must have one of the following signatures: func (v T) U func (v T) (U, error) func (ctx context.Context, v T) U func (ctx context.Context, v T) (U, error) T must be assignable from the ElementType of the Output. If U is a type that has a registered Output type, the result of the Apply will be the corresponding Output type. Otherwise, the result of the Apply will be AnyOutput. Fixes https://github.com/pulumi/pulumi/issues/2149. Fixes https://github.com/pulumi/pulumi/issues/3488. Fixes https://github.com/pulumi/pulumi/issues/3487. Fixes https://github.com/pulumi/pulumi-aws/issues/248. Fixes https://github.com/pulumi/pulumi/issues/3492. Fixes https://github.com/pulumi/pulumi/issues/3491. Fixes https://github.com/pulumi/pulumi/issues/3562.
2020-01-18 16:08:37 +01:00
assert.True(t, known)
assert.Nil(t, err)
}
func newIntOutput() IntOutput {
Await outstanding async work in Go. (#6983) The Pulumi Go SDK does not currently await all outstanding asynchronous work associated with a Pulumi program. Because all relevant asynchronous work is created via the Pulumi SDK, we can track this asynchronous work and ensure that it has all completed prior to returning from `Context.Run`. This is complicated by the fact that many of the existing APIs that are able to create `Output`s--`NewOutput`, `ToOutput`, `Any`, `ToSecret`, and `All`--do not have a `*Context` parameter, and so have no straightforward way to associate themselves with a `*Context`. To address this, these changes add new versions of each of these APIs as methods on `*Context`. Despite these new methods, most Pulumi programs should work without changes: the bulk of `Output`s are created by the SDK itself as part of resource registration, and for `Any` and `All`, we can pick up the context from any `Output`s present in the arguments. The only programs that should require changes are those that create outputs from whole cloth using `NewOutput`, `ToOutput`, or `ToSecret` and create unawaited async work rooted at those outputs. On an implementation level, these changes track asynchronous work using a `sync.WaitGroup` associated with each `*Context`. This `WaitGroup` is passed to each output associated with the context. The SDK increments this `WaitGroup`'s count prior to starting any asynchronous work and decrements it once the work (including any callbacks triggered by the work) is complete. This fixes the Go portion of #3991.
2021-05-14 21:00:21 +02:00
return IntOutput{newOutputState(nil, reflect.TypeOf(42))}
Redesign the Go SDK resource/input/output system. (#3506) The redesign is focused around providing better static typings and improved ease-of-use for the Go SDK. Most of the redesign revolves around three pivots: - Strongly-typed inputs, especially for nested types - Struct-based resource and invoke APIs - Ease-of-use of Apply 1. Strongly-typed inputs Input is the type of a generic input value for a Pulumi resource. This type is used in conjunction with Output to provide polymorphism over strongly-typed input values. The intended pattern for nested Pulumi value types is to define an input interface and a plain, input, and output variant of the value type that implement the input interface. For example, given a nested Pulumi value type with the following shape: ``` type Nested struct { Foo int Bar string } ``` We would define the following: ``` var nestedType = reflect.TypeOf((*Nested)(nil)).Elem() type NestedInput interface { pulumi.Input ToNestedOutput() NestedOutput ToNestedOutputWithContext(context.Context) NestedOutput } type Nested struct { Foo int `pulumi:"foo"` Bar string `pulumi:"bar"` } type NestedInputValue struct { Foo pulumi.IntInput `pulumi:"foo"` Bar pulumi.StringInput `pulumi:"bar"` } func (NestedInputValue) ElementType() reflect.Type { return nestedType } func (v NestedInputValue) ToNestedOutput() NestedOutput { return pulumi.ToOutput(v).(NestedOutput) } func (v NestedInputValue) ToNestedOutputWithContext(ctx context.Context) NestedOutput { return pulumi.ToOutputWithContext(ctx, v).(NestedOutput) } type NestedOutput struct { *pulumi.OutputState } func (NestedOutput) ElementType() reflect.Type { return nestedType } func (o NestedOutput) ToNestedOutput() NestedOutput { return o } func (o NestedOutput) ToNestedOutputWithContext(ctx context.Context) NestedOutput { return o } func (o NestedOutput) Foo() pulumi.IntOutput { return o.Apply(func (v Nested) int { return v.Foo }).(pulumi.IntOutput) } func (o NestedOutput) Bar() pulumi.StringOutput { return o.Apply(func (v Nested) string { return v.Bar }).(pulumi.StringOutput) } ``` The SDK provides input and output types for primitives, arrays, and maps. 2. Struct-based APIs Instead of providing expected output properties in the input map passed to {Read,Register}Resource and returning the outputs as a map, the user now passes a pointer to a struct that implements one of the Resource interfaces and has appropriately typed and tagged fields that represent its output properties. For example, given a custom resource with an int-typed output "foo" and a string-typed output "bar", we would define the following CustomResource type: ``` type MyResource struct { pulumi.CustomResourceState Foo pulumi.IntOutput `pulumi:"foo"` Bar pulumi.StringOutput `pulumi:"bar"` } ``` And invoke RegisterResource like so: ``` var resource MyResource err := ctx.RegisterResource(tok, name, props, &resource, opts...) ``` Invoke arguments and results are also provided via structs, but use plain-old Go types for their fields: ``` type MyInvokeArgs struct { Foo int `pulumi:"foo"` } type MyInvokeResult struct { Bar string `pulumi:"bar"` } var result MyInvokeResult err := ctx.Invoke(tok, MyInvokeArgs{Foo: 42}, &result, opts...) ``` 3. Ease-of-use of Apply All `Apply` methods now accept an interface{} as the callback type. The provided callback value must have one of the following signatures: func (v T) U func (v T) (U, error) func (ctx context.Context, v T) U func (ctx context.Context, v T) (U, error) T must be assignable from the ElementType of the Output. If U is a type that has a registered Output type, the result of the Apply will be the corresponding Output type. Otherwise, the result of the Apply will be AnyOutput. Fixes https://github.com/pulumi/pulumi/issues/2149. Fixes https://github.com/pulumi/pulumi/issues/3488. Fixes https://github.com/pulumi/pulumi/issues/3487. Fixes https://github.com/pulumi/pulumi-aws/issues/248. Fixes https://github.com/pulumi/pulumi/issues/3492. Fixes https://github.com/pulumi/pulumi/issues/3491. Fixes https://github.com/pulumi/pulumi/issues/3562.
2020-01-18 16:08:37 +01:00
}
func TestBasicOutputs(t *testing.T) {
// Just test basic resolve and reject functionality.
{
out, resolve, _ := NewOutput()
go func() {
resolve(42)
}()
v, known, secret, deps, err := await(out)
Redesign the Go SDK resource/input/output system. (#3506) The redesign is focused around providing better static typings and improved ease-of-use for the Go SDK. Most of the redesign revolves around three pivots: - Strongly-typed inputs, especially for nested types - Struct-based resource and invoke APIs - Ease-of-use of Apply 1. Strongly-typed inputs Input is the type of a generic input value for a Pulumi resource. This type is used in conjunction with Output to provide polymorphism over strongly-typed input values. The intended pattern for nested Pulumi value types is to define an input interface and a plain, input, and output variant of the value type that implement the input interface. For example, given a nested Pulumi value type with the following shape: ``` type Nested struct { Foo int Bar string } ``` We would define the following: ``` var nestedType = reflect.TypeOf((*Nested)(nil)).Elem() type NestedInput interface { pulumi.Input ToNestedOutput() NestedOutput ToNestedOutputWithContext(context.Context) NestedOutput } type Nested struct { Foo int `pulumi:"foo"` Bar string `pulumi:"bar"` } type NestedInputValue struct { Foo pulumi.IntInput `pulumi:"foo"` Bar pulumi.StringInput `pulumi:"bar"` } func (NestedInputValue) ElementType() reflect.Type { return nestedType } func (v NestedInputValue) ToNestedOutput() NestedOutput { return pulumi.ToOutput(v).(NestedOutput) } func (v NestedInputValue) ToNestedOutputWithContext(ctx context.Context) NestedOutput { return pulumi.ToOutputWithContext(ctx, v).(NestedOutput) } type NestedOutput struct { *pulumi.OutputState } func (NestedOutput) ElementType() reflect.Type { return nestedType } func (o NestedOutput) ToNestedOutput() NestedOutput { return o } func (o NestedOutput) ToNestedOutputWithContext(ctx context.Context) NestedOutput { return o } func (o NestedOutput) Foo() pulumi.IntOutput { return o.Apply(func (v Nested) int { return v.Foo }).(pulumi.IntOutput) } func (o NestedOutput) Bar() pulumi.StringOutput { return o.Apply(func (v Nested) string { return v.Bar }).(pulumi.StringOutput) } ``` The SDK provides input and output types for primitives, arrays, and maps. 2. Struct-based APIs Instead of providing expected output properties in the input map passed to {Read,Register}Resource and returning the outputs as a map, the user now passes a pointer to a struct that implements one of the Resource interfaces and has appropriately typed and tagged fields that represent its output properties. For example, given a custom resource with an int-typed output "foo" and a string-typed output "bar", we would define the following CustomResource type: ``` type MyResource struct { pulumi.CustomResourceState Foo pulumi.IntOutput `pulumi:"foo"` Bar pulumi.StringOutput `pulumi:"bar"` } ``` And invoke RegisterResource like so: ``` var resource MyResource err := ctx.RegisterResource(tok, name, props, &resource, opts...) ``` Invoke arguments and results are also provided via structs, but use plain-old Go types for their fields: ``` type MyInvokeArgs struct { Foo int `pulumi:"foo"` } type MyInvokeResult struct { Bar string `pulumi:"bar"` } var result MyInvokeResult err := ctx.Invoke(tok, MyInvokeArgs{Foo: 42}, &result, opts...) ``` 3. Ease-of-use of Apply All `Apply` methods now accept an interface{} as the callback type. The provided callback value must have one of the following signatures: func (v T) U func (v T) (U, error) func (ctx context.Context, v T) U func (ctx context.Context, v T) (U, error) T must be assignable from the ElementType of the Output. If U is a type that has a registered Output type, the result of the Apply will be the corresponding Output type. Otherwise, the result of the Apply will be AnyOutput. Fixes https://github.com/pulumi/pulumi/issues/2149. Fixes https://github.com/pulumi/pulumi/issues/3488. Fixes https://github.com/pulumi/pulumi/issues/3487. Fixes https://github.com/pulumi/pulumi-aws/issues/248. Fixes https://github.com/pulumi/pulumi/issues/3492. Fixes https://github.com/pulumi/pulumi/issues/3491. Fixes https://github.com/pulumi/pulumi/issues/3562.
2020-01-18 16:08:37 +01:00
assert.Nil(t, err)
assert.True(t, known)
assert.False(t, secret)
assert.Nil(t, deps)
Redesign the Go SDK resource/input/output system. (#3506) The redesign is focused around providing better static typings and improved ease-of-use for the Go SDK. Most of the redesign revolves around three pivots: - Strongly-typed inputs, especially for nested types - Struct-based resource and invoke APIs - Ease-of-use of Apply 1. Strongly-typed inputs Input is the type of a generic input value for a Pulumi resource. This type is used in conjunction with Output to provide polymorphism over strongly-typed input values. The intended pattern for nested Pulumi value types is to define an input interface and a plain, input, and output variant of the value type that implement the input interface. For example, given a nested Pulumi value type with the following shape: ``` type Nested struct { Foo int Bar string } ``` We would define the following: ``` var nestedType = reflect.TypeOf((*Nested)(nil)).Elem() type NestedInput interface { pulumi.Input ToNestedOutput() NestedOutput ToNestedOutputWithContext(context.Context) NestedOutput } type Nested struct { Foo int `pulumi:"foo"` Bar string `pulumi:"bar"` } type NestedInputValue struct { Foo pulumi.IntInput `pulumi:"foo"` Bar pulumi.StringInput `pulumi:"bar"` } func (NestedInputValue) ElementType() reflect.Type { return nestedType } func (v NestedInputValue) ToNestedOutput() NestedOutput { return pulumi.ToOutput(v).(NestedOutput) } func (v NestedInputValue) ToNestedOutputWithContext(ctx context.Context) NestedOutput { return pulumi.ToOutputWithContext(ctx, v).(NestedOutput) } type NestedOutput struct { *pulumi.OutputState } func (NestedOutput) ElementType() reflect.Type { return nestedType } func (o NestedOutput) ToNestedOutput() NestedOutput { return o } func (o NestedOutput) ToNestedOutputWithContext(ctx context.Context) NestedOutput { return o } func (o NestedOutput) Foo() pulumi.IntOutput { return o.Apply(func (v Nested) int { return v.Foo }).(pulumi.IntOutput) } func (o NestedOutput) Bar() pulumi.StringOutput { return o.Apply(func (v Nested) string { return v.Bar }).(pulumi.StringOutput) } ``` The SDK provides input and output types for primitives, arrays, and maps. 2. Struct-based APIs Instead of providing expected output properties in the input map passed to {Read,Register}Resource and returning the outputs as a map, the user now passes a pointer to a struct that implements one of the Resource interfaces and has appropriately typed and tagged fields that represent its output properties. For example, given a custom resource with an int-typed output "foo" and a string-typed output "bar", we would define the following CustomResource type: ``` type MyResource struct { pulumi.CustomResourceState Foo pulumi.IntOutput `pulumi:"foo"` Bar pulumi.StringOutput `pulumi:"bar"` } ``` And invoke RegisterResource like so: ``` var resource MyResource err := ctx.RegisterResource(tok, name, props, &resource, opts...) ``` Invoke arguments and results are also provided via structs, but use plain-old Go types for their fields: ``` type MyInvokeArgs struct { Foo int `pulumi:"foo"` } type MyInvokeResult struct { Bar string `pulumi:"bar"` } var result MyInvokeResult err := ctx.Invoke(tok, MyInvokeArgs{Foo: 42}, &result, opts...) ``` 3. Ease-of-use of Apply All `Apply` methods now accept an interface{} as the callback type. The provided callback value must have one of the following signatures: func (v T) U func (v T) (U, error) func (ctx context.Context, v T) U func (ctx context.Context, v T) (U, error) T must be assignable from the ElementType of the Output. If U is a type that has a registered Output type, the result of the Apply will be the corresponding Output type. Otherwise, the result of the Apply will be AnyOutput. Fixes https://github.com/pulumi/pulumi/issues/2149. Fixes https://github.com/pulumi/pulumi/issues/3488. Fixes https://github.com/pulumi/pulumi/issues/3487. Fixes https://github.com/pulumi/pulumi-aws/issues/248. Fixes https://github.com/pulumi/pulumi/issues/3492. Fixes https://github.com/pulumi/pulumi/issues/3491. Fixes https://github.com/pulumi/pulumi/issues/3562.
2020-01-18 16:08:37 +01:00
assert.NotNil(t, v)
assert.Equal(t, 42, v.(int))
}
{
out, _, reject := NewOutput()
go func() {
reject(errors.New("boom"))
}()
v, _, _, _, err := await(out)
Redesign the Go SDK resource/input/output system. (#3506) The redesign is focused around providing better static typings and improved ease-of-use for the Go SDK. Most of the redesign revolves around three pivots: - Strongly-typed inputs, especially for nested types - Struct-based resource and invoke APIs - Ease-of-use of Apply 1. Strongly-typed inputs Input is the type of a generic input value for a Pulumi resource. This type is used in conjunction with Output to provide polymorphism over strongly-typed input values. The intended pattern for nested Pulumi value types is to define an input interface and a plain, input, and output variant of the value type that implement the input interface. For example, given a nested Pulumi value type with the following shape: ``` type Nested struct { Foo int Bar string } ``` We would define the following: ``` var nestedType = reflect.TypeOf((*Nested)(nil)).Elem() type NestedInput interface { pulumi.Input ToNestedOutput() NestedOutput ToNestedOutputWithContext(context.Context) NestedOutput } type Nested struct { Foo int `pulumi:"foo"` Bar string `pulumi:"bar"` } type NestedInputValue struct { Foo pulumi.IntInput `pulumi:"foo"` Bar pulumi.StringInput `pulumi:"bar"` } func (NestedInputValue) ElementType() reflect.Type { return nestedType } func (v NestedInputValue) ToNestedOutput() NestedOutput { return pulumi.ToOutput(v).(NestedOutput) } func (v NestedInputValue) ToNestedOutputWithContext(ctx context.Context) NestedOutput { return pulumi.ToOutputWithContext(ctx, v).(NestedOutput) } type NestedOutput struct { *pulumi.OutputState } func (NestedOutput) ElementType() reflect.Type { return nestedType } func (o NestedOutput) ToNestedOutput() NestedOutput { return o } func (o NestedOutput) ToNestedOutputWithContext(ctx context.Context) NestedOutput { return o } func (o NestedOutput) Foo() pulumi.IntOutput { return o.Apply(func (v Nested) int { return v.Foo }).(pulumi.IntOutput) } func (o NestedOutput) Bar() pulumi.StringOutput { return o.Apply(func (v Nested) string { return v.Bar }).(pulumi.StringOutput) } ``` The SDK provides input and output types for primitives, arrays, and maps. 2. Struct-based APIs Instead of providing expected output properties in the input map passed to {Read,Register}Resource and returning the outputs as a map, the user now passes a pointer to a struct that implements one of the Resource interfaces and has appropriately typed and tagged fields that represent its output properties. For example, given a custom resource with an int-typed output "foo" and a string-typed output "bar", we would define the following CustomResource type: ``` type MyResource struct { pulumi.CustomResourceState Foo pulumi.IntOutput `pulumi:"foo"` Bar pulumi.StringOutput `pulumi:"bar"` } ``` And invoke RegisterResource like so: ``` var resource MyResource err := ctx.RegisterResource(tok, name, props, &resource, opts...) ``` Invoke arguments and results are also provided via structs, but use plain-old Go types for their fields: ``` type MyInvokeArgs struct { Foo int `pulumi:"foo"` } type MyInvokeResult struct { Bar string `pulumi:"bar"` } var result MyInvokeResult err := ctx.Invoke(tok, MyInvokeArgs{Foo: 42}, &result, opts...) ``` 3. Ease-of-use of Apply All `Apply` methods now accept an interface{} as the callback type. The provided callback value must have one of the following signatures: func (v T) U func (v T) (U, error) func (ctx context.Context, v T) U func (ctx context.Context, v T) (U, error) T must be assignable from the ElementType of the Output. If U is a type that has a registered Output type, the result of the Apply will be the corresponding Output type. Otherwise, the result of the Apply will be AnyOutput. Fixes https://github.com/pulumi/pulumi/issues/2149. Fixes https://github.com/pulumi/pulumi/issues/3488. Fixes https://github.com/pulumi/pulumi/issues/3487. Fixes https://github.com/pulumi/pulumi-aws/issues/248. Fixes https://github.com/pulumi/pulumi/issues/3492. Fixes https://github.com/pulumi/pulumi/issues/3491. Fixes https://github.com/pulumi/pulumi/issues/3562.
2020-01-18 16:08:37 +01:00
assert.NotNil(t, err)
assert.Nil(t, v)
}
}
func TestArrayOutputs(t *testing.T) {
Await outstanding async work in Go. (#6983) The Pulumi Go SDK does not currently await all outstanding asynchronous work associated with a Pulumi program. Because all relevant asynchronous work is created via the Pulumi SDK, we can track this asynchronous work and ensure that it has all completed prior to returning from `Context.Run`. This is complicated by the fact that many of the existing APIs that are able to create `Output`s--`NewOutput`, `ToOutput`, `Any`, `ToSecret`, and `All`--do not have a `*Context` parameter, and so have no straightforward way to associate themselves with a `*Context`. To address this, these changes add new versions of each of these APIs as methods on `*Context`. Despite these new methods, most Pulumi programs should work without changes: the bulk of `Output`s are created by the SDK itself as part of resource registration, and for `Any` and `All`, we can pick up the context from any `Output`s present in the arguments. The only programs that should require changes are those that create outputs from whole cloth using `NewOutput`, `ToOutput`, or `ToSecret` and create unawaited async work rooted at those outputs. On an implementation level, these changes track asynchronous work using a `sync.WaitGroup` associated with each `*Context`. This `WaitGroup` is passed to each output associated with the context. The SDK increments this `WaitGroup`'s count prior to starting any asynchronous work and decrements it once the work (including any callbacks triggered by the work) is complete. This fixes the Go portion of #3991.
2021-05-14 21:00:21 +02:00
out := ArrayOutput{newOutputState(nil, reflect.TypeOf([]interface{}{}))}
Redesign the Go SDK resource/input/output system. (#3506) The redesign is focused around providing better static typings and improved ease-of-use for the Go SDK. Most of the redesign revolves around three pivots: - Strongly-typed inputs, especially for nested types - Struct-based resource and invoke APIs - Ease-of-use of Apply 1. Strongly-typed inputs Input is the type of a generic input value for a Pulumi resource. This type is used in conjunction with Output to provide polymorphism over strongly-typed input values. The intended pattern for nested Pulumi value types is to define an input interface and a plain, input, and output variant of the value type that implement the input interface. For example, given a nested Pulumi value type with the following shape: ``` type Nested struct { Foo int Bar string } ``` We would define the following: ``` var nestedType = reflect.TypeOf((*Nested)(nil)).Elem() type NestedInput interface { pulumi.Input ToNestedOutput() NestedOutput ToNestedOutputWithContext(context.Context) NestedOutput } type Nested struct { Foo int `pulumi:"foo"` Bar string `pulumi:"bar"` } type NestedInputValue struct { Foo pulumi.IntInput `pulumi:"foo"` Bar pulumi.StringInput `pulumi:"bar"` } func (NestedInputValue) ElementType() reflect.Type { return nestedType } func (v NestedInputValue) ToNestedOutput() NestedOutput { return pulumi.ToOutput(v).(NestedOutput) } func (v NestedInputValue) ToNestedOutputWithContext(ctx context.Context) NestedOutput { return pulumi.ToOutputWithContext(ctx, v).(NestedOutput) } type NestedOutput struct { *pulumi.OutputState } func (NestedOutput) ElementType() reflect.Type { return nestedType } func (o NestedOutput) ToNestedOutput() NestedOutput { return o } func (o NestedOutput) ToNestedOutputWithContext(ctx context.Context) NestedOutput { return o } func (o NestedOutput) Foo() pulumi.IntOutput { return o.Apply(func (v Nested) int { return v.Foo }).(pulumi.IntOutput) } func (o NestedOutput) Bar() pulumi.StringOutput { return o.Apply(func (v Nested) string { return v.Bar }).(pulumi.StringOutput) } ``` The SDK provides input and output types for primitives, arrays, and maps. 2. Struct-based APIs Instead of providing expected output properties in the input map passed to {Read,Register}Resource and returning the outputs as a map, the user now passes a pointer to a struct that implements one of the Resource interfaces and has appropriately typed and tagged fields that represent its output properties. For example, given a custom resource with an int-typed output "foo" and a string-typed output "bar", we would define the following CustomResource type: ``` type MyResource struct { pulumi.CustomResourceState Foo pulumi.IntOutput `pulumi:"foo"` Bar pulumi.StringOutput `pulumi:"bar"` } ``` And invoke RegisterResource like so: ``` var resource MyResource err := ctx.RegisterResource(tok, name, props, &resource, opts...) ``` Invoke arguments and results are also provided via structs, but use plain-old Go types for their fields: ``` type MyInvokeArgs struct { Foo int `pulumi:"foo"` } type MyInvokeResult struct { Bar string `pulumi:"bar"` } var result MyInvokeResult err := ctx.Invoke(tok, MyInvokeArgs{Foo: 42}, &result, opts...) ``` 3. Ease-of-use of Apply All `Apply` methods now accept an interface{} as the callback type. The provided callback value must have one of the following signatures: func (v T) U func (v T) (U, error) func (ctx context.Context, v T) U func (ctx context.Context, v T) (U, error) T must be assignable from the ElementType of the Output. If U is a type that has a registered Output type, the result of the Apply will be the corresponding Output type. Otherwise, the result of the Apply will be AnyOutput. Fixes https://github.com/pulumi/pulumi/issues/2149. Fixes https://github.com/pulumi/pulumi/issues/3488. Fixes https://github.com/pulumi/pulumi/issues/3487. Fixes https://github.com/pulumi/pulumi-aws/issues/248. Fixes https://github.com/pulumi/pulumi/issues/3492. Fixes https://github.com/pulumi/pulumi/issues/3491. Fixes https://github.com/pulumi/pulumi/issues/3562.
2020-01-18 16:08:37 +01:00
go func() {
out.resolve([]interface{}{nil, 0, "x"}, true, false, nil)
Redesign the Go SDK resource/input/output system. (#3506) The redesign is focused around providing better static typings and improved ease-of-use for the Go SDK. Most of the redesign revolves around three pivots: - Strongly-typed inputs, especially for nested types - Struct-based resource and invoke APIs - Ease-of-use of Apply 1. Strongly-typed inputs Input is the type of a generic input value for a Pulumi resource. This type is used in conjunction with Output to provide polymorphism over strongly-typed input values. The intended pattern for nested Pulumi value types is to define an input interface and a plain, input, and output variant of the value type that implement the input interface. For example, given a nested Pulumi value type with the following shape: ``` type Nested struct { Foo int Bar string } ``` We would define the following: ``` var nestedType = reflect.TypeOf((*Nested)(nil)).Elem() type NestedInput interface { pulumi.Input ToNestedOutput() NestedOutput ToNestedOutputWithContext(context.Context) NestedOutput } type Nested struct { Foo int `pulumi:"foo"` Bar string `pulumi:"bar"` } type NestedInputValue struct { Foo pulumi.IntInput `pulumi:"foo"` Bar pulumi.StringInput `pulumi:"bar"` } func (NestedInputValue) ElementType() reflect.Type { return nestedType } func (v NestedInputValue) ToNestedOutput() NestedOutput { return pulumi.ToOutput(v).(NestedOutput) } func (v NestedInputValue) ToNestedOutputWithContext(ctx context.Context) NestedOutput { return pulumi.ToOutputWithContext(ctx, v).(NestedOutput) } type NestedOutput struct { *pulumi.OutputState } func (NestedOutput) ElementType() reflect.Type { return nestedType } func (o NestedOutput) ToNestedOutput() NestedOutput { return o } func (o NestedOutput) ToNestedOutputWithContext(ctx context.Context) NestedOutput { return o } func (o NestedOutput) Foo() pulumi.IntOutput { return o.Apply(func (v Nested) int { return v.Foo }).(pulumi.IntOutput) } func (o NestedOutput) Bar() pulumi.StringOutput { return o.Apply(func (v Nested) string { return v.Bar }).(pulumi.StringOutput) } ``` The SDK provides input and output types for primitives, arrays, and maps. 2. Struct-based APIs Instead of providing expected output properties in the input map passed to {Read,Register}Resource and returning the outputs as a map, the user now passes a pointer to a struct that implements one of the Resource interfaces and has appropriately typed and tagged fields that represent its output properties. For example, given a custom resource with an int-typed output "foo" and a string-typed output "bar", we would define the following CustomResource type: ``` type MyResource struct { pulumi.CustomResourceState Foo pulumi.IntOutput `pulumi:"foo"` Bar pulumi.StringOutput `pulumi:"bar"` } ``` And invoke RegisterResource like so: ``` var resource MyResource err := ctx.RegisterResource(tok, name, props, &resource, opts...) ``` Invoke arguments and results are also provided via structs, but use plain-old Go types for their fields: ``` type MyInvokeArgs struct { Foo int `pulumi:"foo"` } type MyInvokeResult struct { Bar string `pulumi:"bar"` } var result MyInvokeResult err := ctx.Invoke(tok, MyInvokeArgs{Foo: 42}, &result, opts...) ``` 3. Ease-of-use of Apply All `Apply` methods now accept an interface{} as the callback type. The provided callback value must have one of the following signatures: func (v T) U func (v T) (U, error) func (ctx context.Context, v T) U func (ctx context.Context, v T) (U, error) T must be assignable from the ElementType of the Output. If U is a type that has a registered Output type, the result of the Apply will be the corresponding Output type. Otherwise, the result of the Apply will be AnyOutput. Fixes https://github.com/pulumi/pulumi/issues/2149. Fixes https://github.com/pulumi/pulumi/issues/3488. Fixes https://github.com/pulumi/pulumi/issues/3487. Fixes https://github.com/pulumi/pulumi-aws/issues/248. Fixes https://github.com/pulumi/pulumi/issues/3492. Fixes https://github.com/pulumi/pulumi/issues/3491. Fixes https://github.com/pulumi/pulumi/issues/3562.
2020-01-18 16:08:37 +01:00
}()
{
assertApplied(t, out.ApplyT(func(arr []interface{}) (interface{}, error) {
assert.NotNil(t, arr)
if assert.Equal(t, 3, len(arr)) {
assert.Equal(t, nil, arr[0])
assert.Equal(t, 0, arr[1])
assert.Equal(t, "x", arr[2])
}
return nil, nil
}))
}
}
func TestBoolOutputs(t *testing.T) {
Await outstanding async work in Go. (#6983) The Pulumi Go SDK does not currently await all outstanding asynchronous work associated with a Pulumi program. Because all relevant asynchronous work is created via the Pulumi SDK, we can track this asynchronous work and ensure that it has all completed prior to returning from `Context.Run`. This is complicated by the fact that many of the existing APIs that are able to create `Output`s--`NewOutput`, `ToOutput`, `Any`, `ToSecret`, and `All`--do not have a `*Context` parameter, and so have no straightforward way to associate themselves with a `*Context`. To address this, these changes add new versions of each of these APIs as methods on `*Context`. Despite these new methods, most Pulumi programs should work without changes: the bulk of `Output`s are created by the SDK itself as part of resource registration, and for `Any` and `All`, we can pick up the context from any `Output`s present in the arguments. The only programs that should require changes are those that create outputs from whole cloth using `NewOutput`, `ToOutput`, or `ToSecret` and create unawaited async work rooted at those outputs. On an implementation level, these changes track asynchronous work using a `sync.WaitGroup` associated with each `*Context`. This `WaitGroup` is passed to each output associated with the context. The SDK increments this `WaitGroup`'s count prior to starting any asynchronous work and decrements it once the work (including any callbacks triggered by the work) is complete. This fixes the Go portion of #3991.
2021-05-14 21:00:21 +02:00
out := BoolOutput{newOutputState(nil, reflect.TypeOf(false))}
Redesign the Go SDK resource/input/output system. (#3506) The redesign is focused around providing better static typings and improved ease-of-use for the Go SDK. Most of the redesign revolves around three pivots: - Strongly-typed inputs, especially for nested types - Struct-based resource and invoke APIs - Ease-of-use of Apply 1. Strongly-typed inputs Input is the type of a generic input value for a Pulumi resource. This type is used in conjunction with Output to provide polymorphism over strongly-typed input values. The intended pattern for nested Pulumi value types is to define an input interface and a plain, input, and output variant of the value type that implement the input interface. For example, given a nested Pulumi value type with the following shape: ``` type Nested struct { Foo int Bar string } ``` We would define the following: ``` var nestedType = reflect.TypeOf((*Nested)(nil)).Elem() type NestedInput interface { pulumi.Input ToNestedOutput() NestedOutput ToNestedOutputWithContext(context.Context) NestedOutput } type Nested struct { Foo int `pulumi:"foo"` Bar string `pulumi:"bar"` } type NestedInputValue struct { Foo pulumi.IntInput `pulumi:"foo"` Bar pulumi.StringInput `pulumi:"bar"` } func (NestedInputValue) ElementType() reflect.Type { return nestedType } func (v NestedInputValue) ToNestedOutput() NestedOutput { return pulumi.ToOutput(v).(NestedOutput) } func (v NestedInputValue) ToNestedOutputWithContext(ctx context.Context) NestedOutput { return pulumi.ToOutputWithContext(ctx, v).(NestedOutput) } type NestedOutput struct { *pulumi.OutputState } func (NestedOutput) ElementType() reflect.Type { return nestedType } func (o NestedOutput) ToNestedOutput() NestedOutput { return o } func (o NestedOutput) ToNestedOutputWithContext(ctx context.Context) NestedOutput { return o } func (o NestedOutput) Foo() pulumi.IntOutput { return o.Apply(func (v Nested) int { return v.Foo }).(pulumi.IntOutput) } func (o NestedOutput) Bar() pulumi.StringOutput { return o.Apply(func (v Nested) string { return v.Bar }).(pulumi.StringOutput) } ``` The SDK provides input and output types for primitives, arrays, and maps. 2. Struct-based APIs Instead of providing expected output properties in the input map passed to {Read,Register}Resource and returning the outputs as a map, the user now passes a pointer to a struct that implements one of the Resource interfaces and has appropriately typed and tagged fields that represent its output properties. For example, given a custom resource with an int-typed output "foo" and a string-typed output "bar", we would define the following CustomResource type: ``` type MyResource struct { pulumi.CustomResourceState Foo pulumi.IntOutput `pulumi:"foo"` Bar pulumi.StringOutput `pulumi:"bar"` } ``` And invoke RegisterResource like so: ``` var resource MyResource err := ctx.RegisterResource(tok, name, props, &resource, opts...) ``` Invoke arguments and results are also provided via structs, but use plain-old Go types for their fields: ``` type MyInvokeArgs struct { Foo int `pulumi:"foo"` } type MyInvokeResult struct { Bar string `pulumi:"bar"` } var result MyInvokeResult err := ctx.Invoke(tok, MyInvokeArgs{Foo: 42}, &result, opts...) ``` 3. Ease-of-use of Apply All `Apply` methods now accept an interface{} as the callback type. The provided callback value must have one of the following signatures: func (v T) U func (v T) (U, error) func (ctx context.Context, v T) U func (ctx context.Context, v T) (U, error) T must be assignable from the ElementType of the Output. If U is a type that has a registered Output type, the result of the Apply will be the corresponding Output type. Otherwise, the result of the Apply will be AnyOutput. Fixes https://github.com/pulumi/pulumi/issues/2149. Fixes https://github.com/pulumi/pulumi/issues/3488. Fixes https://github.com/pulumi/pulumi/issues/3487. Fixes https://github.com/pulumi/pulumi-aws/issues/248. Fixes https://github.com/pulumi/pulumi/issues/3492. Fixes https://github.com/pulumi/pulumi/issues/3491. Fixes https://github.com/pulumi/pulumi/issues/3562.
2020-01-18 16:08:37 +01:00
go func() {
out.resolve(true, true, false, nil)
Redesign the Go SDK resource/input/output system. (#3506) The redesign is focused around providing better static typings and improved ease-of-use for the Go SDK. Most of the redesign revolves around three pivots: - Strongly-typed inputs, especially for nested types - Struct-based resource and invoke APIs - Ease-of-use of Apply 1. Strongly-typed inputs Input is the type of a generic input value for a Pulumi resource. This type is used in conjunction with Output to provide polymorphism over strongly-typed input values. The intended pattern for nested Pulumi value types is to define an input interface and a plain, input, and output variant of the value type that implement the input interface. For example, given a nested Pulumi value type with the following shape: ``` type Nested struct { Foo int Bar string } ``` We would define the following: ``` var nestedType = reflect.TypeOf((*Nested)(nil)).Elem() type NestedInput interface { pulumi.Input ToNestedOutput() NestedOutput ToNestedOutputWithContext(context.Context) NestedOutput } type Nested struct { Foo int `pulumi:"foo"` Bar string `pulumi:"bar"` } type NestedInputValue struct { Foo pulumi.IntInput `pulumi:"foo"` Bar pulumi.StringInput `pulumi:"bar"` } func (NestedInputValue) ElementType() reflect.Type { return nestedType } func (v NestedInputValue) ToNestedOutput() NestedOutput { return pulumi.ToOutput(v).(NestedOutput) } func (v NestedInputValue) ToNestedOutputWithContext(ctx context.Context) NestedOutput { return pulumi.ToOutputWithContext(ctx, v).(NestedOutput) } type NestedOutput struct { *pulumi.OutputState } func (NestedOutput) ElementType() reflect.Type { return nestedType } func (o NestedOutput) ToNestedOutput() NestedOutput { return o } func (o NestedOutput) ToNestedOutputWithContext(ctx context.Context) NestedOutput { return o } func (o NestedOutput) Foo() pulumi.IntOutput { return o.Apply(func (v Nested) int { return v.Foo }).(pulumi.IntOutput) } func (o NestedOutput) Bar() pulumi.StringOutput { return o.Apply(func (v Nested) string { return v.Bar }).(pulumi.StringOutput) } ``` The SDK provides input and output types for primitives, arrays, and maps. 2. Struct-based APIs Instead of providing expected output properties in the input map passed to {Read,Register}Resource and returning the outputs as a map, the user now passes a pointer to a struct that implements one of the Resource interfaces and has appropriately typed and tagged fields that represent its output properties. For example, given a custom resource with an int-typed output "foo" and a string-typed output "bar", we would define the following CustomResource type: ``` type MyResource struct { pulumi.CustomResourceState Foo pulumi.IntOutput `pulumi:"foo"` Bar pulumi.StringOutput `pulumi:"bar"` } ``` And invoke RegisterResource like so: ``` var resource MyResource err := ctx.RegisterResource(tok, name, props, &resource, opts...) ``` Invoke arguments and results are also provided via structs, but use plain-old Go types for their fields: ``` type MyInvokeArgs struct { Foo int `pulumi:"foo"` } type MyInvokeResult struct { Bar string `pulumi:"bar"` } var result MyInvokeResult err := ctx.Invoke(tok, MyInvokeArgs{Foo: 42}, &result, opts...) ``` 3. Ease-of-use of Apply All `Apply` methods now accept an interface{} as the callback type. The provided callback value must have one of the following signatures: func (v T) U func (v T) (U, error) func (ctx context.Context, v T) U func (ctx context.Context, v T) (U, error) T must be assignable from the ElementType of the Output. If U is a type that has a registered Output type, the result of the Apply will be the corresponding Output type. Otherwise, the result of the Apply will be AnyOutput. Fixes https://github.com/pulumi/pulumi/issues/2149. Fixes https://github.com/pulumi/pulumi/issues/3488. Fixes https://github.com/pulumi/pulumi/issues/3487. Fixes https://github.com/pulumi/pulumi-aws/issues/248. Fixes https://github.com/pulumi/pulumi/issues/3492. Fixes https://github.com/pulumi/pulumi/issues/3491. Fixes https://github.com/pulumi/pulumi/issues/3562.
2020-01-18 16:08:37 +01:00
}()
{
assertApplied(t, out.ApplyT(func(v bool) (interface{}, error) {
assert.True(t, v)
return nil, nil
}))
}
}
func TestMapOutputs(t *testing.T) {
Await outstanding async work in Go. (#6983) The Pulumi Go SDK does not currently await all outstanding asynchronous work associated with a Pulumi program. Because all relevant asynchronous work is created via the Pulumi SDK, we can track this asynchronous work and ensure that it has all completed prior to returning from `Context.Run`. This is complicated by the fact that many of the existing APIs that are able to create `Output`s--`NewOutput`, `ToOutput`, `Any`, `ToSecret`, and `All`--do not have a `*Context` parameter, and so have no straightforward way to associate themselves with a `*Context`. To address this, these changes add new versions of each of these APIs as methods on `*Context`. Despite these new methods, most Pulumi programs should work without changes: the bulk of `Output`s are created by the SDK itself as part of resource registration, and for `Any` and `All`, we can pick up the context from any `Output`s present in the arguments. The only programs that should require changes are those that create outputs from whole cloth using `NewOutput`, `ToOutput`, or `ToSecret` and create unawaited async work rooted at those outputs. On an implementation level, these changes track asynchronous work using a `sync.WaitGroup` associated with each `*Context`. This `WaitGroup` is passed to each output associated with the context. The SDK increments this `WaitGroup`'s count prior to starting any asynchronous work and decrements it once the work (including any callbacks triggered by the work) is complete. This fixes the Go portion of #3991.
2021-05-14 21:00:21 +02:00
out := MapOutput{newOutputState(nil, reflect.TypeOf(map[string]interface{}{}))}
Redesign the Go SDK resource/input/output system. (#3506) The redesign is focused around providing better static typings and improved ease-of-use for the Go SDK. Most of the redesign revolves around three pivots: - Strongly-typed inputs, especially for nested types - Struct-based resource and invoke APIs - Ease-of-use of Apply 1. Strongly-typed inputs Input is the type of a generic input value for a Pulumi resource. This type is used in conjunction with Output to provide polymorphism over strongly-typed input values. The intended pattern for nested Pulumi value types is to define an input interface and a plain, input, and output variant of the value type that implement the input interface. For example, given a nested Pulumi value type with the following shape: ``` type Nested struct { Foo int Bar string } ``` We would define the following: ``` var nestedType = reflect.TypeOf((*Nested)(nil)).Elem() type NestedInput interface { pulumi.Input ToNestedOutput() NestedOutput ToNestedOutputWithContext(context.Context) NestedOutput } type Nested struct { Foo int `pulumi:"foo"` Bar string `pulumi:"bar"` } type NestedInputValue struct { Foo pulumi.IntInput `pulumi:"foo"` Bar pulumi.StringInput `pulumi:"bar"` } func (NestedInputValue) ElementType() reflect.Type { return nestedType } func (v NestedInputValue) ToNestedOutput() NestedOutput { return pulumi.ToOutput(v).(NestedOutput) } func (v NestedInputValue) ToNestedOutputWithContext(ctx context.Context) NestedOutput { return pulumi.ToOutputWithContext(ctx, v).(NestedOutput) } type NestedOutput struct { *pulumi.OutputState } func (NestedOutput) ElementType() reflect.Type { return nestedType } func (o NestedOutput) ToNestedOutput() NestedOutput { return o } func (o NestedOutput) ToNestedOutputWithContext(ctx context.Context) NestedOutput { return o } func (o NestedOutput) Foo() pulumi.IntOutput { return o.Apply(func (v Nested) int { return v.Foo }).(pulumi.IntOutput) } func (o NestedOutput) Bar() pulumi.StringOutput { return o.Apply(func (v Nested) string { return v.Bar }).(pulumi.StringOutput) } ``` The SDK provides input and output types for primitives, arrays, and maps. 2. Struct-based APIs Instead of providing expected output properties in the input map passed to {Read,Register}Resource and returning the outputs as a map, the user now passes a pointer to a struct that implements one of the Resource interfaces and has appropriately typed and tagged fields that represent its output properties. For example, given a custom resource with an int-typed output "foo" and a string-typed output "bar", we would define the following CustomResource type: ``` type MyResource struct { pulumi.CustomResourceState Foo pulumi.IntOutput `pulumi:"foo"` Bar pulumi.StringOutput `pulumi:"bar"` } ``` And invoke RegisterResource like so: ``` var resource MyResource err := ctx.RegisterResource(tok, name, props, &resource, opts...) ``` Invoke arguments and results are also provided via structs, but use plain-old Go types for their fields: ``` type MyInvokeArgs struct { Foo int `pulumi:"foo"` } type MyInvokeResult struct { Bar string `pulumi:"bar"` } var result MyInvokeResult err := ctx.Invoke(tok, MyInvokeArgs{Foo: 42}, &result, opts...) ``` 3. Ease-of-use of Apply All `Apply` methods now accept an interface{} as the callback type. The provided callback value must have one of the following signatures: func (v T) U func (v T) (U, error) func (ctx context.Context, v T) U func (ctx context.Context, v T) (U, error) T must be assignable from the ElementType of the Output. If U is a type that has a registered Output type, the result of the Apply will be the corresponding Output type. Otherwise, the result of the Apply will be AnyOutput. Fixes https://github.com/pulumi/pulumi/issues/2149. Fixes https://github.com/pulumi/pulumi/issues/3488. Fixes https://github.com/pulumi/pulumi/issues/3487. Fixes https://github.com/pulumi/pulumi-aws/issues/248. Fixes https://github.com/pulumi/pulumi/issues/3492. Fixes https://github.com/pulumi/pulumi/issues/3491. Fixes https://github.com/pulumi/pulumi/issues/3562.
2020-01-18 16:08:37 +01:00
go func() {
out.resolve(map[string]interface{}{
"x": 1,
"y": false,
"z": "abc",
}, true, false, nil)
Redesign the Go SDK resource/input/output system. (#3506) The redesign is focused around providing better static typings and improved ease-of-use for the Go SDK. Most of the redesign revolves around three pivots: - Strongly-typed inputs, especially for nested types - Struct-based resource and invoke APIs - Ease-of-use of Apply 1. Strongly-typed inputs Input is the type of a generic input value for a Pulumi resource. This type is used in conjunction with Output to provide polymorphism over strongly-typed input values. The intended pattern for nested Pulumi value types is to define an input interface and a plain, input, and output variant of the value type that implement the input interface. For example, given a nested Pulumi value type with the following shape: ``` type Nested struct { Foo int Bar string } ``` We would define the following: ``` var nestedType = reflect.TypeOf((*Nested)(nil)).Elem() type NestedInput interface { pulumi.Input ToNestedOutput() NestedOutput ToNestedOutputWithContext(context.Context) NestedOutput } type Nested struct { Foo int `pulumi:"foo"` Bar string `pulumi:"bar"` } type NestedInputValue struct { Foo pulumi.IntInput `pulumi:"foo"` Bar pulumi.StringInput `pulumi:"bar"` } func (NestedInputValue) ElementType() reflect.Type { return nestedType } func (v NestedInputValue) ToNestedOutput() NestedOutput { return pulumi.ToOutput(v).(NestedOutput) } func (v NestedInputValue) ToNestedOutputWithContext(ctx context.Context) NestedOutput { return pulumi.ToOutputWithContext(ctx, v).(NestedOutput) } type NestedOutput struct { *pulumi.OutputState } func (NestedOutput) ElementType() reflect.Type { return nestedType } func (o NestedOutput) ToNestedOutput() NestedOutput { return o } func (o NestedOutput) ToNestedOutputWithContext(ctx context.Context) NestedOutput { return o } func (o NestedOutput) Foo() pulumi.IntOutput { return o.Apply(func (v Nested) int { return v.Foo }).(pulumi.IntOutput) } func (o NestedOutput) Bar() pulumi.StringOutput { return o.Apply(func (v Nested) string { return v.Bar }).(pulumi.StringOutput) } ``` The SDK provides input and output types for primitives, arrays, and maps. 2. Struct-based APIs Instead of providing expected output properties in the input map passed to {Read,Register}Resource and returning the outputs as a map, the user now passes a pointer to a struct that implements one of the Resource interfaces and has appropriately typed and tagged fields that represent its output properties. For example, given a custom resource with an int-typed output "foo" and a string-typed output "bar", we would define the following CustomResource type: ``` type MyResource struct { pulumi.CustomResourceState Foo pulumi.IntOutput `pulumi:"foo"` Bar pulumi.StringOutput `pulumi:"bar"` } ``` And invoke RegisterResource like so: ``` var resource MyResource err := ctx.RegisterResource(tok, name, props, &resource, opts...) ``` Invoke arguments and results are also provided via structs, but use plain-old Go types for their fields: ``` type MyInvokeArgs struct { Foo int `pulumi:"foo"` } type MyInvokeResult struct { Bar string `pulumi:"bar"` } var result MyInvokeResult err := ctx.Invoke(tok, MyInvokeArgs{Foo: 42}, &result, opts...) ``` 3. Ease-of-use of Apply All `Apply` methods now accept an interface{} as the callback type. The provided callback value must have one of the following signatures: func (v T) U func (v T) (U, error) func (ctx context.Context, v T) U func (ctx context.Context, v T) (U, error) T must be assignable from the ElementType of the Output. If U is a type that has a registered Output type, the result of the Apply will be the corresponding Output type. Otherwise, the result of the Apply will be AnyOutput. Fixes https://github.com/pulumi/pulumi/issues/2149. Fixes https://github.com/pulumi/pulumi/issues/3488. Fixes https://github.com/pulumi/pulumi/issues/3487. Fixes https://github.com/pulumi/pulumi-aws/issues/248. Fixes https://github.com/pulumi/pulumi/issues/3492. Fixes https://github.com/pulumi/pulumi/issues/3491. Fixes https://github.com/pulumi/pulumi/issues/3562.
2020-01-18 16:08:37 +01:00
}()
{
assertApplied(t, out.ApplyT(func(v map[string]interface{}) (interface{}, error) {
assert.NotNil(t, v)
assert.Equal(t, 1, v["x"])
assert.Equal(t, false, v["y"])
assert.Equal(t, "abc", v["z"])
return nil, nil
}))
}
}
func TestNumberOutputs(t *testing.T) {
Await outstanding async work in Go. (#6983) The Pulumi Go SDK does not currently await all outstanding asynchronous work associated with a Pulumi program. Because all relevant asynchronous work is created via the Pulumi SDK, we can track this asynchronous work and ensure that it has all completed prior to returning from `Context.Run`. This is complicated by the fact that many of the existing APIs that are able to create `Output`s--`NewOutput`, `ToOutput`, `Any`, `ToSecret`, and `All`--do not have a `*Context` parameter, and so have no straightforward way to associate themselves with a `*Context`. To address this, these changes add new versions of each of these APIs as methods on `*Context`. Despite these new methods, most Pulumi programs should work without changes: the bulk of `Output`s are created by the SDK itself as part of resource registration, and for `Any` and `All`, we can pick up the context from any `Output`s present in the arguments. The only programs that should require changes are those that create outputs from whole cloth using `NewOutput`, `ToOutput`, or `ToSecret` and create unawaited async work rooted at those outputs. On an implementation level, these changes track asynchronous work using a `sync.WaitGroup` associated with each `*Context`. This `WaitGroup` is passed to each output associated with the context. The SDK increments this `WaitGroup`'s count prior to starting any asynchronous work and decrements it once the work (including any callbacks triggered by the work) is complete. This fixes the Go portion of #3991.
2021-05-14 21:00:21 +02:00
out := Float64Output{newOutputState(nil, reflect.TypeOf(float64(0)))}
Redesign the Go SDK resource/input/output system. (#3506) The redesign is focused around providing better static typings and improved ease-of-use for the Go SDK. Most of the redesign revolves around three pivots: - Strongly-typed inputs, especially for nested types - Struct-based resource and invoke APIs - Ease-of-use of Apply 1. Strongly-typed inputs Input is the type of a generic input value for a Pulumi resource. This type is used in conjunction with Output to provide polymorphism over strongly-typed input values. The intended pattern for nested Pulumi value types is to define an input interface and a plain, input, and output variant of the value type that implement the input interface. For example, given a nested Pulumi value type with the following shape: ``` type Nested struct { Foo int Bar string } ``` We would define the following: ``` var nestedType = reflect.TypeOf((*Nested)(nil)).Elem() type NestedInput interface { pulumi.Input ToNestedOutput() NestedOutput ToNestedOutputWithContext(context.Context) NestedOutput } type Nested struct { Foo int `pulumi:"foo"` Bar string `pulumi:"bar"` } type NestedInputValue struct { Foo pulumi.IntInput `pulumi:"foo"` Bar pulumi.StringInput `pulumi:"bar"` } func (NestedInputValue) ElementType() reflect.Type { return nestedType } func (v NestedInputValue) ToNestedOutput() NestedOutput { return pulumi.ToOutput(v).(NestedOutput) } func (v NestedInputValue) ToNestedOutputWithContext(ctx context.Context) NestedOutput { return pulumi.ToOutputWithContext(ctx, v).(NestedOutput) } type NestedOutput struct { *pulumi.OutputState } func (NestedOutput) ElementType() reflect.Type { return nestedType } func (o NestedOutput) ToNestedOutput() NestedOutput { return o } func (o NestedOutput) ToNestedOutputWithContext(ctx context.Context) NestedOutput { return o } func (o NestedOutput) Foo() pulumi.IntOutput { return o.Apply(func (v Nested) int { return v.Foo }).(pulumi.IntOutput) } func (o NestedOutput) Bar() pulumi.StringOutput { return o.Apply(func (v Nested) string { return v.Bar }).(pulumi.StringOutput) } ``` The SDK provides input and output types for primitives, arrays, and maps. 2. Struct-based APIs Instead of providing expected output properties in the input map passed to {Read,Register}Resource and returning the outputs as a map, the user now passes a pointer to a struct that implements one of the Resource interfaces and has appropriately typed and tagged fields that represent its output properties. For example, given a custom resource with an int-typed output "foo" and a string-typed output "bar", we would define the following CustomResource type: ``` type MyResource struct { pulumi.CustomResourceState Foo pulumi.IntOutput `pulumi:"foo"` Bar pulumi.StringOutput `pulumi:"bar"` } ``` And invoke RegisterResource like so: ``` var resource MyResource err := ctx.RegisterResource(tok, name, props, &resource, opts...) ``` Invoke arguments and results are also provided via structs, but use plain-old Go types for their fields: ``` type MyInvokeArgs struct { Foo int `pulumi:"foo"` } type MyInvokeResult struct { Bar string `pulumi:"bar"` } var result MyInvokeResult err := ctx.Invoke(tok, MyInvokeArgs{Foo: 42}, &result, opts...) ``` 3. Ease-of-use of Apply All `Apply` methods now accept an interface{} as the callback type. The provided callback value must have one of the following signatures: func (v T) U func (v T) (U, error) func (ctx context.Context, v T) U func (ctx context.Context, v T) (U, error) T must be assignable from the ElementType of the Output. If U is a type that has a registered Output type, the result of the Apply will be the corresponding Output type. Otherwise, the result of the Apply will be AnyOutput. Fixes https://github.com/pulumi/pulumi/issues/2149. Fixes https://github.com/pulumi/pulumi/issues/3488. Fixes https://github.com/pulumi/pulumi/issues/3487. Fixes https://github.com/pulumi/pulumi-aws/issues/248. Fixes https://github.com/pulumi/pulumi/issues/3492. Fixes https://github.com/pulumi/pulumi/issues/3491. Fixes https://github.com/pulumi/pulumi/issues/3562.
2020-01-18 16:08:37 +01:00
go func() {
out.resolve(42.345, true, false, nil)
Redesign the Go SDK resource/input/output system. (#3506) The redesign is focused around providing better static typings and improved ease-of-use for the Go SDK. Most of the redesign revolves around three pivots: - Strongly-typed inputs, especially for nested types - Struct-based resource and invoke APIs - Ease-of-use of Apply 1. Strongly-typed inputs Input is the type of a generic input value for a Pulumi resource. This type is used in conjunction with Output to provide polymorphism over strongly-typed input values. The intended pattern for nested Pulumi value types is to define an input interface and a plain, input, and output variant of the value type that implement the input interface. For example, given a nested Pulumi value type with the following shape: ``` type Nested struct { Foo int Bar string } ``` We would define the following: ``` var nestedType = reflect.TypeOf((*Nested)(nil)).Elem() type NestedInput interface { pulumi.Input ToNestedOutput() NestedOutput ToNestedOutputWithContext(context.Context) NestedOutput } type Nested struct { Foo int `pulumi:"foo"` Bar string `pulumi:"bar"` } type NestedInputValue struct { Foo pulumi.IntInput `pulumi:"foo"` Bar pulumi.StringInput `pulumi:"bar"` } func (NestedInputValue) ElementType() reflect.Type { return nestedType } func (v NestedInputValue) ToNestedOutput() NestedOutput { return pulumi.ToOutput(v).(NestedOutput) } func (v NestedInputValue) ToNestedOutputWithContext(ctx context.Context) NestedOutput { return pulumi.ToOutputWithContext(ctx, v).(NestedOutput) } type NestedOutput struct { *pulumi.OutputState } func (NestedOutput) ElementType() reflect.Type { return nestedType } func (o NestedOutput) ToNestedOutput() NestedOutput { return o } func (o NestedOutput) ToNestedOutputWithContext(ctx context.Context) NestedOutput { return o } func (o NestedOutput) Foo() pulumi.IntOutput { return o.Apply(func (v Nested) int { return v.Foo }).(pulumi.IntOutput) } func (o NestedOutput) Bar() pulumi.StringOutput { return o.Apply(func (v Nested) string { return v.Bar }).(pulumi.StringOutput) } ``` The SDK provides input and output types for primitives, arrays, and maps. 2. Struct-based APIs Instead of providing expected output properties in the input map passed to {Read,Register}Resource and returning the outputs as a map, the user now passes a pointer to a struct that implements one of the Resource interfaces and has appropriately typed and tagged fields that represent its output properties. For example, given a custom resource with an int-typed output "foo" and a string-typed output "bar", we would define the following CustomResource type: ``` type MyResource struct { pulumi.CustomResourceState Foo pulumi.IntOutput `pulumi:"foo"` Bar pulumi.StringOutput `pulumi:"bar"` } ``` And invoke RegisterResource like so: ``` var resource MyResource err := ctx.RegisterResource(tok, name, props, &resource, opts...) ``` Invoke arguments and results are also provided via structs, but use plain-old Go types for their fields: ``` type MyInvokeArgs struct { Foo int `pulumi:"foo"` } type MyInvokeResult struct { Bar string `pulumi:"bar"` } var result MyInvokeResult err := ctx.Invoke(tok, MyInvokeArgs{Foo: 42}, &result, opts...) ``` 3. Ease-of-use of Apply All `Apply` methods now accept an interface{} as the callback type. The provided callback value must have one of the following signatures: func (v T) U func (v T) (U, error) func (ctx context.Context, v T) U func (ctx context.Context, v T) (U, error) T must be assignable from the ElementType of the Output. If U is a type that has a registered Output type, the result of the Apply will be the corresponding Output type. Otherwise, the result of the Apply will be AnyOutput. Fixes https://github.com/pulumi/pulumi/issues/2149. Fixes https://github.com/pulumi/pulumi/issues/3488. Fixes https://github.com/pulumi/pulumi/issues/3487. Fixes https://github.com/pulumi/pulumi-aws/issues/248. Fixes https://github.com/pulumi/pulumi/issues/3492. Fixes https://github.com/pulumi/pulumi/issues/3491. Fixes https://github.com/pulumi/pulumi/issues/3562.
2020-01-18 16:08:37 +01:00
}()
{
assertApplied(t, out.ApplyT(func(v float64) (interface{}, error) {
assert.Equal(t, 42.345, v)
return nil, nil
}))
}
}
func TestStringOutputs(t *testing.T) {
Await outstanding async work in Go. (#6983) The Pulumi Go SDK does not currently await all outstanding asynchronous work associated with a Pulumi program. Because all relevant asynchronous work is created via the Pulumi SDK, we can track this asynchronous work and ensure that it has all completed prior to returning from `Context.Run`. This is complicated by the fact that many of the existing APIs that are able to create `Output`s--`NewOutput`, `ToOutput`, `Any`, `ToSecret`, and `All`--do not have a `*Context` parameter, and so have no straightforward way to associate themselves with a `*Context`. To address this, these changes add new versions of each of these APIs as methods on `*Context`. Despite these new methods, most Pulumi programs should work without changes: the bulk of `Output`s are created by the SDK itself as part of resource registration, and for `Any` and `All`, we can pick up the context from any `Output`s present in the arguments. The only programs that should require changes are those that create outputs from whole cloth using `NewOutput`, `ToOutput`, or `ToSecret` and create unawaited async work rooted at those outputs. On an implementation level, these changes track asynchronous work using a `sync.WaitGroup` associated with each `*Context`. This `WaitGroup` is passed to each output associated with the context. The SDK increments this `WaitGroup`'s count prior to starting any asynchronous work and decrements it once the work (including any callbacks triggered by the work) is complete. This fixes the Go portion of #3991.
2021-05-14 21:00:21 +02:00
out := StringOutput{newOutputState(nil, reflect.TypeOf(""))}
Redesign the Go SDK resource/input/output system. (#3506) The redesign is focused around providing better static typings and improved ease-of-use for the Go SDK. Most of the redesign revolves around three pivots: - Strongly-typed inputs, especially for nested types - Struct-based resource and invoke APIs - Ease-of-use of Apply 1. Strongly-typed inputs Input is the type of a generic input value for a Pulumi resource. This type is used in conjunction with Output to provide polymorphism over strongly-typed input values. The intended pattern for nested Pulumi value types is to define an input interface and a plain, input, and output variant of the value type that implement the input interface. For example, given a nested Pulumi value type with the following shape: ``` type Nested struct { Foo int Bar string } ``` We would define the following: ``` var nestedType = reflect.TypeOf((*Nested)(nil)).Elem() type NestedInput interface { pulumi.Input ToNestedOutput() NestedOutput ToNestedOutputWithContext(context.Context) NestedOutput } type Nested struct { Foo int `pulumi:"foo"` Bar string `pulumi:"bar"` } type NestedInputValue struct { Foo pulumi.IntInput `pulumi:"foo"` Bar pulumi.StringInput `pulumi:"bar"` } func (NestedInputValue) ElementType() reflect.Type { return nestedType } func (v NestedInputValue) ToNestedOutput() NestedOutput { return pulumi.ToOutput(v).(NestedOutput) } func (v NestedInputValue) ToNestedOutputWithContext(ctx context.Context) NestedOutput { return pulumi.ToOutputWithContext(ctx, v).(NestedOutput) } type NestedOutput struct { *pulumi.OutputState } func (NestedOutput) ElementType() reflect.Type { return nestedType } func (o NestedOutput) ToNestedOutput() NestedOutput { return o } func (o NestedOutput) ToNestedOutputWithContext(ctx context.Context) NestedOutput { return o } func (o NestedOutput) Foo() pulumi.IntOutput { return o.Apply(func (v Nested) int { return v.Foo }).(pulumi.IntOutput) } func (o NestedOutput) Bar() pulumi.StringOutput { return o.Apply(func (v Nested) string { return v.Bar }).(pulumi.StringOutput) } ``` The SDK provides input and output types for primitives, arrays, and maps. 2. Struct-based APIs Instead of providing expected output properties in the input map passed to {Read,Register}Resource and returning the outputs as a map, the user now passes a pointer to a struct that implements one of the Resource interfaces and has appropriately typed and tagged fields that represent its output properties. For example, given a custom resource with an int-typed output "foo" and a string-typed output "bar", we would define the following CustomResource type: ``` type MyResource struct { pulumi.CustomResourceState Foo pulumi.IntOutput `pulumi:"foo"` Bar pulumi.StringOutput `pulumi:"bar"` } ``` And invoke RegisterResource like so: ``` var resource MyResource err := ctx.RegisterResource(tok, name, props, &resource, opts...) ``` Invoke arguments and results are also provided via structs, but use plain-old Go types for their fields: ``` type MyInvokeArgs struct { Foo int `pulumi:"foo"` } type MyInvokeResult struct { Bar string `pulumi:"bar"` } var result MyInvokeResult err := ctx.Invoke(tok, MyInvokeArgs{Foo: 42}, &result, opts...) ``` 3. Ease-of-use of Apply All `Apply` methods now accept an interface{} as the callback type. The provided callback value must have one of the following signatures: func (v T) U func (v T) (U, error) func (ctx context.Context, v T) U func (ctx context.Context, v T) (U, error) T must be assignable from the ElementType of the Output. If U is a type that has a registered Output type, the result of the Apply will be the corresponding Output type. Otherwise, the result of the Apply will be AnyOutput. Fixes https://github.com/pulumi/pulumi/issues/2149. Fixes https://github.com/pulumi/pulumi/issues/3488. Fixes https://github.com/pulumi/pulumi/issues/3487. Fixes https://github.com/pulumi/pulumi-aws/issues/248. Fixes https://github.com/pulumi/pulumi/issues/3492. Fixes https://github.com/pulumi/pulumi/issues/3491. Fixes https://github.com/pulumi/pulumi/issues/3562.
2020-01-18 16:08:37 +01:00
go func() {
out.resolve("a stringy output", true, false, nil)
Redesign the Go SDK resource/input/output system. (#3506) The redesign is focused around providing better static typings and improved ease-of-use for the Go SDK. Most of the redesign revolves around three pivots: - Strongly-typed inputs, especially for nested types - Struct-based resource and invoke APIs - Ease-of-use of Apply 1. Strongly-typed inputs Input is the type of a generic input value for a Pulumi resource. This type is used in conjunction with Output to provide polymorphism over strongly-typed input values. The intended pattern for nested Pulumi value types is to define an input interface and a plain, input, and output variant of the value type that implement the input interface. For example, given a nested Pulumi value type with the following shape: ``` type Nested struct { Foo int Bar string } ``` We would define the following: ``` var nestedType = reflect.TypeOf((*Nested)(nil)).Elem() type NestedInput interface { pulumi.Input ToNestedOutput() NestedOutput ToNestedOutputWithContext(context.Context) NestedOutput } type Nested struct { Foo int `pulumi:"foo"` Bar string `pulumi:"bar"` } type NestedInputValue struct { Foo pulumi.IntInput `pulumi:"foo"` Bar pulumi.StringInput `pulumi:"bar"` } func (NestedInputValue) ElementType() reflect.Type { return nestedType } func (v NestedInputValue) ToNestedOutput() NestedOutput { return pulumi.ToOutput(v).(NestedOutput) } func (v NestedInputValue) ToNestedOutputWithContext(ctx context.Context) NestedOutput { return pulumi.ToOutputWithContext(ctx, v).(NestedOutput) } type NestedOutput struct { *pulumi.OutputState } func (NestedOutput) ElementType() reflect.Type { return nestedType } func (o NestedOutput) ToNestedOutput() NestedOutput { return o } func (o NestedOutput) ToNestedOutputWithContext(ctx context.Context) NestedOutput { return o } func (o NestedOutput) Foo() pulumi.IntOutput { return o.Apply(func (v Nested) int { return v.Foo }).(pulumi.IntOutput) } func (o NestedOutput) Bar() pulumi.StringOutput { return o.Apply(func (v Nested) string { return v.Bar }).(pulumi.StringOutput) } ``` The SDK provides input and output types for primitives, arrays, and maps. 2. Struct-based APIs Instead of providing expected output properties in the input map passed to {Read,Register}Resource and returning the outputs as a map, the user now passes a pointer to a struct that implements one of the Resource interfaces and has appropriately typed and tagged fields that represent its output properties. For example, given a custom resource with an int-typed output "foo" and a string-typed output "bar", we would define the following CustomResource type: ``` type MyResource struct { pulumi.CustomResourceState Foo pulumi.IntOutput `pulumi:"foo"` Bar pulumi.StringOutput `pulumi:"bar"` } ``` And invoke RegisterResource like so: ``` var resource MyResource err := ctx.RegisterResource(tok, name, props, &resource, opts...) ``` Invoke arguments and results are also provided via structs, but use plain-old Go types for their fields: ``` type MyInvokeArgs struct { Foo int `pulumi:"foo"` } type MyInvokeResult struct { Bar string `pulumi:"bar"` } var result MyInvokeResult err := ctx.Invoke(tok, MyInvokeArgs{Foo: 42}, &result, opts...) ``` 3. Ease-of-use of Apply All `Apply` methods now accept an interface{} as the callback type. The provided callback value must have one of the following signatures: func (v T) U func (v T) (U, error) func (ctx context.Context, v T) U func (ctx context.Context, v T) (U, error) T must be assignable from the ElementType of the Output. If U is a type that has a registered Output type, the result of the Apply will be the corresponding Output type. Otherwise, the result of the Apply will be AnyOutput. Fixes https://github.com/pulumi/pulumi/issues/2149. Fixes https://github.com/pulumi/pulumi/issues/3488. Fixes https://github.com/pulumi/pulumi/issues/3487. Fixes https://github.com/pulumi/pulumi-aws/issues/248. Fixes https://github.com/pulumi/pulumi/issues/3492. Fixes https://github.com/pulumi/pulumi/issues/3491. Fixes https://github.com/pulumi/pulumi/issues/3562.
2020-01-18 16:08:37 +01:00
}()
{
assertApplied(t, out.ApplyT(func(v string) (interface{}, error) {
assert.Equal(t, "a stringy output", v)
return nil, nil
}))
}
}
func TestResolveOutputToOutput(t *testing.T) {
// Test that resolving an output to an output yields the value, not the output.
{
out, resolve, _ := NewOutput()
go func() {
other, resolveOther, _ := NewOutput()
resolve(other)
go func() { resolveOther(99) }()
}()
assertApplied(t, out.ApplyT(func(v interface{}) (interface{}, error) {
assert.Equal(t, v, 99)
return nil, nil
}))
}
// Similarly, test that resolving an output to a rejected output yields an error.
{
out, resolve, _ := NewOutput()
go func() {
other, _, rejectOther := NewOutput()
resolve(other)
go func() { rejectOther(errors.New("boom")) }()
}()
v, _, _, _, err := await(out)
Redesign the Go SDK resource/input/output system. (#3506) The redesign is focused around providing better static typings and improved ease-of-use for the Go SDK. Most of the redesign revolves around three pivots: - Strongly-typed inputs, especially for nested types - Struct-based resource and invoke APIs - Ease-of-use of Apply 1. Strongly-typed inputs Input is the type of a generic input value for a Pulumi resource. This type is used in conjunction with Output to provide polymorphism over strongly-typed input values. The intended pattern for nested Pulumi value types is to define an input interface and a plain, input, and output variant of the value type that implement the input interface. For example, given a nested Pulumi value type with the following shape: ``` type Nested struct { Foo int Bar string } ``` We would define the following: ``` var nestedType = reflect.TypeOf((*Nested)(nil)).Elem() type NestedInput interface { pulumi.Input ToNestedOutput() NestedOutput ToNestedOutputWithContext(context.Context) NestedOutput } type Nested struct { Foo int `pulumi:"foo"` Bar string `pulumi:"bar"` } type NestedInputValue struct { Foo pulumi.IntInput `pulumi:"foo"` Bar pulumi.StringInput `pulumi:"bar"` } func (NestedInputValue) ElementType() reflect.Type { return nestedType } func (v NestedInputValue) ToNestedOutput() NestedOutput { return pulumi.ToOutput(v).(NestedOutput) } func (v NestedInputValue) ToNestedOutputWithContext(ctx context.Context) NestedOutput { return pulumi.ToOutputWithContext(ctx, v).(NestedOutput) } type NestedOutput struct { *pulumi.OutputState } func (NestedOutput) ElementType() reflect.Type { return nestedType } func (o NestedOutput) ToNestedOutput() NestedOutput { return o } func (o NestedOutput) ToNestedOutputWithContext(ctx context.Context) NestedOutput { return o } func (o NestedOutput) Foo() pulumi.IntOutput { return o.Apply(func (v Nested) int { return v.Foo }).(pulumi.IntOutput) } func (o NestedOutput) Bar() pulumi.StringOutput { return o.Apply(func (v Nested) string { return v.Bar }).(pulumi.StringOutput) } ``` The SDK provides input and output types for primitives, arrays, and maps. 2. Struct-based APIs Instead of providing expected output properties in the input map passed to {Read,Register}Resource and returning the outputs as a map, the user now passes a pointer to a struct that implements one of the Resource interfaces and has appropriately typed and tagged fields that represent its output properties. For example, given a custom resource with an int-typed output "foo" and a string-typed output "bar", we would define the following CustomResource type: ``` type MyResource struct { pulumi.CustomResourceState Foo pulumi.IntOutput `pulumi:"foo"` Bar pulumi.StringOutput `pulumi:"bar"` } ``` And invoke RegisterResource like so: ``` var resource MyResource err := ctx.RegisterResource(tok, name, props, &resource, opts...) ``` Invoke arguments and results are also provided via structs, but use plain-old Go types for their fields: ``` type MyInvokeArgs struct { Foo int `pulumi:"foo"` } type MyInvokeResult struct { Bar string `pulumi:"bar"` } var result MyInvokeResult err := ctx.Invoke(tok, MyInvokeArgs{Foo: 42}, &result, opts...) ``` 3. Ease-of-use of Apply All `Apply` methods now accept an interface{} as the callback type. The provided callback value must have one of the following signatures: func (v T) U func (v T) (U, error) func (ctx context.Context, v T) U func (ctx context.Context, v T) (U, error) T must be assignable from the ElementType of the Output. If U is a type that has a registered Output type, the result of the Apply will be the corresponding Output type. Otherwise, the result of the Apply will be AnyOutput. Fixes https://github.com/pulumi/pulumi/issues/2149. Fixes https://github.com/pulumi/pulumi/issues/3488. Fixes https://github.com/pulumi/pulumi/issues/3487. Fixes https://github.com/pulumi/pulumi-aws/issues/248. Fixes https://github.com/pulumi/pulumi/issues/3492. Fixes https://github.com/pulumi/pulumi/issues/3491. Fixes https://github.com/pulumi/pulumi/issues/3562.
2020-01-18 16:08:37 +01:00
assert.NotNil(t, err)
assert.Nil(t, v)
}
}
// Test that ToOutput works with a struct type.
func TestToOutputStruct(t *testing.T) {
out := ToOutput(nestedTypeInputs{Foo: String("bar"), Bar: Int(42)})
_, ok := out.(nestedTypeOutput)
assert.True(t, ok)
v, known, secret, deps, err := await(out)
Redesign the Go SDK resource/input/output system. (#3506) The redesign is focused around providing better static typings and improved ease-of-use for the Go SDK. Most of the redesign revolves around three pivots: - Strongly-typed inputs, especially for nested types - Struct-based resource and invoke APIs - Ease-of-use of Apply 1. Strongly-typed inputs Input is the type of a generic input value for a Pulumi resource. This type is used in conjunction with Output to provide polymorphism over strongly-typed input values. The intended pattern for nested Pulumi value types is to define an input interface and a plain, input, and output variant of the value type that implement the input interface. For example, given a nested Pulumi value type with the following shape: ``` type Nested struct { Foo int Bar string } ``` We would define the following: ``` var nestedType = reflect.TypeOf((*Nested)(nil)).Elem() type NestedInput interface { pulumi.Input ToNestedOutput() NestedOutput ToNestedOutputWithContext(context.Context) NestedOutput } type Nested struct { Foo int `pulumi:"foo"` Bar string `pulumi:"bar"` } type NestedInputValue struct { Foo pulumi.IntInput `pulumi:"foo"` Bar pulumi.StringInput `pulumi:"bar"` } func (NestedInputValue) ElementType() reflect.Type { return nestedType } func (v NestedInputValue) ToNestedOutput() NestedOutput { return pulumi.ToOutput(v).(NestedOutput) } func (v NestedInputValue) ToNestedOutputWithContext(ctx context.Context) NestedOutput { return pulumi.ToOutputWithContext(ctx, v).(NestedOutput) } type NestedOutput struct { *pulumi.OutputState } func (NestedOutput) ElementType() reflect.Type { return nestedType } func (o NestedOutput) ToNestedOutput() NestedOutput { return o } func (o NestedOutput) ToNestedOutputWithContext(ctx context.Context) NestedOutput { return o } func (o NestedOutput) Foo() pulumi.IntOutput { return o.Apply(func (v Nested) int { return v.Foo }).(pulumi.IntOutput) } func (o NestedOutput) Bar() pulumi.StringOutput { return o.Apply(func (v Nested) string { return v.Bar }).(pulumi.StringOutput) } ``` The SDK provides input and output types for primitives, arrays, and maps. 2. Struct-based APIs Instead of providing expected output properties in the input map passed to {Read,Register}Resource and returning the outputs as a map, the user now passes a pointer to a struct that implements one of the Resource interfaces and has appropriately typed and tagged fields that represent its output properties. For example, given a custom resource with an int-typed output "foo" and a string-typed output "bar", we would define the following CustomResource type: ``` type MyResource struct { pulumi.CustomResourceState Foo pulumi.IntOutput `pulumi:"foo"` Bar pulumi.StringOutput `pulumi:"bar"` } ``` And invoke RegisterResource like so: ``` var resource MyResource err := ctx.RegisterResource(tok, name, props, &resource, opts...) ``` Invoke arguments and results are also provided via structs, but use plain-old Go types for their fields: ``` type MyInvokeArgs struct { Foo int `pulumi:"foo"` } type MyInvokeResult struct { Bar string `pulumi:"bar"` } var result MyInvokeResult err := ctx.Invoke(tok, MyInvokeArgs{Foo: 42}, &result, opts...) ``` 3. Ease-of-use of Apply All `Apply` methods now accept an interface{} as the callback type. The provided callback value must have one of the following signatures: func (v T) U func (v T) (U, error) func (ctx context.Context, v T) U func (ctx context.Context, v T) (U, error) T must be assignable from the ElementType of the Output. If U is a type that has a registered Output type, the result of the Apply will be the corresponding Output type. Otherwise, the result of the Apply will be AnyOutput. Fixes https://github.com/pulumi/pulumi/issues/2149. Fixes https://github.com/pulumi/pulumi/issues/3488. Fixes https://github.com/pulumi/pulumi/issues/3487. Fixes https://github.com/pulumi/pulumi-aws/issues/248. Fixes https://github.com/pulumi/pulumi/issues/3492. Fixes https://github.com/pulumi/pulumi/issues/3491. Fixes https://github.com/pulumi/pulumi/issues/3562.
2020-01-18 16:08:37 +01:00
assert.True(t, known)
assert.False(t, secret)
assert.Nil(t, deps)
Redesign the Go SDK resource/input/output system. (#3506) The redesign is focused around providing better static typings and improved ease-of-use for the Go SDK. Most of the redesign revolves around three pivots: - Strongly-typed inputs, especially for nested types - Struct-based resource and invoke APIs - Ease-of-use of Apply 1. Strongly-typed inputs Input is the type of a generic input value for a Pulumi resource. This type is used in conjunction with Output to provide polymorphism over strongly-typed input values. The intended pattern for nested Pulumi value types is to define an input interface and a plain, input, and output variant of the value type that implement the input interface. For example, given a nested Pulumi value type with the following shape: ``` type Nested struct { Foo int Bar string } ``` We would define the following: ``` var nestedType = reflect.TypeOf((*Nested)(nil)).Elem() type NestedInput interface { pulumi.Input ToNestedOutput() NestedOutput ToNestedOutputWithContext(context.Context) NestedOutput } type Nested struct { Foo int `pulumi:"foo"` Bar string `pulumi:"bar"` } type NestedInputValue struct { Foo pulumi.IntInput `pulumi:"foo"` Bar pulumi.StringInput `pulumi:"bar"` } func (NestedInputValue) ElementType() reflect.Type { return nestedType } func (v NestedInputValue) ToNestedOutput() NestedOutput { return pulumi.ToOutput(v).(NestedOutput) } func (v NestedInputValue) ToNestedOutputWithContext(ctx context.Context) NestedOutput { return pulumi.ToOutputWithContext(ctx, v).(NestedOutput) } type NestedOutput struct { *pulumi.OutputState } func (NestedOutput) ElementType() reflect.Type { return nestedType } func (o NestedOutput) ToNestedOutput() NestedOutput { return o } func (o NestedOutput) ToNestedOutputWithContext(ctx context.Context) NestedOutput { return o } func (o NestedOutput) Foo() pulumi.IntOutput { return o.Apply(func (v Nested) int { return v.Foo }).(pulumi.IntOutput) } func (o NestedOutput) Bar() pulumi.StringOutput { return o.Apply(func (v Nested) string { return v.Bar }).(pulumi.StringOutput) } ``` The SDK provides input and output types for primitives, arrays, and maps. 2. Struct-based APIs Instead of providing expected output properties in the input map passed to {Read,Register}Resource and returning the outputs as a map, the user now passes a pointer to a struct that implements one of the Resource interfaces and has appropriately typed and tagged fields that represent its output properties. For example, given a custom resource with an int-typed output "foo" and a string-typed output "bar", we would define the following CustomResource type: ``` type MyResource struct { pulumi.CustomResourceState Foo pulumi.IntOutput `pulumi:"foo"` Bar pulumi.StringOutput `pulumi:"bar"` } ``` And invoke RegisterResource like so: ``` var resource MyResource err := ctx.RegisterResource(tok, name, props, &resource, opts...) ``` Invoke arguments and results are also provided via structs, but use plain-old Go types for their fields: ``` type MyInvokeArgs struct { Foo int `pulumi:"foo"` } type MyInvokeResult struct { Bar string `pulumi:"bar"` } var result MyInvokeResult err := ctx.Invoke(tok, MyInvokeArgs{Foo: 42}, &result, opts...) ``` 3. Ease-of-use of Apply All `Apply` methods now accept an interface{} as the callback type. The provided callback value must have one of the following signatures: func (v T) U func (v T) (U, error) func (ctx context.Context, v T) U func (ctx context.Context, v T) (U, error) T must be assignable from the ElementType of the Output. If U is a type that has a registered Output type, the result of the Apply will be the corresponding Output type. Otherwise, the result of the Apply will be AnyOutput. Fixes https://github.com/pulumi/pulumi/issues/2149. Fixes https://github.com/pulumi/pulumi/issues/3488. Fixes https://github.com/pulumi/pulumi/issues/3487. Fixes https://github.com/pulumi/pulumi-aws/issues/248. Fixes https://github.com/pulumi/pulumi/issues/3492. Fixes https://github.com/pulumi/pulumi/issues/3491. Fixes https://github.com/pulumi/pulumi/issues/3562.
2020-01-18 16:08:37 +01:00
assert.NoError(t, err)
assert.Equal(t, nestedType{Foo: "bar", Bar: 42}, v)
out = ToOutput(out)
_, ok = out.(nestedTypeOutput)
assert.True(t, ok)
v, known, secret, deps, err = await(out)
Redesign the Go SDK resource/input/output system. (#3506) The redesign is focused around providing better static typings and improved ease-of-use for the Go SDK. Most of the redesign revolves around three pivots: - Strongly-typed inputs, especially for nested types - Struct-based resource and invoke APIs - Ease-of-use of Apply 1. Strongly-typed inputs Input is the type of a generic input value for a Pulumi resource. This type is used in conjunction with Output to provide polymorphism over strongly-typed input values. The intended pattern for nested Pulumi value types is to define an input interface and a plain, input, and output variant of the value type that implement the input interface. For example, given a nested Pulumi value type with the following shape: ``` type Nested struct { Foo int Bar string } ``` We would define the following: ``` var nestedType = reflect.TypeOf((*Nested)(nil)).Elem() type NestedInput interface { pulumi.Input ToNestedOutput() NestedOutput ToNestedOutputWithContext(context.Context) NestedOutput } type Nested struct { Foo int `pulumi:"foo"` Bar string `pulumi:"bar"` } type NestedInputValue struct { Foo pulumi.IntInput `pulumi:"foo"` Bar pulumi.StringInput `pulumi:"bar"` } func (NestedInputValue) ElementType() reflect.Type { return nestedType } func (v NestedInputValue) ToNestedOutput() NestedOutput { return pulumi.ToOutput(v).(NestedOutput) } func (v NestedInputValue) ToNestedOutputWithContext(ctx context.Context) NestedOutput { return pulumi.ToOutputWithContext(ctx, v).(NestedOutput) } type NestedOutput struct { *pulumi.OutputState } func (NestedOutput) ElementType() reflect.Type { return nestedType } func (o NestedOutput) ToNestedOutput() NestedOutput { return o } func (o NestedOutput) ToNestedOutputWithContext(ctx context.Context) NestedOutput { return o } func (o NestedOutput) Foo() pulumi.IntOutput { return o.Apply(func (v Nested) int { return v.Foo }).(pulumi.IntOutput) } func (o NestedOutput) Bar() pulumi.StringOutput { return o.Apply(func (v Nested) string { return v.Bar }).(pulumi.StringOutput) } ``` The SDK provides input and output types for primitives, arrays, and maps. 2. Struct-based APIs Instead of providing expected output properties in the input map passed to {Read,Register}Resource and returning the outputs as a map, the user now passes a pointer to a struct that implements one of the Resource interfaces and has appropriately typed and tagged fields that represent its output properties. For example, given a custom resource with an int-typed output "foo" and a string-typed output "bar", we would define the following CustomResource type: ``` type MyResource struct { pulumi.CustomResourceState Foo pulumi.IntOutput `pulumi:"foo"` Bar pulumi.StringOutput `pulumi:"bar"` } ``` And invoke RegisterResource like so: ``` var resource MyResource err := ctx.RegisterResource(tok, name, props, &resource, opts...) ``` Invoke arguments and results are also provided via structs, but use plain-old Go types for their fields: ``` type MyInvokeArgs struct { Foo int `pulumi:"foo"` } type MyInvokeResult struct { Bar string `pulumi:"bar"` } var result MyInvokeResult err := ctx.Invoke(tok, MyInvokeArgs{Foo: 42}, &result, opts...) ``` 3. Ease-of-use of Apply All `Apply` methods now accept an interface{} as the callback type. The provided callback value must have one of the following signatures: func (v T) U func (v T) (U, error) func (ctx context.Context, v T) U func (ctx context.Context, v T) (U, error) T must be assignable from the ElementType of the Output. If U is a type that has a registered Output type, the result of the Apply will be the corresponding Output type. Otherwise, the result of the Apply will be AnyOutput. Fixes https://github.com/pulumi/pulumi/issues/2149. Fixes https://github.com/pulumi/pulumi/issues/3488. Fixes https://github.com/pulumi/pulumi/issues/3487. Fixes https://github.com/pulumi/pulumi-aws/issues/248. Fixes https://github.com/pulumi/pulumi/issues/3492. Fixes https://github.com/pulumi/pulumi/issues/3491. Fixes https://github.com/pulumi/pulumi/issues/3562.
2020-01-18 16:08:37 +01:00
assert.True(t, known)
assert.False(t, secret)
assert.Nil(t, deps)
Redesign the Go SDK resource/input/output system. (#3506) The redesign is focused around providing better static typings and improved ease-of-use for the Go SDK. Most of the redesign revolves around three pivots: - Strongly-typed inputs, especially for nested types - Struct-based resource and invoke APIs - Ease-of-use of Apply 1. Strongly-typed inputs Input is the type of a generic input value for a Pulumi resource. This type is used in conjunction with Output to provide polymorphism over strongly-typed input values. The intended pattern for nested Pulumi value types is to define an input interface and a plain, input, and output variant of the value type that implement the input interface. For example, given a nested Pulumi value type with the following shape: ``` type Nested struct { Foo int Bar string } ``` We would define the following: ``` var nestedType = reflect.TypeOf((*Nested)(nil)).Elem() type NestedInput interface { pulumi.Input ToNestedOutput() NestedOutput ToNestedOutputWithContext(context.Context) NestedOutput } type Nested struct { Foo int `pulumi:"foo"` Bar string `pulumi:"bar"` } type NestedInputValue struct { Foo pulumi.IntInput `pulumi:"foo"` Bar pulumi.StringInput `pulumi:"bar"` } func (NestedInputValue) ElementType() reflect.Type { return nestedType } func (v NestedInputValue) ToNestedOutput() NestedOutput { return pulumi.ToOutput(v).(NestedOutput) } func (v NestedInputValue) ToNestedOutputWithContext(ctx context.Context) NestedOutput { return pulumi.ToOutputWithContext(ctx, v).(NestedOutput) } type NestedOutput struct { *pulumi.OutputState } func (NestedOutput) ElementType() reflect.Type { return nestedType } func (o NestedOutput) ToNestedOutput() NestedOutput { return o } func (o NestedOutput) ToNestedOutputWithContext(ctx context.Context) NestedOutput { return o } func (o NestedOutput) Foo() pulumi.IntOutput { return o.Apply(func (v Nested) int { return v.Foo }).(pulumi.IntOutput) } func (o NestedOutput) Bar() pulumi.StringOutput { return o.Apply(func (v Nested) string { return v.Bar }).(pulumi.StringOutput) } ``` The SDK provides input and output types for primitives, arrays, and maps. 2. Struct-based APIs Instead of providing expected output properties in the input map passed to {Read,Register}Resource and returning the outputs as a map, the user now passes a pointer to a struct that implements one of the Resource interfaces and has appropriately typed and tagged fields that represent its output properties. For example, given a custom resource with an int-typed output "foo" and a string-typed output "bar", we would define the following CustomResource type: ``` type MyResource struct { pulumi.CustomResourceState Foo pulumi.IntOutput `pulumi:"foo"` Bar pulumi.StringOutput `pulumi:"bar"` } ``` And invoke RegisterResource like so: ``` var resource MyResource err := ctx.RegisterResource(tok, name, props, &resource, opts...) ``` Invoke arguments and results are also provided via structs, but use plain-old Go types for their fields: ``` type MyInvokeArgs struct { Foo int `pulumi:"foo"` } type MyInvokeResult struct { Bar string `pulumi:"bar"` } var result MyInvokeResult err := ctx.Invoke(tok, MyInvokeArgs{Foo: 42}, &result, opts...) ``` 3. Ease-of-use of Apply All `Apply` methods now accept an interface{} as the callback type. The provided callback value must have one of the following signatures: func (v T) U func (v T) (U, error) func (ctx context.Context, v T) U func (ctx context.Context, v T) (U, error) T must be assignable from the ElementType of the Output. If U is a type that has a registered Output type, the result of the Apply will be the corresponding Output type. Otherwise, the result of the Apply will be AnyOutput. Fixes https://github.com/pulumi/pulumi/issues/2149. Fixes https://github.com/pulumi/pulumi/issues/3488. Fixes https://github.com/pulumi/pulumi/issues/3487. Fixes https://github.com/pulumi/pulumi-aws/issues/248. Fixes https://github.com/pulumi/pulumi/issues/3492. Fixes https://github.com/pulumi/pulumi/issues/3491. Fixes https://github.com/pulumi/pulumi/issues/3562.
2020-01-18 16:08:37 +01:00
assert.NoError(t, err)
assert.Equal(t, nestedType{Foo: "bar", Bar: 42}, v)
out = ToOutput(nestedTypeInputs{Foo: ToOutput(String("bar")).(StringInput), Bar: ToOutput(Int(42)).(IntInput)})
_, ok = out.(nestedTypeOutput)
assert.True(t, ok)
v, known, secret, deps, err = await(out)
Redesign the Go SDK resource/input/output system. (#3506) The redesign is focused around providing better static typings and improved ease-of-use for the Go SDK. Most of the redesign revolves around three pivots: - Strongly-typed inputs, especially for nested types - Struct-based resource and invoke APIs - Ease-of-use of Apply 1. Strongly-typed inputs Input is the type of a generic input value for a Pulumi resource. This type is used in conjunction with Output to provide polymorphism over strongly-typed input values. The intended pattern for nested Pulumi value types is to define an input interface and a plain, input, and output variant of the value type that implement the input interface. For example, given a nested Pulumi value type with the following shape: ``` type Nested struct { Foo int Bar string } ``` We would define the following: ``` var nestedType = reflect.TypeOf((*Nested)(nil)).Elem() type NestedInput interface { pulumi.Input ToNestedOutput() NestedOutput ToNestedOutputWithContext(context.Context) NestedOutput } type Nested struct { Foo int `pulumi:"foo"` Bar string `pulumi:"bar"` } type NestedInputValue struct { Foo pulumi.IntInput `pulumi:"foo"` Bar pulumi.StringInput `pulumi:"bar"` } func (NestedInputValue) ElementType() reflect.Type { return nestedType } func (v NestedInputValue) ToNestedOutput() NestedOutput { return pulumi.ToOutput(v).(NestedOutput) } func (v NestedInputValue) ToNestedOutputWithContext(ctx context.Context) NestedOutput { return pulumi.ToOutputWithContext(ctx, v).(NestedOutput) } type NestedOutput struct { *pulumi.OutputState } func (NestedOutput) ElementType() reflect.Type { return nestedType } func (o NestedOutput) ToNestedOutput() NestedOutput { return o } func (o NestedOutput) ToNestedOutputWithContext(ctx context.Context) NestedOutput { return o } func (o NestedOutput) Foo() pulumi.IntOutput { return o.Apply(func (v Nested) int { return v.Foo }).(pulumi.IntOutput) } func (o NestedOutput) Bar() pulumi.StringOutput { return o.Apply(func (v Nested) string { return v.Bar }).(pulumi.StringOutput) } ``` The SDK provides input and output types for primitives, arrays, and maps. 2. Struct-based APIs Instead of providing expected output properties in the input map passed to {Read,Register}Resource and returning the outputs as a map, the user now passes a pointer to a struct that implements one of the Resource interfaces and has appropriately typed and tagged fields that represent its output properties. For example, given a custom resource with an int-typed output "foo" and a string-typed output "bar", we would define the following CustomResource type: ``` type MyResource struct { pulumi.CustomResourceState Foo pulumi.IntOutput `pulumi:"foo"` Bar pulumi.StringOutput `pulumi:"bar"` } ``` And invoke RegisterResource like so: ``` var resource MyResource err := ctx.RegisterResource(tok, name, props, &resource, opts...) ``` Invoke arguments and results are also provided via structs, but use plain-old Go types for their fields: ``` type MyInvokeArgs struct { Foo int `pulumi:"foo"` } type MyInvokeResult struct { Bar string `pulumi:"bar"` } var result MyInvokeResult err := ctx.Invoke(tok, MyInvokeArgs{Foo: 42}, &result, opts...) ``` 3. Ease-of-use of Apply All `Apply` methods now accept an interface{} as the callback type. The provided callback value must have one of the following signatures: func (v T) U func (v T) (U, error) func (ctx context.Context, v T) U func (ctx context.Context, v T) (U, error) T must be assignable from the ElementType of the Output. If U is a type that has a registered Output type, the result of the Apply will be the corresponding Output type. Otherwise, the result of the Apply will be AnyOutput. Fixes https://github.com/pulumi/pulumi/issues/2149. Fixes https://github.com/pulumi/pulumi/issues/3488. Fixes https://github.com/pulumi/pulumi/issues/3487. Fixes https://github.com/pulumi/pulumi-aws/issues/248. Fixes https://github.com/pulumi/pulumi/issues/3492. Fixes https://github.com/pulumi/pulumi/issues/3491. Fixes https://github.com/pulumi/pulumi/issues/3562.
2020-01-18 16:08:37 +01:00
assert.True(t, known)
assert.False(t, secret)
assert.Nil(t, deps)
Redesign the Go SDK resource/input/output system. (#3506) The redesign is focused around providing better static typings and improved ease-of-use for the Go SDK. Most of the redesign revolves around three pivots: - Strongly-typed inputs, especially for nested types - Struct-based resource and invoke APIs - Ease-of-use of Apply 1. Strongly-typed inputs Input is the type of a generic input value for a Pulumi resource. This type is used in conjunction with Output to provide polymorphism over strongly-typed input values. The intended pattern for nested Pulumi value types is to define an input interface and a plain, input, and output variant of the value type that implement the input interface. For example, given a nested Pulumi value type with the following shape: ``` type Nested struct { Foo int Bar string } ``` We would define the following: ``` var nestedType = reflect.TypeOf((*Nested)(nil)).Elem() type NestedInput interface { pulumi.Input ToNestedOutput() NestedOutput ToNestedOutputWithContext(context.Context) NestedOutput } type Nested struct { Foo int `pulumi:"foo"` Bar string `pulumi:"bar"` } type NestedInputValue struct { Foo pulumi.IntInput `pulumi:"foo"` Bar pulumi.StringInput `pulumi:"bar"` } func (NestedInputValue) ElementType() reflect.Type { return nestedType } func (v NestedInputValue) ToNestedOutput() NestedOutput { return pulumi.ToOutput(v).(NestedOutput) } func (v NestedInputValue) ToNestedOutputWithContext(ctx context.Context) NestedOutput { return pulumi.ToOutputWithContext(ctx, v).(NestedOutput) } type NestedOutput struct { *pulumi.OutputState } func (NestedOutput) ElementType() reflect.Type { return nestedType } func (o NestedOutput) ToNestedOutput() NestedOutput { return o } func (o NestedOutput) ToNestedOutputWithContext(ctx context.Context) NestedOutput { return o } func (o NestedOutput) Foo() pulumi.IntOutput { return o.Apply(func (v Nested) int { return v.Foo }).(pulumi.IntOutput) } func (o NestedOutput) Bar() pulumi.StringOutput { return o.Apply(func (v Nested) string { return v.Bar }).(pulumi.StringOutput) } ``` The SDK provides input and output types for primitives, arrays, and maps. 2. Struct-based APIs Instead of providing expected output properties in the input map passed to {Read,Register}Resource and returning the outputs as a map, the user now passes a pointer to a struct that implements one of the Resource interfaces and has appropriately typed and tagged fields that represent its output properties. For example, given a custom resource with an int-typed output "foo" and a string-typed output "bar", we would define the following CustomResource type: ``` type MyResource struct { pulumi.CustomResourceState Foo pulumi.IntOutput `pulumi:"foo"` Bar pulumi.StringOutput `pulumi:"bar"` } ``` And invoke RegisterResource like so: ``` var resource MyResource err := ctx.RegisterResource(tok, name, props, &resource, opts...) ``` Invoke arguments and results are also provided via structs, but use plain-old Go types for their fields: ``` type MyInvokeArgs struct { Foo int `pulumi:"foo"` } type MyInvokeResult struct { Bar string `pulumi:"bar"` } var result MyInvokeResult err := ctx.Invoke(tok, MyInvokeArgs{Foo: 42}, &result, opts...) ``` 3. Ease-of-use of Apply All `Apply` methods now accept an interface{} as the callback type. The provided callback value must have one of the following signatures: func (v T) U func (v T) (U, error) func (ctx context.Context, v T) U func (ctx context.Context, v T) (U, error) T must be assignable from the ElementType of the Output. If U is a type that has a registered Output type, the result of the Apply will be the corresponding Output type. Otherwise, the result of the Apply will be AnyOutput. Fixes https://github.com/pulumi/pulumi/issues/2149. Fixes https://github.com/pulumi/pulumi/issues/3488. Fixes https://github.com/pulumi/pulumi/issues/3487. Fixes https://github.com/pulumi/pulumi-aws/issues/248. Fixes https://github.com/pulumi/pulumi/issues/3492. Fixes https://github.com/pulumi/pulumi/issues/3491. Fixes https://github.com/pulumi/pulumi/issues/3562.
2020-01-18 16:08:37 +01:00
assert.NoError(t, err)
assert.Equal(t, nestedType{Foo: "bar", Bar: 42}, v)
}
type arrayLenInput Array
func (arrayLenInput) ElementType() reflect.Type {
return Array{}.ElementType()
}
func (i arrayLenInput) ToIntOutput() IntOutput {
return i.ToIntOutputWithContext(context.Background())
}
func (i arrayLenInput) ToIntOutputWithContext(ctx context.Context) IntOutput {
return ToOutput(i).ApplyT(func(arr []interface{}) int {
return len(arr)
}).(IntOutput)
}
func (i arrayLenInput) ToIntPtrOutput() IntPtrOutput {
return i.ToIntPtrOutputWithContext(context.Background())
}
func (i arrayLenInput) ToIntPtrOutputWithContext(ctx context.Context) IntPtrOutput {
return ToOutput(i).ApplyT(func(arr []interface{}) *int {
v := len(arr)
return &v
}).(IntPtrOutput)
}
Redesign the Go SDK resource/input/output system. (#3506) The redesign is focused around providing better static typings and improved ease-of-use for the Go SDK. Most of the redesign revolves around three pivots: - Strongly-typed inputs, especially for nested types - Struct-based resource and invoke APIs - Ease-of-use of Apply 1. Strongly-typed inputs Input is the type of a generic input value for a Pulumi resource. This type is used in conjunction with Output to provide polymorphism over strongly-typed input values. The intended pattern for nested Pulumi value types is to define an input interface and a plain, input, and output variant of the value type that implement the input interface. For example, given a nested Pulumi value type with the following shape: ``` type Nested struct { Foo int Bar string } ``` We would define the following: ``` var nestedType = reflect.TypeOf((*Nested)(nil)).Elem() type NestedInput interface { pulumi.Input ToNestedOutput() NestedOutput ToNestedOutputWithContext(context.Context) NestedOutput } type Nested struct { Foo int `pulumi:"foo"` Bar string `pulumi:"bar"` } type NestedInputValue struct { Foo pulumi.IntInput `pulumi:"foo"` Bar pulumi.StringInput `pulumi:"bar"` } func (NestedInputValue) ElementType() reflect.Type { return nestedType } func (v NestedInputValue) ToNestedOutput() NestedOutput { return pulumi.ToOutput(v).(NestedOutput) } func (v NestedInputValue) ToNestedOutputWithContext(ctx context.Context) NestedOutput { return pulumi.ToOutputWithContext(ctx, v).(NestedOutput) } type NestedOutput struct { *pulumi.OutputState } func (NestedOutput) ElementType() reflect.Type { return nestedType } func (o NestedOutput) ToNestedOutput() NestedOutput { return o } func (o NestedOutput) ToNestedOutputWithContext(ctx context.Context) NestedOutput { return o } func (o NestedOutput) Foo() pulumi.IntOutput { return o.Apply(func (v Nested) int { return v.Foo }).(pulumi.IntOutput) } func (o NestedOutput) Bar() pulumi.StringOutput { return o.Apply(func (v Nested) string { return v.Bar }).(pulumi.StringOutput) } ``` The SDK provides input and output types for primitives, arrays, and maps. 2. Struct-based APIs Instead of providing expected output properties in the input map passed to {Read,Register}Resource and returning the outputs as a map, the user now passes a pointer to a struct that implements one of the Resource interfaces and has appropriately typed and tagged fields that represent its output properties. For example, given a custom resource with an int-typed output "foo" and a string-typed output "bar", we would define the following CustomResource type: ``` type MyResource struct { pulumi.CustomResourceState Foo pulumi.IntOutput `pulumi:"foo"` Bar pulumi.StringOutput `pulumi:"bar"` } ``` And invoke RegisterResource like so: ``` var resource MyResource err := ctx.RegisterResource(tok, name, props, &resource, opts...) ``` Invoke arguments and results are also provided via structs, but use plain-old Go types for their fields: ``` type MyInvokeArgs struct { Foo int `pulumi:"foo"` } type MyInvokeResult struct { Bar string `pulumi:"bar"` } var result MyInvokeResult err := ctx.Invoke(tok, MyInvokeArgs{Foo: 42}, &result, opts...) ``` 3. Ease-of-use of Apply All `Apply` methods now accept an interface{} as the callback type. The provided callback value must have one of the following signatures: func (v T) U func (v T) (U, error) func (ctx context.Context, v T) U func (ctx context.Context, v T) (U, error) T must be assignable from the ElementType of the Output. If U is a type that has a registered Output type, the result of the Apply will be the corresponding Output type. Otherwise, the result of the Apply will be AnyOutput. Fixes https://github.com/pulumi/pulumi/issues/2149. Fixes https://github.com/pulumi/pulumi/issues/3488. Fixes https://github.com/pulumi/pulumi/issues/3487. Fixes https://github.com/pulumi/pulumi-aws/issues/248. Fixes https://github.com/pulumi/pulumi/issues/3492. Fixes https://github.com/pulumi/pulumi/issues/3491. Fixes https://github.com/pulumi/pulumi/issues/3562.
2020-01-18 16:08:37 +01:00
// Test that ToOutput converts inputs appropriately.
func TestToOutputConvert(t *testing.T) {
out := ToOutput(nestedTypeInputs{Foo: ID("bar"), Bar: arrayLenInput{Int(42)}})
_, ok := out.(nestedTypeOutput)
assert.True(t, ok)
v, known, secret, deps, err := await(out)
Redesign the Go SDK resource/input/output system. (#3506) The redesign is focused around providing better static typings and improved ease-of-use for the Go SDK. Most of the redesign revolves around three pivots: - Strongly-typed inputs, especially for nested types - Struct-based resource and invoke APIs - Ease-of-use of Apply 1. Strongly-typed inputs Input is the type of a generic input value for a Pulumi resource. This type is used in conjunction with Output to provide polymorphism over strongly-typed input values. The intended pattern for nested Pulumi value types is to define an input interface and a plain, input, and output variant of the value type that implement the input interface. For example, given a nested Pulumi value type with the following shape: ``` type Nested struct { Foo int Bar string } ``` We would define the following: ``` var nestedType = reflect.TypeOf((*Nested)(nil)).Elem() type NestedInput interface { pulumi.Input ToNestedOutput() NestedOutput ToNestedOutputWithContext(context.Context) NestedOutput } type Nested struct { Foo int `pulumi:"foo"` Bar string `pulumi:"bar"` } type NestedInputValue struct { Foo pulumi.IntInput `pulumi:"foo"` Bar pulumi.StringInput `pulumi:"bar"` } func (NestedInputValue) ElementType() reflect.Type { return nestedType } func (v NestedInputValue) ToNestedOutput() NestedOutput { return pulumi.ToOutput(v).(NestedOutput) } func (v NestedInputValue) ToNestedOutputWithContext(ctx context.Context) NestedOutput { return pulumi.ToOutputWithContext(ctx, v).(NestedOutput) } type NestedOutput struct { *pulumi.OutputState } func (NestedOutput) ElementType() reflect.Type { return nestedType } func (o NestedOutput) ToNestedOutput() NestedOutput { return o } func (o NestedOutput) ToNestedOutputWithContext(ctx context.Context) NestedOutput { return o } func (o NestedOutput) Foo() pulumi.IntOutput { return o.Apply(func (v Nested) int { return v.Foo }).(pulumi.IntOutput) } func (o NestedOutput) Bar() pulumi.StringOutput { return o.Apply(func (v Nested) string { return v.Bar }).(pulumi.StringOutput) } ``` The SDK provides input and output types for primitives, arrays, and maps. 2. Struct-based APIs Instead of providing expected output properties in the input map passed to {Read,Register}Resource and returning the outputs as a map, the user now passes a pointer to a struct that implements one of the Resource interfaces and has appropriately typed and tagged fields that represent its output properties. For example, given a custom resource with an int-typed output "foo" and a string-typed output "bar", we would define the following CustomResource type: ``` type MyResource struct { pulumi.CustomResourceState Foo pulumi.IntOutput `pulumi:"foo"` Bar pulumi.StringOutput `pulumi:"bar"` } ``` And invoke RegisterResource like so: ``` var resource MyResource err := ctx.RegisterResource(tok, name, props, &resource, opts...) ``` Invoke arguments and results are also provided via structs, but use plain-old Go types for their fields: ``` type MyInvokeArgs struct { Foo int `pulumi:"foo"` } type MyInvokeResult struct { Bar string `pulumi:"bar"` } var result MyInvokeResult err := ctx.Invoke(tok, MyInvokeArgs{Foo: 42}, &result, opts...) ``` 3. Ease-of-use of Apply All `Apply` methods now accept an interface{} as the callback type. The provided callback value must have one of the following signatures: func (v T) U func (v T) (U, error) func (ctx context.Context, v T) U func (ctx context.Context, v T) (U, error) T must be assignable from the ElementType of the Output. If U is a type that has a registered Output type, the result of the Apply will be the corresponding Output type. Otherwise, the result of the Apply will be AnyOutput. Fixes https://github.com/pulumi/pulumi/issues/2149. Fixes https://github.com/pulumi/pulumi/issues/3488. Fixes https://github.com/pulumi/pulumi/issues/3487. Fixes https://github.com/pulumi/pulumi-aws/issues/248. Fixes https://github.com/pulumi/pulumi/issues/3492. Fixes https://github.com/pulumi/pulumi/issues/3491. Fixes https://github.com/pulumi/pulumi/issues/3562.
2020-01-18 16:08:37 +01:00
assert.True(t, known)
assert.False(t, secret)
assert.Nil(t, deps)
Redesign the Go SDK resource/input/output system. (#3506) The redesign is focused around providing better static typings and improved ease-of-use for the Go SDK. Most of the redesign revolves around three pivots: - Strongly-typed inputs, especially for nested types - Struct-based resource and invoke APIs - Ease-of-use of Apply 1. Strongly-typed inputs Input is the type of a generic input value for a Pulumi resource. This type is used in conjunction with Output to provide polymorphism over strongly-typed input values. The intended pattern for nested Pulumi value types is to define an input interface and a plain, input, and output variant of the value type that implement the input interface. For example, given a nested Pulumi value type with the following shape: ``` type Nested struct { Foo int Bar string } ``` We would define the following: ``` var nestedType = reflect.TypeOf((*Nested)(nil)).Elem() type NestedInput interface { pulumi.Input ToNestedOutput() NestedOutput ToNestedOutputWithContext(context.Context) NestedOutput } type Nested struct { Foo int `pulumi:"foo"` Bar string `pulumi:"bar"` } type NestedInputValue struct { Foo pulumi.IntInput `pulumi:"foo"` Bar pulumi.StringInput `pulumi:"bar"` } func (NestedInputValue) ElementType() reflect.Type { return nestedType } func (v NestedInputValue) ToNestedOutput() NestedOutput { return pulumi.ToOutput(v).(NestedOutput) } func (v NestedInputValue) ToNestedOutputWithContext(ctx context.Context) NestedOutput { return pulumi.ToOutputWithContext(ctx, v).(NestedOutput) } type NestedOutput struct { *pulumi.OutputState } func (NestedOutput) ElementType() reflect.Type { return nestedType } func (o NestedOutput) ToNestedOutput() NestedOutput { return o } func (o NestedOutput) ToNestedOutputWithContext(ctx context.Context) NestedOutput { return o } func (o NestedOutput) Foo() pulumi.IntOutput { return o.Apply(func (v Nested) int { return v.Foo }).(pulumi.IntOutput) } func (o NestedOutput) Bar() pulumi.StringOutput { return o.Apply(func (v Nested) string { return v.Bar }).(pulumi.StringOutput) } ``` The SDK provides input and output types for primitives, arrays, and maps. 2. Struct-based APIs Instead of providing expected output properties in the input map passed to {Read,Register}Resource and returning the outputs as a map, the user now passes a pointer to a struct that implements one of the Resource interfaces and has appropriately typed and tagged fields that represent its output properties. For example, given a custom resource with an int-typed output "foo" and a string-typed output "bar", we would define the following CustomResource type: ``` type MyResource struct { pulumi.CustomResourceState Foo pulumi.IntOutput `pulumi:"foo"` Bar pulumi.StringOutput `pulumi:"bar"` } ``` And invoke RegisterResource like so: ``` var resource MyResource err := ctx.RegisterResource(tok, name, props, &resource, opts...) ``` Invoke arguments and results are also provided via structs, but use plain-old Go types for their fields: ``` type MyInvokeArgs struct { Foo int `pulumi:"foo"` } type MyInvokeResult struct { Bar string `pulumi:"bar"` } var result MyInvokeResult err := ctx.Invoke(tok, MyInvokeArgs{Foo: 42}, &result, opts...) ``` 3. Ease-of-use of Apply All `Apply` methods now accept an interface{} as the callback type. The provided callback value must have one of the following signatures: func (v T) U func (v T) (U, error) func (ctx context.Context, v T) U func (ctx context.Context, v T) (U, error) T must be assignable from the ElementType of the Output. If U is a type that has a registered Output type, the result of the Apply will be the corresponding Output type. Otherwise, the result of the Apply will be AnyOutput. Fixes https://github.com/pulumi/pulumi/issues/2149. Fixes https://github.com/pulumi/pulumi/issues/3488. Fixes https://github.com/pulumi/pulumi/issues/3487. Fixes https://github.com/pulumi/pulumi-aws/issues/248. Fixes https://github.com/pulumi/pulumi/issues/3492. Fixes https://github.com/pulumi/pulumi/issues/3491. Fixes https://github.com/pulumi/pulumi/issues/3562.
2020-01-18 16:08:37 +01:00
assert.NoError(t, err)
assert.Equal(t, nestedType{Foo: "bar", Bar: 1}, v)
}
// Test that ToOutput correctly handles nested inputs and outputs when the argument is an input or interface{}.
func TestToOutputAny(t *testing.T) {
type args struct {
S StringInput
I IntInput
A Input
}
out := ToOutput(&args{
S: ID("hello"),
I: Int(42).ToIntOutput(),
A: Map{"world": Bool(true).ToBoolOutput()},
})
_, ok := out.(AnyOutput)
assert.True(t, ok)
v, known, secret, deps, err := await(out)
Redesign the Go SDK resource/input/output system. (#3506) The redesign is focused around providing better static typings and improved ease-of-use for the Go SDK. Most of the redesign revolves around three pivots: - Strongly-typed inputs, especially for nested types - Struct-based resource and invoke APIs - Ease-of-use of Apply 1. Strongly-typed inputs Input is the type of a generic input value for a Pulumi resource. This type is used in conjunction with Output to provide polymorphism over strongly-typed input values. The intended pattern for nested Pulumi value types is to define an input interface and a plain, input, and output variant of the value type that implement the input interface. For example, given a nested Pulumi value type with the following shape: ``` type Nested struct { Foo int Bar string } ``` We would define the following: ``` var nestedType = reflect.TypeOf((*Nested)(nil)).Elem() type NestedInput interface { pulumi.Input ToNestedOutput() NestedOutput ToNestedOutputWithContext(context.Context) NestedOutput } type Nested struct { Foo int `pulumi:"foo"` Bar string `pulumi:"bar"` } type NestedInputValue struct { Foo pulumi.IntInput `pulumi:"foo"` Bar pulumi.StringInput `pulumi:"bar"` } func (NestedInputValue) ElementType() reflect.Type { return nestedType } func (v NestedInputValue) ToNestedOutput() NestedOutput { return pulumi.ToOutput(v).(NestedOutput) } func (v NestedInputValue) ToNestedOutputWithContext(ctx context.Context) NestedOutput { return pulumi.ToOutputWithContext(ctx, v).(NestedOutput) } type NestedOutput struct { *pulumi.OutputState } func (NestedOutput) ElementType() reflect.Type { return nestedType } func (o NestedOutput) ToNestedOutput() NestedOutput { return o } func (o NestedOutput) ToNestedOutputWithContext(ctx context.Context) NestedOutput { return o } func (o NestedOutput) Foo() pulumi.IntOutput { return o.Apply(func (v Nested) int { return v.Foo }).(pulumi.IntOutput) } func (o NestedOutput) Bar() pulumi.StringOutput { return o.Apply(func (v Nested) string { return v.Bar }).(pulumi.StringOutput) } ``` The SDK provides input and output types for primitives, arrays, and maps. 2. Struct-based APIs Instead of providing expected output properties in the input map passed to {Read,Register}Resource and returning the outputs as a map, the user now passes a pointer to a struct that implements one of the Resource interfaces and has appropriately typed and tagged fields that represent its output properties. For example, given a custom resource with an int-typed output "foo" and a string-typed output "bar", we would define the following CustomResource type: ``` type MyResource struct { pulumi.CustomResourceState Foo pulumi.IntOutput `pulumi:"foo"` Bar pulumi.StringOutput `pulumi:"bar"` } ``` And invoke RegisterResource like so: ``` var resource MyResource err := ctx.RegisterResource(tok, name, props, &resource, opts...) ``` Invoke arguments and results are also provided via structs, but use plain-old Go types for their fields: ``` type MyInvokeArgs struct { Foo int `pulumi:"foo"` } type MyInvokeResult struct { Bar string `pulumi:"bar"` } var result MyInvokeResult err := ctx.Invoke(tok, MyInvokeArgs{Foo: 42}, &result, opts...) ``` 3. Ease-of-use of Apply All `Apply` methods now accept an interface{} as the callback type. The provided callback value must have one of the following signatures: func (v T) U func (v T) (U, error) func (ctx context.Context, v T) U func (ctx context.Context, v T) (U, error) T must be assignable from the ElementType of the Output. If U is a type that has a registered Output type, the result of the Apply will be the corresponding Output type. Otherwise, the result of the Apply will be AnyOutput. Fixes https://github.com/pulumi/pulumi/issues/2149. Fixes https://github.com/pulumi/pulumi/issues/3488. Fixes https://github.com/pulumi/pulumi/issues/3487. Fixes https://github.com/pulumi/pulumi-aws/issues/248. Fixes https://github.com/pulumi/pulumi/issues/3492. Fixes https://github.com/pulumi/pulumi/issues/3491. Fixes https://github.com/pulumi/pulumi/issues/3562.
2020-01-18 16:08:37 +01:00
assert.True(t, known)
assert.False(t, secret)
assert.Nil(t, deps)
Redesign the Go SDK resource/input/output system. (#3506) The redesign is focused around providing better static typings and improved ease-of-use for the Go SDK. Most of the redesign revolves around three pivots: - Strongly-typed inputs, especially for nested types - Struct-based resource and invoke APIs - Ease-of-use of Apply 1. Strongly-typed inputs Input is the type of a generic input value for a Pulumi resource. This type is used in conjunction with Output to provide polymorphism over strongly-typed input values. The intended pattern for nested Pulumi value types is to define an input interface and a plain, input, and output variant of the value type that implement the input interface. For example, given a nested Pulumi value type with the following shape: ``` type Nested struct { Foo int Bar string } ``` We would define the following: ``` var nestedType = reflect.TypeOf((*Nested)(nil)).Elem() type NestedInput interface { pulumi.Input ToNestedOutput() NestedOutput ToNestedOutputWithContext(context.Context) NestedOutput } type Nested struct { Foo int `pulumi:"foo"` Bar string `pulumi:"bar"` } type NestedInputValue struct { Foo pulumi.IntInput `pulumi:"foo"` Bar pulumi.StringInput `pulumi:"bar"` } func (NestedInputValue) ElementType() reflect.Type { return nestedType } func (v NestedInputValue) ToNestedOutput() NestedOutput { return pulumi.ToOutput(v).(NestedOutput) } func (v NestedInputValue) ToNestedOutputWithContext(ctx context.Context) NestedOutput { return pulumi.ToOutputWithContext(ctx, v).(NestedOutput) } type NestedOutput struct { *pulumi.OutputState } func (NestedOutput) ElementType() reflect.Type { return nestedType } func (o NestedOutput) ToNestedOutput() NestedOutput { return o } func (o NestedOutput) ToNestedOutputWithContext(ctx context.Context) NestedOutput { return o } func (o NestedOutput) Foo() pulumi.IntOutput { return o.Apply(func (v Nested) int { return v.Foo }).(pulumi.IntOutput) } func (o NestedOutput) Bar() pulumi.StringOutput { return o.Apply(func (v Nested) string { return v.Bar }).(pulumi.StringOutput) } ``` The SDK provides input and output types for primitives, arrays, and maps. 2. Struct-based APIs Instead of providing expected output properties in the input map passed to {Read,Register}Resource and returning the outputs as a map, the user now passes a pointer to a struct that implements one of the Resource interfaces and has appropriately typed and tagged fields that represent its output properties. For example, given a custom resource with an int-typed output "foo" and a string-typed output "bar", we would define the following CustomResource type: ``` type MyResource struct { pulumi.CustomResourceState Foo pulumi.IntOutput `pulumi:"foo"` Bar pulumi.StringOutput `pulumi:"bar"` } ``` And invoke RegisterResource like so: ``` var resource MyResource err := ctx.RegisterResource(tok, name, props, &resource, opts...) ``` Invoke arguments and results are also provided via structs, but use plain-old Go types for their fields: ``` type MyInvokeArgs struct { Foo int `pulumi:"foo"` } type MyInvokeResult struct { Bar string `pulumi:"bar"` } var result MyInvokeResult err := ctx.Invoke(tok, MyInvokeArgs{Foo: 42}, &result, opts...) ``` 3. Ease-of-use of Apply All `Apply` methods now accept an interface{} as the callback type. The provided callback value must have one of the following signatures: func (v T) U func (v T) (U, error) func (ctx context.Context, v T) U func (ctx context.Context, v T) (U, error) T must be assignable from the ElementType of the Output. If U is a type that has a registered Output type, the result of the Apply will be the corresponding Output type. Otherwise, the result of the Apply will be AnyOutput. Fixes https://github.com/pulumi/pulumi/issues/2149. Fixes https://github.com/pulumi/pulumi/issues/3488. Fixes https://github.com/pulumi/pulumi/issues/3487. Fixes https://github.com/pulumi/pulumi-aws/issues/248. Fixes https://github.com/pulumi/pulumi/issues/3492. Fixes https://github.com/pulumi/pulumi/issues/3491. Fixes https://github.com/pulumi/pulumi/issues/3562.
2020-01-18 16:08:37 +01:00
assert.NoError(t, err)
argsV := v.(*args)
si, ok := argsV.S.(ID)
assert.True(t, ok)
assert.Equal(t, ID("hello"), si)
io, ok := argsV.I.(IntOutput)
assert.True(t, ok)
assert.Equal(t, uint32(outputResolved), io.state)
assert.Equal(t, 42, io.value)
ai, ok := argsV.A.(Map)
assert.True(t, ok)
bo, ok := ai["world"].(BoolOutput)
assert.True(t, ok)
assert.Equal(t, uint32(outputResolved), bo.getState().state)
assert.Equal(t, true, bo.value)
}
func TestToOutputAnyDeps(t *testing.T) {
type args struct {
S StringInput
I IntInput
A Input
R Resource
}
stringDep1, stringDep2 := &ResourceState{}, &ResourceState{}
Await outstanding async work in Go. (#6983) The Pulumi Go SDK does not currently await all outstanding asynchronous work associated with a Pulumi program. Because all relevant asynchronous work is created via the Pulumi SDK, we can track this asynchronous work and ensure that it has all completed prior to returning from `Context.Run`. This is complicated by the fact that many of the existing APIs that are able to create `Output`s--`NewOutput`, `ToOutput`, `Any`, `ToSecret`, and `All`--do not have a `*Context` parameter, and so have no straightforward way to associate themselves with a `*Context`. To address this, these changes add new versions of each of these APIs as methods on `*Context`. Despite these new methods, most Pulumi programs should work without changes: the bulk of `Output`s are created by the SDK itself as part of resource registration, and for `Any` and `All`, we can pick up the context from any `Output`s present in the arguments. The only programs that should require changes are those that create outputs from whole cloth using `NewOutput`, `ToOutput`, or `ToSecret` and create unawaited async work rooted at those outputs. On an implementation level, these changes track asynchronous work using a `sync.WaitGroup` associated with each `*Context`. This `WaitGroup` is passed to each output associated with the context. The SDK increments this `WaitGroup`'s count prior to starting any asynchronous work and decrements it once the work (including any callbacks triggered by the work) is complete. This fixes the Go portion of #3991.
2021-05-14 21:00:21 +02:00
stringOut := StringOutput{newOutputState(nil, reflect.TypeOf(""), stringDep1)}
go func() {
stringOut.resolve("a stringy output", true, false, []Resource{stringDep2})
}()
intDep1, intDep2 := &ResourceState{}, &ResourceState{}
Await outstanding async work in Go. (#6983) The Pulumi Go SDK does not currently await all outstanding asynchronous work associated with a Pulumi program. Because all relevant asynchronous work is created via the Pulumi SDK, we can track this asynchronous work and ensure that it has all completed prior to returning from `Context.Run`. This is complicated by the fact that many of the existing APIs that are able to create `Output`s--`NewOutput`, `ToOutput`, `Any`, `ToSecret`, and `All`--do not have a `*Context` parameter, and so have no straightforward way to associate themselves with a `*Context`. To address this, these changes add new versions of each of these APIs as methods on `*Context`. Despite these new methods, most Pulumi programs should work without changes: the bulk of `Output`s are created by the SDK itself as part of resource registration, and for `Any` and `All`, we can pick up the context from any `Output`s present in the arguments. The only programs that should require changes are those that create outputs from whole cloth using `NewOutput`, `ToOutput`, or `ToSecret` and create unawaited async work rooted at those outputs. On an implementation level, these changes track asynchronous work using a `sync.WaitGroup` associated with each `*Context`. This `WaitGroup` is passed to each output associated with the context. The SDK increments this `WaitGroup`'s count prior to starting any asynchronous work and decrements it once the work (including any callbacks triggered by the work) is complete. This fixes the Go portion of #3991.
2021-05-14 21:00:21 +02:00
intOut := IntOutput{newOutputState(nil, reflect.TypeOf(0), intDep1)}
go func() {
intOut.resolve(42, true, false, []Resource{intDep2})
}()
boolDep1, boolDep2 := &ResourceState{}, &ResourceState{}
Await outstanding async work in Go. (#6983) The Pulumi Go SDK does not currently await all outstanding asynchronous work associated with a Pulumi program. Because all relevant asynchronous work is created via the Pulumi SDK, we can track this asynchronous work and ensure that it has all completed prior to returning from `Context.Run`. This is complicated by the fact that many of the existing APIs that are able to create `Output`s--`NewOutput`, `ToOutput`, `Any`, `ToSecret`, and `All`--do not have a `*Context` parameter, and so have no straightforward way to associate themselves with a `*Context`. To address this, these changes add new versions of each of these APIs as methods on `*Context`. Despite these new methods, most Pulumi programs should work without changes: the bulk of `Output`s are created by the SDK itself as part of resource registration, and for `Any` and `All`, we can pick up the context from any `Output`s present in the arguments. The only programs that should require changes are those that create outputs from whole cloth using `NewOutput`, `ToOutput`, or `ToSecret` and create unawaited async work rooted at those outputs. On an implementation level, these changes track asynchronous work using a `sync.WaitGroup` associated with each `*Context`. This `WaitGroup` is passed to each output associated with the context. The SDK increments this `WaitGroup`'s count prior to starting any asynchronous work and decrements it once the work (including any callbacks triggered by the work) is complete. This fixes the Go portion of #3991.
2021-05-14 21:00:21 +02:00
boolOut := BoolOutput{newOutputState(nil, reflect.TypeOf(true), boolDep1)}
go func() {
boolOut.resolve(true, true, false, []Resource{boolDep2})
}()
res := &ResourceState{}
out := ToOutput(&args{
S: stringOut,
I: intOut,
A: Map{"world": boolOut},
R: res,
})
_, ok := out.(AnyOutput)
assert.True(t, ok)
v, known, secret, deps, err := await(out)
assert.True(t, known)
assert.False(t, secret)
assert.ElementsMatch(t, []Resource{stringDep1, stringDep2, intDep1, intDep2, boolDep1, boolDep2, res}, deps)
assert.NoError(t, err)
argsV := v.(*args)
so, ok := argsV.S.(StringOutput)
assert.True(t, ok)
assert.Equal(t, uint32(outputResolved), so.state)
assert.Equal(t, "a stringy output", so.value)
assert.ElementsMatch(t, []Resource{stringDep1, stringDep2}, so.deps)
io, ok := argsV.I.(IntOutput)
assert.True(t, ok)
assert.Equal(t, uint32(outputResolved), io.state)
assert.Equal(t, 42, io.value)
assert.ElementsMatch(t, []Resource{intDep1, intDep2}, io.deps)
ai, ok := argsV.A.(Map)
assert.True(t, ok)
bo, ok := ai["world"].(BoolOutput)
assert.True(t, ok)
assert.Equal(t, uint32(outputResolved), bo.getState().state)
assert.Equal(t, true, bo.value)
assert.ElementsMatch(t, []Resource{boolDep1, boolDep2}, bo.deps)
}
Redesign the Go SDK resource/input/output system. (#3506) The redesign is focused around providing better static typings and improved ease-of-use for the Go SDK. Most of the redesign revolves around three pivots: - Strongly-typed inputs, especially for nested types - Struct-based resource and invoke APIs - Ease-of-use of Apply 1. Strongly-typed inputs Input is the type of a generic input value for a Pulumi resource. This type is used in conjunction with Output to provide polymorphism over strongly-typed input values. The intended pattern for nested Pulumi value types is to define an input interface and a plain, input, and output variant of the value type that implement the input interface. For example, given a nested Pulumi value type with the following shape: ``` type Nested struct { Foo int Bar string } ``` We would define the following: ``` var nestedType = reflect.TypeOf((*Nested)(nil)).Elem() type NestedInput interface { pulumi.Input ToNestedOutput() NestedOutput ToNestedOutputWithContext(context.Context) NestedOutput } type Nested struct { Foo int `pulumi:"foo"` Bar string `pulumi:"bar"` } type NestedInputValue struct { Foo pulumi.IntInput `pulumi:"foo"` Bar pulumi.StringInput `pulumi:"bar"` } func (NestedInputValue) ElementType() reflect.Type { return nestedType } func (v NestedInputValue) ToNestedOutput() NestedOutput { return pulumi.ToOutput(v).(NestedOutput) } func (v NestedInputValue) ToNestedOutputWithContext(ctx context.Context) NestedOutput { return pulumi.ToOutputWithContext(ctx, v).(NestedOutput) } type NestedOutput struct { *pulumi.OutputState } func (NestedOutput) ElementType() reflect.Type { return nestedType } func (o NestedOutput) ToNestedOutput() NestedOutput { return o } func (o NestedOutput) ToNestedOutputWithContext(ctx context.Context) NestedOutput { return o } func (o NestedOutput) Foo() pulumi.IntOutput { return o.Apply(func (v Nested) int { return v.Foo }).(pulumi.IntOutput) } func (o NestedOutput) Bar() pulumi.StringOutput { return o.Apply(func (v Nested) string { return v.Bar }).(pulumi.StringOutput) } ``` The SDK provides input and output types for primitives, arrays, and maps. 2. Struct-based APIs Instead of providing expected output properties in the input map passed to {Read,Register}Resource and returning the outputs as a map, the user now passes a pointer to a struct that implements one of the Resource interfaces and has appropriately typed and tagged fields that represent its output properties. For example, given a custom resource with an int-typed output "foo" and a string-typed output "bar", we would define the following CustomResource type: ``` type MyResource struct { pulumi.CustomResourceState Foo pulumi.IntOutput `pulumi:"foo"` Bar pulumi.StringOutput `pulumi:"bar"` } ``` And invoke RegisterResource like so: ``` var resource MyResource err := ctx.RegisterResource(tok, name, props, &resource, opts...) ``` Invoke arguments and results are also provided via structs, but use plain-old Go types for their fields: ``` type MyInvokeArgs struct { Foo int `pulumi:"foo"` } type MyInvokeResult struct { Bar string `pulumi:"bar"` } var result MyInvokeResult err := ctx.Invoke(tok, MyInvokeArgs{Foo: 42}, &result, opts...) ``` 3. Ease-of-use of Apply All `Apply` methods now accept an interface{} as the callback type. The provided callback value must have one of the following signatures: func (v T) U func (v T) (U, error) func (ctx context.Context, v T) U func (ctx context.Context, v T) (U, error) T must be assignable from the ElementType of the Output. If U is a type that has a registered Output type, the result of the Apply will be the corresponding Output type. Otherwise, the result of the Apply will be AnyOutput. Fixes https://github.com/pulumi/pulumi/issues/2149. Fixes https://github.com/pulumi/pulumi/issues/3488. Fixes https://github.com/pulumi/pulumi/issues/3487. Fixes https://github.com/pulumi/pulumi-aws/issues/248. Fixes https://github.com/pulumi/pulumi/issues/3492. Fixes https://github.com/pulumi/pulumi/issues/3491. Fixes https://github.com/pulumi/pulumi/issues/3562.
2020-01-18 16:08:37 +01:00
type args struct {
S string
I int
A interface{}
}
type argsInputs struct {
S StringInput
I IntInput
A Input
}
func (*argsInputs) ElementType() reflect.Type {
return reflect.TypeOf((*args)(nil))
}
// Test that ToOutput correctly handles nested inputs when the argument is an input with no corresponding output type.
func TestToOutputInputAny(t *testing.T) {
out := ToOutput(&argsInputs{
S: ID("hello"),
I: Int(42),
A: Map{"world": Bool(true).ToBoolOutput()},
})
_, ok := out.(AnyOutput)
assert.True(t, ok)
v, known, secret, deps, err := await(out)
Redesign the Go SDK resource/input/output system. (#3506) The redesign is focused around providing better static typings and improved ease-of-use for the Go SDK. Most of the redesign revolves around three pivots: - Strongly-typed inputs, especially for nested types - Struct-based resource and invoke APIs - Ease-of-use of Apply 1. Strongly-typed inputs Input is the type of a generic input value for a Pulumi resource. This type is used in conjunction with Output to provide polymorphism over strongly-typed input values. The intended pattern for nested Pulumi value types is to define an input interface and a plain, input, and output variant of the value type that implement the input interface. For example, given a nested Pulumi value type with the following shape: ``` type Nested struct { Foo int Bar string } ``` We would define the following: ``` var nestedType = reflect.TypeOf((*Nested)(nil)).Elem() type NestedInput interface { pulumi.Input ToNestedOutput() NestedOutput ToNestedOutputWithContext(context.Context) NestedOutput } type Nested struct { Foo int `pulumi:"foo"` Bar string `pulumi:"bar"` } type NestedInputValue struct { Foo pulumi.IntInput `pulumi:"foo"` Bar pulumi.StringInput `pulumi:"bar"` } func (NestedInputValue) ElementType() reflect.Type { return nestedType } func (v NestedInputValue) ToNestedOutput() NestedOutput { return pulumi.ToOutput(v).(NestedOutput) } func (v NestedInputValue) ToNestedOutputWithContext(ctx context.Context) NestedOutput { return pulumi.ToOutputWithContext(ctx, v).(NestedOutput) } type NestedOutput struct { *pulumi.OutputState } func (NestedOutput) ElementType() reflect.Type { return nestedType } func (o NestedOutput) ToNestedOutput() NestedOutput { return o } func (o NestedOutput) ToNestedOutputWithContext(ctx context.Context) NestedOutput { return o } func (o NestedOutput) Foo() pulumi.IntOutput { return o.Apply(func (v Nested) int { return v.Foo }).(pulumi.IntOutput) } func (o NestedOutput) Bar() pulumi.StringOutput { return o.Apply(func (v Nested) string { return v.Bar }).(pulumi.StringOutput) } ``` The SDK provides input and output types for primitives, arrays, and maps. 2. Struct-based APIs Instead of providing expected output properties in the input map passed to {Read,Register}Resource and returning the outputs as a map, the user now passes a pointer to a struct that implements one of the Resource interfaces and has appropriately typed and tagged fields that represent its output properties. For example, given a custom resource with an int-typed output "foo" and a string-typed output "bar", we would define the following CustomResource type: ``` type MyResource struct { pulumi.CustomResourceState Foo pulumi.IntOutput `pulumi:"foo"` Bar pulumi.StringOutput `pulumi:"bar"` } ``` And invoke RegisterResource like so: ``` var resource MyResource err := ctx.RegisterResource(tok, name, props, &resource, opts...) ``` Invoke arguments and results are also provided via structs, but use plain-old Go types for their fields: ``` type MyInvokeArgs struct { Foo int `pulumi:"foo"` } type MyInvokeResult struct { Bar string `pulumi:"bar"` } var result MyInvokeResult err := ctx.Invoke(tok, MyInvokeArgs{Foo: 42}, &result, opts...) ``` 3. Ease-of-use of Apply All `Apply` methods now accept an interface{} as the callback type. The provided callback value must have one of the following signatures: func (v T) U func (v T) (U, error) func (ctx context.Context, v T) U func (ctx context.Context, v T) (U, error) T must be assignable from the ElementType of the Output. If U is a type that has a registered Output type, the result of the Apply will be the corresponding Output type. Otherwise, the result of the Apply will be AnyOutput. Fixes https://github.com/pulumi/pulumi/issues/2149. Fixes https://github.com/pulumi/pulumi/issues/3488. Fixes https://github.com/pulumi/pulumi/issues/3487. Fixes https://github.com/pulumi/pulumi-aws/issues/248. Fixes https://github.com/pulumi/pulumi/issues/3492. Fixes https://github.com/pulumi/pulumi/issues/3491. Fixes https://github.com/pulumi/pulumi/issues/3562.
2020-01-18 16:08:37 +01:00
assert.True(t, known)
assert.False(t, secret)
assert.Nil(t, deps)
Redesign the Go SDK resource/input/output system. (#3506) The redesign is focused around providing better static typings and improved ease-of-use for the Go SDK. Most of the redesign revolves around three pivots: - Strongly-typed inputs, especially for nested types - Struct-based resource and invoke APIs - Ease-of-use of Apply 1. Strongly-typed inputs Input is the type of a generic input value for a Pulumi resource. This type is used in conjunction with Output to provide polymorphism over strongly-typed input values. The intended pattern for nested Pulumi value types is to define an input interface and a plain, input, and output variant of the value type that implement the input interface. For example, given a nested Pulumi value type with the following shape: ``` type Nested struct { Foo int Bar string } ``` We would define the following: ``` var nestedType = reflect.TypeOf((*Nested)(nil)).Elem() type NestedInput interface { pulumi.Input ToNestedOutput() NestedOutput ToNestedOutputWithContext(context.Context) NestedOutput } type Nested struct { Foo int `pulumi:"foo"` Bar string `pulumi:"bar"` } type NestedInputValue struct { Foo pulumi.IntInput `pulumi:"foo"` Bar pulumi.StringInput `pulumi:"bar"` } func (NestedInputValue) ElementType() reflect.Type { return nestedType } func (v NestedInputValue) ToNestedOutput() NestedOutput { return pulumi.ToOutput(v).(NestedOutput) } func (v NestedInputValue) ToNestedOutputWithContext(ctx context.Context) NestedOutput { return pulumi.ToOutputWithContext(ctx, v).(NestedOutput) } type NestedOutput struct { *pulumi.OutputState } func (NestedOutput) ElementType() reflect.Type { return nestedType } func (o NestedOutput) ToNestedOutput() NestedOutput { return o } func (o NestedOutput) ToNestedOutputWithContext(ctx context.Context) NestedOutput { return o } func (o NestedOutput) Foo() pulumi.IntOutput { return o.Apply(func (v Nested) int { return v.Foo }).(pulumi.IntOutput) } func (o NestedOutput) Bar() pulumi.StringOutput { return o.Apply(func (v Nested) string { return v.Bar }).(pulumi.StringOutput) } ``` The SDK provides input and output types for primitives, arrays, and maps. 2. Struct-based APIs Instead of providing expected output properties in the input map passed to {Read,Register}Resource and returning the outputs as a map, the user now passes a pointer to a struct that implements one of the Resource interfaces and has appropriately typed and tagged fields that represent its output properties. For example, given a custom resource with an int-typed output "foo" and a string-typed output "bar", we would define the following CustomResource type: ``` type MyResource struct { pulumi.CustomResourceState Foo pulumi.IntOutput `pulumi:"foo"` Bar pulumi.StringOutput `pulumi:"bar"` } ``` And invoke RegisterResource like so: ``` var resource MyResource err := ctx.RegisterResource(tok, name, props, &resource, opts...) ``` Invoke arguments and results are also provided via structs, but use plain-old Go types for their fields: ``` type MyInvokeArgs struct { Foo int `pulumi:"foo"` } type MyInvokeResult struct { Bar string `pulumi:"bar"` } var result MyInvokeResult err := ctx.Invoke(tok, MyInvokeArgs{Foo: 42}, &result, opts...) ``` 3. Ease-of-use of Apply All `Apply` methods now accept an interface{} as the callback type. The provided callback value must have one of the following signatures: func (v T) U func (v T) (U, error) func (ctx context.Context, v T) U func (ctx context.Context, v T) (U, error) T must be assignable from the ElementType of the Output. If U is a type that has a registered Output type, the result of the Apply will be the corresponding Output type. Otherwise, the result of the Apply will be AnyOutput. Fixes https://github.com/pulumi/pulumi/issues/2149. Fixes https://github.com/pulumi/pulumi/issues/3488. Fixes https://github.com/pulumi/pulumi/issues/3487. Fixes https://github.com/pulumi/pulumi-aws/issues/248. Fixes https://github.com/pulumi/pulumi/issues/3492. Fixes https://github.com/pulumi/pulumi/issues/3491. Fixes https://github.com/pulumi/pulumi/issues/3562.
2020-01-18 16:08:37 +01:00
assert.NoError(t, err)
assert.Equal(t, &args{
S: "hello",
I: 42,
A: map[string]interface{}{"world": true},
}, v)
}
// Test that Unsecret will return an Output that has an unwrapped secret
func TestUnsecret(t *testing.T) {
s := ToSecret(String("foo"))
// assert that secret is immediately secret
assert.True(t, IsSecret(s))
unS := Unsecret(s)
// assert that we do not have a secret
assert.False(t, IsSecret(unS))
errChan := make(chan error)
resultChan := make(chan string)
secretChan := make(chan bool)
unS.ApplyT(func(v interface{}) (string, error) {
// assert secretness after the output resolves
secretChan <- IsSecret(unS)
val := v.(string)
if val == "foo" {
// validate the value
resultChan <- val
} else {
errChan <- fmt.Errorf("Invalid result: %v", val)
}
return val, nil
})
for i := 0; i < 2; i++ {
select {
case err := <-errChan:
assert.Nil(t, err)
break
case r := <-resultChan:
assert.Equal(t, "foo", r)
break
case isSecret := <-secretChan:
assert.False(t, isSecret)
break
}
}
}
// Test that SecretT sets appropriate internal state and that IsSecret appropriately reads it.
func TestSecrets(t *testing.T) {
s := ToSecret(String("foo"))
// assert that secret is immediately secret
assert.True(t, IsSecret(s))
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 <- IsSecret(s)
val := v.(string)
if val == "foo" {
// validate the value
resultChan <- val
} else {
errChan <- fmt.Errorf("Invalid result: %v", val)
}
return val, nil
})
for i := 0; i < 2; i++ {
select {
case err := <-errChan:
assert.Nil(t, err)
break
case r := <-resultChan:
assert.Equal(t, "foo", r)
break
case isSecret := <-secretChan:
assert.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, IsSecret(s1))
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 <- IsSecret(s)
val := v.(string)
if val == "foobar" {
// validate the value
resultChan <- val
} else {
errChan <- fmt.Errorf("Invalid result: %v", val)
}
return val, nil
})
for i := 0; i < 2; i++ {
select {
case err := <-errChan:
assert.Nil(t, err)
break
case r := <-resultChan:
assert.Equal(t, "foobar", r)
break
case isSecret := <-secretChan:
assert.True(t, isSecret)
break
}
}
}
func TestNil(t *testing.T) {
ao := Any(nil)
v, known, secret, deps, err := await(ao)
assert.True(t, known)
assert.False(t, secret)
assert.Nil(t, deps)
assert.NoError(t, err)
assert.Equal(t, nil, v)
o := ToOutput(nil)
v, known, secret, deps, err = await(o)
assert.True(t, known)
assert.False(t, secret)
assert.Nil(t, deps)
assert.NoError(t, err)
assert.Equal(t, nil, v)
o = ToOutput(ao)
v, known, secret, deps, err = await(o)
assert.True(t, known)
assert.False(t, secret)
assert.Nil(t, deps)
assert.NoError(t, err)
assert.Equal(t, nil, v)
ao = ToOutput("").ApplyT(func(v string) interface{} {
return nil
}).(AnyOutput)
v, known, secret, deps, err = await(ao)
assert.True(t, known)
assert.False(t, secret)
assert.Nil(t, deps)
assert.NoError(t, err)
assert.Equal(t, nil, v)
bo := ao.ApplyT(func(x interface{}) bool {
return x == nil
})
v, known, secret, deps, err = await(bo)
assert.True(t, known)
assert.False(t, secret)
assert.Nil(t, deps)
assert.NoError(t, err)
assert.Equal(t, true, v)
}
// Test that dependencies flow through all/apply.
func TestDeps(t *testing.T) {
stringDep1, stringDep2 := &ResourceState{}, &ResourceState{}
Await outstanding async work in Go. (#6983) The Pulumi Go SDK does not currently await all outstanding asynchronous work associated with a Pulumi program. Because all relevant asynchronous work is created via the Pulumi SDK, we can track this asynchronous work and ensure that it has all completed prior to returning from `Context.Run`. This is complicated by the fact that many of the existing APIs that are able to create `Output`s--`NewOutput`, `ToOutput`, `Any`, `ToSecret`, and `All`--do not have a `*Context` parameter, and so have no straightforward way to associate themselves with a `*Context`. To address this, these changes add new versions of each of these APIs as methods on `*Context`. Despite these new methods, most Pulumi programs should work without changes: the bulk of `Output`s are created by the SDK itself as part of resource registration, and for `Any` and `All`, we can pick up the context from any `Output`s present in the arguments. The only programs that should require changes are those that create outputs from whole cloth using `NewOutput`, `ToOutput`, or `ToSecret` and create unawaited async work rooted at those outputs. On an implementation level, these changes track asynchronous work using a `sync.WaitGroup` associated with each `*Context`. This `WaitGroup` is passed to each output associated with the context. The SDK increments this `WaitGroup`'s count prior to starting any asynchronous work and decrements it once the work (including any callbacks triggered by the work) is complete. This fixes the Go portion of #3991.
2021-05-14 21:00:21 +02:00
stringOut := StringOutput{newOutputState(nil, reflect.TypeOf(""), stringDep1)}
assert.ElementsMatch(t, []Resource{stringDep1}, stringOut.deps)
go func() {
stringOut.resolve("hello", true, false, []Resource{stringDep2})
}()
boolDep1, boolDep2 := &ResourceState{}, &ResourceState{}
Await outstanding async work in Go. (#6983) The Pulumi Go SDK does not currently await all outstanding asynchronous work associated with a Pulumi program. Because all relevant asynchronous work is created via the Pulumi SDK, we can track this asynchronous work and ensure that it has all completed prior to returning from `Context.Run`. This is complicated by the fact that many of the existing APIs that are able to create `Output`s--`NewOutput`, `ToOutput`, `Any`, `ToSecret`, and `All`--do not have a `*Context` parameter, and so have no straightforward way to associate themselves with a `*Context`. To address this, these changes add new versions of each of these APIs as methods on `*Context`. Despite these new methods, most Pulumi programs should work without changes: the bulk of `Output`s are created by the SDK itself as part of resource registration, and for `Any` and `All`, we can pick up the context from any `Output`s present in the arguments. The only programs that should require changes are those that create outputs from whole cloth using `NewOutput`, `ToOutput`, or `ToSecret` and create unawaited async work rooted at those outputs. On an implementation level, these changes track asynchronous work using a `sync.WaitGroup` associated with each `*Context`. This `WaitGroup` is passed to each output associated with the context. The SDK increments this `WaitGroup`'s count prior to starting any asynchronous work and decrements it once the work (including any callbacks triggered by the work) is complete. This fixes the Go portion of #3991.
2021-05-14 21:00:21 +02:00
boolOut := BoolOutput{newOutputState(nil, reflect.TypeOf(true), boolDep1)}
assert.ElementsMatch(t, []Resource{boolDep1}, boolOut.deps)
go func() {
boolOut.resolve(true, true, false, []Resource{boolDep2})
}()
a := All(stringOut, boolOut).ApplyT(func(args []interface{}) (string, error) {
s := args[0].(string)
b := args[1].(bool)
return fmt.Sprintf("%s: %v", s, b), nil
})
v, known, secret, deps, err := await(a)
assert.Equal(t, "hello: true", v)
assert.True(t, known)
assert.False(t, secret)
assert.ElementsMatch(t, []Resource{stringDep1, stringDep2, boolDep1, boolDep2}, deps)
assert.NoError(t, err)
}
Await outstanding async work in Go. (#6983) The Pulumi Go SDK does not currently await all outstanding asynchronous work associated with a Pulumi program. Because all relevant asynchronous work is created via the Pulumi SDK, we can track this asynchronous work and ensure that it has all completed prior to returning from `Context.Run`. This is complicated by the fact that many of the existing APIs that are able to create `Output`s--`NewOutput`, `ToOutput`, `Any`, `ToSecret`, and `All`--do not have a `*Context` parameter, and so have no straightforward way to associate themselves with a `*Context`. To address this, these changes add new versions of each of these APIs as methods on `*Context`. Despite these new methods, most Pulumi programs should work without changes: the bulk of `Output`s are created by the SDK itself as part of resource registration, and for `Any` and `All`, we can pick up the context from any `Output`s present in the arguments. The only programs that should require changes are those that create outputs from whole cloth using `NewOutput`, `ToOutput`, or `ToSecret` and create unawaited async work rooted at those outputs. On an implementation level, these changes track asynchronous work using a `sync.WaitGroup` associated with each `*Context`. This `WaitGroup` is passed to each output associated with the context. The SDK increments this `WaitGroup`'s count prior to starting any asynchronous work and decrements it once the work (including any callbacks triggered by the work) is complete. This fixes the Go portion of #3991.
2021-05-14 21:00:21 +02:00
func testMixedWaitGroups(t *testing.T, combine func(o1, o2 Output) Output) {
var wg1, wg2 workGroup
Await outstanding async work in Go. (#6983) The Pulumi Go SDK does not currently await all outstanding asynchronous work associated with a Pulumi program. Because all relevant asynchronous work is created via the Pulumi SDK, we can track this asynchronous work and ensure that it has all completed prior to returning from `Context.Run`. This is complicated by the fact that many of the existing APIs that are able to create `Output`s--`NewOutput`, `ToOutput`, `Any`, `ToSecret`, and `All`--do not have a `*Context` parameter, and so have no straightforward way to associate themselves with a `*Context`. To address this, these changes add new versions of each of these APIs as methods on `*Context`. Despite these new methods, most Pulumi programs should work without changes: the bulk of `Output`s are created by the SDK itself as part of resource registration, and for `Any` and `All`, we can pick up the context from any `Output`s present in the arguments. The only programs that should require changes are those that create outputs from whole cloth using `NewOutput`, `ToOutput`, or `ToSecret` and create unawaited async work rooted at those outputs. On an implementation level, these changes track asynchronous work using a `sync.WaitGroup` associated with each `*Context`. This `WaitGroup` is passed to each output associated with the context. The SDK increments this `WaitGroup`'s count prior to starting any asynchronous work and decrements it once the work (including any callbacks triggered by the work) is complete. This fixes the Go portion of #3991.
2021-05-14 21:00:21 +02:00
o1 := newOutput(&wg1, anyOutputType)
o2 := newOutput(&wg2, anyOutputType)
gate := make(chan chan bool)
combine(o1, o2).ApplyT(func(_ interface{}) interface{} {
<-gate <- true
return 0
})
wg1Done, wg2Done := false, false
go func() {
wg1.Wait()
wg1Done = true
wg2.Wait()
wg2Done = true
}()
o1.getState().resolve(0, true, true, nil)
o2.getState().resolve(0, true, true, nil)
c := make(chan bool)
gate <- c
assert.False(t, wg1Done)
assert.False(t, wg2Done)
<-c
wg1.Wait()
wg2.Wait()
}
func TestMixedWaitGroupsAll(t *testing.T) {
testMixedWaitGroups(t, func(o1, o2 Output) Output {
return All(o1, o2)
})
}
func TestMixedWaitGroupsAny(t *testing.T) {
testMixedWaitGroups(t, func(o1, o2 Output) Output {
return Any(struct{ O1, O2 Output }{o1, o2})
})
}
func TestMixedWaitGroupsApply(t *testing.T) {
testMixedWaitGroups(t, func(o1, o2 Output) Output {
return o1.ApplyT(func(_ interface{}) interface{} {
return o2
})
})
}
type Foo interface {
}
type FooInput interface {
Input
ToFooOutput() Output
}
type FooArgs struct {
}
func (FooArgs) ElementType() reflect.Type {
return nil
}
func TestRegisterInputType(t *testing.T) {
assert.PanicsWithError(t, "expected string to be an interface", func() {
RegisterInputType(reflect.TypeOf(""), FooArgs{})
})
assert.PanicsWithError(t, "expected pulumi.Foo to implement pulumi.Input", func() {
RegisterInputType(reflect.TypeOf((*Foo)(nil)).Elem(), FooArgs{})
})
assert.PanicsWithError(t, "expected pulumi.FooArgs to implement interface pulumi.FooInput", func() {
RegisterInputType(reflect.TypeOf((*FooInput)(nil)).Elem(), FooArgs{})
})
}