diff --git a/podman_compose.py b/podman_compose.py index 3baeffb..4fdf7b9 100755 --- a/podman_compose.py +++ b/podman_compose.py @@ -1559,7 +1559,15 @@ def _parse_compose_file(self): } ) compose = {} - for filename in files: + # Iterate over files primitively to allow appending to files in-loop + files_iter = iter(files) + + while True: + try: + filename = next(files_iter) + except StopIteration: + break + with open(filename, "r", encoding="utf-8") as f: content = yaml.safe_load(f) # log(filename, json.dumps(content, indent = 2)) @@ -1573,6 +1581,15 @@ def _parse_compose_file(self): # log(filename, json.dumps(content, indent = 2)) content = rec_subs(content, self.environ) rec_merge(compose, content) + # If `include` is used, append included files to files + if include := compose.get("include", None): + files.append(*include) + # As compose obj is updated and tested with every loop, not deleting `include` + # from it, results in it being tested again and again, original values for + # `include` be appended to `files`, and, included files be processed for ever. + # Solution is to remove 'include' key from compose obj. This doesn't break + # having `include` present and correctly processed in included files + del compose["include"] resolved_services = self._resolve_profiles( compose.get("services", {}), set(args.profile) ) diff --git a/tests/include/docker-compose.base.yaml b/tests/include/docker-compose.base.yaml new file mode 100644 index 0000000..e356a14 --- /dev/null +++ b/tests/include/docker-compose.base.yaml @@ -0,0 +1,7 @@ +version: '3.6' + +services: + web: + image: busybox + command: ["/bin/busybox", "httpd", "-f", "-h", ".", "-p", "8003"] + diff --git a/tests/include/docker-compose.yaml b/tests/include/docker-compose.yaml new file mode 100644 index 0000000..ada2153 --- /dev/null +++ b/tests/include/docker-compose.yaml @@ -0,0 +1,4 @@ +version: '3.6' + +include: + - docker-compose.base.yaml diff --git a/tests/test_podman_compose_include.py b/tests/test_podman_compose_include.py new file mode 100644 index 0000000..c9867f5 --- /dev/null +++ b/tests/test_podman_compose_include.py @@ -0,0 +1,71 @@ +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 up with include + :return: + """ + main_path = Path(__file__).parent.parent + + command_up = [ + "python3", + 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""