mirror of
https://github.com/containers/podman-compose.git
synced 2025-07-01 13:10:24 +02:00
Compare commits
303 Commits
Author | SHA1 | Date | |
---|---|---|---|
5cefd30926 | |||
b37076bc5e | |||
3db72df49d | |||
189c086d5b | |||
32b3d26ab1 | |||
35a66f5a8b | |||
bd29caa797 | |||
f0928dd399 | |||
6c9c09197a | |||
cda84f439f | |||
67616bdaac | |||
7497692b19 | |||
782c44d4c3 | |||
d7762a54f0 | |||
eba2ca2695 | |||
abe5965c9a | |||
9e0da82726 | |||
6acdafd5b1 | |||
8638eb9b6d | |||
e1d938ffa6 | |||
d532e09d7d | |||
1dab256cdd | |||
2a33ef5c79 | |||
5ab734026c | |||
35dc395483 | |||
38a9263424 | |||
cbe9587973 | |||
8bb43100b1 | |||
98f166d2e4 | |||
150ab02446 | |||
ff58a0bff0 | |||
8d899ebb65 | |||
342a39dcfe | |||
d6b8476573 | |||
ae41ef08c3 | |||
da46ee3910 | |||
d80c31f578 | |||
cefa68dc75 | |||
2e46ff0db2 | |||
fbc4c7da80 | |||
11879d3e94 | |||
27cf8da06f | |||
10a30ba24a | |||
a1be62fd31 | |||
15bf02a004 | |||
e45b5d5063 | |||
c46ecb226b | |||
e04b8f3a60 | |||
815450aba9 | |||
92f0a8583a | |||
5f4fc4618c | |||
4d899edeb3 | |||
f9489afaf5 | |||
7d7533772b | |||
65b455f081 | |||
1aa750bacf | |||
98b9bb9f8e | |||
170411de8b | |||
0cf1378cb5 | |||
f5a6df6dc4 | |||
f106ea0c01 | |||
b748c2666c | |||
3973c476c4 | |||
8b1bd0123c | |||
2e7d83f7f0 | |||
52e2912e0b | |||
8ef537e247 | |||
04fcc26a79 | |||
d4760712b7 | |||
7c61f24467 | |||
202c3771a9 | |||
a54f0fa573 | |||
3353697402 | |||
ca1b59c449 | |||
b9f27795c0 | |||
4cd1642be0 | |||
fd401331e5 | |||
37b27fa233 | |||
976847ef9b | |||
c6b3d497d6 | |||
10ad739746 | |||
784d798dac | |||
dd01d039bf | |||
81a0a5933e | |||
c289a3b827 | |||
baccce4f3f | |||
07af8488db | |||
cbc5a8c8b3 | |||
aeaceed7ba | |||
b1eb558b41 | |||
1cdc9e6552 | |||
2f8ed2137c | |||
838957b902 | |||
15380a809d | |||
d4e5859370 | |||
593d7c825e | |||
bfba7ba32d | |||
fe9be2d98f | |||
43a2f1d01f | |||
aa47a373ca | |||
eaec19336f | |||
34bee28bb8 | |||
bfea139362 | |||
4a81bce2a5 | |||
e626f15eff | |||
974250caa5 | |||
29404af723 | |||
d1ba2f4c7d | |||
e03d675b9b | |||
51d180d2d0 | |||
9c905f9012 | |||
bdb3e4e984 | |||
105e390f6b | |||
d79ff01e77 | |||
d9ef3d2cc6 | |||
d23ef4f481 | |||
b685bce400 | |||
7d5bf645f6 | |||
9f7ae38bac | |||
3cee4e015c | |||
498a1994ce | |||
488908f809 | |||
f7bcc4221e | |||
a73df712cc | |||
50dc19f5f8 | |||
9029dce0f6 | |||
a8282c77d6 | |||
f4b775c7e4 | |||
adf30e0475 | |||
41675c3916 | |||
6caf2eae42 | |||
3093b00326 | |||
1c21d655ba | |||
18e5fd64f3 | |||
24bdfd1e17 | |||
c2d3e156c2 | |||
ba95100cff | |||
6022669991 | |||
e29df71d42 | |||
21b9d385b2 | |||
4c17ce2434 | |||
09d54e9dcc | |||
f1dd9b374e | |||
87af67fe94 | |||
f1d663874e | |||
69ffff33f6 | |||
f376700972 | |||
9be3ec985f | |||
6e642dca1f | |||
0f2c717655 | |||
2aa042b9c7 | |||
a177603661 | |||
bc4177fbdc | |||
8206cc3ea2 | |||
60ac5e43b3 | |||
48c6c38fcd | |||
84f1fbd622 | |||
ac5291e10b | |||
9cbc4c1dcd | |||
c63d678887 | |||
6de335beb2 | |||
23799245bd | |||
eda4815715 | |||
cbb0cab814 | |||
7c8696b2b2 | |||
483103ac3a | |||
e9f1029406 | |||
804852b218 | |||
0e3b372a29 | |||
f11e08eaac | |||
62789a2358 | |||
35cf4bcb72 | |||
9964604b17 | |||
61fa24bf21 | |||
ac7ec5c166 | |||
75d7be2b7b | |||
346f7a57f0 | |||
55642247e3 | |||
978a1381bc | |||
4a232f5e32 | |||
aa8c6fd598 | |||
fb0bbd6fe1 | |||
b324029f25 | |||
590c371db2 | |||
16196a1f6d | |||
2dfbb59097 | |||
a34d1d1a31 | |||
d6da65e6c9 | |||
de2c33d7ae | |||
2891be01d7 | |||
a023dc145b | |||
d803c4c3e8 | |||
420d19daf4 | |||
2cfc617f9e | |||
376f0817e6 | |||
054c66b568 | |||
d9fc8e91f2 | |||
145ae47c48 | |||
a67fa0beb5 | |||
3ba0396e7a | |||
973e15ba23 | |||
002c2e400b | |||
626e278794 | |||
a358890d54 | |||
9f3251ff3d | |||
a9cfdb6704 | |||
c0dc3e47fd | |||
41e69be201 | |||
4ae6ccb5c0 | |||
4203f799ee | |||
69e95be2f6 | |||
122a914b9b | |||
db0aad97bd | |||
a3fb4b373a | |||
ab33954f6c | |||
4660feb04a | |||
90f54b9ca5 | |||
7090de3bce | |||
df8fa588a5 | |||
e2ae8bee04 | |||
5c81bbfcb7 | |||
22b0c4b348 | |||
380cf42dcd | |||
deed4d51b0 | |||
08b3ac2633 | |||
f8ea85e3af | |||
0de7e13f1a | |||
da7fd4fe86 | |||
cb294d7519 | |||
351858dbec | |||
8d0dd214ae | |||
229650cba8 | |||
ab832d23c7 | |||
4a7329b9e3 | |||
de3f93c491 | |||
54cc055a5c | |||
f4bf69b68e | |||
5297b004af | |||
fd1fc833b0 | |||
124879a7b9 | |||
368efe2ee3 | |||
eeefd37996 | |||
b7514a0647 | |||
561094954d | |||
ea239c4b77 | |||
9fdee76858 | |||
0a6e0a35e1 | |||
26e6651d6c | |||
462603383c | |||
8411db49d1 | |||
fc90f60bf1 | |||
ed58ac0879 | |||
305f25b4d6 | |||
edadf73d82 | |||
dc04108b3e | |||
348461ca77 | |||
92dbd3690e | |||
a1e9a82693 | |||
9e11c6bfbc | |||
0d24c41afb | |||
585d344d0a | |||
3aa6d4d158 | |||
34f5268e37 | |||
b5eaf314ad | |||
b3c49df6eb | |||
dbbd695463 | |||
1b1d3d8c25 | |||
7d7e64fe5a | |||
3c9c18c6e0 | |||
d0a2a44442 | |||
1e66c28bbb | |||
b6eadd56b1 | |||
d95b4d026b | |||
e2eb883709 | |||
0866492a7e | |||
85050097e5 | |||
daab93b762 | |||
9fe30387ee | |||
0f8348bea7 | |||
55ab3fa7f7 | |||
2091ade7b1 | |||
ca58d7cd58 | |||
3296c8d34f | |||
dab6b1b98d | |||
5bf4c0fdbe | |||
67c5352c3a | |||
5e0f7e5e19 | |||
5040a37d47 | |||
c82859b89f | |||
29195be77c | |||
1c74d6cd11 | |||
9a4af0ce62 | |||
0517b9e34c | |||
24038dace3 | |||
0ea4cbe091 | |||
2056e703d5 | |||
137c6207b2 | |||
4ec57c1013 | |||
d9a3572461 | |||
fa3e0a7772 | |||
0bcf0799b6 | |||
18472b53ac | |||
f0bae1e2d9 |
2
.github/ISSUE_TEMPLATE/bug_report.md
vendored
2
.github/ISSUE_TEMPLATE/bug_report.md
vendored
@ -13,8 +13,6 @@ A clear and concise description of what the bug is.
|
||||
Please make sure it's not a bug in podman (in that case report it to podman)
|
||||
or your understanding of docker-compose or how rootless containers work (for example, it's normal for rootless container not to be able to listen for port less than 1024 like 80)
|
||||
|
||||
please try to reproduce the bug in latest devel branch
|
||||
|
||||
**To Reproduce**
|
||||
Steps to reproduce the behavior:
|
||||
1. what is the content of the current working directory (ex. `docker-compose.yml`, `.env`, `Dockerfile`, ...etc.)
|
||||
|
27
.github/workflows/release.yml
vendored
Normal file
27
.github/workflows/release.yml
vendored
Normal file
@ -0,0 +1,27 @@
|
||||
name: Build and Release
|
||||
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- 'v*.*.*'
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Build binary
|
||||
run: |
|
||||
mkdir -p release/
|
||||
docker build -t podman-compose-bin -v "$PWD/release:/result" .
|
||||
mv "$PWD/release/podman-compose" "$PWD/release/podman-compose-linux-x86"
|
||||
|
||||
- name: Upload release asset
|
||||
uses: softprops/action-gh-release@v2
|
||||
with:
|
||||
files: ./release/podman-compose-linux-x86
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
16
.github/workflows/test.yml
vendored
16
.github/workflows/test.yml
vendored
@ -9,7 +9,7 @@ jobs:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
python-version: [ '3.8', '3.9', '3.10', '3.11', '3.12' ]
|
||||
python-version: [ '3.9', '3.10', '3.11', '3.12', '3.13' ]
|
||||
|
||||
runs-on: ubuntu-latest
|
||||
container:
|
||||
@ -22,18 +22,18 @@ jobs:
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
set -e
|
||||
apt update && apt install -y podman
|
||||
apt-get update && DEBIAN_FRONTEND=noninteractive apt-get install -y podman
|
||||
python -m pip install --upgrade pip
|
||||
if [ -f requirements.txt ]; then pip install -r requirements.txt; fi
|
||||
if [ -f test-requirements.txt ]; then pip install -r test-requirements.txt; fi
|
||||
- name: Run tests in tests/
|
||||
pip install -r requirements.txt
|
||||
pip install -r test-requirements.txt
|
||||
- name: Run integration tests
|
||||
run: |
|
||||
python -m unittest -v tests/*.py
|
||||
python -m unittest discover -v tests/integration
|
||||
env:
|
||||
TESTS_DEBUG: 1
|
||||
- name: Run tests in pytests/
|
||||
- name: Run unit tests
|
||||
run: |
|
||||
coverage run --source podman_compose -m unittest pytests/*.py
|
||||
coverage run --source podman_compose -m unittest discover tests/unit
|
||||
- name: Report coverage
|
||||
run: |
|
||||
coverage combine
|
||||
|
@ -1,17 +1,10 @@
|
||||
default_install_hook_types: [pre-commit, commit-msg]
|
||||
repos:
|
||||
- repo: https://github.com/psf/black
|
||||
rev: 23.3.0
|
||||
- repo: https://github.com/astral-sh/ruff-pre-commit
|
||||
rev: v0.9.6
|
||||
hooks:
|
||||
- id: black
|
||||
# It is recommended to specify the latest version of Python
|
||||
# supported by your project here, or alternatively use
|
||||
# pre-commit's default_language_version, see
|
||||
# https://pre-commit.com/#top_level-default_language_version
|
||||
language_version: python3.10
|
||||
- id: ruff
|
||||
types: [python]
|
||||
args: [
|
||||
"--check", # Don't apply changes automatically
|
||||
]
|
||||
- repo: https://github.com/pycqa/flake8
|
||||
rev: 6.0.0
|
||||
hooks:
|
||||
@ -34,3 +27,8 @@ repos:
|
||||
rev: v2.2.5
|
||||
hooks:
|
||||
- id: codespell
|
||||
|
||||
- repo: https://github.com/gklein/check_signoff
|
||||
rev: v1.0.5
|
||||
hooks:
|
||||
- id: check-signoff
|
||||
|
@ -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
|
||||
|
@ -8,11 +8,6 @@
|
||||
- Developers that want to fix bugs,
|
||||
- Developers that want to implement new functionalities or enhancements.
|
||||
|
||||
## Branches
|
||||
|
||||
Please request your pull request to be merged into the `devel` branch.
|
||||
Changes to the `stable` branch are managed by the repository maintainers.
|
||||
|
||||
## Development environment setup
|
||||
|
||||
Note: Some steps are OPTIONAL but all are RECOMMENDED.
|
||||
@ -54,8 +49,8 @@ Note: Some steps are OPTIONAL but all are RECOMMENDED.
|
||||
7. Run code coverage:
|
||||
|
||||
```shell
|
||||
$ coverage run --source podman_compose -m unittest pytests/*.py
|
||||
$ python -m unittest tests/*.py
|
||||
$ coverage run --source podman_compose -m unittest discover tests/unit
|
||||
$ python3 -m unittest discover tests/integration
|
||||
$ coverage combine
|
||||
$ coverage report
|
||||
$ coverage html
|
||||
@ -68,8 +63,7 @@ Note: Some steps are OPTIONAL but all are RECOMMENDED.
|
||||
- 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`
|
||||
9. Open a pull request to `containers/podman-compose:devel` and wait for a maintainer to review your
|
||||
work.
|
||||
9. Open a pull request to `containers/podman-compose` and wait for a maintainer to review your work.
|
||||
|
||||
## Adding new commands
|
||||
|
||||
|
30
Dockerfile
Normal file
30
Dockerfile
Normal file
@ -0,0 +1,30 @@
|
||||
# Use a base image with necessary build tools
|
||||
FROM python:3.11-slim AS builder
|
||||
|
||||
# Install required packages for building
|
||||
RUN apt-get update && apt-get install -y \
|
||||
gcc \
|
||||
musl-dev \
|
||||
build-essential \
|
||||
python3-dev \
|
||||
&& apt-get clean \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
# Set the working directory
|
||||
WORKDIR /app
|
||||
|
||||
# Copy the application code
|
||||
COPY . .
|
||||
|
||||
# Install PyInstaller
|
||||
RUN pip install pyinstaller
|
||||
RUN pip install -r requirements.txt
|
||||
|
||||
# Create a binary with PyInstaller
|
||||
RUN pyinstaller --onefile --clean podman_compose.py
|
||||
|
||||
# Create /result dir in case it is not mounted
|
||||
RUN mkdir -p /result
|
||||
|
||||
# Export binary
|
||||
RUN cp /app/dist/podman_compose /result/podman-compose
|
@ -74,6 +74,12 @@ pip3 install https://github.com/containers/podman-compose/archive/main.tar.gz
|
||||
brew install podman-compose
|
||||
```
|
||||
|
||||
### Generate binary using docker/podman locally
|
||||
This script will download the repo, generate the binary using [this Dockerfile](https://github.com/containers/podman-compose/blob/main/Dockerfile), and place the binary in the directory where you called this script.
|
||||
```bash
|
||||
sh -c "$(curl -sSL https://raw.githubusercontent.com/containers/podman-compose/main/scripts/download_and_build_podman-compose.sh)"
|
||||
```
|
||||
|
||||
### Manual
|
||||
|
||||
```bash
|
||||
@ -130,7 +136,7 @@ that are meant to test as many cases as we can to make sure we are compatible
|
||||
run a unittest with following command
|
||||
|
||||
```shell
|
||||
python -m unittest pytests/*.py
|
||||
python3 -m unittest discover tests/unit
|
||||
```
|
||||
|
||||
# Contributing guide
|
||||
|
47
RELEASING.md
Normal file
47
RELEASING.md
Normal file
@ -0,0 +1,47 @@
|
||||
Creating a release
|
||||
==================
|
||||
|
||||
This file contains instructions for maintainers on how to release new versions of podman-compose.
|
||||
|
||||
Step 1: Initialize variables for subsequent steps
|
||||
-------------------------------------------------
|
||||
|
||||
```
|
||||
export VERSION=1.2.3
|
||||
```
|
||||
|
||||
Step 2: Release notes PR
|
||||
------------------------
|
||||
|
||||
Open a new branch (e.g. `release`) and run the following:
|
||||
|
||||
```
|
||||
./scripts/make_release_notes.sh $VERSION
|
||||
```
|
||||
|
||||
This collects the release notes using the `towncrier` tool and then commits the result.
|
||||
This step is done as a PR so that CI can check for spelling errors and similar issues.
|
||||
|
||||
Certain file names are not properly supported by the `towncrier` tool and it ignores them.
|
||||
Check `newsfragments` directory for any forgotten release notes
|
||||
|
||||
Step 3: Merge the release notes PR
|
||||
----------------------------------
|
||||
|
||||
Step 4: Perform actual release
|
||||
------------------------------
|
||||
|
||||
Pull the merge commit created on the `main` branch during the step 2.
|
||||
Then run:
|
||||
|
||||
```
|
||||
./scripts/make_release.sh $VERSION
|
||||
```
|
||||
|
||||
This will create release commit, tag and push everything.
|
||||
|
||||
Step 5: Create a release on Github
|
||||
----------------------------------
|
||||
|
||||
The release notes must be added manually by drafting a release on the GitHub UI at
|
||||
https://github.com/containers/podman-compose/releases.
|
38
docs/Changelog-1.3.0.md
Normal file
38
docs/Changelog-1.3.0.md
Normal file
@ -0,0 +1,38 @@
|
||||
Version 1.3.0 (2025-01-07)
|
||||
==========================
|
||||
|
||||
Bug fixes
|
||||
---------
|
||||
|
||||
- Fixed support for de-facto alternative `Dockerfile` names (e.g. `Containerfile`)
|
||||
- Fixed a bug that caused attempts to create already existing pods multiple times.
|
||||
- Fixed compatibility with docker-compose in how symlinks to docker-compose.yml are handled.
|
||||
- Fixed freeze caused by too long log lines without a newline.
|
||||
- Fixed support for `network_mode: none`.
|
||||
- Improved error detection by rejecting service definitions that contain both `network_mode` and
|
||||
`networks` keys, which is not allowed.
|
||||
|
||||
|
||||
Features
|
||||
--------
|
||||
|
||||
- Added support for build labels.
|
||||
- Added support for "platform" property in the build command.
|
||||
- Added support for "ssh" property in the build command.
|
||||
- Added support for cache_from and cache_to fields in build section.
|
||||
- Added support for honoring the condition in the depends_on section of the service, if stated.
|
||||
- Added `x-podman.no_hosts` setting to pass `--no-hosts` to podman run
|
||||
- Added support for compatibility with docker compose for default network behavior when no network
|
||||
defined in service. This is controlled via `default_net_behavior_compat` feature flag.
|
||||
- Added a way to get compatibility of default network names with docker compose.
|
||||
This is selected by setting `default_net_name_compat: true` on `x-podman` global dictionary.
|
||||
- Added support for the `device_cgroup_rules` property in services.
|
||||
- Added support for removing networks in `podman-compose down`.
|
||||
- Added support for network scoped service aliases.
|
||||
- Added support for network level `mac_address` attribute.
|
||||
- Added ability to substitute variables with the environment of the service.
|
||||
|
||||
Misc
|
||||
----
|
||||
|
||||
- Declared compatibility with Python 3.13.
|
39
docs/Changelog-1.4.0.md
Normal file
39
docs/Changelog-1.4.0.md
Normal file
@ -0,0 +1,39 @@
|
||||
Version 1.4.0 (2025-05-10)
|
||||
==========================
|
||||
|
||||
Bug fixes
|
||||
---------
|
||||
|
||||
- Fixed handling of relative includes and extends in compose files
|
||||
- Fixed error when merging arguments in list and dictionary syntax
|
||||
- Fixed issue where short-lived containers could execute twice when using `up` in detached mode
|
||||
- Fixed `up` command hanging on Podman versions earlier than 4.6.0
|
||||
- Fixed issue where `service_healthy` conditions weren't enforced during `up` command
|
||||
- Fixed support for the `--scale` flag
|
||||
- Fixed bug causing dependent containers to start despite `--no-deps` flag
|
||||
- Fixed port command behavior for dynamic host ports
|
||||
- Fixed interpolation of `COMPOSE_PROJECT_NAME` when set from top-level `name` in compose file
|
||||
- Fixed project name evaluation order to match compose spec
|
||||
- Fixed build context when using git URLs
|
||||
- Fixed `KeyError` when `down` is called with non-existent service
|
||||
- Skip `down` during `up` when no active containers exist
|
||||
- Fixed non-zero exit code on failure when using `up -d`
|
||||
- Fixed SIGINT handling during `up` command for graceful shutdown
|
||||
- Fixed `NotImplementedError` when interrupted on Windows
|
||||
|
||||
Features
|
||||
--------
|
||||
|
||||
- Added `--quiet` flag to `config` command to suppress output
|
||||
- Added support for `pids_limit` and `deploy.resources.limits.pids`
|
||||
- Added `--abort-on-container-failure` option
|
||||
- Added `--rmi` argument to `down` command for image removal
|
||||
- Added support for `x-podman.disable-dns` to disable DNS plugin on defined networks
|
||||
- Added support for `x-podman.dns` to set DNS nameservers for defined networks
|
||||
- Improved file descriptor handling - no longer closes externally created descriptors.
|
||||
This allows descriptors created e.g. via systemd socket activation to be passed to
|
||||
containers.
|
||||
- Added support for `cpuset` configuration
|
||||
- Added support for `reset` and `override` tags when merging compose files
|
||||
- Added support for `x-podman.interface_name` to set network interface names
|
||||
- Added support for `x-podman.pod_args` to override default `--pod-args`
|
7
docs/Changelog-1.4.1.md
Normal file
7
docs/Changelog-1.4.1.md
Normal file
@ -0,0 +1,7 @@
|
||||
Version 1.4.1 (2025-06-05)
|
||||
==========================
|
||||
|
||||
Bug fixes
|
||||
---------
|
||||
|
||||
- Fixed relative host path resolution for volume bind mount source
|
@ -7,13 +7,15 @@ are generally specified under fields with "x-podman" prefix in the compose file.
|
||||
|
||||
The following extension keys are available under container configuration:
|
||||
|
||||
* `x-podman.uidmap` - Run the container in a new user namespace using the supplied UID mapping.
|
||||
* `x-podman.uidmaps` - Run the container in a new user namespace using the supplied UID mapping.
|
||||
|
||||
* `x-podman.gidmap` - Run the container in a new user namespace using the supplied GID mapping.
|
||||
* `x-podman.gidmaps` - Run the container in a new user namespace using the supplied GID mapping.
|
||||
|
||||
* `x-podman.rootfs` - Run the container without requiring any image management; the rootfs of the
|
||||
container is assumed to be managed externally.
|
||||
|
||||
* `x-podman.no_hosts` - Run the container without creating /etc/hosts file
|
||||
|
||||
For example, the following docker-compose.yml allows running a podman container with externally managed rootfs.
|
||||
```yml
|
||||
version: "3"
|
||||
@ -25,6 +27,26 @@ services:
|
||||
|
||||
For explanations of these extensions, please refer to the [Podman Documentation](https://docs.podman.io/).
|
||||
|
||||
## Network management
|
||||
|
||||
The following extension keys are available under network configuration:
|
||||
|
||||
* `x-podman.disable-dns` - Disable the DNS plugin for the network when set to 'true'.
|
||||
* `x-podman.dns` - Set nameservers for the network using supplied addresses (cannot be used with x-podman.disable-dns`).
|
||||
|
||||
For example, the following docker-compose.yml allows all containers on the same network to use the
|
||||
specified nameservers:
|
||||
```yml
|
||||
version: "3"
|
||||
network:
|
||||
my_network:
|
||||
x-podman.dns:
|
||||
- "10.1.2.3"
|
||||
- "10.1.2.4"
|
||||
```
|
||||
|
||||
For explanations of these extensions, please refer to the
|
||||
[Podman network create command Documentation](https://docs.podman.io/en/latest/markdown/podman-network-create.1.html).
|
||||
|
||||
## Per-network MAC-addresses
|
||||
|
||||
@ -36,6 +58,12 @@ Podman-compose in addition supports the specification of MAC addresses on a per-
|
||||
is done by adding a `x-podman.mac_address` key to the network configuration in the container. The
|
||||
value of the `x-podman.mac_address` key is the MAC address to be used for the network interface.
|
||||
|
||||
Note that the [compose spec](https://github.com/compose-spec/compose-spec/blob/main/05-services.md#mac_address)
|
||||
now supports `mac_address` on the network level, so we recommend using
|
||||
the standard `mac_address` key for setting the MAC address. The
|
||||
`x-podman.mac_address` is still supported for backwards compatibility.
|
||||
|
||||
|
||||
Specifying a MAC address for the container and for individual networks at the same time is not
|
||||
supported.
|
||||
|
||||
@ -58,7 +86,7 @@ networks:
|
||||
- subnet: "192.168.1.0/24"
|
||||
|
||||
services:
|
||||
webserver
|
||||
webserver:
|
||||
image: "busybox"
|
||||
command: ["/bin/busybox", "httpd", "-f", "-h", "/etc", "-p", "8001"]
|
||||
networks:
|
||||
@ -67,9 +95,13 @@ services:
|
||||
x-podman.mac_address: "02:aa:aa:aa:aa:aa"
|
||||
net1:
|
||||
ipv4_address: "192.168.1.10"
|
||||
x-podman.mac_address: "02:bb:bb:bb:bb:bb"
|
||||
mac_address: "02:bb:bb:bb:bb:bb" # mac_address is supported
|
||||
```
|
||||
|
||||
## Per-network interface name
|
||||
|
||||
Using `x-podman.interface_name` within a containers network config you can specify the interface name inside the container.
|
||||
|
||||
## Podman-specific network modes
|
||||
|
||||
Generic docker-compose supports the following values for `network-mode` for a container:
|
||||
@ -91,6 +123,43 @@ The options to the network modes are passed to the `--network` option of the `po
|
||||
as-is.
|
||||
|
||||
|
||||
## Compatibility of default network names between docker-compose and podman-compose
|
||||
|
||||
Current versions of podman-compose may produce different default external network names than
|
||||
docker-compose under certain conditions. Specifically, docker-compose removes dashes (`-` character)
|
||||
from project name.
|
||||
|
||||
To enable compatibility between docker-compose and podman-compose, specify
|
||||
`default_net_name_compat: true` under global `x-podman` key:
|
||||
|
||||
```
|
||||
x-podman:
|
||||
default_net_name_compat: true
|
||||
```
|
||||
|
||||
By default `default_net_name_compat` is `false`. This will change to `true` at some point and the
|
||||
setting will be removed.
|
||||
|
||||
## Compatibility of default network behavior between docker-compose and podman-compose
|
||||
|
||||
When there is no network defined (neither network-mode nor networks) in service,
|
||||
The behavior of default network in docker-compose and podman-compose are different.
|
||||
|
||||
| Top-level networks | podman-compose | docker-compose |
|
||||
| ------------------------------ | -------------------------- | -------------- |
|
||||
| No networks | default | default |
|
||||
| One network named net0 | net0 | default |
|
||||
| Two networks named net0, net1 | podman(`--network=bridge`) | default |
|
||||
| Contains network named default | default | default |
|
||||
|
||||
To enable compatibility between docker-compose and podman-compose, specify
|
||||
`default_net_behavior_compat: true` under global `x-podman` key:
|
||||
|
||||
```yaml
|
||||
x-podman:
|
||||
default_net_behavior_compat: true
|
||||
```
|
||||
|
||||
## Custom pods management
|
||||
|
||||
Podman-compose can have containers in pods. This can be controlled by extension key x-podman in_pod.
|
||||
@ -109,3 +178,17 @@ services:
|
||||
x-podman:
|
||||
in_pod: false
|
||||
```
|
||||
|
||||
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
|
||||
version: "3"
|
||||
services:
|
||||
cont:
|
||||
image: nopush/podman-compose-test
|
||||
command: ["dumb-init", "/bin/busybox", "httpd", "-f", "-p", "8080"]
|
||||
x-podman:
|
||||
pod_args: ["--infra=false", "--share=", "--cpus=1"]
|
||||
```
|
||||
When not set in docker-compose.yml or on the command line, the pod args default
|
||||
to `["--infra=false", "--share="]`.
|
||||
|
9
examples/docker-inline/docker-compose.yml
Normal file
9
examples/docker-inline/docker-compose.yml
Normal file
@ -0,0 +1,9 @@
|
||||
---
|
||||
version: '3'
|
||||
services:
|
||||
dummy:
|
||||
build:
|
||||
context: .
|
||||
dockerfile_inline: |
|
||||
FROM alpine
|
||||
RUN echo "hello world"
|
1588
podman_compose.py
1588
podman_compose.py
File diff suppressed because it is too large
Load Diff
@ -13,3 +13,43 @@ force-single-line = true
|
||||
[tool.ruff.format]
|
||||
preview = true # needed for quote-style
|
||||
quote-style = "preserve"
|
||||
|
||||
[tool.towncrier]
|
||||
package = "podman_compose"
|
||||
package_dir = "master"
|
||||
directory = "newsfragments"
|
||||
filename = "docs/Changelog-new.md"
|
||||
template = "scripts/Changelog-template.jinja"
|
||||
title_format = "Version {version} ({project_date})"
|
||||
[[tool.towncrier.section]]
|
||||
path = ""
|
||||
|
||||
[[tool.towncrier.type]]
|
||||
directory = "feature"
|
||||
name = "Features"
|
||||
showcontent = true
|
||||
|
||||
[[tool.towncrier.type]]
|
||||
directory = "change"
|
||||
name = "Changes"
|
||||
showcontent = true
|
||||
|
||||
[[tool.towncrier.type]]
|
||||
directory = "bugfix"
|
||||
name = "Bug fixes"
|
||||
showcontent = true
|
||||
|
||||
[[tool.towncrier.type]]
|
||||
directory = "doc"
|
||||
name = "Improved Documentation"
|
||||
showcontent = true
|
||||
|
||||
[[tool.towncrier.type]]
|
||||
directory = "removal"
|
||||
name = "Deprecations and Removals"
|
||||
showcontent = true
|
||||
|
||||
[[tool.towncrier.type]]
|
||||
directory = "misc"
|
||||
name = "Misc"
|
||||
showcontent = true
|
||||
|
@ -1,91 +0,0 @@
|
||||
# SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
import unittest
|
||||
|
||||
from podman_compose import container_to_args
|
||||
|
||||
from .test_container_to_args import create_compose_mock
|
||||
from .test_container_to_args import get_minimal_container
|
||||
|
||||
|
||||
class TestContainerToArgsSecrets(unittest.IsolatedAsyncioTestCase):
|
||||
async def test_pass_secret_as_env_variable(self):
|
||||
c = create_compose_mock()
|
||||
c.declared_secrets = {
|
||||
"my_secret": {"external": "true"} # must have external or name value
|
||||
}
|
||||
|
||||
cnt = get_minimal_container()
|
||||
cnt["secrets"] = [
|
||||
{
|
||||
"source": "my_secret",
|
||||
"target": "ENV_SECRET",
|
||||
"type": "env",
|
||||
},
|
||||
]
|
||||
|
||||
args = await container_to_args(c, cnt)
|
||||
self.assertEqual(
|
||||
args,
|
||||
[
|
||||
"--name=project_name_service_name1",
|
||||
"-d",
|
||||
"--network=bridge",
|
||||
"--network-alias=service_name",
|
||||
"--secret",
|
||||
"my_secret,type=env,target=ENV_SECRET",
|
||||
"busybox",
|
||||
],
|
||||
)
|
||||
|
||||
async def test_secret_as_env_external_true_has_no_name(self):
|
||||
c = create_compose_mock()
|
||||
c.declared_secrets = {
|
||||
"my_secret": {
|
||||
"name": "my_secret", # must have external or name value
|
||||
}
|
||||
}
|
||||
|
||||
cnt = get_minimal_container()
|
||||
cnt["_service"] = "test-service"
|
||||
cnt["secrets"] = [
|
||||
{
|
||||
"source": "my_secret",
|
||||
"target": "ENV_SECRET",
|
||||
"type": "env",
|
||||
}
|
||||
]
|
||||
|
||||
args = await container_to_args(c, cnt)
|
||||
self.assertEqual(
|
||||
args,
|
||||
[
|
||||
"--name=project_name_service_name1",
|
||||
"-d",
|
||||
"--network=bridge",
|
||||
"--network-alias=service_name",
|
||||
"--secret",
|
||||
"my_secret,type=env,target=ENV_SECRET",
|
||||
"busybox",
|
||||
],
|
||||
)
|
||||
|
||||
async def test_pass_secret_as_env_variable_no_external(self):
|
||||
c = create_compose_mock()
|
||||
c.declared_secrets = {
|
||||
"my_secret": {} # must have external or name value
|
||||
}
|
||||
|
||||
cnt = get_minimal_container()
|
||||
cnt["_service"] = "test-service"
|
||||
cnt["secrets"] = [
|
||||
{
|
||||
"source": "my_secret",
|
||||
"target": "ENV_SECRET",
|
||||
"type": "env",
|
||||
}
|
||||
]
|
||||
|
||||
with self.assertRaises(ValueError) as context:
|
||||
await container_to_args(c, cnt)
|
||||
self.assertIn('ERROR: unparsable secret: ', str(context.exception))
|
33
scripts/Changelog-template.jinja
Normal file
33
scripts/Changelog-template.jinja
Normal file
@ -0,0 +1,33 @@
|
||||
{% for section, _ in sections|dictsort(by='key') %}
|
||||
{% set underline = "-" %}
|
||||
{% if section %}
|
||||
{{section}}
|
||||
{{ underline * section|length }}{% set underline = "~" %}
|
||||
|
||||
{% endif %}
|
||||
{% if sections[section] %}
|
||||
{% for category, val in definitions|dictsort if category in sections[section]%}
|
||||
|
||||
{{ definitions[category]['name'] }}
|
||||
{{ underline * definitions[category]['name']|length }}
|
||||
|
||||
{% for text, values in sections[section][category]|dictsort(by='value') %}
|
||||
- {{ text }}
|
||||
{% endfor %}
|
||||
|
||||
{% if sections[section][category]|length == 0 %}
|
||||
|
||||
No significant changes.
|
||||
|
||||
|
||||
{% else %}
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
{% else %}
|
||||
|
||||
No significant changes.
|
||||
|
||||
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
(venv) p12@exec-desktop:~/cod
|
16
scripts/download_and_build_podman-compose.sh
Normal file
16
scripts/download_and_build_podman-compose.sh
Normal file
@ -0,0 +1,16 @@
|
||||
#!/bin/sh
|
||||
|
||||
# Delete repository dir
|
||||
rm -rf podman-compose-src
|
||||
|
||||
# Clone repository
|
||||
git clone https://github.com/containers/podman-compose podman-compose-src
|
||||
|
||||
# Generate binary
|
||||
sh podman-compose-src/scripts/generate_binary_using_dockerfile.sh
|
||||
|
||||
# Move binary outside repo's dir
|
||||
mv podman-compose-src/podman-compose .
|
||||
|
||||
# Delete repository dir
|
||||
rm -rf podman-compose-src
|
57
scripts/generate_binary_using_dockerfile.sh
Normal file
57
scripts/generate_binary_using_dockerfile.sh
Normal file
@ -0,0 +1,57 @@
|
||||
#!/bin/sh
|
||||
|
||||
# Find an available container tool (docker or podman)
|
||||
find_container_tool() {
|
||||
if command -v docker > /dev/null 2>&1; then
|
||||
echo "sudo docker"
|
||||
elif command -v podman > /dev/null 2>&1; then
|
||||
echo "podman"
|
||||
else
|
||||
echo "Error: Neither docker nor podman is available." >&2
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
# Determine which container tool to use
|
||||
CONTAINER_TOOL=$(find_container_tool)
|
||||
|
||||
# Locate the directory containing dockerfile (root)
|
||||
PROJECT_ROOT_DIR="$(cd "$(dirname "$0")" && pwd)/.."
|
||||
|
||||
# Check SELinux status and set appropriate mount option
|
||||
check_selinux() {
|
||||
if command -v getenforce > /dev/null 2>&1; then
|
||||
SELINUX_STATUS=$(getenforce)
|
||||
if [ "$SELINUX_STATUS" = "Enforcing" ] || [ "$SELINUX_STATUS" = "Permissive" ]; then
|
||||
echo ":z"
|
||||
else
|
||||
echo ""
|
||||
fi
|
||||
elif [ -f /sys/fs/selinux/enforce ]; then
|
||||
if [ "$(cat /sys/fs/selinux/enforce)" = "1" ]; then
|
||||
echo ":z"
|
||||
else
|
||||
echo ""
|
||||
fi
|
||||
else
|
||||
echo ""
|
||||
fi
|
||||
}
|
||||
|
||||
# Get the SELinux option for volume mounts if SELinux is enforcing or permissive
|
||||
SELINUX=$(check_selinux)
|
||||
|
||||
# Build binary
|
||||
$CONTAINER_TOOL image rm build-podman-compose
|
||||
|
||||
if expr "$CONTAINER_TOOL" : '.*docker.*' >/dev/null; then
|
||||
$CONTAINER_TOOL build -t build-podman-compose "$PROJECT_ROOT_DIR"
|
||||
$CONTAINER_TOOL run --name build-podman-compose build-podman-compose
|
||||
$CONTAINER_TOOL cp build-podman-compose:/result/podman-compose "$PROJECT_ROOT_DIR/podman-compose"
|
||||
$CONTAINER_TOOL container stop build-podman-compose
|
||||
$CONTAINER_TOOL container rm -f build-podman-compose
|
||||
else
|
||||
$CONTAINER_TOOL build -v "$PROJECT_ROOT_DIR:/result$SELINUX" -t build-podman-compose "$PROJECT_ROOT_DIR"
|
||||
fi
|
||||
$CONTAINER_TOOL image rm python:3.11-slim
|
||||
$CONTAINER_TOOL image rm build-podman-compose
|
@ -1,6 +1,18 @@
|
||||
#!/usr/bin/env bash
|
||||
./scripts/uninstall.sh
|
||||
./scripts/clean_up.sh
|
||||
python3 setup.py register
|
||||
python3 setup.py sdist bdist_wheel
|
||||
twine upload dist/*
|
||||
#!/bin/bash
|
||||
|
||||
set -e
|
||||
|
||||
if [ $# -ne 1 ]; then
|
||||
echo "Usage: make_release.sh VERSION"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
VERSION=$1
|
||||
|
||||
sed "s/__version__ = .*/__version__ = \"$VERSION\"/g" -i podman_compose.py
|
||||
git add podman_compose.py
|
||||
git commit -m "Release $VERSION"
|
||||
|
||||
git tag "v$VERSION" -m "v$VERSION" -s
|
||||
|
||||
git push ssh://github.com/containers/podman-compose main "v$VERSION"
|
||||
|
14
scripts/make_release_notes.sh
Executable file
14
scripts/make_release_notes.sh
Executable file
@ -0,0 +1,14 @@
|
||||
#!/bin/bash
|
||||
|
||||
set -e
|
||||
|
||||
if [ $# -ne 1 ]; then
|
||||
echo "Usage: make_release_notes.sh VERSION"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
VERSION=$1
|
||||
towncrier build --version "$VERSION" --yes
|
||||
git mv "docs/Changelog-new.md" "docs/Changelog-$VERSION.md"
|
||||
git add "newsfragments/"
|
||||
git commit -m "Release notes for $VERSION"
|
6
scripts/make_release_upload.sh
Executable file
6
scripts/make_release_upload.sh
Executable file
@ -0,0 +1,6 @@
|
||||
#!/usr/bin/env bash
|
||||
./scripts/uninstall.sh
|
||||
./scripts/clean_up.sh
|
||||
python3 setup.py register
|
||||
python3 setup.py sdist bdist_wheel
|
||||
twine upload dist/*
|
@ -6,4 +6,6 @@ version = attr: podman_compose.__version__
|
||||
|
||||
[flake8]
|
||||
# The GitHub editor is 127 chars wide
|
||||
max-line-length=127
|
||||
max-line-length=127
|
||||
# These are not being followed yet
|
||||
ignore=E222,E231,E272,E713,W503
|
4
setup.py
4
setup.py
@ -17,11 +17,11 @@ setup(
|
||||
classifiers=[
|
||||
"Programming Language :: Python",
|
||||
"Programming Language :: Python :: 3",
|
||||
"Programming Language :: Python :: 3.7",
|
||||
"Programming Language :: Python :: 3.8",
|
||||
"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",
|
||||
|
@ -29,5 +29,6 @@ pluggy==1.4.0
|
||||
pyproject-api==1.6.1
|
||||
python-dotenv==1.0.1
|
||||
PyYAML==6.0.1
|
||||
requests
|
||||
tomlkit==0.12.4
|
||||
virtualenv==20.25.1
|
||||
virtualenv==20.26.6
|
||||
|
@ -1,25 +0,0 @@
|
||||
# Test podman-compose with build
|
||||
|
||||
```
|
||||
podman-compose build
|
||||
podman-compose up -d
|
||||
curl http://localhost:8080/index.txt
|
||||
curl http://localhost:8000/index.txt
|
||||
podman inspect my-busybox-httpd2
|
||||
podman-compose down
|
||||
```
|
||||
|
||||
expected output would be something like
|
||||
|
||||
```
|
||||
2019-09-03T15:16:38+0000
|
||||
ALT buildno=2 port 8000 2019-09-03T15:16:38+0000
|
||||
{
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
as you can see we were able to override buildno to be 2 instead of 1,
|
||||
and httpd_port to 8000.
|
||||
|
||||
NOTE: build labels are not passed to `podman build`
|
@ -1,22 +0,0 @@
|
||||
# Test podman-compose with build (fail scenario)
|
||||
|
||||
```shell
|
||||
podman-compose build || echo $?
|
||||
```
|
||||
|
||||
expected output would be something like
|
||||
|
||||
```
|
||||
STEP 1/3: FROM busybox
|
||||
STEP 2/3: RUN this_command_does_not_exist
|
||||
/bin/sh: this_command_does_not_exist: not found
|
||||
Error: building at STEP "RUN this_command_does_not_exist": while running runtime: exit status 127
|
||||
|
||||
exit code: 127
|
||||
```
|
||||
|
||||
Expected `podman-compose` exit code:
|
||||
```shell
|
||||
echo $?
|
||||
127
|
||||
```
|
@ -1,4 +0,0 @@
|
||||
|
||||
```
|
||||
podman-compose run --rm sleep /bin/sh -c 'wget -O - http://web:8000/hosts'
|
||||
```
|
@ -1,37 +0,0 @@
|
||||
running the following commands should always give podman-rocks-123
|
||||
|
||||
```
|
||||
podman-compose -f project/container-compose.yaml --env-file env-files/project-1.env up
|
||||
```
|
||||
|
||||
```
|
||||
podman-compose -f $(pwd)/project/container-compose.yaml --env-file $(pwd)/env-files/project-1.env up
|
||||
```
|
||||
|
||||
```
|
||||
podman-compose -f $(pwd)/project/container-compose.env-file-flat.yaml up
|
||||
```
|
||||
|
||||
```
|
||||
podman-compose -f $(pwd)/project/container-compose.env-file-obj.yaml up
|
||||
```
|
||||
|
||||
```
|
||||
podman-compose -f $(pwd)/project/container-compose.env-file-obj-optional.yaml up
|
||||
```
|
||||
|
||||
based on environment variable precedent this command should give podman-rocks-321
|
||||
|
||||
```
|
||||
ZZVAR1=podman-rocks-321 podman-compose -f $(pwd)/project/container-compose.yaml --env-file $(pwd)/env-files/project-1.env up
|
||||
```
|
||||
|
||||
_The below test should print three environment variables_
|
||||
|
||||
```
|
||||
podman-compose -f $(pwd)/project/container-compose.load-.env-in-project.yaml run --rm app
|
||||
|
||||
ZZVAR1=This value is overwritten by env-file-tests/.env
|
||||
ZZVAR2=This value is loaded from .env in project/ directory
|
||||
ZZVAR3=This value is loaded from env-file-tests/.env
|
||||
```
|
@ -1,5 +0,0 @@
|
||||
running the following command should give myval2
|
||||
|
||||
```
|
||||
podman_compose run -l monkey -e ZZVAR1=myval2 env-test
|
||||
```
|
@ -1,9 +0,0 @@
|
||||
version: '3'
|
||||
|
||||
services:
|
||||
env-test:
|
||||
image: busybox
|
||||
command: sh -c "export | grep ZZ"
|
||||
environment:
|
||||
- ZZVAR1=myval1
|
||||
|
@ -1,15 +0,0 @@
|
||||
We have service named sh1 that exits with code 1 and sh2 that exists with code 2
|
||||
|
||||
```
|
||||
podman-compose up --exit-code-from=sh1
|
||||
echo $?
|
||||
```
|
||||
|
||||
the above should give 1.
|
||||
|
||||
```
|
||||
podman-compose up --exit-code-from=sh2
|
||||
echo $?
|
||||
```
|
||||
|
||||
the above should give 2.
|
@ -1,21 +1,11 @@
|
||||
version: "3"
|
||||
services:
|
||||
too_long:
|
||||
image: nopush/podman-compose-test
|
||||
command: ["dumb-init", "/bin/busybox", "sh", "-c", "sleep 3600; exit 0"]
|
||||
tmpfs:
|
||||
- /run
|
||||
- /tmp
|
||||
sh1:
|
||||
image: nopush/podman-compose-test
|
||||
command: ["dumb-init", "/bin/busybox", "sh", "-c", "sleep 1; exit 1"]
|
||||
tmpfs:
|
||||
- /run
|
||||
- /tmp
|
||||
sh2:
|
||||
image: nopush/podman-compose-test
|
||||
command: ["dumb-init", "/bin/busybox", "sh", "-c", "sleep 1; exit 2"]
|
||||
tmpfs:
|
||||
- /run
|
||||
- /tmp
|
||||
|
||||
command: ["dumb-init", "/bin/busybox", "sh", "-c", "sleep 2; exit 0"]
|
||||
sh3:
|
||||
image: nopush/podman-compose-test
|
||||
command: ["dumb-init", "/bin/busybox", "sh", "-c", "sleep 3; exit 0"]
|
11
tests/integration/abort/docker-compose-fail-none.yaml
Normal file
11
tests/integration/abort/docker-compose-fail-none.yaml
Normal file
@ -0,0 +1,11 @@
|
||||
version: "3"
|
||||
services:
|
||||
sh1:
|
||||
image: nopush/podman-compose-test
|
||||
command: ["dumb-init", "/bin/busybox", "sh", "-c", "sleep 1; exit 0"]
|
||||
sh2:
|
||||
image: nopush/podman-compose-test
|
||||
command: ["dumb-init", "/bin/busybox", "sh", "-c", "sleep 2; exit 0"]
|
||||
sh3:
|
||||
image: nopush/podman-compose-test
|
||||
command: ["dumb-init", "/bin/busybox", "sh", "-c", "sleep 3; exit 0"]
|
11
tests/integration/abort/docker-compose-fail-second.yaml
Normal file
11
tests/integration/abort/docker-compose-fail-second.yaml
Normal file
@ -0,0 +1,11 @@
|
||||
version: "3"
|
||||
services:
|
||||
sh1:
|
||||
image: nopush/podman-compose-test
|
||||
command: ["dumb-init", "/bin/busybox", "sh", "-c", "sleep 1; exit 0"]
|
||||
sh2:
|
||||
image: nopush/podman-compose-test
|
||||
command: ["dumb-init", "/bin/busybox", "sh", "-c", "sleep 2; exit 1"]
|
||||
sh3:
|
||||
image: nopush/podman-compose-test
|
||||
command: ["dumb-init", "/bin/busybox", "sh", "-c", "sleep 3; exit 0"]
|
@ -0,0 +1,11 @@
|
||||
version: "3"
|
||||
services:
|
||||
sh1:
|
||||
image: nopush/podman-compose-test
|
||||
command: ["dumb-init", "/bin/busybox", "sh", "-c", "sleep 1; exit 1"]
|
||||
sh2:
|
||||
image: nopush/podman-compose-test
|
||||
command: ["dumb-init", "/bin/busybox", "sh", "-c", "sleep 1; exit 0"]
|
||||
sh3:
|
||||
image: nopush/podman-compose-test
|
||||
command: ["dumb-init", "/bin/busybox", "sh", "-c", "sleep 2; exit 0"]
|
46
tests/integration/abort/test_podman_compose_abort.py
Normal file
46
tests/integration/abort/test_podman_compose_abort.py
Normal file
@ -0,0 +1,46 @@
|
||||
# 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
|
||||
|
||||
|
||||
def compose_yaml_path(failure_order):
|
||||
return os.path.join(test_path(), "abort", f"docker-compose-fail-{failure_order}.yaml")
|
||||
|
||||
|
||||
class TestComposeAbort(unittest.TestCase, RunSubprocessMixin):
|
||||
@parameterized.expand([
|
||||
("exit", "first", 0),
|
||||
("failure", "first", 1),
|
||||
("exit", "second", 0),
|
||||
("failure", "second", 1),
|
||||
("exit", "simultaneous", 0),
|
||||
("failure", "simultaneous", 1),
|
||||
("exit", "none", 0),
|
||||
("failure", "none", 0),
|
||||
])
|
||||
def test_abort(self, abort_type, failure_order, expected_exit_code):
|
||||
try:
|
||||
self.run_subprocess_assert_returncode(
|
||||
[
|
||||
podman_compose_path(),
|
||||
"-f",
|
||||
compose_yaml_path(failure_order),
|
||||
"up",
|
||||
f"--abort-on-container-{abort_type}",
|
||||
],
|
||||
expected_exit_code,
|
||||
)
|
||||
finally:
|
||||
self.run_subprocess_assert_returncode([
|
||||
podman_compose_path(),
|
||||
"-f",
|
||||
compose_yaml_path(failure_order),
|
||||
"down",
|
||||
])
|
1
tests/integration/additional_contexts/__init__.py
Normal file
1
tests/integration/additional_contexts/__init__.py
Normal file
@ -0,0 +1 @@
|
||||
|
@ -7,8 +7,8 @@ import os
|
||||
import subprocess
|
||||
import unittest
|
||||
|
||||
from .test_podman_compose import podman_compose_path
|
||||
from .test_podman_compose import test_path
|
||||
from tests.integration.test_utils import podman_compose_path
|
||||
from tests.integration.test_utils import test_path
|
||||
|
||||
|
||||
def compose_yaml_path():
|
1
tests/integration/build/__init__.py
Normal file
1
tests/integration/build/__init__.py
Normal file
@ -0,0 +1 @@
|
||||
|
1
tests/integration/build/git_url_context/__init__.py
Normal file
1
tests/integration/build/git_url_context/__init__.py
Normal file
@ -0,0 +1 @@
|
||||
|
@ -0,0 +1,9 @@
|
||||
version: "3"
|
||||
services:
|
||||
test_context:
|
||||
build:
|
||||
context: https://github.com/mokibit/test-git-url-as-context.git
|
||||
image: test-git-url-as-context
|
||||
test_context_inline:
|
||||
build: https://github.com/mokibit/test-git-url-as-context.git
|
||||
image: test-git-url-as-context-inline
|
@ -0,0 +1,55 @@
|
||||
# SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
import os
|
||||
from parameterized import parameterized
|
||||
import unittest
|
||||
|
||||
from tests.integration.test_utils import podman_compose_path
|
||||
from tests.integration.test_utils import test_path
|
||||
from tests.integration.test_utils import RunSubprocessMixin
|
||||
|
||||
|
||||
def compose_yaml_path():
|
||||
""" "Returns the path to the compose file used for this test module"""
|
||||
base_path = os.path.join(test_path(), "build/git_url_context")
|
||||
return os.path.join(base_path, "docker-compose.yml")
|
||||
|
||||
|
||||
class TestComposeBuildGitUrlAsContext(unittest.TestCase, RunSubprocessMixin):
|
||||
@parameterized.expand([
|
||||
("git_url_context_test_context_1", "data_1.txt", b'test1\r\n'),
|
||||
("git_url_context_test_context_1", "data_2.txt", b'test2\r\n'),
|
||||
("git_url_context_test_context_inline_1", "data_1.txt", b'test1\r\n'),
|
||||
("git_url_context_test_context_inline_1", "data_2.txt", b'test2\r\n'),
|
||||
])
|
||||
def test_build_git_url_as_context(self, container_name, file_name, output):
|
||||
# test if container can access specific files from git repository when git url is used as
|
||||
# a build context
|
||||
try:
|
||||
out, _ = self.run_subprocess_assert_returncode([
|
||||
podman_compose_path(),
|
||||
"-f",
|
||||
compose_yaml_path(),
|
||||
"up",
|
||||
"-d",
|
||||
])
|
||||
|
||||
out, _ = self.run_subprocess_assert_returncode([
|
||||
"podman",
|
||||
"exec",
|
||||
"-ti",
|
||||
f"{container_name}",
|
||||
"sh",
|
||||
"-c",
|
||||
f"cat {file_name}",
|
||||
])
|
||||
self.assertEqual(out, output)
|
||||
finally:
|
||||
out, _ = self.run_subprocess_assert_returncode([
|
||||
podman_compose_path(),
|
||||
"-f",
|
||||
compose_yaml_path(),
|
||||
"down",
|
||||
"-t",
|
||||
"0",
|
||||
])
|
65
tests/integration/build/test_podman_compose_build.py
Normal file
65
tests/integration/build/test_podman_compose_build.py
Normal file
@ -0,0 +1,65 @@
|
||||
# SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
import os
|
||||
import unittest
|
||||
|
||||
import requests
|
||||
|
||||
from tests.integration.test_utils import podman_compose_path
|
||||
from tests.integration.test_utils import test_path
|
||||
from tests.integration.test_utils import RunSubprocessMixin
|
||||
|
||||
|
||||
def compose_yaml_path():
|
||||
""" "Returns the path to the compose file used for this test module"""
|
||||
base_path = os.path.join(test_path(), "build")
|
||||
return os.path.join(base_path, "docker-compose.yml")
|
||||
|
||||
|
||||
class TestComposeBuild(unittest.TestCase, RunSubprocessMixin):
|
||||
def test_build(self):
|
||||
try:
|
||||
self.run_subprocess_assert_returncode([
|
||||
podman_compose_path(),
|
||||
"-f",
|
||||
compose_yaml_path(),
|
||||
"build",
|
||||
"--no-cache",
|
||||
])
|
||||
|
||||
self.run_subprocess_assert_returncode([
|
||||
podman_compose_path(),
|
||||
"-f",
|
||||
compose_yaml_path(),
|
||||
"up",
|
||||
"-d",
|
||||
])
|
||||
|
||||
request = requests.get('http://localhost:8080/index.txt')
|
||||
self.assertEqual(request.status_code, 200)
|
||||
|
||||
alt_request_success = False
|
||||
try:
|
||||
# FIXME: suspicious behaviour, too often ends up in error
|
||||
alt_request = requests.get('http://localhost:8000/index.txt')
|
||||
self.assertEqual(alt_request.status_code, 200)
|
||||
self.assertIn("ALT buildno=2 port=8000 ", alt_request.text)
|
||||
alt_request_success = True
|
||||
except requests.exceptions.ConnectionError:
|
||||
pass
|
||||
|
||||
if alt_request_success:
|
||||
output, _ = self.run_subprocess_assert_returncode([
|
||||
"podman",
|
||||
"inspect",
|
||||
"my-busybox-httpd2",
|
||||
])
|
||||
self.assertIn("httpd_port=8000", str(output))
|
||||
self.assertIn("buildno=2", str(output))
|
||||
finally:
|
||||
self.run_subprocess_assert_returncode([
|
||||
podman_compose_path(),
|
||||
"-f",
|
||||
compose_yaml_path(),
|
||||
"down",
|
||||
])
|
1
tests/integration/build_fail/__init__.py
Normal file
1
tests/integration/build_fail/__init__.py
Normal file
@ -0,0 +1 @@
|
||||
|
@ -0,0 +1,30 @@
|
||||
# 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")
|
||||
return os.path.join(base_path, "docker-compose.yml")
|
||||
|
||||
|
||||
class TestComposeBuildFail(unittest.TestCase, RunSubprocessMixin):
|
||||
def test_build_fail(self):
|
||||
output, error = self.run_subprocess_assert_returncode(
|
||||
[
|
||||
podman_compose_path(),
|
||||
"-f",
|
||||
compose_yaml_path(),
|
||||
"build",
|
||||
],
|
||||
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))
|
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
tests/integration/build_labels/context/Dockerfile
Normal file
1
tests/integration/build_labels/context/Dockerfile
Normal file
@ -0,0 +1 @@
|
||||
FROM busybox
|
22
tests/integration/build_labels/docker-compose.yml
Normal file
22
tests/integration/build_labels/docker-compose.yml
Normal file
@ -0,0 +1,22 @@
|
||||
version: "3"
|
||||
services:
|
||||
test_build_labels_map:
|
||||
build:
|
||||
context: ./context
|
||||
dockerfile: Dockerfile
|
||||
labels:
|
||||
com.example.description: "Accounting webapp"
|
||||
com.example.department: "Finance"
|
||||
com.example.label-with-empty-value: ""
|
||||
image: my-busybox-build-labels-map
|
||||
command: env
|
||||
test_build_labels_array:
|
||||
build:
|
||||
context: ./context
|
||||
dockerfile: Dockerfile
|
||||
labels:
|
||||
- "com.example.description=Accounting webapp"
|
||||
- "com.example.department=Finance"
|
||||
- "com.example.label-with-empty-value"
|
||||
image: my-busybox-build-labels-array
|
||||
command: env
|
60
tests/integration/build_labels/test_build_labels.py
Normal file
60
tests/integration/build_labels/test_build_labels.py
Normal file
@ -0,0 +1,60 @@
|
||||
# SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
|
||||
import json
|
||||
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
|
||||
|
||||
|
||||
class TestBuildLabels(unittest.TestCase, RunSubprocessMixin):
|
||||
def test_build_labels(self):
|
||||
"""The build context can contain labels which should be added to the resulting image. They
|
||||
can be either an array or a map.
|
||||
"""
|
||||
|
||||
compose_path = os.path.join(test_path(), "build_labels/docker-compose.yml")
|
||||
|
||||
try:
|
||||
self.run_subprocess_assert_returncode([
|
||||
podman_compose_path(),
|
||||
"-f",
|
||||
compose_path,
|
||||
"build",
|
||||
"test_build_labels_map",
|
||||
"test_build_labels_array",
|
||||
])
|
||||
|
||||
expected_labels = {
|
||||
"com.example.department": "Finance",
|
||||
"com.example.description": "Accounting webapp",
|
||||
"com.example.label-with-empty-value": "",
|
||||
}
|
||||
|
||||
out, _ = self.run_subprocess_assert_returncode([
|
||||
"podman",
|
||||
"inspect",
|
||||
"my-busybox-build-labels-map",
|
||||
"my-busybox-build-labels-array",
|
||||
])
|
||||
|
||||
images = json.loads(out)
|
||||
self.assertEqual(len(images), 2)
|
||||
labels_map = images[0].get("Config", {}).get("Labels", {})
|
||||
labels_array = images[1].get("Config", {}).get("Labels", {})
|
||||
for k, v in expected_labels.items():
|
||||
self.assertIn(k, labels_map)
|
||||
self.assertEqual(labels_map[k], v)
|
||||
self.assertIn(k, labels_array)
|
||||
self.assertEqual(labels_array[k], v)
|
||||
|
||||
finally:
|
||||
self.run_subprocess_assert_returncode([
|
||||
"podman",
|
||||
"rmi",
|
||||
"my-busybox-build-labels-map",
|
||||
"my-busybox-build-labels-array",
|
||||
])
|
1
tests/integration/build_secrets/__init__.py
Normal file
1
tests/integration/build_secrets/__init__.py
Normal file
@ -0,0 +1 @@
|
||||
|
@ -7,8 +7,8 @@ import os
|
||||
import subprocess
|
||||
import unittest
|
||||
|
||||
from .test_podman_compose import podman_compose_path
|
||||
from .test_podman_compose import test_path
|
||||
from tests.integration.test_utils import podman_compose_path
|
||||
from tests.integration.test_utils import test_path
|
||||
|
||||
|
||||
def compose_yaml_path():
|
16
tests/integration/build_ssh/context/Dockerfile
Normal file
16
tests/integration/build_ssh/context/Dockerfile
Normal file
@ -0,0 +1,16 @@
|
||||
# Base image
|
||||
FROM alpine:latest
|
||||
|
||||
# Install OpenSSH client
|
||||
RUN apk add openssh
|
||||
|
||||
# Test the SSH agents during the build
|
||||
|
||||
RUN echo -n "default: " >> /result.log
|
||||
RUN --mount=type=ssh ssh-add -L >> /result.log
|
||||
|
||||
RUN echo -n "id1: " >> /result.log
|
||||
RUN --mount=type=ssh,id=id1 ssh-add -L >> /result.log
|
||||
|
||||
RUN echo -n "id2: " >> /result.log
|
||||
RUN --mount=type=ssh,id=id2 ssh-add -L >> /result.log
|
26
tests/integration/build_ssh/docker-compose.yml
Normal file
26
tests/integration/build_ssh/docker-compose.yml
Normal file
@ -0,0 +1,26 @@
|
||||
version: "3"
|
||||
services:
|
||||
test_build_ssh_map:
|
||||
build:
|
||||
context: ./context
|
||||
dockerfile: Dockerfile
|
||||
ssh:
|
||||
default:
|
||||
id1: "./id_ed25519_dummy"
|
||||
id2: "./agent_dummy.sock"
|
||||
image: my-alpine-build-ssh-map
|
||||
command:
|
||||
- cat
|
||||
- /result.log
|
||||
test_build_ssh_array:
|
||||
build:
|
||||
context: ./context
|
||||
dockerfile: Dockerfile
|
||||
ssh:
|
||||
- default
|
||||
- "id1=./id_ed25519_dummy"
|
||||
- "id2=./agent_dummy.sock"
|
||||
image: my-alpine-build-ssh-array
|
||||
command:
|
||||
- cat
|
||||
- /result.log
|
7
tests/integration/build_ssh/id_ed25519_dummy
Normal file
7
tests/integration/build_ssh/id_ed25519_dummy
Normal file
@ -0,0 +1,7 @@
|
||||
-----BEGIN OPENSSH PRIVATE KEY-----
|
||||
b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtzc2gtZW
|
||||
QyNTUxOQAAACBWELzfWvraCAeo0rOM2OxTGqWZx7fNBCglK/1oS8FLpgAAAJhzHuERcx7h
|
||||
EQAAAAtzc2gtZWQyNTUxOQAAACBWELzfWvraCAeo0rOM2OxTGqWZx7fNBCglK/1oS8FLpg
|
||||
AAAEAEIrYvY3jJ2IvAnUa5jIrVe8UG+7G7PzWzZqqBQykZllYQvN9a+toIB6jSs4zY7FMa
|
||||
pZnHt80EKCUr/WhLwUumAAAADnJpbmdvQGJuZHRib3gyAQIDBAUGBw==
|
||||
-----END OPENSSH PRIVATE KEY-----
|
246
tests/integration/build_ssh/test_build_ssh.py
Normal file
246
tests/integration/build_ssh/test_build_ssh.py
Normal file
@ -0,0 +1,246 @@
|
||||
# SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
import os
|
||||
import socket
|
||||
import struct
|
||||
import threading
|
||||
import unittest
|
||||
|
||||
from cryptography.hazmat.primitives import serialization
|
||||
from cryptography.hazmat.primitives.asymmetric.ed25519 import Ed25519PrivateKey
|
||||
|
||||
from tests.integration.test_utils import RunSubprocessMixin
|
||||
from tests.integration.test_utils import podman_compose_path
|
||||
from tests.integration.test_utils import test_path
|
||||
|
||||
expected_lines = [
|
||||
"default: ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIFYQvN9a+toIB6jSs4zY7FMapZnHt80EKCUr/WhLwUum",
|
||||
"id1: ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIFYQvN9a+toIB6jSs4zY7FMapZnHt80EKCUr/WhLwUum",
|
||||
"id2: ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIFYQvN9a+toIB6jSs4zY7FMapZnHt80EKCUr/WhLwUum",
|
||||
]
|
||||
|
||||
|
||||
class TestBuildSsh(unittest.TestCase, RunSubprocessMixin):
|
||||
def test_build_ssh(self):
|
||||
"""The build context can contain the ssh authentications that the image builder should
|
||||
use during image build. They can be either an array or a map.
|
||||
"""
|
||||
|
||||
compose_path = os.path.join(test_path(), "build_ssh/docker-compose.yml")
|
||||
sock_path = os.path.join(test_path(), "build_ssh/agent_dummy.sock")
|
||||
private_key_file = os.path.join(test_path(), "build_ssh/id_ed25519_dummy")
|
||||
|
||||
agent = MockSSHAgent(private_key_file)
|
||||
|
||||
try:
|
||||
# Set SSH_AUTH_SOCK because `default` expects it
|
||||
os.environ['SSH_AUTH_SOCK'] = sock_path
|
||||
|
||||
# Start a mock SSH agent server
|
||||
agent.start_agent(sock_path)
|
||||
|
||||
self.run_subprocess_assert_returncode([
|
||||
podman_compose_path(),
|
||||
"-f",
|
||||
compose_path,
|
||||
"build",
|
||||
"test_build_ssh_map",
|
||||
"test_build_ssh_array",
|
||||
])
|
||||
|
||||
for test_image in [
|
||||
"test_build_ssh_map",
|
||||
"test_build_ssh_array",
|
||||
]:
|
||||
out, _ = self.run_subprocess_assert_returncode([
|
||||
podman_compose_path(),
|
||||
"-f",
|
||||
compose_path,
|
||||
"run",
|
||||
"--rm",
|
||||
test_image,
|
||||
])
|
||||
|
||||
out = out.decode('utf-8')
|
||||
|
||||
# Check if all lines are contained in the output
|
||||
self.assertTrue(
|
||||
all(line in out for line in expected_lines),
|
||||
f"Incorrect output for image {test_image}",
|
||||
)
|
||||
|
||||
finally:
|
||||
# Now we send the stop command to gracefully shut down the server
|
||||
agent.stop_agent()
|
||||
|
||||
if os.path.exists(sock_path):
|
||||
os.remove(sock_path)
|
||||
|
||||
self.run_subprocess_assert_returncode([
|
||||
"podman",
|
||||
"rmi",
|
||||
"my-alpine-build-ssh-map",
|
||||
"my-alpine-build-ssh-array",
|
||||
])
|
||||
|
||||
|
||||
# SSH agent message types
|
||||
SSH_AGENTC_REQUEST_IDENTITIES = 11
|
||||
SSH_AGENT_IDENTITIES_ANSWER = 12
|
||||
SSH_AGENT_FAILURE = 5
|
||||
STOP_REQUEST = 0xFF
|
||||
|
||||
|
||||
class MockSSHAgent:
|
||||
def __init__(self, private_key_path):
|
||||
self.sock_path = None
|
||||
self.server_sock = None
|
||||
self.running = threading.Event()
|
||||
self.keys = [self._load_ed25519_private_key(private_key_path)]
|
||||
self.agent_thread = None # Thread to run the agent
|
||||
|
||||
def _load_ed25519_private_key(self, private_key_path):
|
||||
"""Load ED25519 private key from an OpenSSH private key file."""
|
||||
with open(private_key_path, 'rb') as key_file:
|
||||
private_key = serialization.load_ssh_private_key(key_file.read(), password=None)
|
||||
|
||||
# Ensure it's an Ed25519 key
|
||||
if not isinstance(private_key, Ed25519PrivateKey):
|
||||
raise ValueError("Invalid key type, expected ED25519 private key.")
|
||||
|
||||
# Get the public key corresponding to the private key
|
||||
public_key = private_key.public_key()
|
||||
|
||||
# Serialize the public key to the OpenSSH format
|
||||
public_key_blob = public_key.public_bytes(
|
||||
encoding=serialization.Encoding.Raw, format=serialization.PublicFormat.Raw
|
||||
)
|
||||
|
||||
# SSH key type "ssh-ed25519"
|
||||
key_type = b"ssh-ed25519"
|
||||
|
||||
# Build the key blob (public key part for the agent)
|
||||
key_blob_full = (
|
||||
struct.pack(">I", len(key_type))
|
||||
+ key_type # Key type length + type
|
||||
+ struct.pack(">I", len(public_key_blob))
|
||||
+ public_key_blob # Public key length + key blob
|
||||
)
|
||||
|
||||
# Comment (empty)
|
||||
comment = ""
|
||||
|
||||
return ("ssh-ed25519", key_blob_full, comment)
|
||||
|
||||
def start_agent(self, sock_path):
|
||||
"""Start the mock SSH agent and create a Unix domain socket."""
|
||||
self.sock_path = sock_path
|
||||
if os.path.exists(self.sock_path):
|
||||
os.remove(self.sock_path)
|
||||
|
||||
self.server_sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
|
||||
self.server_sock.bind(self.sock_path)
|
||||
self.server_sock.listen(5)
|
||||
|
||||
os.environ['SSH_AUTH_SOCK'] = self.sock_path
|
||||
|
||||
self.running.set() # Set the running event
|
||||
|
||||
# Start a thread to accept client connections
|
||||
self.agent_thread = threading.Thread(target=self._accept_connections, daemon=True)
|
||||
self.agent_thread.start()
|
||||
|
||||
def _accept_connections(self):
|
||||
"""Accept and handle incoming connections."""
|
||||
while self.running.is_set():
|
||||
try:
|
||||
client_sock, _ = self.server_sock.accept()
|
||||
self._handle_client(client_sock)
|
||||
except Exception as e:
|
||||
print(f"Error accepting connection: {e}")
|
||||
|
||||
def _handle_client(self, client_sock):
|
||||
"""Handle a single client request (like ssh-add)."""
|
||||
try:
|
||||
# Read the message length (first 4 bytes)
|
||||
length_message = client_sock.recv(4)
|
||||
if not length_message:
|
||||
raise "no length message received"
|
||||
|
||||
msg_len = struct.unpack(">I", length_message)[0]
|
||||
|
||||
request_message = client_sock.recv(msg_len)
|
||||
|
||||
# Check for STOP_REQUEST
|
||||
if request_message[0] == STOP_REQUEST:
|
||||
client_sock.close()
|
||||
self.running.clear() # Stop accepting connections
|
||||
return
|
||||
|
||||
# Check for SSH_AGENTC_REQUEST_IDENTITIES
|
||||
if request_message[0] == SSH_AGENTC_REQUEST_IDENTITIES:
|
||||
response = self._mock_list_keys_response()
|
||||
client_sock.sendall(response)
|
||||
else:
|
||||
print("Message not recognized")
|
||||
# Send failure if the message type is not recognized
|
||||
response = struct.pack(">I", 1) + struct.pack(">B", SSH_AGENT_FAILURE)
|
||||
client_sock.sendall(response)
|
||||
|
||||
except socket.error:
|
||||
print("Client socket error.")
|
||||
pass # You can handle specific errors here if needed
|
||||
finally:
|
||||
client_sock.close() # Ensure the client socket is closed
|
||||
|
||||
def _mock_list_keys_response(self):
|
||||
"""Create a mock response for ssh-add -l, listing keys."""
|
||||
|
||||
# Start building the response
|
||||
response = struct.pack(">B", SSH_AGENT_IDENTITIES_ANSWER) # Message type
|
||||
|
||||
# Number of keys
|
||||
response += struct.pack(">I", len(self.keys))
|
||||
|
||||
# For each key, append key blob and comment
|
||||
for key_type, key_blob, comment in self.keys:
|
||||
# Key blob length and content
|
||||
response += struct.pack(">I", len(key_blob)) + key_blob
|
||||
|
||||
# Comment length and content
|
||||
comment_encoded = comment.encode()
|
||||
response += struct.pack(">I", len(comment_encoded)) + comment_encoded
|
||||
|
||||
# Prefix the entire response with the total message length
|
||||
response = struct.pack(">I", len(response)) + response
|
||||
|
||||
return response
|
||||
|
||||
def stop_agent(self):
|
||||
"""Stop the mock SSH agent."""
|
||||
if self.running.is_set(): # First check if the agent is running
|
||||
# Create a temporary connection to send the stop command
|
||||
with socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) as client_sock:
|
||||
client_sock.connect(self.sock_path) # Connect to the server
|
||||
|
||||
stop_command = struct.pack(
|
||||
">B", STOP_REQUEST
|
||||
) # Pack the stop command as a single byte
|
||||
|
||||
# Send the message length first
|
||||
message_length = struct.pack(">I", len(stop_command))
|
||||
client_sock.sendall(message_length) # Send the length first
|
||||
|
||||
client_sock.sendall(stop_command) # Send the stop command
|
||||
|
||||
self.running.clear() # Stop accepting new connections
|
||||
|
||||
# Wait for the agent thread to finish
|
||||
if self.agent_thread:
|
||||
self.agent_thread.join() # Wait for the thread to finish
|
||||
self.agent_thread = None # Reset thread reference
|
||||
|
||||
# Remove the socket file only after the server socket is closed
|
||||
if self.server_sock: # Check if the server socket exists
|
||||
self.server_sock.close() # Close the server socket
|
||||
os.remove(self.sock_path)
|
1
tests/integration/default_net_behavior/__init__.py
Normal file
1
tests/integration/default_net_behavior/__init__.py
Normal file
@ -0,0 +1 @@
|
||||
|
@ -0,0 +1,4 @@
|
||||
services:
|
||||
web:
|
||||
image: busybox
|
||||
command: httpd -f -p 8123 -h /tmp/
|
@ -0,0 +1,7 @@
|
||||
services:
|
||||
web:
|
||||
image: busybox
|
||||
command: httpd -f -p 8123 -h /tmp/
|
||||
|
||||
x-podman:
|
||||
default_net_behavior_compat: true
|
@ -0,0 +1,7 @@
|
||||
services:
|
||||
web:
|
||||
image: busybox
|
||||
command: httpd -f -p 8123 -h /tmp/
|
||||
|
||||
networks:
|
||||
net0: {}
|
@ -0,0 +1,10 @@
|
||||
services:
|
||||
web:
|
||||
image: busybox
|
||||
command: httpd -f -p 8123 -h /tmp/
|
||||
|
||||
networks:
|
||||
net0: {}
|
||||
|
||||
x-podman:
|
||||
default_net_behavior_compat: true
|
@ -0,0 +1,8 @@
|
||||
services:
|
||||
web:
|
||||
image: busybox
|
||||
command: httpd -f -p 8123 -h /tmp/
|
||||
|
||||
networks:
|
||||
net0: {}
|
||||
net1: {}
|
@ -0,0 +1,11 @@
|
||||
services:
|
||||
web:
|
||||
image: busybox
|
||||
command: httpd -f -p 8123 -h /tmp/
|
||||
|
||||
networks:
|
||||
net0: {}
|
||||
net1: {}
|
||||
|
||||
x-podman:
|
||||
default_net_behavior_compat: true
|
@ -0,0 +1,9 @@
|
||||
services:
|
||||
web:
|
||||
image: busybox
|
||||
command: httpd -f -p 8123 -h /tmp/
|
||||
|
||||
networks:
|
||||
net0: {}
|
||||
net1: {}
|
||||
default: {}
|
@ -0,0 +1,12 @@
|
||||
services:
|
||||
web:
|
||||
image: busybox
|
||||
command: httpd -f -p 8123 -h /tmp/
|
||||
|
||||
networks:
|
||||
net0: {}
|
||||
net1: {}
|
||||
default: {}
|
||||
|
||||
x-podman:
|
||||
default_net_behavior_compat: true
|
@ -0,0 +1,65 @@
|
||||
# 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
|
||||
|
||||
|
||||
def compose_yaml_path(scenario):
|
||||
return os.path.join(
|
||||
os.path.join(test_path(), "default_net_behavior"), f"docker-compose_{scenario}.yaml"
|
||||
)
|
||||
|
||||
|
||||
class TestComposeDefaultNetBehavior(unittest.TestCase, RunSubprocessMixin):
|
||||
@parameterized.expand([
|
||||
('no_nets', 'default_net_behavior_default'),
|
||||
('one_net', 'default_net_behavior_net0'),
|
||||
('two_nets', 'podman'),
|
||||
('with_default', 'default_net_behavior_default'),
|
||||
('no_nets_compat', 'default_net_behavior_default'),
|
||||
('one_net_compat', 'default_net_behavior_default'),
|
||||
('two_nets_compat', 'default_net_behavior_default'),
|
||||
('with_default_compat', 'default_net_behavior_default'),
|
||||
])
|
||||
def test_nethost(self, scenario, default_net):
|
||||
try:
|
||||
self.run_subprocess_assert_returncode(
|
||||
[podman_compose_path(), "-f", compose_yaml_path(scenario), "up", "-d"],
|
||||
)
|
||||
|
||||
container_id, _ = self.run_subprocess_assert_returncode(
|
||||
[
|
||||
podman_compose_path(),
|
||||
"-f",
|
||||
compose_yaml_path(scenario),
|
||||
"ps",
|
||||
"--format",
|
||||
'{{.ID}}',
|
||||
],
|
||||
)
|
||||
container_id = container_id.decode('utf-8').split('\n')[0]
|
||||
output, _ = self.run_subprocess_assert_returncode(
|
||||
[
|
||||
"podman",
|
||||
"inspect",
|
||||
container_id,
|
||||
"--format",
|
||||
"{{range $key, $value := .NetworkSettings.Networks }}{{ $key }}\n{{ end }}",
|
||||
],
|
||||
)
|
||||
self.assertEqual(output.decode('utf-8').strip(), default_net)
|
||||
finally:
|
||||
self.run_subprocess_assert_returncode([
|
||||
podman_compose_path(),
|
||||
"-f",
|
||||
compose_yaml_path(scenario),
|
||||
"down",
|
||||
"-t",
|
||||
"0",
|
||||
])
|
1
tests/integration/deps/__init__.py
Normal file
1
tests/integration/deps/__init__.py
Normal file
@ -0,0 +1 @@
|
||||
|
22
tests/integration/deps/docker-compose-conditional-fails.yaml
Normal file
22
tests/integration/deps/docker-compose-conditional-fails.yaml
Normal file
@ -0,0 +1,22 @@
|
||||
version: "3.7"
|
||||
services:
|
||||
web:
|
||||
image: nopush/podman-compose-test
|
||||
command: ["dumb-init", "/bin/busybox", "httpd", "-f", "-h", "/etc/", "-p", "8000"]
|
||||
tmpfs:
|
||||
- /run
|
||||
- /tmp
|
||||
healthcheck:
|
||||
test: ["CMD", "/bin/false"]
|
||||
interval: 10s # Time between health checks
|
||||
timeout: 1s # Time to wait for a response
|
||||
retries: 1 # Number of consecutive failures before marking as unhealthy
|
||||
sleep:
|
||||
image: nopush/podman-compose-test
|
||||
command: ["dumb-init", "/bin/busybox", "sh", "-c", "sleep 3600"]
|
||||
depends_on:
|
||||
web:
|
||||
condition: service_healthy
|
||||
tmpfs:
|
||||
- /run
|
||||
- /tmp
|
@ -0,0 +1,23 @@
|
||||
version: "3.7"
|
||||
services:
|
||||
web:
|
||||
image: nopush/podman-compose-test
|
||||
command: ["dumb-init", "/bin/busybox", "httpd", "-f", "-h", "/etc/", "-p", "8000"]
|
||||
tmpfs:
|
||||
- /run
|
||||
- /tmp
|
||||
healthcheck:
|
||||
test: ["CMD", "wget", "-qO-", "http://localhost:8000/hosts"]
|
||||
start_period: 10s # initialization time for containers that need time to bootstrap
|
||||
interval: 10s # Time between health checks
|
||||
timeout: 5s # Time to wait for a response
|
||||
retries: 3 # Number of consecutive failures before marking as unhealthy
|
||||
sleep:
|
||||
image: nopush/podman-compose-test
|
||||
command: ["dumb-init", "/bin/busybox", "sh", "-c", "sleep 3600"]
|
||||
depends_on:
|
||||
web:
|
||||
condition: service_healthy
|
||||
tmpfs:
|
||||
- /run
|
||||
- /tmp
|
@ -0,0 +1,22 @@
|
||||
version: "3.7"
|
||||
services:
|
||||
web:
|
||||
image: nopush/podman-compose-test
|
||||
command: ["dumb-init", "/bin/busybox", "httpd", "-f", "-h", "/etc/", "-p", "8000"]
|
||||
tmpfs:
|
||||
- /run
|
||||
- /tmp
|
||||
healthcheck:
|
||||
test: ["CMD", "wget", "-qO-", "http://localhost:8000/hosts"]
|
||||
interval: 30s # Time between health checks
|
||||
timeout: 5s # Time to wait for a response
|
||||
retries: 3 # Number of consecutive failures before marking as unhealthy
|
||||
sleep:
|
||||
image: nopush/podman-compose-test
|
||||
command: ["dumb-init", "/bin/busybox", "sh", "-c", "sleep 3600"]
|
||||
depends_on:
|
||||
web:
|
||||
condition: service_healthy
|
||||
tmpfs:
|
||||
- /run
|
||||
- /tmp
|
266
tests/integration/deps/test_podman_compose_deps.py
Normal file
266
tests/integration/deps/test_podman_compose_deps.py
Normal file
@ -0,0 +1,266 @@
|
||||
# SPDX-License-Identifier: GPL-2.0
|
||||
import os
|
||||
import unittest
|
||||
|
||||
from tests.integration.test_utils import PodmanAwareRunSubprocessMixin
|
||||
from tests.integration.test_utils import RunSubprocessMixin
|
||||
from tests.integration.test_utils import is_systemd_available
|
||||
from tests.integration.test_utils import podman_compose_path
|
||||
from tests.integration.test_utils import test_path
|
||||
|
||||
|
||||
def compose_yaml_path(suffix=""):
|
||||
return os.path.join(os.path.join(test_path(), "deps"), f"docker-compose{suffix}.yaml")
|
||||
|
||||
|
||||
class TestComposeBaseDeps(unittest.TestCase, RunSubprocessMixin):
|
||||
def test_deps(self):
|
||||
try:
|
||||
output, _ = self.run_subprocess_assert_returncode([
|
||||
podman_compose_path(),
|
||||
"-f",
|
||||
compose_yaml_path(),
|
||||
"run",
|
||||
"--rm",
|
||||
"sleep",
|
||||
"/bin/sh",
|
||||
"-c",
|
||||
"wget -O - http://web:8000/hosts",
|
||||
])
|
||||
self.assertIn(b"HTTP request sent, awaiting response... 200 OK", output)
|
||||
self.assertIn(b"deps_web_1", output)
|
||||
finally:
|
||||
self.run_subprocess_assert_returncode([
|
||||
podman_compose_path(),
|
||||
"-f",
|
||||
compose_yaml_path(),
|
||||
"down",
|
||||
])
|
||||
|
||||
def test_run_nodeps(self):
|
||||
try:
|
||||
output, _ = self.run_subprocess_assert_returncode([
|
||||
podman_compose_path(),
|
||||
"-f",
|
||||
compose_yaml_path(),
|
||||
"run",
|
||||
"--rm",
|
||||
"--no-deps",
|
||||
"sleep",
|
||||
"/bin/sh",
|
||||
"-c",
|
||||
"wget -O - http://web:8000/hosts || echo Failed to connect",
|
||||
])
|
||||
self.assertNotIn(b"HTTP request sent, awaiting response... 200 OK", output)
|
||||
self.assertNotIn(b"deps_web_1", output)
|
||||
self.assertIn(b"Failed to connect", output)
|
||||
finally:
|
||||
self.run_subprocess_assert_returncode([
|
||||
podman_compose_path(),
|
||||
"-f",
|
||||
compose_yaml_path(),
|
||||
"down",
|
||||
])
|
||||
|
||||
def test_up_nodeps(self):
|
||||
try:
|
||||
self.run_subprocess_assert_returncode([
|
||||
podman_compose_path(),
|
||||
"-f",
|
||||
compose_yaml_path(),
|
||||
"up",
|
||||
"--no-deps",
|
||||
"--detach",
|
||||
"sleep",
|
||||
])
|
||||
output, _ = self.run_subprocess_assert_returncode([
|
||||
podman_compose_path(),
|
||||
"-f",
|
||||
compose_yaml_path(),
|
||||
"ps",
|
||||
])
|
||||
self.assertNotIn(b"deps_web_1", output)
|
||||
self.assertIn(b"deps_sleep_1", output)
|
||||
finally:
|
||||
self.run_subprocess_assert_returncode([
|
||||
podman_compose_path(),
|
||||
"-f",
|
||||
compose_yaml_path(),
|
||||
"down",
|
||||
])
|
||||
|
||||
def test_podman_compose_run(self):
|
||||
"""
|
||||
This will test depends_on as well
|
||||
"""
|
||||
run_cmd = [
|
||||
"coverage",
|
||||
"run",
|
||||
podman_compose_path(),
|
||||
"-f",
|
||||
os.path.join(test_path(), "deps", "docker-compose.yaml"),
|
||||
"run",
|
||||
"--rm",
|
||||
"sleep",
|
||||
"/bin/sh",
|
||||
"-c",
|
||||
"wget -q -O - http://web:8000/hosts",
|
||||
]
|
||||
|
||||
out, _ = self.run_subprocess_assert_returncode(run_cmd)
|
||||
self.assertIn(b"127.0.0.1\tlocalhost", out)
|
||||
|
||||
# Run it again to make sure we can run it twice. I saw an issue where a second run, with
|
||||
# the container left up, would fail
|
||||
run_cmd = [
|
||||
"coverage",
|
||||
"run",
|
||||
podman_compose_path(),
|
||||
"-f",
|
||||
os.path.join(test_path(), "deps", "docker-compose.yaml"),
|
||||
"run",
|
||||
"--rm",
|
||||
"sleep",
|
||||
"/bin/sh",
|
||||
"-c",
|
||||
"wget -q -O - http://web:8000/hosts",
|
||||
]
|
||||
|
||||
out, _ = self.run_subprocess_assert_returncode(run_cmd)
|
||||
self.assertIn(b"127.0.0.1\tlocalhost", out)
|
||||
|
||||
# This leaves a container running. Not sure it's intended, but it matches docker-compose
|
||||
down_cmd = [
|
||||
"coverage",
|
||||
"run",
|
||||
podman_compose_path(),
|
||||
"-f",
|
||||
os.path.join(test_path(), "deps", "docker-compose.yaml"),
|
||||
"down",
|
||||
]
|
||||
|
||||
self.run_subprocess_assert_returncode(down_cmd)
|
||||
|
||||
|
||||
class TestComposeConditionalDeps(unittest.TestCase, RunSubprocessMixin):
|
||||
def test_deps_succeeds(self):
|
||||
suffix = "-conditional-succeeds"
|
||||
try:
|
||||
output, _ = self.run_subprocess_assert_returncode([
|
||||
podman_compose_path(),
|
||||
"-f",
|
||||
compose_yaml_path(suffix),
|
||||
"run",
|
||||
"--rm",
|
||||
"sleep",
|
||||
"/bin/sh",
|
||||
"-c",
|
||||
"wget -O - http://web:8000/hosts",
|
||||
])
|
||||
self.assertIn(b"HTTP request sent, awaiting response... 200 OK", output)
|
||||
self.assertIn(b"deps_web_1", output)
|
||||
finally:
|
||||
self.run_subprocess_assert_returncode([
|
||||
podman_compose_path(),
|
||||
"-f",
|
||||
compose_yaml_path(suffix),
|
||||
"down",
|
||||
])
|
||||
|
||||
def test_deps_fails(self):
|
||||
suffix = "-conditional-fails"
|
||||
try:
|
||||
output, _ = self.run_subprocess_assert_returncode([
|
||||
podman_compose_path(),
|
||||
"-f",
|
||||
compose_yaml_path(suffix),
|
||||
"ps",
|
||||
])
|
||||
self.assertNotIn(b"HTTP request sent, awaiting response... 200 OK", output)
|
||||
self.assertNotIn(b"deps_web_1", output)
|
||||
finally:
|
||||
self.run_subprocess_assert_returncode([
|
||||
podman_compose_path(),
|
||||
"-f",
|
||||
compose_yaml_path(suffix),
|
||||
"down",
|
||||
])
|
||||
|
||||
|
||||
class TestComposeConditionalDepsHealthy(unittest.TestCase, PodmanAwareRunSubprocessMixin):
|
||||
def setUp(self):
|
||||
self.podman_version = self.retrieve_podman_version()
|
||||
|
||||
def test_up_deps_healthy(self):
|
||||
suffix = "-conditional-healthy"
|
||||
try:
|
||||
self.run_subprocess_assert_returncode([
|
||||
podman_compose_path(),
|
||||
"-f",
|
||||
compose_yaml_path(suffix),
|
||||
"up",
|
||||
"sleep",
|
||||
"--detach",
|
||||
])
|
||||
|
||||
# Since the command `podman wait --condition=healthy` is invalid prior to 4.6.0,
|
||||
# we only validate healthy status for podman 4.6.0+, which won't be tested in the
|
||||
# CI pipeline of the podman-compose project where podman 4.3.1 is employed.
|
||||
podman_ver_major, podman_ver_minor, podman_ver_patch = self.podman_version
|
||||
if podman_ver_major >= 4 and podman_ver_minor >= 6 and podman_ver_patch >= 0:
|
||||
self.run_subprocess_assert_returncode([
|
||||
"podman",
|
||||
"wait",
|
||||
"--condition=running",
|
||||
"deps_web_1",
|
||||
"deps_sleep_1",
|
||||
])
|
||||
|
||||
# check both web and sleep are running
|
||||
output, _ = self.run_subprocess_assert_returncode([
|
||||
podman_compose_path(),
|
||||
"-f",
|
||||
compose_yaml_path(),
|
||||
"ps",
|
||||
"--format",
|
||||
"{{.ID}}\t{{.Names}}\t{{.Status}}\t{{.StartedAt}}",
|
||||
])
|
||||
|
||||
# extract container id of web
|
||||
decoded_out = output.decode('utf-8')
|
||||
lines = decoded_out.split("\n")
|
||||
|
||||
web_lines = [line for line in lines if "web" in line]
|
||||
self.assertTrue(web_lines)
|
||||
self.assertEqual(1, len(web_lines))
|
||||
web_cnt_id, web_cnt_name, web_cnt_status, web_cnt_started = web_lines[0].split("\t")
|
||||
self.assertNotEqual("", web_cnt_id)
|
||||
self.assertEqual("deps_web_1", web_cnt_name)
|
||||
|
||||
sleep_lines = [line for line in lines if "sleep" in line]
|
||||
self.assertTrue(sleep_lines)
|
||||
self.assertEqual(1, len(sleep_lines))
|
||||
sleep_cnt_id, sleep_cnt_name, _, sleep_cnt_started = sleep_lines[0].split("\t")
|
||||
self.assertNotEqual("", sleep_cnt_id)
|
||||
self.assertEqual("deps_sleep_1", sleep_cnt_name)
|
||||
|
||||
# When test case is executed inside container like github actions, the absence of
|
||||
# systemd prevents health check from working properly, resulting in failure to
|
||||
# transit to healthy state. As a result, we only assert the `healthy` state where
|
||||
# systemd is functioning.
|
||||
if (
|
||||
is_systemd_available()
|
||||
and podman_ver_major >= 4
|
||||
and podman_ver_minor >= 6
|
||||
and podman_ver_patch >= 0
|
||||
):
|
||||
self.assertIn("healthy", web_cnt_status)
|
||||
self.assertGreaterEqual(int(sleep_cnt_started), int(web_cnt_started))
|
||||
|
||||
finally:
|
||||
self.run_subprocess_assert_returncode([
|
||||
podman_compose_path(),
|
||||
"-f",
|
||||
compose_yaml_path(),
|
||||
"down",
|
||||
])
|
1
tests/integration/env-file-tests/__init__.py
Normal file
1
tests/integration/env-file-tests/__init__.py
Normal file
@ -0,0 +1 @@
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user