Skip to content

Commit 0933d2b

Browse files
authored
feat(containers): Add driver schema (#341)
The schema covers the intersection of the docker and podman platform options (29 portable keys), so a config that validates runs on either backend: - intersection-only types: command is a string, restart_policy excludes unless-stopped, registry.credentials keeps only username/password - backend-specific options (systemd, docker_networks, cgroup_manager, storage_driver, etc.) are intentionally absent
1 parent 4389e8f commit 0933d2b

4 files changed

Lines changed: 469 additions & 4 deletions

File tree

src/molecule_plugins/containers/driver.py

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,11 @@
11
"""Containers Driver Module."""
22

3+
from __future__ import annotations
4+
35
import inspect
46
import os
57
import shutil
8+
from pathlib import Path
69

710
from molecule import logger
811

@@ -51,3 +54,18 @@ def required_collections(self) -> dict[str, str]:
5154
"community.docker": "3.10.2", # keep in sync with src/molecule_plugins/docker/driver.py and requirements.yml
5255
"containers.podman": "1.8.1",
5356
}
57+
58+
def schema_file(self) -> str | None:
59+
"""Return the path to the driver's JSON schema file.
60+
61+
``self._path`` points at the backend driver (docker or podman) so that
62+
molecule can find the embedded playbooks. The schema, however, is
63+
specific to the agnostic *containers* driver, so resolve it relative to
64+
this module instead of the backend.
65+
"""
66+
p = Path(
67+
os.path.dirname(inspect.getfile(self.__class__)), "schema", "driver.json"
68+
)
69+
if p.is_file():
70+
return str(p)
71+
return None
Lines changed: 271 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,271 @@
1+
{
2+
"$defs": {
3+
"MoleculeDriverModel": {
4+
"additionalProperties": true,
5+
"properties": {
6+
"name": {
7+
"enum": ["containers"],
8+
"title": "Name",
9+
"type": "string"
10+
}
11+
},
12+
"required": ["name"],
13+
"title": "MoleculeDriverModel",
14+
"type": "object"
15+
},
16+
"MoleculePlatformModel": {
17+
"additionalProperties": false,
18+
"properties": {
19+
"buildargs": {
20+
"additionalProperties": {
21+
"type": "string"
22+
},
23+
"description": "Arguments that control image build.",
24+
"examples": [{ "http_proxy": "http://proxy.example.com:8080/" }],
25+
"title": "Build Arguments",
26+
"type": "object"
27+
},
28+
"capabilities": {
29+
"description": "List of capabilities to add to the container.",
30+
"examples": ["SYS_ADMIN", "NET_ADMIN"],
31+
"items": {
32+
"type": "string"
33+
},
34+
"title": "Capabilities",
35+
"type": "array"
36+
},
37+
"cert_path": {
38+
"description": "Path to a TLS certificate file. With the docker backend it authenticates with the Docker daemon; with the podman backend it provides certificates (*.crt, *.cert, *.key) to connect to the registry.",
39+
"title": "Cert Path",
40+
"type": "string"
41+
},
42+
"command": {
43+
"description": "Override command of container.",
44+
"examples": ["/sbin/init", "sleep infinity"],
45+
"title": "Command",
46+
"type": "string"
47+
},
48+
"devices": {
49+
"description": "List of device mappings to add a host device to the container. The format is <device-on-host>[:<device-on-container>][:<permissions>].",
50+
"examples": ["/dev/fuse:/dev/fuse:rwm"],
51+
"items": {
52+
"type": "string"
53+
},
54+
"title": "Devices",
55+
"type": "array"
56+
},
57+
"dns_servers": {
58+
"description": "List of DNS servers for the container to use.",
59+
"examples": ["8.8.8.8", "1.1.1.1"],
60+
"items": {
61+
"type": "string"
62+
},
63+
"title": "DNS Servers",
64+
"type": "array"
65+
},
66+
"dockerfile": {
67+
"description": "Path to a Dockerfile.j2 file used to build the image.",
68+
"title": "Dockerfile",
69+
"type": "string"
70+
},
71+
"env": {
72+
"additionalProperties": {
73+
"type": "string"
74+
},
75+
"description": "Set environment variables. This option allows you to specify arbitrary environment variables that are available for the process that will be launched inside of the container.",
76+
"examples": [{ "BAZ": "qux", "FOO": "bar" }],
77+
"title": "Environment",
78+
"type": "object"
79+
},
80+
"etc_hosts": {
81+
"additionalProperties": {
82+
"type": "string"
83+
},
84+
"description": "Dict of host-to-IP mappings, where each host name is a key in the dictionary. Each host name will be added to the container's ``/etc/hosts`` file.",
85+
"examples": [{ "host1.example.com": "10.3.1.5" }],
86+
"title": "etc hosts",
87+
"type": "object"
88+
},
89+
"exposed_ports": {
90+
"description": "List of ports to expose on the container.",
91+
"examples": ["53/udp", "53/tcp"],
92+
"items": {
93+
"type": "string"
94+
},
95+
"title": "Exposed Ports",
96+
"type": "array"
97+
},
98+
"groups": {
99+
"description": "List of inventory groups to add the instance to.",
100+
"examples": ["webserver", "db"],
101+
"items": {
102+
"type": "string"
103+
},
104+
"title": "Groups",
105+
"type": "array"
106+
},
107+
"hostname": {
108+
"description": "Container host name. Sets the container host name that is available inside the container.",
109+
"title": "Hostname",
110+
"type": "string"
111+
},
112+
"image": {
113+
"description": "Repository path (or image name) and tag used to create the container. If an image is not found, the image will be pulled from the registry. If no tag is included, latest will be used.",
114+
"title": "Image",
115+
"type": "string"
116+
},
117+
"name": {
118+
"description": "Name of the container",
119+
"title": "Name",
120+
"type": "string"
121+
},
122+
"override_command": {
123+
"description": "Whether or not to override the default command set by the container image.",
124+
"title": "Override Command",
125+
"type": "boolean"
126+
},
127+
"pid_mode": {
128+
"description": "Set the PID mode for the container.",
129+
"title": "PID Mode",
130+
"type": "string"
131+
},
132+
"pre_build_image": {
133+
"description": "Use a pre-built image instead of building one from a Dockerfile.",
134+
"title": "Pre Build Image",
135+
"type": "boolean"
136+
},
137+
"privileged": {
138+
"description": "Give extended privileges to the container.",
139+
"title": "Privileged",
140+
"type": "boolean"
141+
},
142+
"published_ports": {
143+
"description": "Publish a container's port, or range of ports, to the host. Format - \"ip:hostPort:containerPort\" | \"ip::containerPort\" | \"hostPort:containerPort\" | \"containerPort\".",
144+
"examples": ["8080:80", "0.0.0.0:8053:53/udp"],
145+
"items": {
146+
"type": "string"
147+
},
148+
"title": "Published Ports",
149+
"type": "array"
150+
},
151+
"pull": {
152+
"description": "Whether or not to pull the image.",
153+
"title": "Pull",
154+
"type": "boolean"
155+
},
156+
"registry": {
157+
"additionalProperties": false,
158+
"description": "Registry configuration.",
159+
"properties": {
160+
"credentials": {
161+
"additionalProperties": false,
162+
"description": "Credentials for the registry server.",
163+
"properties": {
164+
"password": {
165+
"description": "Password for the registry server.",
166+
"title": "Password",
167+
"type": "string"
168+
},
169+
"username": {
170+
"description": "Username for the registry server.",
171+
"title": "Username",
172+
"type": "string"
173+
}
174+
},
175+
"required": ["username", "password"],
176+
"title": "Credentials",
177+
"type": "object"
178+
},
179+
"url": {
180+
"description": "Registry server URL.",
181+
"format": "uri",
182+
"title": "Url",
183+
"type": "string"
184+
}
185+
},
186+
"title": "Registry",
187+
"type": "object"
188+
},
189+
"restart_policy": {
190+
"description": "Restart policy to follow when containers exit. Valid values are * no - Do not restart containers on exit. (This needs to be quoted, otherwise it will parse to false) * on-failure - Restart containers when they exit with a non-0 exit code, retrying indefinitely or until the optional restart_retries count is hit * always - Restart containers when they exit, regardless of status, retrying indefinitely",
191+
"enum": ["no", "on-failure", "always"],
192+
"title": "Restart Policy",
193+
"type": "string"
194+
},
195+
"restart_retries": {
196+
"description": "The number of times to retry restarting a container when the restart_policy is set to on-failure. This option is ignored when restart_policy is set to no or always.",
197+
"minimum": 0,
198+
"title": "Restart Retries",
199+
"type": "integer"
200+
},
201+
"security_opts": {
202+
"description": "Security Options.",
203+
"examples": ["seccomp=unconfined"],
204+
"items": {
205+
"type": "string"
206+
},
207+
"title": "Security Options",
208+
"type": "array"
209+
},
210+
"tls_verify": {
211+
"description": "Require HTTPS and verify certificates when contacting the daemon or registry. If explicitly set to true, then TLS verification will be used. If set to false, then TLS verification will not be used.",
212+
"title": "TLS Verify",
213+
"type": "boolean"
214+
},
215+
"tmpfs": {
216+
"description": "Mount a tmpfs directory in the container.",
217+
"examples": ["/tmp", "/run"],
218+
"items": {
219+
"type": "string"
220+
},
221+
"title": "tmpfs",
222+
"type": "array"
223+
},
224+
"tty": {
225+
"description": "Allocate a pseudo-TTY.",
226+
"title": "TTY",
227+
"type": "boolean"
228+
},
229+
"ulimits": {
230+
"description": "Ulimit options.",
231+
"examples": ["nofile:262144:262144"],
232+
"items": {
233+
"type": "string"
234+
},
235+
"title": "Ulimits",
236+
"type": "array"
237+
},
238+
"volumes": {
239+
"description": "Create a bind mount. If you specify a volume ``/HOST-DIR:/CONTAINER-DIR``, the host directory is bind-mounted into the container.",
240+
"examples": ["/sys/fs/cgroup:/sys/fs/cgroup:ro"],
241+
"items": {
242+
"type": "string"
243+
},
244+
"title": "Volumes",
245+
"type": "array"
246+
}
247+
},
248+
"required": ["name"],
249+
"title": "MoleculePlatformModel",
250+
"type": "object"
251+
}
252+
},
253+
"$id": "https://raw.githubusercontent.com/ansible-community/molecule-plugins/main/src/molecule_plugins/containers/schema/driver.json",
254+
"$schema": "https://json-schema.org/draft/2020-12/schema",
255+
"examples": ["molecule/*/molecule.yml"],
256+
"properties": {
257+
"driver": {
258+
"$ref": "#/$defs/MoleculeDriverModel"
259+
},
260+
"platforms": {
261+
"items": {
262+
"$ref": "#/$defs/MoleculePlatformModel"
263+
},
264+
"title": "Platforms",
265+
"type": "array"
266+
}
267+
},
268+
"required": ["driver"],
269+
"title": "Molecule Containers Driver Schema",
270+
"type": "object"
271+
}

test/containers/conftest.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
"""Pytest Fixtures."""
2+
3+
from conftest import random_string, temp_dir # noqa
4+
5+
import pytest
6+
7+
8+
@pytest.fixture()
9+
def driver_name():
10+
"""Return name of the driver to be tested."""
11+
return "containers"

0 commit comments

Comments
 (0)