Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add missing docker compose apis #486

Open
wants to merge 12 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 23 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -129,3 +129,26 @@ dmypy.json
.pyre/
docs/generated_sources
docs/site

### Intellij+all ###
# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider
# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839

# User-specific stuff
.idea/**/workspace.xml
.idea/**/tasks.xml
.idea/**/usage.statistics.xml
.idea/**/dictionaries
.idea/**/shelf

# Generated files
.idea/**/contentModel.xml

# Sensitive or high-churn files
.idea/**/dataSources/
.idea/**/dataSources.ids
.idea/**/dataSources.local.xml
.idea/**/sqlDataSources.xml
.idea/**/dynamic.xml
.idea/**/uiDesigner.xml
.idea/**/dbnavigator.xml
3 changes: 2 additions & 1 deletion python_on_whales/components/compose/cli_wrapper.py
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,8 @@ def config(self, return_json: bool = False) -> Union[ComposeConfig, Dict[str, An
if return_json:
return json.loads(result)
else:
return ComposeConfig(**json.loads(result))
raw_compose_config = json.loads(result)
return ComposeConfig(**raw_compose_config)

@overload
def create(
Expand Down
59 changes: 50 additions & 9 deletions python_on_whales/components/compose/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,20 @@ class ComposeServiceBuild(BaseModel):
context: Optional[Path] = None
dockerfile: Optional[Path] = None
args: Optional[Dict[str, Any]] = None
cache_from: Optional[List[str]] = None
labels: Optional[Dict[str, Any]] = None
network: Optional[str] = None
target: Optional[str] = None


class ComposeServiceHealthcheck(BaseModel):
disable: Optional[bool] = None
interval: Optional[str] = None
start_period: Optional[str] = None
start_interval: Optional[str] = None
test: Optional[List[str]] = None
timeout: Optional[str] = None
retries: Optional[int] = None


class ComposeServicePort(BaseModel):
Expand All @@ -49,6 +62,16 @@ class ComposeServicePort(BaseModel):
target: Optional[int] = None


class ComposeServiceULimitsNoFile(BaseModel):
soft: Optional[int] = None
hard: Optional[int] = None


class ComposeServiceULimits(BaseModel):
nproc: Optional[int] = None
nofile: Optional[ComposeServiceULimitsNoFile] = None


class ComposeServiceVolume(BaseModel):
bind: Optional[dict] = None
source: Optional[str] = None
Expand All @@ -57,27 +80,45 @@ class ComposeServiceVolume(BaseModel):


class ComposeConfigService(BaseModel):
deploy: Optional[ServiceDeployConfig] = None
blkio_config: Optional[Any] = None
cpu_count: Optional[float] = None
cpu_percent: Optional[float] = None
cpu_shares: Optional[int] = None
cpuset: Optional[str] = None
build: Optional[ComposeServiceBuild] = None
cap_add: Annotated[Optional[List[str]], Field(default_factory=list)]
cap_drop: Annotated[Optional[List[str]], Field(default_factory=list)]
cgroup_parent: Optional[str] = None
command: Optional[List[str]] = None
configs: Any = None
cgroup_parent: Optional[str] = None
container_name: Optional[str] = None
cpu_count: Optional[float] = None
cpu_percent: Optional[float] = None
cpu_shares: Optional[int] = None
cpuset: Optional[str] = None
depends_on: Annotated[Dict[str, DependencyCondition], Field(default_factory=dict)]
deploy: Optional[ServiceDeployConfig] = None
devices: List[str] = None
device_cgroup_rules: Annotated[List[str], Field(default_factory=list)]
devices: Any = None
environment: Optional[Dict[str, Optional[str]]] = None
dns: Optional[List[str]] = None
dns_search: Optional[List[str]] = None
entrypoint: Optional[List[str]] = None
environment: Optional[Dict[str, Optional[str]]] = None
expose: Optional[List[str]] = None
external_links: Optional[List[str]] = None
extra_hosts: Optional[List[str]] = None
healthcheck: Optional[ComposeServiceHealthcheck] = None
image: Optional[str] = None
init: Optional[bool] = False
isolation: str = Field(default="default")
labels: Annotated[Optional[Dict[str, str]], Field(default_factory=dict)]
network_mode: Optional[str] = None
networks: Optional[Any] = None
pid: Optional[str] = None
ports: Optional[List[ComposeServicePort]] = None
profiles: Optional[List[str]] = None
restart: str = "no"
secrets: Optional[List[Dict[str, Any]]] = None
stop_grace_period: str = Field(default="10s")
stop_signal: str = Field(default="SIGTERM")
tmpfs: Optional[List[str]] = None
ulimits: Optional[ComposeServiceULimits] = None
userns_mode: Optional[str] = None
volumes: Optional[List[ComposeServiceVolume]] = None


Expand Down
66 changes: 66 additions & 0 deletions tests/python_on_whales/components/complex-compose.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
version: "3.7"

services:
my_service:
build:
context: my_service_build
image: some_random_image
command: ping -c 2 www.google.com
ports:
- "5000:5000"
volumes:
- /tmp:/tmp
- dodo:/dodo
environment:
- DATADOG_HOST=something
dns: 8.8.8.8
dns_search:
- dc1.example.com
- dc2.example.com
expose:
- "5000"
- 8000
external_links:
- "redis"
extra_hosts:
- "somehost:162.242.195.82"
- "otherhost:50.31.209.229"
healthcheck:
test: [ "CMD", "curl", "-f", "http://localhost" ]
interval: 1m30s
timeout: 10s
retries: 3
start_period: 40s
init: true
isolation: "process"
network_mode: "host"
pid: "host"
secrets:
- my_secret
tmpfs: /run
ulimits:
nproc: 65535
nofile:
soft: 20000
hard: 40000
userns_mode: "host"
deploy:
placement:
constraints:
- node.labels.hello-world == yes
resources:
reservations:
cpus: '1'
memory: 20M
limits:
cpus: '2'
memory: 40M
replicas: 4

secrets:
my_secret:
external: true


volumes:
dodo: {}
32 changes: 0 additions & 32 deletions tests/python_on_whales/components/complexe-compose.yml

This file was deleted.

7 changes: 6 additions & 1 deletion tests/python_on_whales/components/test-build-args.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
version: "3.7"
version: "3.9"

services:
my_service:
Expand All @@ -8,9 +8,14 @@ services:
args:
python_version: "3.78"
python_version_1: "3.78"
cache_from:
- alpine:latest
- corp/web_app:3.14
labels:
com.example.description: "Accounting webapp"
com.example.department: "Finance"
network: "host"
target: "prod"
image: "some_random_image"
command: ping -c 7 www.google.com
ports:
Expand Down
54 changes: 52 additions & 2 deletions tests/python_on_whales/components/test_compose.py
Original file line number Diff line number Diff line change
Expand Up @@ -514,10 +514,10 @@ def test_entrypoint_loaded_in_config():
assert docker.compose.config().services["dodo"].entrypoint == ["/bin/sh"]


def test_config_complexe_compose():
def test_config_complex_compose():
"""Checking that the pydantic model does its job"""
compose_file = (
PROJECT_ROOT / "tests/python_on_whales/components/complexe-compose.yml"
PROJECT_ROOT / "tests/python_on_whales/components/complex-compose.yml"
)
docker = DockerClient(compose_files=[compose_file], compose_compatibility=True)
config = docker.compose.config()
Expand All @@ -540,6 +540,49 @@ def test_config_complexe_compose():
assert config.services["my_service"].volumes[1].target == "/dodo"

assert config.services["my_service"].environment == {"DATADOG_HOST": "something"}

assert config.services["my_service"].dns == ["8.8.8.8"]
assert config.services["my_service"].dns_search == [
"dc1.example.com",
"dc2.example.com",
]

assert config.services["my_service"].expose == ["5000", "8000"]

assert config.services["my_service"].external_links == ["redis"]

assert config.services["my_service"].extra_hosts == [
"otherhost:50.31.209.229",
"somehost:162.242.195.82",
]

assert config.services["my_service"].healthcheck.test == [
"CMD",
"curl",
"-f",
"http://localhost",
]
assert config.services["my_service"].healthcheck.interval == "1m30s"
assert config.services["my_service"].healthcheck.timeout == "10s"
assert config.services["my_service"].healthcheck.retries == 3
assert config.services["my_service"].healthcheck.start_period == "40s"

assert config.services["my_service"].init
assert config.services["my_service"].isolation == "process"
assert config.services["my_service"].network_mode == "host"
assert config.services["my_service"].pid == "host"
assert config.services["my_service"].restart == "no"
assert config.services["my_service"].secrets == [{"source": "my_secret"}]
assert config.services["my_service"].stop_grace_period == "10s"
assert config.services["my_service"].stop_signal == "SIGTERM"
assert config.services["my_service"].tmpfs == ["/run"]

assert config.services["my_service"].ulimits.nproc == 65535
assert config.services["my_service"].ulimits.nofile.soft == 20000
assert config.services["my_service"].ulimits.nofile.hard == 40000

assert config.services["my_service"].userns_mode == "host"

assert config.services["my_service"].deploy.placement.constraints == [
"node.labels.hello-world == yes"
]
Expand Down Expand Up @@ -1009,10 +1052,17 @@ def test_build_args():
"python_version": "3.78",
"python_version_1": "3.78",
}
assert config.services["my_service"].build.cache_from == [
"alpine:latest",
"corp/web_app:3.14",
]
assert config.services["my_service"].build.labels == {
"com.example.description": "Accounting webapp",
"com.example.department": "Finance",
}
assert config.services["my_service"].build.network == "host"
assert config.services["my_service"].build.target == "prod"

assert config.services["my_service"].image == "some_random_image"
assert config.services["my_service"].command == [
"ping",
Expand Down
Loading