From ca3779d8029bb5d4c67db7f61e8cd6a60e95eb89 Mon Sep 17 00:00:00 2001 From: Jiawei Yang Date: Tue, 18 Jul 2023 18:18:19 +0000 Subject: [PATCH] SERVER-79060: add config fuzzer to jepsen test --- .../generate_fuzz_config/__init__.py | 134 ++++++++++++++++++ buildscripts/resmokelib/parser.py | 2 + etc/evergreen_yml_components/definitions.yml | 117 +++++++++++++++ .../do_jepsen_setup/create_fuzz_config.sh | 12 ++ evergreen/do_jepsen_setup/mongod.conf | 26 ++++ .../jepsen_docker/setup_config_fuzzer.sh | 14 ++ evergreen/jepsen_test_run.sh | 2 +- 7 files changed, 306 insertions(+), 1 deletion(-) create mode 100644 buildscripts/resmokelib/generate_fuzz_config/__init__.py create mode 100644 evergreen/do_jepsen_setup/create_fuzz_config.sh create mode 100644 evergreen/do_jepsen_setup/mongod.conf create mode 100644 evergreen/jepsen_docker/setup_config_fuzzer.sh diff --git a/buildscripts/resmokelib/generate_fuzz_config/__init__.py b/buildscripts/resmokelib/generate_fuzz_config/__init__.py new file mode 100644 index 00000000000..b3164210e19 --- /dev/null +++ b/buildscripts/resmokelib/generate_fuzz_config/__init__.py @@ -0,0 +1,134 @@ +"""Generate mongod.conf and mongos.conf using config fuzzer.""" +import json +import os.path +import shutil + +from buildscripts.resmokelib.plugin import PluginInterface, Subcommand +from buildscripts.resmokelib import mongo_fuzzer_configs +from buildscripts.resmokelib import utils + +_HELP = """ +Generate a mongod.conf and mongos.conf using config fuzzer. +""" +_COMMAND = "generate-fuzz-config" + + +class GenerateFuzzConfig(Subcommand): + """Interact with generating fuzz config.""" + + def __init__(self, template_path, output_path, mongod_mode, mongos_mode, seed): + """Constructor.""" + self._template_path = template_path + self._output_path = output_path + self._mongod_mode = mongod_mode + self._mongos_mode = mongos_mode + self._seed = seed + + def _generate_mongod_config(self) -> None: + filename = "mongod.conf" + output_file = os.path.join(self._output_path, filename) + user_param = utils.dump_yaml({}) + set_parameters, wt_engine_config, wt_coll_config, \ + wt_index_config = mongo_fuzzer_configs.fuzz_mongod_set_parameters(self._mongod_mode, + self._seed, + user_param) + set_parameters = utils.load_yaml(set_parameters) + set_parameters["mirrorReads"] = json.dumps(set_parameters["mirrorReads"]) + # This is moved from Jepsen mongod.conf to have only one setParameter key value pair. + set_parameters["enableTestCommands"] = True + conf = { + "setParameter": set_parameters, "storage": { + "wiredTiger": { + "engineConfig": {"configString": wt_engine_config}, + "collectionConfig": {"configString": wt_coll_config}, + "indexConfig": {"configString": wt_index_config} + } + } + } + if self._template_path is not None: + try: + shutil.copy(os.path.join(self._template_path, filename), output_file) + except shutil.SameFileError: + pass + + fuzz_config = utils.dump_yaml(conf) + with open(output_file, 'a') as file: + file.write(fuzz_config) + file.write("\n") + + def _generate_mongos_config(self) -> None: + filename = "mongos.conf" + output_file = os.path.join(self._output_path, filename) + user_param = utils.dump_yaml({}) + set_parameters = mongo_fuzzer_configs.fuzz_mongos_set_parameters( + self._mongos_mode, self._seed, user_param) + set_parameters = utils.load_yaml(set_parameters) + conf = {"setParameter": set_parameters} + if self._template_path is not None: + try: + shutil.copy(os.path.join(self._template_path, filename), output_file) + except shutil.SameFileError: + pass + except FileNotFoundError: + print("There is no mongos template in the path, skip generating mongos.conf.") + return + + fuzz_config = utils.dump_yaml(conf) + with open(output_file, 'a') as file: + file.write(fuzz_config) + file.write("\n") + + def execute(self) -> None: + """ + Generate mongod.conf and mongos.conf. + + :return: None + """ + self._generate_mongod_config() + self._generate_mongos_config() + + +class GenerateFuzzConfigPlugin(PluginInterface): + """Interact with generating fuzz config.""" + + def add_subcommand(self, subparsers): + """ + Add 'generate-fuzz-config' subcommand. + + :param subparsers: argparse parser to add to + :return: None + """ + parser = subparsers.add_parser(_COMMAND, help=_HELP) + parser.add_argument("--template", '-t', type=str, required=False, + help="Path to templates to append config-fuzzer-generated parameters.") + parser.add_argument("--output", '-o', type=str, required=True, + help="Path to the output file.") + parser.add_argument( + "--fuzzMongodConfigs", dest="fuzz_mongod_configs", + help="Randomly chooses mongod parameters that were not specified. Use 'stress' to fuzz " + "all configs including stressful storage configurations that may significantly " + "slow down the server. Use 'normal' to only fuzz non-stressful configurations. ", + metavar="MODE", choices=('normal', 'stress')) + parser.add_argument("--fuzzMongosConfigs", dest="fuzz_mongos_configs", + help="Randomly chooses mongos parameters that were not specified", + metavar="MODE", choices=('normal', )) + parser.add_argument("--configFuzzSeed", dest="config_fuzz_seed", metavar="PATH", + help="Sets the seed used by mongod and mongos config fuzzers") + + def parse(self, subcommand, parser, parsed_args, **kwargs): + """ + Return the GenerateFuzzConfig subcommand for execution. + + :param subcommand: equivalent to parsed_args.command + :param parser: parser used + :param parsed_args: output of parsing + :param kwargs: additional args + :return: None or a Subcommand + """ + + if subcommand != _COMMAND: + return None + + return GenerateFuzzConfig(parsed_args.template, parsed_args.output, + parsed_args.fuzz_mongod_configs, parsed_args.fuzz_mongos_configs, + parsed_args.config_fuzz_seed) diff --git a/buildscripts/resmokelib/parser.py b/buildscripts/resmokelib/parser.py index 80e647a9906..b6057bb5d6e 100644 --- a/buildscripts/resmokelib/parser.py +++ b/buildscripts/resmokelib/parser.py @@ -7,6 +7,7 @@ from buildscripts.resmokelib import configure_resmoke from buildscripts.resmokelib.discovery import DiscoveryPlugin from buildscripts.resmokelib.generate_fcv_constants import \ GenerateFCVConstantsPlugin +from buildscripts.resmokelib.generate_fuzz_config import GenerateFuzzConfigPlugin from buildscripts.resmokelib.hang_analyzer import HangAnalyzerPlugin from buildscripts.resmokelib.multiversion import MultiversionPlugin from buildscripts.resmokelib.powercycle import PowercyclePlugin @@ -21,6 +22,7 @@ _PLUGINS = [ GenerateFCVConstantsPlugin(), DiscoveryPlugin(), MultiversionPlugin(), + GenerateFuzzConfigPlugin() ] diff --git a/etc/evergreen_yml_components/definitions.yml b/etc/evergreen_yml_components/definitions.yml index efbbdc59c61..034034af6a0 100644 --- a/etc/evergreen_yml_components/definitions.yml +++ b/etc/evergreen_yml_components/definitions.yml @@ -148,6 +148,7 @@ variables: # 3.4.0-rc3 and 3.4.0-rc4 when running Jepsen with at least --time-limit=600. jepsen_time_limit: --time-limit 1200 jepsen_write_concern: "" + mongod_conf: --mongod-conf mongod_verbose.conf # Template for running Jepsen tests - &run_jepsen_template @@ -1420,6 +1421,23 @@ functions: binary: bash args: - "./src/evergreen/jepsen_docker/setup.sh" + "setup jepsen config fuzzer": + - *f_expansions_write + - command: subprocess.exec + type: system + params: + binary: bash + args: + - "./src/evergreen/do_jepsen_setup/create_fuzz_config.sh" + + "setup jepsen docker config fuzzer": + - *f_expansions_write + - command: subprocess.exec + type: system + params: + binary: bash + args: + - "./src/evergreen/jepsen_docker/setup_config_fuzzer.sh" "run jepsen docker test": - *f_expansions_write - command: subprocess.exec @@ -3793,6 +3811,105 @@ tasks: - func: "do jepsen docker setup" - func: "run jepsen docker test" +- <<: *run_jepsen_template + name: jepsen_config_fuzzer_register_findAndModify + tags: ["jepsen"] + commands: + - func: "do setup" + - func: "do jepsen setup" + - func: "setup jepsen config fuzzer" + - func: "run jepsen test" + vars: + <<: *jepsen_config_vars + mongod_conf: --mongod-conf mongod.conf + jepsen_read_with_find_and_modify: --read-with-find-and-modify + jepsen_storage_engine: --storage-engine wiredTiger + jepsen_test_name: register + +- <<: *run_jepsen_template + name: jepsen_config_fuzzer_register_linearizableRead + tags: ["jepsen"] + commands: + - func: "do setup" + - func: "do jepsen setup" + - func: "setup jepsen config fuzzer" + - func: "run jepsen test" + vars: + <<: *jepsen_config_vars + mongod_conf: --mongod-conf mongod.conf + jepsen_read_concern: --read-concern linearizable + jepsen_storage_engine: --storage-engine wiredTiger + jepsen_test_name: register + +- <<: *run_jepsen_template + name: jepsen_config_fuzzer_set_linearizableRead + tags: ["jepsen"] + commands: + - func: "do setup" + - func: "do jepsen setup" + - func: "setup jepsen config fuzzer" + - func: "run jepsen test" + vars: + <<: *jepsen_config_vars + mongod_conf: --mongod-conf mongod.conf + jepsen_read_concern: --read-concern linearizable + jepsen_storage_engine: --storage-engine wiredTiger + jepsen_test_name: set + +- <<: *run_jepsen_template + name: jepsen_config_fuzzer_read-concern-majority + tags: ["jepsen"] + commands: + - func: "do setup" + - func: "do jepsen setup" + - func: "setup jepsen config fuzzer" + - func: "run jepsen test" + vars: + <<: *jepsen_config_vars + mongod_conf: --mongod-conf mongod.conf + jepsen_storage_engine: --storage-engine wiredTiger + jepsen_test_name: read-concern-majority + +# Smoke test to ensure the Server still works with Jepsen +- <<: *run_jepsen_template + name: jepsen-config_fuzzer_smoke + tags: [] + commands: + - func: "do setup" + - func: "do jepsen setup" + - func: "setup jepsen config fuzzer" + - func: "run jepsen test" + vars: + <<: *jepsen_config_vars + mongod_conf: --mongod-conf mongod.conf + jepsen_storage_engine: --storage-engine wiredTiger + jepsen_test_name: read-concern-majority + jepsen_time_limit: --time-limit 120 + +- <<: *run_jepsen_template + name: jepsen_config_fuzzer_read-concern-majority_w1 + tags: ["jepsen"] + commands: + - func: "do setup" + - func: "do jepsen setup" + - func: "setup jepsen config fuzzer" + - func: "run jepsen test" + vars: + <<: *jepsen_config_vars + mongod_conf: --mongod-conf mongod.conf + jepsen_storage_engine: --storage-engine wiredTiger + jepsen_test_name: read-concern-majority + jepsen_write_concern: --write-concern w1 + +- <<: *run_jepsen_template + name: jepsen_config_fuzzer_list-append + tags: ["jepsen_docker"] + commands: + - func: "do setup" + - func: "do jepsen docker setup" + - func: "setup jepsen docker config fuzzer" + - func: "run jepsen docker test" + ## initial sync multiversion fuzzer ## - <<: *jstestfuzz_template name: initial_sync_multiversion_fuzzer_gen diff --git a/evergreen/do_jepsen_setup/create_fuzz_config.sh b/evergreen/do_jepsen_setup/create_fuzz_config.sh new file mode 100644 index 00000000000..9fcc414abb8 --- /dev/null +++ b/evergreen/do_jepsen_setup/create_fuzz_config.sh @@ -0,0 +1,12 @@ +DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" > /dev/null 2>&1 && pwd)" +. "$DIR/../prelude.sh" + +cd src + +set -o verbose +set -o errexit +activate_venv +$python buildscripts/resmoke.py generate-fuzz-config --template evergreen/do_jepsen_setup --output jepsen-mongodb/resources + +echo "Print config fuzzer generated mongod.conf" +cat jepsen-mongodb/resources/mongod.conf diff --git a/evergreen/do_jepsen_setup/mongod.conf b/evergreen/do_jepsen_setup/mongod.conf new file mode 100644 index 00000000000..46a2b2482c2 --- /dev/null +++ b/evergreen/do_jepsen_setup/mongod.conf @@ -0,0 +1,26 @@ +# This is the template mongod.conf copied from jepsen repo. The only difference should be the +# enableTestCommands: true parameter under setParameter is removed since it will conflict with +# the setParameter section generated by config fuzzer. +systemLog: + component: + replication: + rollback: + verbosity: 2 + storage: + recovery: + verbosity: 2 + destination: file + logAppend: true + path: {{ path-prefix }}/mongod.log + verbosity: 1 + +net: + bindIp: 0.0.0.0 + +storage: + dbPath: {{ path-prefix }}/data + engine: {{ storage-engine }} + +replication: + replSetName: jepsen + enableMajorityReadConcern: {{ enable-majority-read-concern }} diff --git a/evergreen/jepsen_docker/setup_config_fuzzer.sh b/evergreen/jepsen_docker/setup_config_fuzzer.sh new file mode 100644 index 00000000000..e2c5a12cca6 --- /dev/null +++ b/evergreen/jepsen_docker/setup_config_fuzzer.sh @@ -0,0 +1,14 @@ +DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" > /dev/null 2>&1 && pwd)" +. "$DIR/../prelude.sh" + +cd src +set -o verbose +set -o errexit + +activate_venv +$python buildscripts/resmoke.py generate-fuzz-config --template ../jepsen/docker/control/mongodb/resources --output ../jepsen/docker/control/mongodb/resources + +echo "Print config fuzzer generated mongod.conf" +cat ../jepsen/docker/control/mongodb/resources/mongod.conf +echo "Print config fuzzer generated mongos.conf" +cat ../jepsen/docker/control/mongodb/resources/mongos.conf diff --git a/evergreen/jepsen_test_run.sh b/evergreen/jepsen_test_run.sh index b5f79426079..719b4a358d4 100644 --- a/evergreen/jepsen_test_run.sh +++ b/evergreen/jepsen_test_run.sh @@ -26,9 +26,9 @@ lein run test --test ${jepsen_test_name} \ --working-dir ${workdir}/src/jepsen-workdir \ --clock-skew faketime \ --libfaketime-path ${workdir}/src/libfaketime/build/libfaketime.so.1 \ - --mongod-conf mongod_verbose.conf \ --virtualization none \ --nodes-file ../nodes.txt \ + ${mongod_conf} \ ${jepsen_key_time_limit} \ ${jepsen_protocol_version} \ ${jepsen_read_concern} \