Merge pull request #35 from compose-viz/dev

chore: add graph of new components
This commit is contained in:
Xyphuz 2022-06-09 13:59:41 +08:00 committed by GitHub
commit a9df873278
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
19 changed files with 407 additions and 31 deletions

View file

@ -20,18 +20,6 @@ jobs:
sudo apt-get update sudo apt-get update
sudo apt-get install -y graphviz sudo apt-get install -y graphviz
- name: Validate Test Files
run: |
docker compose -f tests/ymls/builds/docker-compose.yml config -q
docker compose -f tests/ymls/depends_on/docker-compose.yml config -q
docker compose -f tests/ymls/extends/docker-compose.yml config -q
docker compose -f tests/ymls/links/docker-compose.yml config -q
docker compose -f tests/ymls/networks/docker-compose.yml config -q
docker compose -f tests/ymls/ports/docker-compose.yml config -q
docker compose -f tests/ymls/volumes/docker-compose.yml config -q
docker compose -f examples/full-stack-node-app/docker-compose.yml config -q
docker compose -f examples/non-normative/docker-compose.yml config -q
- name: Setup Python 3.10.4 - name: Setup Python 3.10.4
uses: actions/setup-python@v3 uses: actions/setup-python@v3
with: with:

View file

@ -5,13 +5,13 @@ import graphviz
from compose_viz.models.compose import Compose from compose_viz.models.compose import Compose
def apply_vertex_style(type) -> dict: def apply_vertex_style(type: str) -> dict:
style = { style = {
"service": { "service": {
"shape": "component", "shape": "component",
}, },
"volume": { "volume": {
"shape": "folder", "shape": "cylinder",
}, },
"network": { "network": {
"shape": "pentagon", "shape": "pentagon",
@ -19,24 +19,39 @@ def apply_vertex_style(type) -> dict:
"port": { "port": {
"shape": "circle", "shape": "circle",
}, },
"env_file": {
"shape": "tab",
},
"porfile": {
"shape": "invhouse",
},
"cgroup": {
"shape": "diamond",
},
"device": {
"shape": "box3d",
},
} }
return style[type] return style[type]
def apply_edge_style(type) -> dict: def apply_edge_style(type: str) -> dict:
style = { style = {
"ports": { "exposes": {
"style": "solid", "style": "solid",
"dir": "both", "dir": "both",
}, },
"links": { "links": {
"style": "solid", "style": "solid",
}, },
"volumes": { "volumes_rw": {
"style": "dashed", "style": "dashed",
"dir": "both", "dir": "both",
}, },
"volumes_ro": {
"style": "dashed",
},
"depends_on": { "depends_on": {
"style": "dotted", "style": "dotted",
}, },
@ -45,6 +60,9 @@ def apply_edge_style(type) -> dict:
"arrowhead": "inv", "arrowhead": "inv",
"arrowtail": "dot", "arrowtail": "dot",
}, },
"env_file": {
"style": "solid",
},
} }
return style[type] return style[type]
@ -71,19 +89,38 @@ class Graph:
def render(self, format: str, cleanup: bool = True) -> None: def render(self, format: str, cleanup: bool = True) -> None:
for service in self.compose.services: for service in self.compose.services:
if service.image is not None: if service.image is not None:
self.add_vertex(service.name, "service", lable=f"{service.name}\n({service.image})") self.add_vertex(
service.name,
"service",
lable=f"{service.container_name if service.container_name else service.name}\n({service.image})",
)
if service.extends is not None: if service.extends is not None:
self.add_vertex(service.name, "service", lable=f"{service.name}\n") self.add_vertex(service.name, "service", lable=f"{service.name}\n")
self.add_edge(service.extends.service_name, service.name, "extends") self.add_edge(service.extends.service_name, service.name, "extends")
if service.cgroup_parent is not None:
self.add_vertex(service.cgroup_parent, "cgroup")
self.add_edge(service.name, service.cgroup_parent, "links")
for network in service.networks: for network in service.networks:
self.add_vertex(network, "network", lable=f"net:{network}") self.add_vertex(network, "network", lable=f"net:{network}")
self.add_edge(service.name, network, "links") self.add_edge(service.name, network, "links")
for volume in service.volumes: for volume in service.volumes:
self.add_vertex(volume.source, "volume") self.add_vertex(volume.source, "volume")
self.add_edge(service.name, volume.source, "volumes", lable=volume.target) self.add_edge(
service.name,
volume.source,
"volumes_rw" if "rw" in volume.access_mode else "volumes_ro",
lable=volume.target,
)
for expose in service.expose:
self.add_vertex(expose, "port")
self.add_edge(expose, service.name, "exposes")
for port in service.ports: for port in service.ports:
self.add_vertex(port.host_port, "port", lable=port.host_port) self.add_vertex(port.host_port, "port", lable=port.host_port)
self.add_edge(port.host_port, service.name, "ports", lable=port.container_port) self.add_edge(port.host_port, service.name, "links", lable=port.container_port)
for env_file in service.env_file:
self.add_vertex(env_file, "env_file")
self.add_edge(env_file, service.name, "env_file")
for link in service.links: for link in service.links:
if ":" in link: if ":" in link:
service_name, alias = link.split(":", 1) service_name, alias = link.split(":", 1)
@ -92,5 +129,13 @@ class Graph:
self.add_edge(link, service.name, "links") self.add_edge(link, service.name, "links")
for depends_on in service.depends_on: for depends_on in service.depends_on:
self.add_edge(service.name, depends_on, "depends_on") self.add_edge(service.name, depends_on, "depends_on")
for porfile in service.profiles:
self.add_vertex(porfile, "porfile")
self.add_edge(service.name, porfile, "links")
for device in service.devices:
self.add_vertex(device.host_path, "device")
self.add_edge(
device.host_path, service.name, "exposes", f"{device.container_path}\n({device.cgroup_permissions})"
)
self.dot.render(outfile=f"{self.filename}.{format}", format=format, cleanup=cleanup) self.dot.render(outfile=f"{self.filename}.{format}", format=format, cleanup=cleanup)

View file

@ -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

View file

@ -1,5 +1,6 @@
from typing import List, Optional from typing import List, Optional
from compose_viz.models.device import Device
from compose_viz.models.extends import Extends from compose_viz.models.extends import Extends
from compose_viz.models.port import Port from compose_viz.models.port import Port
from compose_viz.models.volume import Volume from compose_viz.models.volume import Volume
@ -16,6 +17,12 @@ class Service:
depends_on: List[str] = [], depends_on: List[str] = [],
links: List[str] = [], links: List[str] = [],
extends: Optional[Extends] = None, extends: Optional[Extends] = None,
cgroup_parent: Optional[str] = None,
container_name: Optional[str] = None,
devices: List[Device] = [],
env_file: List[str] = [],
expose: List[str] = [],
profiles: List[str] = [],
) -> None: ) -> None:
self._name = name self._name = name
self._image = image self._image = image
@ -25,6 +32,12 @@ class Service:
self._depends_on = depends_on self._depends_on = depends_on
self._links = links self._links = links
self._extends = extends 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
@property @property
def name(self): def name(self):
@ -57,3 +70,27 @@ class Service:
@property @property
def extends(self): def extends(self):
return self._extends 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

View file

@ -5,6 +5,7 @@ from pydantic import ValidationError
import compose_viz.spec.compose_spec as spec import compose_viz.spec.compose_spec as spec
from compose_viz.models.compose import Compose, Service 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.extends import Extends
from compose_viz.models.port import Port, Protocol from compose_viz.models.port import Port, Protocol
from compose_viz.models.volume import Volume, VolumeType from compose_viz.models.volume import Volume, VolumeType
@ -176,6 +177,50 @@ class Parser:
if service_data.links is not None: if service_data.links is not None:
service_links = service_data.links service_links = service_data.links
cgroup_parent: Optional[str] = None
if service_data.cgroup_parent is not None:
cgroup_parent = service_data.cgroup_parent
container_name: Optional[str] = None
if service_data.container_name is not None:
container_name = service_data.container_name
env_file: List[str] = []
if service_data.env_file is not None:
if type(service_data.env_file) is spec.StringOrList:
if type(service_data.env_file.__root__) is spec.ListOfStrings:
env_file = service_data.env_file.__root__.__root__
elif type(service_data.env_file.__root__) is str:
env_file.append(service_data.env_file.__root__)
expose: List[str] = []
if service_data.expose is not None:
for port in service_data.expose:
expose.append(str(port))
profiles: List[str] = []
if service_data.profiles is not None:
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( services.append(
Service( Service(
name=service_name, name=service_name,
@ -186,6 +231,12 @@ class Parser:
depends_on=service_depends_on, depends_on=service_depends_on,
volumes=service_volumes, volumes=service_volumes,
links=service_links, links=service_links,
cgroup_parent=cgroup_parent,
container_name=container_name,
env_file=env_file,
expose=expose,
profiles=profiles,
devices=devices,
) )
) )

Binary file not shown.

Before

Width:  |  Height:  |  Size: 99 KiB

After

Width:  |  Height:  |  Size: 134 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 48 KiB

After

Width:  |  Height:  |  Size: 93 KiB

View file

@ -6,10 +6,19 @@ services:
- back-tier - back-tier
monitoring: monitoring:
env_file:
- a.env
- b.env
container_name: monitoring-server
image: awesome/monitoring image: awesome/monitoring
networks: networks:
- admin - admin
expose:
- 1234
profiles:
- tools
- foo
cgroup_parent: awesome-parent
backend: backend:
networks: networks:
@ -36,8 +45,12 @@ services:
- "8000:5010" - "8000:5010"
links: links:
- "db:database" - "db:database"
cgroup_parent: awesome-parent
db: db:
image: postgres image: postgres
devices:
- "/dev/ttyUSB2:/dev/ttyUSB3"
- "/dev/sda:/dev/xvda:rwm"
networks: networks:
front-tier: front-tier:

Binary file not shown.

Before

Width:  |  Height:  |  Size: 68 KiB

After

Width:  |  Height:  |  Size: 70 KiB

View file

@ -38,8 +38,7 @@
<!-- 0.0.0.06379&#45;&gt;redis --> <!-- 0.0.0.06379&#45;&gt;redis -->
<g id="edge2" class="edge"> <g id="edge2" class="edge">
<title>0.0.0.06379&#45;&gt;redis</title> <title>0.0.0.06379&#45;&gt;redis</title>
<path fill="none" stroke="black" d="M104.53,-188.06C126.48,-169.27 151.57,-147.8 170.41,-131.68"/> <path fill="none" stroke="black" d="M96.9,-194.6C120.47,-174.43 149.35,-149.71 170.41,-131.68"/>
<polygon fill="black" stroke="black" points="102.22,-185.44 96.9,-194.6 106.77,-190.75 102.22,-185.44"/>
<polygon fill="black" stroke="black" points="172.75,-134.28 178.07,-125.12 168.2,-128.96 172.75,-134.28"/> <polygon fill="black" stroke="black" points="172.75,-134.28 178.07,-125.12 168.2,-128.96 172.75,-134.28"/>
<text text-anchor="middle" x="171.25" y="-146.8" font-family="Times New Roman,serif" font-size="14.00">6379</text> <text text-anchor="middle" x="171.25" y="-146.8" font-family="Times New Roman,serif" font-size="14.00">6379</text>
</g> </g>
@ -67,7 +66,8 @@
<!-- db&#45;data --> <!-- db&#45;data -->
<g id="node6" class="node"> <g id="node6" class="node">
<title>db&#45;data</title> <title>db&#45;data</title>
<polygon fill="none" stroke="black" points="700.75,-36 697.75,-40 676.75,-40 673.75,-36 641.75,-36 641.75,0 700.75,0 700.75,-36"/> <path fill="none" stroke="black" d="M700.75,-32.73C700.75,-34.53 687.52,-36 671.25,-36 654.97,-36 641.75,-34.53 641.75,-32.73 641.75,-32.73 641.75,-3.27 641.75,-3.27 641.75,-1.47 654.97,0 671.25,0 687.52,0 700.75,-1.47 700.75,-3.27 700.75,-3.27 700.75,-32.73 700.75,-32.73"/>
<path fill="none" stroke="black" d="M700.75,-32.73C700.75,-30.92 687.52,-29.45 671.25,-29.45 654.97,-29.45 641.75,-30.92 641.75,-32.73"/>
<text text-anchor="middle" x="671.25" y="-14.3" font-family="Times New Roman,serif" font-size="14.00">db&#45;data</text> <text text-anchor="middle" x="671.25" y="-14.3" font-family="Times New Roman,serif" font-size="14.00">db&#45;data</text>
</g> </g>
<!-- db&#45;&gt;db&#45;data --> <!-- db&#45;&gt;db&#45;data -->
@ -108,8 +108,7 @@
<!-- 0.0.0.05000&#45;&gt;vote --> <!-- 0.0.0.05000&#45;&gt;vote -->
<g id="edge6" class="edge"> <g id="edge6" class="edge">
<title>0.0.0.05000&#45;&gt;vote</title> <title>0.0.0.05000&#45;&gt;vote</title>
<path fill="none" stroke="black" d="M271.25,-327.24C271.25,-304.1 271.25,-279.18 271.25,-260.67"/> <path fill="none" stroke="black" d="M271.25,-337.29C271.25,-311.63 271.25,-282.06 271.25,-260.85"/>
<polygon fill="black" stroke="black" points="267.75,-327.29 271.25,-337.29 274.75,-327.29 267.75,-327.29"/>
<polygon fill="black" stroke="black" points="274.75,-260.58 271.25,-250.58 267.75,-260.58 274.75,-260.58"/> <polygon fill="black" stroke="black" points="274.75,-260.58 271.25,-250.58 267.75,-260.58 274.75,-260.58"/>
<text text-anchor="middle" x="278.25" y="-308.29" font-family="Times New Roman,serif" font-size="14.00">80</text> <text text-anchor="middle" x="278.25" y="-308.29" font-family="Times New Roman,serif" font-size="14.00">80</text>
</g> </g>
@ -143,8 +142,7 @@
<!-- 0.0.0.05001&#45;&gt;result --> <!-- 0.0.0.05001&#45;&gt;result -->
<g id="edge9" class="edge"> <g id="edge9" class="edge">
<title>0.0.0.05001&#45;&gt;result</title> <title>0.0.0.05001&#45;&gt;result</title>
<path fill="none" stroke="black" d="M614.25,-327.24C614.25,-304.1 614.25,-279.18 614.25,-260.67"/> <path fill="none" stroke="black" d="M614.25,-337.29C614.25,-311.63 614.25,-282.06 614.25,-260.85"/>
<polygon fill="black" stroke="black" points="610.75,-327.29 614.25,-337.29 617.75,-327.29 610.75,-327.29"/>
<polygon fill="black" stroke="black" points="617.75,-260.58 614.25,-250.58 610.75,-260.58 617.75,-260.58"/> <polygon fill="black" stroke="black" points="617.75,-260.58 614.25,-250.58 610.75,-260.58 617.75,-260.58"/>
<text text-anchor="middle" x="621.25" y="-308.29" font-family="Times New Roman,serif" font-size="14.00">80</text> <text text-anchor="middle" x="621.25" y="-308.29" font-family="Times New Roman,serif" font-size="14.00">80</text>
</g> </g>
@ -181,7 +179,8 @@
<!-- /var/run/docker.sock --> <!-- /var/run/docker.sock -->
<g id="node13" class="node"> <g id="node13" class="node">
<title>/var/run/docker.sock</title> <title>/var/run/docker.sock</title>
<polygon fill="none" stroke="black" points="926.75,-124 923.75,-128 902.75,-128 899.75,-124 793.75,-124 793.75,-88 926.75,-88 926.75,-124"/> <path fill="none" stroke="black" d="M926.75,-120.73C926.75,-122.53 896.94,-124 860.25,-124 823.55,-124 793.75,-122.53 793.75,-120.73 793.75,-120.73 793.75,-91.27 793.75,-91.27 793.75,-89.47 823.55,-88 860.25,-88 896.94,-88 926.75,-89.47 926.75,-91.27 926.75,-91.27 926.75,-120.73 926.75,-120.73"/>
<path fill="none" stroke="black" d="M926.75,-120.73C926.75,-118.92 896.94,-117.45 860.25,-117.45 823.55,-117.45 793.75,-118.92 793.75,-120.73"/>
<text text-anchor="middle" x="860.25" y="-102.3" font-family="Times New Roman,serif" font-size="14.00">/var/run/docker.sock</text> <text text-anchor="middle" x="860.25" y="-102.3" font-family="Times New Roman,serif" font-size="14.00">/var/run/docker.sock</text>
</g> </g>
<!-- visualizer&#45;&gt;/var/run/docker.sock --> <!-- visualizer&#45;&gt;/var/run/docker.sock -->
@ -201,8 +200,7 @@
<!-- 0.0.0.08080&#45;&gt;visualizer --> <!-- 0.0.0.08080&#45;&gt;visualizer -->
<g id="edge14" class="edge"> <g id="edge14" class="edge">
<title>0.0.0.08080&#45;&gt;visualizer</title> <title>0.0.0.08080&#45;&gt;visualizer</title>
<path fill="none" stroke="black" d="M860.25,-327.24C860.25,-304.1 860.25,-279.18 860.25,-260.67"/> <path fill="none" stroke="black" d="M860.25,-337.29C860.25,-311.63 860.25,-282.06 860.25,-260.85"/>
<polygon fill="black" stroke="black" points="856.75,-327.29 860.25,-337.29 863.75,-327.29 856.75,-327.29"/>
<polygon fill="black" stroke="black" points="863.75,-260.58 860.25,-250.58 856.75,-260.58 863.75,-260.58"/> <polygon fill="black" stroke="black" points="863.75,-260.58 860.25,-250.58 856.75,-260.58 863.75,-260.58"/>
<text text-anchor="middle" x="874.25" y="-308.29" font-family="Times New Roman,serif" font-size="14.00">8080</text> <text text-anchor="middle" x="874.25" y="-308.29" font-family="Times New Roman,serif" font-size="14.00">8080</text>
</g> </g>

Before

Width:  |  Height:  |  Size: 13 KiB

After

Width:  |  Height:  |  Size: 13 KiB

View file

@ -12,11 +12,17 @@ runner = CliRunner()
"test_file_path", "test_file_path",
[ [
"tests/ymls/builds/docker-compose.yml", "tests/ymls/builds/docker-compose.yml",
"tests/ymls/cgroup_parent/docker-compose.yml",
"tests/ymls/container_name/docker-compose.yml",
"tests/ymls/depends_on/docker-compose.yml", "tests/ymls/depends_on/docker-compose.yml",
"tests/ymls/devices/docker-compose.yml",
"tests/ymls/env_file/docker-compose.yml",
"tests/ymls/expose/docker-compose.yml",
"tests/ymls/extends/docker-compose.yml", "tests/ymls/extends/docker-compose.yml",
"tests/ymls/links/docker-compose.yml", "tests/ymls/links/docker-compose.yml",
"tests/ymls/networks/docker-compose.yml", "tests/ymls/networks/docker-compose.yml",
"tests/ymls/ports/docker-compose.yml", "tests/ymls/ports/docker-compose.yml",
"tests/ymls/profiles/docker-compose.yml",
"tests/ymls/volumes/docker-compose.yml", "tests/ymls/volumes/docker-compose.yml",
"examples/full-stack-node-app/docker-compose.yml", "examples/full-stack-node-app/docker-compose.yml",
"examples/non-normative/docker-compose.yml", "examples/non-normative/docker-compose.yml",

22
tests/test_devices.py Normal file
View file

@ -0,0 +1,22 @@
from compose_viz.models.device import Device
def test_device_init_normal() -> None:
try:
d = Device(host_path="/dev/ttyUSB0", container_path="/dev/ttyUSB1")
assert d.host_path == "/dev/ttyUSB0"
assert d.container_path == "/dev/ttyUSB1"
except Exception as e:
assert False, e
def test_device_with_cgroup_permissions() -> None:
try:
d = Device(host_path="/dev/sda1", container_path="/dev/xvda", cgroup_permissions="rwm")
assert d.host_path == "/dev/sda1"
assert d.container_path == "/dev/xvda"
assert d.cgroup_permissions == "rwm"
except Exception as e:
assert False, e

View file

@ -1,6 +1,7 @@
import pytest import pytest
from compose_viz.models.compose import Compose 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.extends import Extends
from compose_viz.models.port import Port, Protocol from compose_viz.models.port import Port, Protocol
from compose_viz.models.service import Service from compose_viz.models.service import Service
@ -241,6 +242,123 @@ from compose_viz.parser import Parser
], ],
), ),
), ),
(
"cgroup_parent/docker-compose",
Compose(
services=[
Service(
name="frontend",
image="awesome/frontend",
cgroup_parent="system",
),
],
),
),
(
"container_name/docker-compose",
Compose(
services=[
Service(
name="frontend",
image="awesome/frontend",
container_name="myfrontend",
),
],
),
),
(
"env_file/docker-compose",
Compose(
services=[
Service(
name="frontend",
image="awesome/frontend",
env_file=["a.env"],
),
Service(
name="backend",
image="awesome/backend",
env_file=["b.env"],
),
Service(
name="db",
image="awesome/db",
env_file=["c.env", "d.env"],
),
],
),
),
(
"expose/docker-compose",
Compose(
services=[
Service(
name="frontend",
image="awesome/frontend",
expose=["27118"],
),
Service(
name="backend",
image="awesome/backend",
expose=["27017", "27018"],
),
],
),
),
(
"profiles/docker-compose",
Compose(
services=[
Service(
name="frontend",
image="awesome/frontend",
profiles=["frontend"],
),
Service(
name="phpmyadmin",
image="phpmyadmin",
profiles=["debug"],
),
Service(
name="db",
image="awesome/db",
profiles=["db", "sql"],
),
],
),
),
(
"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: def test_parse_file(test_file_path: str, expected: Compose) -> None:
@ -275,3 +393,16 @@ def test_parse_file(test_file_path: str, expected: Compose) -> None:
if (actual_service.extends is not None) and (expected_service.extends is not None): if (actual_service.extends is not None) and (expected_service.extends is not None):
assert actual_service.extends.service_name == expected_service.extends.service_name assert actual_service.extends.service_name == expected_service.extends.service_name
assert actual_service.extends.from_file == expected_service.extends.from_file assert actual_service.extends.from_file == expected_service.extends.from_file
assert actual_service.cgroup_parent == expected_service.cgroup_parent
assert actual_service.container_name == expected_service.container_name
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

View file

@ -0,0 +1,6 @@
version: "3.9"
services:
frontend:
image: awesome/frontend
cgroup_parent: "system"

View file

@ -0,0 +1,6 @@
version: "3.9"
services:
frontend:
image: awesome/frontend
container_name: "myfrontend"

View file

@ -0,0 +1,12 @@
version: "3.9"
services:
frontend:
image: awesome/frontend
devices:
- "/dev/ttyUSB0:/dev/ttyUSB1"
backend:
image: awesome/backend
devices:
- "/dev/ttyUSB2:/dev/ttyUSB3"
- "/dev/sda:/dev/xvda:rwm"

View file

@ -0,0 +1,15 @@
version: "3.9"
services:
frontend:
image: awesome/frontend
env_file: a.env
backend:
image: awesome/backend
env_file:
- b.env
db:
image: awesome/db
env_file:
- c.env
- d.env

View file

@ -0,0 +1,12 @@
version: "3.9"
services:
frontend:
image: awesome/frontend
expose:
- "27118"
backend:
image: awesome/backend
expose:
- "27017"
- "27018"

View file

@ -0,0 +1,14 @@
version: "3.9"
services:
frontend:
image: awesome/frontend
profiles: ["frontend"]
phpmyadmin:
image: phpmyadmin
profiles:
- debug
db:
image: awesome/db
profiles:
- db
- sql