diff --git a/gso/services/lso_client.py b/gso/services/lso_client.py index 1529ca1fb520d78193e787eaf24ed0d0b3d1067d..9280cbc4dbcbb6d1bb4d111c1f341b22fb0114b7 100644 --- a/gso/services/lso_client.py +++ b/gso/services/lso_client.py @@ -17,6 +17,7 @@ from orchestrator.workflow import Step, StepList, begin, callback_step, conditio from pydantic import ConfigDict from pydantic_forms.types import FormGenerator from pydantic_forms.validators import Label, LongText, ReadOnlyField +from unidecode import unidecode from gso import settings @@ -42,6 +43,9 @@ def _send_request(parameters: dict, callback_route: str) -> None: parameters: JSON body for the request, which will almost always at least consist of a subscription object, and a boolean value to indicate a dry run. callback_route: The callback route that should be used to resume the workflow. + + Raises: + HTTPError: When receiving a non-successful status code from LSO. """ oss = settings.load_oss_params() params = oss.PROVISIONING_PROXY @@ -106,12 +110,13 @@ def _execute_playbook( callback_route: The endpoint at which GSO expects a callback to continue the workflow executing this step. inventory: An inventory of machines at which the playbook is targeted. Must be in YAML-compatible format. extra_vars: Any extra variables that the playbook relies on. This can include a subscription object, a boolean - value indicating a dry run, a commit comment, etc. + value indicating a dry run, a commit comment, etc. All unicode character values are decoded to prevent + sending special characters to remote machines that don't support this. """ parameters = { "playbook_name": playbook_name, "inventory": inventory, - "extra_vars": extra_vars, + "extra_vars": json.loads(unidecode(json.dumps(extra_vars, ensure_ascii=False))), } _send_request(parameters, callback_route) diff --git a/requirements.txt b/requirements.txt index 2488de134a518a88436df822e7a57e5519ee8935..97fd1fedbe08dc83073afba7953b608a5fd2a189 100644 --- a/requirements.txt +++ b/requirements.txt @@ -8,6 +8,7 @@ celery==5.3.6 azure-identity==1.16.0 msgraph-sdk==1.2.0 ping3==4.0.8 +unidecode==1.3.8 # Test and linting dependencies celery-stubs==0.1.3 diff --git a/setup.py b/setup.py index fa21476f6b401a98419fb8e80aa30a5a8b822eab..bce5bfc27bfdab4845b989f98d31dc31f276530f 100644 --- a/setup.py +++ b/setup.py @@ -21,6 +21,7 @@ setup( "azure-identity==1.16.0", "msgraph-sdk==1.2.0", "ping3==4.0.8", + "unidecode==1.3.8", ], include_package_data=True, ) diff --git a/test/services/test_lso_client.py b/test/services/test_lso_client.py new file mode 100644 index 0000000000000000000000000000000000000000..2f52072a723c8656f79a2ea5abb0ee6127920cc7 --- /dev/null +++ b/test/services/test_lso_client.py @@ -0,0 +1,28 @@ +from unittest.mock import patch + +from gso.services.lso_client import _execute_playbook + + +@patch("gso.services.lso_client.requests.post") +def test_replace_unicode_in_lso_call_success(mock_post): + extra_vars = { + "deployment_description": "I am going to deploy the best GÉANT service EVER!!", + "email": "goat@géant.org", + "translations": {"ja": "ジェアントã®ã‚¹ã‚´ã‚¤ãªã‚µãƒ¼ãƒ“スをデプãƒã‚¤ã™ã‚‹"}, + } + + expected_parameters = { + "playbook_name": "playbook.yaml", + "inventory": {}, + "callback": "http://gso-api:9000/api/callback_route", + "extra_vars": { + "deployment_description": "I am going to deploy the best GEANT service EVER!!", + "email": "goat@geant.org", + "translations": {"ja": "zieantonosugoinasa-bisuwodepuroisuru"}, + }, + } + + execute_playbook = _execute_playbook.__wrapped__ + execute_playbook("playbook.yaml", "/api/callback_route", {}, extra_vars) + + mock_post.assert_called_once_with("https://localhost:44444/api/playbook", json=expected_parameters, timeout=10)