From 316fadb884d7b6f1b9884ab0c284ecc4f318009c Mon Sep 17 00:00:00 2001 From: Eriq Augustine Date: Thu, 2 Jan 2025 14:52:18 -1000 Subject: [PATCH 1/3] Added support for the system stats query endpoint. --- autograder/api/stats/__init__.py | 0 autograder/api/stats/system/__init__.py | 0 autograder/api/stats/system/query.py | 36 ++++++ autograder/cli/stats/__init__.py | 0 autograder/cli/stats/__main__.py | 14 +++ autograder/cli/stats/system/__init__.py | 0 autograder/cli/stats/system/__main__.py | 14 +++ autograder/cli/stats/system/query.py | 20 +++ tests/api/test_api.py | 32 +++++ .../metadata/metadata_describe_base.json | 119 +++++++++++------- .../stats/system/stats_system_query_base.json | 33 +++++ .../metadata/metadata_describe_base.txt | 119 +++++++++++------- .../stats/system/stats_system_query_base.txt | 31 +++++ 13 files changed, 334 insertions(+), 84 deletions(-) create mode 100644 autograder/api/stats/__init__.py create mode 100644 autograder/api/stats/system/__init__.py create mode 100644 autograder/api/stats/system/query.py create mode 100644 autograder/cli/stats/__init__.py create mode 100644 autograder/cli/stats/__main__.py create mode 100644 autograder/cli/stats/system/__init__.py create mode 100644 autograder/cli/stats/system/__main__.py create mode 100644 autograder/cli/stats/system/query.py create mode 100644 tests/api/testdata/stats/system/stats_system_query_base.json create mode 100644 tests/cli/testdata/stats/system/stats_system_query_base.txt diff --git a/autograder/api/stats/__init__.py b/autograder/api/stats/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/autograder/api/stats/system/__init__.py b/autograder/api/stats/system/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/autograder/api/stats/system/query.py b/autograder/api/stats/system/query.py new file mode 100644 index 00000000..aac421fc --- /dev/null +++ b/autograder/api/stats/system/query.py @@ -0,0 +1,36 @@ +import autograder.api.common +import autograder.api.config + +API_ENDPOINT = 'stats/system/query' +API_PARAMS = [ + autograder.api.config.PARAM_USER_EMAIL, + autograder.api.config.PARAM_USER_PASS, + + autograder.api.config.APIParam('limit', + 'The maximum number of records to return.', + required = False, parser_options = {'action': 'store', 'type': int}), + + autograder.api.config.APIParam('after', + 'If supplied, only return stat records after this timestamp.', + required = False), + + autograder.api.config.APIParam('before', + 'If supplied, only return stat records before this timestamp.', + required = False), + + autograder.api.config.APIParam('sort', + 'Sort the results. -1 for ascending, 0 for no sorting, 1 for descending.', + required = False, parser_options = {'action': 'store', 'type': int}), +] + +DESCRIPTION = 'Query system stats from the autograder server.' + +def send(arguments, **kwargs): + return autograder.api.common.handle_api_request(arguments, API_PARAMS, API_ENDPOINT, **kwargs) + +def _get_parser(): + parser = autograder.api.config.get_argument_parser( + description = DESCRIPTION, + params = API_PARAMS) + + return parser diff --git a/autograder/cli/stats/__init__.py b/autograder/cli/stats/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/autograder/cli/stats/__main__.py b/autograder/cli/stats/__main__.py new file mode 100644 index 00000000..3c7d7e22 --- /dev/null +++ b/autograder/cli/stats/__main__.py @@ -0,0 +1,14 @@ +""" +The `autograder.cli.stats` package contains tools for +managing stats from the autograder server. +""" + +import sys + +import autograder.util.cli + +def main(): + return autograder.util.cli.main() + +if (__name__ == '__main__'): + sys.exit(main()) diff --git a/autograder/cli/stats/system/__init__.py b/autograder/cli/stats/system/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/autograder/cli/stats/system/__main__.py b/autograder/cli/stats/system/__main__.py new file mode 100644 index 00000000..b1a5932d --- /dev/null +++ b/autograder/cli/stats/system/__main__.py @@ -0,0 +1,14 @@ +""" +The `autograder.cli.stats.system` package contains tools for +managing system-level stats from the autograder server. +""" + +import sys + +import autograder.util.cli + +def main(): + return autograder.util.cli.main() + +if (__name__ == '__main__'): + sys.exit(main()) diff --git a/autograder/cli/stats/system/query.py b/autograder/cli/stats/system/query.py new file mode 100644 index 00000000..424fc344 --- /dev/null +++ b/autograder/cli/stats/system/query.py @@ -0,0 +1,20 @@ +import json +import sys + +import autograder.api.stats.system.query + +def run(arguments): + result = autograder.api.stats.system.query.send(arguments, exit_on_error = True) + print(json.dumps(result['results'], indent = 4)) + return 0 + +def main(): + return run(_get_parser().parse_args()) + +def _get_parser(): + parser = autograder.api.stats.system.query._get_parser() + + return parser + +if (__name__ == '__main__'): + sys.exit(main()) diff --git a/tests/api/test_api.py b/tests/api/test_api.py index 153b98c0..5484bcda 100644 --- a/tests/api/test_api.py +++ b/tests/api/test_api.py @@ -149,3 +149,35 @@ def clean_output_logs(output): return output _discover_api_tests() + +def fake_system_stats(output): + """ + Beause of the variable and fine-grained level of system stats, + the enture output must be faked. + """ + + return { + "results": [ + { + "timestamp": 100, + "cpu-percent": 1, + "mem-percent": 1, + "net-bytes-sent": 1, + "net-bytes-received": 1, + }, + { + "timestamp": 200, + "cpu-percent": 2, + "mem-percent": 2, + "net-bytes-sent": 2, + "net-bytes-received": 2, + }, + { + "timestamp": 300, + "cpu-percent": 3, + "mem-percent": 3, + "net-bytes-sent": 3, + "net-bytes-received": 3, + }, + ], + } diff --git a/tests/api/testdata/metadata/metadata_describe_base.json b/tests/api/testdata/metadata/metadata_describe_base.json index dcd64570..886a46cf 100644 --- a/tests/api/testdata/metadata/metadata_describe_base.json +++ b/tests/api/testdata/metadata/metadata_describe_base.json @@ -5,123 +5,158 @@ "endpoints": { "courses/admin/update": { "request-type": "*admin.UpdateRequest", - "response-type": "*admin.UpdateResponse" + "response-type": "*admin.UpdateResponse", + "description": "Update an existing course." }, "courses/assignments/get": { "request-type": "*assignments.GetRequest", - "response-type": "*assignments.GetResponse" + "response-type": "*assignments.GetResponse", + "description": "Get the information for a course assignment." }, "courses/assignments/list": { "request-type": "*assignments.ListRequest", - "response-type": "*assignments.ListResponse" + "response-type": "*assignments.ListResponse", + "description": "List the assignments in the course." }, "courses/assignments/submissions/fetch/course/attempts": { - "request-type": "*submissions.FetchCourseAttemptsRequest", - "response-type": "*submissions.FetchCourseAttemptsResponse" + "request-type": "*course.FetchCourseAttemptsRequest", + "response-type": "*course.FetchCourseAttemptsResponse", + "description": "Get all recent submissions and grading information for this assignment." }, "courses/assignments/submissions/fetch/course/scores": { - "request-type": "*submissions.FetchCourseScoresRequest", - "response-type": "*submissions.FetchCourseScoresResponse" + "request-type": "*course.FetchCourseScoresRequest", + "response-type": "*course.FetchCourseScoresResponse", + "description": "Get a summary of the most recent scores for this assignment." }, "courses/assignments/submissions/fetch/user/attempt": { - "request-type": "*submissions.FetchUserAttemptRequest", - "response-type": "*submissions.FetchUserAttemptResponse" + "request-type": "*user.FetchUserAttemptRequest", + "response-type": "*user.FetchUserAttemptResponse", + "description": "Get a submission along with all grading information." }, "courses/assignments/submissions/fetch/user/attempts": { - "request-type": "*submissions.FetchUserAttemptsRequest", - "response-type": "*submissions.FetchUserAttemptsResponse" + "request-type": "*user.FetchUserAttemptsRequest", + "response-type": "*user.FetchUserAttemptsResponse", + "description": "Get all submission attempts made by a user along with all grading information." }, "courses/assignments/submissions/fetch/user/history": { - "request-type": "*submissions.FetchUserHistoryRequest", - "response-type": "*submissions.FetchUserHistoryResponse" + "request-type": "*user.FetchUserHistoryRequest", + "response-type": "*user.FetchUserHistoryResponse", + "description": "Get a summary of the submissions for this assignment." }, "courses/assignments/submissions/fetch/user/peek": { - "request-type": "*submissions.FetchUserPeekRequest", - "response-type": "*submissions.FetchUserPeekResponse" + "request-type": "*user.FetchUserPeekRequest", + "response-type": "*user.FetchUserPeekResponse", + "description": "Get a copy of the grading report for the specified submission. Does not submit a new submission." }, "courses/assignments/submissions/remove": { "request-type": "*submissions.RemoveRequest", - "response-type": "*submissions.RemoveResponse" + "response-type": "*submissions.RemoveResponse", + "description": "Remove a specified submission. Defaults to the most recent submission." }, "courses/assignments/submissions/submit": { "request-type": "*submissions.SubmitRequest", - "response-type": "*submissions.SubmitResponse" + "response-type": "*submissions.SubmitResponse", + "description": "Submit an assignment submission to the autograder." }, "courses/upsert/filespec": { "request-type": "*upsert.FileSpecRequest", - "response-type": "*upsert.UpsertResponse" + "response-type": "*upsert.UpsertResponse", + "description": "Upsert a course using a filespec." }, "courses/upsert/zip": { "request-type": "*upsert.ZipFileRequest", - "response-type": "*upsert.UpsertResponse" + "response-type": "*upsert.UpsertResponse", + "description": "Upsert a course using a zip file." }, "courses/users/drop": { "request-type": "*users.DropRequest", - "response-type": "*users.DropResponse" + "response-type": "*users.DropResponse", + "description": "Drop a user from the course." }, "courses/users/enroll": { "request-type": "*users.EnrollRequest", - "response-type": "*users.EnrollResponse" + "response-type": "*users.EnrollResponse", + "description": "Enroll one or more users to the course." }, "courses/users/get": { "request-type": "*users.GetRequest", - "response-type": "*users.GetResponse" + "response-type": "*users.GetResponse", + "description": "Get the information for a course user." }, "courses/users/list": { "request-type": "*users.ListRequest", - "response-type": "*users.ListResponse" + "response-type": "*users.ListResponse", + "description": "List the users in the course." }, "lms/upload/scores": { - "request-type": "*lms.UploadScoresRequest", - "response-type": "*lms.UploadScoresResponse" + "request-type": "*upload.UploadScoresRequest", + "response-type": "*upload.UploadScoresResponse", + "description": "Upload scores from a tab-separated file to the course's LMS.\nThe file should not have headers, and should have two columns: email and score." }, "lms/user/get": { - "request-type": "*lms.UserGetRequest", - "response-type": "*lms.UserGetResponse" + "request-type": "*user.UserGetRequest", + "response-type": "*user.UserGetResponse", + "description": "Get information for an LMS user." }, "logs/query": { "request-type": "*logs.QueryRequest", - "response-type": "*logs.QueryResponse" + "response-type": "*logs.QueryResponse", + "description": "Query log entries from the autograder server." }, "metadata/describe": { "request-type": "*metadata.DescribeRequest", - "response-type": "*metadata.DescribeResponse" + "response-type": "*metadata.DescribeResponse", + "description": "Describe all endpoints on the server." + }, + "stats/system/query": { + "request-type": "*system.QueryRequest", + "response-type": "*system.QueryResponse", + "description": "Query the system stats for the server." }, "users/auth": { "request-type": "*users.AuthRequest", - "response-type": "*users.AuthResponse" + "response-type": "*users.AuthResponse", + "description": "Authenticate as a user." }, "users/get": { "request-type": "*users.GetRequest", - "response-type": "*users.GetResponse" + "response-type": "*users.GetResponse", + "description": "Get the information for a server user." }, "users/list": { "request-type": "*users.ListRequest", - "response-type": "*users.ListResponse" + "response-type": "*users.ListResponse", + "description": "List the users on the server." }, "users/password/change": { - "request-type": "*users.PasswordChangeRequest", - "response-type": "*users.PasswordChangeResponse" + "request-type": "*password.PasswordChangeRequest", + "response-type": "*password.PasswordChangeResponse", + "description": "Change your password to the one provided." }, "users/password/reset": { - "request-type": "*users.PasswordResetRequest", - "response-type": "*users.PasswordResetResponse" + "request-type": "*password.PasswordResetRequest", + "response-type": "*password.PasswordResetResponse", + "description": "Reset to a random password that will be emailed to you." }, "users/remove": { "request-type": "*users.RemoveRequest", - "response-type": "*users.RemoveResponse" + "response-type": "*users.RemoveResponse", + "description": "Remove a user from the server." }, "users/tokens/create": { - "request-type": "*users.TokensCreateRequest", - "response-type": "*users.TokensCreateResponse" + "request-type": "*tokens.TokensCreateRequest", + "response-type": "*tokens.TokensCreateResponse", + "description": "Create a new authentication token." }, "users/tokens/delete": { - "request-type": "*users.TokensDeleteRequest", - "response-type": "*users.TokensDeleteResponse" + "request-type": "*tokens.TokensDeleteRequest", + "response-type": "*tokens.TokensDeleteResponse", + "description": "Delete an authentication token." }, "users/upsert": { "request-type": "*users.UpsertRequest", - "response-type": "*users.UpsertResponse" + "response-type": "*users.UpsertResponse", + "description": "Upsert one or more users to the server (update if exists, insert otherwise)." } } } diff --git a/tests/api/testdata/stats/system/stats_system_query_base.json b/tests/api/testdata/stats/system/stats_system_query_base.json new file mode 100644 index 00000000..5e735658 --- /dev/null +++ b/tests/api/testdata/stats/system/stats_system_query_base.json @@ -0,0 +1,33 @@ +{ + "module": "autograder.api.stats.system.query", + "arguments": { + "user": "server-admin@test.edulinq.org", + "pass": "server-admin" + }, + "output-modifier": "fake_system_stats", + "output": { + "results": [ + { + "timestamp": 100, + "cpu-percent": 1, + "mem-percent": 1, + "net-bytes-sent": 1, + "net-bytes-received": 1 + }, + { + "timestamp": 200, + "cpu-percent": 2, + "mem-percent": 2, + "net-bytes-sent": 2, + "net-bytes-received": 2 + }, + { + "timestamp": 300, + "cpu-percent": 3, + "mem-percent": 3, + "net-bytes-sent": 3, + "net-bytes-received": 3 + } + ] + } +} diff --git a/tests/cli/testdata/metadata/metadata_describe_base.txt b/tests/cli/testdata/metadata/metadata_describe_base.txt index 3dca8b41..f187645c 100644 --- a/tests/cli/testdata/metadata/metadata_describe_base.txt +++ b/tests/cli/testdata/metadata/metadata_describe_base.txt @@ -7,123 +7,158 @@ "endpoints": { "courses/admin/update": { "request-type": "*admin.UpdateRequest", - "response-type": "*admin.UpdateResponse" + "response-type": "*admin.UpdateResponse", + "description": "Update an existing course." }, "courses/assignments/get": { "request-type": "*assignments.GetRequest", - "response-type": "*assignments.GetResponse" + "response-type": "*assignments.GetResponse", + "description": "Get the information for a course assignment." }, "courses/assignments/list": { "request-type": "*assignments.ListRequest", - "response-type": "*assignments.ListResponse" + "response-type": "*assignments.ListResponse", + "description": "List the assignments in the course." }, "courses/assignments/submissions/fetch/course/attempts": { - "request-type": "*submissions.FetchCourseAttemptsRequest", - "response-type": "*submissions.FetchCourseAttemptsResponse" + "request-type": "*course.FetchCourseAttemptsRequest", + "response-type": "*course.FetchCourseAttemptsResponse", + "description": "Get all recent submissions and grading information for this assignment." }, "courses/assignments/submissions/fetch/course/scores": { - "request-type": "*submissions.FetchCourseScoresRequest", - "response-type": "*submissions.FetchCourseScoresResponse" + "request-type": "*course.FetchCourseScoresRequest", + "response-type": "*course.FetchCourseScoresResponse", + "description": "Get a summary of the most recent scores for this assignment." }, "courses/assignments/submissions/fetch/user/attempt": { - "request-type": "*submissions.FetchUserAttemptRequest", - "response-type": "*submissions.FetchUserAttemptResponse" + "request-type": "*user.FetchUserAttemptRequest", + "response-type": "*user.FetchUserAttemptResponse", + "description": "Get a submission along with all grading information." }, "courses/assignments/submissions/fetch/user/attempts": { - "request-type": "*submissions.FetchUserAttemptsRequest", - "response-type": "*submissions.FetchUserAttemptsResponse" + "request-type": "*user.FetchUserAttemptsRequest", + "response-type": "*user.FetchUserAttemptsResponse", + "description": "Get all submission attempts made by a user along with all grading information." }, "courses/assignments/submissions/fetch/user/history": { - "request-type": "*submissions.FetchUserHistoryRequest", - "response-type": "*submissions.FetchUserHistoryResponse" + "request-type": "*user.FetchUserHistoryRequest", + "response-type": "*user.FetchUserHistoryResponse", + "description": "Get a summary of the submissions for this assignment." }, "courses/assignments/submissions/fetch/user/peek": { - "request-type": "*submissions.FetchUserPeekRequest", - "response-type": "*submissions.FetchUserPeekResponse" + "request-type": "*user.FetchUserPeekRequest", + "response-type": "*user.FetchUserPeekResponse", + "description": "Get a copy of the grading report for the specified submission. Does not submit a new submission." }, "courses/assignments/submissions/remove": { "request-type": "*submissions.RemoveRequest", - "response-type": "*submissions.RemoveResponse" + "response-type": "*submissions.RemoveResponse", + "description": "Remove a specified submission. Defaults to the most recent submission." }, "courses/assignments/submissions/submit": { "request-type": "*submissions.SubmitRequest", - "response-type": "*submissions.SubmitResponse" + "response-type": "*submissions.SubmitResponse", + "description": "Submit an assignment submission to the autograder." }, "courses/upsert/filespec": { "request-type": "*upsert.FileSpecRequest", - "response-type": "*upsert.UpsertResponse" + "response-type": "*upsert.UpsertResponse", + "description": "Upsert a course using a filespec." }, "courses/upsert/zip": { "request-type": "*upsert.ZipFileRequest", - "response-type": "*upsert.UpsertResponse" + "response-type": "*upsert.UpsertResponse", + "description": "Upsert a course using a zip file." }, "courses/users/drop": { "request-type": "*users.DropRequest", - "response-type": "*users.DropResponse" + "response-type": "*users.DropResponse", + "description": "Drop a user from the course." }, "courses/users/enroll": { "request-type": "*users.EnrollRequest", - "response-type": "*users.EnrollResponse" + "response-type": "*users.EnrollResponse", + "description": "Enroll one or more users to the course." }, "courses/users/get": { "request-type": "*users.GetRequest", - "response-type": "*users.GetResponse" + "response-type": "*users.GetResponse", + "description": "Get the information for a course user." }, "courses/users/list": { "request-type": "*users.ListRequest", - "response-type": "*users.ListResponse" + "response-type": "*users.ListResponse", + "description": "List the users in the course." }, "lms/upload/scores": { - "request-type": "*lms.UploadScoresRequest", - "response-type": "*lms.UploadScoresResponse" + "request-type": "*upload.UploadScoresRequest", + "response-type": "*upload.UploadScoresResponse", + "description": "Upload scores from a tab-separated file to the course's LMS.\nThe file should not have headers, and should have two columns: email and score." }, "lms/user/get": { - "request-type": "*lms.UserGetRequest", - "response-type": "*lms.UserGetResponse" + "request-type": "*user.UserGetRequest", + "response-type": "*user.UserGetResponse", + "description": "Get information for an LMS user." }, "logs/query": { "request-type": "*logs.QueryRequest", - "response-type": "*logs.QueryResponse" + "response-type": "*logs.QueryResponse", + "description": "Query log entries from the autograder server." }, "metadata/describe": { "request-type": "*metadata.DescribeRequest", - "response-type": "*metadata.DescribeResponse" + "response-type": "*metadata.DescribeResponse", + "description": "Describe all endpoints on the server." + }, + "stats/system/query": { + "request-type": "*system.QueryRequest", + "response-type": "*system.QueryResponse", + "description": "Query the system stats for the server." }, "users/auth": { "request-type": "*users.AuthRequest", - "response-type": "*users.AuthResponse" + "response-type": "*users.AuthResponse", + "description": "Authenticate as a user." }, "users/get": { "request-type": "*users.GetRequest", - "response-type": "*users.GetResponse" + "response-type": "*users.GetResponse", + "description": "Get the information for a server user." }, "users/list": { "request-type": "*users.ListRequest", - "response-type": "*users.ListResponse" + "response-type": "*users.ListResponse", + "description": "List the users on the server." }, "users/password/change": { - "request-type": "*users.PasswordChangeRequest", - "response-type": "*users.PasswordChangeResponse" + "request-type": "*password.PasswordChangeRequest", + "response-type": "*password.PasswordChangeResponse", + "description": "Change your password to the one provided." }, "users/password/reset": { - "request-type": "*users.PasswordResetRequest", - "response-type": "*users.PasswordResetResponse" + "request-type": "*password.PasswordResetRequest", + "response-type": "*password.PasswordResetResponse", + "description": "Reset to a random password that will be emailed to you." }, "users/remove": { "request-type": "*users.RemoveRequest", - "response-type": "*users.RemoveResponse" + "response-type": "*users.RemoveResponse", + "description": "Remove a user from the server." }, "users/tokens/create": { - "request-type": "*users.TokensCreateRequest", - "response-type": "*users.TokensCreateResponse" + "request-type": "*tokens.TokensCreateRequest", + "response-type": "*tokens.TokensCreateResponse", + "description": "Create a new authentication token." }, "users/tokens/delete": { - "request-type": "*users.TokensDeleteRequest", - "response-type": "*users.TokensDeleteResponse" + "request-type": "*tokens.TokensDeleteRequest", + "response-type": "*tokens.TokensDeleteResponse", + "description": "Delete an authentication token." }, "users/upsert": { "request-type": "*users.UpsertRequest", - "response-type": "*users.UpsertResponse" + "response-type": "*users.UpsertResponse", + "description": "Upsert one or more users to the server (update if exists, insert otherwise)." } } } diff --git a/tests/cli/testdata/stats/system/stats_system_query_base.txt b/tests/cli/testdata/stats/system/stats_system_query_base.txt new file mode 100644 index 00000000..8380e8a4 --- /dev/null +++ b/tests/cli/testdata/stats/system/stats_system_query_base.txt @@ -0,0 +1,31 @@ +{ + "cli": "autograder.cli.stats.system.query", + "arguments": [ + "--user", "server-admin@test.edulinq.org", + "--pass", "server-admin" + ] +} +--- +[ + { + "timestamp": 100, + "cpu-percent": 1, + "mem-percent": 1, + "net-bytes-sent": 1, + "net-bytes-received": 1 + }, + { + "timestamp": 200, + "cpu-percent": 2, + "mem-percent": 2, + "net-bytes-sent": 2, + "net-bytes-received": 2 + }, + { + "timestamp": 300, + "cpu-percent": 3, + "mem-percent": 3, + "net-bytes-sent": 3, + "net-bytes-received": 3 + } +] From 9ac77708b53b1d519b5793554131d67e3edcb0d7 Mon Sep 17 00:00:00 2001 From: Eriq Augustine Date: Sat, 4 Jan 2025 21:28:52 -1000 Subject: [PATCH 2/3] Fixed typos in a comment. --- tests/api/test_api.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/api/test_api.py b/tests/api/test_api.py index 5484bcda..34a4397d 100644 --- a/tests/api/test_api.py +++ b/tests/api/test_api.py @@ -152,8 +152,8 @@ def clean_output_logs(output): def fake_system_stats(output): """ - Beause of the variable and fine-grained level of system stats, - the enture output must be faked. + Because of the variable and fine-grained level of system stats, + the entire output must be faked. """ return { From 6ed31f140cec90c03eb49be24631b34c480a34fe Mon Sep 17 00:00:00 2001 From: Eriq Augustine Date: Sun, 5 Jan 2025 08:34:09 -1000 Subject: [PATCH 3/3] Added in another sleep for docker tests when verifying test data. --- .ci/backend.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.ci/backend.py b/.ci/backend.py index 6b9ad53d..3492b265 100644 --- a/.ci/backend.py +++ b/.ci/backend.py @@ -21,6 +21,7 @@ DOCKER_CONTAINER_NAME = 'autograder-py-verify-test-data' DOCKER_START_SLEEP_TIME_SECS = 0.25 DOCKER_STOP_WAIT_TIME_SECS = 1 +DOCKER_STOP_FINAL_WAIT_TIME_SECS = 0.5 SOURCE_START_SLEEP_TIME_SECS = 0.25 SOURCE_KILL_SLEEP_TIME_SECS = 0.25 @@ -91,6 +92,7 @@ def stop(self, **kwargs): ] util.run(args) + time.sleep(DOCKER_STOP_FINAL_WAIT_TIME_SECS) self._is_running = False def reset(self, **kwargs):