mirror of
https://github.com/containers/podman-compose.git
synced 2025-07-04 14:40:13 +02:00
Compare commits
384 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 | |||
d38b26bb01 | |||
22a4ad5806 | |||
37e2cb28d4 | |||
0cd3902c5f | |||
6ef759c6fd | |||
16cbcf4152 | |||
67ce900885 | |||
4e9f76768c | |||
84f7fdd7da | |||
405001b990 | |||
6b1aeff55f | |||
f06975b346 | |||
546cad5171 | |||
e07c28d127 | |||
935029dc33 | |||
80b2aa6ed0 | |||
360b85bf2d | |||
650a835eca | |||
82740cc311 | |||
0f645e4c70 | |||
3b15170ccf | |||
3359380ec6 | |||
14f39e5b86 | |||
e799a0b0ea | |||
6d8d3e94fe | |||
65d1fdeaa3 | |||
d905a7c638 | |||
2e8ed2f924 | |||
040b73adab | |||
f3e9a96c96 | |||
04b107805a | |||
2c5d00d3e7 | |||
cac90f69b8 | |||
b513f50f30 | |||
8f618b6fab | |||
cac836b0f5 | |||
3bb305cef4 | |||
09034a0c38 | |||
e668a339ce | |||
0065082db9 | |||
5d4de80ab7 | |||
23ad5c3ef7 | |||
45efe461b0 | |||
4f73f2b79e | |||
1d64f2cf8c | |||
2ce6d1a1e7 | |||
4e22faefd6 | |||
7a2da76ab8 | |||
79865c2e13 | |||
33d7d35a4d | |||
c23a8b2cbd | |||
36a3d3c207 | |||
b202a09501 | |||
35cbc49160 | |||
5c4aa40032 | |||
0ee7c2630a | |||
cef1785cd5 | |||
b4cfef12e9 | |||
b761050b0b | |||
e1d0ea7b4e | |||
1430578568 | |||
8f41cd3cdb | |||
a73dac2e39 | |||
d31a8b124d | |||
5df4e786ee | |||
27e27e9fe9 | |||
70a0e2d003 | |||
58641f0545 | |||
eea8bac496 | |||
09a8a3edf9 | |||
a6c4263738 | |||
9599cc039e | |||
0a6c057486 | |||
2b4ecee082 | |||
77f2e8e5b0 | |||
12d46ca836 | |||
72a94d5185 | |||
3e1f7d554b | |||
d7cf0966d3 | |||
1f35c00694 | |||
f0bae1e2d9 | |||
c31b4e2816 |
1
.codespellignore
Normal file
1
.codespellignore
Normal file
@ -0,0 +1 @@
|
|||||||
|
assertIn
|
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)
|
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)
|
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**
|
**To Reproduce**
|
||||||
Steps to reproduce the behavior:
|
Steps to reproduce the behavior:
|
||||||
1. what is the content of the current working directory (ex. `docker-compose.yml`, `.env`, `Dockerfile`, ...etc.)
|
1. what is the content of the current working directory (ex. `docker-compose.yml`, `.env`, `Dockerfile`, ...etc.)
|
||||||
|
3
.github/PULL_REQUEST_TEMPLATE.md
vendored
3
.github/PULL_REQUEST_TEMPLATE.md
vendored
@ -4,4 +4,7 @@
|
|||||||
If this PR adds a new feature that improves compatibility with docker-compose, please add a link
|
If this PR adds a new feature that improves compatibility with docker-compose, please add a link
|
||||||
to the exact part of compose spec that the PR touches.
|
to the exact part of compose spec that the PR touches.
|
||||||
|
|
||||||
|
For any user-visible change please add a release note to newsfragments directory, e.g.
|
||||||
|
newsfragments/my_feature.feature. See newsfragments/README.md for more details.
|
||||||
|
|
||||||
All changes require additional unit tests.
|
All changes require additional unit tests.
|
||||||
|
2
.github/workflows/codespell.yml
vendored
2
.github/workflows/codespell.yml
vendored
@ -18,3 +18,5 @@ jobs:
|
|||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
- name: Codespell
|
- name: Codespell
|
||||||
uses: codespell-project/actions-codespell@v2
|
uses: codespell-project/actions-codespell@v2
|
||||||
|
with:
|
||||||
|
ignore_words_file: .codespellignore
|
||||||
|
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:
|
strategy:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
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
|
runs-on: ubuntu-latest
|
||||||
container:
|
container:
|
||||||
@ -22,18 +22,18 @@ jobs:
|
|||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
run: |
|
run: |
|
||||||
set -e
|
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
|
python -m pip install --upgrade pip
|
||||||
if [ -f requirements.txt ]; then pip install -r requirements.txt; fi
|
pip install -r requirements.txt
|
||||||
if [ -f test-requirements.txt ]; then pip install -r test-requirements.txt; fi
|
pip install -r test-requirements.txt
|
||||||
- name: Run tests in tests/
|
- name: Run integration tests
|
||||||
run: |
|
run: |
|
||||||
python -m unittest tests/*.py
|
python -m unittest discover -v tests/integration
|
||||||
env:
|
env:
|
||||||
TESTS_DEBUG: 1
|
TESTS_DEBUG: 1
|
||||||
- name: Run tests in pytests/
|
- name: Run unit tests
|
||||||
run: |
|
run: |
|
||||||
coverage run --source podman_compose -m unittest pytests/*.py
|
coverage run --source podman_compose -m unittest discover tests/unit
|
||||||
- name: Report coverage
|
- name: Report coverage
|
||||||
run: |
|
run: |
|
||||||
coverage combine
|
coverage combine
|
||||||
|
3
.gitignore
vendored
3
.gitignore
vendored
@ -105,3 +105,6 @@ venv.bak/
|
|||||||
|
|
||||||
# mypy
|
# mypy
|
||||||
.mypy_cache/
|
.mypy_cache/
|
||||||
|
|
||||||
|
|
||||||
|
.vscode
|
||||||
|
@ -1,17 +1,10 @@
|
|||||||
|
default_install_hook_types: [pre-commit, commit-msg]
|
||||||
repos:
|
repos:
|
||||||
- repo: https://github.com/psf/black
|
- repo: https://github.com/astral-sh/ruff-pre-commit
|
||||||
rev: 23.3.0
|
rev: v0.9.6
|
||||||
hooks:
|
hooks:
|
||||||
- id: black
|
- id: ruff
|
||||||
# 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
|
|
||||||
types: [python]
|
types: [python]
|
||||||
args: [
|
|
||||||
"--check", # Don't apply changes automatically
|
|
||||||
]
|
|
||||||
- repo: https://github.com/pycqa/flake8
|
- repo: https://github.com/pycqa/flake8
|
||||||
rev: 6.0.0
|
rev: 6.0.0
|
||||||
hooks:
|
hooks:
|
||||||
@ -34,3 +27,8 @@ repos:
|
|||||||
rev: v2.2.5
|
rev: v2.2.5
|
||||||
hooks:
|
hooks:
|
||||||
- id: codespell
|
- id: codespell
|
||||||
|
|
||||||
|
- repo: https://github.com/gklein/check_signoff
|
||||||
|
rev: v1.0.5
|
||||||
|
hooks:
|
||||||
|
- id: check-signoff
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
[MESSAGES CONTROL]
|
[MESSAGES CONTROL]
|
||||||
# C0111 missing-docstring: missing-class-docstring, missing-function-docstring, missing-method-docstring, missing-module-docstrin
|
# 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
|
# 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 _ for ignored variables
|
||||||
# allow generic names like a,b,c and i,j,k,l,m,n and x,y,z
|
# allow generic names like a,b,c and i,j,k,l,m,n and x,y,z
|
||||||
# allow k,v for key/value
|
# allow k,v for key/value
|
||||||
|
@ -8,11 +8,6 @@
|
|||||||
- Developers that want to fix bugs,
|
- Developers that want to fix bugs,
|
||||||
- Developers that want to implement new functionalities or enhancements.
|
- 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
|
## Development environment setup
|
||||||
|
|
||||||
Note: Some steps are OPTIONAL but all are RECOMMENDED.
|
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:
|
7. Run code coverage:
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
$ coverage run --source podman_compose -m unittest pytests/*.py
|
$ coverage run --source podman_compose -m unittest discover tests/unit
|
||||||
$ python -m unittest tests/*.py
|
$ python3 -m unittest discover tests/integration
|
||||||
$ coverage combine
|
$ coverage combine
|
||||||
$ coverage report
|
$ coverage report
|
||||||
$ coverage html
|
$ 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
|
- In the commit message, reference the Issue ID that your code fixes and a brief description of
|
||||||
the changes.
|
the changes.
|
||||||
Example: `Fixes #516: Allow empty network`
|
Example: `Fixes #516: Allow empty network`
|
||||||
9. Open a pull request to `containers/podman-compose:devel` and wait for a maintainer to review your
|
9. Open a pull request to `containers/podman-compose` and wait for a maintainer to review your work.
|
||||||
work.
|
|
||||||
|
|
||||||
## Adding new commands
|
## 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
|
46
README.md
46
README.md
@ -10,7 +10,11 @@ This project focuses on:
|
|||||||
This project only depends on:
|
This project only depends on:
|
||||||
|
|
||||||
* `podman`
|
* `podman`
|
||||||
* [podman dnsname plugin](https://github.com/containers/dnsname): It is usually found in the `podman-plugins` or `podman-dnsname` distro packages, those packages are not pulled by default and you need to install them. This allows containers to be able to resolve each other if they are on the same CNI network.
|
* [podman dnsname plugin](https://github.com/containers/dnsname): It is usually found in
|
||||||
|
the `podman-plugins` or `podman-dnsname` distro packages, those packages are not pulled
|
||||||
|
by default and you need to install them. This allows containers to be able to resolve
|
||||||
|
each other if they are on the same CNI network. This is not necessary when podman is using
|
||||||
|
netavark as a network backend.
|
||||||
* Python3
|
* Python3
|
||||||
* [PyYAML](https://pyyaml.org/)
|
* [PyYAML](https://pyyaml.org/)
|
||||||
* [python-dotenv](https://pypi.org/project/python-dotenv/)
|
* [python-dotenv](https://pypi.org/project/python-dotenv/)
|
||||||
@ -48,9 +52,11 @@ like `hostnet`. If you desire that behavior, pass it the standard way like `netw
|
|||||||
|
|
||||||
## Installation
|
## Installation
|
||||||
|
|
||||||
|
### Pip
|
||||||
|
|
||||||
Install the latest stable version from PyPI:
|
Install the latest stable version from PyPI:
|
||||||
|
|
||||||
```
|
```bash
|
||||||
pip3 install podman-compose
|
pip3 install podman-compose
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -58,14 +64,39 @@ pass `--user` to install inside regular user home without being root.
|
|||||||
|
|
||||||
Or latest development version from GitHub:
|
Or latest development version from GitHub:
|
||||||
|
|
||||||
```
|
```bash
|
||||||
pip3 install https://github.com/containers/podman-compose/archive/devel.tar.gz
|
pip3 install https://github.com/containers/podman-compose/archive/main.tar.gz
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Homebrew
|
||||||
|
|
||||||
|
```bash
|
||||||
|
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
|
||||||
|
curl -o /usr/local/bin/podman-compose https://raw.githubusercontent.com/containers/podman-compose/main/podman_compose.py
|
||||||
|
chmod +x /usr/local/bin/podman-compose
|
||||||
|
```
|
||||||
|
|
||||||
|
or inside your home
|
||||||
|
|
||||||
|
```bash
|
||||||
|
curl -o ~/.local/bin/podman-compose https://raw.githubusercontent.com/containers/podman-compose/main/podman_compose.py
|
||||||
|
chmod +x ~/.local/bin/podman-compose
|
||||||
|
```
|
||||||
|
|
||||||
or install from Fedora (starting from f31) repositories:
|
or install from Fedora (starting from f31) repositories:
|
||||||
|
|
||||||
```
|
```bash
|
||||||
sudo dnf install podman-compose
|
sudo dnf install podman-compose
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -74,10 +105,9 @@ sudo dnf install podman-compose
|
|||||||
We have included fully functional sample stacks inside `examples/` directory.
|
We have included fully functional sample stacks inside `examples/` directory.
|
||||||
You can get more examples from [awesome-compose](https://github.com/docker/awesome-compose).
|
You can get more examples from [awesome-compose](https://github.com/docker/awesome-compose).
|
||||||
|
|
||||||
|
|
||||||
A quick example would be
|
A quick example would be
|
||||||
|
|
||||||
```
|
```bash
|
||||||
cd examples/busybox
|
cd examples/busybox
|
||||||
podman-compose --help
|
podman-compose --help
|
||||||
podman-compose up --help
|
podman-compose up --help
|
||||||
@ -106,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
|
run a unittest with following command
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
python -m unittest pytests/*.py
|
python3 -m unittest discover tests/unit
|
||||||
```
|
```
|
||||||
|
|
||||||
# Contributing guide
|
# 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.
|
40
docs/Changelog-1.2.0.md
Normal file
40
docs/Changelog-1.2.0.md
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
Version v1.2.0 (2024-06-26)
|
||||||
|
===========================
|
||||||
|
|
||||||
|
Bug fixes
|
||||||
|
---------
|
||||||
|
|
||||||
|
- Fixed handling of `--in-pod` argument. Previously it was hard to provide false value to it.
|
||||||
|
- podman-compose no longer creates pods when registering systemd unit.
|
||||||
|
- Fixed warning `RuntimeWarning: coroutine 'create_pods' was never awaited`
|
||||||
|
- Fixed error when setting up IPAM network with default driver.
|
||||||
|
- Fixed support for having list and dictionary `depends_on` sections in related compose files.
|
||||||
|
- Fixed logging of failed build message.
|
||||||
|
- Fixed support for multiple entries in `include` section.
|
||||||
|
- Fixed environment variable precedence order.
|
||||||
|
|
||||||
|
Changes
|
||||||
|
-------
|
||||||
|
|
||||||
|
- `x-podman` dictionary in container root has been migrated to `x-podman.*` fields in container root.
|
||||||
|
|
||||||
|
New features
|
||||||
|
------------
|
||||||
|
|
||||||
|
- Added support for `--publish` in `podman-compose run`.
|
||||||
|
- Added support for Podman external root filesystem management (`--rootfs` option).
|
||||||
|
- Added support for `podman-compose images` command.
|
||||||
|
- Added support for `env_file` being configured via dictionaries.
|
||||||
|
- Added support for enabling GPU access.
|
||||||
|
- Added support for selinux in verbose mount specification.
|
||||||
|
- Added support for `additional_contexts` section.
|
||||||
|
- Added support for multi-line environment files.
|
||||||
|
- Added support for passing contents of `podman-compose.yml` via stdin.
|
||||||
|
- Added support for specifying the value for `--in-pod` setting in `podman-compose.yml` file.
|
||||||
|
- Added support for environmental secrets.
|
||||||
|
|
||||||
|
Documentation
|
||||||
|
-------------
|
||||||
|
|
||||||
|
- Added instructions on how to install podman-compose on Homebrew.
|
||||||
|
- Added explanation that netavark is an alternative to dnsname plugin
|
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
|
@ -1,6 +1,52 @@
|
|||||||
# Podman specific extensions to the docker-compose format
|
# Podman specific extensions to the docker-compose format
|
||||||
|
|
||||||
Podman-compose supports the following extension to the docker-compose format.
|
Podman-compose supports the following extension to the docker-compose format. These extensions
|
||||||
|
are generally specified under fields with "x-podman" prefix in the compose file.
|
||||||
|
|
||||||
|
## Container management
|
||||||
|
|
||||||
|
The following extension keys are available under container configuration:
|
||||||
|
|
||||||
|
* `x-podman.uidmaps` - Run the container in a new user namespace using the supplied UID 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"
|
||||||
|
services:
|
||||||
|
my_service:
|
||||||
|
command: ["/bin/busybox"]
|
||||||
|
x-podman.rootfs: "/path/to/rootfs"
|
||||||
|
```
|
||||||
|
|
||||||
|
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
|
## Per-network MAC-addresses
|
||||||
|
|
||||||
@ -12,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
|
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.
|
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
|
Specifying a MAC address for the container and for individual networks at the same time is not
|
||||||
supported.
|
supported.
|
||||||
|
|
||||||
@ -34,7 +86,7 @@ networks:
|
|||||||
- subnet: "192.168.1.0/24"
|
- subnet: "192.168.1.0/24"
|
||||||
|
|
||||||
services:
|
services:
|
||||||
webserver
|
webserver:
|
||||||
image: "busybox"
|
image: "busybox"
|
||||||
command: ["/bin/busybox", "httpd", "-f", "-h", "/etc", "-p", "8001"]
|
command: ["/bin/busybox", "httpd", "-f", "-h", "/etc", "-p", "8001"]
|
||||||
networks:
|
networks:
|
||||||
@ -43,9 +95,13 @@ services:
|
|||||||
x-podman.mac_address: "02:aa:aa:aa:aa:aa"
|
x-podman.mac_address: "02:aa:aa:aa:aa:aa"
|
||||||
net1:
|
net1:
|
||||||
ipv4_address: "192.168.1.10"
|
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
|
## Podman-specific network modes
|
||||||
|
|
||||||
Generic docker-compose supports the following values for `network-mode` for a container:
|
Generic docker-compose supports the following values for `network-mode` for a container:
|
||||||
@ -65,3 +121,74 @@ In addition, podman-compose supports the following podman-specific values for `n
|
|||||||
|
|
||||||
The options to the network modes are passed to the `--network` option of the `podman create` command
|
The options to the network modes are passed to the `--network` option of the `podman create` command
|
||||||
as-is.
|
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.
|
||||||
|
It allows providing custom value for --in-pod and is especially relevant when --userns has to be set.
|
||||||
|
|
||||||
|
For example, the following docker-compose.yml allows using userns_mode by overriding the default
|
||||||
|
value of --in-pod (unless it was specifically provided by "--in-pod=True" in command line interface).
|
||||||
|
```yml
|
||||||
|
version: "3"
|
||||||
|
services:
|
||||||
|
cont:
|
||||||
|
image: nopush/podman-compose-test
|
||||||
|
userns_mode: keep-id:uid=1000
|
||||||
|
command: ["dumb-init", "/bin/busybox", "httpd", "-f", "-p", "8080"]
|
||||||
|
|
||||||
|
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"
|
11
examples/nvidia-smi/docker-compose.yaml
Normal file
11
examples/nvidia-smi/docker-compose.yaml
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
services:
|
||||||
|
test:
|
||||||
|
image: nvidia/cuda:12.3.1-base-ubuntu20.04
|
||||||
|
command: nvidia-smi
|
||||||
|
deploy:
|
||||||
|
resources:
|
||||||
|
reservations:
|
||||||
|
devices:
|
||||||
|
- driver: nvidia
|
||||||
|
count: 1
|
||||||
|
capabilities: [gpu]
|
13
newsfragments/README.txt
Normal file
13
newsfragments/README.txt
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
This is the directory for news fragments used by towncrier: https://github.com/hawkowl/towncrier
|
||||||
|
|
||||||
|
You create a news fragment in this directory when you make a change, and the file gets removed from
|
||||||
|
this directory when the news is published.
|
||||||
|
|
||||||
|
towncrier has a few standard types of news fragments, signified by the file extension. These are:
|
||||||
|
|
||||||
|
.feature: Signifying a new feature.
|
||||||
|
.bugfix: Signifying a bug fix.
|
||||||
|
.doc: Signifying a documentation improvement.
|
||||||
|
.removal: Signifying a deprecation or removal of public API.
|
||||||
|
.change: Signifying a change of behavior
|
||||||
|
.misc: Miscellaneous change
|
1837
podman_compose.py
1837
podman_compose.py
File diff suppressed because it is too large
Load Diff
@ -13,3 +13,43 @@ force-single-line = true
|
|||||||
[tool.ruff.format]
|
[tool.ruff.format]
|
||||||
preview = true # needed for quote-style
|
preview = true # needed for quote-style
|
||||||
quote-style = "preserve"
|
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,115 +0,0 @@
|
|||||||
# SPDX-License-Identifier: GPL-2.0
|
|
||||||
from __future__ import annotations
|
|
||||||
|
|
||||||
import argparse
|
|
||||||
import copy
|
|
||||||
import os
|
|
||||||
import unittest
|
|
||||||
|
|
||||||
import yaml
|
|
||||||
from parameterized import parameterized
|
|
||||||
|
|
||||||
from podman_compose import PodmanCompose
|
|
||||||
from podman_compose import normalize_service
|
|
||||||
|
|
||||||
test_keys = ["command", "entrypoint"]
|
|
||||||
|
|
||||||
|
|
||||||
class TestCanMergeCmdEnt(unittest.TestCase):
|
|
||||||
@parameterized.expand([
|
|
||||||
({"$$$": []}, {"$$$": []}),
|
|
||||||
({"$$$": ["sh"]}, {"$$$": ["sh"]}),
|
|
||||||
({"$$$": ["sh", "-c", "date"]}, {"$$$": ["sh", "-c", "date"]}),
|
|
||||||
({"$$$": "sh"}, {"$$$": ["sh"]}),
|
|
||||||
({"$$$": "sleep infinity"}, {"$$$": ["sleep", "infinity"]}),
|
|
||||||
(
|
|
||||||
{"$$$": "bash -c 'sleep infinity'"},
|
|
||||||
{"$$$": ["bash", "-c", "sleep infinity"]},
|
|
||||||
),
|
|
||||||
])
|
|
||||||
def test_normalize_service(self, input_template, expected_template):
|
|
||||||
for key in test_keys:
|
|
||||||
test_input, _, expected = template_to_expression(
|
|
||||||
input_template, {}, expected_template, key
|
|
||||||
)
|
|
||||||
self.assertEqual(normalize_service(test_input), expected)
|
|
||||||
|
|
||||||
@parameterized.expand([
|
|
||||||
({}, {"$$$": []}, {"$$$": []}),
|
|
||||||
({"$$$": []}, {}, {"$$$": []}),
|
|
||||||
({"$$$": []}, {"$$$": "sh-2"}, {"$$$": ["sh-2"]}),
|
|
||||||
({"$$$": "sh-2"}, {"$$$": []}, {"$$$": []}),
|
|
||||||
({}, {"$$$": "sh"}, {"$$$": ["sh"]}),
|
|
||||||
({"$$$": "sh"}, {}, {"$$$": ["sh"]}),
|
|
||||||
({"$$$": "sh-1"}, {"$$$": "sh-2"}, {"$$$": ["sh-2"]}),
|
|
||||||
({"$$$": ["sh-1"]}, {"$$$": "sh-2"}, {"$$$": ["sh-2"]}),
|
|
||||||
({"$$$": "sh-1"}, {"$$$": ["sh-2"]}, {"$$$": ["sh-2"]}),
|
|
||||||
({"$$$": "sh-1"}, {"$$$": ["sh-2", "sh-3"]}, {"$$$": ["sh-2", "sh-3"]}),
|
|
||||||
({"$$$": ["sh-1"]}, {"$$$": ["sh-2", "sh-3"]}, {"$$$": ["sh-2", "sh-3"]}),
|
|
||||||
({"$$$": ["sh-1", "sh-2"]}, {"$$$": ["sh-3", "sh-4"]}, {"$$$": ["sh-3", "sh-4"]}),
|
|
||||||
({}, {"$$$": ["sh-3", "sh 4"]}, {"$$$": ["sh-3", "sh 4"]}),
|
|
||||||
({"$$$": "sleep infinity"}, {"$$$": "sh"}, {"$$$": ["sh"]}),
|
|
||||||
({"$$$": "sh"}, {"$$$": "sleep infinity"}, {"$$$": ["sleep", "infinity"]}),
|
|
||||||
(
|
|
||||||
{},
|
|
||||||
{"$$$": "bash -c 'sleep infinity'"},
|
|
||||||
{"$$$": ["bash", "-c", "sleep infinity"]},
|
|
||||||
),
|
|
||||||
])
|
|
||||||
def test_parse_compose_file_when_multiple_composes(
|
|
||||||
self, base_template, override_template, expected_template
|
|
||||||
):
|
|
||||||
for key in test_keys:
|
|
||||||
base, override, expected = template_to_expression(
|
|
||||||
base_template, override_template, expected_template, key
|
|
||||||
)
|
|
||||||
compose_test_1 = {"services": {"test-service": base}}
|
|
||||||
compose_test_2 = {"services": {"test-service": override}}
|
|
||||||
dump_yaml(compose_test_1, "test-compose-1.yaml")
|
|
||||||
dump_yaml(compose_test_2, "test-compose-2.yaml")
|
|
||||||
|
|
||||||
podman_compose = PodmanCompose()
|
|
||||||
set_args(podman_compose, ["test-compose-1.yaml", "test-compose-2.yaml"])
|
|
||||||
|
|
||||||
podman_compose._parse_compose_file() # pylint: disable=protected-access
|
|
||||||
|
|
||||||
actual = {}
|
|
||||||
if podman_compose.services:
|
|
||||||
podman_compose.services["test-service"].pop("_deps")
|
|
||||||
actual = podman_compose.services["test-service"]
|
|
||||||
self.assertEqual(actual, expected)
|
|
||||||
|
|
||||||
|
|
||||||
def template_to_expression(base, override, expected, key):
|
|
||||||
base_copy = copy.deepcopy(base)
|
|
||||||
override_copy = copy.deepcopy(override)
|
|
||||||
expected_copy = copy.deepcopy(expected)
|
|
||||||
|
|
||||||
expected_copy[key] = expected_copy.pop("$$$")
|
|
||||||
if "$$$" in base:
|
|
||||||
base_copy[key] = base_copy.pop("$$$")
|
|
||||||
if "$$$" in override:
|
|
||||||
override_copy[key] = override_copy.pop("$$$")
|
|
||||||
return base_copy, override_copy, expected_copy
|
|
||||||
|
|
||||||
|
|
||||||
def set_args(podman_compose: PodmanCompose, file_names: list[str]) -> None:
|
|
||||||
podman_compose.global_args = argparse.Namespace()
|
|
||||||
podman_compose.global_args.file = file_names
|
|
||||||
podman_compose.global_args.project_name = None
|
|
||||||
podman_compose.global_args.env_file = None
|
|
||||||
podman_compose.global_args.profile = []
|
|
||||||
podman_compose.global_args.in_pod = True
|
|
||||||
podman_compose.global_args.no_normalize = None
|
|
||||||
|
|
||||||
|
|
||||||
def dump_yaml(compose: dict, name: str) -> None:
|
|
||||||
with open(name, "w", encoding="utf-8") as outfile:
|
|
||||||
yaml.safe_dump(compose, outfile, default_flow_style=False)
|
|
||||||
|
|
||||||
|
|
||||||
def test_clean_test_yamls() -> None:
|
|
||||||
test_files = ["test-compose-1.yaml", "test-compose-2.yaml"]
|
|
||||||
for file in test_files:
|
|
||||||
if os.path.exists(file):
|
|
||||||
os.remove(file)
|
|
@ -1,163 +0,0 @@
|
|||||||
# SPDX-License-Identifier: GPL-2.0
|
|
||||||
|
|
||||||
import unittest
|
|
||||||
from unittest import mock
|
|
||||||
|
|
||||||
from podman_compose import container_to_args
|
|
||||||
|
|
||||||
|
|
||||||
def create_compose_mock(project_name="test_project_name"):
|
|
||||||
compose = mock.Mock()
|
|
||||||
compose.project_name = project_name
|
|
||||||
compose.dirname = "test_dirname"
|
|
||||||
compose.container_names_by_service.get = mock.Mock(return_value=None)
|
|
||||||
compose.prefer_volume_over_mount = False
|
|
||||||
compose.default_net = None
|
|
||||||
compose.networks = {}
|
|
||||||
return compose
|
|
||||||
|
|
||||||
|
|
||||||
def get_minimal_container():
|
|
||||||
return {
|
|
||||||
"name": "project_name_service_name1",
|
|
||||||
"service_name": "service_name",
|
|
||||||
"image": "busybox",
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
class TestContainerToArgs(unittest.IsolatedAsyncioTestCase):
|
|
||||||
async def test_minimal(self):
|
|
||||||
c = create_compose_mock()
|
|
||||||
|
|
||||||
cnt = get_minimal_container()
|
|
||||||
|
|
||||||
args = await container_to_args(c, cnt)
|
|
||||||
self.assertEqual(
|
|
||||||
args,
|
|
||||||
[
|
|
||||||
"--name=project_name_service_name1",
|
|
||||||
"-d",
|
|
||||||
"--network=bridge",
|
|
||||||
"--network-alias=service_name",
|
|
||||||
"busybox",
|
|
||||||
],
|
|
||||||
)
|
|
||||||
|
|
||||||
async def test_runtime(self):
|
|
||||||
c = create_compose_mock()
|
|
||||||
|
|
||||||
cnt = get_minimal_container()
|
|
||||||
cnt["runtime"] = "runsc"
|
|
||||||
|
|
||||||
args = await container_to_args(c, cnt)
|
|
||||||
self.assertEqual(
|
|
||||||
args,
|
|
||||||
[
|
|
||||||
"--name=project_name_service_name1",
|
|
||||||
"-d",
|
|
||||||
"--network=bridge",
|
|
||||||
"--network-alias=service_name",
|
|
||||||
"--runtime",
|
|
||||||
"runsc",
|
|
||||||
"busybox",
|
|
||||||
],
|
|
||||||
)
|
|
||||||
|
|
||||||
async def test_sysctl_list(self):
|
|
||||||
c = create_compose_mock()
|
|
||||||
|
|
||||||
cnt = get_minimal_container()
|
|
||||||
cnt["sysctls"] = [
|
|
||||||
"net.core.somaxconn=1024",
|
|
||||||
"net.ipv4.tcp_syncookies=0",
|
|
||||||
]
|
|
||||||
|
|
||||||
args = await container_to_args(c, cnt)
|
|
||||||
self.assertEqual(
|
|
||||||
args,
|
|
||||||
[
|
|
||||||
"--name=project_name_service_name1",
|
|
||||||
"-d",
|
|
||||||
"--network=bridge",
|
|
||||||
"--network-alias=service_name",
|
|
||||||
"--sysctl",
|
|
||||||
"net.core.somaxconn=1024",
|
|
||||||
"--sysctl",
|
|
||||||
"net.ipv4.tcp_syncookies=0",
|
|
||||||
"busybox",
|
|
||||||
],
|
|
||||||
)
|
|
||||||
|
|
||||||
async def test_sysctl_map(self):
|
|
||||||
c = create_compose_mock()
|
|
||||||
|
|
||||||
cnt = get_minimal_container()
|
|
||||||
cnt["sysctls"] = {
|
|
||||||
"net.core.somaxconn": 1024,
|
|
||||||
"net.ipv4.tcp_syncookies": 0,
|
|
||||||
}
|
|
||||||
|
|
||||||
args = await container_to_args(c, cnt)
|
|
||||||
self.assertEqual(
|
|
||||||
args,
|
|
||||||
[
|
|
||||||
"--name=project_name_service_name1",
|
|
||||||
"-d",
|
|
||||||
"--network=bridge",
|
|
||||||
"--network-alias=service_name",
|
|
||||||
"--sysctl",
|
|
||||||
"net.core.somaxconn=1024",
|
|
||||||
"--sysctl",
|
|
||||||
"net.ipv4.tcp_syncookies=0",
|
|
||||||
"busybox",
|
|
||||||
],
|
|
||||||
)
|
|
||||||
|
|
||||||
async def test_sysctl_wrong_type(self):
|
|
||||||
c = create_compose_mock()
|
|
||||||
cnt = get_minimal_container()
|
|
||||||
|
|
||||||
# check whether wrong types are correctly rejected
|
|
||||||
for wrong_type in [True, 0, 0.0, "wrong", ()]:
|
|
||||||
with self.assertRaises(TypeError):
|
|
||||||
cnt["sysctls"] = wrong_type
|
|
||||||
await container_to_args(c, cnt)
|
|
||||||
|
|
||||||
async def test_pid(self):
|
|
||||||
c = create_compose_mock()
|
|
||||||
cnt = get_minimal_container()
|
|
||||||
|
|
||||||
cnt["pid"] = "host"
|
|
||||||
|
|
||||||
args = await container_to_args(c, cnt)
|
|
||||||
self.assertEqual(
|
|
||||||
args,
|
|
||||||
[
|
|
||||||
"--name=project_name_service_name1",
|
|
||||||
"-d",
|
|
||||||
"--network=bridge",
|
|
||||||
"--network-alias=service_name",
|
|
||||||
"--pid",
|
|
||||||
"host",
|
|
||||||
"busybox",
|
|
||||||
],
|
|
||||||
)
|
|
||||||
|
|
||||||
async def test_http_proxy(self):
|
|
||||||
c = create_compose_mock()
|
|
||||||
|
|
||||||
cnt = get_minimal_container()
|
|
||||||
cnt["http_proxy"] = False
|
|
||||||
|
|
||||||
args = await container_to_args(c, cnt)
|
|
||||||
self.assertEqual(
|
|
||||||
args,
|
|
||||||
[
|
|
||||||
"--name=project_name_service_name1",
|
|
||||||
"-d",
|
|
||||||
"--http-proxy=false",
|
|
||||||
"--network=bridge",
|
|
||||||
"--network-alias=service_name",
|
|
||||||
"busybox",
|
|
||||||
],
|
|
||||||
)
|
|
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
|
#!/bin/bash
|
||||||
./scripts/uninstall.sh
|
|
||||||
./scripts/clean_up.sh
|
set -e
|
||||||
python3 setup.py register
|
|
||||||
python3 setup.py sdist bdist_wheel
|
if [ $# -ne 1 ]; then
|
||||||
twine upload dist/*
|
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]
|
[flake8]
|
||||||
# The GitHub editor is 127 chars wide
|
# 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=[
|
classifiers=[
|
||||||
"Programming Language :: Python",
|
"Programming Language :: Python",
|
||||||
"Programming Language :: Python :: 3",
|
"Programming Language :: Python :: 3",
|
||||||
"Programming Language :: Python :: 3.7",
|
|
||||||
"Programming Language :: Python :: 3.8",
|
|
||||||
"Programming Language :: Python :: 3.9",
|
"Programming Language :: Python :: 3.9",
|
||||||
"Programming Language :: Python :: 3.10",
|
"Programming Language :: Python :: 3.10",
|
||||||
"Programming Language :: Python :: 3.11",
|
"Programming Language :: Python :: 3.11",
|
||||||
|
"Programming Language :: Python :: 3.12",
|
||||||
|
"Programming Language :: Python :: 3.13",
|
||||||
"Intended Audience :: Developers",
|
"Intended Audience :: Developers",
|
||||||
"Operating System :: OS Independent",
|
"Operating System :: OS Independent",
|
||||||
"Development Status :: 3 - Alpha",
|
"Development Status :: 3 - Alpha",
|
||||||
|
@ -29,5 +29,6 @@ pluggy==1.4.0
|
|||||||
pyproject-api==1.6.1
|
pyproject-api==1.6.1
|
||||||
python-dotenv==1.0.1
|
python-dotenv==1.0.1
|
||||||
PyYAML==6.0.1
|
PyYAML==6.0.1
|
||||||
|
requests
|
||||||
tomlkit==0.12.4
|
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,9 +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
|
|
||||||
```
|
|
@ -1 +0,0 @@
|
|||||||
ZZVAR1=podman-rocks-123
|
|
@ -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"
|
version: "3"
|
||||||
services:
|
services:
|
||||||
too_long:
|
|
||||||
image: nopush/podman-compose-test
|
|
||||||
command: ["dumb-init", "/bin/busybox", "sh", "-c", "sleep 3600; exit 0"]
|
|
||||||
tmpfs:
|
|
||||||
- /run
|
|
||||||
- /tmp
|
|
||||||
sh1:
|
sh1:
|
||||||
image: nopush/podman-compose-test
|
image: nopush/podman-compose-test
|
||||||
command: ["dumb-init", "/bin/busybox", "sh", "-c", "sleep 1; exit 1"]
|
command: ["dumb-init", "/bin/busybox", "sh", "-c", "sleep 1; exit 1"]
|
||||||
tmpfs:
|
|
||||||
- /run
|
|
||||||
- /tmp
|
|
||||||
sh2:
|
sh2:
|
||||||
image: nopush/podman-compose-test
|
image: nopush/podman-compose-test
|
||||||
command: ["dumb-init", "/bin/busybox", "sh", "-c", "sleep 1; exit 2"]
|
command: ["dumb-init", "/bin/busybox", "sh", "-c", "sleep 2; exit 0"]
|
||||||
tmpfs:
|
sh3:
|
||||||
- /run
|
image: nopush/podman-compose-test
|
||||||
- /tmp
|
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",
|
||||||
|
])
|
14
tests/integration/additional_contexts/README.md
Normal file
14
tests/integration/additional_contexts/README.md
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
# Test podman-compose with build.additional_contexts
|
||||||
|
|
||||||
|
```
|
||||||
|
podman-compose build
|
||||||
|
podman-compose up
|
||||||
|
podman-compose down
|
||||||
|
```
|
||||||
|
|
||||||
|
expected output would be
|
||||||
|
|
||||||
|
```
|
||||||
|
[dict] | Data for dict
|
||||||
|
[list] | Data for list
|
||||||
|
```
|
1
tests/integration/additional_contexts/__init__.py
Normal file
1
tests/integration/additional_contexts/__init__.py
Normal file
@ -0,0 +1 @@
|
|||||||
|
|
@ -0,0 +1 @@
|
|||||||
|
Data for dict
|
@ -0,0 +1 @@
|
|||||||
|
Data for list
|
3
tests/integration/additional_contexts/project/Dockerfile
Normal file
3
tests/integration/additional_contexts/project/Dockerfile
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
FROM busybox
|
||||||
|
COPY --from=data data.txt /data/data.txt
|
||||||
|
CMD ["busybox", "cat", "/data/data.txt"]
|
@ -0,0 +1,12 @@
|
|||||||
|
version: "3.7"
|
||||||
|
services:
|
||||||
|
dict:
|
||||||
|
build:
|
||||||
|
context: .
|
||||||
|
additional_contexts:
|
||||||
|
data: ../data_for_dict
|
||||||
|
list:
|
||||||
|
build:
|
||||||
|
context: .
|
||||||
|
additional_contexts:
|
||||||
|
- data=../data_for_list
|
@ -0,0 +1,44 @@
|
|||||||
|
# SPDX-License-Identifier: GPL-2.0
|
||||||
|
|
||||||
|
|
||||||
|
"""Test how additional contexts are passed to podman."""
|
||||||
|
|
||||||
|
import os
|
||||||
|
import subprocess
|
||||||
|
import unittest
|
||||||
|
|
||||||
|
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"""
|
||||||
|
return os.path.join(test_path(), "additional_contexts", "project")
|
||||||
|
|
||||||
|
|
||||||
|
class TestComposeBuildAdditionalContexts(unittest.TestCase):
|
||||||
|
def test_build_additional_context(self):
|
||||||
|
"""podman build should receive additional contexts as --build-context
|
||||||
|
|
||||||
|
See additional_context/project/docker-compose.yaml for context paths
|
||||||
|
"""
|
||||||
|
cmd = (
|
||||||
|
"coverage",
|
||||||
|
"run",
|
||||||
|
podman_compose_path(),
|
||||||
|
"--dry-run",
|
||||||
|
"--verbose",
|
||||||
|
"-f",
|
||||||
|
os.path.join(compose_yaml_path(), "docker-compose.yml"),
|
||||||
|
"build",
|
||||||
|
)
|
||||||
|
p = subprocess.run(
|
||||||
|
cmd,
|
||||||
|
stdout=subprocess.PIPE,
|
||||||
|
check=False,
|
||||||
|
stderr=subprocess.STDOUT,
|
||||||
|
text=True,
|
||||||
|
)
|
||||||
|
self.assertEqual(p.returncode, 0)
|
||||||
|
self.assertIn("--build-context=data=../data_for_dict", p.stdout)
|
||||||
|
self.assertIn("--build-context=data=../data_for_list", p.stdout)
|
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 subprocess
|
||||||
import unittest
|
import unittest
|
||||||
|
|
||||||
from .test_podman_compose import podman_compose_path
|
from tests.integration.test_utils import podman_compose_path
|
||||||
from .test_podman_compose import test_path
|
from tests.integration.test_utils import test_path
|
||||||
|
|
||||||
|
|
||||||
def compose_yaml_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
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user