mirror of https://github.com/pulumi/pulumi.git
2023 lines
63 KiB
Go
2023 lines
63 KiB
Go
// Copyright 2016-2021, 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"
|
|
"github.com/pulumi/pulumi/sdk/v3/go/common/resource"
|
|
rarchive "github.com/pulumi/pulumi/sdk/v3/go/common/resource/archive"
|
|
rasset "github.com/pulumi/pulumi/sdk/v3/go/common/resource/asset"
|
|
"github.com/pulumi/pulumi/sdk/v3/go/common/resource/plugin"
|
|
"github.com/pulumi/pulumi/sdk/v3/go/internal"
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
"google.golang.org/protobuf/types/known/structpb"
|
|
)
|
|
|
|
type simpleComponentResource struct {
|
|
ResourceState
|
|
}
|
|
|
|
func newSimpleComponentResource(ctx *Context, urn URN) ComponentResource {
|
|
var res simpleComponentResource
|
|
res.urn.OutputState = ctx.newOutputState(res.urn.ElementType(), &res)
|
|
internal.ResolveOutput(res.urn, urn, true, false, resourcesToInternal(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)
|
|
internal.ResolveOutput(res.urn, urn, true, false, resourcesToInternal(nil))
|
|
internal.ResolveOutput(res.id, id, id != "", false, resourcesToInternal(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)
|
|
internal.ResolveOutput(res.urn, urn, true, false, resourcesToInternal(nil))
|
|
internal.ResolveOutput(res.id, id, id != "", false, resourcesToInternal(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) {
|
|
t.Parallel()
|
|
|
|
// Create interesting inputs.
|
|
ctx, err := NewContext(context.Background(), RunInfo{})
|
|
assert.NoError(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(""))
|
|
internal.FulfillOutput(out2, nil, false, false, resourcesToInternal(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.NoError(t, err)
|
|
|
|
if assert.NoError(t, err) {
|
|
assert.Equal(t, reflect.TypeOf(inputs).NumField(), len(resolved))
|
|
assert.Equal(t, 10, len(deps))
|
|
assert.Equal(t, 25, 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.NoError(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) {
|
|
t.Parallel()
|
|
|
|
ctx, err := NewContext(context.Background(), RunInfo{})
|
|
assert.NoError(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.NoError(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.NoError(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) {
|
|
t.Parallel()
|
|
|
|
ctx, err := NewContext(context.Background(), RunInfo{})
|
|
assert.NoError(t, err)
|
|
|
|
secret := resource.MakeSecret(resource.NewPropertyValue("foo"))
|
|
|
|
_, isSecret, err := unmarshalPropertyValue(ctx, secret)
|
|
assert.NoError(t, err)
|
|
assert.True(t, isSecret)
|
|
|
|
var sv string
|
|
isSecret, err = unmarshalOutput(ctx, secret, reflect.ValueOf(&sv).Elem())
|
|
assert.NoError(t, err)
|
|
assert.Equal(t, "foo", sv)
|
|
assert.True(t, isSecret)
|
|
}
|
|
|
|
func TestUnmarshalInternalMapValue(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
ctx, err := NewContext(context.Background(), RunInfo{})
|
|
assert.NoError(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.NoError(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) {
|
|
t.Parallel()
|
|
|
|
// Create interesting inputs.
|
|
ctx, err := NewContext(context.Background(), RunInfo{})
|
|
assert.NoError(t, err)
|
|
|
|
out, resolve, _ := NewOutput()
|
|
resolve("outputty")
|
|
out2 := ctx.newOutputState(reflect.TypeOf(""))
|
|
internal.FulfillOutput(out2, nil, false, true, resourcesToInternal(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.NoError(t, err)
|
|
|
|
if assert.NoError(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, 15, 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.NoError(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) {
|
|
t.Parallel()
|
|
|
|
var theResource simpleCustomResource
|
|
out := internal.NewOutput(nil, reflect.TypeOf((*StringOutput)(nil)).Elem(), &theResource)
|
|
internal.ResolveOutput(out, "outputty", true, false, resourcesToInternal(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) {
|
|
t.Parallel()
|
|
|
|
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 {
|
|
tt := tt
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
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) {
|
|
t.Parallel()
|
|
|
|
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 {
|
|
tt := tt
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
RegisterResourcePackage(pkg, tt.resourcePackage)
|
|
assert.Panics(t, func() {
|
|
RegisterResourcePackage(pkg, tt.resourcePackage)
|
|
})
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestRegisterResourceModule(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
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 {
|
|
tt := tt
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
RegisterResourceModule(pkg, mod, tt.resourceModule)
|
|
assert.Panics(t, func() {
|
|
RegisterResourceModule(pkg, mod, tt.resourceModule)
|
|
})
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestInvalidAsset(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
ctx, err := NewContext(context.Background(), RunInfo{})
|
|
assert.NoError(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.EqualError(t, err, "invalid asset")
|
|
}
|
|
|
|
func TestInvalidArchive(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
ctx, err := NewContext(context.Background(), RunInfo{})
|
|
assert.NoError(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.EqualError(t, err, "invalid archive")
|
|
}
|
|
|
|
func TestDependsOnComponent(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
ctx, err := NewContext(context.Background(), RunInfo{})
|
|
assert.NoError(t, err)
|
|
|
|
registerResource := func(name string, res Resource, custom bool, 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, custom)
|
|
require.NoError(t, err)
|
|
|
|
return res, inputs.deps
|
|
}
|
|
|
|
newResource := func(name string, options ...ResourceOption) (Resource, []string) {
|
|
var res testResource
|
|
return registerResource(name, &res, true, options...)
|
|
}
|
|
|
|
newComponent := func(name string, options ...ResourceOption) (Resource, []string) {
|
|
var res simpleComponentResource
|
|
return registerResource(name, &res, false, 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) {
|
|
t.Parallel()
|
|
|
|
ctx, err := NewContext(context.Background(), RunInfo{})
|
|
assert.NoError(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([]resource.PropertyValue{})},
|
|
}
|
|
//nolint:paralleltest // parallel parent, would require refactor to silence lint
|
|
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...)
|
|
internal.ResolveOutput(out, value.value, known, secret, resourcesToInternal(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)
|
|
//nolint:paralleltest // very small test, parallel parent
|
|
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) {
|
|
t.Parallel()
|
|
|
|
ctx, err := NewContext(context.Background(), RunInfo{})
|
|
assert.NoError(t, err)
|
|
|
|
RegisterOutputType(TemplateOptionsOutput{})
|
|
RegisterOutputType(TemplateOptionsPtrOutput{})
|
|
RegisterOutputType(TemplateTagSpecificationOutput{})
|
|
RegisterOutputType(TemplateTagSpecificationArrayOutput{})
|
|
|
|
templateOptionsPtrOutputType := reflect.TypeOf((*TemplateOptionsPtrOutput)(nil)).Elem()
|
|
unknownTemplateOptionsPtrOutput := ctx.newOutput(templateOptionsPtrOutputType).(TemplateOptionsPtrOutput)
|
|
internal.ResolveOutput(unknownTemplateOptionsPtrOutput, nil, false, false, resourcesToInternal(nil)) /*known*/ /*secret*/
|
|
|
|
unknownSecretTemplateOptionsPtrOutput := ctx.newOutput(templateOptionsPtrOutputType).(TemplateOptionsPtrOutput)
|
|
internal.ResolveOutput(unknownSecretTemplateOptionsPtrOutput, nil, false, true, resourcesToInternal(nil)) /*known*/ /*secret*/
|
|
|
|
stringOutputType := reflect.TypeOf((*StringOutput)(nil)).Elem()
|
|
unknownStringOutput := ctx.newOutput(stringOutputType).(StringOutput)
|
|
internal.ResolveOutput(unknownStringOutput, "", false, false, resourcesToInternal(nil)) /*known*/ /*secret*/
|
|
|
|
assetOutputType := reflect.TypeOf((*AssetOutput)(nil)).Elem()
|
|
fileAssetOutput := ctx.newOutput(assetOutputType).(AssetOutput)
|
|
internal.ResolveOutput(fileAssetOutput, &asset{path: "foo.txt"}, true, false, resourcesToInternal(nil)) /*known*/ /*secret*/
|
|
fileAssetSecretOutput := ctx.newOutput(assetOutputType).(AssetOutput)
|
|
internal.ResolveOutput(fileAssetSecretOutput, &asset{path: "foo.txt"}, true, true, resourcesToInternal(nil)) /*known*/ /*secret*/
|
|
fileAssetOutputDeps := ctx.newOutput(assetOutputType).(AssetOutput)
|
|
internal.ResolveOutput(fileAssetOutputDeps, &asset{path: "foo.txt"}, true, false, resourcesToInternal([]Resource{newSimpleCustomResource(ctx, "fakeURN", "fakeID")})) /*known*/ /*secret*/
|
|
|
|
anyOutputType := reflect.TypeOf((*AnyOutput)(nil)).Elem()
|
|
|
|
nestedOutput := ctx.newOutput(anyOutputType).(AnyOutput)
|
|
internal.ResolveOutput(nestedOutput, fileAssetOutput, true, false, resourcesToInternal(nil)) /*known*/ /*secret*/
|
|
|
|
nestedPtrOutput := ctx.newOutput(anyOutputType).(AnyOutput)
|
|
internal.ResolveOutput(nestedPtrOutput, &fileAssetOutput, true, false, resourcesToInternal(nil)) /*known*/ /*secret*/
|
|
|
|
nestedNestedOutput := ctx.newOutput(anyOutputType).(AnyOutput)
|
|
internal.ResolveOutput(nestedNestedOutput, nestedOutput, true, false, resourcesToInternal(nil)) /*known*/ /*secret*/
|
|
|
|
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(&rasset.Asset{
|
|
Path: "foo.txt",
|
|
}),
|
|
}),
|
|
},
|
|
{
|
|
name: "bucket object with file archive",
|
|
input: &BucketObjectArgs{
|
|
Source: NewFileArchive("bar.zip"),
|
|
},
|
|
expected: resource.NewObjectProperty(resource.PropertyMap{
|
|
"source": resource.NewArchiveProperty(&rarchive.Archive{
|
|
Path: "bar.zip",
|
|
}),
|
|
}),
|
|
},
|
|
{
|
|
name: "bucket object with file asset output",
|
|
input: &BucketObjectArgs{
|
|
Source: fileAssetOutput,
|
|
},
|
|
expected: resource.NewObjectProperty(resource.PropertyMap{
|
|
"source": resource.NewAssetProperty(&rasset.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(&rasset.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(&rasset.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(&rasset.Asset{
|
|
Path: "foo.txt",
|
|
}),
|
|
}),
|
|
},
|
|
{
|
|
name: "nested ptr output",
|
|
input: &MyNestedOutputArgs{
|
|
Nested: nestedPtrOutput,
|
|
},
|
|
expected: resource.NewObjectProperty(resource.PropertyMap{
|
|
"nested": resource.NewAssetProperty(&rasset.Asset{
|
|
Path: "foo.txt",
|
|
}),
|
|
}),
|
|
},
|
|
{
|
|
name: "nested nested output",
|
|
input: &MyNestedOutputArgs{
|
|
Nested: nestedNestedOutput,
|
|
},
|
|
expected: resource.NewObjectProperty(resource.PropertyMap{
|
|
"nested": resource.NewAssetProperty(&rasset.Asset{
|
|
Path: "foo.txt",
|
|
}),
|
|
}),
|
|
},
|
|
}
|
|
for _, tt := range tests {
|
|
tt := tt
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
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) {
|
|
t.Parallel()
|
|
|
|
_, err := NewContext(context.Background(), RunInfo{})
|
|
assert.NoError(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 {
|
|
tt := tt
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
inputs := Map{"value": tt.input}
|
|
expected := resource.PropertyMap{"value": tt.expected}
|
|
|
|
actual, _, _, err := marshalInputs(inputs)
|
|
assert.NoError(t, err)
|
|
assert.Equal(t, expected, actual)
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestMarshalInputsPropertyDependencies(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
pmap, pdeps, deps, err := marshalInputs(testInputs{
|
|
S: String("a string"),
|
|
A: Bool(true),
|
|
})
|
|
assert.NoError(t, err)
|
|
assert.Equal(t, resource.PropertyMap{
|
|
"s": resource.NewStringProperty("a string"),
|
|
"a": resource.NewBoolProperty(true),
|
|
}, pmap)
|
|
assert.Nil(t, deps)
|
|
// Expect a non-empty property deps map, even when there aren't any deps.
|
|
assert.Equal(t, map[string][]URN{"s": nil, "a": nil}, pdeps)
|
|
}
|
|
|
|
func TestUnmarshalPropertyMap(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
cases := []struct {
|
|
name string
|
|
input resource.PropertyMap
|
|
expected Map
|
|
}{
|
|
{
|
|
name: "empty",
|
|
input: resource.PropertyMap{},
|
|
expected: Map{},
|
|
},
|
|
{
|
|
name: "primitive types",
|
|
input: resource.PropertyMap{
|
|
"nil": resource.NewNullProperty(),
|
|
"bool": resource.NewBoolProperty(true),
|
|
"number": resource.NewNumberProperty(42),
|
|
"string": resource.NewStringProperty("hello"),
|
|
},
|
|
expected: Map{
|
|
"nil": nil,
|
|
"bool": Bool(true),
|
|
"number": Float64(42),
|
|
"string": String("hello"),
|
|
},
|
|
},
|
|
{
|
|
name: "array",
|
|
input: resource.PropertyMap{
|
|
"array": resource.NewArrayProperty([]resource.PropertyValue{
|
|
resource.NewNullProperty(),
|
|
resource.NewBoolProperty(true),
|
|
resource.NewNumberProperty(42),
|
|
resource.NewStringProperty("hello"),
|
|
}),
|
|
},
|
|
expected: Map{
|
|
"array": Array{
|
|
nil,
|
|
Bool(true),
|
|
Float64(42),
|
|
String("hello"),
|
|
},
|
|
},
|
|
},
|
|
{
|
|
name: "object",
|
|
input: resource.PropertyMap{
|
|
"object": resource.NewObjectProperty(resource.PropertyMap{
|
|
"nil": resource.NewNullProperty(),
|
|
"bool": resource.NewBoolProperty(true),
|
|
"number": resource.NewNumberProperty(42),
|
|
"string": resource.NewStringProperty("hello"),
|
|
}),
|
|
},
|
|
expected: Map{
|
|
"object": Map{
|
|
"nil": nil,
|
|
"bool": Bool(true),
|
|
"number": Float64(42),
|
|
"string": String("hello"),
|
|
},
|
|
},
|
|
},
|
|
}
|
|
|
|
for _, c := range cases {
|
|
c := c
|
|
t.Run(c.name, func(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
ctx, err := NewContext(context.Background(), RunInfo{})
|
|
require.NoError(t, err)
|
|
|
|
actual, err := unmarshalPropertyMap(ctx, c.input)
|
|
assert.NoError(t, err)
|
|
assert.Equal(t, c.expected, actual)
|
|
})
|
|
}
|
|
|
|
t.Run("unknown", func(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
ctx, err := NewContext(context.Background(), RunInfo{})
|
|
require.NoError(t, err)
|
|
|
|
actual, err := unmarshalPropertyMap(ctx, resource.PropertyMap{
|
|
"computed": resource.NewComputedProperty(resource.Computed{}),
|
|
"unknown": resource.MakeComputed(resource.NewStringProperty("")),
|
|
})
|
|
require.NoError(t, err)
|
|
|
|
assert.Len(t, actual, 2)
|
|
_, known, secret, _, err := awaitWithContext(ctx.Context(), actual["computed"].(AnyOutput))
|
|
require.NoError(t, err)
|
|
assert.False(t, known)
|
|
assert.False(t, secret)
|
|
|
|
_, known, secret, _, err = awaitWithContext(ctx.Context(), actual["unknown"].(AnyOutput))
|
|
require.NoError(t, err)
|
|
assert.False(t, known)
|
|
assert.False(t, secret)
|
|
})
|
|
|
|
t.Run("secret", func(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
ctx, err := NewContext(context.Background(), RunInfo{})
|
|
require.NoError(t, err)
|
|
|
|
actual, err := unmarshalPropertyMap(ctx, resource.PropertyMap{
|
|
"secret": resource.MakeSecret(resource.NewStringProperty("secret string")),
|
|
"unknown": resource.MakeSecret(resource.MakeComputed(resource.NewStringProperty(""))),
|
|
})
|
|
require.NoError(t, err)
|
|
|
|
assert.Len(t, actual, 2)
|
|
value, known, secret, _, err := awaitWithContext(ctx.Context(), actual["secret"].(StringOutput))
|
|
require.NoError(t, err)
|
|
assert.Equal(t, "secret string", value.(string))
|
|
assert.True(t, known)
|
|
assert.True(t, secret)
|
|
|
|
_, known, secret, _, err = awaitWithContext(ctx.Context(), actual["unknown"].(AnyOutput))
|
|
require.NoError(t, err)
|
|
assert.False(t, known)
|
|
assert.True(t, secret)
|
|
})
|
|
|
|
t.Run("output", func(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
ctx, err := NewContext(context.Background(), RunInfo{})
|
|
require.NoError(t, err)
|
|
|
|
dependencies := []resource.URN{"urn:pulumi:test_stack::test_project::pkg:index:type::name"}
|
|
|
|
actual, err := unmarshalPropertyMap(ctx, resource.PropertyMap{
|
|
"standard": resource.NewOutputProperty(resource.Output{
|
|
Element: resource.NewStringProperty("a string"),
|
|
Known: true,
|
|
Secret: false,
|
|
Dependencies: dependencies,
|
|
}),
|
|
"secret": resource.NewOutputProperty(resource.Output{
|
|
Element: resource.NewStringProperty("secret string"),
|
|
Known: true,
|
|
Secret: true,
|
|
Dependencies: dependencies,
|
|
}),
|
|
"unknown": resource.NewOutputProperty(resource.Output{
|
|
Dependencies: dependencies,
|
|
}),
|
|
"secret unknown": resource.NewOutputProperty(resource.Output{
|
|
Dependencies: dependencies,
|
|
Secret: true,
|
|
}),
|
|
"nested": resource.NewOutputProperty(resource.Output{
|
|
Element: resource.NewOutputProperty(resource.Output{
|
|
Element: resource.NewNumberProperty(42),
|
|
Known: true,
|
|
Secret: true,
|
|
}),
|
|
Known: true,
|
|
Dependencies: dependencies,
|
|
}),
|
|
})
|
|
require.NoError(t, err)
|
|
|
|
assertDeps := func(actual []Resource) {
|
|
assert.Len(t, actual, 1)
|
|
|
|
value, known, _, _, err := awaitWithContext(ctx.Context(), actual[0].URN())
|
|
require.NoError(t, err)
|
|
assert.True(t, known)
|
|
assert.Equal(t, URN("urn:pulumi:test_stack::test_project::pkg:index:type::name"), value.(URN))
|
|
}
|
|
|
|
assert.Len(t, actual, 5)
|
|
value, known, secret, deps, err := awaitWithContext(ctx.Context(), actual["standard"].(StringOutput))
|
|
require.NoError(t, err)
|
|
assert.Equal(t, "a string", value.(string))
|
|
assert.True(t, known)
|
|
assert.False(t, secret)
|
|
assertDeps(deps)
|
|
|
|
value, known, secret, deps, err = awaitWithContext(ctx.Context(), actual["secret"].(StringOutput))
|
|
require.NoError(t, err)
|
|
assert.Equal(t, "secret string", value.(string))
|
|
assert.True(t, known)
|
|
assert.True(t, secret)
|
|
assertDeps(deps)
|
|
|
|
_, known, secret, deps, err = awaitWithContext(ctx.Context(), actual["unknown"].(AnyOutput))
|
|
require.NoError(t, err)
|
|
assert.False(t, known)
|
|
assert.False(t, secret)
|
|
assertDeps(deps)
|
|
|
|
_, known, secret, deps, err = awaitWithContext(ctx.Context(), actual["secret unknown"].(AnyOutput))
|
|
require.NoError(t, err)
|
|
assert.False(t, known)
|
|
assert.True(t, secret)
|
|
assertDeps(deps)
|
|
|
|
value, known, secret, deps, err = awaitWithContext(ctx.Context(), actual["nested"].(Float64Output))
|
|
require.NoError(t, err)
|
|
assert.Equal(t, 42.0, value.(float64))
|
|
assert.True(t, known)
|
|
assert.True(t, secret)
|
|
assertDeps(deps)
|
|
})
|
|
|
|
t.Run("resource", func(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
ctx, err := NewContext(context.Background(), RunInfo{})
|
|
require.NoError(t, err)
|
|
|
|
actual, err := unmarshalPropertyMap(ctx, resource.PropertyMap{
|
|
"unknown id": resource.NewResourceReferenceProperty(resource.ResourceReference{
|
|
URN: "urn:pulumi:test_stack::test_project::pkg:index:type::name",
|
|
}),
|
|
})
|
|
require.NoError(t, err)
|
|
|
|
assert.Len(t, actual, 1)
|
|
value, known, secret, _, err := awaitWithContext(ctx.Context(), actual["unknown id"].(ResourceOutput))
|
|
require.NoError(t, err)
|
|
assert.True(t, known)
|
|
assert.False(t, secret)
|
|
res, ok := value.(Resource)
|
|
require.True(t, ok)
|
|
|
|
value, known, secret, _, err = awaitWithContext(ctx.Context(), res.URN())
|
|
require.NoError(t, err)
|
|
assert.Equal(t, URN("urn:pulumi:test_stack::test_project::pkg:index:type::name"), value)
|
|
assert.True(t, known)
|
|
assert.False(t, secret)
|
|
})
|
|
}
|