diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..e21bd05 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,6 @@ +FROM python:3.9 +WORKDIR /app +COPY . . +RUN make install +CMD ["/usr/local/bin/filibuster", "--server-only"] +EXPOSE 5005 diff --git a/filibuster/lifecycle/__init__.py b/filibuster/lifecycle/__init__.py index b37b625..785ca58 100644 --- a/filibuster/lifecycle/__init__.py +++ b/filibuster/lifecycle/__init__.py @@ -12,7 +12,7 @@ def num_services_running(services): num_running = len(services) for service in services: if not service_running(service): - debug("! service " + service + " not yet running!") + # debug("! service " + service + " not yet running!") num_running -= 1 return num_running diff --git a/filibuster/server/__init__.py b/filibuster/server/__init__.py index ee2b0f8..e225efa 100644 --- a/filibuster/server/__init__.py +++ b/filibuster/server/__init__.py @@ -74,6 +74,7 @@ server_only_mode = False should_terminate_immediately = False teardown_completed = False +instrumentation = None def run_test(functional_test, only_initial_execution, disable_dynamic_reduction, forced_failure, should_suppress_combinations, setup_script, teardown_script): @@ -167,6 +168,7 @@ def run_test(functional_test, only_initial_execution, disable_dynamic_reduction, requests_to_fail = next_test_execution.failures # Set current test execution. + notice("Setting current test execution.") current_test_execution = next_test_execution notice("Set current test execution to next execution.") @@ -283,6 +285,7 @@ def generate_additional_test_executions(generated_id, execution_index, instrumen global current_test_execution_batch global requests_to_fail global suppress_combinations + global instrumentation # List of additional test executions. additional_test_executions = [] @@ -312,7 +315,8 @@ def generate_additional_test_executions(generated_id, execution_index, instrumen break # Iterate list of faults. - instrumentation = read_analysis_file(analysis_file) + if instrumentation is None: + instrumentation = read_analysis_file(analysis_file) for module in instrumentation: pattern = instrumentation[module]['pattern'] @@ -376,42 +380,78 @@ def generate_additional_test_executions(generated_id, execution_index, instrumen additional_test_executions.append(new_execution) # Error testing. - if instrumentation_type == 'request_received': - if 'errors' in instrumentation[module]: - for error in instrumentation[module]['errors']: - if 'target_service_name' in req and req['target_service_name'] is not None: - target_service_name = req['target_service_name'] - - service_pattern = error['service_name'] - service_matcher = re.compile(service_pattern) - service_matching = service_matcher.match(target_service_name) - - if service_matching is not None: - for type in error['types']: - # warning("Checking if we need to inject error: " + str(type)) - # warning("already_failed: " + str(already_failed)) - - if not already_failed: - # For this execution, we need to fail everything we did before to get here - # but, we also need to fail this additional one request as well. - # (also, add the exception so we know what to throw later.) - new_req = copy.deepcopy(req) - new_req['failure_metadata'] = {} - for key in type: - new_req['failure_metadata'][key] = type[key] - new_failures = copy.deepcopy(failures) - new_failures.append(TestExecution.filter_request_for_failures(new_req)) - new_failures = sorted(new_failures, key=lambda k: k['execution_index']) - - new_execution = TestExecution(log, new_failures) - if should_schedule(new_execution, additional_test_executions): - if new_execution not in additional_test_executions: - debug("Adding req failure for request: " + str( - req['execution_index'])) - debug("=> failure description: " + str(type)) - additional_test_executions.append(new_execution) - else: - warning("Request does not have a target service, it's made outside of the system.") + # if instrumentation_type == 'request_received': + # if 'errors' in instrumentation[module]: + # for error in instrumentation[module]['errors']: + # if 'target_service_name' in req and req['target_service_name'] is not None: + # target_service_name = req['target_service_name'] + # + # service_pattern = error['service_name'] + # service_matcher = re.compile(service_pattern) + # service_matching = service_matcher.match(target_service_name) + # + # if service_matching is not None: + # for type in error['types']: + # # warning("Checking if we need to inject error: " + str(type)) + # # warning("already_failed: " + str(already_failed)) + # + # if not already_failed: + # # For this execution, we need to fail everything we did before to get here + # # but, we also need to fail this additional one request as well. + # # (also, add the exception so we know what to throw later.) + # new_req = copy.deepcopy(req) + # new_req['failure_metadata'] = {} + # for key in type: + # new_req['failure_metadata'][key] = type[key] + # new_failures = copy.deepcopy(failures) + # new_failures.append(TestExecution.filter_request_for_failures(new_req)) + # new_failures = sorted(new_failures, key=lambda k: k['execution_index']) + # + # new_execution = TestExecution(log, new_failures) + # if should_schedule(new_execution, additional_test_executions): + # if new_execution not in additional_test_executions: + # debug("Adding req failure for request: " + str( + # req['execution_index'])) + # debug("=> failure description: " + str(type)) + # additional_test_executions.append(new_execution) + # else: + # warning("Request does not have a target service, it's made outside of the system.") + + if 'errors' in instrumentation[module]: + for error in instrumentation[module]['errors']: + if 'target_service_name' in req and req['target_service_name'] is not None: + target_service_name = req['target_service_name'] + else: + target_service_name = "" + + service_pattern = error['service_name'] + service_matcher = re.compile(service_pattern) + service_matching = service_matcher.match(target_service_name) + + if service_matching is not None: + for type in error['types']: + # warning("Checking if we need to inject error: " + str(type)) + # warning("already_failed: " + str(already_failed)) + + if not already_failed: + # For this execution, we need to fail everything we did before to get here + # but, we also need to fail this additional one request as well. + # (also, add the exception so we know what to throw later.) + new_req = copy.deepcopy(req) + new_req['failure_metadata'] = {} + for key in type: + new_req['failure_metadata'][key] = type[key] + new_failures = copy.deepcopy(failures) + new_failures.append(TestExecution.filter_request_for_failures(new_req)) + new_failures = sorted(new_failures, key=lambda k: k['execution_index']) + + new_execution = TestExecution(log, new_failures) + if should_schedule(new_execution, additional_test_executions): + if new_execution not in additional_test_executions: + debug("Adding req failure for request: " + str( + req['execution_index'])) + debug("=> failure description: " + str(type)) + additional_test_executions.append(new_execution) append_quantity = 0 @@ -562,9 +602,9 @@ def has_next_iteration(iteration, caller): elif current_test_execution is None and test_executions_scheduled.size() > 0: # Wait until current test execution is set. - # print("current not yet set, waiting.") + print("current not yet set, waiting.") wait_until_current_test_execution() - # print("current now set, returning true") + print("current now set, returning true") print("has_next_iteration called: " + str(iteration) + " for caller " + str(caller)) return jsonify({"has-next-iteration": True}) @@ -691,7 +731,7 @@ def health_check(): return jsonify({"status": "OK"}) -@app.route("/terminate", methods=['GET']) +@app.route("/filibuster/terminate", methods=['GET']) def terminate(): global should_terminate_immediately should_terminate_immediately = True @@ -699,7 +739,7 @@ def terminate(): return jsonify({}) -@app.route("/teardowns-completed/", methods=['GET']) +@app.route("/filibuster/teardowns-completed/", methods=['GET']) def teardowns_completed(iteration): global teardown_completed global current_test_execution @@ -708,11 +748,11 @@ def teardowns_completed(iteration): # to be set immediately and not asynchronously otherwise beforeEach will run before # we have swapped the test execution. if current_test_execution is not None: - notice("Nulling current test execution.") + notice("Nulling current test execution because teardown is completed.") current_test_execution = None teardown_completed = True - # notice("Teardown completed for iteration: " + str(iteration)) + notice("Teardown completed for iteration: " + str(iteration)) return jsonify({}) @@ -734,6 +774,13 @@ def new_test_execution_check(service_name): return jsonify({"new-test-execution": new_test_execution}) +@app.route("/filibuster/analysis-file", methods=['POST']) +def analysis_file(): + global instrumentation + instrumentation = request.get_json() + return jsonify({}) + + @app.route("/filibuster/create", methods=['PUT']) def create(): try: @@ -1066,25 +1113,29 @@ def wait_indefinitely_until_shutdown(period=0.25): def wait_for_teardown_completed(period=0.25): global teardown_completed global current_test_execution + global server_only_mode + notice("Waiting for teardown completed: BLOCKED PYTHON WAITING FOR AFTEREACH.") - while True: - if teardown_completed: - notice("Teardown completed; nulling out current test execution: PYTHON UNBLOCKED.") - # This unblocks python. - teardown_completed = False - break + if server_only_mode: + while True: + if teardown_completed: + notice("Teardown completed. Marking teardown_completed.") + # This unblocks python. + teardown_completed = False + break - time.sleep(period) + time.sleep(period) def wait_until_current_test_execution(period=0.25): global current_test_execution notice("Waiting for current test execution.") - while True: - if current_test_execution is not None: - notice("Current test execution populated: UNBLOCKED JAVA.") - break + if server_only_mode: + while True: + if current_test_execution is not None: + notice("Current test execution populated: UNBLOCKED JAVA.") + break - time.sleep(period) + time.sleep(period) diff --git a/filibuster_cli.py b/filibuster_cli.py index eb23367..d2d9b6e 100644 --- a/filibuster_cli.py +++ b/filibuster_cli.py @@ -91,6 +91,12 @@ def test(server_only, else: test_to_execute = functional_test + if os.environ.get('SHOULD_SUPPRESS_COMBINATIONS', ''): + should_suppress_combinations = True + + if os.environ.get('DISABLE_DYNAMIC_REDUCTION', ''): + disable_dynamic_reduction = True + start_filibuster_server_and_run_test(test_to_execute, abs_analysis_file, counterexample_file, diff --git a/requirements.txt b/requirements.txt index 9ebd083..cb6168a 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,6 +1,6 @@ click>=7.1.2 -Flask==1.0.0 requests -sphinx -sphinx-rtd-theme -myst-parser \ No newline at end of file +Flask==2.0.0 +pytest +requests +Jinja2 \ No newline at end of file