mirror of
https://github.com/containers/podman-compose.git
synced 2025-07-03 22:20:13 +02:00
Compare commits
145 Commits
Author | SHA1 | Date | |
---|---|---|---|
9cbc4c1dcd | |||
c63d678887 | |||
6de335beb2 | |||
23799245bd | |||
eda4815715 | |||
cbb0cab814 | |||
7c8696b2b2 | |||
483103ac3a | |||
e9f1029406 | |||
804852b218 | |||
0e3b372a29 | |||
f11e08eaac | |||
62789a2358 | |||
35cf4bcb72 | |||
9964604b17 | |||
61fa24bf21 | |||
ac7ec5c166 | |||
75d7be2b7b | |||
346f7a57f0 | |||
55642247e3 | |||
978a1381bc | |||
4a232f5e32 | |||
aa8c6fd598 | |||
fb0bbd6fe1 | |||
b324029f25 | |||
590c371db2 | |||
16196a1f6d | |||
2dfbb59097 | |||
a34d1d1a31 | |||
d6da65e6c9 | |||
de2c33d7ae | |||
2891be01d7 | |||
a023dc145b | |||
d803c4c3e8 | |||
420d19daf4 | |||
2cfc617f9e | |||
376f0817e6 | |||
054c66b568 | |||
d9fc8e91f2 | |||
145ae47c48 | |||
a67fa0beb5 | |||
3ba0396e7a | |||
973e15ba23 | |||
002c2e400b | |||
626e278794 | |||
a358890d54 | |||
9f3251ff3d | |||
a9cfdb6704 | |||
c0dc3e47fd | |||
41e69be201 | |||
4ae6ccb5c0 | |||
4203f799ee | |||
69e95be2f6 | |||
122a914b9b | |||
db0aad97bd | |||
a3fb4b373a | |||
ab33954f6c | |||
4660feb04a | |||
90f54b9ca5 | |||
7090de3bce | |||
df8fa588a5 | |||
e2ae8bee04 | |||
5c81bbfcb7 | |||
22b0c4b348 | |||
380cf42dcd | |||
deed4d51b0 | |||
08b3ac2633 | |||
f8ea85e3af | |||
0de7e13f1a | |||
da7fd4fe86 | |||
cb294d7519 | |||
351858dbec | |||
8d0dd214ae | |||
229650cba8 | |||
ab832d23c7 | |||
4a7329b9e3 | |||
de3f93c491 | |||
54cc055a5c | |||
f4bf69b68e | |||
5297b004af | |||
fd1fc833b0 | |||
124879a7b9 | |||
368efe2ee3 | |||
eeefd37996 | |||
b7514a0647 | |||
561094954d | |||
ea239c4b77 | |||
9fdee76858 | |||
0a6e0a35e1 | |||
26e6651d6c | |||
462603383c | |||
8411db49d1 | |||
fc90f60bf1 | |||
ed58ac0879 | |||
305f25b4d6 | |||
edadf73d82 | |||
dc04108b3e | |||
348461ca77 | |||
92dbd3690e | |||
a1e9a82693 | |||
9e11c6bfbc | |||
0d24c41afb | |||
585d344d0a | |||
3aa6d4d158 | |||
34f5268e37 | |||
b5eaf314ad | |||
b3c49df6eb | |||
dbbd695463 | |||
1b1d3d8c25 | |||
7d7e64fe5a | |||
3c9c18c6e0 | |||
d0a2a44442 | |||
1e66c28bbb | |||
b6eadd56b1 | |||
d95b4d026b | |||
e2eb883709 | |||
0866492a7e | |||
85050097e5 | |||
daab93b762 | |||
9fe30387ee | |||
0f8348bea7 | |||
55ab3fa7f7 | |||
2091ade7b1 | |||
ca58d7cd58 | |||
3296c8d34f | |||
dab6b1b98d | |||
5bf4c0fdbe | |||
67c5352c3a | |||
5e0f7e5e19 | |||
5040a37d47 | |||
c82859b89f | |||
29195be77c | |||
1c74d6cd11 | |||
9a4af0ce62 | |||
0517b9e34c | |||
24038dace3 | |||
0ea4cbe091 | |||
2056e703d5 | |||
137c6207b2 | |||
4ec57c1013 | |||
d9a3572461 | |||
fa3e0a7772 | |||
0bcf0799b6 | |||
18472b53ac | |||
f0bae1e2d9 |
2
.github/ISSUE_TEMPLATE/bug_report.md
vendored
2
.github/ISSUE_TEMPLATE/bug_report.md
vendored
@ -13,8 +13,6 @@ A clear and concise description of what the bug is.
|
|||||||
Please make sure it's not a bug in podman (in that case report it to podman)
|
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.)
|
||||||
|
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 -v 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
|
||||||
|
@ -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
|
@ -74,6 +74,12 @@ pip3 install https://github.com/containers/podman-compose/archive/main.tar.gz
|
|||||||
brew install podman-compose
|
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
|
### Manual
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
@ -130,7 +136,7 @@ that are meant to test as many cases as we can to make sure we are compatible
|
|||||||
run a unittest with following command
|
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
|
||||||
|
```
|
||||||
|
|
||||||
|
This will create release commit, tag and push everything.
|
||||||
|
|
||||||
|
Step 5: Create a release on Github
|
||||||
|
----------------------------------
|
||||||
|
|
||||||
|
The release notes must be added manually by drafting a release on the GitHub UI at
|
||||||
|
https://github.com/containers/podman-compose/releases.
|
38
docs/Changelog-1.3.0.md
Normal file
38
docs/Changelog-1.3.0.md
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
Version 1.3.0 (2025-01-07)
|
||||||
|
==========================
|
||||||
|
|
||||||
|
Bug fixes
|
||||||
|
---------
|
||||||
|
|
||||||
|
- Fixed support for de-facto alternative `Dockerfile` names (e.g. `Containerfile`)
|
||||||
|
- Fixed a bug that caused attempts to create already existing pods multiple times.
|
||||||
|
- Fixed compatibility with docker-compose in how symlinks to docker-compose.yml are handled.
|
||||||
|
- Fixed freeze caused by too long log lines without a newline.
|
||||||
|
- Fixed support for `network_mode: none`.
|
||||||
|
- Improved error detection by rejecting service definitions that contain both `network_mode` and
|
||||||
|
`networks` keys, which is not allowed.
|
||||||
|
|
||||||
|
|
||||||
|
Features
|
||||||
|
--------
|
||||||
|
|
||||||
|
- Added support for build labels.
|
||||||
|
- Added support for "platform" property in the build command.
|
||||||
|
- Added support for "ssh" property in the build command.
|
||||||
|
- Added support for cache_from and cache_to fields in build section.
|
||||||
|
- Added support for honoring the condition in the depends_on section of the service, if stated.
|
||||||
|
- Added `x-podman.no_hosts` setting to pass `--no-hosts` to podman run
|
||||||
|
- Added support for compatibility with docker compose for default network behavior when no network
|
||||||
|
defined in service. This is controlled via `default_net_behavior_compat` feature flag.
|
||||||
|
- Added a way to get compatibility of default network names with docker compose.
|
||||||
|
This is selected by setting `default_net_name_compat: true` on `x-podman` global dictionary.
|
||||||
|
- Added support for the `device_cgroup_rules` property in services.
|
||||||
|
- Added support for removing networks in `podman-compose down`.
|
||||||
|
- Added support for network scoped service aliases.
|
||||||
|
- Added support for network level `mac_address` attribute.
|
||||||
|
- Added ability to substitute variables with the environment of the service.
|
||||||
|
|
||||||
|
Misc
|
||||||
|
----
|
||||||
|
|
||||||
|
- Declared compatibility with Python 3.13.
|
@ -7,13 +7,15 @@ are generally specified under fields with "x-podman" prefix in the compose file.
|
|||||||
|
|
||||||
The following extension keys are available under container configuration:
|
The following extension keys are available under container configuration:
|
||||||
|
|
||||||
* `x-podman.uidmap` - Run the container in a new user namespace using the supplied UID mapping.
|
* `x-podman.uidmaps` - Run the container in a new user namespace using the supplied UID mapping.
|
||||||
|
|
||||||
* `x-podman.gidmap` - Run the container in a new user namespace using the supplied GID mapping.
|
* `x-podman.gidmaps` - Run the container in a new user namespace using the supplied GID mapping.
|
||||||
|
|
||||||
* `x-podman.rootfs` - Run the container without requiring any image management; the rootfs of the
|
* `x-podman.rootfs` - Run the container without requiring any image management; the rootfs of the
|
||||||
container is assumed to be managed externally.
|
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.
|
For example, the following docker-compose.yml allows running a podman container with externally managed rootfs.
|
||||||
```yml
|
```yml
|
||||||
version: "3"
|
version: "3"
|
||||||
@ -36,6 +38,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.
|
||||||
|
|
||||||
@ -67,7 +75,7 @@ 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
|
||||||
```
|
```
|
||||||
|
|
||||||
## Podman-specific network modes
|
## Podman-specific network modes
|
||||||
@ -91,6 +99,43 @@ The options to the network modes are passed to the `--network` option of the `po
|
|||||||
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
|
## Custom pods management
|
||||||
|
|
||||||
Podman-compose can have containers in pods. This can be controlled by extension key x-podman in_pod.
|
Podman-compose can have containers in pods. This can be controlled by extension key x-podman in_pod.
|
||||||
|
1095
podman_compose.py
1095
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,91 +0,0 @@
|
|||||||
# SPDX-License-Identifier: GPL-2.0
|
|
||||||
|
|
||||||
import unittest
|
|
||||||
|
|
||||||
from podman_compose import container_to_args
|
|
||||||
|
|
||||||
from .test_container_to_args import create_compose_mock
|
|
||||||
from .test_container_to_args import get_minimal_container
|
|
||||||
|
|
||||||
|
|
||||||
class TestContainerToArgsSecrets(unittest.IsolatedAsyncioTestCase):
|
|
||||||
async def test_pass_secret_as_env_variable(self):
|
|
||||||
c = create_compose_mock()
|
|
||||||
c.declared_secrets = {
|
|
||||||
"my_secret": {"external": "true"} # must have external or name value
|
|
||||||
}
|
|
||||||
|
|
||||||
cnt = get_minimal_container()
|
|
||||||
cnt["secrets"] = [
|
|
||||||
{
|
|
||||||
"source": "my_secret",
|
|
||||||
"target": "ENV_SECRET",
|
|
||||||
"type": "env",
|
|
||||||
},
|
|
||||||
]
|
|
||||||
|
|
||||||
args = await container_to_args(c, cnt)
|
|
||||||
self.assertEqual(
|
|
||||||
args,
|
|
||||||
[
|
|
||||||
"--name=project_name_service_name1",
|
|
||||||
"-d",
|
|
||||||
"--network=bridge",
|
|
||||||
"--network-alias=service_name",
|
|
||||||
"--secret",
|
|
||||||
"my_secret,type=env,target=ENV_SECRET",
|
|
||||||
"busybox",
|
|
||||||
],
|
|
||||||
)
|
|
||||||
|
|
||||||
async def test_secret_as_env_external_true_has_no_name(self):
|
|
||||||
c = create_compose_mock()
|
|
||||||
c.declared_secrets = {
|
|
||||||
"my_secret": {
|
|
||||||
"name": "my_secret", # must have external or name value
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
cnt = get_minimal_container()
|
|
||||||
cnt["_service"] = "test-service"
|
|
||||||
cnt["secrets"] = [
|
|
||||||
{
|
|
||||||
"source": "my_secret",
|
|
||||||
"target": "ENV_SECRET",
|
|
||||||
"type": "env",
|
|
||||||
}
|
|
||||||
]
|
|
||||||
|
|
||||||
args = await container_to_args(c, cnt)
|
|
||||||
self.assertEqual(
|
|
||||||
args,
|
|
||||||
[
|
|
||||||
"--name=project_name_service_name1",
|
|
||||||
"-d",
|
|
||||||
"--network=bridge",
|
|
||||||
"--network-alias=service_name",
|
|
||||||
"--secret",
|
|
||||||
"my_secret,type=env,target=ENV_SECRET",
|
|
||||||
"busybox",
|
|
||||||
],
|
|
||||||
)
|
|
||||||
|
|
||||||
async def test_pass_secret_as_env_variable_no_external(self):
|
|
||||||
c = create_compose_mock()
|
|
||||||
c.declared_secrets = {
|
|
||||||
"my_secret": {} # must have external or name value
|
|
||||||
}
|
|
||||||
|
|
||||||
cnt = get_minimal_container()
|
|
||||||
cnt["_service"] = "test-service"
|
|
||||||
cnt["secrets"] = [
|
|
||||||
{
|
|
||||||
"source": "my_secret",
|
|
||||||
"target": "ENV_SECRET",
|
|
||||||
"type": "env",
|
|
||||||
}
|
|
||||||
]
|
|
||||||
|
|
||||||
with self.assertRaises(ValueError) as context:
|
|
||||||
await container_to_args(c, cnt)
|
|
||||||
self.assertIn('ERROR: unparsable secret: ', str(context.exception))
|
|
33
scripts/Changelog-template.jinja
Normal file
33
scripts/Changelog-template.jinja
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
{% for section, _ in sections|dictsort(by='key') %}
|
||||||
|
{% set underline = "-" %}
|
||||||
|
{% if section %}
|
||||||
|
{{section}}
|
||||||
|
{{ underline * section|length }}{% set underline = "~" %}
|
||||||
|
|
||||||
|
{% endif %}
|
||||||
|
{% if sections[section] %}
|
||||||
|
{% for category, val in definitions|dictsort if category in sections[section]%}
|
||||||
|
|
||||||
|
{{ definitions[category]['name'] }}
|
||||||
|
{{ underline * definitions[category]['name']|length }}
|
||||||
|
|
||||||
|
{% for text, values in sections[section][category]|dictsort(by='value') %}
|
||||||
|
- {{ text }}
|
||||||
|
{% endfor %}
|
||||||
|
|
||||||
|
{% if sections[section][category]|length == 0 %}
|
||||||
|
|
||||||
|
No significant changes.
|
||||||
|
|
||||||
|
|
||||||
|
{% else %}
|
||||||
|
{% endif %}
|
||||||
|
{% endfor %}
|
||||||
|
{% else %}
|
||||||
|
|
||||||
|
No significant changes.
|
||||||
|
|
||||||
|
|
||||||
|
{% endif %}
|
||||||
|
{% endfor %}
|
||||||
|
(venv) p12@exec-desktop:~/cod
|
16
scripts/download_and_build_podman-compose.sh
Normal file
16
scripts/download_and_build_podman-compose.sh
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
# Delete repository dir
|
||||||
|
rm -rf podman-compose-src
|
||||||
|
|
||||||
|
# Clone repository
|
||||||
|
git clone https://github.com/containers/podman-compose podman-compose-src
|
||||||
|
|
||||||
|
# Generate binary
|
||||||
|
sh podman-compose-src/scripts/generate_binary_using_dockerfile.sh
|
||||||
|
|
||||||
|
# Move binary outside repo's dir
|
||||||
|
mv podman-compose-src/podman-compose .
|
||||||
|
|
||||||
|
# Delete repository dir
|
||||||
|
rm -rf podman-compose-src
|
57
scripts/generate_binary_using_dockerfile.sh
Normal file
57
scripts/generate_binary_using_dockerfile.sh
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
# Find an available container tool (docker or podman)
|
||||||
|
find_container_tool() {
|
||||||
|
if command -v docker > /dev/null 2>&1; then
|
||||||
|
echo "sudo docker"
|
||||||
|
elif command -v podman > /dev/null 2>&1; then
|
||||||
|
echo "podman"
|
||||||
|
else
|
||||||
|
echo "Error: Neither docker nor podman is available." >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# Determine which container tool to use
|
||||||
|
CONTAINER_TOOL=$(find_container_tool)
|
||||||
|
|
||||||
|
# Locate the directory containing dockerfile (root)
|
||||||
|
PROJECT_ROOT_DIR="$(cd "$(dirname "$0")" && pwd)/.."
|
||||||
|
|
||||||
|
# Check SELinux status and set appropriate mount option
|
||||||
|
check_selinux() {
|
||||||
|
if command -v getenforce > /dev/null 2>&1; then
|
||||||
|
SELINUX_STATUS=$(getenforce)
|
||||||
|
if [ "$SELINUX_STATUS" = "Enforcing" ] || [ "$SELINUX_STATUS" = "Permissive" ]; then
|
||||||
|
echo ":z"
|
||||||
|
else
|
||||||
|
echo ""
|
||||||
|
fi
|
||||||
|
elif [ -f /sys/fs/selinux/enforce ]; then
|
||||||
|
if [ "$(cat /sys/fs/selinux/enforce)" = "1" ]; then
|
||||||
|
echo ":z"
|
||||||
|
else
|
||||||
|
echo ""
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
echo ""
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# Get the SELinux option for volume mounts if SELinux is enforcing or permissive
|
||||||
|
SELINUX=$(check_selinux)
|
||||||
|
|
||||||
|
# Build binary
|
||||||
|
$CONTAINER_TOOL image rm build-podman-compose
|
||||||
|
|
||||||
|
if expr "$CONTAINER_TOOL" : '.*docker.*' >/dev/null; then
|
||||||
|
$CONTAINER_TOOL build -t build-podman-compose "$PROJECT_ROOT_DIR"
|
||||||
|
$CONTAINER_TOOL run --name build-podman-compose build-podman-compose
|
||||||
|
$CONTAINER_TOOL cp build-podman-compose:/result/podman-compose "$PROJECT_ROOT_DIR/podman-compose"
|
||||||
|
$CONTAINER_TOOL container stop build-podman-compose
|
||||||
|
$CONTAINER_TOOL container rm -f build-podman-compose
|
||||||
|
else
|
||||||
|
$CONTAINER_TOOL build -v "$PROJECT_ROOT_DIR:/result$SELINUX" -t build-podman-compose "$PROJECT_ROOT_DIR"
|
||||||
|
fi
|
||||||
|
$CONTAINER_TOOL image rm python:3.11-slim
|
||||||
|
$CONTAINER_TOOL image rm build-podman-compose
|
@ -1,6 +1,18 @@
|
|||||||
#!/usr/bin/env bash
|
#!/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/*
|
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.25.1
|
||||||
|
@ -1,25 +0,0 @@
|
|||||||
# Test podman-compose with build
|
|
||||||
|
|
||||||
```
|
|
||||||
podman-compose build
|
|
||||||
podman-compose up -d
|
|
||||||
curl http://localhost:8080/index.txt
|
|
||||||
curl http://localhost:8000/index.txt
|
|
||||||
podman inspect my-busybox-httpd2
|
|
||||||
podman-compose down
|
|
||||||
```
|
|
||||||
|
|
||||||
expected output would be something like
|
|
||||||
|
|
||||||
```
|
|
||||||
2019-09-03T15:16:38+0000
|
|
||||||
ALT buildno=2 port 8000 2019-09-03T15:16:38+0000
|
|
||||||
{
|
|
||||||
...
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
as you can see we were able to override buildno to be 2 instead of 1,
|
|
||||||
and httpd_port to 8000.
|
|
||||||
|
|
||||||
NOTE: build labels are not passed to `podman build`
|
|
@ -1,22 +0,0 @@
|
|||||||
# Test podman-compose with build (fail scenario)
|
|
||||||
|
|
||||||
```shell
|
|
||||||
podman-compose build || echo $?
|
|
||||||
```
|
|
||||||
|
|
||||||
expected output would be something like
|
|
||||||
|
|
||||||
```
|
|
||||||
STEP 1/3: FROM busybox
|
|
||||||
STEP 2/3: RUN this_command_does_not_exist
|
|
||||||
/bin/sh: this_command_does_not_exist: not found
|
|
||||||
Error: building at STEP "RUN this_command_does_not_exist": while running runtime: exit status 127
|
|
||||||
|
|
||||||
exit code: 127
|
|
||||||
```
|
|
||||||
|
|
||||||
Expected `podman-compose` exit code:
|
|
||||||
```shell
|
|
||||||
echo $?
|
|
||||||
127
|
|
||||||
```
|
|
@ -1,4 +0,0 @@
|
|||||||
|
|
||||||
```
|
|
||||||
podman-compose run --rm sleep /bin/sh -c 'wget -O - http://web:8000/hosts'
|
|
||||||
```
|
|
@ -1,37 +0,0 @@
|
|||||||
running the following commands should always give podman-rocks-123
|
|
||||||
|
|
||||||
```
|
|
||||||
podman-compose -f project/container-compose.yaml --env-file env-files/project-1.env up
|
|
||||||
```
|
|
||||||
|
|
||||||
```
|
|
||||||
podman-compose -f $(pwd)/project/container-compose.yaml --env-file $(pwd)/env-files/project-1.env up
|
|
||||||
```
|
|
||||||
|
|
||||||
```
|
|
||||||
podman-compose -f $(pwd)/project/container-compose.env-file-flat.yaml up
|
|
||||||
```
|
|
||||||
|
|
||||||
```
|
|
||||||
podman-compose -f $(pwd)/project/container-compose.env-file-obj.yaml up
|
|
||||||
```
|
|
||||||
|
|
||||||
```
|
|
||||||
podman-compose -f $(pwd)/project/container-compose.env-file-obj-optional.yaml up
|
|
||||||
```
|
|
||||||
|
|
||||||
based on environment variable precedent this command should give podman-rocks-321
|
|
||||||
|
|
||||||
```
|
|
||||||
ZZVAR1=podman-rocks-321 podman-compose -f $(pwd)/project/container-compose.yaml --env-file $(pwd)/env-files/project-1.env up
|
|
||||||
```
|
|
||||||
|
|
||||||
_The below test should print three environment variables_
|
|
||||||
|
|
||||||
```
|
|
||||||
podman-compose -f $(pwd)/project/container-compose.load-.env-in-project.yaml run --rm app
|
|
||||||
|
|
||||||
ZZVAR1=This value is overwritten by env-file-tests/.env
|
|
||||||
ZZVAR2=This value is loaded from .env in project/ directory
|
|
||||||
ZZVAR3=This value is loaded from env-file-tests/.env
|
|
||||||
```
|
|
@ -1,5 +0,0 @@
|
|||||||
running the following command should give myval2
|
|
||||||
|
|
||||||
```
|
|
||||||
podman_compose run -l monkey -e ZZVAR1=myval2 env-test
|
|
||||||
```
|
|
@ -1,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
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_podman_compose import podman_compose_path
|
||||||
|
from tests.integration.test_podman_compose import test_path
|
||||||
|
from tests.integration.test_utils import RunSubprocessMixin
|
||||||
|
|
||||||
|
|
||||||
|
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",
|
||||||
|
])
|
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_podman_compose import podman_compose_path
|
||||||
|
from tests.integration.test_podman_compose import test_path
|
||||||
|
from tests.integration.test_utils import RunSubprocessMixin
|
||||||
|
|
||||||
|
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)
|
@ -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
|
22
tests/integration/deps/docker-compose-conditional-fails.yaml
Normal file
22
tests/integration/deps/docker-compose-conditional-fails.yaml
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
version: "3.7"
|
||||||
|
services:
|
||||||
|
web:
|
||||||
|
image: nopush/podman-compose-test
|
||||||
|
command: ["dumb-init", "/bin/busybox", "httpd", "-f", "-h", "/etc/", "-p", "8000"]
|
||||||
|
tmpfs:
|
||||||
|
- /run
|
||||||
|
- /tmp
|
||||||
|
healthcheck:
|
||||||
|
test: ["CMD", "/bin/false"]
|
||||||
|
interval: 10s # Time between health checks
|
||||||
|
timeout: 1s # Time to wait for a response
|
||||||
|
retries: 1 # Number of consecutive failures before marking as unhealthy
|
||||||
|
sleep:
|
||||||
|
image: nopush/podman-compose-test
|
||||||
|
command: ["dumb-init", "/bin/busybox", "sh", "-c", "sleep 3600"]
|
||||||
|
depends_on:
|
||||||
|
web:
|
||||||
|
condition: service_healthy
|
||||||
|
tmpfs:
|
||||||
|
- /run
|
||||||
|
- /tmp
|
@ -0,0 +1,22 @@
|
|||||||
|
version: "3.7"
|
||||||
|
services:
|
||||||
|
web:
|
||||||
|
image: nopush/podman-compose-test
|
||||||
|
command: ["dumb-init", "/bin/busybox", "httpd", "-f", "-h", "/etc/", "-p", "8000"]
|
||||||
|
tmpfs:
|
||||||
|
- /run
|
||||||
|
- /tmp
|
||||||
|
healthcheck:
|
||||||
|
test: ["CMD", "wget", "-qO-", "http://localhost:8000/hosts"]
|
||||||
|
interval: 30s # Time between health checks
|
||||||
|
timeout: 5s # Time to wait for a response
|
||||||
|
retries: 3 # Number of consecutive failures before marking as unhealthy
|
||||||
|
sleep:
|
||||||
|
image: nopush/podman-compose-test
|
||||||
|
command: ["dumb-init", "/bin/busybox", "sh", "-c", "sleep 3600"]
|
||||||
|
depends_on:
|
||||||
|
web:
|
||||||
|
condition: service_healthy
|
||||||
|
tmpfs:
|
||||||
|
- /run
|
||||||
|
- /tmp
|
@ -7,5 +7,5 @@ services:
|
|||||||
- /tmp
|
- /tmp
|
||||||
env_file:
|
env_file:
|
||||||
- path: ../env-files/project-1.env
|
- path: ../env-files/project-1.env
|
||||||
- path: ../env-files/project-2.env
|
- path: ../env-files/project-2.env # this file exists
|
||||||
required: false
|
required: false
|
@ -0,0 +1,11 @@
|
|||||||
|
services:
|
||||||
|
app:
|
||||||
|
image: busybox
|
||||||
|
command: ["/bin/busybox", "sh", "-c", "env | grep ZZ"]
|
||||||
|
tmpfs:
|
||||||
|
- /run
|
||||||
|
- /tmp
|
||||||
|
env_file:
|
||||||
|
- path: ../env-files/project-1.env
|
||||||
|
- path: ../env-files/project-3.env # this file is missing
|
||||||
|
required: false
|
@ -1,9 +1,10 @@
|
|||||||
version: '3'
|
version: "3"
|
||||||
|
|
||||||
services:
|
services:
|
||||||
env-test:
|
env-test:
|
||||||
image: busybox
|
image: busybox
|
||||||
command: sh -c "export | grep ZZ"
|
command: sh -c "export | grep ZZ"
|
||||||
environment:
|
environment:
|
||||||
- ZZVAR1=myval1
|
ZZVAR1: myval1
|
||||||
|
ZZVAR2: 2-$ZZVAR1
|
||||||
|
ZZVAR3: 3-$ZZVAR2
|
@ -1,11 +1,5 @@
|
|||||||
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"]
|
@ -0,0 +1 @@
|
|||||||
|
FROM nopush/podman-compose-test as base
|
1
tests/integration/filesystem/compose_symlink/docker-compose.yml
Symbolic link
1
tests/integration/filesystem/compose_symlink/docker-compose.yml
Symbolic link
@ -0,0 +1 @@
|
|||||||
|
../compose_symlink_dest/docker-compose.yml
|
1
tests/integration/filesystem/compose_symlink/file
Normal file
1
tests/integration/filesystem/compose_symlink/file
Normal file
@ -0,0 +1 @@
|
|||||||
|
data_compose_symlink
|
@ -0,0 +1,7 @@
|
|||||||
|
version: "3"
|
||||||
|
services:
|
||||||
|
container1:
|
||||||
|
image: nopush/podman-compose-test
|
||||||
|
command: ["/bin/busybox", "cat", "/file"]
|
||||||
|
volumes:
|
||||||
|
- "./file:/file"
|
1
tests/integration/filesystem/compose_symlink_dest/file
Normal file
1
tests/integration/filesystem/compose_symlink_dest/file
Normal file
@ -0,0 +1 @@
|
|||||||
|
data_compose_symlink_dest
|
@ -4,8 +4,8 @@ services:
|
|||||||
image: busybox
|
image: busybox
|
||||||
command: ["/bin/busybox", "sh", "-c", "export | grep EXAMPLE"]
|
command: ["/bin/busybox", "sh", "-c", "export | grep EXAMPLE"]
|
||||||
environment:
|
environment:
|
||||||
EXAMPLE_VARIABLE: "Host user: $USER"
|
EXAMPLE_VARIABLE: "Host user: $EXAMPLE_VARIABLE_USER"
|
||||||
EXAMPLE_BRACES: "Host user: ${USER}"
|
EXAMPLE_BRACES: "Host user: ${EXAMPLE_VARIABLE_USER}"
|
||||||
EXAMPLE_COLON_DASH_DEFAULT: ${NOT_A_VARIABLE:-My default}
|
EXAMPLE_COLON_DASH_DEFAULT: ${NOT_A_VARIABLE:-My default}
|
||||||
EXAMPLE_DASH_DEFAULT: ${NOT_A_VARIABLE-My other default}
|
EXAMPLE_DASH_DEFAULT: ${NOT_A_VARIABLE-My other default}
|
||||||
EXAMPLE_DOT_ENV: $DOT_ENV_VARIABLE
|
EXAMPLE_DOT_ENV: $DOT_ENV_VARIABLE
|
@ -1,6 +1,6 @@
|
|||||||
version: '3'
|
version: '3'
|
||||||
|
|
||||||
# --ipam-driver must not be pass when driver is "default"
|
# --ipam-driver must not be passed when driver is "default"
|
||||||
networks:
|
networks:
|
||||||
ipam_test_default:
|
ipam_test_default:
|
||||||
ipam:
|
ipam:
|
125
tests/integration/lifetime/test_lifetime.py
Normal file
125
tests/integration/lifetime/test_lifetime.py
Normal file
@ -0,0 +1,125 @@
|
|||||||
|
# SPDX-License-Identifier: GPL-2.0
|
||||||
|
|
||||||
|
|
||||||
|
import os
|
||||||
|
import unittest
|
||||||
|
|
||||||
|
from parameterized import parameterized
|
||||||
|
|
||||||
|
from tests.integration.test_podman_compose import podman_compose_path
|
||||||
|
from tests.integration.test_podman_compose import test_path
|
||||||
|
from tests.integration.test_utils import RunSubprocessMixin
|
||||||
|
|
||||||
|
|
||||||
|
class TestLifetime(unittest.TestCase, RunSubprocessMixin):
|
||||||
|
def test_up_single_container(self):
|
||||||
|
"""Podman compose up should be able to start containers one after another"""
|
||||||
|
|
||||||
|
compose_path = os.path.join(test_path(), "lifetime/up_single_container/docker-compose.yml")
|
||||||
|
|
||||||
|
try:
|
||||||
|
self.run_subprocess_assert_returncode([
|
||||||
|
podman_compose_path(),
|
||||||
|
"-f",
|
||||||
|
compose_path,
|
||||||
|
"up",
|
||||||
|
"-d",
|
||||||
|
"container1",
|
||||||
|
])
|
||||||
|
|
||||||
|
self.run_subprocess_assert_returncode([
|
||||||
|
podman_compose_path(),
|
||||||
|
"-f",
|
||||||
|
compose_path,
|
||||||
|
"up",
|
||||||
|
"-d",
|
||||||
|
"container2",
|
||||||
|
])
|
||||||
|
|
||||||
|
out, _ = self.run_subprocess_assert_returncode([
|
||||||
|
podman_compose_path(),
|
||||||
|
"-f",
|
||||||
|
compose_path,
|
||||||
|
"logs",
|
||||||
|
"container1",
|
||||||
|
])
|
||||||
|
|
||||||
|
self.assertEqual(out, b"test1\n")
|
||||||
|
|
||||||
|
out, _ = self.run_subprocess_assert_returncode([
|
||||||
|
podman_compose_path(),
|
||||||
|
"-f",
|
||||||
|
compose_path,
|
||||||
|
"logs",
|
||||||
|
"container2",
|
||||||
|
])
|
||||||
|
|
||||||
|
self.assertEqual(out, b"test2\n")
|
||||||
|
|
||||||
|
finally:
|
||||||
|
out, _ = self.run_subprocess_assert_returncode([
|
||||||
|
podman_compose_path(),
|
||||||
|
"-f",
|
||||||
|
compose_path,
|
||||||
|
"down",
|
||||||
|
])
|
||||||
|
|
||||||
|
@parameterized.expand([
|
||||||
|
("no_ports", "up_single_container_many_times"),
|
||||||
|
("with_ports", "up_single_container_many_times_with_ports"),
|
||||||
|
])
|
||||||
|
def test_up_single_container_many_times(self, name, subdir):
|
||||||
|
"""Podman compose up should be able to start a container many times after it finishes
|
||||||
|
running.
|
||||||
|
"""
|
||||||
|
|
||||||
|
compose_path = os.path.join(test_path(), f"lifetime/{subdir}/docker-compose.yml")
|
||||||
|
|
||||||
|
try:
|
||||||
|
self.run_subprocess_assert_returncode([
|
||||||
|
podman_compose_path(),
|
||||||
|
"-f",
|
||||||
|
compose_path,
|
||||||
|
"up",
|
||||||
|
"-d",
|
||||||
|
"container1",
|
||||||
|
])
|
||||||
|
|
||||||
|
for _ in range(0, 3):
|
||||||
|
self.run_subprocess_assert_returncode([
|
||||||
|
podman_compose_path(),
|
||||||
|
"-f",
|
||||||
|
compose_path,
|
||||||
|
"up",
|
||||||
|
"-d",
|
||||||
|
"container2",
|
||||||
|
])
|
||||||
|
|
||||||
|
out, _ = self.run_subprocess_assert_returncode([
|
||||||
|
podman_compose_path(),
|
||||||
|
"-f",
|
||||||
|
compose_path,
|
||||||
|
"logs",
|
||||||
|
"container1",
|
||||||
|
])
|
||||||
|
|
||||||
|
self.assertEqual(out, b"test1\n")
|
||||||
|
|
||||||
|
out, _ = self.run_subprocess_assert_returncode([
|
||||||
|
podman_compose_path(),
|
||||||
|
"-f",
|
||||||
|
compose_path,
|
||||||
|
"logs",
|
||||||
|
"container2",
|
||||||
|
])
|
||||||
|
|
||||||
|
# BUG: container should be started 3 times, not 4.
|
||||||
|
self.assertEqual(out, b"test2\n" * 4)
|
||||||
|
|
||||||
|
finally:
|
||||||
|
out, _ = self.run_subprocess_assert_returncode([
|
||||||
|
podman_compose_path(),
|
||||||
|
"-f",
|
||||||
|
compose_path,
|
||||||
|
"down",
|
||||||
|
])
|
@ -0,0 +1,8 @@
|
|||||||
|
version: "3"
|
||||||
|
services:
|
||||||
|
container1:
|
||||||
|
image: nopush/podman-compose-test
|
||||||
|
command: ["/bin/bash", "-c", "echo test1; sleep infinity"]
|
||||||
|
container2:
|
||||||
|
image: nopush/podman-compose-test
|
||||||
|
command: ["/bin/bash", "-c", "echo test2; sleep infinity"]
|
@ -0,0 +1,9 @@
|
|||||||
|
version: "3"
|
||||||
|
services:
|
||||||
|
container1:
|
||||||
|
image: nopush/podman-compose-test
|
||||||
|
command: ["/bin/bash", "-c", "echo test1; sleep infinity"]
|
||||||
|
container2:
|
||||||
|
image: nopush/podman-compose-test
|
||||||
|
restart: never
|
||||||
|
command: ["/bin/bash", "-c", "echo test2"]
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user