Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[sdk/python] Don't error on type mismatches when using input values for outputs #11422

Merged
merged 1 commit into from Nov 21, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
@@ -0,0 +1,4 @@
changes:
- type: fix
scope: sdk/python
description: Don't error on type mismatches when using input values for outputs
28 changes: 22 additions & 6 deletions sdk/python/lib/pulumi/runtime/rpc.py
Expand Up @@ -789,6 +789,7 @@ def translate_output_properties(
typ: Optional[type] = None,
transform_using_type_metadata: bool = False,
path: Optional["_Path"] = None,
return_none_on_dict_type_mismatch: bool = False,
) -> Any:
"""
Recursively rewrite keys of objects returned by the engine to conform with a naming
Expand Down Expand Up @@ -827,7 +828,12 @@ def translate_output_properties(
if is_rpc_secret(output):
unwrapped = unwrap_rpc_secret(output)
result = translate_output_properties(
unwrapped, output_transformer, typ, transform_using_type_metadata
unwrapped,
output_transformer,
typ,
transform_using_type_metadata,
path,
return_none_on_dict_type_mismatch,
)
return wrap_rpc_secret(result)

Expand Down Expand Up @@ -860,7 +866,8 @@ def translate_output_properties(
output_transformer,
get_type(k),
transform_using_type_metadata,
path=_Path(k, parent=path),
_Path(k, parent=path),
return_none_on_dict_type_mismatch,
)
for k, v in output.items()
}
Expand All @@ -878,6 +885,8 @@ def translate_output_properties(
if transform_using_type_metadata:
# pylint: disable=C3001
translate = lambda k: k
elif return_none_on_dict_type_mismatch:
return None
else:
raise AssertionError(
(
Expand All @@ -893,7 +902,8 @@ def translate_output_properties(
output_transformer,
get_type(k),
transform_using_type_metadata,
path=_Path(k, parent=path),
_Path(k, parent=path),
return_none_on_dict_type_mismatch,
)
for k, v in output.items()
}
Expand All @@ -906,7 +916,8 @@ def translate_output_properties(
output_transformer,
element_type,
transform_using_type_metadata,
path=_Path(str(i), parent=path),
_Path(str(i), parent=path),
return_none_on_dict_type_mismatch,
)
for i, v in enumerate(output)
]
Expand Down Expand Up @@ -1044,14 +1055,19 @@ def resolve_outputs(
for key, value in list(serialized_props.items()):
translated_key = translate(key)
if translated_key not in all_properties:
# input prop the engine didn't give us a final value for.Just use the value passed into the resource by
# the user.
# input prop the engine didn't give us a final value for.
# Just use the value passed into the resource by the user.
# Set `return_none_on_dict_type_mismatch` to return `None` rather than raising an error when the value
# is a dict and the type doesn't match (which is what would happen if the value didn't exist as an
# input prop). This allows `pulumi up` to work without erroring when there is an input and output prop
# with the same name but different types.
all_properties[translated_key] = translate_output_properties(
deserialize_property(value),
translate_to_pass,
types.get(key),
transform_using_type_metadata,
path=_Path(translated_key, resource=f"{res._name}"),
return_none_on_dict_type_mismatch=True,
)

resolve_properties(resolvers, all_properties, translated_deps)
Expand Down
@@ -0,0 +1,13 @@
# Copyright 2016-2022, 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.
@@ -0,0 +1,81 @@
# Copyright 2016-2022, 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.

from typing import Optional

import pulumi


class Instance(pulumi.CustomResource):
public_ip: pulumi.Output[str]
def __init__(self, resource_name, name: pulumi.Input[str] = None, value: pulumi.Input[str] = None, opts = None):
if opts is None:
opts = pulumi.ResourceOptions()
if name is None and not opts.urn:
raise TypeError("Missing required property 'name'")
__props__: dict = dict()
__props__["public_ip"] = None
__props__["name"] = name
__props__["value"] = value
super(Instance, self).__init__("aws:ec2/instance:Instance", resource_name, __props__, opts)


@pulumi.input_type
class DefaultLogGroupArgs:
def __init__(self, *, skip: Optional[bool] = None):
if skip is not None:
pulumi.set(self, "skip", skip)

@property
@pulumi.getter
def skip(self) -> Optional[bool]:
return pulumi.get(self, "skip")

@skip.setter
def skip(self, value: Optional[bool]):
pulumi.set(self, "skip", value)


@pulumi.input_type
class FargateTaskDefinitionArgs:
def __init__(self, *, log_group: Optional[DefaultLogGroupArgs] = None):
if log_group is not None:
pulumi.set(self, "log_group", log_group)

@property
@pulumi.getter(name="logGroup")
def log_group(self) -> Optional[DefaultLogGroupArgs]:
return pulumi.get(self, "log_group")

@log_group.setter
def log_group(self, value: Optional[DefaultLogGroupArgs]):
pulumi.set(self, "log_group", value)


# This resource has an input named `logGroup` typed as `DefaultLogGroupArgs` and an output named `logGroup` typed
# as `Instance`. When the provider returns no value for `logGroup`, it should not try to set the output to the
# input value due to the type mismatch.
class FargateTaskDefinition(pulumi.ComponentResource):
def __init__(self, resource_name: str, log_group: Optional[pulumi.InputType[DefaultLogGroupArgs]] = None):
__props__ = FargateTaskDefinitionArgs.__new__(FargateTaskDefinitionArgs)
__props__.__dict__["log_group"] = log_group
super().__init__("awsx:ecs:FargateTaskDefinition", resource_name, __props__, None, remote=True)

@property
@pulumi.getter(name="logGroup")
def log_group(self) -> pulumi.Output[Optional[Instance]]:
return pulumi.get(self, "log_group")


task_def = FargateTaskDefinition("task_def", log_group=DefaultLogGroupArgs(skip=True))
@@ -0,0 +1,34 @@
# Copyright 2016-2022, 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.

from os import path
from ..util import LanghostTest


class InputValuesForOutputsTest(LanghostTest):
"""
"""
def test_input_values_for_outputs(self):
self.run_test(
program=path.join(self.base_path(), "input_values_for_outputs"),
expected_resource_count=1)

def register_resource(self, _ctx, _dry_run, ty, name, _resource, _dependencies, _parent, _custom, protect,
_provider, _property_deps, _delete_before_replace, _ignore_changes, _version, _import,
_replace_on_changes):
return {
"urn": self.make_urn(ty, name),
"id": name,
"object": {} # return no outputs
}