[sdk/python] Unmarshal output values in component providers (#8212)
This commit is contained in:
parent
a5f72ddbeb
commit
d3b2dedd1d
|
@ -11,6 +11,9 @@
|
|||
- [sdk/dotnet] - Marshal output values.
|
||||
[#8316](https://github.com/pulumi/pulumi/pull/8316)
|
||||
|
||||
- [sdk/python] - Unmarshal output values in component provider.
|
||||
[#8212](https://github.com/pulumi/pulumi/pull/8212)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
- [engine] - Compute dependents correctly during targeted deletes.
|
||||
|
|
|
@ -25,6 +25,7 @@ import sys
|
|||
import grpc
|
||||
import grpc.aio
|
||||
|
||||
from google.protobuf import struct_pb2
|
||||
from pulumi.provider.provider import Provider, CallResult, ConstructResult
|
||||
from pulumi.resource import ProviderResource, Resource, DependencyResource, DependencyProviderResource, \
|
||||
_parse_resource_reference
|
||||
|
@ -81,8 +82,7 @@ class ProviderServicer(ResourceProviderServicer):
|
|||
preview=request.dryRun)
|
||||
|
||||
pulumi.runtime.config.set_all_config(dict(request.config), request.configSecretKeys)
|
||||
|
||||
inputs = await self._construct_inputs(request)
|
||||
inputs = await self._construct_inputs(request.inputs, request.inputDependencies)
|
||||
|
||||
result = self.provider.construct(name=request.name,
|
||||
resource_type=request.type,
|
||||
|
@ -102,28 +102,32 @@ class ProviderServicer(ResourceProviderServicer):
|
|||
return response
|
||||
|
||||
@staticmethod
|
||||
async def _construct_inputs(request: proto.ConstructRequest) -> Dict[str, pulumi.Input[Any]]:
|
||||
async def _construct_inputs(inputs: struct_pb2.Struct, input_dependencies: Any) -> Dict[str, pulumi.Input[Any]]:
|
||||
|
||||
def deps(key: str) -> Set[str]:
|
||||
return set(urn for urn in
|
||||
request.inputDependencies.get(
|
||||
input_dependencies.get(
|
||||
key,
|
||||
proto.ConstructRequest.PropertyDependencies()
|
||||
).urns)
|
||||
|
||||
return {
|
||||
k: await ProviderServicer._create_output(the_input, deps=deps(k))
|
||||
k: await ProviderServicer._select_value(the_input, deps=deps(k))
|
||||
for k, the_input in
|
||||
rpc.deserialize_properties(request.inputs, keep_unknowns=True).items()
|
||||
rpc.deserialize_properties(inputs, keep_unknowns=True).items()
|
||||
}
|
||||
|
||||
@staticmethod
|
||||
async def _create_output(the_input: Any, deps: Set[str]) -> Any:
|
||||
async def _select_value(the_input: Any, deps: Set[str]) -> Any:
|
||||
is_secret = rpc.is_rpc_secret(the_input)
|
||||
|
||||
# If it's a resource reference or a prompt value, return it directly without wrapping
|
||||
# it as an output.
|
||||
if await _is_resource_reference(the_input, deps) or (not is_secret and len(deps) == 0):
|
||||
# If the input isn't a secret and either doesn't have any dependencies, already contains Outputs (from
|
||||
# deserialized output values), or is a resource reference, then return it directly without wrapping it
|
||||
# as an output.
|
||||
if not is_secret and (
|
||||
len(deps) == 0 or
|
||||
_contains_outputs(the_input) or
|
||||
await _is_resource_reference(the_input, deps)):
|
||||
return the_input
|
||||
|
||||
# Otherwise, wrap it as an output so we can handle secrets
|
||||
|
@ -220,7 +224,7 @@ class ProviderServicer(ResourceProviderServicer):
|
|||
).urns)
|
||||
|
||||
return {
|
||||
k: await ProviderServicer._create_output(the_input, deps=deps(k))
|
||||
k: await ProviderServicer._select_value(the_input, deps=deps(k))
|
||||
for k, the_input in
|
||||
# We need to keep_internal, to keep the `__self__` that would normally be filtered because
|
||||
# it starts with "__".
|
||||
|
@ -249,7 +253,7 @@ class ProviderServicer(ResourceProviderServicer):
|
|||
return proto.CallResponse(**resp)
|
||||
|
||||
async def Configure(self, request, context) -> proto.ConfigureResponse: # pylint: disable=invalid-overridden-method
|
||||
return proto.ConfigureResponse(acceptSecrets=True, acceptResources=True)
|
||||
return proto.ConfigureResponse(acceptSecrets=True, acceptResources=True, acceptOutputs=True)
|
||||
|
||||
async def GetPluginInfo(self, request, context) -> proto.PluginInfo: # pylint: disable=invalid-overridden-method
|
||||
return proto.PluginInfo(version=self.provider.version)
|
||||
|
@ -331,6 +335,25 @@ async def _is_resource_reference(the_input: Any, deps: Set[str]) -> bool:
|
|||
and next(iter(deps)) == await cast(Resource, the_input).urn.future())
|
||||
|
||||
|
||||
def _contains_outputs(the_input: Any) -> bool:
|
||||
"""
|
||||
Returns true if the input contains Outputs (deeply).
|
||||
"""
|
||||
if known_types.is_output(the_input):
|
||||
return True
|
||||
|
||||
if isinstance(the_input, list):
|
||||
for e in the_input:
|
||||
if _contains_outputs(e):
|
||||
return True
|
||||
elif isinstance(the_input, dict):
|
||||
for k in the_input:
|
||||
if _contains_outputs(the_input[k]):
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
|
||||
def _create_provider_resource(ref: str) -> ProviderResource:
|
||||
"""
|
||||
Rehydrate the provider reference into a registered ProviderResource,
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
# Copyright 2016-2021, Pulumi Corporation.
|
||||
# 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.
|
||||
|
|
|
@ -114,12 +114,13 @@ class ConfigureResponse:
|
|||
def __init__(self,
|
||||
acceptSecrets: bool=False,
|
||||
supportsPreview: bool=False,
|
||||
acceptResources: bool=False) -> None: ...
|
||||
acceptResources: bool=False,
|
||||
acceptOutputs: bool=False) -> None: ...
|
||||
|
||||
acceptSecrets: bool
|
||||
supportsPreview: bool
|
||||
acceptResources: bool
|
||||
|
||||
acceptOutputs: bool
|
||||
|
||||
class GetSchemaRequest:
|
||||
version: int
|
||||
|
|
|
@ -12,24 +12,42 @@
|
|||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
from typing import Dict, Any
|
||||
import functools
|
||||
from typing import Dict, Any, Optional, Tuple, List, Set, Callable, Awaitable
|
||||
from semver import VersionInfo as Version
|
||||
|
||||
import os
|
||||
import pytest
|
||||
|
||||
from pulumi.runtime.settings import Settings, configure
|
||||
from pulumi.provider.server import ProviderServicer
|
||||
from pulumi.runtime import proto, rpc
|
||||
from pulumi.runtime import proto, rpc, rpc_manager, ResourceModule, Mocks
|
||||
from pulumi.resource import CustomResource, ResourceOptions
|
||||
from pulumi.runtime.proto.provider_pb2 import ConstructRequest
|
||||
import google.protobuf.struct_pb2 as struct_pb2
|
||||
from google.protobuf import struct_pb2
|
||||
import pulumi.output
|
||||
|
||||
|
||||
def pulumi_test(coro):
|
||||
wrapped = pulumi.runtime.test(coro)
|
||||
|
||||
@functools.wraps(wrapped)
|
||||
def wrapper(*args, **kwargs):
|
||||
configure(Settings())
|
||||
rpc._RESOURCE_PACKAGES.clear()
|
||||
rpc._RESOURCE_MODULES.clear()
|
||||
rpc_manager.RPC_MANAGER = rpc_manager.RPCManager()
|
||||
|
||||
wrapped(*args, **kwargs)
|
||||
|
||||
return wrapper
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_construct_inputs_parses_request():
|
||||
value = 'foobar'
|
||||
inputs = _as_struct({'echo': value})
|
||||
req = ConstructRequest(inputs=inputs)
|
||||
inputs = await ProviderServicer._construct_inputs(req)
|
||||
inputs = await ProviderServicer._construct_inputs(req.inputs, req.inputDependencies) # pylint: disable=no-member
|
||||
assert len(inputs) == 1
|
||||
assert inputs['echo'] == value
|
||||
|
||||
|
@ -39,7 +57,7 @@ async def test_construct_inputs_preserves_unknowns():
|
|||
unknown = '04da6b54-80e4-46f7-96ec-b56ff0331ba9'
|
||||
inputs = _as_struct({'echo': unknown})
|
||||
req = ConstructRequest(inputs=inputs)
|
||||
inputs = await ProviderServicer._construct_inputs(req)
|
||||
inputs = await ProviderServicer._construct_inputs(req.inputs, req.inputDependencies) # pylint: disable=no-member
|
||||
assert len(inputs) == 1
|
||||
assert isinstance(inputs['echo'], pulumi.output.Unknown)
|
||||
|
||||
|
@ -48,3 +66,487 @@ def _as_struct(key_values: Dict[str, Any]) -> struct_pb2.Struct:
|
|||
the_struct = struct_pb2.Struct()
|
||||
the_struct.update(key_values) # pylint: disable=no-member
|
||||
return the_struct
|
||||
|
||||
class MockResource(CustomResource):
|
||||
def __init__(self, name: str, opts: Optional[ResourceOptions] = None):
|
||||
CustomResource.__init__(self, "test:index:MockResource", name, opts=opts)
|
||||
|
||||
class MockInputDependencies:
|
||||
""" A mock for ConstructRequest.inputDependencies
|
||||
|
||||
We need only support a `get() -> T where T.urns: List[str]` operation.
|
||||
"""
|
||||
def __init__(self, urns: Optional[List[str]]):
|
||||
self.urns = urns if urns else []
|
||||
|
||||
def get(self, *args):
|
||||
#pylint: disable=unused-argument
|
||||
return self
|
||||
|
||||
class TestModule(ResourceModule):
|
||||
def construct(self, name: str, typ: str, urn: str):
|
||||
if typ == "test:index:MockResource":
|
||||
return MockResource(name, opts=ResourceOptions(urn=urn))
|
||||
raise Exception(f"unknown resource type {typ}")
|
||||
|
||||
def version(self) -> Optional[Version]:
|
||||
return None
|
||||
|
||||
class TestMocks(Mocks):
|
||||
def call(self, args: pulumi.runtime.MockCallArgs) -> Any:
|
||||
raise Exception(f"unknown function {args.token}")
|
||||
|
||||
def new_resource(self, args: pulumi.runtime.MockResourceArgs) -> Tuple[Optional[str], dict]:
|
||||
return args.name+"_id", args.inputs
|
||||
|
||||
def assert_output_equal(value: Any,
|
||||
known: bool, secret: bool,
|
||||
deps: Optional[List[str]] = None):
|
||||
async def check(actual: Any):
|
||||
assert isinstance(actual, pulumi.Output)
|
||||
|
||||
if callable(value):
|
||||
res = value(await actual.future())
|
||||
if isinstance(res, Awaitable):
|
||||
await res
|
||||
else:
|
||||
assert (await actual.future()) == value
|
||||
|
||||
assert known == await actual.is_known()
|
||||
assert secret == await actual.is_secret()
|
||||
|
||||
actual_deps: Set[Optional[str]] = set()
|
||||
resources = await actual.resources()
|
||||
for r in resources:
|
||||
urn = await r.urn.future()
|
||||
actual_deps.add(urn)
|
||||
|
||||
assert actual_deps == set(deps if deps else [])
|
||||
return True
|
||||
return check
|
||||
|
||||
|
||||
def create_secret(value: Any):
|
||||
return {rpc._special_sig_key: rpc._special_secret_sig, "value": value}
|
||||
|
||||
def create_resource_ref(urn: str, id_: Optional[str]):
|
||||
ref = {rpc._special_sig_key: rpc._special_resource_sig, "urn": urn}
|
||||
if id_ is not None:
|
||||
ref["id"] = id_
|
||||
return ref
|
||||
|
||||
def create_output_value(value: Optional[Any] = None,
|
||||
secret: Optional[bool] = None,
|
||||
dependencies: Optional[List[str]] = None):
|
||||
val: Dict[str, Any] = {rpc._special_sig_key: rpc._special_output_value_sig}
|
||||
if value is not None:
|
||||
val["value"] = value
|
||||
if secret is not None:
|
||||
val["secret"] = secret
|
||||
if dependencies is not None:
|
||||
val["dependencies"] = dependencies
|
||||
return val
|
||||
|
||||
test_urn = "urn:pulumi:stack::project::test:index:MockResource::name"
|
||||
test_id = "name_id"
|
||||
|
||||
class UnmarshalOutputTestCase:
|
||||
def __init__(self,
|
||||
name: str,
|
||||
input_: Any,
|
||||
deps: Optional[List[str]] = None,
|
||||
expected: Optional[Any] = None,
|
||||
assert_: Optional[Callable[[Any], Awaitable]] = None):
|
||||
self.name = name
|
||||
self.input_ = input_
|
||||
self.deps = deps
|
||||
self.expected = expected
|
||||
self.assert_ = assert_
|
||||
|
||||
async def run(self):
|
||||
pulumi.runtime.set_mocks(TestMocks(), "project", "stack", True)
|
||||
pulumi.runtime.register_resource_module("test", "index", TestModule())
|
||||
# This registers the resource purely for the purpose of the test.
|
||||
pulumi.runtime.settings.get_monitor().resources[test_urn] = \
|
||||
pulumi.runtime.mocks.MockMonitor.ResourceRegistration(test_urn, test_id, dict())
|
||||
|
||||
inputs = { "value": self.input_ }
|
||||
input_struct = _as_struct(inputs)
|
||||
req = ConstructRequest(inputs=input_struct)
|
||||
result = await ProviderServicer._construct_inputs(
|
||||
req.inputs, MockInputDependencies(self.deps)) # pylint: disable=no-member
|
||||
actual = result["value"]
|
||||
if self.assert_:
|
||||
await self.assert_(actual)
|
||||
else:
|
||||
assert actual == self.expected
|
||||
|
||||
|
||||
class Assert:
|
||||
"""Describes a series of asserts to be performed.
|
||||
|
||||
Each assert can be:
|
||||
- An async value to be awaited and asserted.
|
||||
assert await val
|
||||
|
||||
- A sync function to be called and asserted on. This will be called on the
|
||||
same set of arguments that the class was called on.
|
||||
assert fn(actual)
|
||||
|
||||
- A plain value to be asserted on.
|
||||
assert val
|
||||
|
||||
"""
|
||||
def __init__(self, *asserts):
|
||||
self.asserts = asserts
|
||||
|
||||
async def __call__(self, *args, **kargs):
|
||||
for assert_ in self.asserts:
|
||||
assert await Assert.__eval(assert_, *args, **kargs)
|
||||
|
||||
@staticmethod
|
||||
async def __eval(a, *args, **kargs) -> Any:
|
||||
if isinstance(a, Awaitable):
|
||||
return await a
|
||||
elif isinstance(a, Callable):
|
||||
a_res = a(*args, **kargs)
|
||||
return await Assert.__eval(a_res, *args, **kargs)
|
||||
return a
|
||||
|
||||
@staticmethod
|
||||
def async_equal(a, b):
|
||||
"""Asserts that two values are equal when evaluated with async and
|
||||
given the args that `Asserts` were called on.
|
||||
"""
|
||||
async def check(*args, **kargs):
|
||||
a_res = await Assert.__eval(a, *args, **kargs)
|
||||
b_res = await Assert.__eval(b, *args, **kargs)
|
||||
assert a_res == b_res
|
||||
return True
|
||||
return check
|
||||
|
||||
async def array_nested_resource_ref(actual):
|
||||
async def helper(v: Any):
|
||||
assert isinstance(v, list)
|
||||
assert isinstance(v[0], MockResource)
|
||||
assert await v[0].urn.future() == test_urn
|
||||
assert await v[0].id.future() == test_id
|
||||
await assert_output_equal(helper, True, False, [test_urn])(actual)
|
||||
|
||||
async def object_nested_resource_ref(actual):
|
||||
async def helper(v: Any):
|
||||
assert isinstance(v["foo"], MockResource)
|
||||
assert await v["foo"].urn.future() == test_urn
|
||||
assert await v["foo"].id.future() == test_id
|
||||
await assert_output_equal(helper, True, False, [test_urn])(actual)
|
||||
|
||||
async def object_nested_resource_ref_and_secret(actual):
|
||||
async def helper(v: Any):
|
||||
assert isinstance(v["foo"], MockResource)
|
||||
assert await v["foo"].urn.future() == test_urn
|
||||
assert await v["foo"].id.future() == test_id
|
||||
assert v["bar"] == "ssh"
|
||||
await assert_output_equal(helper, True, True, [test_urn])(actual)
|
||||
|
||||
|
||||
deserialization_tests = [
|
||||
UnmarshalOutputTestCase(
|
||||
name="unknown",
|
||||
input_=rpc.UNKNOWN,
|
||||
deps=["fakeURN"],
|
||||
assert_=assert_output_equal(None, False, False, ["fakeURN"]),
|
||||
),
|
||||
UnmarshalOutputTestCase(
|
||||
name="array nested unknown",
|
||||
input_=[rpc.UNKNOWN],
|
||||
deps=["fakeURN"],
|
||||
assert_=assert_output_equal(None, False, False, ["fakeURN"]),
|
||||
),
|
||||
UnmarshalOutputTestCase(
|
||||
name="object nested unknown",
|
||||
input_={"foo": rpc.UNKNOWN},
|
||||
deps=["fakeURN"],
|
||||
assert_=assert_output_equal(None, False, False, ["fakeURN"]),
|
||||
),
|
||||
UnmarshalOutputTestCase(
|
||||
name="unknown output value",
|
||||
input_=create_output_value(None, False, ["fakeURN"]),
|
||||
deps=["fakeURN"],
|
||||
assert_=assert_output_equal(None, False, False, ["fakeURN"]),
|
||||
),
|
||||
UnmarshalOutputTestCase(
|
||||
name="unknown output value (no deps)",
|
||||
input_=create_output_value(),
|
||||
assert_=assert_output_equal(None, False, False),
|
||||
),
|
||||
UnmarshalOutputTestCase(
|
||||
name="array nested unknown output value",
|
||||
input_=[create_output_value(None, False, ["fakeURN"])],
|
||||
deps=["fakeURN"],
|
||||
assert_=Assert(
|
||||
lambda actual: isinstance(actual, list),
|
||||
lambda actual: assert_output_equal(None, False, False, ["fakeURN"])(actual[0])
|
||||
),
|
||||
),
|
||||
UnmarshalOutputTestCase(
|
||||
name="array nested unknown output value (no deps)",
|
||||
input_=[create_output_value(None, False, ["fakeURN"])],
|
||||
assert_=Assert(
|
||||
lambda actual: isinstance(actual, list),
|
||||
lambda actual: assert_output_equal(None, False, False, ["fakeURN"])(actual[0])
|
||||
),
|
||||
),
|
||||
UnmarshalOutputTestCase(
|
||||
name="object nested unknown output value",
|
||||
input_= { "foo": create_output_value(None, False, ["fakeURN"]) },
|
||||
deps=["fakeURN"],
|
||||
assert_=Assert(
|
||||
lambda actual: not isinstance(actual, pulumi.Output),
|
||||
lambda actual: assert_output_equal(None, False, False, ["fakeURN"])(actual["foo"]),
|
||||
),
|
||||
),
|
||||
UnmarshalOutputTestCase(
|
||||
name="object nested unknown output value (no deps)",
|
||||
input_= { "foo": create_output_value(None, False, ["fakeURN"]) },
|
||||
assert_=Assert(
|
||||
lambda actual: not isinstance(actual, pulumi.Output),
|
||||
lambda actual: assert_output_equal(None, False, False, ["fakeURN"])(actual["foo"]),
|
||||
),
|
||||
),
|
||||
UnmarshalOutputTestCase(
|
||||
name="string value (no deps)",
|
||||
input_="hi",
|
||||
expected="hi",
|
||||
),
|
||||
UnmarshalOutputTestCase(
|
||||
name="array nested string value (no deps)",
|
||||
input_=["hi"],
|
||||
expected=["hi"],
|
||||
),
|
||||
UnmarshalOutputTestCase(
|
||||
name="object nested string value (no deps)",
|
||||
input_= { "foo": "hi" },
|
||||
expected= { "foo": "hi" },
|
||||
),
|
||||
UnmarshalOutputTestCase(
|
||||
name="string output value",
|
||||
input_=create_output_value("hi", False, ["fakeURN"]),
|
||||
deps=["fakeURN"],
|
||||
assert_=assert_output_equal("hi", True, False, ["fakeURN"]),
|
||||
),
|
||||
UnmarshalOutputTestCase(
|
||||
name="string output value (no deps)",
|
||||
input_=create_output_value("hi"),
|
||||
assert_=assert_output_equal("hi", True, False),
|
||||
),
|
||||
UnmarshalOutputTestCase(
|
||||
name="array nested string output value",
|
||||
input_=[create_output_value("hi", False, ["fakeURN"])],
|
||||
deps=["fakeURN"],
|
||||
assert_=Assert(
|
||||
lambda actual: isinstance(actual, list),
|
||||
lambda actual: assert_output_equal("hi", True, False, ["fakeURN"])(actual[0]),
|
||||
),
|
||||
),
|
||||
UnmarshalOutputTestCase(
|
||||
name="array nested string output value (no deps)",
|
||||
input_=[create_output_value("hi", False, ["fakeURN"])],
|
||||
assert_=Assert(
|
||||
lambda actual: isinstance(actual, list),
|
||||
lambda actual: assert_output_equal("hi", True, False, ["fakeURN"])(actual[0]),
|
||||
),
|
||||
),
|
||||
UnmarshalOutputTestCase(
|
||||
name="object nested string output value",
|
||||
input_={ "foo": create_output_value("hi", False, ["fakeURN"])},
|
||||
deps=["fakeURN"],
|
||||
assert_=Assert(
|
||||
lambda actual: not isinstance(actual, pulumi.Output),
|
||||
lambda actual: assert_output_equal("hi", True, False, ["fakeURN"])(actual["foo"])
|
||||
),
|
||||
),
|
||||
UnmarshalOutputTestCase(
|
||||
name="object nested string output value (no deps)",
|
||||
input_={ "foo": create_output_value("hi", False, ["fakeURN"])},
|
||||
assert_=Assert(
|
||||
lambda actual: not isinstance(actual, pulumi.Output),
|
||||
lambda actual: assert_output_equal("hi", True, False, ["fakeURN"])(actual["foo"])
|
||||
),
|
||||
),
|
||||
UnmarshalOutputTestCase(
|
||||
name="string secrets (no deps)",
|
||||
input_=create_secret("shh"),
|
||||
assert_=assert_output_equal("shh", True, True),
|
||||
),
|
||||
UnmarshalOutputTestCase(
|
||||
name="array nested string secrets (no deps)",
|
||||
input_=[create_secret("shh")],
|
||||
assert_=assert_output_equal(["shh"], True, True),
|
||||
),
|
||||
UnmarshalOutputTestCase(
|
||||
name="object nested string secrets (no deps)",
|
||||
input_={ "foo": create_secret("shh")},
|
||||
assert_=assert_output_equal({"foo": "shh"}, True, True),
|
||||
),
|
||||
UnmarshalOutputTestCase(
|
||||
name="string secret output value (no deps)",
|
||||
input_=create_output_value("shh", True),
|
||||
assert_=assert_output_equal("shh", True, True),
|
||||
),
|
||||
UnmarshalOutputTestCase(
|
||||
name="array nested string secret output value (no deps)",
|
||||
input_=[create_output_value("shh", True)],
|
||||
assert_=Assert(
|
||||
lambda actual: isinstance(actual, list),
|
||||
lambda actual: assert_output_equal("shh", True, True)(actual[0]),
|
||||
),
|
||||
),
|
||||
UnmarshalOutputTestCase(
|
||||
name="object nested string secret output value (no deps)",
|
||||
input_={"foo": create_output_value("shh", True)},
|
||||
assert_=Assert(
|
||||
lambda actual: not isinstance(actual, pulumi.Output),
|
||||
lambda actual: assert_output_equal("shh", True, True)(actual["foo"]),
|
||||
),
|
||||
),
|
||||
UnmarshalOutputTestCase(
|
||||
name="string secret output value",
|
||||
input_=create_output_value("shh", True, ["fakeURN1", "fakeURN2"]),
|
||||
deps=["fakeURN1", "fakeURN2"],
|
||||
assert_=assert_output_equal("shh", True, True, ["fakeURN1", "fakeURN2"]),
|
||||
),
|
||||
UnmarshalOutputTestCase(
|
||||
name="string secret output value (no deps)",
|
||||
input_=create_output_value("shh", True, ["fakeURN1", "fakeURN2"]),
|
||||
assert_=assert_output_equal("shh", True, True, ["fakeURN1", "fakeURN2"]),
|
||||
),
|
||||
UnmarshalOutputTestCase(
|
||||
name="array nested string secret output value",
|
||||
input_=[create_output_value("shh", True, ["fakeURN1", "fakeURN2"])],
|
||||
deps=["fakeURN1", "fakeURN2"],
|
||||
assert_=Assert(
|
||||
lambda actual: isinstance(actual, list),
|
||||
lambda actual: assert_output_equal("shh", True, True, ["fakeURN1", "fakeURN2"])(actual[0]),
|
||||
),
|
||||
),
|
||||
UnmarshalOutputTestCase(
|
||||
name="array nested string secret output value (no deps)",
|
||||
input_=[create_output_value("shh", True, ["fakeURN1", "fakeURN2"])],
|
||||
assert_=Assert(
|
||||
lambda actual: isinstance(actual, list),
|
||||
lambda actual: assert_output_equal("shh", True, True, ["fakeURN1", "fakeURN2"])(actual[0]),
|
||||
),
|
||||
),
|
||||
UnmarshalOutputTestCase(
|
||||
name="object nested string secret output value",
|
||||
input_={ "foo": create_output_value("shh", True, ["fakeURN1", "fakeURN2"])},
|
||||
deps=["fakeURN1", "fakeURN2"],
|
||||
assert_=Assert(
|
||||
lambda actual: not isinstance(actual, pulumi.Output),
|
||||
lambda actual: assert_output_equal("shh", True, True, ["fakeURN1", "fakeURN2"])(actual["foo"]),
|
||||
),
|
||||
),
|
||||
UnmarshalOutputTestCase(
|
||||
name="object nested string secret output value (no deps)",
|
||||
input_={ "foo": create_output_value("shh", True, ["fakeURN1", "fakeURN2"])},
|
||||
assert_=Assert(
|
||||
lambda actual: not isinstance(actual, pulumi.Output),
|
||||
lambda actual: assert_output_equal("shh", True, True, ["fakeURN1", "fakeURN2"])(actual["foo"]),
|
||||
),
|
||||
),
|
||||
UnmarshalOutputTestCase(
|
||||
name="resource ref",
|
||||
input_=create_resource_ref(test_urn, test_id),
|
||||
deps=[test_urn],
|
||||
assert_=Assert(
|
||||
lambda actual: isinstance(actual, MockResource),
|
||||
Assert.async_equal(lambda actual: actual.urn.future(), test_urn),
|
||||
Assert.async_equal(lambda actual: actual.id.future(), test_id),
|
||||
),
|
||||
),
|
||||
UnmarshalOutputTestCase(
|
||||
name="resource ref (no deps)",
|
||||
input_=create_resource_ref(test_urn, test_id),
|
||||
assert_=Assert(
|
||||
lambda actual: isinstance(actual, MockResource),
|
||||
Assert.async_equal(lambda actual: actual.urn.future(), test_urn),
|
||||
Assert.async_equal(lambda actual: actual.id.future(), test_id),
|
||||
),
|
||||
),
|
||||
UnmarshalOutputTestCase(
|
||||
name="array nested resource ref",
|
||||
input_=[create_resource_ref(test_urn, test_id)],
|
||||
deps=[test_urn],
|
||||
assert_=array_nested_resource_ref
|
||||
),
|
||||
UnmarshalOutputTestCase(
|
||||
name="array nested resource ref (no deps)",
|
||||
input_=[create_resource_ref(test_urn, test_id)],
|
||||
assert_=Assert(
|
||||
lambda actual: isinstance(actual, list),
|
||||
lambda actual: isinstance(actual[0], MockResource),
|
||||
Assert.async_equal(lambda actual: actual[0].urn.future(), test_urn),
|
||||
Assert.async_equal(lambda actual: actual[0].id.future(), test_id),
|
||||
),
|
||||
),
|
||||
UnmarshalOutputTestCase(
|
||||
name="object nested resource ref",
|
||||
input_={ "foo": create_resource_ref(test_urn, test_id) },
|
||||
deps=[test_urn],
|
||||
assert_=object_nested_resource_ref
|
||||
),
|
||||
UnmarshalOutputTestCase(
|
||||
name="object nested resource ref (no deps)",
|
||||
input_={ "foo": create_resource_ref(test_urn, test_id) },
|
||||
assert_=Assert(
|
||||
lambda actual: isinstance(actual["foo"], MockResource),
|
||||
Assert.async_equal(lambda actual: actual["foo"].urn.future(), test_urn),
|
||||
Assert.async_equal(lambda actual: actual["foo"].id.future(), test_id),
|
||||
),
|
||||
),
|
||||
UnmarshalOutputTestCase(
|
||||
name="object nested resource ref and secret",
|
||||
input_={
|
||||
"foo": create_resource_ref(test_urn, test_id),
|
||||
"bar": create_secret("ssh"),
|
||||
},
|
||||
deps=[test_urn],
|
||||
assert_=object_nested_resource_ref_and_secret
|
||||
),
|
||||
UnmarshalOutputTestCase(
|
||||
name="object nested resource ref and secret output value",
|
||||
input_={
|
||||
"foo": create_resource_ref(test_urn, test_id),
|
||||
"bar": create_output_value("shh", True),
|
||||
},
|
||||
deps=[test_urn],
|
||||
assert_=Assert(
|
||||
lambda actual: not isinstance(actual, pulumi.Output),
|
||||
lambda actual: isinstance(actual["foo"], MockResource),
|
||||
Assert.async_equal(lambda actual: actual["foo"].urn.future(), test_urn),
|
||||
Assert.async_equal(lambda actual: actual["foo"].id.future(), test_id),
|
||||
lambda actual: assert_output_equal("shh", True, True)(actual["bar"]),
|
||||
),
|
||||
),
|
||||
UnmarshalOutputTestCase(
|
||||
name="object nested resource ref and secret output value (no deps)",
|
||||
input_={
|
||||
"foo": create_resource_ref(test_urn, test_id),
|
||||
"bar": create_output_value("shh", True),
|
||||
},
|
||||
assert_=Assert(
|
||||
lambda actual: not isinstance(actual, pulumi.Output),
|
||||
lambda actual: isinstance(actual["foo"], MockResource),
|
||||
Assert.async_equal(lambda actual: actual["foo"].urn.future(), test_urn),
|
||||
Assert.async_equal(lambda actual: actual["foo"].id.future(), test_id),
|
||||
lambda actual: assert_output_equal("shh", True, True)(actual["bar"]),
|
||||
),
|
||||
),
|
||||
]
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"testcase", deserialization_tests, ids=list(map(lambda x: x.name, deserialization_tests)))
|
||||
@pulumi_test
|
||||
async def test_deserialize_correctly(testcase):
|
||||
await testcase.run()
|
||||
|
|
|
@ -179,7 +179,7 @@ class NextSerializationTests(unittest.TestCase):
|
|||
self.assertEqual(id, prop["id"])
|
||||
|
||||
res = rpc.deserialize_properties(prop)
|
||||
self.assertTrue(isinstance(res, MyCustomResource))
|
||||
self.assertIsInstance(res, MyCustomResource)
|
||||
|
||||
rpc._RESOURCE_MODULES.clear()
|
||||
res = rpc.deserialize_properties(prop)
|
||||
|
@ -209,7 +209,7 @@ class NextSerializationTests(unittest.TestCase):
|
|||
self.assertEqual(id, prop["id"])
|
||||
|
||||
res = rpc.deserialize_properties(prop)
|
||||
self.assertTrue(isinstance(res, MyCustomResource))
|
||||
self.assertIsInstance(res, MyCustomResource)
|
||||
|
||||
rpc._RESOURCE_MODULES.clear()
|
||||
res = rpc.deserialize_properties(prop)
|
||||
|
|
Loading…
Reference in a new issue