diff --git a/compose_viz/models/device.py b/compose_viz/models/device.py new file mode 100644 index 0000000..80d6b1e --- /dev/null +++ b/compose_viz/models/device.py @@ -0,0 +1,20 @@ +from typing import Optional + + +class Device: + def __init__(self, host_path: str, container_path: str, cgroup_permissions: Optional[str] = None): + self._host_path = host_path + self._container_path = container_path + self._cgroup_permissions = cgroup_permissions + + @property + def host_path(self): + return self._host_path + + @property + def container_path(self): + return self._container_path + + @property + def cgroup_permissions(self): + return self._cgroup_permissions diff --git a/compose_viz/models/service.py b/compose_viz/models/service.py index abd94f6..8e578c2 100644 --- a/compose_viz/models/service.py +++ b/compose_viz/models/service.py @@ -1,5 +1,6 @@ from typing import List, Optional +from compose_viz.models.device import Device from compose_viz.models.extends import Extends from compose_viz.models.port import Port from compose_viz.models.volume import Volume @@ -18,7 +19,7 @@ class Service: extends: Optional[Extends] = None, cgroup_parent: Optional[str] = None, container_name: Optional[str] = None, - devices: List[str] = [], + devices: List[Device] = [], env_file: List[str] = [], expose: List[str] = [], profiles: List[str] = [], @@ -31,12 +32,12 @@ class Service: self._depends_on = depends_on self._links = links self._extends = extends - self.cgroup_parent = cgroup_parent - self.container_name = container_name - self.devices = devices - self.env_file = env_file - self.expose = expose - self.profiles = profiles + self._cgroup_parent = cgroup_parent + self._container_name = container_name + self._devices = devices + self._env_file = env_file + self._expose = expose + self._profiles = profiles @property def name(self): @@ -69,3 +70,27 @@ class Service: @property def extends(self): return self._extends + + @property + def cgroup_parent(self): + return self._cgroup_parent + + @property + def container_name(self): + return self._container_name + + @property + def devices(self): + return self._devices + + @property + def env_file(self): + return self._env_file + + @property + def expose(self): + return self._expose + + @property + def profiles(self): + return self._profiles diff --git a/compose_viz/parser.py b/compose_viz/parser.py index 6f296d4..c67186c 100644 --- a/compose_viz/parser.py +++ b/compose_viz/parser.py @@ -5,6 +5,7 @@ from pydantic import ValidationError import compose_viz.spec.compose_spec as spec from compose_viz.models.compose import Compose, Service +from compose_viz.models.device import Device from compose_viz.models.extends import Extends from compose_viz.models.port import Port, Protocol from compose_viz.models.volume import Volume, VolumeType @@ -202,6 +203,24 @@ class Parser: if type(service_data.profiles) is spec.ListOfStrings: profiles = service_data.profiles.__root__ + devices: List[Device] = [] + if service_data.devices is not None: + for device_data in service_data.devices: + if type(device_data) is str: + assert ":" in device_data, "Invalid volume input, aborting." + + spilt_data = device_data.split(":") + if len(spilt_data) == 2: + devices.append(Device(host_path=spilt_data[0], container_path=spilt_data[1])) + elif len(spilt_data) == 3: + devices.append( + Device( + host_path=spilt_data[0], + container_path=spilt_data[1], + cgroup_permissions=spilt_data[2], + ) + ) + services.append( Service( name=service_name, @@ -217,6 +236,7 @@ class Parser: env_file=env_file, expose=expose, profiles=profiles, + devices=devices, ) ) diff --git a/tests/test_parse_file.py b/tests/test_parse_file.py index 226d755..095c024 100644 --- a/tests/test_parse_file.py +++ b/tests/test_parse_file.py @@ -1,6 +1,7 @@ import pytest from compose_viz.models.compose import Compose +from compose_viz.models.device import Device from compose_viz.models.extends import Extends from compose_viz.models.port import Port, Protocol from compose_viz.models.service import Service @@ -326,6 +327,38 @@ from compose_viz.parser import Parser ], ), ), + ( + "devices/docker-compose", + Compose( + services=[ + Service( + name="frontend", + image="awesome/frontend", + devices=[ + Device( + host_path="/dev/ttyUSB0", + container_path="/dev/ttyUSB1", + ) + ], + ), + Service( + name="backend", + image="awesome/backend", + devices=[ + Device( + host_path="/dev/ttyUSB2", + container_path="/dev/ttyUSB3", + ), + Device( + host_path="/dev/sda", + container_path="/dev/xvda", + cgroup_permissions="rwm", + ), + ], + ), + ], + ), + ), ], ) def test_parse_file(test_file_path: str, expected: Compose) -> None: @@ -367,3 +400,9 @@ def test_parse_file(test_file_path: str, expected: Compose) -> None: assert actual_service.expose == expected_service.expose assert actual_service.env_file == expected_service.env_file assert actual_service.profiles == expected_service.profiles + + assert len(actual_service.devices) == len(expected_service.devices) + for actual_device, expected_device in zip(actual_service.devices, expected_service.devices): + assert actual_device.host_path == expected_device.host_path + assert actual_device.container_path == expected_device.container_path + assert actual_device.cgroup_permissions == expected_device.cgroup_permissions diff --git a/tests/ymls/devices/docker-compose.yml b/tests/ymls/devices/docker-compose.yml index f3fa6f8..2f810a3 100644 --- a/tests/ymls/devices/docker-compose.yml +++ b/tests/ymls/devices/docker-compose.yml @@ -4,9 +4,9 @@ services: frontend: image: awesome/frontend devices: - - "/dev/ttyUSB0:/dev/ttyUSB0" + - "/dev/ttyUSB0:/dev/ttyUSB1" backend: image: awesome/backend devices: - - "/dev/ttyUSB1:/dev/ttyUSB1" - - "/dev/ttyUSB2:/dev/ttyUSB2" + - "/dev/ttyUSB2:/dev/ttyUSB3" + - "/dev/sda:/dev/xvda:rwm"