dockercomposefile 0.1.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- dockercomposefile/__init__.py +13 -0
- dockercomposefile/builder.py +58 -0
- dockercomposefile/exporter.py +83 -0
- dockercomposefile/models/__init__.py +97 -0
- dockercomposefile/models/build.py +123 -0
- dockercomposefile/models/common.py +412 -0
- dockercomposefile/models/compose.py +58 -0
- dockercomposefile/models/config.py +45 -0
- dockercomposefile/models/deploy.py +117 -0
- dockercomposefile/models/develop.py +33 -0
- dockercomposefile/models/model.py +20 -0
- dockercomposefile/models/network.py +67 -0
- dockercomposefile/models/secret.py +42 -0
- dockercomposefile/models/service.py +598 -0
- dockercomposefile/models/volume.py +73 -0
- dockercomposefile-0.1.0.dist-info/METADATA +143 -0
- dockercomposefile-0.1.0.dist-info/RECORD +20 -0
- dockercomposefile-0.1.0.dist-info/WHEEL +5 -0
- dockercomposefile-0.1.0.dist-info/licenses/LICENSE +201 -0
- dockercomposefile-0.1.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
"""dockercomposefile - Pydantic models for Docker Compose files."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from .builder import ComposeBuilder
|
|
6
|
+
from .exporter import ComposeExporter
|
|
7
|
+
from .models.compose import DockerComposeFile
|
|
8
|
+
|
|
9
|
+
__all__ = [
|
|
10
|
+
"DockerComposeFile",
|
|
11
|
+
"ComposeBuilder",
|
|
12
|
+
"ComposeExporter",
|
|
13
|
+
]
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
"""Builder module for parsing YAML into Pydantic models."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
from typing import Any
|
|
7
|
+
|
|
8
|
+
import yaml
|
|
9
|
+
|
|
10
|
+
from .models.compose import DockerComposeFile
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class ComposeBuilder:
|
|
14
|
+
"""Build Pydantic DockerComposeFile models from YAML sources."""
|
|
15
|
+
|
|
16
|
+
@staticmethod
|
|
17
|
+
def from_file(path: str | Path) -> DockerComposeFile:
|
|
18
|
+
"""Load a Compose file from a filesystem path.
|
|
19
|
+
|
|
20
|
+
Args:
|
|
21
|
+
path: Path to the YAML file.
|
|
22
|
+
|
|
23
|
+
Returns:
|
|
24
|
+
Parsed DockerComposeFile model.
|
|
25
|
+
"""
|
|
26
|
+
path = Path(path)
|
|
27
|
+
with path.open("r", encoding="utf-8") as fh:
|
|
28
|
+
data = yaml.safe_load(fh)
|
|
29
|
+
if data is None:
|
|
30
|
+
data = {}
|
|
31
|
+
return ComposeBuilder.from_dict(data)
|
|
32
|
+
|
|
33
|
+
@staticmethod
|
|
34
|
+
def from_string(yaml_str: str) -> DockerComposeFile:
|
|
35
|
+
"""Parse a Compose file from a YAML string.
|
|
36
|
+
|
|
37
|
+
Args:
|
|
38
|
+
yaml_str: YAML content as a string.
|
|
39
|
+
|
|
40
|
+
Returns:
|
|
41
|
+
Parsed DockerComposeFile model.
|
|
42
|
+
"""
|
|
43
|
+
data = yaml.safe_load(yaml_str)
|
|
44
|
+
if data is None:
|
|
45
|
+
data = {}
|
|
46
|
+
return ComposeBuilder.from_dict(data)
|
|
47
|
+
|
|
48
|
+
@staticmethod
|
|
49
|
+
def from_dict(data: dict[str, Any]) -> DockerComposeFile:
|
|
50
|
+
"""Build a DockerComposeFile model from a plain dictionary.
|
|
51
|
+
|
|
52
|
+
Args:
|
|
53
|
+
data: Dictionary representing the compose file structure.
|
|
54
|
+
|
|
55
|
+
Returns:
|
|
56
|
+
Parsed DockerComposeFile model.
|
|
57
|
+
"""
|
|
58
|
+
return DockerComposeFile.model_validate(data)
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
"""Exporter module for serializing Pydantic models to YAML."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
from typing import Any
|
|
7
|
+
|
|
8
|
+
import yaml
|
|
9
|
+
|
|
10
|
+
from .models.compose import DockerComposeFile
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class _CustomRepresenter(yaml.SafeDumper):
|
|
14
|
+
"""Custom YAML representer that preserves empty dicts and ordering."""
|
|
15
|
+
|
|
16
|
+
def represent_none(self, data):
|
|
17
|
+
return self.represent_scalar("tag:yaml.org,2002:null", "")
|
|
18
|
+
|
|
19
|
+
def represent_empty_dict(self, data):
|
|
20
|
+
return self.represent_mapping("tag:yaml.org,2002:map", {})
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
_CustomRepresenter.add_representer(type(None), _CustomRepresenter.represent_none)
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def _custom_dump(data: Any, stream: Any = None, **kwargs: Any) -> Any:
|
|
27
|
+
"""Dump data to YAML with custom formatting."""
|
|
28
|
+
return yaml.dump(
|
|
29
|
+
data,
|
|
30
|
+
stream,
|
|
31
|
+
Dumper=_CustomRepresenter,
|
|
32
|
+
default_flow_style=False,
|
|
33
|
+
allow_unicode=True,
|
|
34
|
+
sort_keys=False,
|
|
35
|
+
width=float("inf"),
|
|
36
|
+
**kwargs,
|
|
37
|
+
)
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
class ComposeExporter:
|
|
41
|
+
"""Export Pydantic DockerComposeFile models to YAML."""
|
|
42
|
+
|
|
43
|
+
@staticmethod
|
|
44
|
+
def to_file(compose: DockerComposeFile, path: str | Path) -> None:
|
|
45
|
+
"""Serialize a DockerComposeFile to a YAML file.
|
|
46
|
+
|
|
47
|
+
Args:
|
|
48
|
+
compose: The DockerComposeFile model to export.
|
|
49
|
+
path: Destination file path.
|
|
50
|
+
"""
|
|
51
|
+
path = Path(path)
|
|
52
|
+
yaml_str = ComposeExporter.to_string(compose)
|
|
53
|
+
with path.open("w", encoding="utf-8") as fh:
|
|
54
|
+
fh.write(yaml_str)
|
|
55
|
+
|
|
56
|
+
@staticmethod
|
|
57
|
+
def to_string(compose: DockerComposeFile) -> str:
|
|
58
|
+
"""Serialize a DockerComposeFile to a YAML string.
|
|
59
|
+
|
|
60
|
+
Args:
|
|
61
|
+
compose: The DockerComposeFile model to export.
|
|
62
|
+
|
|
63
|
+
Returns:
|
|
64
|
+
YAML string representation.
|
|
65
|
+
"""
|
|
66
|
+
data = ComposeExporter.to_dict(compose)
|
|
67
|
+
return _custom_dump(data)
|
|
68
|
+
|
|
69
|
+
@staticmethod
|
|
70
|
+
def to_dict(compose: DockerComposeFile) -> dict[str, Any]:
|
|
71
|
+
"""Serialize a DockerComposeFile to a plain dictionary.
|
|
72
|
+
|
|
73
|
+
Args:
|
|
74
|
+
compose: The DockerComposeFile model to export.
|
|
75
|
+
|
|
76
|
+
Returns:
|
|
77
|
+
Dictionary representation suitable for YAML serialization.
|
|
78
|
+
"""
|
|
79
|
+
return compose.model_dump(
|
|
80
|
+
mode="json",
|
|
81
|
+
exclude_none=True,
|
|
82
|
+
by_alias=True,
|
|
83
|
+
)
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
"""Pydantic models for Docker Compose files."""
|
|
2
|
+
|
|
3
|
+
from .build import BuildConfig, BuildSecret
|
|
4
|
+
from .common import ComposeBaseModel
|
|
5
|
+
from .compose import DockerComposeFile, Include
|
|
6
|
+
from .config import Config, ServiceConfig
|
|
7
|
+
from .deploy import (
|
|
8
|
+
DeployConfig,
|
|
9
|
+
Placement,
|
|
10
|
+
PlacementPreference,
|
|
11
|
+
Resources,
|
|
12
|
+
ResourceLimits,
|
|
13
|
+
ResourceReservations,
|
|
14
|
+
RestartPolicy,
|
|
15
|
+
RollbackConfig,
|
|
16
|
+
UpdateConfig,
|
|
17
|
+
)
|
|
18
|
+
from .develop import DevelopConfig, WatchExec, WatchRule
|
|
19
|
+
from .model import Model, ServiceModelConfig
|
|
20
|
+
from .network import IPAM, IPAMConfig, Network, ServiceNetworkConfig
|
|
21
|
+
from .secret import Secret, ServiceSecret
|
|
22
|
+
from .service import (
|
|
23
|
+
BlkioConfig,
|
|
24
|
+
BlkioLimit,
|
|
25
|
+
BlkioWeight,
|
|
26
|
+
CredentialSpec,
|
|
27
|
+
DependsOnConfig,
|
|
28
|
+
EnvFileEntry,
|
|
29
|
+
ExtendsConfig,
|
|
30
|
+
GpuConfig,
|
|
31
|
+
Healthcheck,
|
|
32
|
+
Logging,
|
|
33
|
+
PortConfig,
|
|
34
|
+
PostStartHook,
|
|
35
|
+
ProviderConfig,
|
|
36
|
+
Service,
|
|
37
|
+
Ulimit,
|
|
38
|
+
)
|
|
39
|
+
from .volume import (
|
|
40
|
+
BindConfig,
|
|
41
|
+
ImageMountConfig,
|
|
42
|
+
TmpfsConfig,
|
|
43
|
+
Volume,
|
|
44
|
+
VolumeMount,
|
|
45
|
+
VolumeOptions,
|
|
46
|
+
)
|
|
47
|
+
|
|
48
|
+
__all__ = [
|
|
49
|
+
"BindConfig",
|
|
50
|
+
"BlkioConfig",
|
|
51
|
+
"BlkioLimit",
|
|
52
|
+
"BlkioWeight",
|
|
53
|
+
"BuildConfig",
|
|
54
|
+
"BuildSecret",
|
|
55
|
+
"ComposeBaseModel",
|
|
56
|
+
"DockerComposeFile",
|
|
57
|
+
"Config",
|
|
58
|
+
"CredentialSpec",
|
|
59
|
+
"DeployConfig",
|
|
60
|
+
"DependsOnConfig",
|
|
61
|
+
"DevelopConfig",
|
|
62
|
+
"EnvFileEntry",
|
|
63
|
+
"ExtendsConfig",
|
|
64
|
+
"GpuConfig",
|
|
65
|
+
"Healthcheck",
|
|
66
|
+
"IPAM",
|
|
67
|
+
"IPAMConfig",
|
|
68
|
+
"ImageMountConfig",
|
|
69
|
+
"Include",
|
|
70
|
+
"Logging",
|
|
71
|
+
"Model",
|
|
72
|
+
"Network",
|
|
73
|
+
"Placement",
|
|
74
|
+
"PlacementPreference",
|
|
75
|
+
"PortConfig",
|
|
76
|
+
"PostStartHook",
|
|
77
|
+
"ProviderConfig",
|
|
78
|
+
"ResourceLimits",
|
|
79
|
+
"ResourceReservations",
|
|
80
|
+
"Resources",
|
|
81
|
+
"RestartPolicy",
|
|
82
|
+
"RollbackConfig",
|
|
83
|
+
"Secret",
|
|
84
|
+
"Service",
|
|
85
|
+
"ServiceConfig",
|
|
86
|
+
"ServiceModelConfig",
|
|
87
|
+
"ServiceNetworkConfig",
|
|
88
|
+
"ServiceSecret",
|
|
89
|
+
"TmpfsConfig",
|
|
90
|
+
"Ulimit",
|
|
91
|
+
"UpdateConfig",
|
|
92
|
+
"Volume",
|
|
93
|
+
"VolumeMount",
|
|
94
|
+
"VolumeOptions",
|
|
95
|
+
"WatchExec",
|
|
96
|
+
"WatchRule",
|
|
97
|
+
]
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
"""Build configuration models."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from typing import Any
|
|
6
|
+
|
|
7
|
+
from pydantic import BaseModel, Field, field_validator
|
|
8
|
+
|
|
9
|
+
from .common import (
|
|
10
|
+
ComposeBaseModel,
|
|
11
|
+
_parse_additional_contexts,
|
|
12
|
+
_parse_list_or_dict,
|
|
13
|
+
validate_byte_value,
|
|
14
|
+
)
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class BuildSecret(BaseModel):
|
|
18
|
+
"""Secret reference for build."""
|
|
19
|
+
|
|
20
|
+
source: str
|
|
21
|
+
target: str | None = None
|
|
22
|
+
uid: str | None = None
|
|
23
|
+
gid: str | None = None
|
|
24
|
+
mode: str | None = None
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
class BuildConfig(ComposeBaseModel):
|
|
28
|
+
"""Build configuration for a service."""
|
|
29
|
+
|
|
30
|
+
context: str | None = None
|
|
31
|
+
dockerfile: str | None = None
|
|
32
|
+
dockerfile_inline: str | None = None
|
|
33
|
+
args: dict[str, str | None] | list[str] | None = Field(
|
|
34
|
+
default=None, validate_default=True
|
|
35
|
+
)
|
|
36
|
+
ssh: str | list[str] | None = Field(default=None, validate_default=True)
|
|
37
|
+
labels: dict[str, str] | list[str] | None = Field(
|
|
38
|
+
default=None, validate_default=True
|
|
39
|
+
)
|
|
40
|
+
cache_from: list[str] | None = None
|
|
41
|
+
cache_to: list[str] | None = None
|
|
42
|
+
entitlements: list[str] | None = None
|
|
43
|
+
additional_contexts: dict[str, str] | list[str] | None = Field(
|
|
44
|
+
default=None, validate_default=True
|
|
45
|
+
)
|
|
46
|
+
isolation: str | None = None
|
|
47
|
+
network: str | None = None
|
|
48
|
+
shm_size: str | int | None = Field(default=None, validate_default=True)
|
|
49
|
+
target: str | None = None
|
|
50
|
+
platforms: list[str] | None = None
|
|
51
|
+
tags: list[str] | None = None
|
|
52
|
+
pull: bool | None = None
|
|
53
|
+
no_cache: bool | None = None
|
|
54
|
+
privileged: bool | None = None
|
|
55
|
+
secrets: list[str | BuildSecret] | None = Field(
|
|
56
|
+
default=None, validate_default=True
|
|
57
|
+
)
|
|
58
|
+
provenance: bool | str | None = None
|
|
59
|
+
sbom: bool | str | None = None
|
|
60
|
+
ulimits: dict[str, Any] | None = None
|
|
61
|
+
extra_hosts: dict[str, str] | list[str] | None = Field(
|
|
62
|
+
default=None, validate_default=True
|
|
63
|
+
)
|
|
64
|
+
|
|
65
|
+
@classmethod
|
|
66
|
+
def model_validate(cls, obj: Any) -> "BuildConfig":
|
|
67
|
+
if isinstance(obj, str):
|
|
68
|
+
obj = {"context": obj}
|
|
69
|
+
return super().model_validate(obj)
|
|
70
|
+
|
|
71
|
+
# -- validators ----------------------------------------------------------
|
|
72
|
+
|
|
73
|
+
@field_validator("args", mode="before")
|
|
74
|
+
@staticmethod
|
|
75
|
+
def _args(v: Any) -> dict[str, str | None] | list[str] | None:
|
|
76
|
+
return _parse_list_or_dict(v) if v is not None else None
|
|
77
|
+
|
|
78
|
+
@field_validator("ssh", mode="before")
|
|
79
|
+
@staticmethod
|
|
80
|
+
def _ssh(v: Any) -> str | list[str] | None:
|
|
81
|
+
return v if isinstance(v, (str, list)) or v is None else None
|
|
82
|
+
|
|
83
|
+
@field_validator("labels", mode="before")
|
|
84
|
+
@staticmethod
|
|
85
|
+
def _labels(v: Any) -> dict[str, str] | list[str] | None:
|
|
86
|
+
return _parse_list_or_dict(v) if v is not None else None
|
|
87
|
+
|
|
88
|
+
@field_validator("additional_contexts", mode="before")
|
|
89
|
+
@staticmethod
|
|
90
|
+
def _additional_contexts(v: Any) -> dict[str, str] | list[str] | None:
|
|
91
|
+
return _parse_additional_contexts(v) if v is not None else None
|
|
92
|
+
|
|
93
|
+
@field_validator("shm_size", mode="before")
|
|
94
|
+
@staticmethod
|
|
95
|
+
def _shm_size(v: Any) -> str | int | None:
|
|
96
|
+
return validate_byte_value(v)
|
|
97
|
+
|
|
98
|
+
@field_validator("secrets", mode="before")
|
|
99
|
+
@staticmethod
|
|
100
|
+
def _secrets(v: Any) -> list[str | BuildSecret] | None:
|
|
101
|
+
if v is None:
|
|
102
|
+
return None
|
|
103
|
+
if isinstance(v, list):
|
|
104
|
+
result: list[str | BuildSecret] = []
|
|
105
|
+
for item in v:
|
|
106
|
+
if isinstance(item, str):
|
|
107
|
+
result.append(item)
|
|
108
|
+
elif isinstance(item, dict):
|
|
109
|
+
result.append(BuildSecret.model_validate(item))
|
|
110
|
+
return result
|
|
111
|
+
return None
|
|
112
|
+
|
|
113
|
+
@field_validator("extra_hosts", mode="before")
|
|
114
|
+
@staticmethod
|
|
115
|
+
def _extra_hosts(v: Any) -> dict[str, str] | list[str] | None:
|
|
116
|
+
if v is None:
|
|
117
|
+
return None
|
|
118
|
+
if isinstance(v, dict):
|
|
119
|
+
return {str(k): str(val) for k, val in v.items()}
|
|
120
|
+
if isinstance(v, list):
|
|
121
|
+
return [str(item) for item in v]
|
|
122
|
+
return None
|
|
123
|
+
|