85 Commits
v1.4.0 ... main

Author SHA1 Message Date
2ed50b9538 Merge pull request #1258 from mokibit/add-env-var-interpolation-to-keys
Implement environment variable interpolation to YAML dictionary keys
2025-07-01 00:15:05 +03:00
e97d446a04 Implement environment variable interpolation to YAML dictionary keys
`podman-compose` currently does not support interpolating environment
variables in dictionary keys, despite the compose file specification
indicating this capability.
See the relevant compose-spec documentation:
https://github.com/compose-spec/compose-spec/blob/main/12-interpolation.md

This feature is useful in `labels` or `environment` sections, where keys
can be user-defined strings. To enable interpolation, an alternate equal
sign syntax must be used, e.g.:
services:
  foo:
    labels:
      - "$VAR_NAME=label_value"

After this PR `podman-compose` will align more closely with the compose
file specification, allowing for the interpolation of environment
variables in dictionary keys.

Signed-off-by: Monika Kairaityte <monika@kibit.lt>
2025-06-30 23:54:27 +03:00
9fe6e7f284 Merge pull request #1253 from whym/yaml-parse-error
Hide stack trace shown on YAML parse error by default
2025-06-30 16:06:11 +03:00
764efd360c Hide stack trace shown on YAML parse error by default
Fixes https://github.com/containers/podman-compose/issues/1139

Signed-off-by: Yusuke Matsubara <whym@whym.org>
2025-06-30 15:33:25 +03:00
b06224389e Merge pull request #1257 from astrojuanlu/migrate-pep-621
Migrate to PEP 621
2025-06-30 15:31:10 +03:00
0e37b31e45 Adapt release script
Signed-off-by: Juan Luis Cano Rodríguez <hello@juanlu.space>
2025-06-29 20:57:28 +02:00
3918f7e5f8 Add requires-python lower boundary
Signed-off-by: Juan Luis Cano Rodríguez <hello@juanlu.space>
2025-06-29 20:57:28 +02:00
d7cd02e3e9 Migrate to SPDX identifier for the license
Signed-off-by: Juan Luis Cano Rodríguez <hello@juanlu.space>
2025-06-29 20:57:28 +02:00
05f341b3c5 Python 2 is EOL
Signed-off-by: Juan Luis Cano Rodríguez <hello@juanlu.space>
2025-06-29 20:57:28 +02:00
f8d05babd7 Migrate to PEP 621
Signed-off-by: Juan Luis Cano Rodríguez <hello@juanlu.space>
2025-06-29 20:57:28 +02:00
8eb55735e9 Merge pull request #1250 from mokibit/fix-formatting-systemd-cmd-help
Fix formatting of description and help of `systemd` command
2025-06-27 11:22:02 +03:00
1c0c63aaf2 Fix formatting of description of systemd command
When running "podman-compose", the list of commands gets displayed.
The "systemd" command is an outlier, showing multiple lines, unintended
at this location.

This change moves the longer command description to its proper place,
that is, it gets shown when "podman-compose systemd --help" is
executed.

Signed-off-by: Cleber Rosa <crosa@redhat.com
Signed-off-by: Monika Kairaityte <monika@kibit.lt>
2025-06-27 11:15:34 +03:00
2f8dbdcd09 Remove assignment to variable that gets overriden and never used
Signed-off-by: Cleber Rosa <crosa@redhat.com>
Signed-off-by: Monika Kairaityte <monika@kibit.lt>
2025-06-26 20:54:54 +03:00
e789d98bf0 Merge pull request #1249 from p12tic/update-contributing
Update CONTRIBUTING.md
2025-06-26 14:59:42 +03:00
0de04b32bb CONTRIBUTING: Suggest contributors to split their commits
Signed-off-by: Povilas Kanapickas <povilas@radix.lt>
2025-06-26 14:37:43 +03:00
d864e195ce CONTRIBUTING: Update instructions on commit message
Signed-off-by: Povilas Kanapickas <povilas@radix.lt>
2025-06-26 14:37:41 +03:00
96ec9617f1 CONTRIBUTING: Update instructions for creating virtualenv
Signed-off-by: Povilas Kanapickas <povilas@radix.lt>
2025-06-26 14:37:35 +03:00
b48317c22b Merge pull request #1248 from mokibit/add-short-syntax-for-env-variables
Implement short syntax for env variables in compose.yml "environment:"
2025-06-26 14:21:52 +03:00
0cbf70a4e9 Implement short syntax for env variables in compose.yml "environment:"
This commit allows compose file to directly use environment variable
values in "environment:" section when variables were set in `.env` file.
This functionality was missing, as docker-compose supports both: short
and variable interpolation syntax forms:
environment:
	- FOO
and
environment:
	- FOO=${FOO}
Relevant docker-compose documentation:
https://docs.docker.com/compose/how-tos/environment-variables/set-environment-variables/
podman-compose is more compatible with docker-compose after this change.

Signed-off-by: Monika Kairaityte <monika@kibit.lt>
2025-06-26 12:40:39 +03:00
7105198ae1 Merge pull request #1247 from whym/log-formatter-bug
Fixes #1237: Remove unnecessary 'or "text"'
2025-06-25 22:45:13 +03:00
8f9f6d0657 Remove unnecessary 'or "text"'
Fixes #1237

Signed-off-by: Yusuke Matsubara <whym@whym.org>
2025-06-25 21:53:34 +09:00
61392e9cba Merge pull request #1243 from mokibit/fix-dockerfile-definition
Fix dockerfile definition if directory name ends with ".git"
2025-06-17 19:53:15 +03:00
dd471c8918 Fix dockerfile definition if directory name ends with ".git"
After changes in 92f0a8583a, the
dockerfile parameter is igored if the (local) work directory's name ends
in `.git`.
This commit fixes the regression and adds more tests.

Signed-off-by: Monika Kairaityte <monika@kibit.lt>
2025-06-17 19:43:29 +03:00
1113c833da Merge pull request #1242 from uosis/docker-compat
Add docker_compose_compat setting
2025-06-16 12:04:47 +03:00
4177bae807 Add docker_compose_compat setting
Signed-off-by: Uosis <uosisl+github@gmail.com>
2025-06-15 18:31:06 -06:00
fa2252801a Merge pull request #1241 from uosis/name-separator
Add support for using hyphens for name separation
2025-06-15 21:04:19 +03:00
6635b8b570 cleanup bool parsing
Signed-off-by: Uosis <uosisl+github@gmail.com>
2025-06-13 19:20:09 -06:00
8f55227167 add name_separator_compat
Signed-off-by: Uosis <uosisl+github@gmail.com>
2025-06-13 19:20:09 -06:00
9cde3993f2 Merge pull request #1238 from uosis/env-var-override
Add support for providing x-podman settings using environment variables
2025-06-11 22:38:25 +03:00
04155d0d09 Add documentation for env variables
Signed-off-by: Povilas Kanapickas <povilas@radix.lt>
2025-06-11 22:31:56 +03:00
605495233e allow overriding x-podman using env vars 2025-06-11 22:27:45 +03:00
3e579f65f0 Merge pull request #1235 from p12tic/readme
Update README
2025-06-05 18:34:40 +03:00
034b86ea73 README: Remove basic usage section
There are plenty of resources on compose format, no need to repeat
anything. Beginners won't be helped by the examples and experienced
users will look into better places anyway.

Signed-off-by: Povilas Kanapickas <povilas@radix.lt>
2025-06-05 18:24:19 +03:00
bbdb63604e README: Update section on tests
Signed-off-by: Povilas Kanapickas <povilas@radix.lt>
2025-06-05 18:24:18 +03:00
93371b0f4e README: Merge all repositories under single section
Signed-off-by: Povilas Kanapickas <povilas@radix.lt>
2025-06-05 18:24:17 +03:00
27d1fc67a0 README: Add installation instructions on Debian
Signed-off-by: Povilas Kanapickas <povilas@radix.lt>
2025-06-05 18:24:16 +03:00
77bc9c5602 Merge pull request #1234 from p12tic/release-notes
Release notes for 1.4.1
2025-06-05 17:20:19 +03:00
82dd0acab2 Release notes for 1.4.1
Signed-off-by: Povilas Kanapickas <povilas@radix.lt>
2025-06-05 17:12:49 +03:00
0e4f686f4b Merge pull request #1231 from mokibit/fix-volume-bind-source
Fix relative host path resolution for volume bind mount source
2025-06-05 16:56:18 +03:00
0491269f53 Fix relative host path resolution for volume bind mount source
e03d675b9b broke relative host path
resolution by deleting os.chdir(). After this commit current working
directory is not relevant anymore.

Fixes e03d675b9b.

Signed-off-by: Monika Kairaityte <monika@kibit.lt>
2025-06-05 16:46:07 +03:00
ee90712843 Merge pull request #1210 from jarovo/main
Add relabel option to secrets
2025-06-05 13:02:18 +03:00
82d7622c45 Add relabel option to secrets
On selinux enabled system, the secrets cannot be read without proper
relabeling or correct policy being set.

This patch enables user to instruc podman-copose to use :z or :Z podman
volume options to make podman relabel the file under bind-mount.

More info here:
https://unix.stackexchange.com/questions/728801/host-wide-consequences-of-setting-selinux-z-z-option-on-container-bind-mounts?rq=1

Signed-off-by: Jaroslav Henner <1187265+jarovo@users.noreply.github.com>
2025-06-05 00:13:58 +02:00
4c6df85efa Merge pull request #1224 from p12tic/podman-label
Expose io.podman.compose.service label
2025-05-29 22:18:42 +03:00
7b3276e5d7 Expose io.podman.compose.service label
Signed-off-by: Povilas Kanapickas <povilas@radix.lt>
2025-05-29 22:01:35 +03:00
fc6bfc9931 Merge pull request #1223 from p12tic/github-mypy
.github: Run mypy as part of checks
2025-05-29 21:38:59 +03:00
949af2a50c .github: Run mypy as part of checks
Signed-off-by: Povilas Kanapickas <povilas@radix.lt>
2025-05-29 21:23:21 +03:00
b650efbb33 Merge pull request #1222 from mokibit/print-full-dockerfile-path
Print full Dockerfile path instead of context on error
2025-05-28 17:34:45 +03:00
0b8b483cb7 Print full dockerfile path instead of context on error
Current error message does not provide the exact path where the
Dockerfile is being searched.

This commit distinguishes two types of errors when Dockerfile is not
found in build context:
- when the context does not have provided custom-named Dockerfile, the
path in the error message is absolute: "OSError: Dockerfile not found in
.../podman-compose/tests/integration/build_fail/context_no_file/custom-named-Dockerfile"
- when the context does not have any Dockerfile, the path in the error
message is: "OSError: Dockerfile not found in
.../podman-compose/tests/integration/build_fail/context_no_file"
Only this error message was used before the fix.

Signed-off-by: Monika Kairaityte <monika@kibit.lt>
Co-authored-by: Siteshwar Vashisht <svashisht@redhat.com>
2025-05-28 16:19:38 +03:00
0dcc864fdd Merge pull request #1205 from mokibit/add-missing-init-py-files
tests/integration: Add missing __init__.py files to actually run tests and fix the broken ones
2025-05-26 22:27:48 +03:00
56238b10e3 tests/integration: Fix service_scale tests
Signed-off-by: Monika Kairaityte <monika@kibit.lt>
2025-05-26 18:19:11 +03:00
d4ebf62e0e tests/integration: Fix lifetime tests
Signed-off-by: Monika Kairaityte <monika@kibit.lt>
2025-05-26 18:19:11 +03:00
83c7e9462e tests/integration: Add missing __init__.py files to actually run tests
Signed-off-by: Monika Kairaityte <monika@kibit.lt>
2025-05-24 22:09:30 +03:00
835e3abe95 Merge pull request #1220 from p12tic/types
Enforce types using mypy
2025-05-24 17:35:52 +03:00
248a63ebb0 test-requirements: Upgrade ruff
Signed-off-by: Povilas Kanapickas <povilas@radix.lt>
2025-05-24 17:26:42 +03:00
efea0ee652 Address unused argument warnings
Signed-off-by: Povilas Kanapickas <povilas@radix.lt>
2025-05-24 17:26:31 +03:00
3c2978c9ca examples: Add type annotations
Signed-off-by: Povilas Kanapickas <povilas@radix.lt>
2025-05-24 17:19:29 +03:00
5765e5306b Use correct logging methods
Signed-off-by: Povilas Kanapickas <povilas@radix.lt>
2025-05-24 17:19:29 +03:00
0be50ffdfb Fix return value from compose_systemd()
Signed-off-by: Povilas Kanapickas <povilas@radix.lt>
2025-05-24 17:19:29 +03:00
1eae76ddca Add return type annotations to test_utils.py
Signed-off-by: Povilas Kanapickas <povilas@radix.lt>
2025-05-24 17:19:29 +03:00
6c46678082 Fix mypy warnings
Signed-off-by: Povilas Kanapickas <povilas@radix.lt>
2025-05-24 17:19:29 +03:00
a3f48f830d tests/integration: Add type annotations
Signed-off-by: Povilas Kanapickas <povilas@radix.lt>
2025-05-24 17:11:38 +03:00
dedb081550 tests/unit: Add type annotations
Signed-off-by: Povilas Kanapickas <povilas@radix.lt>
2025-05-24 17:11:36 +03:00
ea22227625 Add mypy configuration
Signed-off-by: Povilas Kanapickas <povilas@radix.lt>
2025-05-24 17:11:33 +03:00
6b2665683c Merge pull request #1211 from p12tic/tests-importable-directories
Move tests to directories that can be imported
2025-05-24 17:09:03 +03:00
58df8497aa Move tests to directories that can be imported
Signed-off-by: Povilas Kanapickas <povilas@radix.lt>
2025-05-24 16:47:49 +03:00
741cb008c8 Merge pull request #1213 from p12tic/fixes
Miscellaneous code quality fixes
2025-05-24 16:05:18 +03:00
39e21d8c11 Remove extraneous await on non-async function
Signed-off-by: Povilas Kanapickas <povilas@radix.lt>
2025-05-24 15:29:53 +03:00
02166f584a Use more standard call to list.append
Signed-off-by: Povilas Kanapickas <povilas@radix.lt>
2025-05-24 15:29:53 +03:00
8aeeafb98c Rename redefined variables
Signed-off-by: Povilas Kanapickas <povilas@radix.lt>
2025-05-24 15:29:53 +03:00
9162fe6438 Remove unused code
Signed-off-by: Povilas Kanapickas <povilas@radix.lt>
2025-05-24 15:29:49 +03:00
cc10a61017 Merge pull request #1218 from mokibit/fix-build-ssh-path
Fix build ssh path to be relative to directory of compose file
2025-05-24 15:25:31 +03:00
08d06df0f2 Fix build ssh path to be relative to directory of compose file
Signed-off-by: Monika Kairaityte <monika@kibit.lt>
2025-05-24 12:58:59 +03:00
c26e188991 Merge pull request #1214 from mokibit/fix-cmd-healthcheck
Fix CMD healthchecks running with `/bin/sh`
2025-05-21 15:52:43 +03:00
a983129e88 tests/unit: Add unit tests for fixing CMD healthcheck
Signed-off-by: Monika Kairaityte <monika@kibit.lt>
2025-05-20 16:54:25 +03:00
76b3055934 Fix CMD healthchecks running with /bin/sh
Signed-off-by: Ben Krieger <ben.krieger@intel.com>
2025-05-20 16:54:25 +03:00
f5e3162e91 Merge pull request #1212 from p12tic/normalize-depends-unittest
tests: Rewrite test_normalize_depends_on to unittest
2025-05-19 18:26:45 +03:00
225999eab1 tests: Rewrite test_normalize_depends_on to unittest
This test was forgotten about during initial migration.

Signed-off-by: Povilas Kanapickas <povilas@radix.lt>
2025-05-19 17:56:28 +03:00
b86f8b1d61 Merge pull request #1201 from Norbiros/feat/custom-pod-name
Allow specifying custom pod name in `--in-pod`
2025-05-18 18:59:58 +03:00
3d47849d28 Allow specifying custom pod name in --in-pod
Fixes #958: missing or incorrect use of --in-pod and pod-args
Fixes #693: --in-pod 'name' no function

Signed-off-by: norbiros <norbiros@protonmail.com>
Signed-off-by: Norbiros <norbiros@protonmail.com>
2025-05-18 18:46:15 +03:00
bfaf77a506 Merge pull request #1208 from mokibit/reset-depends-on
Fix reset tag attribute to also reset `depends_on` parameter
2025-05-18 18:42:52 +03:00
0c1c4ffea9 Fix reset tag attribute to also reset depends_on parameter
Signed-off-by: Monika Kairaityte <monika@kibit.lt>
2025-05-16 22:33:00 +03:00
98f065b3e2 Merge pull request #1203 from mokibit/fix-test-paths-for-reset-override-tags
tests/integration: Fix paths to compose.yaml files for testing override and reset tags
2025-05-16 22:23:14 +03:00
6e30673260 tests/integration: Fix paths for testing override and reset tags
Tests were not run due to missing __init__.py files.
This commit adds missing files and fixes paths to compose.yaml files.

Signed-off-by: Monika Kairaityte <monika@kibit.lt>
2025-05-16 21:51:11 +03:00
9c09789948 Merge pull request #1193 from marat2509/main
feat(systemd): add unregister command to remove systemd service registration
2025-05-13 02:22:37 +03:00
01214fa013 Add unregister command to remove systemd service registration
Signed-off-by: marat2509 <marat2509@users.noreply.github.com>
2025-05-12 22:53:09 +03:00
136 changed files with 2164 additions and 834 deletions

View File

@ -14,12 +14,19 @@ jobs:
options: --privileged --cgroupns=host
steps:
- uses: actions/checkout@v4
- name: Analysing the code with ruff
- name: Install dependencies
run: |
set -e
pip install -r test-requirements.txt
- name: Analysing the code using ruff
run: |
set -e
ruff format --check
ruff check
- name: Analysing the code using mypy
run: |
set -e
mypy .
- name: Analysing the code with pylint
run: |
pylint podman_compose.py

View File

@ -19,11 +19,12 @@ Note: Some steps are OPTIONAL but all are RECOMMENDED.
$ cd podman-compose
```
2. (OPTIONAL) Create a Python virtual environment. Example using
[virtualenv wrapper](https://virtualenvwrapper.readthedocs.io/en/latest/):
2. (OPTIONAL) Create a Python virtual environment. Example using python builtin
`venv` module:
```shell
$ mkvirtualenv podman-compose
$ python3 -m venv .venv
$ . .venv/bin/activate
```
3. Install the project runtime and development requirements:
@ -60,9 +61,25 @@ Note: Some steps are OPTIONAL but all are RECOMMENDED.
- Make sure you include a `Signed-off-by` message in your commits.
Read [this guide](https://github.com/containers/common/blob/main/CONTRIBUTING.md#sign-your-prs)
to learn how to sign your commits.
- In the commit message, reference the Issue ID that your code fixes and a brief description of
the changes.
Example: `Fixes #516: Allow empty network`
- In the commit message body, reference the Issue ID that your code fixes and a brief description of the changes.
Example:
```
Allow empty network
<description, such as links to the compose spec and so on>
Fixes https://github.com/containers/podman-compose/issues/516
```
- If your commit requires a refactoring, first do the refactoring and
commit it separately before starting feature work. This makes the
pull request easier to review. Additionally, pull request will be
less risky, because if it breaks something, it's way easier to
isolate the offending code, understand what's broken and fix it.
Due to the latter reason it's best to commit in as many independent
commits as reasonable.
This will result in pull requests being merged much faster.
9. Open a pull request to `containers/podman-compose` and wait for a maintainer to review your work.
## Adding new commands

View File

@ -68,7 +68,23 @@ Or latest development version from GitHub:
pip3 install https://github.com/containers/podman-compose/archive/main.tar.gz
```
### Homebrew
### Package repositories
podman-compose is available from the following package repositories:
Debian:
```bash
sudo apt install podman-compose
```
Fedora (starting from f31) repositories:
```bash
sudo dnf install podman-compose
```
Homebrew:
```bash
brew install podman-compose
@ -94,51 +110,22 @@ curl -o ~/.local/bin/podman-compose https://raw.githubusercontent.com/containers
chmod +x ~/.local/bin/podman-compose
```
or install from Fedora (starting from f31) repositories:
```bash
sudo dnf install podman-compose
```
## Basic Usage
We have included fully functional sample stacks inside `examples/` directory.
You can get more examples from [awesome-compose](https://github.com/docker/awesome-compose).
A quick example would be
```bash
cd examples/busybox
podman-compose --help
podman-compose up --help
podman-compose up
```
A more rich example can be found in [examples/awx3](examples/awx3)
which have
- A Postgres Database
- RabbitMQ server
- MemCached server
- a django web server
- a django tasks
When testing the `AWX3` example, if you got errors, just wait for db migrations to end.
There is also AWX 17.1.0
## Tests
Inside `tests/` directory we have many useless docker-compose stacks
that are meant to test as many cases as we can to make sure we are compatible
podman-compose is tested via unit and integration tests.
### Unit tests with unittest
run a unittest with following command
Unit tests can be run via the following:
```shell
python3 -m unittest discover tests/unit
```
Integration tests can be run via the following:
```shell
python3 -m unittest discover tests/integration
```
# Contributing guide
If you are a user or a developer and want to contribute please check the [CONTRIBUTING](CONTRIBUTING.md) section

7
docs/Changelog-1.4.1.md Normal file
View File

@ -0,0 +1,7 @@
Version 1.4.1 (2025-06-05)
==========================
Bug fixes
---------
- Fixed relative host path resolution for volume bind mount source

View File

@ -27,6 +27,22 @@ services:
For explanations of these extensions, please refer to the [Podman Documentation](https://docs.podman.io/).
## Secrets
The following extension keys are available under `secret` configuration:
x-podman.relabel - Configure SELinux relabeling
For example, the following configures custom-secret to use mount with private and unshared content.
Only the current container can use a private volume.
```yml
secrets:
custom-secret:
x-podman.relabel: Z
```
For explanations of these extensions, please refer to the [podman-run --volume documentation](https://docs.podman.io/en/latest/markdown/podman-run.1.html#volume-v-source-volume-host-dir-container-dir-options)).
## Network management
The following extension keys are available under network configuration:
@ -123,6 +139,44 @@ The options to the network modes are passed to the `--network` option of the `po
as-is.
## Docker Compose Compatibility
podman-compose aims to be compatible with docker-compose, but there are some differences in
behavior and features. The following sections describe how to enable compatibility with docker-compose
and how to handle some of the differences.
Compatibility settings can either be set explicitly as described below, or by setting the `docker_compose_compat` meta
settings to `true` under the global `x-podman` key:
```yaml
x-podman:
docker_compose_compat: true
```
This will enable all compatibility settings described below, and is equivalent to setting each of them to `true`.
This setting can also be changed by setting the `PODMAN_COMPOSE_DOCKER_COMPOSE_COMPAT` environment variable.
## Compatibility of name separators between docker-compose and podman-compose
Currently, podman-compose is using underscores (`_` character) as a separator in names of
containers, images, etc., while docker-compose has switched to hyphens (`-` character). This setting
allows to switch podman-compose to use hyphens as well.
To enable compatibility between docker-compose and podman-compose, specify
`name_separator_compat: true` under global `x-podman` key:
```
x-podman:
name_separator_compat: true
```
By default `name_separator_compat` is `false`. This will change to `true` at some point and the
setting will be removed.
This setting can also be changed by setting `PODMAN_COMPOSE_NAME_SEPARATOR_COMPAT` environment
variable.
## Compatibility of default network names between docker-compose and podman-compose
Current versions of podman-compose may produce different default external network names than
@ -140,6 +194,9 @@ x-podman:
By default `default_net_name_compat` is `false`. This will change to `true` at some point and the
setting will be removed.
This setting can also be changed by setting `PODMAN_COMPOSE_DEFAULT_NET_NAME_COMPAT` environment
variable.
## Compatibility of default network behavior between docker-compose and podman-compose
When there is no network defined (neither network-mode nor networks) in service,
@ -160,6 +217,9 @@ x-podman:
default_net_behavior_compat: true
```
This setting can also be changed by setting `PODMAN_COMPOSE_DEFAULT_NET_BEHAVIOR_COMPAT` environment
variable.
## Custom pods management
Podman-compose can have containers in pods. This can be controlled by extension key x-podman in_pod.
@ -179,6 +239,9 @@ x-podman:
in_pod: false
```
This setting can also be changed by setting `PODMAN_COMPOSE_IN_POD` environment
variable.
It is also possible to override the default arguments for pod creation that are
used when --pod-args is not passed on the command line:
```yml
@ -192,3 +255,6 @@ x-podman:
```
When not set in docker-compose.yml or on the command line, the pod args default
to `["--infra=false", "--share="]`.
This setting can also be changed by setting `PODMAN_COMPOSE_POD_ARGS` environment
variable.

View File

@ -3,8 +3,8 @@
import asyncio # noqa: F401
import os
import aioredis
from aiohttp import web
import aioredis # type: ignore[import-not-found]
from aiohttp import web # type: ignore[import-not-found]
REDIS_HOST = os.environ.get("REDIS_HOST", "localhost")
REDIS_PORT = int(os.environ.get("REDIS_PORT", "6379"))
@ -16,13 +16,13 @@ routes = web.RouteTableDef()
@routes.get("/")
async def hello(request): # pylint: disable=unused-argument
async def hello(request: web.Request) -> web.Response: # pylint: disable=unused-argument
counter = await redis.incr("mycounter")
return web.Response(text=f"counter={counter}")
@routes.get("/hello.json")
async def hello_json(request): # pylint: disable=unused-argument
async def hello_json(request: web.Request) -> web.Response: # pylint: disable=unused-argument
counter = await redis.incr("mycounter")
data = {"counter": counter}
return web.json_response(data)
@ -31,7 +31,7 @@ async def hello_json(request): # pylint: disable=unused-argument
app.add_routes(routes)
def main():
def main() -> None:
web.run_app(app, port=8080)

View File

@ -0,0 +1 @@
- Add unregister command to remove systemd service registration (`podman-compose systemd -a unregister`)

View File

@ -0,0 +1 @@
- Change behaviour of `--in-pod` to handle custom pod names instead of only disabling pod feature

View File

@ -0,0 +1 @@
- Add new docker_compose_compat x-podman meta setting to enable all Docker Compose compatibility settings

View File

@ -0,0 +1 @@
Add support for environment variable interpolation for YAML keys.

View File

@ -0,0 +1 @@
Fixed build ssh path to a local SSH key, to be relative to the directory of compose file.

View File

@ -0,0 +1 @@
Fixed support for CMD healthchecks to run using the given command directly and not using `/bin/sh -c`.

View File

@ -0,0 +1 @@
Fixed regression of dockerfile definition if working directory name ends with ".git".

View File

@ -0,0 +1 @@
Implemented short syntax for environment variables set in `.env` for compose.yml "environment:" section.

View File

@ -0,0 +1 @@
Fixed regression of log output including "text" in detached mode.

View File

@ -0,0 +1 @@
Hide the stack trace on a YAML parse error.

View File

@ -0,0 +1 @@
Added `io.podman.compose.service` label to created containers. It contains the same value as com.docker.compose.service.

View File

@ -0,0 +1 @@
- Add new name_separator_compat x-podman setting to change name separator to hyphen, same as Docker Compose

View File

@ -0,0 +1 @@
- Add relabel option to secret to make possible to read the secret file by the contained process.

View File

@ -0,0 +1 @@
- Add support for setting x-podman values using PODMAN_COMPOSE_* environment variables.

File diff suppressed because it is too large Load Diff

View File

@ -1,3 +1,59 @@
[build-system]
build-backend = "setuptools.build_meta"
requires = ["setuptools"]
[project]
name = "podman-compose"
authors = [
{ email = "alsadi@gmail.com", name = "Muayyad Alsadi" },
]
description = "A script to run docker-compose.yml using podman"
dependencies = [
"python-dotenv",
"pyyaml",
]
requires-python = ">=3.9"
classifiers = [
"Development Status :: 3 - Alpha",
"Intended Audience :: Developers",
"Operating System :: OS Independent",
"Programming Language :: Python",
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3.9",
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11",
"Programming Language :: Python :: 3.12",
"Programming Language :: Python :: 3.13",
"Topic :: Software Development :: Build Tools",
]
keywords = [
"podman",
"podman-compose",
]
license = "GPL-2.0-only"
dynamic = ["version", "readme"]
[project.urls]
homepage = "https://github.com/containers/podman-compose"
[project.optional-dependencies]
devel = [
"coverage",
"parameterized",
"pre-commit",
"ruff",
]
[project.scripts]
podman-compose = "podman_compose:main"
[tool.setuptools]
py-modules = ["podman_compose"]
[tool.setuptools.dynamic]
readme = {file = ["README.md"], content-type = "text/markdown"}
version = {attr = "podman_compose.__version__"}
[tool.ruff]
line-length = 100
target-version = "py38"
@ -53,3 +109,21 @@ quote-style = "preserve"
directory = "misc"
name = "Misc"
showcontent = true
[tool.mypy]
python_version = "3.9"
namespace_packages = true
explicit_package_bases = true
pretty = true
warn_redundant_casts = true
disallow_untyped_calls = false
disallow_untyped_defs = true
no_implicit_optional = true
mypy_path = "$MYPY_CONFIG_FILE_DIR"
exclude = "build"
[[tool.mypy.overrides]]
module = [
"parameterized.*",
]
ignore_missing_imports = true

View File

@ -1,6 +1,5 @@
#!/usr/bin/env bash
./scripts/uninstall.sh
./scripts/clean_up.sh
python3 setup.py register
python3 setup.py sdist bdist_wheel
pyproject-build
twine upload dist/*

View File

@ -1,11 +1,5 @@
[bdist_wheel]
universal = 1
[metadata]
version = attr: podman_compose.__version__
[flake8]
# The GitHub editor is 127 chars wide
max-line-length=127
# These are not being followed yet
ignore=E222,E231,E272,E713,W503
ignore=E222,E231,E272,E713,W503

View File

@ -1,49 +0,0 @@
# SPDX-License-Identifier: GPL-2.0
import os
from setuptools import setup
try:
README = open(os.path.join(os.path.dirname(__file__), "README.md"), encoding="utf-8").read()
except: # noqa: E722 # pylint: disable=bare-except
README = ""
setup(
name="podman-compose",
description="A script to run docker-compose.yml using podman",
long_description=README,
long_description_content_type="text/markdown",
classifiers=[
"Programming Language :: Python",
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3.9",
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11",
"Programming Language :: Python :: 3.12",
"Programming Language :: Python :: 3.13",
"Intended Audience :: Developers",
"Operating System :: OS Independent",
"Development Status :: 3 - Alpha",
"Topic :: Software Development :: Build Tools",
"License :: OSI Approved :: GNU General Public License v2 (GPLv2)",
],
keywords="podman, podman-compose",
author="Muayyad Alsadi",
author_email="alsadi@gmail.com",
url="https://github.com/containers/podman-compose",
py_modules=["podman_compose"],
entry_points={"console_scripts": ["podman-compose = podman_compose:main"]},
include_package_data=True,
license="GPL-2.0-only",
install_requires=[
"pyyaml",
"python-dotenv",
],
extras_require={"devel": ["ruff", "pre-commit", "coverage", "parameterized"]},
# test_suite='tests',
# tests_require=[
# 'coverage',
# 'tox',
# ]
)

View File

@ -1,10 +1,15 @@
-e .
coverage==7.4.3
cryptography==44.0.3
parameterized==0.9.0
pytest==8.0.2
tox==4.13.0
ruff==0.3.1
mypy==1.15.0
ruff==0.11.11
pylint==3.1.0
types-PyYAML==6.0.12.20250402
types-requests==2.32.0.20250328
types-setuptools==80.7.0.20250516
# The packages below are transitive dependencies of the packages above and are included here
# to make testing reproducible.
@ -23,6 +28,7 @@ filelock==3.13.1
iniconfig==2.0.0
isort==5.13.2
mccabe==0.7.0
mypy_extensions==1.1.0
packaging==23.2
platformdirs==4.2.0
pluggy==1.4.0
@ -31,4 +37,5 @@ python-dotenv==1.0.1
PyYAML==6.0.1
requests
tomlkit==0.12.4
typing_extensions==4.13.2
virtualenv==20.26.6

View File

@ -2,7 +2,7 @@ import os
import subprocess
def create_base_test_image():
def create_base_test_image() -> None:
subprocess.check_call(
['podman', 'build', '-t', 'nopush/podman-compose-test', '.'],
cwd=os.path.join(os.path.dirname(__file__), "base_image"),

View File

@ -10,7 +10,7 @@ from tests.integration.test_utils import podman_compose_path
from tests.integration.test_utils import test_path
def compose_yaml_path(failure_order):
def compose_yaml_path(failure_order: str) -> str:
return os.path.join(test_path(), "abort", f"docker-compose-fail-{failure_order}.yaml")
@ -25,7 +25,7 @@ class TestComposeAbort(unittest.TestCase, RunSubprocessMixin):
("exit", "none", 0),
("failure", "none", 0),
])
def test_abort(self, abort_type, failure_order, expected_exit_code):
def test_abort(self, abort_type: str, failure_order: str, expected_exit_code: int) -> None:
try:
self.run_subprocess_assert_returncode(
[

View File

@ -11,13 +11,13 @@ from tests.integration.test_utils import podman_compose_path
from tests.integration.test_utils import test_path
def compose_yaml_path():
def compose_yaml_path() -> str:
""" "Returns the path to the compose file used for this test module"""
return os.path.join(test_path(), "additional_contexts", "project")
class TestComposeBuildAdditionalContexts(unittest.TestCase):
def test_build_additional_context(self):
def test_build_additional_context(self) -> None:
"""podman build should receive additional contexts as --build-context
See additional_context/project/docker-compose.yaml for context paths

View File

@ -0,0 +1 @@
test

View File

@ -3,3 +3,12 @@ services:
test:
build: ./context
image: build-fail-img
test_no_dockerfile:
build:
context: ./context_no_file
image: busybox
test_no_custom_dockerfile:
build:
context: ./context_no_file
dockerfile: Dockerfile-alt
image: busybox

View File

@ -22,9 +22,48 @@ class TestComposeBuildFail(unittest.TestCase, RunSubprocessMixin):
"-f",
compose_yaml_path(),
"build",
"test",
],
expected_returncode=127,
)
self.assertIn("RUN this_command_does_not_exist", str(output))
self.assertIn("this_command_does_not_exist: not found", str(error))
self.assertIn("while running runtime: exit status 127", str(error))
def test_dockerfile_does_not_exist(self):
out, error = self.run_subprocess_assert_returncode(
[
podman_compose_path(),
"-f",
compose_yaml_path(),
"build",
"test_no_dockerfile",
],
expected_returncode=1,
)
error = error.decode('utf-8')
result = '\n'.join(error.splitlines()[-1:])
expected_path = os.path.join(os.path.dirname(__file__), "context_no_file")
expected = f'OSError: Dockerfile not found in {expected_path}'
self.assertEqual(expected, result)
def test_custom_dockerfile_does_not_exist(self):
out, error = self.run_subprocess_assert_returncode(
[
podman_compose_path(),
"-f",
compose_yaml_path(),
"build",
"test_no_custom_dockerfile",
],
expected_returncode=1,
)
error = error.decode('utf-8')
result = '\n'.join(error.splitlines()[-1:])
expected_path = os.path.join(os.path.dirname(__file__), "context_no_file/Dockerfile-alt")
expected = f'OSError: Dockerfile not found in {expected_path}'
self.assertEqual(expected, result)

View File

@ -10,7 +10,7 @@ from tests.integration.test_utils import podman_compose_path
from tests.integration.test_utils import test_path
def compose_yaml_path(scenario):
def compose_yaml_path(scenario: str) -> str:
return os.path.join(
os.path.join(test_path(), "default_net_behavior"), f"docker-compose_{scenario}.yaml"
)
@ -27,13 +27,13 @@ class TestComposeDefaultNetBehavior(unittest.TestCase, RunSubprocessMixin):
('two_nets_compat', 'default_net_behavior_default'),
('with_default_compat', 'default_net_behavior_default'),
])
def test_nethost(self, scenario, default_net):
def test_nethost(self, scenario: str, default_net: str) -> None:
try:
self.run_subprocess_assert_returncode(
[podman_compose_path(), "-f", compose_yaml_path(scenario), "up", "-d"],
)
container_id, _ = self.run_subprocess_assert_returncode(
container_id_out, _ = self.run_subprocess_assert_returncode(
[
podman_compose_path(),
"-f",
@ -43,7 +43,7 @@ class TestComposeDefaultNetBehavior(unittest.TestCase, RunSubprocessMixin):
'{{.ID}}',
],
)
container_id = container_id.decode('utf-8').split('\n')[0]
container_id = container_id_out.decode('utf-8').split('\n')[0]
output, _ = self.run_subprocess_assert_returncode(
[
"podman",

View File

@ -9,12 +9,12 @@ from tests.integration.test_utils import podman_compose_path
from tests.integration.test_utils import test_path
def compose_yaml_path(suffix=""):
def compose_yaml_path(suffix: str = "") -> str:
return os.path.join(os.path.join(test_path(), "deps"), f"docker-compose{suffix}.yaml")
class TestComposeBaseDeps(unittest.TestCase, RunSubprocessMixin):
def test_deps(self):
def test_deps(self) -> None:
try:
output, _ = self.run_subprocess_assert_returncode([
podman_compose_path(),
@ -37,7 +37,7 @@ class TestComposeBaseDeps(unittest.TestCase, RunSubprocessMixin):
"down",
])
def test_run_nodeps(self):
def test_run_nodeps(self) -> None:
try:
output, _ = self.run_subprocess_assert_returncode([
podman_compose_path(),
@ -62,7 +62,7 @@ class TestComposeBaseDeps(unittest.TestCase, RunSubprocessMixin):
"down",
])
def test_up_nodeps(self):
def test_up_nodeps(self) -> None:
try:
self.run_subprocess_assert_returncode([
podman_compose_path(),
@ -89,7 +89,7 @@ class TestComposeBaseDeps(unittest.TestCase, RunSubprocessMixin):
"down",
])
def test_podman_compose_run(self):
def test_podman_compose_run(self) -> None:
"""
This will test depends_on as well
"""
@ -143,7 +143,7 @@ class TestComposeBaseDeps(unittest.TestCase, RunSubprocessMixin):
class TestComposeConditionalDeps(unittest.TestCase, RunSubprocessMixin):
def test_deps_succeeds(self):
def test_deps_succeeds(self) -> None:
suffix = "-conditional-succeeds"
try:
output, _ = self.run_subprocess_assert_returncode([
@ -167,7 +167,7 @@ class TestComposeConditionalDeps(unittest.TestCase, RunSubprocessMixin):
"down",
])
def test_deps_fails(self):
def test_deps_fails(self) -> None:
suffix = "-conditional-fails"
try:
output, _ = self.run_subprocess_assert_returncode([
@ -188,10 +188,10 @@ class TestComposeConditionalDeps(unittest.TestCase, RunSubprocessMixin):
class TestComposeConditionalDepsHealthy(unittest.TestCase, PodmanAwareRunSubprocessMixin):
def setUp(self):
def setUp(self) -> None:
self.podman_version = self.retrieve_podman_version()
def test_up_deps_healthy(self):
def test_up_deps_healthy(self) -> None:
suffix = "-conditional-healthy"
try:
self.run_subprocess_assert_returncode([
@ -261,6 +261,6 @@ class TestComposeConditionalDepsHealthy(unittest.TestCase, PodmanAwareRunSubproc
self.run_subprocess_assert_returncode([
podman_compose_path(),
"-f",
compose_yaml_path(),
compose_yaml_path(suffix),
"down",
])

View File

@ -1,2 +0,0 @@
ZZVAR1='This value is overwritten by env-file-tests/.env'
ZZVAR3='This value is loaded from env-file-tests/.env'

View File

@ -0,0 +1,2 @@
ZZVAR1='This value is overwritten by env_file_tests/.env'
ZZVAR3='This value is loaded from env_file_tests/.env'

View File

@ -1,2 +1,3 @@
ZZVAR1='This value is loaded but should be overwritten'
ZZVAR2='This value is loaded from .env in project/ directory'
ZZVAR3=TEST

View File

@ -0,0 +1,10 @@
services:
app:
image: nopush/podman-compose-test
command: ["/bin/busybox", "sh", "-c", "env | grep ZZVAR3"]
# 'env_file:' section is not used, so .env file is searched in the same directory as compose.yml
# file
environment:
# this is short syntax: podman-compose takes only this variable value from '.env' file and
# sends it to container environment
- ZZVAR3

View File

@ -8,12 +8,12 @@ from tests.integration.test_utils import podman_compose_path
from tests.integration.test_utils import test_path
def compose_base_path():
return os.path.join(test_path(), "env-file-tests")
def compose_base_path() -> str:
return os.path.join(test_path(), "env_file_tests")
class TestComposeEnvFile(unittest.TestCase, RunSubprocessMixin):
def test_path_env_file_inline(self):
def test_path_env_file_inline(self) -> None:
# Test taking env variable value directly from env-file when its path is inline path
base_path = compose_base_path()
path_compose_file = os.path.join(base_path, "project/container-compose.yaml")
@ -42,7 +42,7 @@ class TestComposeEnvFile(unittest.TestCase, RunSubprocessMixin):
"down",
])
def test_path_env_file_flat_in_compose_file(self):
def test_path_env_file_flat_in_compose_file(self) -> None:
# Test taking env variable value from env-file/project-1.env which was declared in
# compose file's env_file
base_path = compose_base_path()
@ -74,7 +74,7 @@ class TestComposeEnvFile(unittest.TestCase, RunSubprocessMixin):
"down",
])
def test_path_env_file_obj_in_compose_file(self):
def test_path_env_file_obj_in_compose_file(self) -> None:
# take variable value from env-file project-1.env which was declared in compose
# file's env_file by -path: ...
base_path = compose_base_path()
@ -106,7 +106,7 @@ class TestComposeEnvFile(unittest.TestCase, RunSubprocessMixin):
"down",
])
def test_exists_optional_env_file_path_in_compose_file(self):
def test_exists_optional_env_file_path_in_compose_file(self) -> None:
# test taking env variable values from several env-files when one of them is optional
# and exists
base_path = compose_base_path()
@ -139,7 +139,7 @@ class TestComposeEnvFile(unittest.TestCase, RunSubprocessMixin):
"down",
])
def test_missing_optional_env_file_path_in_compose_file(self):
def test_missing_optional_env_file_path_in_compose_file(self) -> None:
# test taking env variable values from several env-files when one of them is optional and
# is missing (silently skip it)
base_path = compose_base_path()
@ -173,7 +173,7 @@ class TestComposeEnvFile(unittest.TestCase, RunSubprocessMixin):
"down",
])
def test_var_value_inline_overrides_env_file_path_inline(self):
def test_var_value_inline_overrides_env_file_path_inline(self) -> None:
# Test overriding env value when value is declared in inline command
base_path = compose_base_path()
path_compose_file = os.path.join(base_path, "project/container-compose.yaml")
@ -204,7 +204,7 @@ class TestComposeEnvFile(unittest.TestCase, RunSubprocessMixin):
"down",
])
def test_taking_env_variables_from_env_files_from_different_directories(self):
def test_taking_env_variables_from_env_files_from_different_directories(self) -> None:
# FIXME: It is not clear what this test actually tests, but from README.md it looks like:
# Test overriding env values by directory env-files-tests/.env file values
# and only take value from project/.env, when it does not exist in env-files-tests/.env
@ -233,7 +233,7 @@ class TestComposeEnvFile(unittest.TestCase, RunSubprocessMixin):
[
'ZZVAR1=This value is loaded but should be overwritten\r',
'ZZVAR2=This value is loaded from .env in project/ directory\r',
'ZZVAR3=\r',
'ZZVAR3=TEST\r',
'',
],
)
@ -244,3 +244,36 @@ class TestComposeEnvFile(unittest.TestCase, RunSubprocessMixin):
path_compose_file,
"down",
])
def test_env_var_value_accessed_in_compose_file_short_syntax(self) -> None:
# Test that compose file can access the environment variable set in .env file using
# short syntax, that is: only the name of environment variable is used in "environment:" in
# compose.yml file and its value is picked up directly from .env file
# long syntax of environment variables interpolation is tested in
# tests/integration/interpolation
base_path = compose_base_path()
compose_file_path = os.path.join(base_path, "project/container-compose.short_syntax.yaml")
try:
self.run_subprocess_assert_returncode([
podman_compose_path(),
"-f",
compose_file_path,
"up",
"-d",
])
output, _ = self.run_subprocess_assert_returncode([
podman_compose_path(),
"-f",
compose_file_path,
"logs",
])
# ZZVAR3 was set in .env file
self.assertEqual(output, b"ZZVAR3=TEST\n")
finally:
self.run_subprocess_assert_returncode([
podman_compose_path(),
"-f",
compose_file_path,
"down",
])

View File

@ -8,14 +8,14 @@ from tests.integration.test_utils import podman_compose_path
from tests.integration.test_utils import test_path
def compose_yaml_path():
return os.path.join(os.path.join(test_path(), "env-tests"), "container-compose.yml")
def compose_yaml_path() -> str:
return os.path.join(os.path.join(test_path(), "env_tests"), "container-compose.yml")
class TestComposeEnv(unittest.TestCase, RunSubprocessMixin):
"""Test that inline environment variable overrides environment variable from compose file."""
def test_env(self):
def test_env(self) -> None:
try:
output, _ = self.run_subprocess_assert_returncode([
podman_compose_path(),
@ -50,7 +50,7 @@ class TestComposeEnv(unittest.TestCase, RunSubprocessMixin):
- https://github.com/compose-spec/compose-spec/blob/main/04-version-and-name.md
"""
def test_project_name(self):
def test_project_name(self) -> None:
try:
output, _ = self.run_subprocess_assert_returncode([
podman_compose_path(),
@ -68,7 +68,7 @@ class TestComposeEnv(unittest.TestCase, RunSubprocessMixin):
"down",
])
def test_project_name_override(self):
def test_project_name_override(self) -> None:
try:
output, _ = self.run_subprocess_assert_returncode([
podman_compose_path(),

View File

@ -0,0 +1 @@

View File

@ -8,12 +8,12 @@ from tests.integration.test_utils import podman_compose_path
from tests.integration.test_utils import test_path
def compose_yaml_path():
return os.path.join(os.path.join(test_path(), "exit-from"), "docker-compose.yaml")
def compose_yaml_path() -> str:
return os.path.join(os.path.join(test_path(), "exit_from"), "docker-compose.yaml")
class TestComposeExitFrom(unittest.TestCase, RunSubprocessMixin):
def test_exit_code_sh1(self):
def test_exit_code_sh1(self) -> None:
try:
self.run_subprocess_assert_returncode(
[
@ -33,7 +33,7 @@ class TestComposeExitFrom(unittest.TestCase, RunSubprocessMixin):
"down",
])
def test_exit_code_sh2(self):
def test_exit_code_sh2(self) -> None:
try:
self.run_subprocess_assert_returncode(
[
@ -53,13 +53,13 @@ class TestComposeExitFrom(unittest.TestCase, RunSubprocessMixin):
"down",
])
def test_podman_compose_exit_from(self):
def test_podman_compose_exit_from(self) -> None:
up_cmd = [
"coverage",
"run",
podman_compose_path(),
"-f",
os.path.join(test_path(), "exit-from", "docker-compose.yaml"),
compose_yaml_path(),
"up",
]

View File

@ -8,12 +8,12 @@ from tests.integration.test_utils import podman_compose_path
from tests.integration.test_utils import test_path
def compose_yaml_path():
def compose_yaml_path() -> str:
return os.path.join(os.path.join(test_path(), "extends"), "docker-compose.yaml")
class TestComposeExteds(unittest.TestCase, RunSubprocessMixin):
def test_extends_service_launch_echo(self):
def test_extends_service_launch_echo(self) -> None:
try:
self.run_subprocess_assert_returncode([
podman_compose_path(),
@ -38,7 +38,7 @@ class TestComposeExteds(unittest.TestCase, RunSubprocessMixin):
"down",
])
def test_extends_service_launch_echo1(self):
def test_extends_service_launch_echo1(self) -> None:
try:
self.run_subprocess_assert_returncode([
podman_compose_path(),
@ -63,7 +63,7 @@ class TestComposeExteds(unittest.TestCase, RunSubprocessMixin):
"down",
])
def test_extends_service_launch_env1(self):
def test_extends_service_launch_env1(self) -> None:
try:
self.run_subprocess_assert_returncode([
podman_compose_path(),

View File

@ -9,12 +9,12 @@ from tests.integration.test_utils import podman_compose_path
from tests.integration.test_utils import test_path
def compose_yaml_path():
def compose_yaml_path() -> str:
return os.path.join(os.path.join(test_path(), "extends_w_empty_service"), "docker-compose.yml")
class TestComposeExtendsWithEmptyService(unittest.TestCase, RunSubprocessMixin):
def test_extends_w_empty_service(self):
def test_extends_w_empty_service(self) -> None:
try:
self.run_subprocess_assert_returncode(
[
@ -39,7 +39,7 @@ class TestComposeExtendsWithEmptyService(unittest.TestCase, RunSubprocessMixin):
"down",
])
def test_podman_compose_extends_w_empty_service(self):
def test_podman_compose_extends_w_empty_service(self) -> None:
"""
Test that podman-compose can execute podman-compose -f <file> up with extended File which
includes an empty service. (e.g. if the file is used as placeholder for more complex

View File

@ -8,12 +8,12 @@ from tests.integration.test_utils import podman_compose_path
from tests.integration.test_utils import test_path
def compose_yaml_path():
def compose_yaml_path() -> str:
return os.path.join(os.path.join(test_path(), "extends_w_file"), "docker-compose.yml")
class TestComposeExtendsWithFile(unittest.TestCase, RunSubprocessMixin):
def test_extends_w_file(self): # when file is Dockerfile for building the image
def test_extends_w_file(self) -> None: # when file is Dockerfile for building the image
try:
self.run_subprocess_assert_returncode(
[

View File

@ -9,12 +9,12 @@ from tests.integration.test_utils import podman_compose_path
from tests.integration.test_utils import test_path
def compose_yaml_path():
def compose_yaml_path() -> str:
return os.path.join(os.path.join(test_path(), "extends_w_file_subdir"), "docker-compose.yml")
class TestComposeExtendsWithFileSubdir(unittest.TestCase, RunSubprocessMixin):
def test_extends_w_file_subdir(self): # when file is Dockerfile for building the image
def test_extends_w_file_subdir(self) -> None: # when file is Dockerfile for building the image
try:
self.run_subprocess_assert_returncode(
[
@ -39,7 +39,7 @@ class TestComposeExtendsWithFileSubdir(unittest.TestCase, RunSubprocessMixin):
"down",
])
def test_podman_compose_extends_w_file_subdir(self):
def test_podman_compose_extends_w_file_subdir(self) -> None:
"""
Test that podman-compose can execute podman-compose -f <file> up with extended File which
includes a build context

View File

@ -10,7 +10,7 @@ from tests.integration.test_utils import test_path
class TestFilesystem(unittest.TestCase, RunSubprocessMixin):
def test_compose_symlink(self):
def test_compose_symlink(self) -> None:
"""The context of podman-compose.yml should come from the same directory as the file even
if it is a symlink
"""

View File

@ -0,0 +1,8 @@
version: "3"
services:
cont:
image: nopush/podman-compose-test
command: ["dumb-init", "/bin/busybox", "httpd", "-f", "-p", "8080"]
x-podman:
in_pod: custom_test_pod_name

View File

@ -6,26 +6,26 @@ import unittest
from tests.integration.test_utils import RunSubprocessMixin
def base_path():
def base_path() -> str:
"""Returns the base path for the project"""
return os.path.dirname(os.path.dirname(os.path.dirname(os.path.dirname(__file__))))
def test_path():
def test_path() -> str:
"""Returns the path to the tests directory"""
return os.path.join(base_path(), "tests/integration")
def podman_compose_path():
def podman_compose_path() -> str:
"""Returns the path to the podman compose script"""
return os.path.join(base_path(), "podman_compose.py")
def is_root():
def is_root() -> bool:
return os.geteuid() == 0
def failure_exitcode_when_rootful():
def failure_exitcode_when_rootful() -> int:
if is_root():
return 125
return 0
@ -37,7 +37,7 @@ def failure_exitcode_when_rootful():
# Test all combinations of command line argument in_pod and compose file argument in_pod.
class TestPodmanComposeInPod(unittest.TestCase, RunSubprocessMixin):
# compose file provides x-podman in_pod=false
def test_x_podman_in_pod_false_command_line_in_pod_not_exists(self):
def test_x_podman_in_pod_false_command_line_in_pod_not_exists(self) -> None:
"""
Test that podman-compose will not create a pod, when x-podman in_pod=false and command line
does not provide this option
@ -82,7 +82,7 @@ class TestPodmanComposeInPod(unittest.TestCase, RunSubprocessMixin):
# throws an error, can not actually find this pod because it was not created
self.run_subprocess_assert_returncode(command_rm_pod, expected_returncode=1)
def test_x_podman_in_pod_false_command_line_in_pod_true(self):
def test_x_podman_in_pod_false_command_line_in_pod_true(self) -> None:
"""
Test that podman-compose does not allow pod creating even with command line in_pod=True
when --userns and --pod are set together: throws an error
@ -115,7 +115,7 @@ class TestPodmanComposeInPod(unittest.TestCase, RunSubprocessMixin):
# been created) and have expected_returncode=1 (see FIXME above)
self.run_subprocess_assert_returncode(command_rm_pod)
def test_x_podman_in_pod_false_command_line_in_pod_false(self):
def test_x_podman_in_pod_false_command_line_in_pod_false(self) -> None:
"""
Test that podman-compose will not create a pod as command line sets in_pod=False
"""
@ -160,7 +160,7 @@ class TestPodmanComposeInPod(unittest.TestCase, RunSubprocessMixin):
# can not actually find this pod because it was not created
self.run_subprocess_assert_returncode(command_rm_pod, 1)
def test_x_podman_in_pod_false_command_line_in_pod_empty_string(self):
def test_x_podman_in_pod_false_command_line_in_pod_empty_string(self) -> None:
"""
Test that podman-compose will not create a pod, when x-podman in_pod=false and command line
command line in_pod=""
@ -207,7 +207,7 @@ class TestPodmanComposeInPod(unittest.TestCase, RunSubprocessMixin):
self.run_subprocess_assert_returncode(command_rm_pod, 1)
# compose file provides x-podman in_pod=true
def test_x_podman_in_pod_true_command_line_in_pod_not_exists(self):
def test_x_podman_in_pod_true_command_line_in_pod_not_exists(self) -> None:
"""
Test that podman-compose does not allow pod creating when --userns and --pod are set
together even when x-podman in_pod=true: throws an error
@ -240,7 +240,7 @@ class TestPodmanComposeInPod(unittest.TestCase, RunSubprocessMixin):
# created) and have expected_returncode=1 (see FIXME above)
self.run_subprocess_assert_returncode(command_rm_pod)
def test_x_podman_in_pod_true_command_line_in_pod_true(self):
def test_x_podman_in_pod_true_command_line_in_pod_true(self) -> None:
"""
Test that podman-compose does not allow pod creating when --userns and --pod are set
together even when x-podman in_pod=true and and command line in_pod=True: throws an error
@ -274,7 +274,7 @@ class TestPodmanComposeInPod(unittest.TestCase, RunSubprocessMixin):
# been created) and have expected_returncode=1 (see FIXME above)
self.run_subprocess_assert_returncode(command_rm_pod)
def test_x_podman_in_pod_true_command_line_in_pod_false(self):
def test_x_podman_in_pod_true_command_line_in_pod_false(self) -> None:
"""
Test that podman-compose will not create a pod as command line sets in_pod=False
"""
@ -319,7 +319,7 @@ class TestPodmanComposeInPod(unittest.TestCase, RunSubprocessMixin):
# can not actually find this pod because it was not created
self.run_subprocess_assert_returncode(command_rm_pod, 1)
def test_x_podman_in_pod_true_command_line_in_pod_empty_string(self):
def test_x_podman_in_pod_true_command_line_in_pod_empty_string(self) -> None:
"""
Test that podman-compose does not allow pod creating when --userns and --pod are set
together even when x-podman in_pod=true and command line in_pod="": throws an error
@ -354,7 +354,7 @@ class TestPodmanComposeInPod(unittest.TestCase, RunSubprocessMixin):
self.run_subprocess_assert_returncode(command_rm_pod)
# compose file does not provide x-podman in_pod
def test_x_podman_in_pod_not_exists_command_line_in_pod_not_exists(self):
def test_x_podman_in_pod_not_exists_command_line_in_pod_not_exists(self) -> None:
"""
Test that podman-compose does not allow pod creating when --userns and --pod are set
together: throws an error
@ -387,7 +387,7 @@ class TestPodmanComposeInPod(unittest.TestCase, RunSubprocessMixin):
# created) and have expected_returncode=1 (see FIXME above)
self.run_subprocess_assert_returncode(command_rm_pod)
def test_x_podman_in_pod_not_exists_command_line_in_pod_true(self):
def test_x_podman_in_pod_not_exists_command_line_in_pod_true(self) -> None:
"""
Test that podman-compose does not allow pod creating when --userns and --pod are set
together even when x-podman in_pod=true: throws an error
@ -421,7 +421,7 @@ class TestPodmanComposeInPod(unittest.TestCase, RunSubprocessMixin):
# been created) and have expected_returncode=1 (see FIXME above)
self.run_subprocess_assert_returncode(command_rm_pod)
def test_x_podman_in_pod_not_exists_command_line_in_pod_false(self):
def test_x_podman_in_pod_not_exists_command_line_in_pod_false(self) -> None:
"""
Test that podman-compose will not create a pod as command line sets in_pod=False
"""
@ -467,7 +467,136 @@ class TestPodmanComposeInPod(unittest.TestCase, RunSubprocessMixin):
# can not actually find this pod because it was not created
self.run_subprocess_assert_returncode(command_rm_pod, 1)
def test_x_podman_in_pod_not_exists_command_line_in_pod_empty_string(self):
def test_x_podman_in_pod_not_exists_command_line_in_pod_not_exists_docker_compat(self) -> None:
"""
Test that podman-compose will not create a pod when docker compat is requested.
"""
command_up = [
"python3",
os.path.join(base_path(), "podman_compose.py"),
"-f",
os.path.join(
base_path(),
"tests",
"integration",
"in_pod",
"custom_x-podman_not_exists",
"docker-compose.yml",
),
"up",
"-d",
]
down_cmd = [
"python3",
podman_compose_path(),
"-f",
os.path.join(
base_path(),
"tests",
"integration",
"in_pod",
"custom_x-podman_not_exists",
"docker-compose.yml",
),
"down",
]
env = {
"PODMAN_COMPOSE_DOCKER_COMPOSE_COMPAT": "1",
}
try:
self.run_subprocess_assert_returncode(
command_up, failure_exitcode_when_rootful(), env=env
)
finally:
self.run_subprocess_assert_returncode(down_cmd, env=env)
command_rm_pod = ["podman", "pod", "rm", "pod_custom_x-podman_not_exists"]
# can not actually find this pod because it was not created
self.run_subprocess_assert_returncode(command_rm_pod, 1)
def test_x_podman_in_pod_not_exists_command_line_in_pod_not_exists_env_var(self) -> None:
"""
Test that podman-compose will not create a pod when env var is set.
"""
command_up = [
"python3",
os.path.join(base_path(), "podman_compose.py"),
"-f",
os.path.join(
base_path(),
"tests",
"integration",
"in_pod",
"custom_x-podman_not_exists",
"docker-compose.yml",
),
"up",
"-d",
]
down_cmd = [
"python3",
podman_compose_path(),
"-f",
os.path.join(
base_path(),
"tests",
"integration",
"in_pod",
"custom_x-podman_not_exists",
"docker-compose.yml",
),
"down",
]
env = {
"PODMAN_COMPOSE_IN_POD": "0",
}
try:
self.run_subprocess_assert_returncode(
command_up, failure_exitcode_when_rootful(), env=env
)
finally:
self.run_subprocess_assert_returncode(down_cmd, env=env)
command_rm_pod = ["podman", "pod", "rm", "pod_custom_x-podman_not_exists"]
# can not actually find this pod because it was not created
self.run_subprocess_assert_returncode(command_rm_pod, 1)
def test_x_podman_in_pod_custom_name(self) -> None:
"""
Test that podman-compose will create a pod with a custom name
"""
command_up = [
"python3",
os.path.join(base_path(), "podman_compose.py"),
"-f",
os.path.join(
base_path(),
"tests",
"integration",
"in_pod",
"custom_x-podman_custom_name",
"docker-compose.yml",
),
"up",
"--no-start",
]
try:
self.run_subprocess_assert_returncode(command_up)
finally:
command_rm_pod = ["podman", "pod", "rm", "custom_test_pod_name"]
self.run_subprocess_assert_returncode(command_rm_pod)
def test_x_podman_in_pod_not_exists_command_line_in_pod_empty_string(self) -> None:
"""
Test that podman-compose does not allow pod creating when --userns and --pod are set
together: throws an error

View File

@ -7,7 +7,7 @@ from tests.integration.test_utils import RunSubprocessMixin
class TestPodmanComposeInclude(unittest.TestCase, RunSubprocessMixin):
def test_podman_compose_include(self):
def test_podman_compose_include(self) -> None:
"""
Test that podman-compose can execute podman-compose -f <file> up with include
:return:

View File

@ -1 +1,2 @@
DOT_ENV_VARIABLE=This value is from the .env file
TEST_LABELS=TEST

View File

@ -11,4 +11,10 @@ services:
EXAMPLE_DOT_ENV: $DOT_ENV_VARIABLE
EXAMPLE_LITERAL: This is a $$literal
EXAMPLE_EMPTY: $NOT_A_VARIABLE
labels_test:
image: busybox
labels:
- "$TEST_LABELS=test_labels"
- test.${TEST_LABELS}=${TEST_LABELS}
- "${TEST_LABELS}.test2=test2(`${TEST_LABELS}`)"

View File

@ -1,5 +1,6 @@
# SPDX-License-Identifier: GPL-2.0
import json
import os
import unittest
@ -8,12 +9,12 @@ from tests.integration.test_utils import podman_compose_path
from tests.integration.test_utils import test_path
def compose_yaml_path():
def compose_yaml_path() -> str:
return os.path.join(os.path.join(test_path(), "interpolation"), "docker-compose.yml")
class TestComposeInterpolation(unittest.TestCase, RunSubprocessMixin):
def test_interpolation(self):
def test_interpolation(self) -> None:
try:
self.run_subprocess_assert_returncode([
"env",
@ -36,6 +37,18 @@ class TestComposeInterpolation(unittest.TestCase, RunSubprocessMixin):
self.assertIn("EXAMPLE_DOT_ENV='This value is from the .env file'", str(output))
self.assertIn("EXAMPLE_EMPTY=''", str(output))
self.assertIn("EXAMPLE_LITERAL='This is a $literal'", str(output))
output, _ = self.run_subprocess_assert_returncode([
"podman",
"inspect",
"interpolation_labels_test_1",
])
inspect_output = json.loads(output)
labels_dict = inspect_output[0].get("Config", {}).get("Labels", {})
self.assertIn(('TEST', 'test_labels'), labels_dict.items())
self.assertIn(('TEST.test2', 'test2(`TEST`)'), labels_dict.items())
self.assertIn(('test.TEST', 'TEST'), labels_dict.items())
finally:
self.run_subprocess_assert_returncode([
podman_compose_path(),

View File

@ -9,12 +9,12 @@ from tests.integration.test_utils import podman_compose_path
from tests.integration.test_utils import test_path
def compose_yaml_path():
def compose_yaml_path() -> str:
return os.path.join(os.path.join(test_path(), "ipam_default"), "docker-compose.yaml")
class TestComposeIpamDefault(unittest.TestCase, RunSubprocessMixin):
def test_ipam_default(self):
def test_ipam_default(self) -> None:
try:
self.run_subprocess_assert_returncode(
[podman_compose_path(), "-f", compose_yaml_path(), "up", "-d"],

View File

View File

@ -2,6 +2,7 @@
import os
import time
import unittest
from parameterized import parameterized
@ -12,7 +13,7 @@ from tests.integration.test_utils import test_path
class TestLifetime(unittest.TestCase, RunSubprocessMixin):
def test_up_single_container(self):
def test_up_single_container(self) -> None:
"""Podman compose up should be able to start containers one after another"""
compose_path = os.path.join(test_path(), "lifetime/up_single_container/docker-compose.yml")
@ -68,7 +69,7 @@ class TestLifetime(unittest.TestCase, RunSubprocessMixin):
("no_ports", "up_single_container_many_times"),
("with_ports", "up_single_container_many_times_with_ports"),
])
def test_up_single_container_many_times(self, name, subdir):
def test_up_single_container_many_times(self, name: str, subdir: str) -> None:
"""Podman compose up should be able to start a container many times after it finishes
running.
"""
@ -85,15 +86,14 @@ class TestLifetime(unittest.TestCase, RunSubprocessMixin):
"container1",
])
for _ in range(0, 3):
self.run_subprocess_assert_returncode([
podman_compose_path(),
"-f",
compose_path,
"up",
"-d",
"container2",
])
self.run_subprocess_assert_returncode([
podman_compose_path(),
"-f",
compose_path,
"up",
"-d",
"container2",
])
out, _ = self.run_subprocess_assert_returncode([
podman_compose_path(),
@ -105,6 +105,7 @@ class TestLifetime(unittest.TestCase, RunSubprocessMixin):
self.assertEqual(out, b"test1\n")
# "restart: always" keeps restarting container until its removal
out, _ = self.run_subprocess_assert_returncode([
podman_compose_path(),
"-f",
@ -113,13 +114,22 @@ class TestLifetime(unittest.TestCase, RunSubprocessMixin):
"container2",
])
# BUG: container should be started 3 times, not 4.
self.assertEqual(out, b"test2\n" * 4)
if not out.startswith(b"test2\ntest2"):
time.sleep(1)
out, _ = self.run_subprocess_assert_returncode([
podman_compose_path(),
"-f",
compose_path,
"logs",
"container2",
])
self.assertTrue(out.startswith(b"test2\ntest2"))
finally:
out, _ = self.run_subprocess_assert_returncode([
podman_compose_path(),
"-f",
compose_path,
"down",
"-t",
"0",
])

View File

@ -5,5 +5,5 @@ services:
command: ["/bin/bash", "-c", "echo test1; sleep infinity"]
container2:
image: nopush/podman-compose-test
restart: never
restart: always
command: ["/bin/bash", "-c", "echo test2"]

View File

@ -6,6 +6,6 @@ services:
command: ["/bin/bash", "-c", "echo test1; sleep infinity"]
container2:
image: nopush/podman-compose-test
restart: never
restart: always
ports: "9002:9002"
command: ["/bin/bash", "-c", "echo test2"]

View File

@ -0,0 +1 @@

View File

@ -0,0 +1 @@

View File

@ -9,16 +9,19 @@ from tests.integration.test_utils import podman_compose_path
from tests.integration.test_utils import test_path
def compose_yaml_path():
return os.path.join(os.path.join(test_path(), "override_tag_attribute"), "docker-compose.yaml")
def compose_yaml_path() -> str:
return os.path.join(
test_path(),
"merge/reset_and_override_tags/override_tag_attribute/docker-compose.yaml",
)
class TestComposeOverrideTagAttribute(unittest.TestCase, RunSubprocessMixin):
# test if a service attribute from docker-compose.yaml file is overridden
def test_override_tag_attribute(self):
def test_override_tag_attribute(self) -> None:
override_file = os.path.join(
os.path.join(test_path(), "override_tag_attribute"),
"docker-compose.override_attribute.yaml",
test_path(),
"merge/reset_and_override_tags/override_tag_attribute/docker-compose.override_attribute.yaml",
)
try:
self.run_subprocess_assert_returncode([

View File

@ -9,16 +9,19 @@ from tests.integration.test_utils import podman_compose_path
from tests.integration.test_utils import test_path
def compose_yaml_path():
return os.path.join(os.path.join(test_path(), "override_tag_service"), "docker-compose.yaml")
def compose_yaml_path() -> str:
return os.path.join(
test_path(),
"merge/reset_and_override_tags/override_tag_service/docker-compose.yaml",
)
class TestComposeOverrideTagService(unittest.TestCase, RunSubprocessMixin):
# test if whole service from docker-compose.yaml file is overridden in another file
def test_override_tag_service(self):
def test_override_tag_service(self) -> None:
override_file = os.path.join(
os.path.join(test_path(), "override_tag_service"),
"docker-compose.override_service.yaml",
test_path(),
"merge/reset_and_override_tags/override_tag_service/docker-compose.override_service.yaml",
)
try:
self.run_subprocess_assert_returncode([

View File

@ -3,3 +3,4 @@ services:
app:
image: busybox
command: !reset {}
depends_on: !reset null

View File

@ -3,3 +3,5 @@ services:
app:
image: busybox
command: ["/bin/busybox", "echo", "Zero"]
depends_on:
- db

View File

@ -8,15 +8,19 @@ from tests.integration.test_utils import podman_compose_path
from tests.integration.test_utils import test_path
def compose_yaml_path():
return os.path.join(os.path.join(test_path(), "reset_tag_attribute"), "docker-compose.yaml")
def compose_yaml_path() -> str:
return os.path.join(
test_path(),
"merge/reset_and_override_tags/reset_tag_attribute/docker-compose.yaml",
)
class TestComposeResetTagAttribute(unittest.TestCase, RunSubprocessMixin):
# test if the attribute of the service is correctly reset
def test_reset_tag_attribute(self):
def test_reset_tag_attribute(self) -> None:
reset_file = os.path.join(
os.path.join(test_path(), "reset_tag_attribute"), "docker-compose.reset_attribute.yaml"
test_path(),
"merge/reset_and_override_tags/reset_tag_attribute/docker-compose.reset_attribute.yaml",
)
try:
self.run_subprocess_assert_returncode([
@ -49,6 +53,8 @@ class TestComposeResetTagAttribute(unittest.TestCase, RunSubprocessMixin):
"logs",
])
self.assertEqual(output, b"")
# depends_on: !reset null testing: if this test works, depends_on is correctly reset.
# Otherwise the test would break, since "db" dependency service is not provided
finally:
self.run_subprocess_assert_returncode([
podman_compose_path(),

View File

@ -8,15 +8,18 @@ from tests.integration.test_utils import podman_compose_path
from tests.integration.test_utils import test_path
def compose_yaml_path():
return os.path.join(os.path.join(test_path(), "reset_tag_service"), "docker-compose.yaml")
def compose_yaml_path() -> str:
return os.path.join(
test_path(), "merge/reset_and_override_tags/reset_tag_service/docker-compose.yaml"
)
class TestComposeResetTagService(unittest.TestCase, RunSubprocessMixin):
# test if whole service from docker-compose.yaml file is reset
def test_reset_tag_service(self):
def test_reset_tag_service(self) -> None:
reset_file = os.path.join(
os.path.join(test_path(), "reset_tag_service"), "docker-compose.reset_service.yaml"
test_path(),
"merge/reset_and_override_tags/reset_tag_service/docker-compose.reset_service.yaml",
)
try:
self.run_subprocess_assert_returncode([

View File

@ -9,14 +9,14 @@ from tests.integration.test_utils import podman_compose_path
from tests.integration.test_utils import test_path
def compose_yaml_path(compose_name):
def compose_yaml_path(compose_name: str) -> str:
""" "Returns the path to the compose file used for this test module"""
base_path = os.path.join(test_path(), "volumes_merge/")
base_path = os.path.join(test_path(), "merge/volumes_merge/")
return os.path.join(base_path, compose_name)
class TestComposeVolumesMerge(unittest.TestCase, RunSubprocessMixin):
def test_volumes_merge(self):
def test_volumes_merge(self) -> None:
# test if additional compose file overrides host path and access mode of a volume
try:
self.run_subprocess_assert_returncode([
@ -55,7 +55,7 @@ class TestComposeVolumesMerge(unittest.TestCase, RunSubprocessMixin):
binds_info = volumes_info["HostConfig"]["Binds"]
binds_info.sort()
file_path = os.path.join(test_path(), "volumes_merge/override.txt")
file_path = os.path.join(test_path(), "merge/volumes_merge/override.txt")
expected = [
f'{file_path}:/var/www/html/index.html:ro,rprivate,rbind',
f'{file_path}:/var/www/html/index2.html:rw,rprivate,rbind',

View File

@ -8,12 +8,12 @@ from tests.integration.test_utils import podman_compose_path
from tests.integration.test_utils import test_path
def compose_yaml_path():
def compose_yaml_path() -> str:
return os.path.join(os.path.join(test_path(), "multicompose"), "docker-compose.yml")
class TestComposeMulticompose(unittest.TestCase, RunSubprocessMixin):
def test_multicompose(self):
def test_multicompose(self) -> None:
try:
self.run_subprocess_assert_returncode(
[

View File

@ -0,0 +1 @@

View File

@ -0,0 +1,7 @@
services:
web:
image: busybox
command: httpd -f -p 8123 -h /tmp/
x-podman:
name_separator_compat: true

View File

@ -0,0 +1,4 @@
services:
web:
image: busybox
command: httpd -f -p 8123 -h /tmp/

View File

@ -0,0 +1,60 @@
# SPDX-License-Identifier: GPL-2.0
import os
import unittest
from parameterized import parameterized
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 TestComposeNameSeparatorCompat(unittest.TestCase, RunSubprocessMixin):
@parameterized.expand([
('default', {}, '_'),
('default', {'PODMAN_COMPOSE_NAME_SEPARATOR_COMPAT': '1'}, '-'),
('default', {'PODMAN_COMPOSE_DOCKER_COMPOSE_COMPAT': '1'}, '-'),
('compat', {}, '-'),
('compat', {'PODMAN_COMPOSE_NAME_SEPARATOR_COMPAT': '1'}, '-'),
('compat', {'PODMAN_COMPOSE_NAME_SEPARATOR_COMPAT': '0'}, '_'),
])
def test_container_name(self, file: str, env: dict[str, str], expected_sep: str) -> None:
compose_yaml_path = os.path.join(
test_path(), "name_separator_compat", f"docker-compose_{file}.yaml"
)
try:
self.run_subprocess_assert_returncode(
[podman_compose_path(), "-f", compose_yaml_path, "up", "-d"],
env=env,
)
container_name_out, _ = self.run_subprocess_assert_returncode(
[
podman_compose_path(),
"-f",
compose_yaml_path,
"ps",
"--format",
'{{.Names}}',
],
env=env,
)
container_name = container_name_out.decode('utf-8').strip()
expected_container_name = f'name_separator_compat{expected_sep}web{expected_sep}1'
self.assertEqual(container_name, expected_container_name)
finally:
self.run_subprocess_assert_returncode(
[
podman_compose_path(),
"-f",
compose_yaml_path,
"down",
"-t",
"0",
],
env=env,
)

View File

@ -10,20 +10,20 @@ from tests.integration.test_utils import podman_compose_path
from tests.integration.test_utils import test_path
def compose_yaml_path():
def compose_yaml_path() -> str:
return os.path.join(os.path.join(test_path(), "nethost"), "docker-compose.yaml")
class TestComposeNethost(unittest.TestCase, RunSubprocessMixin):
# check if container listens for http requests and sends response back
# as network_mode: host allows to connect to container easily
def test_nethost(self):
def test_nethost(self) -> None:
try:
self.run_subprocess_assert_returncode(
[podman_compose_path(), "-f", compose_yaml_path(), "up", "-d"],
)
container_id, _ = self.run_subprocess_assert_returncode(
container_id_out, _ = self.run_subprocess_assert_returncode(
[
podman_compose_path(),
"-f",
@ -33,7 +33,7 @@ class TestComposeNethost(unittest.TestCase, RunSubprocessMixin):
'{{.ID}}',
],
)
container_id = container_id.decode('utf-8').split('\n')[0]
container_id = container_id_out.decode('utf-8').split('\n')[0]
output, _ = self.run_subprocess_assert_returncode(
[
"podman",

View File

@ -11,13 +11,13 @@ from tests.integration.test_utils import podman_compose_path
from tests.integration.test_utils import test_path
def compose_yaml_path():
def compose_yaml_path() -> str:
return os.path.join(os.path.join(test_path(), "nets_test1"), "docker-compose.yml")
class TestComposeNetsTest1(unittest.TestCase, RunSubprocessMixin):
# test if port mapping works as expected
def test_nets_test1(self):
def test_nets_test1(self) -> None:
try:
self.run_subprocess_assert_returncode(
[

View File

@ -11,13 +11,13 @@ from tests.integration.test_utils import podman_compose_path
from tests.integration.test_utils import test_path
def compose_yaml_path():
def compose_yaml_path() -> str:
return os.path.join(os.path.join(test_path(), "nets_test2"), "docker-compose.yml")
class TestComposeNetsTest2(unittest.TestCase, RunSubprocessMixin):
# test if port mapping works as expected with networks top-level element
def test_nets_test2(self):
def test_nets_test2(self) -> None:
try:
self.run_subprocess_assert_returncode(
[

View File

View File

@ -10,7 +10,7 @@ from tests.integration.test_utils import podman_compose_path
from tests.integration.test_utils import test_path
def compose_yaml_path():
def compose_yaml_path() -> str:
return os.path.join(os.path.join(test_path(), "nets_test3"), "docker-compose.yml")
@ -28,8 +28,12 @@ class TestComposeNetsTest3(unittest.TestCase, RunSubprocessMixin):
("nets_test3_web1_1", "alias21", b"", 1),
])
def test_nets_test3(
self, container_name, nework_alias_name, expected_text, expected_returncode
):
self,
container_name: str,
nework_alias_name: str,
expected_text: bytes,
expected_returncode: int,
) -> None:
try:
self.run_subprocess_assert_returncode(
[

View File

@ -8,14 +8,14 @@ from tests.integration.test_utils import podman_compose_path
from tests.integration.test_utils import test_path
def compose_yaml_path():
def compose_yaml_path() -> str:
return os.path.join(os.path.join(test_path(), "nets_test_ip"), "docker-compose.yml")
class TestComposeNetsTestIp(unittest.TestCase, RunSubprocessMixin):
# test if services retain custom ipv4_address and mac_address matching the subnet provided
# in networks top-level element
def test_nets_test_ip(self):
def test_nets_test_ip(self) -> None:
try:
self.run_subprocess_assert_returncode(
[

View File

@ -9,6 +9,7 @@ Tests the podman networking parameters
# pylint: disable=redefined-outer-name
import os
import unittest
from typing import Generator
from tests.integration.test_utils import RunSubprocessMixin
from tests.integration.test_utils import podman_compose_path
@ -17,11 +18,11 @@ from tests.integration.test_utils import test_path
class TestPodmanComposeNetwork(RunSubprocessMixin, unittest.TestCase):
@staticmethod
def compose_file():
def compose_file() -> str:
"""Returns the path to the compose file used for this test module"""
return os.path.join(test_path(), "nets_test_ip", "docker-compose.yml")
def teardown(self):
def teardown(self) -> Generator[None, None, None]:
"""
Ensures that the services within the "profile compose file" are removed between
each test case.
@ -40,7 +41,7 @@ class TestPodmanComposeNetwork(RunSubprocessMixin, unittest.TestCase):
]
self.run_subprocess(down_cmd)
def test_networks(self):
def test_networks(self) -> None:
up_cmd = [
"coverage",
"run",
@ -115,7 +116,7 @@ class TestPodmanComposeNetwork(RunSubprocessMixin, unittest.TestCase):
self.assertIn(f"ether {mac}", out.decode('utf-8'))
self.assertIn(f"inet {ip}/", out.decode('utf-8'))
def test_down_with_network(self):
def test_down_with_network(self) -> None:
try:
self.run_subprocess_assert_returncode([
"coverage",

View File

@ -10,10 +10,10 @@ from tests.integration.test_utils import test_path
class TestPodmanComposeNetworkInterfaceName(RunSubprocessMixin, unittest.TestCase):
def compose_file(self):
def compose_file(self) -> str:
return os.path.join(test_path(), "network_interface_name", "docker-compose.yml")
def up(self):
def up(self) -> None:
up_cmd = [
"coverage",
"run",
@ -26,7 +26,7 @@ class TestPodmanComposeNetworkInterfaceName(RunSubprocessMixin, unittest.TestCas
]
self.run_subprocess_assert_returncode(up_cmd)
def down(self):
def down(self) -> None:
down_cmd = [
"coverage",
"run",
@ -38,7 +38,7 @@ class TestPodmanComposeNetworkInterfaceName(RunSubprocessMixin, unittest.TestCas
]
self.run_subprocess(down_cmd)
def test_interface_name(self):
def test_interface_name(self) -> None:
try:
self.up()

View File

@ -11,18 +11,18 @@ from tests.integration.test_utils import test_path
class TestPodmanComposeNetworkScopedAliases(RunSubprocessMixin, unittest.TestCase):
@staticmethod
def compose_file():
def compose_file() -> str:
"""Returns the path to the compose file used for this test module"""
return os.path.join(test_path(), "network_scoped_aliases", "docker-compose.yaml")
def test_network_scoped_aliases(self):
def test_network_scoped_aliases(self) -> None:
try:
self.up()
self.verify()
finally:
self.down()
def up(self):
def up(self) -> None:
up_cmd = [
"coverage",
"run",
@ -36,7 +36,7 @@ class TestPodmanComposeNetworkScopedAliases(RunSubprocessMixin, unittest.TestCas
self.run_subprocess_assert_returncode(up_cmd)
def down(self):
def down(self) -> None:
down_cmd = [
"coverage",
"run",
@ -48,7 +48,7 @@ class TestPodmanComposeNetworkScopedAliases(RunSubprocessMixin, unittest.TestCas
]
self.run_subprocess(down_cmd)
def verify(self):
def verify(self) -> None:
expected_results = [
("utils-net0", "web1", ["172.19.3.11"]),
("utils-net0", "secure-web", ["172.19.3.11"]),
@ -72,7 +72,7 @@ class TestPodmanComposeNetworkScopedAliases(RunSubprocessMixin, unittest.TestCas
addresses = self.parse_dnslookup(out.decode())
self.assertEqual(addresses, expected_result)
def parse_dnslookup(self, output):
def parse_dnslookup(self, output: str) -> list[str]:
lines = output.splitlines()
addresses = []
for line in lines:

View File

@ -8,13 +8,13 @@ from tests.integration.test_utils import podman_compose_path
from tests.integration.test_utils import test_path
def compose_yaml_path():
def compose_yaml_path() -> str:
return os.path.join(os.path.join(test_path(), "no_services"), "docker-compose.yaml")
class TestComposeNoServices(unittest.TestCase, RunSubprocessMixin):
# test if a network was created, but not the services
def test_no_services(self):
def test_no_services(self) -> None:
try:
output, return_code = self.run_subprocess_assert_returncode(
[

View File

@ -0,0 +1,4 @@
version: "3"
services:foo
web1:
image: busybox

View File

@ -0,0 +1,4 @@
version: "3"
services:
web1:
image: busybox

Some files were not shown because too many files have changed in this diff Show More