Skip to content

Commit bcc565d

Browse files
Robin VAN DE MERGHELRobin-Van-de-Merghel
authored andcommitted
feat: Add legacy adaptor in CI
[3b20343] fix: Fixed ClientSelector to raise an error if the CS is badly configured [4b715ef] fix: Fixed doc, and went back on the client selector [ff736bb] docs: Add documentation to start the integration tests. [7107442] feat: Add legacy adaptor in CI [7ae3428] fix: Fixed to support better other branches [2066814] feat: Add diracx repo choice in the integration test [7859c6b] fix: Use JobStateUpdateClient instead of deactivating it [e3b1d68] fix: Resolved dirac-proxy-init issue with multi-VO [d2f99c0] fix: Resolved bug for setJobAttributes [fe09e19] fix: Fixed ClientWMS test and reversed TEST_DIRACX flag [1447a20] fix: Use opensearch db for job parametersDB [f6116aa] fix: Made mariadb works better [c4ab3f2] fix: Fixed CI to use sandoxes [a5a02bc] fix: Activate job endpoints in CI [a1bc1b1] fix: Applied changes to the ci workflows [282efcb] refactor: Moved down diracx installation to fix auth [ac22d9d] feat: Add DiracX tests into DIRAC CI, and use only latest tag. [0ebf454] fix: Moved up futureServices argument to prevent "Missing mandatory /DiracX/URL configuration" [d98b705] fix: Fixed small bug and refactored ClientSelector [8ce234c] fix: Small fixes with docker compose cmd and removed grep [08d9eb1] fix: Add filters to remove 'Client' from services name [bc44891] fix: Remove error when a future client is not available to warn instead [a1f553d] fix: Changed to make the legacy adaptor automatic. [3fb8c4d] fix: Fixed destroy command to support MacOSX [42df58d] fix: Going back, removed CI, and going to add it to DiracX [28b9789] fix: Small fix (forgot to change to docker**compose) [61384af] feat: Add in CI new legacy adapted services. [a3a7f5b] feat: Add legacy adaptor in CI
1 parent d8de07b commit bcc565d

File tree

16 files changed

+273
-65
lines changed

16 files changed

+273
-65
lines changed

.github/workflows/integration.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,8 @@ jobs:
3737
ARGS: DIRAC_USE_JSON_ENCODE=NO MYSQL_VER=mysql:8.0.40
3838
- TEST_NAME: "Backward Compatibility"
3939
ARGS: CLIENT_INSTALLATION_BRANCH=rel-v8r0 PILOT_INSTALLATION_BRANCH=rel-v8r0
40+
- TEST_NAME: "Test DiracX latest"
41+
ARGS: TEST_DIRACX=Yes
4042

4143
steps:
4244
- uses: actions/checkout@v4

docs/source/DeveloperGuide/CodeTesting/index.rst

Lines changed: 41 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -309,10 +309,8 @@ Running the above might take a while. Supposing you are interested in running on
309309
./integration_tests.py prepare-environment [FLAGS]
310310
./integration_tests.py install-server
311311
312-
which (in some minutes) will give you a fully dockerized server setup
313-
(`docker container ls` will list the created container, and you can see what's going on inside with the standard `docker exec -it server /bin/bash`.
314-
Now, suppose that you want to run `WorkloadManagementSystem/Test_JobDB.py`,
315-
the first thing to do is that you should first login in the docker container, by doing:
312+
which (in some minutes) will give you a fully dockerized server setup. `docker container ls` will list the created container, and you can see what's going on inside with the standard `docker exec -it server /bin/bash`.
313+
Now, suppose that you want to run `WorkloadManagementSystem/Test_JobDB.py`, the first thing to do is that you should first login in the docker container, by doing:
316314

317315
.. code-block:: bash
318316
@@ -326,7 +324,7 @@ Now you can run the test with:
326324
327325
pytest --no-check-dirac-environment LocalRepo/ALTERNATIVE_MODULES/DIRAC/tests/Integration/WorkloadManagementSystem/Test_JobDB.py
328326
329-
You can find the logs of the services in `/home/dirac/ServerInstallDIR/diracos/runit/`
327+
You can find the logs of the services in `/home/dirac/ServerInstallDIR/diracos/runit/`.
330328

331329
You can also login in client and mysql with:
332330

@@ -335,7 +333,45 @@ You can also login in client and mysql with:
335333
./integration_tests.py exec-client
336334
./integration_tests.py exec-mysql
337335
336+
To restart a service, you can go into the server docker, and run `runsvctrl`:
338337

338+
.. code-block:: bash
339+
340+
./integration_tests.py exec-server
341+
...
342+
runsvctrl t /home/dirac/ServerInstallDIR/diracos/runit/WorkloadManagement/JobMonitor/
343+
344+
And you can also restart all services (it can take some time):
345+
346+
.. code-block:: bash
347+
348+
./integration_tests.py exec-server
349+
...
350+
runsvctrl t /home/dirac/ServerInstallDIR/diracos/runit/*/*
351+
352+
353+
You can also test DiracX in integration tests. To do that, you have to provide in the `prepare-environment` command the following flag: `TEST_DIRACX=Yes`. It will run DiracX alongside DIRAC, and use the available and activated legacy adapted services.
354+
355+
To deactivate a service from being used with DiracX, you can add it in `integration_tests.py` in the `DIRACX_DISABLED_SERVICES` list:
356+
357+
.. code-block:: python
358+
359+
DIRACX_DISABLED_SERVICES = [
360+
"WorkloadManagement/JobMonitoring",
361+
]
362+
363+
By setting `TEST_DIRACX=Yes` only, it will take the last version of DiracX by default. If you want to provide your own, you have to build your DiracX project, and provide the `dist` folder path when calling `prepare-client`. This path has to be absolute.
364+
365+
.. code-block:: bash
366+
367+
./integration-tests.py prepare-client TEST_DIRACX=Yes --diracx-dist-dir my-dist-folder/
368+
369+
It will then mount your dist folder into DIRAC and DiracX (in `/diracx_sources`) to install the right dependencies.
370+
371+
For MacOS, there are two bugs that can be fixed.
372+
373+
- The first one is about `docker compose` not being recognized. To fix that, you can set in your environment variables `DOCKER_COMPOSE_CMD="docker-compose"`.
374+
- The second one, is for macs with M1 (or more recent ones) chips which are not supported by the `opensearch` docker image. By setting the `ES_PLATFORM` flag to `linux/arm64` you will be able to start `opensearch` without issue.
339375

340376
Validation and System tests
341377
---------------------------

integration_tests.py

Lines changed: 41 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,9 @@
2525
DEFAULT_HOST_OS = "el9"
2626
DEFAULT_MYSQL_VER = "mysql:8.4.4"
2727
DEFAULT_ES_VER = "opensearchproject/opensearch:2.18.0"
28+
# In MacOSX with Arm (MX), there's an issue with opensearch
29+
# You *must* set the ES_PLATFORM flag to `linux/arm64` to make it work.
30+
DEFAULT_ES_PLATFORM = "linux/amd64"
2831
DEFAULT_IAM_VER = "indigoiam/iam-login-service:v1.10.2"
2932
FEATURE_VARIABLES = {
3033
"DIRACOSVER": "master",
@@ -36,8 +39,12 @@
3639
"INSTALLATION_BRANCH": "",
3740
"DEBUG": "Yes",
3841
}
39-
DIRACX_OPTIONS = ()
4042
DEFAULT_MODULES = {"DIRAC": Path(__file__).parent.absolute()}
43+
# All services that have a FutureClient, but we *explicitly* deactivate
44+
# (for example if we did not finish to develop it)
45+
DIRACX_DISABLED_SERVICES = [
46+
"WorkloadManagement/JobMonitoring",
47+
]
4148

4249
# Static configuration
4350
DB_USER = "Dirac"
@@ -71,6 +78,10 @@
7178
}
7279
LOG_PATTERN = re.compile(r"^[\d\-]{10} [\d:]{8} UTC [^\s]+ ([A-Z]+):")
7380

81+
# In niche cases where we use MacOSX with Orbstack, some commands may not work with docker compose
82+
# If you're in that case, set in your environment `export DOCKER_COMPOSE_CMD="docker-compose"`
83+
DOCKER_COMPOSE_CMD = shlex.split(os.environ.get("DOCKER_COMPOSE_CMD", "docker compose"))
84+
7485

7586
class NaturalOrderGroup(typer.core.TyperGroup):
7687
"""Group for showing subcommands in the correct order"""
@@ -140,6 +151,19 @@ def list_commands(self, ctx):
140151
After restarting your terminal you command completion is available using:
141152
142153
typer ./integration_tests.py run ...
154+
155+
## DiracX
156+
157+
If you want to activate DiracX, you have to set the flag TEST_DIRACX to "Yes".
158+
It will search for legacy adapted services (services with a future client activated)
159+
and do the necessary to make DIRAC work alongside DiracX.
160+
161+
To deactivate a legacy adapted service (to pass CI for example), you have to add it in
162+
the `DIRACX_DISABLED_SERVICES` list. If you don't, the program will set this service to be used
163+
with DiracX, and if it is badly adapted, errors will be raised.
164+
165+
> Note that you can provide a DiracX project (repository, branch) by building it and providing
166+
the dist folder to the prepare-environment command.
143167
""",
144168
)
145169

@@ -193,8 +217,8 @@ def destroy():
193217
typer.secho("Shutting down and removing containers", err=True, fg=c.GREEN)
194218
with _gen_docker_compose(DEFAULT_MODULES) as docker_compose_fn:
195219
os.execvpe(
196-
"docker",
197-
["docker", "compose", "-f", docker_compose_fn, "down", "--remove-orphans", "-t", "0", "--volumes"],
220+
DOCKER_COMPOSE_CMD[0],
221+
[*DOCKER_COMPOSE_CMD, "-f", docker_compose_fn, "down", "--remove-orphans", "-t", "0", "--volumes"],
198222
_make_env({}),
199223
)
200224

@@ -253,7 +277,7 @@ def prepare_environment(
253277
typer.secho("Running docker compose to create containers", fg=c.GREEN)
254278
with _gen_docker_compose(modules, diracx_dist_dir=diracx_dist_dir) as docker_compose_fn:
255279
subprocess.run(
256-
["docker", "compose", "-f", docker_compose_fn, "up", "-d", "dirac-server", "dirac-client", "dirac-pilot"]
280+
[*DOCKER_COMPOSE_CMD, "-f", docker_compose_fn, "up", "-d", "dirac-server", "dirac-client", "dirac-pilot"]
257281
+ extra_services,
258282
check=True,
259283
env=docker_compose_env,
@@ -360,7 +384,7 @@ def prepare_environment(
360384
subStderr = open(docker_compose_fn_final / "stderr", "w")
361385

362386
subprocess.Popen(
363-
["docker", "compose", "-f", docker_compose_fn_final / "docker-compose.yml", "up", "-d", "diracx"],
387+
[*DOCKER_COMPOSE_CMD, "-f", docker_compose_fn_final / "docker-compose.yml", "up", "-d", "diracx"],
364388
env=docker_compose_env,
365389
stdin=None,
366390
stdout=subStdout,
@@ -569,7 +593,7 @@ def _gen_docker_compose(modules, *, diracx_dist_dir=None):
569593
# Load the docker compose configuration and mount the necessary volumes
570594
input_fn = Path(__file__).parent / "tests/CI/docker-compose.yml"
571595
docker_compose = yaml.safe_load(input_fn.read_text())
572-
# diracx-wait-for-db needs the volume to be able to run the witing script
596+
# diracx-wait-for-db needs the volume to be able to run the waiting script
573597
for ctn in ("dirac-server", "dirac-client", "dirac-pilot", "diracx-wait-for-db"):
574598
if "volumes" not in docker_compose["services"][ctn]:
575599
docker_compose["services"][ctn]["volumes"] = []
@@ -619,7 +643,7 @@ def _gen_docker_compose(modules, *, diracx_dist_dir=None):
619643
def _check_containers_running(*, is_up=True):
620644
with _gen_docker_compose(DEFAULT_MODULES) as docker_compose_fn:
621645
running_containers = subprocess.run(
622-
["docker", "compose", "-f", docker_compose_fn, "ps", "-q", "-a"],
646+
[*DOCKER_COMPOSE_CMD, "-f", docker_compose_fn, "ps", "-q", "-a"],
623647
stdout=subprocess.PIPE,
624648
env=_make_env({}),
625649
# docker compose ps has a non-zero exit code when no containers are running
@@ -701,6 +725,7 @@ def _make_env(flags):
701725
else:
702726
env["MYSQL_ADMIN_COMMAND"] = "mysqladmin"
703727
env["ES_VER"] = flags.pop("ES_VER", DEFAULT_ES_VER)
728+
env["ES_PLATFORM"] = flags.pop("ES_PLATFORM", DEFAULT_ES_PLATFORM)
704729
env["IAM_VER"] = flags.pop("IAM_VER", DEFAULT_IAM_VER)
705730
if "CVMFS_DIR" not in env or not Path(env["CVMFS_DIR"]).is_dir():
706731
typer.secho(f"CVMFS_DIR environment value: {env.get('CVMFS_DIR', 'NOT SET')}", fg=c.YELLOW)
@@ -1163,10 +1188,16 @@ def _make_config(modules, flags, release_var, editable):
11631188
typer.secho(f"Required feature variable {key!r} is missing", err=True, fg=c.RED)
11641189
raise typer.Exit(code=1)
11651190

1166-
# If we test DiracX, enable all the options
1191+
# If we test DiracX, add specific config
11671192
if config["TEST_DIRACX"].lower() in ("yes", "true"):
1168-
for key in DIRACX_OPTIONS:
1169-
config[key] = "Yes"
1193+
if DIRACX_DISABLED_SERVICES:
1194+
# We link all disabled services
1195+
# config["DIRACX_DISABLED_SERVICES"] = "Service1 Service2 Service3 ..."
1196+
diracx_disabled_services = " ".join(DIRACX_DISABLED_SERVICES)
1197+
1198+
typer.secho(f"The following services won't be legacy adapted: {diracx_disabled_services}", fg="yellow")
1199+
1200+
config["DIRACX_DISABLED_SERVICES"] = diracx_disabled_services
11701201

11711202
config["TESTREPO"] = [f"/home/dirac/LocalRepo/TestCode/{name}" for name in modules]
11721203
config["ALTERNATIVE_MODULES"] = [f"/home/dirac/LocalRepo/ALTERNATIVE_MODULES/{name}" for name in modules]

src/DIRAC/ConfigurationSystem/Client/Helpers/Registry.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -457,7 +457,7 @@ def getVOForGroup(group):
457457
458458
:return: str
459459
"""
460-
return getVO() or gConfig.getValue(f"{gBaseRegistrySection}/Groups/{group}/VO", "")
460+
return gConfig.getValue(f"{gBaseRegistrySection}/Groups/{group}/VO", "") or getVO()
461461

462462

463463
def getIdPForGroup(group):

src/DIRAC/Core/Security/DiracX.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@
4747
def addTokenToPEM(pemPath, group):
4848
from DIRAC.Core.Base.Client import Client
4949

50-
vo = Registry.getVOMSVOForGroup(group)
50+
vo = Registry.getVOForGroup(group)
5151
if not vo:
5252
gLogger.error(f"ERROR: Could not find VO for group {group}, DiracX will not work!")
5353
disabledVOs = gConfig.getValue("/DiracX/DisabledVOs", [])

src/DIRAC/Core/Tornado/Client/ClientSelector.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@
1717
from DIRAC.Core.DISET.TransferClient import TransferClient
1818
from DIRAC.Core.Tornado.Client.TornadoClient import TornadoClient
1919

20-
2120
sLog = gLogger.getSubLogger(__name__)
2221

2322

@@ -82,6 +81,10 @@ def ClientSelector(disetClient, *args, **kwargs): # We use same interface as RP
8281
rpc = tornadoClient(*args, **kwargs)
8382
else:
8483
rpc = disetClient(*args, **kwargs)
84+
except NotImplementedError as e:
85+
# We catch explicitly NotImplementedError to avoid just printing "there's an error"
86+
# If we mis-configured the CS for legacy adapted services, we MUST have an error.
87+
raise e
8588
except Exception as e: # pylint: disable=broad-except
8689
# If anything went wrong in the resolution, we return default RPCClient
8790
# So the behaviour is exactly the same as before implementation of Tornado

src/DIRAC/Core/Utilities/Extensions.py

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,15 @@ def findServices(modules):
7373
return findModules(modules, "Service", "*Handler")
7474

7575

76+
def findFutureServices(modules):
77+
"""Find the legacy adapted services for one or more DIRAC extension(s)
78+
79+
:param list/str/module module: One or more Python modules or Python module names
80+
:returns: list of tuples of the form (SystemName, ServiceName)
81+
"""
82+
return findModules(modules, "FutureClient")
83+
84+
7685
@iterateThenSort
7786
def findDatabases(module):
7887
"""Find the DB SQL schema defintions for one or more DIRAC extension(s)
@@ -182,7 +191,7 @@ def parseArgs():
182191
parser = argparse.ArgumentParser()
183192
subparsers = parser.add_subparsers(required=True, dest="function")
184193
defaultExtensions = extensionsByPriority()
185-
for func in [findSystems, findAgents, findExecutors, findServices, findDatabases]:
194+
for func in [findSystems, findAgents, findExecutors, findServices, findDatabases, findFutureServices]:
186195
subparser = subparsers.add_parser(func.__name__)
187196
subparser.add_argument("--extensions", nargs="+", default=defaultExtensions)
188197
subparser.set_defaults(func=func)

src/DIRAC/WorkloadManagementSystem/Client/JobMonitoringClient.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,12 +14,14 @@
1414

1515
@createClient("WorkloadManagement/JobMonitoring")
1616
class JobMonitoringClient(Client):
17+
# Set to None to raise an error if this service is set as "legacy adapted"
18+
# See ClientSelector
19+
diracxClient = None
20+
1721
def __init__(self, **kwargs):
1822
super().__init__(**kwargs)
1923
self.setServer("WorkloadManagement/JobMonitoring")
2024

21-
diracxClient = futureJobMonitoringClient
22-
2325
@ignoreEncodeWarning
2426
def getJobsStatus(self, jobIDs):
2527
res = self._getRPC().getJobsStatus(jobIDs)

src/DIRAC/WorkloadManagementSystem/FutureClient/JobStateUpdateClient.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -77,11 +77,11 @@ def setJobApplicationStatus(self, jobID: str | int, appStatus: str, source: str
7777
def setJobAttribute(self, jobID: str | int, attribute: str, value: str):
7878
with DiracXClient() as api:
7979
if attribute == "Status":
80-
api.jobs.set_job_statuses(
80+
return api.jobs.set_job_statuses(
8181
{jobID: {datetime.now(tz=timezone.utc): {"Status": value}}},
8282
)
8383
else:
84-
api.jobs.patch_metadata({jobID: {attribute: value}})
84+
return api.jobs.patch_metadata({jobID: {attribute: value}})
8585

8686
@stripValueIfOK
8787
@convertToReturnValue

tests/CI/check_db_initialized.sh

Lines changed: 40 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,43 @@
11
#!/bin/bash
2-
dbMissing=true;
3-
allDBs=(AccountingDB FTS3DB JobDB JobLoggingDB PilotAgentsDB ProductionDB ProxyDB ReqDB ResourceManagementDB ResourceStatusDB SandboxMetadataDB StorageManagementDB TaskQueueDB TransformationDB)
4-
while ${dbMissing};
5-
do
6-
dbMissing=false;
7-
allExistingDBs=$(mysql -uDirac -pDirac -h mysql -P 3306 -e "show databases;");
8-
for db in "${allDBs[@]}";
9-
do
10-
if grep -q "${db}" <<< "${allExistingDBs}";
11-
then
12-
echo "${db} OK";
13-
else
14-
echo "${db} not created";
15-
dbMissing=true;
16-
fi;
17-
done;
18-
if ${dbMissing};
19-
then
20-
sleep 1;
2+
DB_USER="Dirac"
3+
DB_PASS="Dirac"
4+
DB_HOST="mysql"
5+
DB_PORT=3306
6+
DB_CMD=""
7+
8+
# Detect available client: maria or mysql
9+
if command -v mariadb >/dev/null 2>&1; then
10+
DB_CMD="mariadb -u${DB_USER} -p${DB_PASS} -h${DB_HOST} -P${DB_PORT}"
11+
elif command -v mysql >/dev/null 2>&1; then
12+
DB_CMD="mysql -u${DB_USER} -p${DB_PASS} -h${DB_HOST} -P${DB_PORT}"
13+
else
14+
echo "❌ Neither mysql nor mariadb client found in PATH."
15+
exit 1
16+
fi
17+
18+
echo "Using client: ${DB_CMD%% *}"
19+
20+
dbMissing=true
21+
allDBs=(
22+
AccountingDB FTS3DB JobDB JobLoggingDB PilotAgentsDB ProductionDB
23+
ProxyDB ReqDB ResourceManagementDB ResourceStatusDB
24+
SandboxMetadataDB StorageManagementDB TaskQueueDB TransformationDB
25+
)
26+
27+
while $dbMissing; do
28+
dbMissing=false
29+
allExistingDBs=$($DB_CMD -e "SHOW DATABASES;" 2>/dev/null)
30+
31+
for db in "${allDBs[@]}"; do
32+
if grep -q "^${db}$" <<<"$allExistingDBs"; then
33+
echo "${db} exists"
34+
else
35+
echo "⚠️ ${db} not created yet"
36+
dbMissing=true
2137
fi
38+
done
39+
40+
$dbMissing && sleep 1
2241
done
42+
43+
echo "🎉 All databases are present."

0 commit comments

Comments
 (0)