Repository: MatterMiners/tardis

Issue:
pyauditor install broken in latest docker image
After #319 was merged into master, I wanted to try out the latest version of C/T.

I noticed that loading the auditor plugin fails. I could track this back back to a broken install of pyauditor in the docker container:

- With the latest docker image the pyauditor module is not populated with the classes we export
```
$ docker run -it --rm --entrypoint /bin/sh matterminers/cobald-tardis:latest
/srv # python
Python 3.10.13 (main, Oct 19 2023, 06:08:04) [GCC 12.2.1 20220924] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import pyauditor
>>> dir(pyauditor)
['__doc__', '__file__', '__loader__', '__name__', '__package__', '__path__', '__spec__']
```

- With the latest tagged version it works (i.e. `AuditorClientBuilder` and other classes are visible)
```
$ docker run -it --rm --entrypoint /bin/sh matterminers/cobald-tardis:0.8.0
/srv # python
Python 3.10.13 (main, Oct 19 2023, 06:08:04) [GCC 12.2.1 20220924] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import pyauditor
>>> dir(pyauditor)
['AuditorClient', 'AuditorClientBlocking', 'AuditorClientBuilder', 'Component', 'Meta', 'Record', 'Score', '__all__', '__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__path__', '__spec__', 'pyauditor']
```

- However, installing the latest pyauditor version in a venv also works
```
$ python -m venv .venv
$ source .venv/bin/activate
$ pip install python-auditor
Successfully installed python-auditor-0.3.0
$ python
Python 3.11.5 (main, Sep  2 2023, 14:16:33) [GCC 13.2.1 20230801] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import pyauditor
>>> dir(pyauditor)
['AuditorClient', 'AuditorClientBlocking', 'AuditorClientBuilder', 'Component', 'Meta', 'Record', 'Score', '__all__', '__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__path__', '__spec__', 'pyauditor']
```
- I also checked with Python3.10 using the `python:3.10` container (as this is the version used by the C/T container), and there it also works


Any idea why this is happening?

Comments:

Patch:
diff --git a/CONTRIBUTORS b/CONTRIBUTORS
index 370201a..c68f844 100644
--- a/CONTRIBUTORS
+++ b/CONTRIBUTORS
@@ -11,8 +11,8 @@ Rene Caspart <rene.caspart@cern.ch>
 Leon Schuhmacher <ji7635@partner.kit.edu>
 R. Florian von Cube <florian.voncube@gmail.com>
 mschnepf <matthias.schnepf@kit.edu>
-Alexander Haas <104835302+haasal@users.noreply.github.com>
 Benjamin Rottler <benjamin.rottler@cern.ch>
+Alexander Haas <104835302+haasal@users.noreply.github.com>
 mschnepf <maschnepf@schnepf-net.de>
 Dirk Sammel <dirk.sammel@cern.ch>
 Matthias J. Schnepf <maschnepf@schnepf-net.de>
@@ -21,4 +21,5 @@ LGTM Migrator <lgtm-migrator@users.noreply.github.com>
 Matthias Schnepf <maschnepf@schnepf-net.de>
 PSchuhmacher <leon_schuhmacher@yahoo.de>
 Peter Wienemann <peter.wienemann@uni-bonn.de>
+Raphael Kleinemühl <r.kleinemuehl@posteo.de>
 rfvc <florian.voncube@gmail.com>
diff --git a/docs/source/changelog.rst b/docs/source/changelog.rst
index f9e7ec6..f11fe78 100644
--- a/docs/source/changelog.rst
+++ b/docs/source/changelog.rst
@@ -1,4 +1,4 @@
-.. Created by changelog.py at 2023-11-10, command
+.. Created by changelog.py at 2023-11-25, command
    '/Users/giffler/.cache/pre-commit/repor6pnmwlm/py_env-python3.10/bin/changelog docs/source/changes compile --categories Added Changed Fixed Security Deprecated --output=docs/source/changelog.rst'
    based on the format of 'https://keepachangelog.com/'
 
@@ -6,9 +6,14 @@
 CHANGELOG
 #########
 
-[Unreleased] - 2023-11-10
+[Unreleased] - 2023-11-25
 =========================
 
+Changed
+-------
+
+* Enable support for SSH command restrictions in Moab adapter
+
 Fixed
 -----
 
diff --git a/docs/source/changes/322.add_support_for_ssh_command_restrictions_moab.yaml b/docs/source/changes/322.add_support_for_ssh_command_restrictions_moab.yaml
new file mode 100644
index 0000000..2156f60
--- /dev/null
+++ b/docs/source/changes/322.add_support_for_ssh_command_restrictions_moab.yaml
@@ -0,0 +1,7 @@
+category: changed
+summary: "Enable support for SSH command restrictions in Moab adapter"
+description: |
+  The NEMO HPC is going to enable 2FA on the login nodes and SSH can be restricted to certain commands only. This 
+  requires to avoid `&&` and `$(whoami)` in commands.
+pull requests:
+- 322
diff --git a/setup.py b/setup.py
index 01a5e31..3643bcc 100644
--- a/setup.py
+++ b/setup.py
@@ -92,7 +92,7 @@ setup(
         "pydantic<2.0.0",
         "asyncstdlib",
         "typing_extensions",
-        "python-auditor==0.3.0",
+        "python-auditor==0.3.1",
         "tzlocal",
         *REST_REQUIRES,
     ],
diff --git a/tardis/adapters/sites/moab.py b/tardis/adapters/sites/moab.py
index b918423..fd71dbb 100644
--- a/tardis/adapters/sites/moab.py
+++ b/tardis/adapters/sites/moab.py
@@ -31,12 +31,16 @@ logger = logging.getLogger("cobald.runtime.tardis.adapters.sites.moab")
 async def showq(
     *resource_attributes: Tuple[AttributeDict, ...], executor: Executor
 ) -> Iterable[Mapping]:
-    cmd = "showq --xml -w user=$(whoami) && showq -c --xml -w user=$(whoami)"
+    showq_active_cmd = "showq --xml -w user=$(USER)"
+    showq_completed_cmd = "showq -c --xml -w user=$(USER)"
     logger.debug("Moab status update is running.")
-    response = await executor.run_command(cmd)
+    combined_response_stdout = ""
+    for cmd in (showq_active_cmd, showq_completed_cmd):
+        response = await executor.run_command(cmd)
+        combined_response_stdout += response.stdout
     # combine two XML outputs to one
     xml_output = minidom.parseString(
-        response["stdout"].replace("\n", "").replace("</Data><Data>", "")
+        combined_response_stdout.replace("\n", "").replace("</Data><Data>", "")
     )
     xml_jobs_list = xml_output.getElementsByTagName("queue")
     # parse XML output


Test patch:
diff --git a/tests/adapters_t/sites_t/test_moab.py b/tests/adapters_t/sites_t/test_moab.py
index c948cc4..5a0c104 100644
--- a/tests/adapters_t/sites_t/test_moab.py
+++ b/tests/adapters_t/sites_t/test_moab.py
@@ -9,7 +9,7 @@ from tests.utilities.utilities import mock_executor_run_command
 from tests.utilities.utilities import run_async
 
 from unittest import TestCase
-from unittest.mock import MagicMock, patch
+from unittest.mock import MagicMock, call, patch
 
 from datetime import datetime
 from warnings import filterwarnings
@@ -283,6 +283,7 @@ class TestMoabAdapter(TestCase):
 
     @mock_executor_run_command(TEST_RESOURCE_STATE_TRANSLATION_RESPONSE)
     def test_resource_state_translation(self):
+        self.mock_executor.reset_mock()
         for num, (_, state) in enumerate(STATE_TRANSLATIONS):
             job_id = f"76242{num:02}"
             return_resource_attributes = run_async(
@@ -291,9 +292,13 @@ class TestMoabAdapter(TestCase):
             )
             self.assertEqual(return_resource_attributes.resource_status, state)
 
-        self.mock_executor.return_value.run_command.assert_called_with(
-            "showq --xml -w user=$(whoami) && showq -c --xml -w user=$(whoami)"
-        )
+            self.mock_executor.return_value.run_command.assert_has_calls(
+                [
+                    call("showq --xml -w user=$(USER)"),
+                    call("showq -c --xml -w user=$(USER)"),
+                ]
+            )
+            self.mock_executor.reset_mock()
 
     @mock_executor_run_command(TEST_RESOURCE_STATUS_RESPONSE_RUNNING)
     def test_resource_status_update(self):