-
Notifications
You must be signed in to change notification settings - Fork 5.5k
config: allow unknown fields flag (take 2) #4096
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
Changes from 5 commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -48,7 +48,17 @@ ProtoValidationException::ProtoValidationException(const std::string& validation | |
| ENVOY_LOG_MISC(debug, "Proto validation error; throwing {}", what()); | ||
| } | ||
|
|
||
| bool MessageUtil::allow_unknown_fields = false; | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Just for clarity, can we make this an enum with 2 states? Then the function can take the enum value like I mentioned before (and CLI can convert to enum).
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Done, let me know if I need to change the names of anything to conform with the style. |
||
|
|
||
| void MessageUtil::loadFromJson(const std::string& json, Protobuf::Message& message) { | ||
| Protobuf::util::JsonParseOptions options; | ||
| const auto status = Protobuf::util::JsonStringToMessage(json, &message, options); | ||
| if (!status.ok()) { | ||
| throw EnvoyException("Unable to parse JSON as proto (" + status.ToString() + "): " + json); | ||
| } | ||
| } | ||
|
|
||
| void MessageUtil::loadFromJsonLenient(const std::string& json, Protobuf::Message& message) { | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Nit: could factor out some of the body of these two to a shared util.
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The other way of doing this is to have one function, and make the parameter for leniency be an enum instead of bool. That would be my vote as you would share code and have callsite clarity.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Thanks. I combined the logic into one function which is only called from two places (json convert and a test). The old |
||
| Protobuf::util::JsonParseOptions options; | ||
| options.ignore_unknown_fields = true; | ||
| const auto status = Protobuf::util::JsonStringToMessage(json, &message, options); | ||
|
|
@@ -68,6 +78,7 @@ void MessageUtil::loadFromFile(const std::string& path, Protobuf::Message& messa | |
| if (StringUtil::endsWith(path, ".pb")) { | ||
| // Attempt to parse the binary format. | ||
| if (message.ParseFromString(contents)) { | ||
| MessageUtil::checkUnknownFields(message); | ||
| return; | ||
| } | ||
| throw EnvoyException("Unable to parse file \"" + path + "\" as a binary protobuf (type " + | ||
|
|
@@ -121,6 +132,10 @@ void MessageUtil::jsonConvert(const Protobuf::Message& source, Protobuf::Message | |
| throw EnvoyException(fmt::format("Unable to convert protobuf message to JSON string: {} {}", | ||
| status.ToString(), source.DebugString())); | ||
| } | ||
| if (MessageUtil::allow_unknown_fields) { | ||
| MessageUtil::loadFromJsonLenient(json, dest); | ||
| return; | ||
| } | ||
| MessageUtil::loadFromJson(json, dest); | ||
| } | ||
|
|
||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -158,7 +158,17 @@ class MessageUtil { | |
| return HashUtil::xxHash64(text); | ||
| } | ||
|
|
||
| static bool allow_unknown_fields; | ||
|
|
||
| static void checkUnknownFields(const Protobuf::Message& message) { | ||
| if (!MessageUtil::allow_unknown_fields && | ||
| !message.GetReflection()->GetUnknownFields(message).empty()) { | ||
| throw EnvoyException("Protobuf Any (type " + message.GetTypeName() + ") has unknown fields"); | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is also used when loading from file, so not always Any.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Done, thanks |
||
| } | ||
| } | ||
|
|
||
| static void loadFromJson(const std::string& json, Protobuf::Message& message); | ||
| static void loadFromJsonLenient(const std::string& json, Protobuf::Message& message); | ||
| static void loadFromYaml(const std::string& yaml, Protobuf::Message& message); | ||
| static void loadFromFile(const std::string& path, Protobuf::Message& message); | ||
|
|
||
|
|
@@ -214,6 +224,7 @@ class MessageUtil { | |
| if (!message.UnpackTo(&typed_message)) { | ||
| throw EnvoyException("Unable to unpack " + message.DebugString()); | ||
| } | ||
| checkUnknownFields(typed_message); | ||
| return typed_message; | ||
| }; | ||
|
|
||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -54,6 +54,17 @@ TEST(UtilityTest, LoadBinaryProtoFromFile) { | |
| EXPECT_TRUE(TestUtility::protoEqual(bootstrap, proto_from_file)); | ||
| } | ||
|
|
||
| TEST(UtilityTest, LoadBinaryProtoUnknownFieldFromFile) { | ||
| ProtobufWkt::Duration source_duration; | ||
| source_duration.set_seconds(42); | ||
| const std::string filename = | ||
| TestEnvironment::writeStringToFileForTest("proto.pb", source_duration.SerializeAsString()); | ||
| envoy::config::bootstrap::v2::Bootstrap proto_from_file; | ||
| EXPECT_THROW_WITH_MESSAGE( | ||
| MessageUtil::loadFromFile(filename, proto_from_file), EnvoyException, | ||
| "Protobuf Any (type envoy.config.bootstrap.v2.Bootstrap) has unknown fields"); | ||
| } | ||
|
|
||
| TEST(UtilityTest, LoadTextProtoFromFile) { | ||
| envoy::config::bootstrap::v2::Bootstrap bootstrap; | ||
| bootstrap.mutable_cluster_manager() | ||
|
|
@@ -212,6 +223,25 @@ TEST(UtilityTest, HashedValueStdHash) { | |
| EXPECT_NE(set.find(hv3), set.end()); | ||
| } | ||
|
|
||
| TEST(UtilityTest, AnyConvertWrongType) { | ||
| ProtobufWkt::Duration source_duration; | ||
| source_duration.set_seconds(42); | ||
| ProtobufWkt::Any source_any; | ||
| source_any.PackFrom(source_duration); | ||
| EXPECT_THROW_WITH_REGEX(MessageUtil::anyConvert<ProtobufWkt::Timestamp>(source_any), | ||
| EnvoyException, "Unable to unpack .*"); | ||
| } | ||
|
|
||
| TEST(UtilityTest, AnyConvertWrongFields) { | ||
| const ProtobufWkt::Struct obj = MessageUtil::keyValueStruct("test_key", "test_value"); | ||
| ProtobufWkt::Any source_any; | ||
| source_any.PackFrom(obj); | ||
| source_any.set_type_url("type.google.com/google.protobuf.Timestamp"); | ||
| EXPECT_THROW_WITH_MESSAGE(MessageUtil::anyConvert<ProtobufWkt::Timestamp>(source_any), | ||
| EnvoyException, | ||
| "Protobuf Any (type google.protobuf.Timestamp) has unknown fields"); | ||
| } | ||
|
|
||
| TEST(UtilityTest, JsonConvertSuccess) { | ||
| ProtobufWkt::Duration source_duration; | ||
| source_duration.set_seconds(42); | ||
|
|
@@ -221,9 +251,11 @@ TEST(UtilityTest, JsonConvertSuccess) { | |
| } | ||
|
|
||
| TEST(UtilityTest, JsonConvertUnknownFieldSuccess) { | ||
| MessageUtil::allow_unknown_fields = true; | ||
| const ProtobufWkt::Struct obj = MessageUtil::keyValueStruct("test_key", "test_value"); | ||
| envoy::config::bootstrap::v2::Bootstrap bootstrap; | ||
| EXPECT_NO_THROW(MessageUtil::jsonConvert(obj, bootstrap)); | ||
| MessageUtil::allow_unknown_fields = false; | ||
| } | ||
|
|
||
| TEST(UtilityTest, JsonConvertFail) { | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Optionally also add a test to ensure server options have the desired effect. |
||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -46,12 +46,7 @@ class ThriftConnManagerIntegrationTest | |
| route_config: | ||
| name: "routes" | ||
| routes: | ||
| - match: | ||
| method_name: "execute" | ||
| route: | ||
| cluster: "cluster_0" | ||
| - match: | ||
| method_name: "poke" | ||
| - match: {} | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Oops. Could you revert the original and switch method_name to method? That should eliminate the unknown fields. (I'll circle back and add a second cluster to this test and use it for the one of the routes.)
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Actually, that'll just make half the tests fail. You can leave this and I'll fix it myself.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes, I am getting errors if I put it back... |
||
| route: | ||
| cluster: "cluster_0" | ||
| )EOF"; | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -78,7 +78,6 @@ def _proto_doc_aspect_impl(target, ctx): | |
| return [OutputGroupInfo(rst = transitive_outputs)] | ||
|
|
||
| proto_doc_aspect = aspect( | ||
| implementation = _proto_doc_aspect_impl, | ||
| attr_aspects = ["deps"], | ||
| attrs = { | ||
| "_protoc": attr.label( | ||
|
|
@@ -92,4 +91,5 @@ proto_doc_aspect = aspect( | |
| cfg = "host", | ||
| ), | ||
| }, | ||
| implementation = _proto_doc_aspect_impl, | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I assume this is just some Buildifier artifact..
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes, not sure why |
||
| ) | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
note: we were ignoring mismatched type errors during unpacking.