Skip to content

Commit eeebf25

Browse files
author
Alan Christie
committed
feat: Initial logic to support docker-compose v2
1 parent 409937d commit eeebf25

File tree

5 files changed

+94
-58
lines changed

5 files changed

+94
-58
lines changed

.github/workflows/build.yaml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,9 +35,9 @@ jobs:
3535
- '3.10'
3636
steps:
3737
- name: Checkout
38-
uses: actions/checkout@v3
38+
uses: actions/checkout@v4
3939
- name: Set up Python ${{ matrix.python-version }}
40-
uses: actions/setup-python@v3
40+
uses: actions/setup-python@v4
4141
with:
4242
python-version: ${{ matrix.python-version }}
4343
- name: Install requirements

.github/workflows/publish.yaml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -29,11 +29,11 @@ jobs:
2929
runs-on: ubuntu-latest
3030
steps:
3131
- name: Checkout
32-
uses: actions/checkout@v3
32+
uses: actions/checkout@v4
3333
- name: Inject slug/short variables
34-
uses: rlespinasse/github-slug-action@v3.x
34+
uses: rlespinasse/github-slug-action@v4
3535
- name: Set up Python
36-
uses: actions/setup-python@v3
36+
uses: actions/setup-python@v4
3737
with:
3838
python-version: '3.11'
3939
- name: Install dependencies

README.rst

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -336,8 +336,8 @@ Installation
336336
This is a Python 3 utility, so try to run it from a recent (ideally 3.10)
337337
Python environment.
338338

339-
To use the utility you will need to have installed `Docker`_ and,
340-
if you want to test nextflow jobs, `nextflow`_.
339+
To use the utility you will need to have installed `Docker`_, `docker-compose`,
340+
and, if you want to test nextflow jobs, `nextflow`_.
341341

342342
.. _PyPI: https://pypi.org/project/im-jote/
343343
.. _Docker: https://docs.docker.com/get-docker/

requirements.txt

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
docker-compose == 1.29.2
21
im-data-manager-job-decoder == 1.17.2
32
munch == 2.5.0
43
wheel == 0.40.0

src/jote/compose.py

Lines changed: 87 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,15 @@
88
This module is designed to simulate the actions of the Data Manager
99
and Job Operator that are running in the DM kubernetes deployment.
1010
"""
11+
12+
import contextlib
1113
import copy
1214
import os
1315
import shutil
1416
import subprocess
17+
import sys
1518
import time
16-
from typing import Any, Dict, Optional, Tuple
19+
from typing import Any, Dict, List, Optional, Tuple
1720

1821
# The 'simulated' instance directory,
1922
# created by the Data Manager prior to launching the corresponding Job.
@@ -59,13 +62,44 @@
5962
"""
6063

6164

65+
def _get_docker_compose_command() -> str:
66+
# Try 'docker compose' (v2) and then 'docker-compose' (v1)
67+
# we need one or the other.
68+
dc_command: str = ""
69+
try:
70+
_ = subprocess.run(
71+
["docker", "compose", "version"],
72+
capture_output=True,
73+
check=False,
74+
timeout=4,
75+
)
76+
dc_command = "docker compose"
77+
except FileNotFoundError:
78+
with contextlib.suppress(FileNotFoundError):
79+
_ = subprocess.run(
80+
["docker-compose", "version"],
81+
capture_output=True,
82+
check=False,
83+
timeout=4,
84+
)
85+
dc_command = "docker-compose"
86+
if not dc_command:
87+
print("ERROR: Neither 'docker compose' nor 'docker-compose' has been found")
88+
print("One of these is required.")
89+
print("Please install one of them.")
90+
sys.exit(1)
91+
92+
assert dc_command
93+
return dc_command
94+
95+
6296
def _get_docker_compose_version() -> str:
63-
result = subprocess.run(
64-
["docker-compose", "version"], capture_output=True, check=False, timeout=4
65-
)
97+
dc_command = _get_docker_compose_command()
98+
version_cmd: List[str] = dc_command.split() + ["version"]
99+
result = subprocess.run(version_cmd, capture_output=True, check=False, timeout=4)
66100

67101
# stdout will contain the version on the first line: -
68-
# "docker-compose version 1.29.2, build unknown"
102+
# "docker-compose version v1.29.2, build unknown"
69103
# Ignore the first 23 characters of the first line...
70104
return str(result.stdout.decode("utf-8").split("\n")[0][23:])
71105

@@ -77,10 +111,12 @@ def get_test_root() -> str:
77111

78112

79113
class Compose:
80-
"""A class handling the execution of 'docker-compose'
114+
"""A class handling the execution of 'docker compose'
81115
for an individual test.
82116
"""
83117

118+
# The docker-compose command (for the first test)
119+
_COMPOSE_COMMAND: Optional[str] = None
84120
# The docker-compose version (for the first test)
85121
_COMPOSE_VERSION: Optional[str] = None
86122

@@ -144,10 +180,14 @@ def create(self) -> str:
144180
if os.path.exists(test_path):
145181
shutil.rmtree(test_path)
146182

183+
# Do we have the command?
184+
if not Compose._COMPOSE_COMMAND:
185+
Compose._COMPOSE_COMMAND = _get_docker_compose_command()
186+
print(f"# Compose command: {Compose._COMPOSE_COMMAND}")
147187
# Do we have the docker-compose version the user's installed?
148188
if not Compose._COMPOSE_VERSION:
149189
Compose._COMPOSE_VERSION = _get_docker_compose_version()
150-
print(f"# Compose: docker-compose ({Compose._COMPOSE_VERSION})")
190+
print(f"# Compose version: {Compose._COMPOSE_VERSION}")
151191

152192
# Make the test directory
153193
# (where the test is launched from)
@@ -159,15 +199,8 @@ def create(self) -> str:
159199
os.makedirs(inst_path)
160200

161201
# Run as a specific user/group ID?
162-
if self._user_id is not None:
163-
user_id = self._user_id
164-
else:
165-
user_id = os.getuid()
166-
if self._group_id is not None:
167-
group_id = self._group_id
168-
else:
169-
group_id = os.getgid()
170-
202+
user_id = self._user_id if self._user_id is not None else os.getuid()
203+
group_id = self._group_id if self._group_id is not None else os.getgid()
171204
# Write the Docker compose content to a file in the test directory
172205
additional_environment: str = ""
173206
if self._test_environment:
@@ -214,10 +247,11 @@ def run(
214247
caller along with the stdout and stderr content.
215248
A non-zero exit code does not necessarily mean the test has failed.
216249
"""
250+
assert Compose._COMPOSE_COMMAND
217251

218252
execution_directory: str = self.get_test_path()
219253

220-
print('# Compose: Executing the test ("docker-compose up")...')
254+
print(f'# Compose: Executing the test ("{Compose._COMPOSE_COMMAND} up")...')
221255
print(f'# Compose: Execution directory is "{execution_directory}"')
222256

223257
cwd = os.getcwd()
@@ -237,23 +271,24 @@ def run(
237271
# we set the prefix for the network name and can use compose files
238272
# from different directories. Without this the network name
239273
# is prefixed by the directory the compose file is in.
274+
up_cmd: List[str] = Compose._COMPOSE_COMMAND.split() + [
275+
"-p",
276+
"data-manager",
277+
"up",
278+
"--exit-code-from",
279+
"job",
280+
"--abort-on-container-exit",
281+
]
240282
test = subprocess.run(
241-
[
242-
"docker-compose",
243-
"-p",
244-
"data-manager",
245-
"up",
246-
"--exit-code-from",
247-
"job",
248-
"--abort-on-container-exit",
249-
],
283+
up_cmd,
250284
capture_output=True,
251285
timeout=timeout_minutes * 60,
252286
check=False,
253287
env=env,
254288
)
289+
down_cmd: List[str] = Compose._COMPOSE_COMMAND.split() + ["down"]
255290
_ = subprocess.run(
256-
["docker-compose", "down"],
291+
down_cmd,
257292
capture_output=True,
258293
timeout=240,
259294
check=False,
@@ -279,9 +314,10 @@ def delete(self) -> None:
279314
def run_group_compose_file(compose_file: str, delay_seconds: int = 0) -> bool:
280315
"""Starts a group compose file in a detached state.
281316
The file is expected to be a compose file in the 'data-manager' directory.
282-
We pull the continer imag to reduce the 'docker-compose up' time
317+
We pull the container image to reduce the 'docker-compose up' time
283318
and then optionally wait for a period of seconds.
284319
"""
320+
assert Compose._COMPOSE_COMMAND
285321

286322
print("# Compose: Starting test group containers...")
287323

@@ -290,13 +326,13 @@ def run_group_compose_file(compose_file: str, delay_seconds: int = 0) -> bool:
290326
try:
291327
# Pre-pull the docker-compose images.
292328
# This saves start-up execution time.
329+
pull_cmd: List[str] = Compose._COMPOSE_COMMAND.split() + [
330+
"-f",
331+
os.path.join("data-manager", compose_file),
332+
"pull",
333+
]
293334
_ = subprocess.run(
294-
[
295-
"docker-compose",
296-
"-f",
297-
os.path.join("data-manager", compose_file),
298-
"pull",
299-
],
335+
pull_cmd,
300336
capture_output=False,
301337
check=False,
302338
)
@@ -306,16 +342,16 @@ def run_group_compose_file(compose_file: str, delay_seconds: int = 0) -> bool:
306342
# we set the prefix for the network name and services from this container
307343
# are visible to the test container. Without this the network name
308344
# is prefixed by the directory the compose file is in.
345+
up_cmd: List[str] = Compose._COMPOSE_COMMAND.split() + [
346+
"-f",
347+
os.path.join("data-manager", compose_file),
348+
"-p",
349+
"data-manager",
350+
"up",
351+
"-d",
352+
]
309353
_ = subprocess.run(
310-
[
311-
"docker-compose",
312-
"-f",
313-
os.path.join("data-manager", compose_file),
314-
"-p",
315-
"data-manager",
316-
"up",
317-
"-d",
318-
],
354+
up_cmd,
319355
capture_output=False,
320356
check=False,
321357
)
@@ -335,19 +371,20 @@ def stop_group_compose_file(compose_file: str) -> bool:
335371
"""Stops a group compose file.
336372
The file is expected to be a compose file in the 'data-manager' directory.
337373
"""
374+
assert Compose._COMPOSE_COMMAND
338375

339376
print("# Compose: Stopping test group containers...")
340377

341378
try:
342379
# Bring the compose file down...
380+
down_cmd: List[str] = Compose._COMPOSE_COMMAND.split() + [
381+
"-f",
382+
os.path.join("data-manager", compose_file),
383+
"down",
384+
"--remove-orphans",
385+
]
343386
_ = subprocess.run(
344-
[
345-
"docker-compose",
346-
"-f",
347-
os.path.join("data-manager", compose_file),
348-
"down",
349-
"--remove-orphans",
350-
],
387+
down_cmd,
351388
capture_output=False,
352389
timeout=240,
353390
check=False,

0 commit comments

Comments
 (0)