From a75975cb47a73755c7797a613f22b5ad0a5547a4 Mon Sep 17 00:00:00 2001 From: John Lambert Date: Wed, 25 Oct 2023 16:41:36 -0400 Subject: [PATCH] https://serval-api.org/ - add the slash --- docker-compose.yml | 2 +- samples/ServalApp/serval_auth_module.py | 2 +- scripts/load_testing.py | 394 ++++++++++-------- src/Serval.ApiServer/appsettings.json | 2 +- src/Serval.ApiServer/wwwroot/js/auth0.js | 2 +- tests/Serval.E2ETests/MissingServicesTests.cs | 2 +- tests/Serval.E2ETests/ServalApiSlowTests.cs | 2 +- tests/Serval.E2ETests/ServalApiTests.cs | 2 +- 8 files changed, 229 insertions(+), 179 deletions(-) diff --git a/docker-compose.yml b/docker-compose.yml index 74cc7ee5..72da99cf 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -9,7 +9,7 @@ services: environment: - ASPNETCORE_ENVIRONMENT=Staging - Auth__Domain=sil-appbuilder.auth0.com - - Auth__Audience=https://serval-api.org + - Auth__Audience=https://serval-api.org/ - ASPNETCORE_Kestrel__Endpoints__Http__Url=http://*:80 - ASPNETCORE_Kestrel__Endpoints__Http2__Url=http://*:81 - ASPNETCORE_Kestrel__Endpoints__Http2__Protocols=Http2 diff --git a/samples/ServalApp/serval_auth_module.py b/samples/ServalApp/serval_auth_module.py index fd18b41b..273669dc 100644 --- a/samples/ServalApp/serval_auth_module.py +++ b/samples/ServalApp/serval_auth_module.py @@ -32,7 +32,7 @@ def update_token(self): data = { "client_id": f"{self.__client_id}", "client_secret": f"{self.__client_secret}", - "audience": "https://serval-api.org", + "audience": "https://serval-api.org/", "grant_type": "client_credentials", } diff --git a/scripts/load_testing.py b/scripts/load_testing.py index 0d7ab7fd..0237065e 100644 --- a/scripts/load_testing.py +++ b/scripts/load_testing.py @@ -7,6 +7,7 @@ import string from tqdm import tqdm + def main(): start = time.time() SERVAL_AUTH_URL = os.environ.get("SERVAL_AUTH_URL") @@ -17,173 +18,217 @@ def main(): NUM_NMT_ENGINES_TO_ADD = 10_000 NUM_SMT_ENGINES_TO_ADD = 500 - base_url = "localhost" #"https://qa-int.serval-api.org" + base_url = "localhost" # "https://qa-int.serval-api.org" urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning) - print('Fetching authorization token...') + print("Fetching authorization token...") data = { "client_id": f"{SERVAL_CLIENT_ID}", - "client_secret":f"{SERVAL_CLIENT_SECRET}", - "audience":"https://serval-api.org", - "grant_type":"client_credentials" - } - - encoded_data = json.dumps(data).encode('utf-8') - - http = urllib3.PoolManager() #Use the following parameters if base_url is not localhost: cert_reqs='CERT_NONE', assert_hostname=False - r:urllib3.response.HTTPResponse =http.request( - 'POST', - f'{SERVAL_AUTH_URL}/oauth/token', + "client_secret": f"{SERVAL_CLIENT_SECRET}", + "audience": "https://serval-api.org/", + "grant_type": "client_credentials", + } + + encoded_data = json.dumps(data).encode("utf-8") + + http = ( + urllib3.PoolManager() + ) # Use the following parameters if base_url is not localhost: cert_reqs='CERT_NONE', assert_hostname=False + r: urllib3.response.HTTPResponse = http.request( + "POST", + f"{SERVAL_AUTH_URL}/oauth/token", body=encoded_data, - headers={"content-type": "application/json"} - ) - access_token = json.loads(r.data.decode('utf-8'))['access_token'] + headers={"content-type": "application/json"}, + ) + access_token = json.loads(r.data.decode("utf-8"))["access_token"] src_id = "" trg_id = "" - print('Setting up bombardier...') - bombardier_path_to_exe = 'load_testing_data/bombardier-linux-amd64' + print("Setting up bombardier...") + bombardier_path_to_exe = "load_testing_data/bombardier-linux-amd64" if not os.path.exists(bombardier_path_to_exe): - with http.request('GET', 'https://github.com/codesenberg/bombardier/releases/download/v1.2.6/bombardier-linux-amd64', preload_content=False) as r, open(bombardier_path_to_exe, 'wb') as out_file: + with http.request( + "GET", + "https://github.com/codesenberg/bombardier/releases/download/v1.2.6/bombardier-linux-amd64", + preload_content=False, + ) as r, open(bombardier_path_to_exe, "wb") as out_file: shutil.copyfileobj(r, out_file) os.chmod(bombardier_path_to_exe, mode=stat.S_IXUSR) try: - print('Bombarding get all translation engines endpoint...') + print("Bombarding get all translation engines endpoint...") os.system( "./" + bombardier_path_to_exe - + f' --print r -k -l -d 60s -r {REQUESTS_PER_SECOND} -c {NUM_CONCURRENT_CONNECTIONS} -H "authorization: Bearer {access_token}" -H "accept: application/json" -m "GET" "{base_url}/api/v1/translation/engines"') + + f' --print r -k -l -d 60s -r {REQUESTS_PER_SECOND} -c {NUM_CONCURRENT_CONNECTIONS} -H "authorization: Bearer {access_token}" -H "accept: application/json" -m "GET" "{base_url}/api/v1/translation/engines"' + ) print("Posting engines to DB...") - nmt_engine_ids:set[str] = set() - smt_engine_ids:set[str] = set() + nmt_engine_ids: set[str] = set() + smt_engine_ids: set[str] = set() + def post_nmt_engine(): - r:urllib3.response.HTTPResponse =http.request( - 'POST', - f'{base_url}/api/v1/translation/engines', - body = json.dumps({"name": "load_testing_engine", "targetLanguage": "en_Latn", "sourceLanguage" : "ell_Grek", "type":"Nmt"}).encode('utf-8'), - headers={"content-type": "application/json", "accept":"application/json", "authorization" : f"Bearer {access_token}"} + r: urllib3.response.HTTPResponse = http.request( + "POST", + f"{base_url}/api/v1/translation/engines", + body=json.dumps( + { + "name": "load_testing_engine", + "targetLanguage": "en_Latn", + "sourceLanguage": "ell_Grek", + "type": "Nmt", + } + ).encode("utf-8"), + headers={ + "content-type": "application/json", + "accept": "application/json", + "authorization": f"Bearer {access_token}", + }, ) - nmt_engine_ids.add(json.loads(r.data.decode('utf-8'))['id']) + nmt_engine_ids.add(json.loads(r.data.decode("utf-8"))["id"]) print("Posting NMT") for _ in tqdm(range(NUM_NMT_ENGINES_TO_ADD)): post_nmt_engine() def post_smt_engine(): - r:urllib3.response.HTTPResponse =http.request( - 'POST', - f'{base_url}/api/v1/translation/engines', - body = json.dumps({"name": "load_testing_engine", "targetLanguage": "en_Latn", "sourceLanguage" : "ell_Grek", "type":"SmtTransfer"}).encode('utf-8'), - headers={"content-type": "application/json", "accept":"application/json", "authorization" : f"Bearer {access_token}"} + r: urllib3.response.HTTPResponse = http.request( + "POST", + f"{base_url}/api/v1/translation/engines", + body=json.dumps( + { + "name": "load_testing_engine", + "targetLanguage": "en_Latn", + "sourceLanguage": "ell_Grek", + "type": "SmtTransfer", + } + ).encode("utf-8"), + headers={ + "content-type": "application/json", + "accept": "application/json", + "authorization": f"Bearer {access_token}", + }, ) - smt_engine_ids.add(json.loads(r.data.decode('utf-8'))['id']) + smt_engine_ids.add(json.loads(r.data.decode("utf-8"))["id"]) print("Posting SMT") for _ in tqdm(range(NUM_SMT_ENGINES_TO_ADD)): post_smt_engine() - #use bombadier get - print('Bombarding get all translation engines endpoint after adding docs...') + # use bombadier get + print("Bombarding get all translation engines endpoint after adding docs...") os.system( "./" + bombardier_path_to_exe - + f' --print r -k -l -d 60s -r {REQUESTS_PER_SECOND} -c {NUM_CONCURRENT_CONNECTIONS} -H "authorization: Bearer {access_token}" -H "accept: application/json" -m "GET" "{base_url}/api/v1/translation/engines"') - #add necessary files - print('Adding corpus to smt engine...') - with open('load_testing_data/testsrc.txt', 'r') as src_file: + + f' --print r -k -l -d 60s -r {REQUESTS_PER_SECOND} -c {NUM_CONCURRENT_CONNECTIONS} -H "authorization: Bearer {access_token}" -H "accept: application/json" -m "GET" "{base_url}/api/v1/translation/engines"' + ) + # add necessary files + print("Adding corpus to smt engine...") + with open("load_testing_data/testsrc.txt", "r") as src_file: src_data = src_file.read() - r:urllib3.response.HTTPResponse =http.request_encode_body( - 'POST', - f'{base_url}/api/v1/files', + r: urllib3.response.HTTPResponse = http.request_encode_body( + "POST", + f"{base_url}/api/v1/files", fields={ - 'file': ('testsrc.txt', src_data, 'text/plain'), - 'format':'Text' - }, - headers={'accept' : 'application/json', "authorization" : f"Bearer {access_token}"}, - encode_multipart=True + "file": ("testsrc.txt", src_data, "text/plain"), + "format": "Text", + }, + headers={ + "accept": "application/json", + "authorization": f"Bearer {access_token}", + }, + encode_multipart=True, ) - src_id = json.loads(r.data.decode('utf-8'))['id'] + src_id = json.loads(r.data.decode("utf-8"))["id"] - with open('load_testing_data/testtarg.txt', 'r') as targ_file: + with open("load_testing_data/testtarg.txt", "r") as targ_file: targ_data = targ_file.read() - r:urllib3.response.HTTPResponse =http.request( - 'POST', - f'{base_url}/api/v1/files', - fields={'file':('testtarg.txt', targ_data, 'text/plain'), 'format':'Text'}, - headers={'accept': 'application/json', "authorization" : f"Bearer {access_token}"} + r: urllib3.response.HTTPResponse = http.request( + "POST", + f"{base_url}/api/v1/files", + fields={ + "file": ("testtarg.txt", targ_data, "text/plain"), + "format": "Text", + }, + headers={ + "accept": "application/json", + "authorization": f"Bearer {access_token}", + }, ) - trg_id = json.loads(r.data.decode('utf-8'))['id'] + trg_id = json.loads(r.data.decode("utf-8"))["id"] print("Building an SMT engine for bombardment...") - #add corpora and build an smt + # add corpora and build an smt smt_id = list(smt_engine_ids)[0] http.request( - 'POST', - f'{base_url}/api/v1/translation/engines/{smt_id}/corpora', + "POST", + f"{base_url}/api/v1/translation/engines/{smt_id}/corpora", body=json.dumps( { - "sourceLanguage":"ell_Grek", - "targetLanguage":"en_Latn", - "sourceFiles": - [ - { - "fileId" : src_id, - "textId":"all" - } - ], - "targetFiles": - [ - { - "fileId" : trg_id, - "textId":"all" - } - ] - }).encode('utf-8'), - headers={'content-type':'application/json', 'accept': 'application/json', "authorization" : f"Bearer {access_token}"} + "sourceLanguage": "ell_Grek", + "targetLanguage": "en_Latn", + "sourceFiles": [{"fileId": src_id, "textId": "all"}], + "targetFiles": [{"fileId": trg_id, "textId": "all"}], + } + ).encode("utf-8"), + headers={ + "content-type": "application/json", + "accept": "application/json", + "authorization": f"Bearer {access_token}", + }, ) # corpus_id = json.loads(r.data.decode('utf-8'))['id'] - r:urllib3.response.HTTPResponse = http.request( - 'POST', - f'{base_url}/api/v1/translation/engines/{smt_id}/builds', - body=json.dumps({}).encode('utf-8'), - headers={"authorization" : f"Bearer {access_token}", 'content-type':'application/json'} + r: urllib3.response.HTTPResponse = http.request( + "POST", + f"{base_url}/api/v1/translation/engines/{smt_id}/builds", + body=json.dumps({}).encode("utf-8"), + headers={ + "authorization": f"Bearer {access_token}", + "content-type": "application/json", + }, ) - #waiting for build + # waiting for build is_built = False retry_index = 0 while not is_built: - r:urllib3.response.HTTPResponse = http.request( - 'GET', - f'{base_url}/api/v1/translation/engines/{smt_id}/current-build', - headers={"authorization" : f"Bearer {access_token}"} + r: urllib3.response.HTTPResponse = http.request( + "GET", + f"{base_url}/api/v1/translation/engines/{smt_id}/current-build", + headers={"authorization": f"Bearer {access_token}"}, ) is_built = r.status == 204 - if r.status//100 != 2: - raise Exception(f"Received response of {r.status} while trying to build engine; cannot continue testing!") + if r.status // 100 != 2: + raise Exception( + f"Received response of {r.status} while trying to build engine; cannot continue testing!" + ) if retry_index > 15: - r:urllib3.response.HTTPResponse = http.request( - 'POST', - f'{base_url}/api/v1/translation/engines/{smt_id}/current-build/cancel', - headers={"authorization" : f"Bearer {access_token}"} + r: urllib3.response.HTTPResponse = http.request( + "POST", + f"{base_url}/api/v1/translation/engines/{smt_id}/current-build/cancel", + headers={"authorization": f"Bearer {access_token}"}, ) - print("Engine is taking too long to build to continue testing. Cancelling build...") - raise Exception("Engine is taking too long to build to continue testing. Cancelling build...") - time.sleep(60 if retry_index == 0 else 20*retry_index) + print( + "Engine is taking too long to build to continue testing. Cancelling build..." + ) + raise Exception( + "Engine is taking too long to build to continue testing. Cancelling build..." + ) + time.sleep(60 if retry_index == 0 else 20 * retry_index) retry_index += 1 - segment_file_name = "".join(random.choices(string.ascii_letters, k=24)) + '.json' - f = open(segment_file_name, 'w', encoding='utf-8') + segment_file_name = ( + "".join(random.choices(string.ascii_letters, k=24)) + ".json" + ) + f = open(segment_file_name, "w", encoding="utf-8") f.write(json.dumps("Βίβλος γενέσεως Ἰησοῦ Χριστοῦ")) f.flush() f.close() - #bombard get word graph + # bombard get word graph print("Bombarding word graph endpoint...") os.system( "./" @@ -197,77 +242,72 @@ def post_smt_engine(): nmt_id = list(nmt_engine_ids)[0] print("Building NMT engine...") - r:urllib3.response.HTTPResponse = http.request( - 'POST', - f'{base_url}/api/v1/translation/engines/{nmt_id}/corpora', + r: urllib3.response.HTTPResponse = http.request( + "POST", + f"{base_url}/api/v1/translation/engines/{nmt_id}/corpora", body=json.dumps( { - "sourceLanguage":"ell_Grek", - "targetLanguage":"en_Latn", - "sourceFiles": - [ - { - "fileId" : src_id, - "textId":"all" - } - ], - "targetFiles": - [ - { - "fileId" : trg_id, - "textId":"all" - } - ] - }).encode('utf-8'), - headers={'content-type':'application/json', 'accept': 'application/json', "authorization" : f"Bearer {access_token}"} + "sourceLanguage": "ell_Grek", + "targetLanguage": "en_Latn", + "sourceFiles": [{"fileId": src_id, "textId": "all"}], + "targetFiles": [{"fileId": trg_id, "textId": "all"}], + } + ).encode("utf-8"), + headers={ + "content-type": "application/json", + "accept": "application/json", + "authorization": f"Bearer {access_token}", + }, ) - corpus_id = json.loads(r.data.decode('utf-8'))['id'] + corpus_id = json.loads(r.data.decode("utf-8"))["id"] - r:urllib3.response.HTTPResponse = http.request( - 'POST', - f'{base_url}/api/v1/translation/engines/{nmt_id}/builds', + r: urllib3.response.HTTPResponse = http.request( + "POST", + f"{base_url}/api/v1/translation/engines/{nmt_id}/builds", body=json.dumps( { - "pretranslate" : [ - { - "corpusId": corpus_id, - "textIds": [ - "all" - ] - } - ], - "options":"{\"max_steps\":10}" + "pretranslate": [{"corpusId": corpus_id, "textIds": ["all"]}], + "options": '{"max_steps":10}', } ), - headers={"authorization" : f"Bearer {access_token}", 'content-type':'application/json'} + headers={ + "authorization": f"Bearer {access_token}", + "content-type": "application/json", + }, ) - #waiting for build + # waiting for build is_built = False retry_index = 0 while not is_built: - r:urllib3.response.HTTPResponse = http.request( - 'GET', - f'{base_url}/api/v1/translation/engines/{nmt_id}/current-build', - headers={"authorization" : f"Bearer {access_token}"} + r: urllib3.response.HTTPResponse = http.request( + "GET", + f"{base_url}/api/v1/translation/engines/{nmt_id}/current-build", + headers={"authorization": f"Bearer {access_token}"}, ) is_built = r.status == 204 - if r.status//100 != 2: - raise Exception(f"Received response of {r.status} while trying to build engine; cannot continue testing!") + if r.status // 100 != 2: + raise Exception( + f"Received response of {r.status} while trying to build engine; cannot continue testing!" + ) if retry_index > 15: - print("Engine is taking too long to build to continue testing. Cancelling build...") - r:urllib3.response.HTTPResponse = http.request( - 'POST', - f'{base_url}/api/v1/translation/engines/{nmt_id}/current-build/cancel', - headers={"authorization" : f"Bearer {access_token}"} + print( + "Engine is taking too long to build to continue testing. Cancelling build..." ) - raise Exception("Engine is taking too long to build to continue testing. Cancelling build...") - time.sleep(240 if retry_index == 0 else 60*retry_index) + r: urllib3.response.HTTPResponse = http.request( + "POST", + f"{base_url}/api/v1/translation/engines/{nmt_id}/current-build/cancel", + headers={"authorization": f"Bearer {access_token}"}, + ) + raise Exception( + "Engine is taking too long to build to continue testing. Cancelling build..." + ) + time.sleep(240 if retry_index == 0 else 60 * retry_index) retry_index += 1 print("Bombarding pretranslation endpoint...") - #bombard get pretrans + # bombard get pretrans os.system( "./" + bombardier_path_to_exe @@ -276,17 +316,21 @@ def post_smt_engine(): except Exception as e: print("Something went wrong:", str(e) if str(e) != "" else "[No information]") finally: - #cleanup files, smt, nmt, bombardier - print('Cleaning up...') - print('Deleting added translation engines...') + # cleanup files, smt, nmt, bombardier + print("Cleaning up...") + print("Deleting added translation engines...") + def delete_engine(engine_id): - r:urllib3.response.HTTPResponse =http.request( - 'DELETE', - f'{base_url}/api/v1/translation/engines/{engine_id}', - body = json.dumps({"id":engine_id}).encode('utf-8'), - headers={"content-type": "application/json", "authorization" : f"Bearer {access_token}"} + r: urllib3.response.HTTPResponse = http.request( + "DELETE", + f"{base_url}/api/v1/translation/engines/{engine_id}", + body=json.dumps({"id": engine_id}).encode("utf-8"), + headers={ + "content-type": "application/json", + "authorization": f"Bearer {access_token}", + }, ) - if(r.status != 200): + if r.status != 200: print(f"Failed to delete engine {engine_id}") for engine_id in tqdm(nmt_engine_ids): @@ -294,25 +338,31 @@ def delete_engine(engine_id): for engine_id in tqdm(smt_engine_ids): delete_engine(engine_id) - r:urllib3.response.HTTPResponse =http.request( - 'DELETE', - f'{base_url}/api/v1/files/{src_id}', - headers={"content-type": "application/json", "authorization" : f"Bearer {access_token}"} + r: urllib3.response.HTTPResponse = http.request( + "DELETE", + f"{base_url}/api/v1/files/{src_id}", + headers={ + "content-type": "application/json", + "authorization": f"Bearer {access_token}", + }, ) - if(r.status != 200): + if r.status != 200: print(f"Failed to delete file {src_id}") - r:urllib3.response.HTTPResponse =http.request( - 'DELETE', - f'{base_url}/api/v1/files/{trg_id}', - headers={"content-type": "application/json", "authorization" : f"Bearer {access_token}"} + r: urllib3.response.HTTPResponse = http.request( + "DELETE", + f"{base_url}/api/v1/files/{trg_id}", + headers={ + "content-type": "application/json", + "authorization": f"Bearer {access_token}", + }, ) - if(r.status != 200): + if r.status != 200: print(f"Failed to delete file {trg_id}") - os.remove(bombardier_path_to_exe) - print("Finished testing in", round((time.time()-start)/60, 2), "minutes.") + print("Finished testing in", round((time.time() - start) / 60, 2), "minutes.") + if __name__ == "__main__": - main() \ No newline at end of file + main() diff --git a/src/Serval.ApiServer/appsettings.json b/src/Serval.ApiServer/appsettings.json index 3643b3ab..de5cb333 100644 --- a/src/Serval.ApiServer/appsettings.json +++ b/src/Serval.ApiServer/appsettings.json @@ -2,7 +2,7 @@ "AllowedHosts": "*", "Auth": { "Domain": "sil-appbuilder.auth0.com", - "Audience": "https://serval-api.org" + "Audience": "https://serval-api.org/" }, "DataFile": { "FilesDirectory": "/var/lib/serval/files" diff --git a/src/Serval.ApiServer/wwwroot/js/auth0.js b/src/Serval.ApiServer/wwwroot/js/auth0.js index 0aacdf63..9bc94342 100644 --- a/src/Serval.ApiServer/wwwroot/js/auth0.js +++ b/src/Serval.ApiServer/wwwroot/js/auth0.js @@ -3,7 +3,7 @@ var f = window.fetch; window.fetch = function (url, opts) { if (opts && opts.body && typeof opts.body === 'string' && opts.body.indexOf('client_credentials') !== -1) { // We know the audience - just add it. - opts.body += '&audience=https://serval-api.org'; + opts.body += '&audience=https://serval-api.org/'; } return f(url, opts); }; diff --git a/tests/Serval.E2ETests/MissingServicesTests.cs b/tests/Serval.E2ETests/MissingServicesTests.cs index 3d66c50f..e48a1977 100644 --- a/tests/Serval.E2ETests/MissingServicesTests.cs +++ b/tests/Serval.E2ETests/MissingServicesTests.cs @@ -9,7 +9,7 @@ public class MissingServicesTests [SetUp] public void Setup() { - _helperClient = new ServalClientHelper("https://machine.sil.org", ignoreSSLErrors: true); + _helperClient = new ServalClientHelper("https://serval-api.org/", ignoreSSLErrors: true); } [Test] diff --git a/tests/Serval.E2ETests/ServalApiSlowTests.cs b/tests/Serval.E2ETests/ServalApiSlowTests.cs index 11babee2..e12500a7 100644 --- a/tests/Serval.E2ETests/ServalApiSlowTests.cs +++ b/tests/Serval.E2ETests/ServalApiSlowTests.cs @@ -10,7 +10,7 @@ public class ServalApiSlowTests [SetUp] public void SetUp() { - _helperClient = new ServalClientHelper("https://machine.sil.org", ignoreSSLErrors: true); + _helperClient = new ServalClientHelper("https://serval-api.org/", ignoreSSLErrors: true); } [Test] diff --git a/tests/Serval.E2ETests/ServalApiTests.cs b/tests/Serval.E2ETests/ServalApiTests.cs index 090da202..833c8c83 100644 --- a/tests/Serval.E2ETests/ServalApiTests.cs +++ b/tests/Serval.E2ETests/ServalApiTests.cs @@ -9,7 +9,7 @@ public class ServalApiTests [SetUp] public void SetUp() { - _helperClient = new ServalClientHelper("https://serval-api.org", ignoreSSLErrors: true); + _helperClient = new ServalClientHelper("https://serval-api.org/", ignoreSSLErrors: true); } [Test]