pulumi/sdk/go/pulumi/rpc_test.go
Justin Van Patten fbf62399ca
[sdk/go] Fix regression marshaling assets/archives (#8290)
This change fixes a regression marshaling assets/archives that was introduced after adding support for marshaling output values.

For example, when setting an `Asset` on a field typed as `AssetOrAchiveInput`, in `marshalInput`, the input was being converted into an `AssetOrArchiveOutput` via the `ToAssetOrArchiveOutputWithContext`. Awaiting the output returns an `*asset` struct, which is itself an `AssetInput`, which causes infinite recursion when passed recursively to `marshalInput`.

The fix is to skip the `Input` checks in recursive calls, which is equivalent to the previous behavior before the regression was introduced.

Another issue was that when the input is converted to an output, this would result in `marshalInput` always returning an output value, even if the original passed-in value was not an output. To address this, if the output's value is known, not a secret, and has no dependencies, we can return the value itself rather than wrapping it as an output.
2021-10-27 08:44:42 -07:00

1713 lines
54 KiB
Go

// 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: unused,deadcode,lll
package pulumi
import (
"context"
"fmt"
"reflect"
"testing"
"github.com/blang/semver"
structpb "github.com/golang/protobuf/ptypes/struct"
"github.com/pulumi/pulumi/sdk/v3/go/common/resource"
"github.com/pulumi/pulumi/sdk/v3/go/common/resource/plugin"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
type simpleComponentResource struct {
ResourceState
}
func newSimpleComponentResource(ctx *Context, urn URN) ComponentResource {
var res simpleComponentResource
res.urn.OutputState = ctx.newOutputState(res.urn.ElementType(), &res)
res.urn.resolve(urn, true, false, nil)
return &res
}
type simpleCustomResource struct {
CustomResourceState
}
func newSimpleCustomResource(ctx *Context, urn URN, id ID) CustomResource {
var res simpleCustomResource
res.urn.OutputState = ctx.newOutputState(res.urn.ElementType(), &res)
res.id.OutputState = ctx.newOutputState(res.id.ElementType(), &res)
res.urn.resolve(urn, true, false, nil)
res.id.resolve(id, id != "", false, nil)
return &res
}
type simpleProviderResource struct {
ProviderResourceState
}
func newSimpleProviderResource(ctx *Context, urn URN, id ID) ProviderResource {
var res simpleProviderResource
res.urn.OutputState = ctx.newOutputState(res.urn.ElementType(), &res)
res.id.OutputState = ctx.newOutputState(res.id.ElementType(), &res)
res.urn.resolve(urn, true, false, nil)
res.id.resolve(id, id != "", false, nil)
res.pkg = string(resource.URN(urn).Type().Name())
return &res
}
type testResourcePackage struct {
version semver.Version
}
func (rp *testResourcePackage) ConstructProvider(ctx *Context, name, typ, urn string) (ProviderResource, error) {
if typ != "pulumi:providers:test" {
return nil, fmt.Errorf("unknown provider type %v", typ)
}
id := "id"
if resource.URN(urn).Name() == "preview" {
id = ""
}
return newSimpleProviderResource(ctx, URN(urn), ID(id)), nil
}
func (rp *testResourcePackage) Version() semver.Version {
return rp.version
}
type testResourceModule struct {
version semver.Version
}
func (rm *testResourceModule) Construct(ctx *Context, name, typ, urn string) (Resource, error) {
switch typ {
case "test:index:custom":
id := "id"
if resource.URN(urn).Name() == "preview" {
id = ""
}
return newSimpleCustomResource(ctx, URN(urn), ID(id)), nil
case "test:index:component":
return newSimpleComponentResource(ctx, URN(urn)), nil
default:
return nil, fmt.Errorf("unknown resource type %v", typ)
}
}
func (rm *testResourceModule) Version() semver.Version {
return rm.version
}
type test struct {
S string `pulumi:"s"`
A bool `pulumi:"a"`
B int `pulumi:"b"`
StringAsset Asset `pulumi:"cStringAsset"`
FileAsset Asset `pulumi:"cFileAsset"`
RemoteAsset Asset `pulumi:"cRemoteAsset"`
AssetArchive Archive `pulumi:"dAssetArchive"`
FileArchive Archive `pulumi:"dFileArchive"`
RemoteArchive Archive `pulumi:"dRemoteArchive"`
E interface{} `pulumi:"e"`
Array []interface{} `pulumi:"fArray"`
Map map[string]interface{} `pulumi:"fMap"`
G string `pulumi:"g"`
H string `pulumi:"h"`
I string `pulumi:"i"`
CustomResource CustomResource `pulumi:"jCustomResource"`
ComponentResource ComponentResource `pulumi:"jComponentResource"`
ProviderResource ProviderResource `pulumi:"jProviderResource"`
PreviewCustomResource CustomResource `pulumi:"jPreviewCustomResource"`
PreviewProviderResource ProviderResource `pulumi:"jPreviewProviderResource"`
MissingCustomResource CustomResource `pulumi:"kCustomResource"`
MissingComponentResource ComponentResource `pulumi:"kComponentResource"`
MissingProviderResource ProviderResource `pulumi:"kProviderResource"`
MissingPreviewCustomResource CustomResource `pulumi:"kPreviewCustomResource"`
MissingPreviewProviderResource ProviderResource `pulumi:"kPreviewProviderResource"`
}
type testInputs struct {
S StringInput
A BoolInput
B IntInput
StringAsset AssetInput
FileAsset AssetInput
RemoteAsset AssetInput
AssetArchive ArchiveInput
FileArchive ArchiveInput
RemoteArchive ArchiveInput
E Input
Array ArrayInput
Map MapInput
G StringInput
H StringInput
I StringInput
CustomResource CustomResource
ComponentResource ComponentResource
ProviderResource ProviderResource
PreviewCustomResource CustomResource
PreviewProviderResource ProviderResource
MissingCustomResource CustomResource
MissingComponentResource ComponentResource
MissingProviderResource ProviderResource
MissingPreviewCustomResource CustomResource
MissingPreviewProviderResource ProviderResource
}
func (testInputs) ElementType() reflect.Type {
return reflect.TypeOf(test{})
}
// TestMarshalRoundtrip ensures that marshaling a complex structure to and from its on-the-wire gRPC format succeeds.
func TestMarshalRoundtrip(t *testing.T) {
// Create interesting inputs.
ctx, err := NewContext(context.Background(), RunInfo{})
assert.Nil(t, err)
customURN := resource.NewURN("stack", "project", "", "test:index:custom", "test")
componentURN := resource.NewURN("stack", "project", "", "test:index:component", "test")
providerURN := resource.NewURN("stack", "project", "", "pulumi:providers:test", "test")
previewCustomURN := resource.NewURN("stack", "project", "", "test:index:custom", "preview")
previewProviderURN := resource.NewURN("stack", "project", "", "pulumi:providers:test", "preview")
missingCustomURN := resource.NewURN("stack", "project", "", "missing:index:custom", "test")
missingComponentURN := resource.NewURN("stack", "project", "", "missing:index:component", "test")
missingProviderURN := resource.NewURN("stack", "project", "", "pulumi:providers:missing", "test")
missingPreviewCustomURN := resource.NewURN("stack", "project", "", "missing:index:custom", "preview")
missingPreviewProviderURN := resource.NewURN("stack", "project", "", "pulumi:providers:missing", "preview")
RegisterResourcePackage("test", &testResourcePackage{})
RegisterResourceModule("test", "index", &testResourceModule{})
out, resolve, _ := ctx.NewOutput()
resolve("outputty")
out2 := ctx.newOutputState(reflect.TypeOf(""))
out2.fulfill(nil, false, false, nil, nil)
inputs := testInputs{
S: String("a string"),
A: Bool(true),
B: Int(42),
StringAsset: NewStringAsset("put a lime in the coconut"),
FileAsset: NewFileAsset("foo.txt"),
RemoteAsset: NewRemoteAsset("https://pulumi.com/fake/txt"),
AssetArchive: NewAssetArchive(map[string]interface{}{
"subAsset": NewFileAsset("bar.txt"),
"subArchive": NewFileArchive("bar.zip"),
}),
FileArchive: NewFileArchive("foo.zip"),
RemoteArchive: NewRemoteArchive("https://pulumi.com/fake/archive.zip"),
E: out,
Array: Array{Int(0), Float64(1.3), String("x"), Bool(false)},
Map: Map{
"x": String("y"),
"y": Float64(999.9),
"z": Bool(false),
},
G: StringOutput{out2},
H: URN("foo"),
I: StringOutput{},
CustomResource: newSimpleCustomResource(ctx, URN(customURN), "id"),
ComponentResource: ctx.newDependencyResource(URN(componentURN)),
ProviderResource: newSimpleProviderResource(ctx, URN(providerURN), "id"),
PreviewCustomResource: newSimpleCustomResource(ctx, URN(previewCustomURN), ""),
PreviewProviderResource: newSimpleProviderResource(ctx, URN(previewProviderURN), ""),
MissingCustomResource: newSimpleCustomResource(ctx, URN(missingCustomURN), "id"),
MissingComponentResource: ctx.newDependencyResource(URN(missingComponentURN)),
MissingProviderResource: newSimpleProviderResource(ctx, URN(missingProviderURN), "id"),
MissingPreviewCustomResource: newSimpleCustomResource(ctx, URN(missingPreviewCustomURN), ""),
MissingPreviewProviderResource: newSimpleProviderResource(ctx, URN(missingPreviewProviderURN), ""),
}
// Marshal those inputs.
resolved, pdeps, deps, err := marshalInputs(inputs)
assert.Nil(t, err)
if assert.Nil(t, err) {
assert.Equal(t, reflect.TypeOf(inputs).NumField(), len(resolved))
assert.Equal(t, 10, len(deps))
assert.Equal(t, 10, len(pdeps))
// Now just unmarshal and ensure the resulting map matches.
resV, secret, err := unmarshalPropertyValue(ctx, resource.NewObjectProperty(resolved))
assert.False(t, secret)
if assert.Nil(t, err) {
if assert.NotNil(t, resV) {
res := resV.(map[string]interface{})
assert.Equal(t, "a string", res["s"])
assert.Equal(t, true, res["a"])
assert.Equal(t, 42.0, res["b"])
assert.Equal(t, "put a lime in the coconut", res["cStringAsset"].(Asset).Text())
assert.Equal(t, "foo.txt", res["cFileAsset"].(Asset).Path())
assert.Equal(t, "https://pulumi.com/fake/txt", res["cRemoteAsset"].(Asset).URI())
ar := res["dAssetArchive"].(Archive).Assets()
assert.Equal(t, 2, len(ar))
assert.Equal(t, "bar.txt", ar["subAsset"].(Asset).Path())
assert.Equal(t, "bar.zip", ar["subArchive"].(Archive).Path())
assert.Equal(t, "foo.zip", res["dFileArchive"].(Archive).Path())
assert.Equal(t, "https://pulumi.com/fake/archive.zip", res["dRemoteArchive"].(Archive).URI())
assert.Equal(t, "outputty", res["e"])
aa := res["fArray"].([]interface{})
assert.Equal(t, 4, len(aa))
assert.Equal(t, 0.0, aa[0])
assert.Less(t, 1.3-aa[1].(float64), 0.00001)
assert.Equal(t, 1.3-aa[1].(float64), 0.0)
assert.Equal(t, "x", aa[2])
assert.Equal(t, false, aa[3])
am := res["fMap"].(map[string]interface{})
assert.Equal(t, 3, len(am))
assert.Equal(t, "y", am["x"])
assert.Equal(t, 999.9, am["y"])
assert.Equal(t, false, am["z"])
assert.Equal(t, nil, res["g"])
assert.Equal(t, "foo", res["h"])
assert.Equal(t, nil, res["i"])
custom := res["jCustomResource"].(*simpleCustomResource)
urn, _, _, _ := custom.URN().awaitURN(context.Background())
assert.Equal(t, URN(customURN), urn)
id, _, _, _ := custom.ID().awaitID(context.Background())
assert.Equal(t, ID("id"), id)
component := res["jComponentResource"].(*simpleComponentResource)
urn, _, _, _ = component.URN().awaitURN(context.Background())
assert.Equal(t, URN(componentURN), urn)
provider := res["jProviderResource"].(*simpleProviderResource)
urn, _, _, _ = provider.URN().awaitURN(context.Background())
assert.Equal(t, URN(providerURN), urn)
id, _, _, _ = provider.ID().awaitID(context.Background())
assert.Equal(t, ID("id"), id)
previewCustom := res["jPreviewCustomResource"].(*simpleCustomResource)
urn, _, _, _ = previewCustom.URN().awaitURN(context.Background())
assert.Equal(t, URN(previewCustomURN), urn)
_, known, _, _ := previewCustom.ID().awaitID(context.Background())
assert.False(t, known)
previewProvider := res["jPreviewProviderResource"].(*simpleProviderResource)
urn, _, _, _ = previewProvider.URN().awaitURN(context.Background())
assert.Equal(t, URN(previewProviderURN), urn)
_, known, _, _ = previewProvider.ID().awaitID(context.Background())
assert.False(t, known)
missingCustom := res["kCustomResource"].(CustomResource)
urn, _, _, _ = missingCustom.URN().awaitURN(context.Background())
assert.Equal(t, URN(missingCustomURN), urn)
id, _, _, _ = missingCustom.ID().awaitID(context.Background())
assert.Equal(t, ID("id"), id)
missingComponent := res["kComponentResource"].(ComponentResource)
urn, _, _, _ = missingComponent.URN().awaitURN(context.Background())
assert.Equal(t, URN(missingComponentURN), urn)
missingProvider := res["kProviderResource"].(ProviderResource)
urn, _, _, _ = missingProvider.URN().awaitURN(context.Background())
assert.Equal(t, URN(missingProviderURN), urn)
id, _, _, _ = missingProvider.ID().awaitID(context.Background())
assert.Equal(t, ID("id"), id)
missingPreviewCustom := res["kPreviewCustomResource"].(CustomResource)
urn, _, _, _ = missingPreviewCustom.URN().awaitURN(context.Background())
assert.Equal(t, URN(missingPreviewCustomURN), urn)
_, known, _, _ = missingPreviewCustom.ID().awaitID(context.Background())
assert.False(t, known)
missingPreviewProvider := res["kPreviewProviderResource"].(ProviderResource)
urn, _, _, _ = missingPreviewProvider.URN().awaitURN(context.Background())
assert.Equal(t, URN(missingPreviewProviderURN), urn)
_, known, _, _ = missingPreviewProvider.ID().awaitID(context.Background())
assert.False(t, known)
}
}
}
}
type nestedTypeInput interface {
Input
}
var nestedTypeType = reflect.TypeOf((*nestedType)(nil)).Elem()
type nestedType struct {
Foo string `pulumi:"foo"`
Bar int `pulumi:"bar"`
}
type nestedTypeInputs struct {
Foo StringInput `pulumi:"foo"`
Bar IntInput `pulumi:"bar"`
}
func (nestedTypeInputs) ElementType() reflect.Type {
return nestedTypeType
}
func (nestedTypeInputs) isNestedType() {}
type nestedTypeOutput struct{ *OutputState }
func (nestedTypeOutput) ElementType() reflect.Type {
return nestedTypeType
}
func (nestedTypeOutput) isNestedType() {}
func init() {
RegisterOutputType(nestedTypeOutput{})
}
type testResourceArgs struct {
URN URN `pulumi:"urn"`
ID ID `pulumi:"id"`
Any interface{} `pulumi:"any"`
Archive Archive `pulumi:"archive"`
Array []interface{} `pulumi:"array"`
Asset Asset `pulumi:"asset"`
Bool bool `pulumi:"bool"`
Float64 float64 `pulumi:"float64"`
Int int `pulumi:"int"`
Map map[string]interface{} `pulumi:"map"`
String string `pulumi:"string"`
Nested nestedType `pulumi:"nested"`
}
type testResourceInputs struct {
URN URNInput
ID IDInput
Any Input
Archive ArchiveInput
Array ArrayInput
Asset AssetInput
Bool BoolInput
Float64 Float64Input
Int IntInput
Map MapInput
String StringInput
Nested nestedTypeInput
}
func (*testResourceInputs) ElementType() reflect.Type {
return reflect.TypeOf((*testResourceArgs)(nil))
}
type testResource struct {
CustomResourceState
Any AnyOutput `pulumi:"any"`
Archive ArchiveOutput `pulumi:"archive"`
Array ArrayOutput `pulumi:"array"`
Asset AssetOutput `pulumi:"asset"`
Bool BoolOutput `pulumi:"bool"`
Float64 Float64Output `pulumi:"float64"`
Int IntOutput `pulumi:"int"`
Map MapOutput `pulumi:"map"`
String StringOutput `pulumi:"string"`
Nested nestedTypeOutput `pulumi:"nested"`
}
func TestResourceState(t *testing.T) {
ctx, err := NewContext(context.Background(), RunInfo{})
assert.Nil(t, err)
var theResource testResource
state := ctx.makeResourceState("", "", &theResource, nil, nil, "", nil, nil)
resolved, _, _, _ := marshalInputs(&testResourceInputs{
Any: String("foo"),
Archive: NewRemoteArchive("https://pulumi.com/fake/archive.zip"),
Array: Array{String("foo")},
Asset: NewStringAsset("put a lime in the coconut"),
Bool: Bool(true),
Float64: Float64(3.14),
Int: Int(-1),
Map: Map{"foo": String("bar")},
String: String("qux"),
Nested: nestedTypeInputs{
Foo: String("bar"),
Bar: Int(42),
},
})
s, err := plugin.MarshalProperties(
resolved,
plugin.MarshalOptions{KeepUnknowns: true})
assert.NoError(t, err)
state.resolve(ctx, nil, nil, "foo", "bar", s, nil)
input := &testResourceInputs{
URN: theResource.URN(),
ID: theResource.ID(),
Any: theResource.Any,
Archive: theResource.Archive,
Array: theResource.Array,
Asset: theResource.Asset,
Bool: theResource.Bool,
Float64: theResource.Float64,
Int: theResource.Int,
Map: theResource.Map,
String: theResource.String,
Nested: theResource.Nested,
}
resolved, pdeps, deps, err := marshalInputs(input)
assert.Nil(t, err)
assert.Equal(t, map[string][]URN{
"urn": {"foo"},
"id": {"foo"},
"any": {"foo"},
"archive": {"foo"},
"array": {"foo"},
"asset": {"foo"},
"bool": {"foo"},
"float64": {"foo"},
"int": {"foo"},
"map": {"foo"},
"string": {"foo"},
"nested": {"foo"},
}, pdeps)
assert.Equal(t, []URN{"foo"}, deps)
res, secret, err := unmarshalPropertyValue(ctx, resource.NewObjectProperty(resolved))
assert.Nil(t, err)
assert.False(t, secret)
assert.Equal(t, map[string]interface{}{
"urn": "foo",
"id": "bar",
"any": "foo",
"archive": NewRemoteArchive("https://pulumi.com/fake/archive.zip"),
"array": []interface{}{"foo"},
"asset": NewStringAsset("put a lime in the coconut"),
"bool": true,
"float64": 3.14,
"int": -1.0,
"map": map[string]interface{}{"foo": "bar"},
"string": "qux",
"nested": map[string]interface{}{
"foo": "bar",
"bar": 42.0,
},
}, res)
}
func TestUnmarshalSecret(t *testing.T) {
ctx, err := NewContext(context.Background(), RunInfo{})
assert.Nil(t, err)
secret := resource.MakeSecret(resource.NewPropertyValue("foo"))
_, isSecret, err := unmarshalPropertyValue(ctx, secret)
assert.Nil(t, err)
assert.True(t, isSecret)
var sv string
isSecret, err = unmarshalOutput(ctx, secret, reflect.ValueOf(&sv).Elem())
assert.Nil(t, err)
assert.Equal(t, "foo", sv)
assert.True(t, isSecret)
}
func TestUnmarshalInternalMapValue(t *testing.T) {
ctx, err := NewContext(context.Background(), RunInfo{})
assert.Nil(t, err)
m := make(map[string]interface{})
m["foo"] = "bar"
m["__default"] = "buzz"
pmap := resource.NewObjectProperty(resource.NewPropertyMapFromMap(m))
var mv map[string]string
_, err = unmarshalOutput(ctx, pmap, reflect.ValueOf(&mv).Elem())
assert.Nil(t, err)
val, ok := mv["foo"]
assert.True(t, ok)
assert.Equal(t, "bar", val)
_, ok = mv["__default"]
assert.False(t, ok)
}
// TestMarshalRoundtripNestedSecret ensures that marshaling a complex structure to and from
// its on-the-wire gRPC format succeeds including a nested secret property.
func TestMarshalRoundtripNestedSecret(t *testing.T) {
// Create interesting inputs.
ctx, err := NewContext(context.Background(), RunInfo{})
assert.Nil(t, err)
out, resolve, _ := NewOutput()
resolve("outputty")
out2 := ctx.newOutputState(reflect.TypeOf(""))
out2.fulfill(nil, false, true, nil, nil)
inputs := testInputs{
S: String("a string"),
A: Bool(true),
B: Int(42),
StringAsset: NewStringAsset("put a lime in the coconut"),
FileAsset: NewFileAsset("foo.txt"),
RemoteAsset: NewRemoteAsset("https://pulumi.com/fake/txt"),
AssetArchive: NewAssetArchive(map[string]interface{}{
"subAsset": NewFileAsset("bar.txt"),
"subArchive": NewFileArchive("bar.zip"),
}),
FileArchive: NewFileArchive("foo.zip"),
RemoteArchive: NewRemoteArchive("https://pulumi.com/fake/archive.zip"),
E: out,
Array: Array{Int(0), Float64(1.3), String("x"), Bool(false)},
Map: Map{
"x": String("y"),
"y": Float64(999.9),
"z": Bool(false),
},
G: StringOutput{out2},
H: URN("foo"),
I: StringOutput{},
}
// Marshal those inputs.
resolved, pdeps, deps, err := marshalInputs(inputs)
assert.Nil(t, err)
if assert.Nil(t, err) {
// The value we marshaled above omits the 10 Resource-typed fields, so we don't expect those fields to appear
// in the unmarshaled value.
const resourceFields = 10
assert.Equal(t, reflect.TypeOf(inputs).NumField()-resourceFields, len(resolved))
assert.Equal(t, 0, len(deps))
assert.Equal(t, 0, len(pdeps))
// Now just unmarshal and ensure the resulting map matches.
resV, secret, err := unmarshalPropertyValue(ctx, resource.NewObjectProperty(resolved))
assert.True(t, secret)
if assert.Nil(t, err) {
if assert.NotNil(t, resV) {
res := resV.(map[string]interface{})
assert.Equal(t, "a string", res["s"])
assert.Equal(t, true, res["a"])
assert.Equal(t, 42.0, res["b"])
assert.Equal(t, "put a lime in the coconut", res["cStringAsset"].(Asset).Text())
assert.Equal(t, "foo.txt", res["cFileAsset"].(Asset).Path())
assert.Equal(t, "https://pulumi.com/fake/txt", res["cRemoteAsset"].(Asset).URI())
ar := res["dAssetArchive"].(Archive).Assets()
assert.Equal(t, 2, len(ar))
assert.Equal(t, "bar.txt", ar["subAsset"].(Asset).Path())
assert.Equal(t, "bar.zip", ar["subArchive"].(Archive).Path())
assert.Equal(t, "foo.zip", res["dFileArchive"].(Archive).Path())
assert.Equal(t, "https://pulumi.com/fake/archive.zip", res["dRemoteArchive"].(Archive).URI())
assert.Equal(t, "outputty", res["e"])
aa := res["fArray"].([]interface{})
assert.Equal(t, 4, len(aa))
assert.Equal(t, 0.0, aa[0])
assert.Less(t, 1.3-aa[1].(float64), 0.00001)
assert.Equal(t, 1.3-aa[1].(float64), 0.0)
assert.Equal(t, "x", aa[2])
assert.Equal(t, false, aa[3])
am := res["fMap"].(map[string]interface{})
assert.Equal(t, 3, len(am))
assert.Equal(t, "y", am["x"])
assert.Equal(t, 999.9, am["y"])
assert.Equal(t, false, am["z"])
assert.Equal(t, nil, res["g"])
assert.Equal(t, "foo", res["h"])
assert.Equal(t, nil, res["i"])
}
}
}
}
type UntypedArgs map[string]interface{}
func (UntypedArgs) ElementType() reflect.Type {
return reflect.TypeOf((*map[string]interface{})(nil)).Elem()
}
func TestMapInputMarshalling(t *testing.T) {
var theResource simpleCustomResource
out := newOutput(nil, reflect.TypeOf((*StringOutput)(nil)).Elem(), &theResource)
out.getState().resolve("outputty", true, false, nil)
inputs1 := Map(map[string]Input{
"prop": out,
"nested": Map(map[string]Input{
"foo": String("foo"),
"bar": Int(42),
}),
})
inputs2 := UntypedArgs(map[string]interface{}{
"prop": "outputty",
"nested": map[string]interface{}{
"foo": "foo",
"bar": 42,
},
})
cases := []struct {
inputs Input
depUrns []string
expectOutputValue bool
}{
{inputs: inputs1, depUrns: []string{""}, expectOutputValue: true},
{inputs: inputs2, depUrns: nil},
}
for _, c := range cases {
resolved, _, depUrns, err := marshalInputs(c.inputs)
assert.NoError(t, err)
if c.expectOutputValue {
assert.Equal(t, "outputty", resolved["prop"].OutputValue().Element.StringValue())
} else {
assert.Equal(t, "outputty", resolved["prop"].StringValue())
}
assert.Equal(t, "foo", resolved["nested"].ObjectValue()["foo"].StringValue())
assert.Equal(t, 42.0, resolved["nested"].ObjectValue()["bar"].NumberValue())
assert.Equal(t, len(c.depUrns), len(depUrns))
for i := range c.depUrns {
assert.Equal(t, URN(c.depUrns[i]), depUrns[i])
}
}
}
func TestVersionedMap(t *testing.T) {
resourceModules := versionedMap{
versions: map[string][]Versioned{},
}
_ = resourceModules.Store("test", &testResourcePackage{version: semver.MustParse("1.0.1-alpha1")})
_ = resourceModules.Store("test", &testResourcePackage{version: semver.MustParse("1.0.2")})
_ = resourceModules.Store("test", &testResourcePackage{version: semver.MustParse("2.2.0")})
_ = resourceModules.Store("unrelated", &testResourcePackage{version: semver.MustParse("1.0.3")})
_ = resourceModules.Store("wild", &testResourcePackage{})
_ = resourceModules.Store("unreleased", &testResourcePackage{version: semver.MustParse("1.0.0-alpha1")})
_ = resourceModules.Store("unreleased", &testResourcePackage{version: semver.MustParse("1.0.0-beta1")})
tests := []struct {
name string
pkg string
version semver.Version
expectFound bool
expectedVersion semver.Version
}{
{
name: "unknown not found",
pkg: "unknown",
version: semver.Version{},
expectFound: false,
},
{
name: "unknown not found",
pkg: "unknown",
version: semver.MustParse("0.0.1"),
expectFound: false,
},
{
name: "different major version not found",
pkg: "test",
version: semver.MustParse("0.0.1"),
expectFound: false,
},
{
name: "different major version not found",
pkg: "test",
version: semver.MustParse("3.0.0"),
expectFound: false,
},
{
name: "wildcard returns highest version",
pkg: "test",
version: semver.Version{},
expectFound: true,
expectedVersion: semver.MustParse("2.2.0"),
},
{
name: "major version respected 1.0.0",
pkg: "test",
version: semver.MustParse("1.0.0"),
expectFound: true,
expectedVersion: semver.MustParse("1.0.2"),
},
{
name: "major version respected 2.0.0",
pkg: "test",
version: semver.MustParse("2.0.0"),
expectFound: true,
expectedVersion: semver.MustParse("2.2.0"),
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
pkg, found := resourceModules.Load(tt.pkg, tt.version)
assert.Equal(t, tt.expectFound, found)
if tt.expectFound {
assert.Equal(t, tt.expectedVersion, pkg.Version())
}
})
}
}
func TestRegisterResourcePackage(t *testing.T) {
pkg := "testPkg"
tests := []struct {
name string
resourcePackage ResourcePackage
}{
{
name: "wildcard version",
resourcePackage: &testResourcePackage{},
},
{
name: "version",
resourcePackage: &testResourcePackage{version: semver.MustParse("1.2.3")},
},
{
name: "alpha version",
resourcePackage: &testResourcePackage{version: semver.MustParse("1.0.0-alpha1")},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
RegisterResourcePackage(pkg, tt.resourcePackage)
assert.Panics(t, func() {
RegisterResourcePackage(pkg, tt.resourcePackage)
})
})
}
}
func TestRegisterResourceModule(t *testing.T) {
pkg := "testPkg"
mod := "testMod"
tests := []struct {
name string
resourceModule ResourceModule
}{
{
name: "wildcard version",
resourceModule: &testResourceModule{},
},
{
name: "version",
resourceModule: &testResourceModule{version: semver.MustParse("1.2.3")},
},
{
name: "alpha version",
resourceModule: &testResourceModule{version: semver.MustParse("1.0.0-alpha1")},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
RegisterResourceModule(pkg, mod, tt.resourceModule)
assert.Panics(t, func() {
RegisterResourceModule(pkg, mod, tt.resourceModule)
})
})
}
}
func TestInvalidAsset(t *testing.T) {
ctx, err := NewContext(context.Background(), RunInfo{})
assert.Nil(t, err)
var d Asset
_, err = unmarshalOutput(ctx, resource.NewStringProperty("foo"), reflect.ValueOf(&d).Elem())
require.NoError(t, err)
require.NotNil(t, d)
require.True(t, d.(*asset).invalid)
_, _, err = marshalInput(d, assetType, true)
assert.Error(t, err)
}
func TestInvalidArchive(t *testing.T) {
ctx, err := NewContext(context.Background(), RunInfo{})
assert.Nil(t, err)
var d Archive
_, err = unmarshalOutput(ctx, resource.NewStringProperty("foo"), reflect.ValueOf(&d).Elem())
require.NoError(t, err)
require.NotNil(t, d)
require.True(t, d.(*archive).invalid)
_, _, err = marshalInput(d, archiveType, true)
assert.Error(t, err)
}
func TestDependsOnComponent(t *testing.T) {
ctx, err := NewContext(context.Background(), RunInfo{})
assert.Nil(t, err)
registerResource := func(name string, res Resource, options ...ResourceOption) (Resource, []string) {
opts := merge(options...)
state := ctx.makeResourceState("", "", res, nil, nil, "", nil, nil)
state.resolve(ctx, nil, nil, name, "", &structpb.Struct{}, nil)
inputs, err := ctx.prepareResourceInputs(res, Map{}, "", opts, state, false)
require.NoError(t, err)
return res, inputs.deps
}
newResource := func(name string, options ...ResourceOption) (Resource, []string) {
var res testResource
return registerResource(name, &res, options...)
}
newComponent := func(name string, options ...ResourceOption) (Resource, []string) {
var res simpleComponentResource
return registerResource(name, &res, options...)
}
resA, _ := newResource("resA", nil)
comp1, _ := newComponent("comp1", nil)
resB, _ := newResource("resB", Parent(comp1))
newResource("resC", Parent(resB))
comp2, _ := newComponent("comp2", Parent(comp1))
resD, deps := newResource("resD", DependsOn([]Resource{resA}), Parent(comp2))
assert.Equal(t, []string{"resA"}, deps)
_, deps = newResource("resE", DependsOn([]Resource{resD}), Parent(comp2))
assert.Equal(t, []string{"resD"}, deps)
_, deps = newResource("resF", DependsOn([]Resource{resA}))
assert.Equal(t, []string{"resA"}, deps)
resG, deps := newResource("resG", DependsOn([]Resource{comp1}))
assert.Equal(t, []string{"resB", "resD", "resE"}, deps)
_, deps = newResource("resH", DependsOn([]Resource{comp2}))
assert.Equal(t, []string{"resD", "resE"}, deps)
_, deps = newResource("resI", DependsOn([]Resource{resG}))
assert.Equal(t, []string{"resG"}, deps)
}
func TestOutputValueMarshalling(t *testing.T) {
ctx, err := NewContext(context.Background(), RunInfo{})
assert.Nil(t, err)
values := []struct {
value interface{}
expected resource.PropertyValue
}{
{value: nil, expected: resource.NewNullProperty()},
{value: 0, expected: resource.NewNumberProperty(0)},
{value: 1, expected: resource.NewNumberProperty(1)},
{value: "", expected: resource.NewStringProperty("")},
{value: "hi", expected: resource.NewStringProperty("hi")},
{value: map[string]string{}, expected: resource.NewObjectProperty(resource.PropertyMap{})},
{value: []string{}, expected: resource.NewArrayProperty(nil)},
}
for _, value := range values {
for _, deps := range [][]resource.URN{nil, {"fakeURN1", "fakeURN2"}} {
for _, known := range []bool{true, false} {
for _, secret := range []bool{true, false} {
var resources []Resource
if len(deps) > 0 {
for _, dep := range deps {
resources = append(resources, ctx.newDependencyResource(URN(dep)))
}
}
out := ctx.newOutput(anyOutputType, resources...)
out.getState().resolve(value.value, known, secret, nil)
inputs := Map{"value": out}
expectedValue := value.expected
if !known || secret || len(deps) > 0 {
v := resource.Output{
Known: known,
Secret: secret,
Dependencies: deps,
}
if known {
v.Element = value.expected
}
expectedValue = resource.NewOutputProperty(v)
}
expected := resource.PropertyMap{"value": expectedValue}
if value.value == nil && known && !secret && len(deps) == 0 {
// marshalInputs excludes plain nil values.
expected = resource.PropertyMap{}
}
name := fmt.Sprintf("value=%v, known=%v, secret=%v, deps=%v", value, known, secret, deps)
t.Run(name, func(t *testing.T) {
actual, _, _, err := marshalInputs(inputs)
assert.NoError(t, err)
assert.Equal(t, expected, actual)
})
}
}
}
}
}
type foo struct {
TemplateOptions *TemplateOptions `pulumi:"templateOptions"`
}
type fooArgs struct {
TemplateOptions TemplateOptionsPtrInput
}
func (fooArgs) ElementType() reflect.Type {
return reflect.TypeOf((*foo)(nil)).Elem()
}
type TemplateOptions struct {
Description *string `pulumi:"description"`
TagSpecifications []TemplateTagSpecification `pulumi:"tagSpecifications"`
}
type TemplateOptionsInput interface {
Input
ToTemplateOptionsOutput() TemplateOptionsOutput
ToTemplateOptionsOutputWithContext(context.Context) TemplateOptionsOutput
}
type TemplateOptionsArgs struct {
Description StringPtrInput
TagSpecifications TemplateTagSpecificationArrayInput
}
func (TemplateOptionsArgs) ElementType() reflect.Type {
return reflect.TypeOf((*TemplateOptions)(nil)).Elem()
}
func (i TemplateOptionsArgs) ToTemplateOptionsOutput() TemplateOptionsOutput {
return i.ToTemplateOptionsOutputWithContext(context.Background())
}
func (i TemplateOptionsArgs) ToTemplateOptionsOutputWithContext(ctx context.Context) TemplateOptionsOutput {
return ToOutputWithContext(ctx, i).(TemplateOptionsOutput)
}
func (i TemplateOptionsArgs) ToTemplateOptionsPtrOutput() TemplateOptionsPtrOutput {
return i.ToTemplateOptionsPtrOutputWithContext(context.Background())
}
func (i TemplateOptionsArgs) ToTemplateOptionsPtrOutputWithContext(ctx context.Context) TemplateOptionsPtrOutput {
return ToOutputWithContext(ctx, i).(TemplateOptionsOutput).ToTemplateOptionsPtrOutputWithContext(ctx)
}
type TemplateOptionsPtrInput interface {
Input
ToTemplateOptionsPtrOutput() TemplateOptionsPtrOutput
ToTemplateOptionsPtrOutputWithContext(context.Context) TemplateOptionsPtrOutput
}
type templateOptionsPtrType TemplateOptionsArgs
func TemplateOptionsPtr(v *TemplateOptionsArgs) TemplateOptionsPtrInput {
return (*templateOptionsPtrType)(v)
}
func (*templateOptionsPtrType) ElementType() reflect.Type {
return reflect.TypeOf((**TemplateOptions)(nil)).Elem()
}
func (i *templateOptionsPtrType) ToTemplateOptionsPtrOutput() TemplateOptionsPtrOutput {
return i.ToTemplateOptionsPtrOutputWithContext(context.Background())
}
func (i *templateOptionsPtrType) ToTemplateOptionsPtrOutputWithContext(ctx context.Context) TemplateOptionsPtrOutput {
return ToOutputWithContext(ctx, i).(TemplateOptionsPtrOutput)
}
type TemplateOptionsOutput struct{ *OutputState }
func (TemplateOptionsOutput) ElementType() reflect.Type {
return reflect.TypeOf((*TemplateOptions)(nil)).Elem()
}
func (o TemplateOptionsOutput) ToTemplateOptionsOutput() TemplateOptionsOutput {
return o
}
func (o TemplateOptionsOutput) ToTemplateOptionsOutputWithContext(ctx context.Context) TemplateOptionsOutput {
return o
}
func (o TemplateOptionsOutput) ToTemplateOptionsPtrOutput() TemplateOptionsPtrOutput {
return o.ToTemplateOptionsPtrOutputWithContext(context.Background())
}
func (o TemplateOptionsOutput) ToTemplateOptionsPtrOutputWithContext(ctx context.Context) TemplateOptionsPtrOutput {
return o.ApplyTWithContext(ctx, func(_ context.Context, v TemplateOptions) *TemplateOptions {
return &v
}).(TemplateOptionsPtrOutput)
}
type TemplateOptionsPtrOutput struct{ *OutputState }
func (TemplateOptionsPtrOutput) ElementType() reflect.Type {
return reflect.TypeOf((**TemplateOptions)(nil)).Elem()
}
func (o TemplateOptionsPtrOutput) ToTemplateOptionsPtrOutput() TemplateOptionsPtrOutput {
return o
}
func (o TemplateOptionsPtrOutput) ToTemplateOptionsPtrOutputWithContext(ctx context.Context) TemplateOptionsPtrOutput {
return o
}
type TemplateTagSpecification struct {
Name *string `pulumi:"name"`
Tags map[string]string `pulumi:"tags"`
}
type TemplateTagSpecificationInput interface {
Input
ToTemplateTagSpecificationOutput() TemplateTagSpecificationOutput
ToTemplateTagSpecificationOutputWithContext(context.Context) TemplateTagSpecificationOutput
}
type TemplateTagSpecificationArgs struct {
Name StringPtrInput `pulumi:"name"`
Tags StringMapInput `pulumi:"tags"`
}
func (TemplateTagSpecificationArgs) ElementType() reflect.Type {
return reflect.TypeOf((*TemplateTagSpecification)(nil)).Elem()
}
func (i TemplateTagSpecificationArgs) ToTemplateTagSpecificationOutput() TemplateTagSpecificationOutput {
return i.ToTemplateTagSpecificationOutputWithContext(context.Background())
}
func (i TemplateTagSpecificationArgs) ToTemplateTagSpecificationOutputWithContext(ctx context.Context) TemplateTagSpecificationOutput {
return ToOutputWithContext(ctx, i).(TemplateTagSpecificationOutput)
}
type TemplateTagSpecificationArrayInput interface {
Input
ToTemplateTagSpecificationArrayOutput() TemplateTagSpecificationArrayOutput
ToTemplateTagSpecificationArrayOutputWithContext(context.Context) TemplateTagSpecificationArrayOutput
}
type TemplateTagSpecificationArray []TemplateTagSpecificationInput
func (TemplateTagSpecificationArray) ElementType() reflect.Type {
return reflect.TypeOf((*[]TemplateTagSpecification)(nil)).Elem()
}
func (i TemplateTagSpecificationArray) ToTemplateTagSpecificationArrayOutput() TemplateTagSpecificationArrayOutput {
return i.ToTemplateTagSpecificationArrayOutputWithContext(context.Background())
}
func (i TemplateTagSpecificationArray) ToTemplateTagSpecificationArrayOutputWithContext(ctx context.Context) TemplateTagSpecificationArrayOutput {
return ToOutputWithContext(ctx, i).(TemplateTagSpecificationArrayOutput)
}
type TemplateTagSpecificationOutput struct{ *OutputState }
func (TemplateTagSpecificationOutput) ElementType() reflect.Type {
return reflect.TypeOf((*TemplateTagSpecification)(nil)).Elem()
}
func (o TemplateTagSpecificationOutput) ToTemplateTagSpecificationOutput() TemplateTagSpecificationOutput {
return o
}
func (o TemplateTagSpecificationOutput) ToTemplateTagSpecificationOutputWithContext(ctx context.Context) TemplateTagSpecificationOutput {
return o
}
type TemplateTagSpecificationArrayOutput struct{ *OutputState }
func (TemplateTagSpecificationArrayOutput) ElementType() reflect.Type {
return reflect.TypeOf((*[]TemplateTagSpecification)(nil)).Elem()
}
func (o TemplateTagSpecificationArrayOutput) ToTemplateTagSpecificationArrayOutput() TemplateTagSpecificationArrayOutput {
return o
}
func (o TemplateTagSpecificationArrayOutput) ToTemplateTagSpecificationArrayOutputWithContext(ctx context.Context) TemplateTagSpecificationArrayOutput {
return o
}
type bucketObjectArgs struct {
Source AssetOrArchive `pulumi:"source"`
}
type BucketObjectArgs struct {
Source AssetOrArchiveInput
}
func (BucketObjectArgs) ElementType() reflect.Type {
return reflect.TypeOf((*bucketObjectArgs)(nil)).Elem()
}
type myResourceArgs struct {
Res Resource `pulumi:"res"`
}
type MyResourceArgs struct {
Res ResourceInput
}
func (MyResourceArgs) ElementType() reflect.Type {
return reflect.TypeOf((*myResourceArgs)(nil)).Elem()
}
type myNestedOutputArgs struct {
Nested interface{} `pulumi:"nested"`
}
type MyNestedOutputArgs struct {
Nested Input
}
func (MyNestedOutputArgs) ElementType() reflect.Type {
return reflect.TypeOf((*myNestedOutputArgs)(nil)).Elem()
}
func TestOutputValueMarshallingNested(t *testing.T) {
ctx, err := NewContext(context.Background(), RunInfo{})
assert.Nil(t, err)
RegisterOutputType(TemplateOptionsOutput{})
RegisterOutputType(TemplateOptionsPtrOutput{})
RegisterOutputType(TemplateTagSpecificationOutput{})
RegisterOutputType(TemplateTagSpecificationArrayOutput{})
templateOptionsPtrOutputType := reflect.TypeOf((*TemplateOptionsPtrOutput)(nil)).Elem()
unknownTemplateOptionsPtrOutput := ctx.newOutput(templateOptionsPtrOutputType).(TemplateOptionsPtrOutput)
unknownTemplateOptionsPtrOutput.getState().resolve(nil, false /*known*/, false /*secret*/, nil)
unknownSecretTemplateOptionsPtrOutput := ctx.newOutput(templateOptionsPtrOutputType).(TemplateOptionsPtrOutput)
unknownSecretTemplateOptionsPtrOutput.getState().resolve(nil, false /*known*/, true /*secret*/, nil)
stringOutputType := reflect.TypeOf((*StringOutput)(nil)).Elem()
unknownStringOutput := ctx.newOutput(stringOutputType).(StringOutput)
unknownStringOutput.getState().resolve("", false /*known*/, false /*secret*/, nil)
assetOutputType := reflect.TypeOf((*AssetOutput)(nil)).Elem()
fileAssetOutput := ctx.newOutput(assetOutputType).(AssetOutput)
fileAssetOutput.getState().resolve(&asset{path: "foo.txt"}, true /*known*/, false /*secret*/, nil)
fileAssetSecretOutput := ctx.newOutput(assetOutputType).(AssetOutput)
fileAssetSecretOutput.getState().resolve(&asset{path: "foo.txt"}, true /*known*/, true /*secret*/, nil)
fileAssetOutputDeps := ctx.newOutput(assetOutputType).(AssetOutput)
fileAssetOutputDeps.getState().resolve(&asset{path: "foo.txt"}, true /*known*/, false, /*secret*/
[]Resource{newSimpleCustomResource(ctx, "fakeURN", "fakeID")})
anyOutputType := reflect.TypeOf((*AnyOutput)(nil)).Elem()
nestedOutput := ctx.newOutput(anyOutputType).(AnyOutput)
nestedOutput.getState().resolve(fileAssetOutput, true /*known*/, false /*secret*/, nil)
nestedPtrOutput := ctx.newOutput(anyOutputType).(AnyOutput)
nestedPtrOutput.getState().resolve(&fileAssetOutput, true /*known*/, false /*secret*/, nil)
nestedNestedOutput := ctx.newOutput(anyOutputType).(AnyOutput)
nestedNestedOutput.getState().resolve(nestedOutput, true /*known*/, false /*secret*/, nil)
tests := []struct {
name string
input Input
expected resource.PropertyValue
}{
{
name: "empty",
input: fooArgs{},
expected: resource.NewObjectProperty(resource.PropertyMap{}),
},
{
name: "options empty",
input: fooArgs{
TemplateOptions: TemplateOptionsArgs{},
},
expected: resource.NewObjectProperty(resource.PropertyMap{
"templateOptions": resource.NewObjectProperty(resource.PropertyMap{}),
}),
},
{
name: "options unknown",
input: fooArgs{
TemplateOptions: unknownTemplateOptionsPtrOutput,
},
expected: resource.NewObjectProperty(resource.PropertyMap{
"templateOptions": resource.NewOutputProperty(resource.Output{}),
}),
},
{
name: "options unknown secret",
input: fooArgs{
TemplateOptions: unknownSecretTemplateOptionsPtrOutput,
},
expected: resource.NewObjectProperty(resource.PropertyMap{
"templateOptions": resource.NewOutputProperty(resource.Output{
Secret: true,
}),
}),
},
{
name: "options plain known description",
input: fooArgs{
TemplateOptions: TemplateOptionsArgs{
Description: String("hello"),
},
},
expected: resource.NewObjectProperty(resource.PropertyMap{
"templateOptions": resource.NewObjectProperty(resource.PropertyMap{
"description": resource.NewStringProperty("hello"),
}),
}),
},
{
name: "options plain known secret description",
input: fooArgs{
TemplateOptions: TemplateOptionsArgs{
Description: ToSecret(String("hello")).(StringOutput),
},
},
expected: resource.NewObjectProperty(resource.PropertyMap{
"templateOptions": resource.NewObjectProperty(resource.PropertyMap{
"description": resource.NewOutputProperty(resource.Output{
Element: resource.NewStringProperty("hello"),
Known: true,
Secret: true,
}),
}),
}),
},
{
name: "options output known secret description",
input: fooArgs{
TemplateOptions: TemplateOptionsArgs{
Description: ToSecret(String("hello")).(StringOutput),
}.ToTemplateOptionsOutput(),
},
expected: resource.NewObjectProperty(resource.PropertyMap{
"templateOptions": resource.NewOutputProperty(resource.Output{
Element: resource.NewObjectProperty(resource.PropertyMap{
"description": resource.NewStringProperty("hello"),
}),
Known: true,
Secret: true,
}),
}),
},
{
name: "options plain unknown description",
input: fooArgs{
TemplateOptions: TemplateOptionsArgs{
Description: unknownStringOutput,
},
},
expected: resource.NewObjectProperty(resource.PropertyMap{
"templateOptions": resource.NewObjectProperty(resource.PropertyMap{
"description": resource.NewOutputProperty(resource.Output{}),
}),
}),
},
{
name: "options tag specifications nested unknown",
input: fooArgs{
TemplateOptions: TemplateOptionsArgs{
TagSpecifications: TemplateTagSpecificationArray{
TemplateTagSpecificationArgs{
Name: String("hello"),
Tags: StringMap{
"first": String("second"),
"third": unknownStringOutput,
},
},
},
},
},
expected: resource.NewObjectProperty(resource.PropertyMap{
"templateOptions": resource.NewObjectProperty(resource.PropertyMap{
"tagSpecifications": resource.NewArrayProperty([]resource.PropertyValue{
resource.NewObjectProperty(resource.PropertyMap{
"name": resource.NewStringProperty("hello"),
"tags": resource.NewObjectProperty(resource.PropertyMap{
"first": resource.NewStringProperty("second"),
"third": resource.NewOutputProperty(resource.Output{}),
}),
}),
}),
}),
}),
},
{
name: "bucket object with file asset",
input: &BucketObjectArgs{
Source: NewFileAsset("foo.txt"),
},
expected: resource.NewObjectProperty(resource.PropertyMap{
"source": resource.NewAssetProperty(&resource.Asset{
Path: "foo.txt",
}),
}),
},
{
name: "bucket object with file archive",
input: &BucketObjectArgs{
Source: NewFileArchive("bar.zip"),
},
expected: resource.NewObjectProperty(resource.PropertyMap{
"source": resource.NewArchiveProperty(&resource.Archive{
Path: "bar.zip",
}),
}),
},
{
name: "bucket object with file asset output",
input: &BucketObjectArgs{
Source: fileAssetOutput,
},
expected: resource.NewObjectProperty(resource.PropertyMap{
"source": resource.NewAssetProperty(&resource.Asset{
Path: "foo.txt",
}),
}),
},
{
name: "bucket object with file asset secret output",
input: &BucketObjectArgs{
Source: fileAssetSecretOutput,
},
expected: resource.NewObjectProperty(resource.PropertyMap{
"source": resource.NewOutputProperty(resource.Output{
Element: resource.NewAssetProperty(&resource.Asset{
Path: "foo.txt",
}),
Known: true,
Secret: true,
}),
}),
},
{
name: "bucket object with file asset with deps",
input: &BucketObjectArgs{
Source: fileAssetOutputDeps,
},
expected: resource.NewObjectProperty(resource.PropertyMap{
"source": resource.NewOutputProperty(resource.Output{
Element: resource.NewAssetProperty(&resource.Asset{
Path: "foo.txt",
}),
Known: true,
Dependencies: []resource.URN{"fakeURN"},
}),
}),
},
{
name: "resource",
input: &MyResourceArgs{
Res: NewResourceInput(newSimpleCustomResource(ctx, "fakeURN", "fakeID")),
},
expected: resource.NewObjectProperty(resource.PropertyMap{
"res": resource.NewResourceReferenceProperty(resource.ResourceReference{
URN: "fakeURN",
ID: resource.NewStringProperty("fakeID"),
}),
}),
},
{
name: "nested output",
input: &MyNestedOutputArgs{
Nested: nestedOutput,
},
expected: resource.NewObjectProperty(resource.PropertyMap{
"nested": resource.NewAssetProperty(&resource.Asset{
Path: "foo.txt",
}),
}),
},
{
name: "nested ptr output",
input: &MyNestedOutputArgs{
Nested: nestedPtrOutput,
},
expected: resource.NewObjectProperty(resource.PropertyMap{
"nested": resource.NewAssetProperty(&resource.Asset{
Path: "foo.txt",
}),
}),
},
{
name: "nested nested output",
input: &MyNestedOutputArgs{
Nested: nestedNestedOutput,
},
expected: resource.NewObjectProperty(resource.PropertyMap{
"nested": resource.NewAssetProperty(&resource.Asset{
Path: "foo.txt",
}),
}),
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
inputs := Map{"value": tt.input}
expected := resource.PropertyMap{"value": tt.expected}
actual, _, _, err := marshalInputs(inputs)
assert.NoError(t, err)
assert.Equal(t, expected, actual)
})
}
}
type rubberTreeArgs struct {
Size *TreeSize `pulumi:"size"`
}
type RubberTreeArgs struct {
Size TreeSizePtrInput
}
func (RubberTreeArgs) ElementType() reflect.Type {
return reflect.TypeOf((*rubberTreeArgs)(nil)).Elem()
}
type TreeSize string
const (
TreeSizeSmall = TreeSize("small")
TreeSizeMedium = TreeSize("medium")
TreeSizeLarge = TreeSize("large")
)
func (TreeSize) ElementType() reflect.Type {
return reflect.TypeOf((*TreeSize)(nil)).Elem()
}
func (e TreeSize) ToTreeSizeOutput() TreeSizeOutput {
return ToOutput(e).(TreeSizeOutput)
}
func (e TreeSize) ToTreeSizeOutputWithContext(ctx context.Context) TreeSizeOutput {
return ToOutputWithContext(ctx, e).(TreeSizeOutput)
}
func (e TreeSize) ToTreeSizePtrOutput() TreeSizePtrOutput {
return e.ToTreeSizePtrOutputWithContext(context.Background())
}
func (e TreeSize) ToTreeSizePtrOutputWithContext(ctx context.Context) TreeSizePtrOutput {
return e.ToTreeSizeOutputWithContext(ctx).ToTreeSizePtrOutputWithContext(ctx)
}
func (e TreeSize) ToStringOutput() StringOutput {
return ToOutput(String(e)).(StringOutput)
}
func (e TreeSize) ToStringOutputWithContext(ctx context.Context) StringOutput {
return ToOutputWithContext(ctx, String(e)).(StringOutput)
}
func (e TreeSize) ToStringPtrOutput() StringPtrOutput {
return String(e).ToStringPtrOutputWithContext(context.Background())
}
func (e TreeSize) ToStringPtrOutputWithContext(ctx context.Context) StringPtrOutput {
return String(e).ToStringOutputWithContext(ctx).ToStringPtrOutputWithContext(ctx)
}
type TreeSizeOutput struct{ *OutputState }
func (TreeSizeOutput) ElementType() reflect.Type {
return reflect.TypeOf((*TreeSize)(nil)).Elem()
}
func (o TreeSizeOutput) ToTreeSizeOutput() TreeSizeOutput {
return o
}
func (o TreeSizeOutput) ToTreeSizeOutputWithContext(ctx context.Context) TreeSizeOutput {
return o
}
func (o TreeSizeOutput) ToTreeSizePtrOutput() TreeSizePtrOutput {
return o.ToTreeSizePtrOutputWithContext(context.Background())
}
func (o TreeSizeOutput) ToTreeSizePtrOutputWithContext(ctx context.Context) TreeSizePtrOutput {
return o.ApplyTWithContext(ctx, func(_ context.Context, v TreeSize) *TreeSize {
return &v
}).(TreeSizePtrOutput)
}
func (o TreeSizeOutput) ToStringOutput() StringOutput {
return o.ToStringOutputWithContext(context.Background())
}
func (o TreeSizeOutput) ToStringOutputWithContext(ctx context.Context) StringOutput {
return o.ApplyTWithContext(ctx, func(_ context.Context, e TreeSize) string {
return string(e)
}).(StringOutput)
}
func (o TreeSizeOutput) ToStringPtrOutput() StringPtrOutput {
return o.ToStringPtrOutputWithContext(context.Background())
}
func (o TreeSizeOutput) ToStringPtrOutputWithContext(ctx context.Context) StringPtrOutput {
return o.ApplyTWithContext(ctx, func(_ context.Context, e TreeSize) *string {
v := string(e)
return &v
}).(StringPtrOutput)
}
type TreeSizePtrOutput struct{ *OutputState }
func (TreeSizePtrOutput) ElementType() reflect.Type {
return reflect.TypeOf((**TreeSize)(nil)).Elem()
}
func (o TreeSizePtrOutput) ToTreeSizePtrOutput() TreeSizePtrOutput {
return o
}
func (o TreeSizePtrOutput) ToTreeSizePtrOutputWithContext(ctx context.Context) TreeSizePtrOutput {
return o
}
func (o TreeSizePtrOutput) Elem() TreeSizeOutput {
return o.ApplyT(func(v *TreeSize) TreeSize {
if v != nil {
return *v
}
var ret TreeSize
return ret
}).(TreeSizeOutput)
}
func (o TreeSizePtrOutput) ToStringPtrOutput() StringPtrOutput {
return o.ToStringPtrOutputWithContext(context.Background())
}
func (o TreeSizePtrOutput) ToStringPtrOutputWithContext(ctx context.Context) StringPtrOutput {
return o.ApplyTWithContext(ctx, func(_ context.Context, e *TreeSize) *string {
if e == nil {
return nil
}
v := string(*e)
return &v
}).(StringPtrOutput)
}
type TreeSizeInput interface {
Input
ToTreeSizeOutput() TreeSizeOutput
ToTreeSizeOutputWithContext(context.Context) TreeSizeOutput
}
var treeSizePtrType = reflect.TypeOf((**TreeSize)(nil)).Elem()
type TreeSizePtrInput interface {
Input
ToTreeSizePtrOutput() TreeSizePtrOutput
ToTreeSizePtrOutputWithContext(context.Context) TreeSizePtrOutput
}
type treeSizePtr string
func TreeSizePtr(v string) TreeSizePtrInput {
return (*treeSizePtr)(&v)
}
func (*treeSizePtr) ElementType() reflect.Type {
return treeSizePtrType
}
func (in *treeSizePtr) ToTreeSizePtrOutput() TreeSizePtrOutput {
return ToOutput(in).(TreeSizePtrOutput)
}
func (in *treeSizePtr) ToTreeSizePtrOutputWithContext(ctx context.Context) TreeSizePtrOutput {
return ToOutputWithContext(ctx, in).(TreeSizePtrOutput)
}
type TreeSizeMapInput interface {
Input
ToTreeSizeMapOutput() TreeSizeMapOutput
ToTreeSizeMapOutputWithContext(context.Context) TreeSizeMapOutput
}
type TreeSizeMap map[string]TreeSize
func (TreeSizeMap) ElementType() reflect.Type {
return reflect.TypeOf((*map[string]TreeSize)(nil)).Elem()
}
func (i TreeSizeMap) ToTreeSizeMapOutput() TreeSizeMapOutput {
return i.ToTreeSizeMapOutputWithContext(context.Background())
}
func (i TreeSizeMap) ToTreeSizeMapOutputWithContext(ctx context.Context) TreeSizeMapOutput {
return ToOutputWithContext(ctx, i).(TreeSizeMapOutput)
}
type TreeSizeMapOutput struct{ *OutputState }
func (TreeSizeMapOutput) ElementType() reflect.Type {
return reflect.TypeOf((*map[string]TreeSize)(nil)).Elem()
}
func (o TreeSizeMapOutput) ToTreeSizeMapOutput() TreeSizeMapOutput {
return o
}
func (o TreeSizeMapOutput) ToTreeSizeMapOutputWithContext(ctx context.Context) TreeSizeMapOutput {
return o
}
func (o TreeSizeMapOutput) MapIndex(k StringInput) TreeSizeOutput {
return All(o, k).ApplyT(func(vs []interface{}) TreeSize {
return vs[0].(map[string]TreeSize)[vs[1].(string)]
}).(TreeSizeOutput)
}
func TestOutputValueMarshallingEnums(t *testing.T) {
_, err := NewContext(context.Background(), RunInfo{})
assert.Nil(t, err)
RegisterOutputType(TreeSizeOutput{})
RegisterOutputType(TreeSizePtrOutput{})
RegisterOutputType(TreeSizeMapOutput{})
tests := []struct {
name string
input Input
expected resource.PropertyValue
}{
{
name: "empty",
input: &RubberTreeArgs{
Size: TreeSize("medium"),
},
expected: resource.NewObjectProperty(resource.PropertyMap{
"size": resource.NewStringProperty("medium"),
}),
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
inputs := Map{"value": tt.input}
expected := resource.PropertyMap{"value": tt.expected}
actual, _, _, err := marshalInputs(inputs)
assert.NoError(t, err)
assert.Equal(t, expected, actual)
})
}
}