Skip to content

Commit

Permalink
Add envs parameter to compose up/config
Browse files Browse the repository at this point in the history
  • Loading branch information
lugoues committed Nov 19, 2022
1 parent 7dc3102 commit f71c924
Show file tree
Hide file tree
Showing 5 changed files with 144 additions and 6 deletions.
5 changes: 5 additions & 0 deletions python_on_whales/client_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ class ClientConfig:
compose_project_name: Optional[str] = None
compose_project_directory: Optional[ValidPath] = None
compose_compatibility: Optional[bool] = None
compose_envs: Optional[Dict[str, str]] = None
client_call: List[str] = field(default_factory=lambda: ["docker"])
_client_call_with_path: Optional[List[Union[Path, str]]] = None

Expand Down Expand Up @@ -165,6 +166,10 @@ def docker_cmd(self) -> Command:
def docker_compose_cmd(self) -> Command:
return self.client_config.docker_compose_cmd

@property
def docker_compose_envs(self) -> Dict[str, str]:
return self.client_config.compose_envs or {}


class ReloadableObject(DockerCLICaller):
def __init__(
Expand Down
21 changes: 16 additions & 5 deletions python_on_whales/components/compose/cli_wrapper.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,9 @@ def build(
full_cmd += services
run(full_cmd, capture_stdout=False)

def config(self, return_json: bool = False) -> Union[ComposeConfig, Dict[str, Any]]:
def config(
self, return_json: bool = False, envs: Dict[str, str] = {}
) -> Union[ComposeConfig, Dict[str, Any]]:
"""Returns the configuration of the compose stack for further inspection.
For example
Expand All @@ -76,12 +78,15 @@ def config(self, return_json: bool = False) -> Union[ComposeConfig, Dict[str, An
lists and dicts corresponding to the json response, unmodified.
It may be useful if you just want to print the config or want to access
a field that was not in the `ComposeConfig` class.
envs: A dictionary of environment variables to set for the compose process.
# Returns
A `ComposeConfig` object if `return_json` is `False`, and a `dict` otherwise.
"""
full_cmd = self.docker_compose_cmd + ["config", "--format", "json"]
result = run(full_cmd, capture_stdout=True)
run_envs = {**self.docker_compose_envs, **envs}

result = run(full_cmd, capture_stdout=True, env=run_envs)
if return_json:
return json.loads(result)
else:
Expand Down Expand Up @@ -485,7 +490,7 @@ def run(
command: List[str] = [],
detach: bool = False,
# entrypoint: Optional[List[str]] = None,
# envs: Dict[str, str] = {},
envs: Dict[str, str] = {},
# labels: Dict[str, str] = {},
name: Optional[str] = None,
tty: bool = True,
Expand Down Expand Up @@ -567,10 +572,12 @@ def run(
full_cmd.append(service)
full_cmd += command

run_envs = {**self.docker_compose_envs, **envs}

if stream:
return stream_stdout_and_stderr(full_cmd)
else:
result = run(full_cmd, tty=tty)
result = run(full_cmd, tty=tty, env=run_envs)
if detach:
Container = python_on_whales.components.container.cli_wrapper.Container
return Container(self.client_config, result, is_immutable_id=True)
Expand Down Expand Up @@ -651,6 +658,7 @@ def up(
log_prefix: bool = True,
start: bool = True,
quiet: bool = False,
envs: Dict[str, str] = {},
):
"""Start the containers.
Expand Down Expand Up @@ -681,6 +689,7 @@ def up(
start: Start the service after creating them.
quiet: By default, some progress bars and logs are sent to stderr and stdout.
Set `quiet=True` to avoid having any output.
envs: A dictionary of environment variables to set for the compose process.
# Returns
`None` at the moment. The plan is to be able to capture and stream the logs later.
Expand All @@ -700,13 +709,15 @@ def up(
full_cmd.add_flag("--no-log-prefix", not log_prefix)
full_cmd.add_flag("--no-start", not start)

run_envs = {**self.docker_compose_envs, **envs}

if services == []:
return
elif services is not None:
services = to_list(services)
full_cmd += services
# important information is written to both stdout AND stderr.
run(full_cmd, capture_stdout=quiet, capture_stderr=quiet)
run(full_cmd, capture_stdout=quiet, capture_stderr=quiet, env=run_envs)

def version(self) -> str:
"""Returns the version of docker compose as a `str`."""
Expand Down
5 changes: 4 additions & 1 deletion python_on_whales/docker_client.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import base64
import warnings
from typing import List, Optional
from typing import Dict, List, Optional

from python_on_whales.client_config import ClientConfig, DockerCLICaller
from python_on_whales.components.buildx.cli_wrapper import BuildxCLI
Expand Down Expand Up @@ -56,6 +56,7 @@ class DockerClient(DockerCLICaller):
the [documentation for profiles](https://docs.docker.com/compose/profiles/).
compose_env_file: .env file containing the environments variables to inject
into the compose project. By default, it uses `./.env`.
compose_envs: Environment variables to inject into the docker compose process.
compose_project_name: The name of the compose project. It will be prefixed to
networks, volumes and containers created by compose.
compose_project_directory: Use an alternate working directory. By default, it
Expand Down Expand Up @@ -100,6 +101,7 @@ def __init__(
compose_project_name: Optional[str] = None,
compose_project_directory: Optional[ValidPath] = None,
compose_compatibility: Optional[bool] = None,
compose_envs: Optional[Dict[str, str]] = None,
client_binary: str = "docker",
client_call: List[str] = ["docker"],
):
Expand Down Expand Up @@ -128,6 +130,7 @@ def __init__(
compose_project_name=compose_project_name,
compose_project_directory=compose_project_directory,
compose_compatibility=compose_compatibility,
compose_envs=compose_envs,
client_call=client_call,
)
super().__init__(client_config)
Expand Down
2 changes: 2 additions & 0 deletions tests/python_on_whales/components/dummy_compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ services:
busybox:
image: busybox:latest
command: sleep infinity
environment:
- SOME_VARIABLE=${SOME_VARIABLE_TO_INSERT:-nothing}
busybox-2-electric-boogaloo:
image: busybox:latest
depends_on:
Expand Down
117 changes: 117 additions & 0 deletions tests/python_on_whales/components/test_compose.py
Original file line number Diff line number Diff line change
Expand Up @@ -218,6 +218,123 @@ def test_docker_compose_up_down_some_services():
docker.compose.down(timeout=1)


def test_docker_compose_config_process_envs(tmp_path: Path):
docker = DockerClient(
compose_files=[
PROJECT_ROOT
/ "tests/python_on_whales/components/dummy_compose_ends_quickly.yml"
],
compose_compatibility=True,
)
envs = {"SOME_VARIABLE_TO_INSERT": "test-value"}

output = docker.compose.config(envs=envs)

assert output.services["alpine"].environment["SOME_VARIABLE"] == "test-value"


def test_docker_compose_config_process_envs_override(tmp_path: Path):
docker = DockerClient(
compose_files=[
PROJECT_ROOT
/ "tests/python_on_whales/components/dummy_compose_ends_quickly.yml"
],
compose_compatibility=True,
compose_envs={"SOME_VARIABLE_TO_INSERT": "test-client"},
)

output = docker.compose.config(envs={"SOME_VARIABLE_TO_INSERT": "test-call"})
assert output.services["alpine"].environment["SOME_VARIABLE"] == "test-call"


def test_docker_compose_up_process_envs(tmp_path: Path):
docker = DockerClient(
compose_files=[
PROJECT_ROOT
/ "tests/python_on_whales/components/dummy_compose_ends_quickly.yml"
],
compose_compatibility=True,
)

docker.compose.up(envs={"SOME_VARIABLE_TO_INSERT": "test-value"})

assert not docker.container.inspect("alpine").state.running

result = docker.compose.execute(
"alpine", ["bash", "-c", "echo $SOME_VARIABLE"], tty=False
)

assert result == "test-value"


def test_docker_compose_up_process_envs_override(tmp_path: Path):
docker = DockerClient(
compose_files=[
PROJECT_ROOT
/ "tests/python_on_whales/components/dummy_compose_ends_quickly.yml"
],
compose_compatibility=True,
compose_envs={"SOME_VARIABLE_TO_INSERT": "test-client"},
)

docker.compose.up(envs={"SOME_VARIABLE_TO_INSERT": "test-call"})

assert not docker.container.inspect("alpine").state.running

result = docker.compose.execute(
"alpine", ["bash", "-c", "echo $SOME_VARIABLE"], tty=False
)

assert result == "test-call"


def test_docker_compose_run_process_envs(tmp_path: Path):
docker = DockerClient(
compose_files=[
PROJECT_ROOT
/ "tests/python_on_whales/components/dummy_compose_ends_quickly.yml"
],
compose_compatibility=True,
)

docker.compose.up(envs={"SOME_VARIABLE_TO_INSERT": "test-up"})

assert not docker.container.inspect("alpine").state.running

result = docker.compose.run(
"busybox",
["echo", "${SOME_VARIABLE}"],
remove=True,
tty=False,
envs={"SOME_VARIABLE_TO_INSERT": "test-exec"},
)
assert result == "test-exec"


def test_docker_compose_run_process_envs_override(tmp_path: Path):
docker = DockerClient(
compose_files=[
PROJECT_ROOT
/ "tests/python_on_whales/components/dummy_compose_ends_quickly.yml"
],
compose_compatibility=True,
compose_envs={"SOME_VARIABLE_TO_INSERT": "test-client"},
)

docker.compose.up()

assert not docker.container.inspect("alpine").state.running

result = docker.compose.run(
"busybox",
["echo", "${SOME_VARIABLE}"],
remove=True,
tty=False,
envs={"SOME_VARIABLE_TO_INSERT": "test-exec"},
)
assert result == "test-exec"


def test_docker_compose_ps():
docker.compose.up(["my_service", "busybox"], detach=True)
containers = docker.compose.ps()
Expand Down

0 comments on commit f71c924

Please sign in to comment.