Check for valid PB types in serialize_property (#3060)

Just what it says on the tin. This allows us to return an incrementally
better error.

Fixes #2939.
This commit is contained in:
Pat Gavlin 2019-08-09 16:48:28 -07:00 committed by GitHub
parent a190e54988
commit 7fef102bc3
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 115 additions and 20 deletions

View file

@ -7,6 +7,7 @@ name = "pypi"
protobuf = ">=3.6.0"
grpcio = ">=1.9.1"
dill = ">=0.3.0"
six = ">=1.12.0"
[dev-packages]
pylint = ">=2.1"

View file

@ -1,7 +1,7 @@
{
"_meta": {
"hash": {
"sha256": "420aade1da2a271ef162050a84c18d86047a509e9bd25f5111f139b411223f3c"
"sha256": "b51d443cfdc6681b0f87cca71b6fa6a930ca951c3e86e082e70d307133b36381"
},
"pipfile-spec": 6,
"requires": {},
@ -61,33 +61,33 @@
},
"protobuf": {
"hashes": [
"sha256:03f43eac9d5b651f976e91cf46a25b75e5779d98f0f4114b0abfed83376d75f8",
"sha256:0c94b21e6de01362f91a86b372555d22a60b59708599ca9d5032ae9fdf8e3538",
"sha256:2d2a9f30f61f4063fadd7fb68a2510a6939b43c0d6ceeec5c4704f22225da28e",
"sha256:34a0b05fca061e4abb77dd180209f68d8637115ff319f51e28a6a9382d69853a",
"sha256:358710fd0db25372edcf1150fa691f48376a134a6c69ce29f38f185eea7699e6",
"sha256:41e47198b94c27ba05a08b4a95160656105745c462af574e4bcb0807164065c0",
"sha256:8c61cc8a76e9d381c665aecc5105fa0f1878cf7db8b5cd17202603bcb386d0fc",
"sha256:a6eebc4db759e58fdac02efcd3028b811effac881d8a5bad1996e4e8ee6acb47",
"sha256:a9c12f7c98093da0a46ba76ec40ace725daa1ac4038c41e4b1466afb5c45bb01",
"sha256:cb95068492ba0859b8c9e61fa8ba206a83c64e5d0916fb4543700b2e2b214115",
"sha256:cd98476ce7bb4dcd6a7b101f5eecdc073dafea19f311e36eb8fba1a349346277",
"sha256:ce64cfbea18c535176bdaa10ba740c0fc4c6d998a3f511c17bedb0ae4b3b167c",
"sha256:dcbb59eac73fd454e8f2c5fba9e3d3320fd4707ed6a9d3ea3717924a6f0903ea",
"sha256:dd67f34458ae716029e2a71ede998e9092493b62a519236ca52e3c5202096c87",
"sha256:e3c96056eb5b7284a20e256cb0bf783c8f36ad82a4ae5434a7b7cd02384144a7",
"sha256:f612d584d7a27e2f39e7b17878430a959c1bc09a74ba09db096b468558e5e126",
"sha256:f6de8a7d6122297b81566e5bd4df37fd5d62bec14f8f90ebff8ede1c9726cd0a",
"sha256:fa529d9261682b24c2aaa683667253175c9acebe0a31105394b221090da75832"
"sha256:00a1b0b352dc7c809749526d1688a64b62ea400c5b05416f93cfb1b11a036295",
"sha256:01acbca2d2c8c3f7f235f1842440adbe01bbc379fa1cbdd80753801432b3fae9",
"sha256:0a795bca65987b62d6b8a2d934aa317fd1a4d06a6dd4df36312f5b0ade44a8d9",
"sha256:0ec035114213b6d6e7713987a759d762dd94e9f82284515b3b7331f34bfaec7f",
"sha256:31b18e1434b4907cb0113e7a372cd4d92c047ce7ba0fa7ea66a404d6388ed2c1",
"sha256:32a3abf79b0bef073c70656e86d5bd68a28a1fbb138429912c4fc07b9d426b07",
"sha256:55f85b7808766e5e3f526818f5e2aeb5ba2edcc45bcccede46a3ccc19b569cb0",
"sha256:64ab9bc971989cbdd648c102a96253fdf0202b0c38f15bd34759a8707bdd5f64",
"sha256:64cf847e843a465b6c1ba90fb6c7f7844d54dbe9eb731e86a60981d03f5b2e6e",
"sha256:917c8662b585470e8fd42f052661fc66d59fccaae450a60044307dcbf82a3335",
"sha256:afed9003d7f2be2c3df20f64220c30faec441073731511728a2cb4cab4cd46a6",
"sha256:b883d7eb129b1b57c5128146bc7c2d1f15de457e96a549827fbee6f26eeedc46",
"sha256:bf8e05d638b585d1752c5a84247134a0350d3a8b73d3632489a014a9f6f1e758",
"sha256:d831b047bd69becaf64019a47179eb22118a50dd008340655266a906c69c6417",
"sha256:de2760583ed28749ff885789c1cbc6c9c06d6de92fc825740ab99deb2f25ea4d",
"sha256:eabc4cf1bc19689af8022ba52fd668564a8d96e0d08f3b4732d26a64255216a4",
"sha256:fcff6086c86fb1628d94ea455c7b9de898afc50378042927a59df8065a79a549"
],
"index": "pypi",
"version": "==3.8.0"
"version": "==3.9.1"
},
"six": {
"hashes": [
"sha256:3350809f0555b11f552448330d0b52d5f24c91a322ea4a15ef22629740f3761c",
"sha256:d16a0141ec1a18405cd4ce8b4613101da75da0e9a7aec5bdd4fa804d0e0eba73"
],
"index": "pypi",
"version": "==1.12.0"
}
},
@ -149,6 +149,7 @@
"sha256:3350809f0555b11f552448330d0b52d5f24c91a322ea4a15ef22629740f3761c",
"sha256:d16a0141ec1a18405cd4ce8b4613101da75da0e9a7aec5bdd4fa804d0e0eba73"
],
"index": "pypi",
"version": "==1.12.0"
},
"typed-ast": {

View file

@ -21,6 +21,7 @@ import inspect
from typing import List, Any, Callable, Dict, Optional, TYPE_CHECKING
from google.protobuf import struct_pb2
import six
from . import known_types, settings
from .. import log
@ -43,6 +44,15 @@ _special_archive_sig = "0def7320c3a5731c473e5ecbe6d01bc7"
_special_secret_sig = "1b47061264138c4ac30d75fd1eb44270"
"""special_secret_sig is a randomly assigned hash used to identify secrets in maps. See pkg/resource/properties.go"""
_INT_OR_FLOAT = six.integer_types + (float,)
def isLegalProtobufValue(value: Any) -> bool:
"""
Returns True if the given value is a legal Protobuf value as per the source at
https://github.com/protocolbuffers/protobuf/blob/master/python/google/protobuf/internal/well_known_types.py#L714-L732
"""
return value is None or isinstance(value, (bool, six.string_types, _INT_OR_FLOAT, dict, list))
async def serialize_properties(inputs: 'Inputs',
property_deps: Dict[str, List['Resource']],
input_transformer: Optional[Callable[[str], str]] = None) -> struct_pb2.Struct:
@ -166,6 +176,10 @@ async def serialize_property(value: 'Input[Any]',
return obj
# Ensure that we have a value that Protobuf understands.
if not isLegalProtobufValue(value):
raise ValueError(f"unexpected input of type {type(value).__name__}")
return value
# pylint: disable=too-many-return-statements

View file

@ -0,0 +1,13 @@
# Copyright 2016-2018, Pulumi Corporation.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

View file

@ -0,0 +1,24 @@
# Copyright 2016-2018, Pulumi Corporation.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from pulumi import CustomResource
class MyClass:
def __init__(self):
self.prop = "oh no!"
class MyResource(CustomResource):
def __init__(self, name):
CustomResource.__init__(self, "test:index:MyResource", name, {"class": MyClass()})
MyResource("testResource1")

View file

@ -0,0 +1,27 @@
# Copyright 2016-2018, Pulumi Corporation.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from os import path
from ..util import LanghostTest
class UnhandledExceptionTest(LanghostTest):
def test_unhandled_exception(self):
self.run_test(
program=path.join(self.base_path(), "resource_op_bad_inputs"),
expected_error="Program exited with non-zero exit code: 1")
def register_resource(self, _ctx, _dry_run, _ty, _name, _resource,
_dependencies, _parent, _custom, _protect, _provider, _property_deps, _delete_before_replace,
_ignore_changes, _version):
raise Exception("oh no")

View file

@ -235,6 +235,21 @@ class NextSerializationTests(unittest.TestCase):
self.assertEqual(rpc._special_archive_sig, prop[rpc._special_sig_key])
self.assertEqual("foo.tar.gz", prop["path"])
@async_test
async def test_bad_inputs(self):
class MyClass:
def __init__(self):
self.prop = "oh no!"
error = None
try:
prop = await rpc.serialize_property(MyClass(), [])
except ValueError as err:
error = err
self.assertIsNotNone(error)
self.assertEqual("unexpected input of type MyClass", str(error))
class DeserializationTests(unittest.TestCase):
def test_unsupported_sig(self):
struct = struct_pb2.Struct()