diff options
author | Ali Mir <ali.mir@mongodb.com> | 2021-03-30 14:52:45 +0000 |
---|---|---|
committer | Evergreen Agent <no-reply@evergreen.mongodb.com> | 2021-04-06 21:08:15 +0000 |
commit | 6aaad044a819a50a690b932afeda9aa278ba0f2e (patch) | |
tree | cca2a96f43f41e396e8a30c715c813cb97b007a0 /buildscripts/idl | |
parent | 3b46d7c55ad4c0c9eb9135a0ac04c0990fae2f75 (diff) | |
download | mongo-6aaad044a819a50a690b932afeda9aa278ba0f2e.tar.gz |
SERVER-53211 Check for incompatible changes in generic_argument.idl in IDL compatibility checker script
Diffstat (limited to 'buildscripts/idl')
8 files changed, 361 insertions, 2 deletions
diff --git a/buildscripts/idl/idl/syntax.py b/buildscripts/idl/idl/syntax.py index 13ebcb272b1..dbc02937631 100644 --- a/buildscripts/idl/idl/syntax.py +++ b/buildscripts/idl/idl/syntax.py @@ -208,6 +208,22 @@ class SymbolTable(object): return struct return None + def get_generic_argument_list(self, name): + # type: (str) -> GenericArgumentList + """Get a generic argument list from the SymbolTable based on the list name.""" + for gen_arg_list in self.generic_argument_lists: + if gen_arg_list.name == name: + return gen_arg_list + return None + + def get_generic_reply_field_list(self, name): + # type: (str) -> GenericReplyFieldList + """Get a generic reply field list from the SymbolTable based on the list name.""" + for gen_reply_field_list in self.generic_reply_field_lists: + if gen_reply_field_list.name == name: + return gen_reply_field_list + return None + def resolve_type_from_name(self, ctxt, location, field_name, field_type_name): # type: (errors.ParserContext, common.SourceLocation, str, str) -> Optional[Union[Enum, Struct, Type]] """Find the type or struct a field refers to or log an error.""" diff --git a/buildscripts/idl/idl_check_compatibility.py b/buildscripts/idl/idl_check_compatibility.py index 4b9d1c5138d..120a42d5a4f 100644 --- a/buildscripts/idl/idl_check_compatibility.py +++ b/buildscripts/idl/idl_check_compatibility.py @@ -24,6 +24,8 @@ # delete this exception statement from your version. If you delete this # exception statement from all source files in the program, then also delete # it in the license file. +# +# pylint: disable=too-many-lines """Checks compatibility of old and new IDL files. In order to support user-selectable API versions for the server, server commands are now @@ -35,14 +37,13 @@ This script accepts two directories as arguments, the "old" and the "new" IDL di Before running this script, run checkout_idl_files_from_past_releases.py to find and create directories containing the old IDL files from previous releases. """ -# pylint: disable=too-many-lines import argparse import os import sys from dataclasses import dataclass from enum import Enum -from typing import Dict, List, Optional, Tuple, Union +from typing import Dict, List, Set, Optional, Tuple, Union from idl import parser, syntax, errors, common from idl.compiler import CompilerImportResolver @@ -995,6 +996,52 @@ def check_compatibility(old_idl_dir: str, new_idl_dir: str, return ctxt.errors +def get_generic_arguments(gen_args_file_path: str) -> Tuple[Set[str], Set[str]]: + """Get arguments and reply fields from generic_argument.idl and check validity.""" + arguments: Set[str] = set() + reply_fields: Set[str] = set() + + with open(gen_args_file_path) as gen_args_file: + parsed_idl_file = parser.parse(gen_args_file, gen_args_file_path, + CompilerImportResolver([])) + if parsed_idl_file.errors: + parsed_idl_file.errors.dump_errors() + raise ValueError(f"Cannot parse {gen_args_file_path}") + for argument in parsed_idl_file.spec.symbols.get_generic_argument_list( + "generic_args_api_v1").fields: + arguments.add(argument.name) + + for reply_field in parsed_idl_file.spec.symbols.get_generic_reply_field_list( + "generic_reply_fields_api_v1").fields: + reply_fields.add(reply_field.name) + + return arguments, reply_fields + + +def check_generic_arguments_compatibility(old_gen_args_file_path: str, new_gen_args_file_path: str + ) -> IDLCompatibilityErrorCollection: + """Check IDL compatibility between old and new generic_argument.idl files.""" + # IDLCompatibilityContext takes in both 'old_idl_dir' and 'new_idl_dir', + # but for generic_argument.idl, the parent directories aren't helpful for logging purposes. + # Instead, we pass in "old generic_argument.idl" and "new generic_argument.idl" + # to make error messages clearer. + ctxt = IDLCompatibilityContext("old generic_argument.idl", "new generic_argument.idl", + IDLCompatibilityErrorCollection()) + + old_arguments, old_reply_fields = get_generic_arguments(old_gen_args_file_path) + new_arguments, new_reply_fields = get_generic_arguments(new_gen_args_file_path) + + for old_argument in old_arguments: + if old_argument not in new_arguments: + ctxt.add_generic_argument_removed(old_argument, new_gen_args_file_path) + + for old_reply_field in old_reply_fields: + if old_reply_field not in new_reply_fields: + ctxt.add_generic_argument_removed_reply_field(old_reply_field, new_gen_args_file_path) + + return ctxt.errors + + def main(): """Run the script.""" arg_parser = argparse.ArgumentParser(description=__doc__) @@ -1017,6 +1064,13 @@ def main(): if error_reply_coll.has_errors(): sys.exit(1) + old_generic_args_path = os.path.join(args.old_idl_dir, "mongo/idl/generic_argument.idl") + new_generic_args_path = os.path.join(args.new_idl_dir, "mongo/idl/generic_argument.idl") + error_gen_args_coll = check_generic_arguments_compatibility(old_generic_args_path, + new_generic_args_path) + if error_gen_args_coll.has_errors(): + sys.exit(1) + if __name__ == "__main__": main() diff --git a/buildscripts/idl/idl_compatibility_errors.py b/buildscripts/idl/idl_compatibility_errors.py index 935e5e1703f..dc5c30a2105 100644 --- a/buildscripts/idl/idl_compatibility_errors.py +++ b/buildscripts/idl/idl_compatibility_errors.py @@ -25,6 +25,7 @@ # exception statement from all source files in the program, then also delete # it in the license file. # +# pylint: disable=too-many-lines """ Common error handling code for IDL compatibility checker. @@ -109,6 +110,8 @@ ERROR_ID_NEW_ADDITIONAL_COMPLEX_ACCESS_CHECK = "ID0065" ERROR_ID_REMOVED_ACCESS_CHECK_FIELD = "ID0066" ERROR_ID_ADDED_ACCESS_CHECK_FIELD = "ID0067" ERROR_ID_COMMAND_STRICT_TRUE_ERROR = "ID0068" +ERROR_ID_GENERIC_ARGUMENT_REMOVED = "ID0069" +ERROR_ID_GENERIC_ARGUMENT_REMOVED_REPLY_FIELD = "ID0070" # TODO (SERVER-55203): Remove SKIPPED_COMMANDS logic. # Any breaking changes added to API V1 before releasing 5.0 should be added to SKIPPED_COMMANDS to @@ -971,6 +974,20 @@ class IDLCompatibilityContext(object): "'%s' has added the access_check field in the new command when it did not exist in the " "old command and the api_version is '1'") % (command_name), file) + def add_generic_argument_removed(self, field_name: str, file: str) -> None: + """Add an error about a generic argument that was removed.""" + self._add_error( + ERROR_ID_GENERIC_ARGUMENT_REMOVED, field_name, + ("The generic argument '%s' was removed from the new generic_argument.idl file") % + (field_name), file) + + def add_generic_argument_removed_reply_field(self, field_name: str, file: str) -> None: + """Add an error about a generic reply field that was removed.""" + self._add_error( + ERROR_ID_GENERIC_ARGUMENT_REMOVED_REPLY_FIELD, field_name, + ("The generic reply field '%s' was removed from the new generic_argument.idl file") % + (field_name), file) + def _assert_unique_error_messages() -> None: """Assert that error codes are unique.""" diff --git a/buildscripts/idl/tests/compatibility_test_fail/new_generic_argument/generic_argument.idl b/buildscripts/idl/tests/compatibility_test_fail/new_generic_argument/generic_argument.idl new file mode 100644 index 00000000000..157ba6cb002 --- /dev/null +++ b/buildscripts/idl/tests/compatibility_test_fail/new_generic_argument/generic_argument.idl @@ -0,0 +1,55 @@ +# Copyright (C) 2020-present MongoDB, Inc. +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the Server Side Public License, version 1, +# as published by MongoDB, Inc. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# Server Side Public License for more details. +# +# You should have received a copy of the Server Side Public License +# along with this program. If not, see +# <http://www.mongodb.com/licensing/server-side-public-license>. +# +# As a special exception, the copyright holders give permission to link the +# code of portions of this program with the OpenSSL library under certain +# conditions as described in each individual source file and distribute +# linked combinations including the program with the OpenSSL library. You +# must comply with the Server Side Public License in all respects for +# all of the code used other than as permitted herein. If you modify file(s) +# with this exception, you may extend this exception to your version of the +# file(s), but you are not obligated to do so. If you do not wish to do so, +# delete this exception statement from your version. If you delete this +# exception statement from all source files in the program, then also delete +# it in the license file. +# +global: + cpp_namespace: "mongo" + +generic_argument_lists: + generic_args_api_v1: + description: "Validation fails due to a removed argument" + fields: + genericArgument: + forward_to_shards: false + + generic_args_unstable_v1: + description: "IDL checker provides no guarantees about unstable generic arguments" + fields: + unstableGenericArgument: + forward_to_shards: false + +generic_reply_field_lists: + generic_reply_fields_api_v1: + description: "Validation fails due to a removed reply field" + fields: + genericReplyField: + forward_from_shards: false + + generic_reply_fields_unstable_v1: + description: "IDL checker provides no guarantees about unstable generic reply fields" + fields: + unstableGenericReplyField: + forward_from_shards: false
\ No newline at end of file diff --git a/buildscripts/idl/tests/compatibility_test_fail/old_generic_argument/generic_argument.idl b/buildscripts/idl/tests/compatibility_test_fail/old_generic_argument/generic_argument.idl new file mode 100644 index 00000000000..8220162ccfa --- /dev/null +++ b/buildscripts/idl/tests/compatibility_test_fail/old_generic_argument/generic_argument.idl @@ -0,0 +1,59 @@ +# Copyright (C) 2020-present MongoDB, Inc. +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the Server Side Public License, version 1, +# as published by MongoDB, Inc. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# Server Side Public License for more details. +# +# You should have received a copy of the Server Side Public License +# along with this program. If not, see +# <http://www.mongodb.com/licensing/server-side-public-license>. +# +# As a special exception, the copyright holders give permission to link the +# code of portions of this program with the OpenSSL library under certain +# conditions as described in each individual source file and distribute +# linked combinations including the program with the OpenSSL library. You +# must comply with the Server Side Public License in all respects for +# all of the code used other than as permitted herein. If you modify file(s) +# with this exception, you may extend this exception to your version of the +# file(s), but you are not obligated to do so. If you do not wish to do so, +# delete this exception statement from your version. If you delete this +# exception statement from all source files in the program, then also delete +# it in the license file. +# +global: + cpp_namespace: "mongo" + +generic_argument_lists: + generic_args_api_v1: + description: "Validation fails due to a removed argument" + fields: + genericArgument: + forward_to_shards: false + removedGenericArgument: + forward_to_shards: false + + generic_args_unstable_v1: + description: "IDL checker provides no guarantees about unstable generic arguments" + fields: + unstableGenericArgument: + forward_to_shards: false + +generic_reply_field_lists: + generic_reply_fields_api_v1: + description: "Validation fails due to a removed reply field" + fields: + genericReplyField: + forward_from_shards: false + removedGenericReplyField: + forward_from_shards: false + + generic_reply_fields_unstable_v1: + description: "IDL checker provides no guarantees about unstable generic reply fields" + fields: + unstableGenericReplyField: + forward_from_shards: false
\ No newline at end of file diff --git a/buildscripts/idl/tests/compatibility_test_pass/new_generic_argument/generic_argument.idl b/buildscripts/idl/tests/compatibility_test_pass/new_generic_argument/generic_argument.idl new file mode 100644 index 00000000000..8aceb008004 --- /dev/null +++ b/buildscripts/idl/tests/compatibility_test_pass/new_generic_argument/generic_argument.idl @@ -0,0 +1,61 @@ +# Copyright (C) 2020-present MongoDB, Inc. +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the Server Side Public License, version 1, +# as published by MongoDB, Inc. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# Server Side Public License for more details. +# +# You should have received a copy of the Server Side Public License +# along with this program. If not, see +# <http://www.mongodb.com/licensing/server-side-public-license>. +# +# As a special exception, the copyright holders give permission to link the +# code of portions of this program with the OpenSSL library under certain +# conditions as described in each individual source file and distribute +# linked combinations including the program with the OpenSSL library. You +# must comply with the Server Side Public License in all respects for +# all of the code used other than as permitted herein. If you modify file(s) +# with this exception, you may extend this exception to your version of the +# file(s), but you are not obligated to do so. If you do not wish to do so, +# delete this exception statement from your version. If you delete this +# exception statement from all source files in the program, then also delete +# it in the license file. +# +global: + cpp_namespace: "mongo" + +generic_argument_lists: + generic_args_api_v1: + description: "Add a new generic argument and should still pass" + fields: + genericArgument: + forward_to_shards: false + addedGenericArgument: + forward_to_shards: false + + generic_args_unstable_v1: + description: "IDL checker provides no guarantees about unstable generic arguments. + Removing an unstable argument should still pass" + fields: + unstableGenericArgument: + forward_to_shards: false + +generic_reply_field_lists: + generic_reply_fields_api_v1: + description: "Add a new generic reply field and should still pass" + fields: + genericReplyField: + forward_from_shards: false + addedGenericReplyField: + forward_from_shards: false + + generic_reply_fields_unstable_v1: + description: "IDL checker provides no guarantees about unstable generic reply fields. + Removing an unstable reply field should still pass" + fields: + unstableGenericReplyField: + forward_from_shards: false
\ No newline at end of file diff --git a/buildscripts/idl/tests/compatibility_test_pass/old_generic_argument/generic_argument.idl b/buildscripts/idl/tests/compatibility_test_pass/old_generic_argument/generic_argument.idl new file mode 100644 index 00000000000..6f4b37df168 --- /dev/null +++ b/buildscripts/idl/tests/compatibility_test_pass/old_generic_argument/generic_argument.idl @@ -0,0 +1,62 @@ +# Copyright (C) 2020-present MongoDB, Inc. +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the Server Side Public License, version 1, +# as published by MongoDB, Inc. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# Server Side Public License for more details. +# +# You should have received a copy of the Server Side Public License +# along with this program. If not, see +# <http://www.mongodb.com/licensing/server-side-public-license>. +# +# As a special exception, the copyright holders give permission to link the +# code of portions of this program with the OpenSSL library under certain +# conditions as described in each individual source file and distribute +# linked combinations including the program with the OpenSSL library. You +# must comply with the Server Side Public License in all respects for +# all of the code used other than as permitted herein. If you modify file(s) +# with this exception, you may extend this exception to your version of the +# file(s), but you are not obligated to do so. If you do not wish to do so, +# delete this exception statement from your version. If you delete this +# exception statement from all source files in the program, then also delete +# it in the license file. +# +global: + cpp_namespace: "mongo" + +generic_argument_lists: + generic_args_api_v1: + description: "Add a new generic argument and should still pass" + fields: + genericArgument: + forward_to_shards: false + + generic_args_unstable_v1: + description: "IDL checker provides no guarantees about unstable generic arguments. + Removing an unstable argument should still pass" + fields: + unstableGenericArgument: + forward_to_shards: false + removedUnstableGenericArgument: + forward_to_shards: false + + +generic_reply_field_lists: + generic_reply_fields_api_v1: + description: "Add a new generic reply field and should still pass" + fields: + genericReplyField: + forward_from_shards: false + + generic_reply_fields_unstable_v1: + description: "IDL checker provides no guarantees about unstable generic reply fields. + Removing an unstable reply field should still pass" + fields: + unstableGenericReplyField: + forward_from_shards: false + removedUnstableGenericReplyField: + forward_from_shards: false
\ No newline at end of file diff --git a/buildscripts/idl/tests/test_compatibility.py b/buildscripts/idl/tests/test_compatibility.py index 45e429ba002..89a4e6130c6 100644 --- a/buildscripts/idl/tests/test_compatibility.py +++ b/buildscripts/idl/tests/test_compatibility.py @@ -1207,6 +1207,41 @@ class TestIDLCompatibilityChecker(unittest.TestCase): idl_compatibility_errors.ERROR_ID_TYPE_NOT_ARRAY) self.assertRegex(str(missing_array_command_parameter_new_error), "array<ArrayTypeStruct>") + def test_generic_argument_compatibility_pass(self): + """Tests that compatible old and new generic_argument.idl files should pass.""" + dir_path = path.dirname(path.realpath(__file__)) + self.assertFalse( + idl_check_compatibility.check_generic_arguments_compatibility( + path.join(dir_path, + "compatibility_test_pass/old_generic_argument/generic_argument.idl"), + path.join(dir_path, + "compatibility_test_pass/new_generic_argument/generic_argument.idl")). + has_errors()) + + def test_generic_argument_compatibility_fail(self): + """Tests that incompatible old and new generic_argument.idl files should fail.""" + dir_path = path.dirname(path.realpath(__file__)) + error_collection = idl_check_compatibility.check_generic_arguments_compatibility( + path.join(dir_path, + "compatibility_test_fail/old_generic_argument/generic_argument.idl"), + path.join(dir_path, + "compatibility_test_fail/new_generic_argument/generic_argument.idl")) + + self.assertTrue(error_collection.has_errors()) + self.assertTrue(error_collection.count() == 2) + + removed_generic_argument_error = error_collection.get_error_by_command_name( + "removedGenericArgument") + self.assertTrue(removed_generic_argument_error.error_id == + idl_compatibility_errors.ERROR_ID_GENERIC_ARGUMENT_REMOVED) + self.assertRegex(str(removed_generic_argument_error), "removedGenericArgument") + + removed_generic_reply_field_error = error_collection.get_error_by_command_name( + "removedGenericReplyField") + self.assertTrue(removed_generic_reply_field_error.error_id == + idl_compatibility_errors.ERROR_ID_GENERIC_ARGUMENT_REMOVED_REPLY_FIELD) + self.assertRegex(str(removed_generic_reply_field_error), "removedGenericReplyField") + def test_error_reply(self): """Tests the compatibility checker with the ErrorReply struct.""" dir_path = path.dirname(path.realpath(__file__)) |