mirror of
https://github.com/containers/podman-compose.git
synced 2025-04-10 01:49:09 +02:00
Merge branch 'containers:main' into container_scaling_update
This commit is contained in:
commit
04b9982bc5
@ -1,7 +1,7 @@
|
||||
[MESSAGES CONTROL]
|
||||
# C0111 missing-docstring: missing-class-docstring, missing-function-docstring, missing-method-docstring, missing-module-docstrin
|
||||
# consider-using-with: we need it for color formatter pipe
|
||||
disable=too-many-lines,too-many-branches,too-many-locals,too-many-statements,too-many-arguments,too-many-instance-attributes,fixme,multiple-statements,missing-docstring,line-too-long,consider-using-f-string,consider-using-with,unnecessary-lambda-assignment
|
||||
disable=too-many-lines,too-many-branches,too-many-locals,too-many-statements,too-many-arguments,too-many-instance-attributes,fixme,multiple-statements,missing-docstring,line-too-long,consider-using-f-string,consider-using-with,unnecessary-lambda-assignment,broad-exception-caught
|
||||
# allow _ for ignored variables
|
||||
# allow generic names like a,b,c and i,j,k,l,m,n and x,y,z
|
||||
# allow k,v for key/value
|
||||
|
1
newsfragments/1165-PROJECT_NAME-interpolation.bugfix
Normal file
1
newsfragments/1165-PROJECT_NAME-interpolation.bugfix
Normal file
@ -0,0 +1 @@
|
||||
- Fixed interpolation of the environment variable **COMPOSE_PROJECT_NAME** when it is set from the top-level **name** value within the Compose file.
|
1
newsfragments/1165-project-name-evaluation-order.bugfix
Normal file
1
newsfragments/1165-project-name-evaluation-order.bugfix
Normal file
@ -0,0 +1 @@
|
||||
- Fixed project name evaluation order to match the order defined in the [compose spec](https://docs.docker.com/compose/how-tos/project-name/#set-a-project-name).
|
1
newsfragments/sigint-up.bugfix
Normal file
1
newsfragments/sigint-up.bugfix
Normal file
@ -0,0 +1 @@
|
||||
- Fixed handling SIGINT when running "up" command to shutdown gracefully
|
@ -1980,6 +1980,25 @@ class PodmanCompose:
|
||||
sys.exit(1)
|
||||
content = normalize(content)
|
||||
# log(filename, json.dumps(content, indent = 2))
|
||||
|
||||
# See also https://docs.docker.com/compose/how-tos/project-name/#set-a-project-name
|
||||
# **project_name** is initialized to the argument of the `-p` command line flag.
|
||||
if not project_name:
|
||||
project_name = self.environ.get("COMPOSE_PROJECT_NAME")
|
||||
if not project_name:
|
||||
project_name = content.get("name")
|
||||
if not project_name:
|
||||
project_name = dir_basename.lower()
|
||||
# More strict then actually needed for simplicity:
|
||||
# podman requires [a-zA-Z0-9][a-zA-Z0-9_.-]*
|
||||
project_name_normalized = norm_re.sub("", project_name)
|
||||
if not project_name_normalized:
|
||||
raise RuntimeError(f"Project name [{project_name}] normalized to empty")
|
||||
project_name = project_name_normalized
|
||||
|
||||
self.project_name = project_name
|
||||
self.environ.update({"COMPOSE_PROJECT_NAME": self.project_name})
|
||||
|
||||
content = rec_subs(content, self.environ)
|
||||
if isinstance(services := content.get('services'), dict):
|
||||
for service in services.values():
|
||||
@ -2012,19 +2031,6 @@ class PodmanCompose:
|
||||
log.debug(" ** merged:\n%s", json.dumps(compose, indent=2))
|
||||
# ver = compose.get('version')
|
||||
|
||||
if not project_name:
|
||||
project_name = compose.get("name")
|
||||
if project_name is None:
|
||||
# More strict then actually needed for simplicity:
|
||||
# podman requires [a-zA-Z0-9][a-zA-Z0-9_.-]*
|
||||
project_name = self.environ.get("COMPOSE_PROJECT_NAME", dir_basename.lower())
|
||||
project_name = norm_re.sub("", project_name)
|
||||
if not project_name:
|
||||
raise RuntimeError(f"Project name [{dir_basename}] normalized to empty")
|
||||
|
||||
self.project_name = project_name
|
||||
self.environ.update({"COMPOSE_PROJECT_NAME": self.project_name})
|
||||
|
||||
services = compose.get("services")
|
||||
if services is None:
|
||||
services = {}
|
||||
@ -2629,7 +2635,7 @@ async def compose_build(compose, args):
|
||||
status = 0
|
||||
for t in asyncio.as_completed(tasks):
|
||||
s = await t
|
||||
if s is not None:
|
||||
if s is not None and s != 0:
|
||||
status = s
|
||||
|
||||
return status
|
||||
@ -2794,9 +2800,22 @@ async def compose_up(compose: PodmanCompose, args):
|
||||
max_service_length = curr_length if curr_length > max_service_length else max_service_length
|
||||
|
||||
tasks = set()
|
||||
|
||||
async def handle_sigint():
|
||||
log.info("Caught SIGINT or Ctrl+C, shutting down...")
|
||||
try:
|
||||
log.info("Shutting down gracefully, please wait...")
|
||||
down_args = argparse.Namespace(**dict(args.__dict__, volumes=False))
|
||||
await compose.commands["down"](compose, down_args)
|
||||
except Exception as e:
|
||||
log.error("Error during shutdown: %s", e)
|
||||
finally:
|
||||
for task in tasks:
|
||||
task.cancel()
|
||||
|
||||
if sys.platform != 'win32':
|
||||
loop = asyncio.get_event_loop()
|
||||
loop.add_signal_handler(signal.SIGINT, lambda: [t.cancel("User exit") for t in tasks])
|
||||
loop.add_signal_handler(signal.SIGINT, lambda: asyncio.create_task(handle_sigint()))
|
||||
|
||||
for i, cnt in enumerate(compose.containers):
|
||||
# Add colored service prefix to output by piping output through sed
|
||||
|
0
tests/integration/build_fail_multi/__init__.py
Normal file
0
tests/integration/build_fail_multi/__init__.py
Normal file
3
tests/integration/build_fail_multi/bad/Dockerfile
Normal file
3
tests/integration/build_fail_multi/bad/Dockerfile
Normal file
@ -0,0 +1,3 @@
|
||||
FROM busybox
|
||||
|
||||
RUN false
|
8
tests/integration/build_fail_multi/docker-compose.yml
Normal file
8
tests/integration/build_fail_multi/docker-compose.yml
Normal file
@ -0,0 +1,8 @@
|
||||
version: "3"
|
||||
services:
|
||||
bad:
|
||||
build:
|
||||
context: bad
|
||||
good:
|
||||
build:
|
||||
context: good
|
3
tests/integration/build_fail_multi/good/Dockerfile
Normal file
3
tests/integration/build_fail_multi/good/Dockerfile
Normal file
@ -0,0 +1,3 @@
|
||||
FROM busybox
|
||||
#ensure that this build finishes second so that it has a chance to overwrite the return code
|
||||
RUN sleep 0.5
|
@ -0,0 +1,31 @@
|
||||
# SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
import os
|
||||
import unittest
|
||||
|
||||
from tests.integration.test_utils import RunSubprocessMixin
|
||||
from tests.integration.test_utils import podman_compose_path
|
||||
from tests.integration.test_utils import test_path
|
||||
|
||||
|
||||
def compose_yaml_path():
|
||||
""" "Returns the path to the compose file used for this test module"""
|
||||
base_path = os.path.join(test_path(), "build_fail_multi")
|
||||
return os.path.join(base_path, "docker-compose.yml")
|
||||
|
||||
|
||||
class TestComposeBuildFailMulti(unittest.TestCase, RunSubprocessMixin):
|
||||
def test_build_fail_multi(self):
|
||||
output, error = self.run_subprocess_assert_returncode(
|
||||
[
|
||||
podman_compose_path(),
|
||||
"-f",
|
||||
compose_yaml_path(),
|
||||
"build",
|
||||
# prevent the successful build from being cached to ensure it runs long enough
|
||||
"--no-cache",
|
||||
],
|
||||
expected_returncode=1,
|
||||
)
|
||||
self.assertIn("RUN false", str(output))
|
||||
self.assertIn("while running runtime: exit status 1", str(error))
|
@ -1,5 +1,7 @@
|
||||
version: "3"
|
||||
|
||||
name: my-project-name
|
||||
|
||||
services:
|
||||
env-test:
|
||||
image: busybox
|
||||
@ -8,3 +10,9 @@ services:
|
||||
ZZVAR1: myval1
|
||||
ZZVAR2: 2-$ZZVAR1
|
||||
ZZVAR3: 3-$ZZVAR2
|
||||
|
||||
project-name-test:
|
||||
image: busybox
|
||||
command: sh -c "echo $$PNAME"
|
||||
environment:
|
||||
PNAME: ${COMPOSE_PROJECT_NAME}
|
||||
|
@ -36,3 +36,54 @@ class TestComposeEnv(unittest.TestCase, RunSubprocessMixin):
|
||||
compose_yaml_path(),
|
||||
"down",
|
||||
])
|
||||
|
||||
"""
|
||||
Tests interpolation of COMPOSE_PROJECT_NAME in the podman-compose config,
|
||||
which is different from external environment variables because COMPOSE_PROJECT_NAME
|
||||
is a predefined environment variable generated from the `name` value in the top-level
|
||||
of the compose.yaml.
|
||||
|
||||
See also
|
||||
- https://docs.docker.com/reference/compose-file/interpolation/
|
||||
- https://docs.docker.com/reference/compose-file/version-and-name/#name-top-level-element
|
||||
- https://docs.docker.com/compose/how-tos/environment-variables/envvars/
|
||||
- https://github.com/compose-spec/compose-spec/blob/main/04-version-and-name.md
|
||||
"""
|
||||
|
||||
def test_project_name(self):
|
||||
try:
|
||||
output, _ = self.run_subprocess_assert_returncode([
|
||||
podman_compose_path(),
|
||||
"-f",
|
||||
compose_yaml_path(),
|
||||
"run",
|
||||
"project-name-test",
|
||||
])
|
||||
self.assertIn("my-project-name", str(output))
|
||||
finally:
|
||||
self.run_subprocess_assert_returncode([
|
||||
podman_compose_path(),
|
||||
"-f",
|
||||
compose_yaml_path(),
|
||||
"down",
|
||||
])
|
||||
|
||||
def test_project_name_override(self):
|
||||
try:
|
||||
output, _ = self.run_subprocess_assert_returncode([
|
||||
podman_compose_path(),
|
||||
"-f",
|
||||
compose_yaml_path(),
|
||||
"run",
|
||||
"-e",
|
||||
"COMPOSE_PROJECT_NAME=project-name-override",
|
||||
"project-name-test",
|
||||
])
|
||||
self.assertIn("project-name-override", str(output))
|
||||
finally:
|
||||
self.run_subprocess_assert_returncode([
|
||||
podman_compose_path(),
|
||||
"-f",
|
||||
compose_yaml_path(),
|
||||
"down",
|
||||
])
|
||||
|
@ -1,14 +1,18 @@
|
||||
version: "3"
|
||||
services:
|
||||
web1:
|
||||
container1:
|
||||
image: busybox
|
||||
command: httpd -f -p 80 -h /var/www/html
|
||||
command: ["busybox", "sleep", "infinity"]
|
||||
volumes:
|
||||
- type: bind
|
||||
source: ./docker-compose.yml
|
||||
target: /var/www/html/index.html
|
||||
source: ./host_test_text.txt
|
||||
target: /test_text.txt
|
||||
bind:
|
||||
selinux: z
|
||||
ports:
|
||||
- "8080:80"
|
||||
|
||||
container2:
|
||||
image: busybox
|
||||
command: ["busybox", "sleep", "infinity"]
|
||||
volumes:
|
||||
- type: bind
|
||||
source: ./host_test_text.txt
|
||||
target: /test_text.txt
|
||||
|
1
tests/integration/selinux/host_test_text.txt
Normal file
1
tests/integration/selinux/host_test_text.txt
Normal file
@ -0,0 +1 @@
|
||||
# There must be a source file in the host for volumes type: bind
|
58
tests/integration/selinux/test_podman_compose_selinux.py
Normal file
58
tests/integration/selinux/test_podman_compose_selinux.py
Normal file
@ -0,0 +1,58 @@
|
||||
# SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
import json
|
||||
import os
|
||||
import subprocess
|
||||
import unittest
|
||||
|
||||
from tests.integration.test_utils import RunSubprocessMixin
|
||||
from tests.integration.test_utils import podman_compose_path
|
||||
from tests.integration.test_utils import test_path
|
||||
|
||||
|
||||
class TestPodmanCompose(unittest.TestCase, RunSubprocessMixin):
|
||||
def test_selinux(self):
|
||||
# test if when using volumes type:bind with selinux:z option, container ackquires a
|
||||
# respective host:source:z mapping in CreateCommand list
|
||||
compose_path = os.path.join(test_path(), "selinux", "docker-compose.yml")
|
||||
try:
|
||||
# change working directory to where docker-compose.yml file is so that containers can
|
||||
# directly access host source file for mounting from that working directory
|
||||
subprocess.run(
|
||||
[
|
||||
podman_compose_path(),
|
||||
"-f",
|
||||
compose_path,
|
||||
"up",
|
||||
"-d",
|
||||
"container1",
|
||||
"container2",
|
||||
],
|
||||
cwd=os.path.join(test_path(), 'selinux'),
|
||||
)
|
||||
out, _ = self.run_subprocess_assert_returncode([
|
||||
"podman",
|
||||
"inspect",
|
||||
"selinux_container1_1",
|
||||
])
|
||||
inspect_out = json.loads(out)
|
||||
create_command_list = inspect_out[0].get("Config", []).get("CreateCommand", {})
|
||||
self.assertIn('./host_test_text.txt:/test_text.txt:z', create_command_list)
|
||||
|
||||
out, _ = self.run_subprocess_assert_returncode([
|
||||
"podman",
|
||||
"inspect",
|
||||
"selinux_container2_1",
|
||||
])
|
||||
inspect_out = json.loads(out)
|
||||
create_command_list = inspect_out[0].get("Config", []).get("CreateCommand", {})
|
||||
self.assertIn('./host_test_text.txt:/test_text.txt', create_command_list)
|
||||
finally:
|
||||
out, _ = self.run_subprocess_assert_returncode([
|
||||
podman_compose_path(),
|
||||
"-f",
|
||||
compose_path,
|
||||
"down",
|
||||
"-t",
|
||||
"0",
|
||||
])
|
Loading…
Reference in New Issue
Block a user