forked from extern/podman-compose
Compare commits
65 Commits
v1.0.6
...
devel-asyn
Author | SHA1 | Date | |
---|---|---|---|
1b85ebe506 | |||
9ed05a23ef | |||
d475260951 | |||
831caa6276 | |||
9ac33392a0 | |||
c5be5bae90 | |||
c6a1c4c432 | |||
3c9628b462 | |||
38b13a34ea | |||
bce40c2db3 | |||
78f8cad7c4 | |||
7942a540cd | |||
cb9cf6002f | |||
06587c1dca | |||
bc9168b039 | |||
57c527c2c9 | |||
d1f5ac9edc | |||
0164c1db56 | |||
e5cdce4e7d | |||
280f1770bf | |||
f75d12af21 | |||
5454c3ad0f | |||
901adf47d0 | |||
bf07e91163 | |||
3890eacf57 | |||
cfd24cc2e8 | |||
79bfad103c | |||
d1509468c3 | |||
9011e9faa1 | |||
517aeba330 | |||
85d5d5dcc9 | |||
1ffd24dcf9 | |||
8c66b1cda7 | |||
a0005db474 | |||
221cf14501 | |||
a61945b516 | |||
6b6330c587 | |||
5d279c4948 | |||
5a3bdbf89b | |||
1eb166445b | |||
82182b7bc6 | |||
3f4618866b | |||
91bc6ebdb4 | |||
59a59c1a3a | |||
620f5d7473 | |||
6f902faed0 | |||
ccdf01e9b0 | |||
e6b1eabe4c | |||
75de39c239 | |||
874192568f | |||
0b853f29f4 | |||
847f01a6c6 | |||
e511e6420f | |||
a9723ec1cf | |||
1cb608d8a7 | |||
252f1d57a5 | |||
13856d2e9c | |||
8d8df0bc28 | |||
bc5f0123d9 | |||
9a08f85ffd | |||
8625d7a4e8 | |||
016c97fd1e | |||
2df11674c4 | |||
5eff38e743 | |||
7f5ce26b1b |
2
.coveragerc
Normal file
2
.coveragerc
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
[run]
|
||||||
|
parallel=True
|
6
.github/workflows/pylint.yml
vendored
6
.github/workflows/pylint.yml
vendored
@ -11,8 +11,8 @@ jobs:
|
|||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v3
|
||||||
- name: Install psf/black requirements
|
- name: Install psf/black requirements
|
||||||
run: |
|
run: |
|
||||||
apt-get update
|
sudo apt-get update
|
||||||
apt-get install -y python3 python3-venv
|
sudo apt-get install -y python3 python3-venv
|
||||||
- uses: psf/black@stable
|
- uses: psf/black@stable
|
||||||
with:
|
with:
|
||||||
options: "--check --verbose"
|
options: "--check --verbose"
|
||||||
@ -22,7 +22,7 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
python-version: ["3.8", "3.9", "3.10"]
|
python-version: ["3.7", "3.8", "3.9", "3.10", "3.11"]
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v3
|
||||||
- name: Set up Python ${{ matrix.python-version }}
|
- name: Set up Python ${{ matrix.python-version }}
|
||||||
|
9
.github/workflows/pytest.yml
vendored
9
.github/workflows/pytest.yml
vendored
@ -21,9 +21,10 @@ jobs:
|
|||||||
python-version: "3.10"
|
python-version: "3.10"
|
||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
run: |
|
run: |
|
||||||
|
sudo apt update && apt install podman
|
||||||
python -m pip install --upgrade pip
|
python -m pip install --upgrade pip
|
||||||
pip install flake8 pytest
|
|
||||||
if [ -f requirements.txt ]; then pip install -r requirements.txt; fi
|
if [ -f requirements.txt ]; then pip install -r requirements.txt; fi
|
||||||
|
if [ -f test-requirements.txt ]; then pip install -r test-requirements.txt; fi
|
||||||
- name: Lint with flake8
|
- name: Lint with flake8
|
||||||
run: |
|
run: |
|
||||||
# stop the build if there are Python syntax errors or undefined names
|
# stop the build if there are Python syntax errors or undefined names
|
||||||
@ -32,5 +33,7 @@ jobs:
|
|||||||
flake8 . --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics
|
flake8 . --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics
|
||||||
- name: Test with pytest
|
- name: Test with pytest
|
||||||
run: |
|
run: |
|
||||||
python -m pytest ./pytests
|
coverage run --source podman_compose -m pytest ./pytests
|
||||||
|
python -m pytest ./tests
|
||||||
|
coverage combine
|
||||||
|
coverage report
|
||||||
|
@ -9,6 +9,9 @@ repos:
|
|||||||
# https://pre-commit.com/#top_level-default_language_version
|
# https://pre-commit.com/#top_level-default_language_version
|
||||||
language_version: python3.10
|
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:
|
||||||
|
@ -39,7 +39,15 @@ $ pre-commit install
|
|||||||
```shell
|
```shell
|
||||||
$ pre-commit run --all-files
|
$ pre-commit run --all-files
|
||||||
```
|
```
|
||||||
6. Commit your code to your fork's branch.
|
6. Run code coverage
|
||||||
|
```shell
|
||||||
|
coverage run --source podman_compose -m pytest ./pytests
|
||||||
|
python -m pytest ./tests
|
||||||
|
coverage combine
|
||||||
|
coverage report
|
||||||
|
coverage html
|
||||||
|
```
|
||||||
|
7. Commit your code to your fork's branch.
|
||||||
- Make sure you include a `Signed-off-by` message in your commits. Read [this guide](https://docs.github.com/en/authentication/managing-commit-signature-verification/signing-commits) to learn how to sign your commits
|
- Make sure you include a `Signed-off-by` message in your commits. Read [this guide](https://docs.github.com/en/authentication/managing-commit-signature-verification/signing-commits) to learn how to sign your commits
|
||||||
- In the commit message reference the Issue ID that your code fixes and a brief description of the changes. Example: `Fixes #516: allow empty network`
|
- In the commit message reference the Issue ID that your code fixes and a brief description of the changes. Example: `Fixes #516: allow empty network`
|
||||||
7. Open a PR to `containers/podman-compose:devel` and wait for a maintainer to review your work.
|
7. Open a PR to `containers/podman-compose:devel` and wait for a maintainer to review your work.
|
||||||
@ -48,18 +56,18 @@ $ pre-commit run --all-files
|
|||||||
|
|
||||||
To add a command you need to add a function that is decorated
|
To add a command you need to add a function that is decorated
|
||||||
with `@cmd_run` passing the compose instance, command name and
|
with `@cmd_run` passing the compose instance, command name and
|
||||||
description. the wrapped function should accept two arguments
|
description. This function must be declared `async` the wrapped
|
||||||
the compose instance and the command-specific arguments (resulted
|
function should accept two arguments the compose instance and
|
||||||
from python's `argparse` package) inside that command you can
|
the command-specific arguments (resulted from python's `argparse`
|
||||||
run PodMan like this `compose.podman.run(['inspect', 'something'])`
|
package) inside that command you can run PodMan like this
|
||||||
and inside that function you can access `compose.pods`
|
`await compose.podman.run(['inspect', 'something'])`and inside
|
||||||
and `compose.containers` ...etc.
|
that function you can access `compose.pods` and `compose.containers`
|
||||||
Here is an example
|
...etc. Here is an example
|
||||||
|
|
||||||
```
|
```
|
||||||
@cmd_run(podman_compose, 'build', 'build images defined in the stack')
|
@cmd_run(podman_compose, 'build', 'build images defined in the stack')
|
||||||
def compose_build(compose, args):
|
async def compose_build(compose, args):
|
||||||
compose.podman.run(['build', 'something'])
|
await compose.podman.run(['build', 'something'])
|
||||||
```
|
```
|
||||||
|
|
||||||
## Command arguments parsing
|
## Command arguments parsing
|
||||||
@ -90,10 +98,10 @@ do something like:
|
|||||||
|
|
||||||
```
|
```
|
||||||
@cmd_run(podman_compose, 'up', 'up desc')
|
@cmd_run(podman_compose, 'up', 'up desc')
|
||||||
def compose_up(compose, args):
|
async def compose_up(compose, args):
|
||||||
compose.commands['down'](compose, args)
|
await compose.commands['down'](compose, args)
|
||||||
# or
|
# or
|
||||||
compose.commands['down'](argparse.Namespace(foo=123))
|
await compose.commands['down'](argparse.Namespace(foo=123))
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
---
|
---
|
||||||
- name: Manage AWX Container Images
|
- name: Manage AWX Container Images
|
||||||
block:
|
block:
|
||||||
- name: Export Docker awx image if it isnt local and there isnt a registry defined
|
- name: Export Docker awx image if it isn't local and there isn't a registry defined
|
||||||
docker_image:
|
docker_image:
|
||||||
name: "{{ awx_image }}"
|
name: "{{ awx_image }}"
|
||||||
tag: "{{ awx_version }}"
|
tag: "{{ awx_version }}"
|
||||||
|
@ -7,6 +7,6 @@ RUN pip install --no-cache-dir -r requirements.txt
|
|||||||
|
|
||||||
COPY . .
|
COPY . .
|
||||||
|
|
||||||
CMD [ "python", "-m", "App.web" ]
|
CMD [ "python", "-m", "app.web" ]
|
||||||
EXPOSE 8080
|
EXPOSE 8080
|
||||||
|
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
|
# pylint: disable=import-error
|
||||||
|
# pylint: disable=unused-import
|
||||||
import os
|
import os
|
||||||
import asyncio
|
import asyncio # noqa: F401
|
||||||
|
|
||||||
import aioredis
|
import aioredis
|
||||||
from aiohttp import web
|
from aiohttp import web
|
||||||
@ -14,13 +16,13 @@ routes = web.RouteTableDef()
|
|||||||
|
|
||||||
|
|
||||||
@routes.get("/")
|
@routes.get("/")
|
||||||
async def hello(request):
|
async def hello(request): # pylint: disable=unused-argument
|
||||||
counter = await redis.incr("mycounter")
|
counter = await redis.incr("mycounter")
|
||||||
return web.Response(text=f"counter={counter}")
|
return web.Response(text=f"counter={counter}")
|
||||||
|
|
||||||
|
|
||||||
@routes.get("/hello.json")
|
@routes.get("/hello.json")
|
||||||
async def hello_json(request):
|
async def hello_json(request): # pylint: disable=unused-argument
|
||||||
counter = await redis.incr("mycounter")
|
counter = await redis.incr("mycounter")
|
||||||
data = {"counter": counter}
|
data = {"counter": counter}
|
||||||
return web.json_response(data)
|
return web.json_response(data)
|
File diff suppressed because it is too large
Load Diff
168
pytests/test_can_merge_build.py
Normal file
168
pytests/test_can_merge_build.py
Normal file
@ -0,0 +1,168 @@
|
|||||||
|
import copy
|
||||||
|
import os
|
||||||
|
import argparse
|
||||||
|
import yaml
|
||||||
|
from podman_compose import normalize_service, PodmanCompose
|
||||||
|
|
||||||
|
|
||||||
|
test_cases_simple = [
|
||||||
|
({"test": "test"}, {"test": "test"}),
|
||||||
|
({"build": "."}, {"build": {"context": "."}}),
|
||||||
|
({"build": "./dir-1"}, {"build": {"context": "./dir-1"}}),
|
||||||
|
({"build": {"context": "./dir-1"}}, {"build": {"context": "./dir-1"}}),
|
||||||
|
(
|
||||||
|
{"build": {"dockerfile": "dockerfile-1"}},
|
||||||
|
{"build": {"dockerfile": "dockerfile-1"}},
|
||||||
|
),
|
||||||
|
(
|
||||||
|
{"build": {"context": "./dir-1", "dockerfile": "dockerfile-1"}},
|
||||||
|
{"build": {"context": "./dir-1", "dockerfile": "dockerfile-1"}},
|
||||||
|
),
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
def test_normalize_service_simple():
|
||||||
|
for test_case, expected in copy.deepcopy(test_cases_simple):
|
||||||
|
test_original = copy.deepcopy(test_case)
|
||||||
|
test_case = normalize_service(test_case)
|
||||||
|
test_result = expected == test_case
|
||||||
|
if not test_result:
|
||||||
|
print("test: ", test_original)
|
||||||
|
print("expected: ", expected)
|
||||||
|
print("actual: ", test_case)
|
||||||
|
assert test_result
|
||||||
|
|
||||||
|
|
||||||
|
test_cases_sub_dir = [
|
||||||
|
({"test": "test"}, {"test": "test"}),
|
||||||
|
({"build": "."}, {"build": {"context": "./sub_dir/."}}),
|
||||||
|
({"build": "./dir-1"}, {"build": {"context": "./sub_dir/dir-1"}}),
|
||||||
|
({"build": {"context": "./dir-1"}}, {"build": {"context": "./sub_dir/dir-1"}}),
|
||||||
|
(
|
||||||
|
{"build": {"dockerfile": "dockerfile-1"}},
|
||||||
|
{"build": {"context": "./sub_dir", "dockerfile": "dockerfile-1"}},
|
||||||
|
),
|
||||||
|
(
|
||||||
|
{"build": {"context": "./dir-1", "dockerfile": "dockerfile-1"}},
|
||||||
|
{"build": {"context": "./sub_dir/dir-1", "dockerfile": "dockerfile-1"}},
|
||||||
|
),
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
def test_normalize_service_with_sub_dir():
|
||||||
|
for test_case, expected in copy.deepcopy(test_cases_sub_dir):
|
||||||
|
test_original = copy.deepcopy(test_case)
|
||||||
|
test_case = normalize_service(test_case, sub_dir="./sub_dir")
|
||||||
|
test_result = expected == test_case
|
||||||
|
if not test_result:
|
||||||
|
print("test: ", test_original)
|
||||||
|
print("expected: ", expected)
|
||||||
|
print("actual: ", test_case)
|
||||||
|
assert test_result
|
||||||
|
|
||||||
|
|
||||||
|
test_cases_merges = [
|
||||||
|
({}, {}, {}),
|
||||||
|
({}, {"test": "test"}, {"test": "test"}),
|
||||||
|
({"test": "test"}, {}, {"test": "test"}),
|
||||||
|
({"test": "test-1"}, {"test": "test-2"}, {"test": "test-2"}),
|
||||||
|
({}, {"build": "."}, {"build": {"context": "."}}),
|
||||||
|
({"build": "."}, {}, {"build": {"context": "."}}),
|
||||||
|
({"build": "./dir-1"}, {"build": "./dir-2"}, {"build": {"context": "./dir-2"}}),
|
||||||
|
({}, {"build": {"context": "./dir-1"}}, {"build": {"context": "./dir-1"}}),
|
||||||
|
({"build": {"context": "./dir-1"}}, {}, {"build": {"context": "./dir-1"}}),
|
||||||
|
(
|
||||||
|
{"build": {"context": "./dir-1"}},
|
||||||
|
{"build": {"context": "./dir-2"}},
|
||||||
|
{"build": {"context": "./dir-2"}},
|
||||||
|
),
|
||||||
|
(
|
||||||
|
{},
|
||||||
|
{"build": {"dockerfile": "dockerfile-1"}},
|
||||||
|
{"build": {"dockerfile": "dockerfile-1"}},
|
||||||
|
),
|
||||||
|
(
|
||||||
|
{"build": {"dockerfile": "dockerfile-1"}},
|
||||||
|
{},
|
||||||
|
{"build": {"dockerfile": "dockerfile-1"}},
|
||||||
|
),
|
||||||
|
(
|
||||||
|
{"build": {"dockerfile": "./dockerfile-1"}},
|
||||||
|
{"build": {"dockerfile": "./dockerfile-2"}},
|
||||||
|
{"build": {"dockerfile": "./dockerfile-2"}},
|
||||||
|
),
|
||||||
|
(
|
||||||
|
{"build": {"dockerfile": "./dockerfile-1"}},
|
||||||
|
{"build": {"context": "./dir-2"}},
|
||||||
|
{"build": {"dockerfile": "./dockerfile-1", "context": "./dir-2"}},
|
||||||
|
),
|
||||||
|
(
|
||||||
|
{"build": {"dockerfile": "./dockerfile-1", "context": "./dir-1"}},
|
||||||
|
{"build": {"dockerfile": "./dockerfile-2", "context": "./dir-2"}},
|
||||||
|
{"build": {"dockerfile": "./dockerfile-2", "context": "./dir-2"}},
|
||||||
|
),
|
||||||
|
(
|
||||||
|
{"build": {"dockerfile": "./dockerfile-1"}},
|
||||||
|
{"build": {"dockerfile": "./dockerfile-2", "args": ["ENV1=1"]}},
|
||||||
|
{"build": {"dockerfile": "./dockerfile-2", "args": ["ENV1=1"]}},
|
||||||
|
),
|
||||||
|
(
|
||||||
|
{"build": {"dockerfile": "./dockerfile-2", "args": ["ENV1=1"]}},
|
||||||
|
{"build": {"dockerfile": "./dockerfile-1"}},
|
||||||
|
{"build": {"dockerfile": "./dockerfile-1", "args": ["ENV1=1"]}},
|
||||||
|
),
|
||||||
|
(
|
||||||
|
{"build": {"dockerfile": "./dockerfile-2", "args": ["ENV1=1"]}},
|
||||||
|
{"build": {"dockerfile": "./dockerfile-1", "args": ["ENV2=2"]}},
|
||||||
|
{"build": {"dockerfile": "./dockerfile-1", "args": ["ENV1=1", "ENV2=2"]}},
|
||||||
|
),
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
def test__parse_compose_file_when_multiple_composes() -> None:
|
||||||
|
for test_input, test_override, expected_result in copy.deepcopy(test_cases_merges):
|
||||||
|
compose_test_1 = {"services": {"test-service": test_input}}
|
||||||
|
compose_test_2 = {"services": {"test-service": test_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_compose = {}
|
||||||
|
if podman_compose.services:
|
||||||
|
podman_compose.services["test-service"].pop("_deps")
|
||||||
|
actual_compose = podman_compose.services["test-service"]
|
||||||
|
if actual_compose != expected_result:
|
||||||
|
print("compose: ", test_input)
|
||||||
|
print("override: ", test_override)
|
||||||
|
print("expected: ", expected_result)
|
||||||
|
print("actual: ", actual_compose)
|
||||||
|
|
||||||
|
compose_expected = expected_result
|
||||||
|
|
||||||
|
assert compose_expected == actual_compose
|
||||||
|
|
||||||
|
|
||||||
|
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 = True
|
||||||
|
|
||||||
|
|
||||||
|
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)
|
122
pytests/test_can_merge_cmd_ent.py
Normal file
122
pytests/test_can_merge_cmd_ent.py
Normal file
@ -0,0 +1,122 @@
|
|||||||
|
import copy
|
||||||
|
import os
|
||||||
|
import argparse
|
||||||
|
import yaml
|
||||||
|
from podman_compose import normalize_service, PodmanCompose
|
||||||
|
|
||||||
|
test_keys = ["command", "entrypoint"]
|
||||||
|
|
||||||
|
test_cases_normalise_pre_merge = [
|
||||||
|
({"$$$": []}, {"$$$": []}),
|
||||||
|
({"$$$": ["sh"]}, {"$$$": ["sh"]}),
|
||||||
|
({"$$$": ["sh", "-c", "date"]}, {"$$$": ["sh", "-c", "date"]}),
|
||||||
|
({"$$$": "sh"}, {"$$$": ["sh"]}),
|
||||||
|
({"$$$": "sleep infinity"}, {"$$$": ["sleep", "infinity"]}),
|
||||||
|
(
|
||||||
|
{"$$$": "bash -c 'sleep infinity'"},
|
||||||
|
{"$$$": ["bash", "-c", "sleep infinity"]},
|
||||||
|
),
|
||||||
|
]
|
||||||
|
|
||||||
|
test_cases_merges = [
|
||||||
|
({}, {"$$$": []}, {"$$$": []}),
|
||||||
|
({"$$$": []}, {}, {"$$$": []}),
|
||||||
|
({"$$$": []}, {"$$$": "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 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 test_normalize_service():
|
||||||
|
for test_input_template, expected_template in test_cases_normalise_pre_merge:
|
||||||
|
for key in test_keys:
|
||||||
|
test_input, _, expected = template_to_expression(
|
||||||
|
test_input_template, {}, expected_template, key
|
||||||
|
)
|
||||||
|
test_input = normalize_service(test_input)
|
||||||
|
test_result = expected == test_input
|
||||||
|
if not test_result:
|
||||||
|
print("base_template: ", test_input_template)
|
||||||
|
print("expected: ", expected)
|
||||||
|
print("actual: ", test_input)
|
||||||
|
assert test_result
|
||||||
|
|
||||||
|
|
||||||
|
def test__parse_compose_file_when_multiple_composes() -> None:
|
||||||
|
for base_template, override_template, expected_template in copy.deepcopy(
|
||||||
|
test_cases_merges
|
||||||
|
):
|
||||||
|
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"]
|
||||||
|
if actual != expected:
|
||||||
|
print("compose: ", base)
|
||||||
|
print("override: ", override)
|
||||||
|
print("result: ", expected)
|
||||||
|
|
||||||
|
assert expected == actual
|
||||||
|
|
||||||
|
|
||||||
|
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)
|
298
pytests/test_normalize_final_build.py
Normal file
298
pytests/test_normalize_final_build.py
Normal file
@ -0,0 +1,298 @@
|
|||||||
|
# pylint: disable=protected-access
|
||||||
|
|
||||||
|
import argparse
|
||||||
|
import copy
|
||||||
|
import os
|
||||||
|
import yaml
|
||||||
|
from podman_compose import (
|
||||||
|
normalize_service,
|
||||||
|
normalize,
|
||||||
|
normalize_final,
|
||||||
|
normalize_service_final,
|
||||||
|
PodmanCompose,
|
||||||
|
)
|
||||||
|
|
||||||
|
cwd = os.path.abspath(".")
|
||||||
|
test_cases_simple_normalization = [
|
||||||
|
({"image": "test-image"}, {"image": "test-image"}),
|
||||||
|
(
|
||||||
|
{"build": "."},
|
||||||
|
{
|
||||||
|
"build": {"context": cwd, "dockerfile": "Dockerfile"},
|
||||||
|
},
|
||||||
|
),
|
||||||
|
(
|
||||||
|
{"build": "../relative"},
|
||||||
|
{
|
||||||
|
"build": {
|
||||||
|
"context": os.path.normpath(os.path.join(cwd, "../relative")),
|
||||||
|
"dockerfile": "Dockerfile",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
),
|
||||||
|
(
|
||||||
|
{"build": "./relative"},
|
||||||
|
{
|
||||||
|
"build": {
|
||||||
|
"context": os.path.normpath(os.path.join(cwd, "./relative")),
|
||||||
|
"dockerfile": "Dockerfile",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
),
|
||||||
|
(
|
||||||
|
{"build": "/workspace/absolute"},
|
||||||
|
{
|
||||||
|
"build": {
|
||||||
|
"context": "/workspace/absolute",
|
||||||
|
"dockerfile": "Dockerfile",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
),
|
||||||
|
(
|
||||||
|
{
|
||||||
|
"build": {
|
||||||
|
"dockerfile": "Dockerfile",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"build": {
|
||||||
|
"context": cwd,
|
||||||
|
"dockerfile": "Dockerfile",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
),
|
||||||
|
(
|
||||||
|
{
|
||||||
|
"build": {
|
||||||
|
"context": ".",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"build": {
|
||||||
|
"context": cwd,
|
||||||
|
"dockerfile": "Dockerfile",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
),
|
||||||
|
(
|
||||||
|
{
|
||||||
|
"build": {"context": "../", "dockerfile": "test-dockerfile"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"build": {
|
||||||
|
"context": os.path.normpath(os.path.join(cwd, "../")),
|
||||||
|
"dockerfile": "test-dockerfile",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
),
|
||||||
|
(
|
||||||
|
{
|
||||||
|
"build": {"context": ".", "dockerfile": "./dev/test-dockerfile"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"build": {
|
||||||
|
"context": cwd,
|
||||||
|
"dockerfile": "./dev/test-dockerfile",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
),
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
#
|
||||||
|
# [service.build] is normalised after merges
|
||||||
|
#
|
||||||
|
def test_normalize_service_final_returns_absolute_path_in_context() -> None:
|
||||||
|
project_dir = cwd
|
||||||
|
for test_input, expected_service in copy.deepcopy(test_cases_simple_normalization):
|
||||||
|
actual_service = normalize_service_final(test_input, project_dir)
|
||||||
|
assert expected_service == actual_service
|
||||||
|
|
||||||
|
|
||||||
|
def test_normalize_returns_absolute_path_in_context() -> None:
|
||||||
|
project_dir = cwd
|
||||||
|
for test_input, expected_result in copy.deepcopy(test_cases_simple_normalization):
|
||||||
|
compose_test = {"services": {"test-service": test_input}}
|
||||||
|
compose_expected = {"services": {"test-service": expected_result}}
|
||||||
|
actual_compose = normalize_final(compose_test, project_dir)
|
||||||
|
assert compose_expected == actual_compose
|
||||||
|
|
||||||
|
|
||||||
|
#
|
||||||
|
# running full parse over single compose files
|
||||||
|
#
|
||||||
|
def test__parse_compose_file_when_single_compose() -> None:
|
||||||
|
for test_input, expected_result in copy.deepcopy(test_cases_simple_normalization):
|
||||||
|
compose_test = {"services": {"test-service": test_input}}
|
||||||
|
dump_yaml(compose_test, "test-compose.yaml")
|
||||||
|
|
||||||
|
podman_compose = PodmanCompose()
|
||||||
|
set_args(podman_compose, ["test-compose.yaml"], no_normalize=None)
|
||||||
|
|
||||||
|
podman_compose._parse_compose_file()
|
||||||
|
|
||||||
|
actual_compose = {}
|
||||||
|
if podman_compose.services:
|
||||||
|
podman_compose.services["test-service"].pop("_deps")
|
||||||
|
actual_compose = podman_compose.services["test-service"]
|
||||||
|
if actual_compose != expected_result:
|
||||||
|
print("compose: ", test_input)
|
||||||
|
print("result: ", expected_result)
|
||||||
|
|
||||||
|
assert expected_result == actual_compose
|
||||||
|
|
||||||
|
|
||||||
|
test_cases_with_merges = [
|
||||||
|
(
|
||||||
|
{},
|
||||||
|
{"build": "."},
|
||||||
|
{"build": {"context": cwd, "dockerfile": "Dockerfile"}},
|
||||||
|
),
|
||||||
|
(
|
||||||
|
{"build": "."},
|
||||||
|
{},
|
||||||
|
{"build": {"context": cwd, "dockerfile": "Dockerfile"}},
|
||||||
|
),
|
||||||
|
(
|
||||||
|
{"build": "/workspace/absolute"},
|
||||||
|
{"build": "./relative"},
|
||||||
|
{
|
||||||
|
"build": {
|
||||||
|
"context": os.path.normpath(os.path.join(cwd, "./relative")),
|
||||||
|
"dockerfile": "Dockerfile",
|
||||||
|
}
|
||||||
|
},
|
||||||
|
),
|
||||||
|
(
|
||||||
|
{"build": "./relative"},
|
||||||
|
{"build": "/workspace/absolute"},
|
||||||
|
{"build": {"context": "/workspace/absolute", "dockerfile": "Dockerfile"}},
|
||||||
|
),
|
||||||
|
(
|
||||||
|
{"build": "./relative"},
|
||||||
|
{"build": "/workspace/absolute"},
|
||||||
|
{"build": {"context": "/workspace/absolute", "dockerfile": "Dockerfile"}},
|
||||||
|
),
|
||||||
|
(
|
||||||
|
{"build": {"dockerfile": "test-dockerfile"}},
|
||||||
|
{},
|
||||||
|
{"build": {"context": cwd, "dockerfile": "test-dockerfile"}},
|
||||||
|
),
|
||||||
|
(
|
||||||
|
{},
|
||||||
|
{"build": {"dockerfile": "test-dockerfile"}},
|
||||||
|
{"build": {"context": cwd, "dockerfile": "test-dockerfile"}},
|
||||||
|
),
|
||||||
|
(
|
||||||
|
{},
|
||||||
|
{"build": {"dockerfile": "test-dockerfile"}},
|
||||||
|
{"build": {"context": cwd, "dockerfile": "test-dockerfile"}},
|
||||||
|
),
|
||||||
|
(
|
||||||
|
{"build": {"dockerfile": "test-dockerfile-1"}},
|
||||||
|
{"build": {"dockerfile": "test-dockerfile-2"}},
|
||||||
|
{"build": {"context": cwd, "dockerfile": "test-dockerfile-2"}},
|
||||||
|
),
|
||||||
|
(
|
||||||
|
{"build": "/workspace/absolute"},
|
||||||
|
{"build": {"dockerfile": "test-dockerfile"}},
|
||||||
|
{"build": {"context": "/workspace/absolute", "dockerfile": "test-dockerfile"}},
|
||||||
|
),
|
||||||
|
(
|
||||||
|
{"build": {"dockerfile": "test-dockerfile"}},
|
||||||
|
{"build": "/workspace/absolute"},
|
||||||
|
{"build": {"context": "/workspace/absolute", "dockerfile": "test-dockerfile"}},
|
||||||
|
),
|
||||||
|
(
|
||||||
|
{"build": {"dockerfile": "./test-dockerfile-1"}},
|
||||||
|
{"build": {"dockerfile": "./test-dockerfile-2", "args": ["ENV1=1"]}},
|
||||||
|
{
|
||||||
|
"build": {
|
||||||
|
"context": cwd,
|
||||||
|
"dockerfile": "./test-dockerfile-2",
|
||||||
|
"args": ["ENV1=1"],
|
||||||
|
}
|
||||||
|
},
|
||||||
|
),
|
||||||
|
(
|
||||||
|
{"build": {"dockerfile": "./test-dockerfile-1", "args": ["ENV1=1"]}},
|
||||||
|
{"build": {"dockerfile": "./test-dockerfile-2"}},
|
||||||
|
{
|
||||||
|
"build": {
|
||||||
|
"context": cwd,
|
||||||
|
"dockerfile": "./test-dockerfile-2",
|
||||||
|
"args": ["ENV1=1"],
|
||||||
|
}
|
||||||
|
},
|
||||||
|
),
|
||||||
|
(
|
||||||
|
{"build": {"dockerfile": "./test-dockerfile-1", "args": ["ENV1=1"]}},
|
||||||
|
{"build": {"dockerfile": "./test-dockerfile-2", "args": ["ENV2=2"]}},
|
||||||
|
{
|
||||||
|
"build": {
|
||||||
|
"context": cwd,
|
||||||
|
"dockerfile": "./test-dockerfile-2",
|
||||||
|
"args": ["ENV1=1", "ENV2=2"],
|
||||||
|
}
|
||||||
|
},
|
||||||
|
),
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
#
|
||||||
|
# running full parse over merged
|
||||||
|
#
|
||||||
|
def test__parse_compose_file_when_multiple_composes() -> None:
|
||||||
|
for test_input, test_override, expected_result in copy.deepcopy(
|
||||||
|
test_cases_with_merges
|
||||||
|
):
|
||||||
|
compose_test_1 = {"services": {"test-service": test_input}}
|
||||||
|
compose_test_2 = {"services": {"test-service": test_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"],
|
||||||
|
no_normalize=None,
|
||||||
|
)
|
||||||
|
|
||||||
|
podman_compose._parse_compose_file()
|
||||||
|
|
||||||
|
actual_compose = {}
|
||||||
|
if podman_compose.services:
|
||||||
|
podman_compose.services["test-service"].pop("_deps")
|
||||||
|
actual_compose = podman_compose.services["test-service"]
|
||||||
|
if actual_compose != expected_result:
|
||||||
|
print("compose: ", test_input)
|
||||||
|
print("override: ", test_override)
|
||||||
|
print("result: ", expected_result)
|
||||||
|
compose_expected = expected_result
|
||||||
|
|
||||||
|
assert compose_expected == actual_compose
|
||||||
|
|
||||||
|
|
||||||
|
def set_args(
|
||||||
|
podman_compose: PodmanCompose, file_names: list[str], no_normalize: bool
|
||||||
|
) -> 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 = no_normalize
|
||||||
|
|
||||||
|
|
||||||
|
def dump_yaml(compose: dict, name: str) -> None:
|
||||||
|
# Path(Path.cwd()/"subdirectory").mkdir(parents=True, exist_ok=True)
|
||||||
|
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", "test-compose.yaml"]
|
||||||
|
for file in test_files:
|
||||||
|
if os.path.exists(file):
|
||||||
|
os.remove(file)
|
@ -1,3 +1,4 @@
|
|||||||
|
# pylint: disable=redefined-outer-name
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from podman_compose import parse_short_mount
|
from podman_compose import parse_short_mount
|
||||||
|
@ -3,3 +3,7 @@ universal = 1
|
|||||||
|
|
||||||
[metadata]
|
[metadata]
|
||||||
version = attr: podman_compose.__version__
|
version = attr: podman_compose.__version__
|
||||||
|
|
||||||
|
[flake8]
|
||||||
|
# The GitHub editor is 127 chars wide
|
||||||
|
max-line-length=127
|
14
setup.py
14
setup.py
@ -2,24 +2,25 @@ import os
|
|||||||
from setuptools import setup
|
from setuptools import setup
|
||||||
|
|
||||||
try:
|
try:
|
||||||
readme = open(os.path.join(os.path.dirname(__file__), "README.md")).read()
|
README = open(
|
||||||
except:
|
os.path.join(os.path.dirname(__file__), "README.md"), encoding="utf-8"
|
||||||
readme = ""
|
).read()
|
||||||
|
except: # noqa: E722 # pylint: disable=bare-except
|
||||||
|
README = ""
|
||||||
|
|
||||||
setup(
|
setup(
|
||||||
name="podman-compose",
|
name="podman-compose",
|
||||||
description="A script to run docker-compose.yml using podman",
|
description="A script to run docker-compose.yml using podman",
|
||||||
long_description=readme,
|
long_description=README,
|
||||||
long_description_content_type="text/markdown",
|
long_description_content_type="text/markdown",
|
||||||
classifiers=[
|
classifiers=[
|
||||||
"Programming Language :: Python",
|
"Programming Language :: Python",
|
||||||
"Programming Language :: Python :: 3",
|
"Programming Language :: Python :: 3",
|
||||||
"Programming Language :: Python :: 3.5",
|
|
||||||
"Programming Language :: Python :: 3.6",
|
|
||||||
"Programming Language :: Python :: 3.7",
|
"Programming Language :: Python :: 3.7",
|
||||||
"Programming Language :: Python :: 3.8",
|
"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",
|
||||||
"Intended Audience :: Developers",
|
"Intended Audience :: Developers",
|
||||||
"Operating System :: OS Independent",
|
"Operating System :: OS Independent",
|
||||||
"Development Status :: 3 - Alpha",
|
"Development Status :: 3 - Alpha",
|
||||||
@ -44,6 +45,7 @@ setup(
|
|||||||
"black",
|
"black",
|
||||||
"pylint",
|
"pylint",
|
||||||
"pre-commit",
|
"pre-commit",
|
||||||
|
"coverage"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
# test_suite='tests',
|
# test_suite='tests',
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
# process, which may cause wedges in the gate later.
|
# process, which may cause wedges in the gate later.
|
||||||
|
|
||||||
coverage
|
coverage
|
||||||
pytest-cov
|
|
||||||
pytest
|
pytest
|
||||||
tox
|
tox
|
||||||
black
|
black
|
||||||
|
flake8
|
22
tests/build_fail/README.md
Normal file
22
tests/build_fail/README.md
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
# 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
|
||||||
|
```
|
3
tests/build_fail/context/Dockerfile
Normal file
3
tests/build_fail/context/Dockerfile
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
FROM busybox
|
||||||
|
RUN this_command_does_not_exist
|
||||||
|
CMD ["sh"]
|
5
tests/build_fail/docker-compose.yml
Normal file
5
tests/build_fail/docker-compose.yml
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
version: "3"
|
||||||
|
services:
|
||||||
|
test:
|
||||||
|
build: ./context
|
||||||
|
image: build-fail-img
|
26
tests/conftest.py
Normal file
26
tests/conftest.py
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
"""conftest.py
|
||||||
|
|
||||||
|
Defines global pytest fixtures available to all tests.
|
||||||
|
"""
|
||||||
|
# pylint: disable=redefined-outer-name
|
||||||
|
from pathlib import Path
|
||||||
|
import os
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def base_path():
|
||||||
|
"""Returns the base path for the project"""
|
||||||
|
return Path(__file__).parent.parent
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def test_path(base_path):
|
||||||
|
"""Returns the path to the tests directory"""
|
||||||
|
return os.path.join(base_path, "tests")
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def podman_compose_path(base_path):
|
||||||
|
"""Returns the path to the podman compose script"""
|
||||||
|
return os.path.join(base_path, "podman_compose.py")
|
@ -1,4 +1,4 @@
|
|||||||
|
|
||||||
```
|
```
|
||||||
podman-compose run --rm sleep /bin/sh -c 'wget -O - http://localhost:8000/hosts'
|
podman-compose run --rm sleep /bin/sh -c 'wget -O - http://web:8000/hosts'
|
||||||
```
|
```
|
||||||
|
@ -9,7 +9,8 @@ services:
|
|||||||
sleep:
|
sleep:
|
||||||
image: busybox
|
image: busybox
|
||||||
command: ["/bin/busybox", "sh", "-c", "sleep 3600"]
|
command: ["/bin/busybox", "sh", "-c", "sleep 3600"]
|
||||||
depends_on: "web"
|
depends_on:
|
||||||
|
- "web"
|
||||||
tmpfs:
|
tmpfs:
|
||||||
- /run
|
- /run
|
||||||
- /tmp
|
- /tmp
|
||||||
|
9
tests/env-file-tests/README.md
Normal file
9
tests/env-file-tests/README.md
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
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
tests/env-file-tests/env-files/project-1.env
Normal file
1
tests/env-file-tests/env-files/project-1.env
Normal file
@ -0,0 +1 @@
|
|||||||
|
ZZVAR1=podman-rocks-123
|
9
tests/env-file-tests/project/container-compose.yaml
Normal file
9
tests/env-file-tests/project/container-compose.yaml
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
services:
|
||||||
|
app:
|
||||||
|
image: busybox
|
||||||
|
command: ["/bin/busybox", "sh", "-c", "env | grep ZZ"]
|
||||||
|
tmpfs:
|
||||||
|
- /run
|
||||||
|
- /tmp
|
||||||
|
environment:
|
||||||
|
ZZVAR1: $ZZVAR1
|
7
tests/extends_w_empty_service/common-services.yml
Normal file
7
tests/extends_w_empty_service/common-services.yml
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
services:
|
||||||
|
webapp_default:
|
||||||
|
|
||||||
|
webapp_special:
|
||||||
|
image: busybox
|
||||||
|
volumes:
|
||||||
|
- "/data"
|
10
tests/extends_w_empty_service/docker-compose.yml
Normal file
10
tests/extends_w_empty_service/docker-compose.yml
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
version: "3"
|
||||||
|
services:
|
||||||
|
web:
|
||||||
|
image: busybox
|
||||||
|
extends:
|
||||||
|
file: common-services.yml
|
||||||
|
service: webapp_default
|
||||||
|
environment:
|
||||||
|
- DEBUG=1
|
||||||
|
cpu_shares: 5
|
7
tests/include/docker-compose.base.yaml
Normal file
7
tests/include/docker-compose.base.yaml
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
version: '3.6'
|
||||||
|
|
||||||
|
services:
|
||||||
|
web:
|
||||||
|
image: busybox
|
||||||
|
command: ["/bin/busybox", "httpd", "-f", "-h", ".", "-p", "8003"]
|
||||||
|
|
4
tests/include/docker-compose.yaml
Normal file
4
tests/include/docker-compose.yaml
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
version: '3.6'
|
||||||
|
|
||||||
|
include:
|
||||||
|
- docker-compose.base.yaml
|
24
tests/profile/docker-compose.yml
Normal file
24
tests/profile/docker-compose.yml
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
version: "3"
|
||||||
|
services:
|
||||||
|
default-service:
|
||||||
|
image: busybox
|
||||||
|
command: ["/bin/busybox", "httpd", "-f", "-h", "/etc/", "-p", "8000"]
|
||||||
|
tmpfs:
|
||||||
|
- /run
|
||||||
|
- /tmp
|
||||||
|
service-1:
|
||||||
|
image: busybox
|
||||||
|
command: ["/bin/busybox", "httpd", "-f", "-h", "/etc/", "-p", "8000"]
|
||||||
|
tmpfs:
|
||||||
|
- /run
|
||||||
|
- /tmp
|
||||||
|
profiles:
|
||||||
|
- profile-1
|
||||||
|
service-2:
|
||||||
|
image: busybox
|
||||||
|
command: ["/bin/busybox", "httpd", "-f", "-h", "/etc/", "-p", "8000"]
|
||||||
|
tmpfs:
|
||||||
|
- /run
|
||||||
|
- /tmp
|
||||||
|
profiles:
|
||||||
|
- profile-2
|
@ -1,7 +1,7 @@
|
|||||||
version: "3"
|
version: "3"
|
||||||
services:
|
services:
|
||||||
redis:
|
redis:
|
||||||
image: redis:alpine
|
image: docker.io/library/redis:alpine
|
||||||
command: ["redis-server", "--appendonly yes", "--notify-keyspace-events", "Ex"]
|
command: ["redis-server", "--appendonly yes", "--notify-keyspace-events", "Ex"]
|
||||||
volumes:
|
volumes:
|
||||||
- ./data/redis:/data:z
|
- ./data/redis:/data:z
|
||||||
@ -12,7 +12,7 @@ services:
|
|||||||
- SECRET_KEY=aabbcc
|
- SECRET_KEY=aabbcc
|
||||||
- ENV_IS_SET
|
- ENV_IS_SET
|
||||||
web:
|
web:
|
||||||
image: busybox
|
image: docker.io/library/busybox
|
||||||
command: ["/bin/busybox", "httpd", "-f", "-h", "/var/www/html", "-p", "8000"]
|
command: ["/bin/busybox", "httpd", "-f", "-h", "/var/www/html", "-p", "8000"]
|
||||||
working_dir: /var/www/html
|
working_dir: /var/www/html
|
||||||
volumes:
|
volumes:
|
||||||
@ -21,19 +21,19 @@ services:
|
|||||||
- /run
|
- /run
|
||||||
- /tmp
|
- /tmp
|
||||||
web1:
|
web1:
|
||||||
image: busybox
|
image: docker.io/library/busybox
|
||||||
command: ["/bin/busybox", "httpd", "-f", "-h", "/var/www/html", "-p", "8001"]
|
command: ["/bin/busybox", "httpd", "-f", "-h", "/var/www/html", "-p", "8001"]
|
||||||
working_dir: /var/www/html
|
working_dir: /var/www/html
|
||||||
volumes:
|
volumes:
|
||||||
- ./data/web:/var/www/html:ro,z
|
- ./data/web:/var/www/html:ro,z
|
||||||
web2:
|
web2:
|
||||||
image: busybox
|
image: docker.io/library/busybox
|
||||||
command: ["/bin/busybox", "httpd", "-f", "-h", "/var/www/html", "-p", "8002"]
|
command: ["/bin/busybox", "httpd", "-f", "-h", "/var/www/html", "-p", "8002"]
|
||||||
working_dir: /var/www/html
|
working_dir: /var/www/html
|
||||||
volumes:
|
volumes:
|
||||||
- ~/Downloads/www:/var/www/html:ro,z
|
- ~/Downloads/www:/var/www/html:ro,z
|
||||||
web3:
|
web3:
|
||||||
image: busybox
|
image: docker.io/library/busybox
|
||||||
command: ["/bin/busybox", "httpd", "-f", "-h", "/var/www/html", "-p", "8003"]
|
command: ["/bin/busybox", "httpd", "-f", "-h", "/var/www/html", "-p", "8003"]
|
||||||
working_dir: /var/www/html
|
working_dir: /var/www/html
|
||||||
volumes:
|
volumes:
|
||||||
|
@ -21,7 +21,8 @@ def test_podman_compose_extends_w_file_subdir():
|
|||||||
main_path = Path(__file__).parent.parent
|
main_path = Path(__file__).parent.parent
|
||||||
|
|
||||||
command_up = [
|
command_up = [
|
||||||
"python3",
|
"coverage",
|
||||||
|
"run",
|
||||||
str(main_path.joinpath("podman_compose.py")),
|
str(main_path.joinpath("podman_compose.py")),
|
||||||
"-f",
|
"-f",
|
||||||
str(main_path.joinpath("tests", "extends_w_file_subdir", "docker-compose.yml")),
|
str(main_path.joinpath("tests", "extends_w_file_subdir", "docker-compose.yml")),
|
||||||
@ -30,12 +31,14 @@ def test_podman_compose_extends_w_file_subdir():
|
|||||||
]
|
]
|
||||||
|
|
||||||
command_check_container = [
|
command_check_container = [
|
||||||
"podman",
|
"coverage",
|
||||||
"container",
|
"run",
|
||||||
|
str(main_path.joinpath("podman_compose.py")),
|
||||||
|
"-f",
|
||||||
|
str(main_path.joinpath("tests", "extends_w_file_subdir", "docker-compose.yml")),
|
||||||
"ps",
|
"ps",
|
||||||
"--all",
|
|
||||||
"--format",
|
"--format",
|
||||||
'"{{.Image}}"',
|
'{{.Image}}',
|
||||||
]
|
]
|
||||||
|
|
||||||
command_down = [
|
command_down = [
|
||||||
@ -46,16 +49,40 @@ def test_podman_compose_extends_w_file_subdir():
|
|||||||
"docker.io/library/busybox",
|
"docker.io/library/busybox",
|
||||||
]
|
]
|
||||||
|
|
||||||
out, err, returncode = capture(command_up)
|
out, _, returncode = capture(command_up)
|
||||||
assert 0 == returncode
|
assert 0 == returncode
|
||||||
# check container was created and exists
|
# check container was created and exists
|
||||||
out, err, returncode = capture(command_check_container)
|
out, err, returncode = capture(command_check_container)
|
||||||
assert 0 == returncode
|
assert 0 == returncode
|
||||||
assert out == b'"localhost/subdir_test:me"\n'
|
assert b'localhost/subdir_test:me\n' == out
|
||||||
out, err, returncode = capture(command_down)
|
out, _, returncode = capture(command_down)
|
||||||
# cleanup test image(tags)
|
# cleanup test image(tags)
|
||||||
assert 0 == returncode
|
assert 0 == returncode
|
||||||
|
print('ok')
|
||||||
# check container did not exists anymore
|
# check container did not exists anymore
|
||||||
out, err, returncode = capture(command_check_container)
|
out, _, returncode = capture(command_check_container)
|
||||||
|
assert 0 == returncode
|
||||||
|
assert b'' == out
|
||||||
|
|
||||||
|
|
||||||
|
def test_podman_compose_extends_w_empty_service():
|
||||||
|
"""
|
||||||
|
Test that podman-compose can execute podman-compose -f <file> up with extended File which
|
||||||
|
includes an empty service. (e.g. if the file is used as placeholder for more complex configurations.)
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
main_path = Path(__file__).parent.parent
|
||||||
|
|
||||||
|
command_up = [
|
||||||
|
"python3",
|
||||||
|
str(main_path.joinpath("podman_compose.py")),
|
||||||
|
"-f",
|
||||||
|
str(
|
||||||
|
main_path.joinpath("tests", "extends_w_empty_service", "docker-compose.yml")
|
||||||
|
),
|
||||||
|
"up",
|
||||||
|
"-d",
|
||||||
|
]
|
||||||
|
|
||||||
|
_, _, returncode = capture(command_up)
|
||||||
assert 0 == returncode
|
assert 0 == returncode
|
||||||
assert out == b""
|
|
||||||
|
78
tests/test_podman_compose_config.py
Normal file
78
tests/test_podman_compose_config.py
Normal file
@ -0,0 +1,78 @@
|
|||||||
|
"""
|
||||||
|
test_podman_compose_config.py
|
||||||
|
|
||||||
|
Tests the podman-compose config command which is used to return defined compose services.
|
||||||
|
"""
|
||||||
|
# pylint: disable=redefined-outer-name
|
||||||
|
import os
|
||||||
|
from test_podman_compose import capture
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def profile_compose_file(test_path):
|
||||||
|
""" "Returns the path to the `profile` compose file used for this test module"""
|
||||||
|
return os.path.join(test_path, "profile", "docker-compose.yml")
|
||||||
|
|
||||||
|
|
||||||
|
def test_config_no_profiles(podman_compose_path, profile_compose_file):
|
||||||
|
"""
|
||||||
|
Tests podman-compose config command without profile enablement.
|
||||||
|
|
||||||
|
:param podman_compose_path: The fixture used to specify the path to the podman compose file.
|
||||||
|
:param profile_compose_file: The fixtued used to specify the path to the "profile" compose used in the test.
|
||||||
|
"""
|
||||||
|
config_cmd = ["coverage", "run", podman_compose_path, "-f", profile_compose_file, "config"]
|
||||||
|
|
||||||
|
out, _, return_code = capture(config_cmd)
|
||||||
|
assert return_code == 0
|
||||||
|
|
||||||
|
string_output = out.decode("utf-8")
|
||||||
|
assert "default-service" in string_output
|
||||||
|
assert "service-1" not in string_output
|
||||||
|
assert "service-2" not in string_output
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
"profiles, expected_services",
|
||||||
|
[
|
||||||
|
(
|
||||||
|
["--profile", "profile-1", "config"],
|
||||||
|
{"default-service": True, "service-1": True, "service-2": False},
|
||||||
|
),
|
||||||
|
(
|
||||||
|
["--profile", "profile-2", "config"],
|
||||||
|
{"default-service": True, "service-1": False, "service-2": True},
|
||||||
|
),
|
||||||
|
(
|
||||||
|
["--profile", "profile-1", "--profile", "profile-2", "config"],
|
||||||
|
{"default-service": True, "service-1": True, "service-2": True},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
def test_config_profiles(
|
||||||
|
podman_compose_path, profile_compose_file, profiles, expected_services
|
||||||
|
):
|
||||||
|
"""
|
||||||
|
Tests podman-compose
|
||||||
|
:param podman_compose_path: The fixture used to specify the path to the podman compose file.
|
||||||
|
:param profile_compose_file: The fixtued used to specify the path to the "profile" compose used in the test.
|
||||||
|
:param profiles: The enabled profiles for the parameterized test.
|
||||||
|
:param expected_services: Dictionary used to model the expected "enabled" services in the profile.
|
||||||
|
Key = service name, Value = True if the service is enabled, otherwise False.
|
||||||
|
"""
|
||||||
|
config_cmd = ["coverage", "run", podman_compose_path, "-f", profile_compose_file]
|
||||||
|
config_cmd.extend(profiles)
|
||||||
|
|
||||||
|
out, _, return_code = capture(config_cmd)
|
||||||
|
assert return_code == 0
|
||||||
|
|
||||||
|
actual_output = out.decode("utf-8")
|
||||||
|
|
||||||
|
assert len(expected_services) == 3
|
||||||
|
|
||||||
|
actual_services = {}
|
||||||
|
for service, _ in expected_services.items():
|
||||||
|
actual_services[service] = service in actual_output
|
||||||
|
|
||||||
|
assert expected_services == actual_services
|
72
tests/test_podman_compose_include.py
Normal file
72
tests/test_podman_compose_include.py
Normal file
@ -0,0 +1,72 @@
|
|||||||
|
from pathlib import Path
|
||||||
|
import subprocess
|
||||||
|
|
||||||
|
|
||||||
|
def capture(command):
|
||||||
|
proc = subprocess.Popen(
|
||||||
|
command,
|
||||||
|
stdout=subprocess.PIPE,
|
||||||
|
stderr=subprocess.PIPE,
|
||||||
|
)
|
||||||
|
out, err = proc.communicate()
|
||||||
|
return out, err, proc.returncode
|
||||||
|
|
||||||
|
|
||||||
|
def test_podman_compose_include():
|
||||||
|
"""
|
||||||
|
Test that podman-compose can execute podman-compose -f <file> up with include
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
main_path = Path(__file__).parent.parent
|
||||||
|
|
||||||
|
command_up = [
|
||||||
|
"coverage",
|
||||||
|
"run",
|
||||||
|
str(main_path.joinpath("podman_compose.py")),
|
||||||
|
"-f",
|
||||||
|
str(main_path.joinpath("tests", "include", "docker-compose.yaml")),
|
||||||
|
"up",
|
||||||
|
"-d",
|
||||||
|
]
|
||||||
|
|
||||||
|
command_check_container = [
|
||||||
|
"podman",
|
||||||
|
"ps",
|
||||||
|
"-a",
|
||||||
|
"--filter",
|
||||||
|
"label=io.podman.compose.project=include",
|
||||||
|
"--format",
|
||||||
|
'"{{.Image}}"',
|
||||||
|
]
|
||||||
|
|
||||||
|
command_container_id = [
|
||||||
|
"podman",
|
||||||
|
"ps",
|
||||||
|
"-a",
|
||||||
|
"--filter",
|
||||||
|
"label=io.podman.compose.project=include",
|
||||||
|
"--format",
|
||||||
|
'"{{.ID}}"',
|
||||||
|
]
|
||||||
|
|
||||||
|
command_down = ["podman", "rm", "--force", "CONTAINER_ID"]
|
||||||
|
|
||||||
|
out, _, returncode = capture(command_up)
|
||||||
|
assert 0 == returncode
|
||||||
|
out, _, returncode = capture(command_check_container)
|
||||||
|
assert 0 == returncode
|
||||||
|
assert out == b'"docker.io/library/busybox:latest"\n'
|
||||||
|
# Get container ID to remove it
|
||||||
|
out, _, returncode = capture(command_container_id)
|
||||||
|
assert 0 == returncode
|
||||||
|
assert out != b""
|
||||||
|
container_id = out.decode().strip().replace('"', "")
|
||||||
|
command_down[3] = container_id
|
||||||
|
out, _, returncode = capture(command_down)
|
||||||
|
# cleanup test image(tags)
|
||||||
|
assert 0 == returncode
|
||||||
|
assert out != b""
|
||||||
|
# check container did not exists anymore
|
||||||
|
out, _, returncode = capture(command_check_container)
|
||||||
|
assert 0 == returncode
|
||||||
|
assert out == b""
|
180
tests/test_podman_compose_tests.py
Normal file
180
tests/test_podman_compose_tests.py
Normal file
@ -0,0 +1,180 @@
|
|||||||
|
"""
|
||||||
|
test_podman_compose_up_down.py
|
||||||
|
|
||||||
|
Tests the podman compose up and down commands used to create and remove services.
|
||||||
|
"""
|
||||||
|
# pylint: disable=redefined-outer-name
|
||||||
|
import os
|
||||||
|
import time
|
||||||
|
|
||||||
|
from test_podman_compose import capture
|
||||||
|
|
||||||
|
|
||||||
|
def test_exit_from(podman_compose_path, test_path):
|
||||||
|
up_cmd = [
|
||||||
|
"coverage",
|
||||||
|
"run",
|
||||||
|
podman_compose_path,
|
||||||
|
"-f",
|
||||||
|
os.path.join(test_path, "exit-from", "docker-compose.yaml"),
|
||||||
|
"up"
|
||||||
|
]
|
||||||
|
|
||||||
|
out, _, return_code = capture(up_cmd + ["--exit-code-from", "sh1"])
|
||||||
|
assert return_code == 1
|
||||||
|
|
||||||
|
out, _, return_code = capture(up_cmd + ["--exit-code-from", "sh2"])
|
||||||
|
assert return_code == 2
|
||||||
|
|
||||||
|
|
||||||
|
def test_run(podman_compose_path, test_path):
|
||||||
|
"""
|
||||||
|
This will test depends_on as well
|
||||||
|
"""
|
||||||
|
run_cmd = [
|
||||||
|
"coverage",
|
||||||
|
"run",
|
||||||
|
podman_compose_path,
|
||||||
|
"-f",
|
||||||
|
os.path.join(test_path, "deps", "docker-compose.yaml"),
|
||||||
|
"run",
|
||||||
|
"--rm",
|
||||||
|
"sleep",
|
||||||
|
"/bin/sh",
|
||||||
|
"-c",
|
||||||
|
"wget -q -O - http://web:8000/hosts"
|
||||||
|
]
|
||||||
|
|
||||||
|
out, _, return_code = capture(run_cmd)
|
||||||
|
assert b'127.0.0.1\tlocalhost' in out
|
||||||
|
|
||||||
|
# Run it again to make sure we can run it twice. I saw an issue where a second run, with the container left up,
|
||||||
|
# would fail
|
||||||
|
run_cmd = [
|
||||||
|
"coverage",
|
||||||
|
"run",
|
||||||
|
podman_compose_path,
|
||||||
|
"-f",
|
||||||
|
os.path.join(test_path, "deps", "docker-compose.yaml"),
|
||||||
|
"run",
|
||||||
|
"--rm",
|
||||||
|
"sleep",
|
||||||
|
"/bin/sh",
|
||||||
|
"-c",
|
||||||
|
"wget -q -O - http://web:8000/hosts"
|
||||||
|
]
|
||||||
|
|
||||||
|
out, _, return_code = capture(run_cmd)
|
||||||
|
assert b'127.0.0.1\tlocalhost' in out
|
||||||
|
assert return_code == 0
|
||||||
|
|
||||||
|
# This leaves a container running. Not sure it's intended, but it matches docker-compose
|
||||||
|
down_cmd = [
|
||||||
|
"coverage",
|
||||||
|
"run",
|
||||||
|
podman_compose_path,
|
||||||
|
"-f",
|
||||||
|
os.path.join(test_path, "deps", "docker-compose.yaml"),
|
||||||
|
"down",
|
||||||
|
]
|
||||||
|
|
||||||
|
out, _, return_code = capture(run_cmd)
|
||||||
|
assert return_code == 0
|
||||||
|
|
||||||
|
|
||||||
|
def test_up_with_ports(podman_compose_path, test_path):
|
||||||
|
|
||||||
|
|
||||||
|
up_cmd = [
|
||||||
|
"coverage",
|
||||||
|
"run",
|
||||||
|
podman_compose_path,
|
||||||
|
"-f",
|
||||||
|
os.path.join(test_path, "ports", "docker-compose.yml"),
|
||||||
|
"up",
|
||||||
|
"-d",
|
||||||
|
"--force-recreate"
|
||||||
|
]
|
||||||
|
|
||||||
|
down_cmd = [
|
||||||
|
"coverage",
|
||||||
|
"run",
|
||||||
|
podman_compose_path,
|
||||||
|
"-f",
|
||||||
|
os.path.join(test_path, "ports", "docker-compose.yml"),
|
||||||
|
"down",
|
||||||
|
"--volumes"
|
||||||
|
]
|
||||||
|
|
||||||
|
try:
|
||||||
|
out, _, return_code = capture(up_cmd)
|
||||||
|
assert return_code == 0
|
||||||
|
|
||||||
|
|
||||||
|
finally:
|
||||||
|
out, _, return_code = capture(down_cmd)
|
||||||
|
assert return_code == 0
|
||||||
|
|
||||||
|
|
||||||
|
def test_down_with_vols(podman_compose_path, test_path):
|
||||||
|
|
||||||
|
up_cmd = [
|
||||||
|
"coverage",
|
||||||
|
"run",
|
||||||
|
podman_compose_path,
|
||||||
|
"-f",
|
||||||
|
os.path.join(test_path, "vol", "docker-compose.yaml"),
|
||||||
|
"up",
|
||||||
|
"-d"
|
||||||
|
]
|
||||||
|
|
||||||
|
down_cmd = [
|
||||||
|
"coverage",
|
||||||
|
"run",
|
||||||
|
podman_compose_path,
|
||||||
|
"-f",
|
||||||
|
os.path.join(test_path, "vol", "docker-compose.yaml"),
|
||||||
|
"down",
|
||||||
|
"--volumes"
|
||||||
|
]
|
||||||
|
|
||||||
|
try:
|
||||||
|
out, _, return_code = capture(["podman", "volume", "create", "my-app-data"])
|
||||||
|
assert return_code == 0
|
||||||
|
out, _, return_code = capture(["podman", "volume", "create", "actual-name-of-volume"])
|
||||||
|
assert return_code == 0
|
||||||
|
|
||||||
|
out, _, return_code = capture(up_cmd)
|
||||||
|
assert return_code == 0
|
||||||
|
|
||||||
|
capture(["podman", "inspect", "volume", ""])
|
||||||
|
|
||||||
|
finally:
|
||||||
|
out, _, return_code = capture(down_cmd)
|
||||||
|
capture(["podman", "volume", "rm", "my-app-data"])
|
||||||
|
capture(["podman", "volume", "rm", "actual-name-of-volume"])
|
||||||
|
assert return_code == 0
|
||||||
|
|
||||||
|
|
||||||
|
def test_down_with_orphans(podman_compose_path, test_path):
|
||||||
|
|
||||||
|
container_id, _ , return_code = capture(["podman", "run", "--rm", "-d", "busybox", "/bin/busybox", "httpd", "-f", "-h", "/etc/", "-p", "8000"])
|
||||||
|
|
||||||
|
down_cmd = [
|
||||||
|
"coverage",
|
||||||
|
"run",
|
||||||
|
podman_compose_path,
|
||||||
|
"-f",
|
||||||
|
os.path.join(test_path, "ports", "docker-compose.yml"),
|
||||||
|
"down",
|
||||||
|
"--volumes",
|
||||||
|
"--remove-orphans"
|
||||||
|
]
|
||||||
|
|
||||||
|
out, _, return_code = capture(down_cmd)
|
||||||
|
assert return_code == 0
|
||||||
|
|
||||||
|
_, _, exists = capture(["podman", "container", "exists", container_id.decode("utf-8")])
|
||||||
|
|
||||||
|
assert exists == 1
|
||||||
|
|
91
tests/test_podman_compose_up_down.py
Normal file
91
tests/test_podman_compose_up_down.py
Normal file
@ -0,0 +1,91 @@
|
|||||||
|
"""
|
||||||
|
test_podman_compose_up_down.py
|
||||||
|
|
||||||
|
Tests the podman compose up and down commands used to create and remove services.
|
||||||
|
"""
|
||||||
|
# pylint: disable=redefined-outer-name
|
||||||
|
import os
|
||||||
|
from test_podman_compose import capture
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def profile_compose_file(test_path):
|
||||||
|
""" "Returns the path to the `profile` compose file used for this test module"""
|
||||||
|
return os.path.join(test_path, "profile", "docker-compose.yml")
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture(autouse=True)
|
||||||
|
def teardown(podman_compose_path, profile_compose_file):
|
||||||
|
"""
|
||||||
|
Ensures that the services within the "profile compose file" are removed between each test case.
|
||||||
|
|
||||||
|
:param podman_compose_path: The path to the podman compose script.
|
||||||
|
:param profile_compose_file: The path to the compose file used for this test module.
|
||||||
|
"""
|
||||||
|
# run the test case
|
||||||
|
yield
|
||||||
|
|
||||||
|
down_cmd = [
|
||||||
|
"coverage",
|
||||||
|
"run",
|
||||||
|
podman_compose_path,
|
||||||
|
"--profile",
|
||||||
|
"profile-1",
|
||||||
|
"--profile",
|
||||||
|
"profile-2",
|
||||||
|
"-f",
|
||||||
|
profile_compose_file,
|
||||||
|
"down",
|
||||||
|
]
|
||||||
|
capture(down_cmd)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
"profiles, expected_services",
|
||||||
|
[
|
||||||
|
(
|
||||||
|
["--profile", "profile-1", "up", "-d"],
|
||||||
|
{"default-service": True, "service-1": True, "service-2": False},
|
||||||
|
),
|
||||||
|
(
|
||||||
|
["--profile", "profile-2", "up", "-d"],
|
||||||
|
{"default-service": True, "service-1": False, "service-2": True},
|
||||||
|
),
|
||||||
|
(
|
||||||
|
["--profile", "profile-1", "--profile", "profile-2", "up", "-d"],
|
||||||
|
{"default-service": True, "service-1": True, "service-2": True},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
def test_up(podman_compose_path, profile_compose_file, profiles, expected_services):
|
||||||
|
up_cmd = [
|
||||||
|
"coverage",
|
||||||
|
"run",
|
||||||
|
podman_compose_path,
|
||||||
|
"-f",
|
||||||
|
profile_compose_file,
|
||||||
|
]
|
||||||
|
up_cmd.extend(profiles)
|
||||||
|
|
||||||
|
out, _, return_code = capture(up_cmd)
|
||||||
|
assert return_code == 0
|
||||||
|
|
||||||
|
check_cmd = [
|
||||||
|
"podman",
|
||||||
|
"container",
|
||||||
|
"ps",
|
||||||
|
"--format",
|
||||||
|
'"{{.Names}}"',
|
||||||
|
]
|
||||||
|
out, _, return_code = capture(check_cmd)
|
||||||
|
assert return_code == 0
|
||||||
|
|
||||||
|
assert len(expected_services) == 3
|
||||||
|
actual_output = out.decode("utf-8")
|
||||||
|
|
||||||
|
actual_services = {}
|
||||||
|
for service, _ in expected_services.items():
|
||||||
|
actual_services[service] = service in actual_output
|
||||||
|
|
||||||
|
assert expected_services == actual_services
|
15
tests/uidmaps/docker-compose.yml
Normal file
15
tests/uidmaps/docker-compose.yml
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
version: "3.7"
|
||||||
|
services:
|
||||||
|
touch:
|
||||||
|
image: busybox
|
||||||
|
command: 'touch /mnt/test'
|
||||||
|
volumes:
|
||||||
|
- ./:/mnt
|
||||||
|
user: 999:999
|
||||||
|
x-podman:
|
||||||
|
uidmaps:
|
||||||
|
- "0:1:1"
|
||||||
|
- "999:0:1"
|
||||||
|
gidmaps:
|
||||||
|
- "0:1:1"
|
||||||
|
- "999:0:1"
|
7
tests/volumes_merge/docker-compose.override.yaml
Normal file
7
tests/volumes_merge/docker-compose.override.yaml
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
version: "3"
|
||||||
|
services:
|
||||||
|
web:
|
||||||
|
volumes:
|
||||||
|
- ./override.txt:/var/www/html/index.html:ro,z
|
||||||
|
- ./override.txt:/var/www/html/index2.html:z
|
||||||
|
- ./override.txt:/var/www/html/index3.html
|
11
tests/volumes_merge/docker-compose.yaml
Normal file
11
tests/volumes_merge/docker-compose.yaml
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
version: "3"
|
||||||
|
services:
|
||||||
|
web:
|
||||||
|
image: busybox
|
||||||
|
command: ["/bin/busybox", "httpd", "-f", "-h", "/var/www/html", "-p", "8080"]
|
||||||
|
ports:
|
||||||
|
- 8080:8080
|
||||||
|
volumes:
|
||||||
|
- ./index.txt:/var/www/html/index.html:ro,z
|
||||||
|
- ./index.txt:/var/www/html/index2.html
|
||||||
|
- ./index.txt:/var/www/html/index3.html:ro
|
1
tests/volumes_merge/index.txt
Normal file
1
tests/volumes_merge/index.txt
Normal file
@ -0,0 +1 @@
|
|||||||
|
The file from docker-compose.yaml
|
1
tests/volumes_merge/override.txt
Normal file
1
tests/volumes_merge/override.txt
Normal file
@ -0,0 +1 @@
|
|||||||
|
The file from docker-compose.override.yaml
|
Reference in New Issue
Block a user