diff --git a/.github/workflows/cd.yml b/.github/workflows/cd.yml
index 0868300..d8ac70b 100644
--- a/.github/workflows/cd.yml
+++ b/.github/workflows/cd.yml
@@ -10,8 +10,9 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
-
+
- name: Build and Publish to PyPi
uses: JRubics/poetry-publish@v1.10
with:
- pypi_token: ${{ secrets.PYPI_TOKEN }}
\ No newline at end of file
+ pypi_token: ${{ secrets.PYPI_TOKEN }}
+ extra_build_dependency_packages: "graphviz"
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index c40e9d4..1577053 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -12,81 +12,31 @@ jobs:
steps:
# Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it
- uses: actions/checkout@v3
-
+
- name: Switch to Current Branch
run: git checkout ${{ env.BRANCH }}
-
+
+ - run: |
+ sudo apt-get update
+ 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
uses: actions/setup-python@v3
with:
python-version: '3.10.4'
-
- - name: Validate Test Files
- run: |
- docker-compose -f tests/in/000001.yaml config -q
- docker-compose -f tests/in/000010.yaml config -q
- docker-compose -f tests/in/000011.yaml config -q
- docker-compose -f tests/in/000100.yaml config -q
- docker-compose -f tests/in/000101.yaml config -q
- docker-compose -f tests/in/000110.yaml config -q
- docker-compose -f tests/in/000111.yaml config -q
- docker-compose -f tests/in/001000.yaml config -q
- docker-compose -f tests/in/001001.yaml config -q
- docker-compose -f tests/in/001010.yaml config -q
- docker-compose -f tests/in/001011.yaml config -q
- docker-compose -f tests/in/001100.yaml config -q
- docker-compose -f tests/in/001101.yaml config -q
- docker-compose -f tests/in/001110.yaml config -q
- docker-compose -f tests/in/001111.yaml config -q
- docker-compose -f tests/in/010000.yaml config -q
- docker-compose -f tests/in/010001.yaml config -q
- docker-compose -f tests/in/010010.yaml config -q
- docker-compose -f tests/in/010011.yaml config -q
- docker-compose -f tests/in/010100.yaml config -q
- docker-compose -f tests/in/010101.yaml config -q
- docker-compose -f tests/in/010110.yaml config -q
- docker-compose -f tests/in/010111.yaml config -q
- docker-compose -f tests/in/011000.yaml config -q
- docker-compose -f tests/in/011001.yaml config -q
- docker-compose -f tests/in/011010.yaml config -q
- docker-compose -f tests/in/011011.yaml config -q
- docker-compose -f tests/in/011100.yaml config -q
- docker-compose -f tests/in/011101.yaml config -q
- docker-compose -f tests/in/011110.yaml config -q
- docker-compose -f tests/in/011111.yaml config -q
- docker-compose -f tests/in/100000.yaml config -q
- docker-compose -f tests/in/100001.yaml config -q
- docker-compose -f tests/in/100010.yaml config -q
- docker-compose -f tests/in/100011.yaml config -q
- docker-compose -f tests/in/100100.yaml config -q
- docker-compose -f tests/in/100101.yaml config -q
- docker-compose -f tests/in/100110.yaml config -q
- docker-compose -f tests/in/100111.yaml config -q
- docker-compose -f tests/in/101000.yaml config -q
- docker-compose -f tests/in/101001.yaml config -q
- docker-compose -f tests/in/101010.yaml config -q
- docker-compose -f tests/in/101011.yaml config -q
- docker-compose -f tests/in/101100.yaml config -q
- docker-compose -f tests/in/101101.yaml config -q
- docker-compose -f tests/in/101110.yaml config -q
- docker-compose -f tests/in/101111.yaml config -q
- docker-compose -f tests/in/110000.yaml config -q
- docker-compose -f tests/in/110001.yaml config -q
- docker-compose -f tests/in/110010.yaml config -q
- docker-compose -f tests/in/110011.yaml config -q
- docker-compose -f tests/in/110100.yaml config -q
- docker-compose -f tests/in/110101.yaml config -q
- docker-compose -f tests/in/110110.yaml config -q
- docker-compose -f tests/in/110111.yaml config -q
- docker-compose -f tests/in/111000.yaml config -q
- docker-compose -f tests/in/111001.yaml config -q
- docker-compose -f tests/in/111010.yaml config -q
- docker-compose -f tests/in/111011.yaml config -q
- docker-compose -f tests/in/111100.yaml config -q
- docker-compose -f tests/in/111101.yaml config -q
- docker-compose -f tests/in/111110.yaml config -q
- docker-compose -f tests/in/111111.yaml config -q
-
+
- name: Setup Poetry
uses: Gr1N/setup-poetry@v7
with:
@@ -96,6 +46,10 @@ jobs:
run: |
poetry install --no-root
- - name: Validate Custom Input File
+ - name: Execute pre-commit
run: |
- poetry run python -m pytest
+ poetry run python -m pre_commit run --all-files --show-diff-on-failure
+
+ - name: Run Pytest
+ run: |
+ poetry run python -m pytest --cov=compose_viz tests/ --tb=short
diff --git a/.github/workflows/release-tagged-version.yml b/.github/workflows/release-tagged-version.yml
index 3c6d547..f2ce249 100644
--- a/.github/workflows/release-tagged-version.yml
+++ b/.github/workflows/release-tagged-version.yml
@@ -18,15 +18,19 @@ jobs:
steps:
# Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it
- uses: actions/checkout@v3
-
+
- name: Switch to Current Branch
run: git checkout ${{ env.BRANCH }}
-
+
+ - run: |
+ sudo apt-get update
+ sudo apt-get install -y graphviz
+
- name: Setup Python 3.10.4
uses: actions/setup-python@v3
with:
python-version: '3.10.4'
-
+
- name: Setup Poetry
uses: Gr1N/setup-poetry@v7
with:
@@ -34,7 +38,7 @@ jobs:
- run: |
poetry install --no-root
poetry build
-
+
- name: "Release Tagged Version"
uses: "marvinpinto/action-automatic-releases@latest"
with:
@@ -42,4 +46,4 @@ jobs:
prerelease: false
files: |
LICENSE
- dist/**
\ No newline at end of file
+ dist/**
diff --git a/.gitignore b/.gitignore
index fa94aa8..1fdfab5 100644
--- a/.gitignore
+++ b/.gitignore
@@ -158,4 +158,7 @@ cython_debug/
# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
# and can be added to the global gitignore or merged into this file. For a more nuclear
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
-#.idea/
\ No newline at end of file
+#.idea/
+
+*.png
+!examples/**/*.png
diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
new file mode 100644
index 0000000..9b26d29
--- /dev/null
+++ b/.pre-commit-config.yaml
@@ -0,0 +1,38 @@
+exclude: |
+ (?x)^(
+ README.md|
+ LICENSE|
+ tests/ymls/others/
+ )
+repos:
+ - repo: https://github.com/pre-commit/pre-commit-hooks
+ rev: v4.2.0
+ hooks:
+ - id: check-yaml
+ - id: end-of-file-fixer
+ - id: trailing-whitespace
+ - repo: https://github.com/pycqa/flake8
+ rev: 4.0.1
+ hooks:
+ - id: flake8
+ args:
+ - "--max-line-length=120"
+ - repo: https://github.com/pycqa/isort
+ rev: 5.10.1
+ hooks:
+ - id: isort
+ - repo: https://github.com/psf/black
+ rev: 22.3.0
+ hooks:
+ - id: black
+ args: # arguments to configure black
+ - --line-length=120
+ - repo: local
+ hooks:
+ - id: pyright
+ name: pyright
+ entry: pyright
+ language: node
+ pass_filenames: false
+ types: [python]
+ additional_dependencies: ['pyright@1.1.247']
diff --git a/README.md b/README.md
index 060e486..e09d13d 100644
--- a/README.md
+++ b/README.md
@@ -38,6 +38,8 @@
Getting Started
+ - Prerequisities
+ - Installation
- Usage
- Options
- Example
@@ -54,7 +56,9 @@
## About The Project
-`compose-viz` is a [docker-compose](https://github.com/docker/compose)/[podman-compose](https://github.com/containers/podman-compose) graph visualization tool that allows you to gernerate graph in [DOT](https://graphviz.org/doc/info/lang.html) format or `.png`.
+`compose-viz` is a compose file visualization tool that supports [compose-spec](https://github.com/compose-spec/compose-spec/blob/master/spec.md) and allows you to gernerate graph in [DOT](https://graphviz.org/doc/info/lang.html) format or `.png`.
+
+If you are looking for a compose file vizualization tool, and you are using one of the [compose-spec](https://github.com/compose-spec/compose-spec/blob/master/spec.md) implementations (e.g. [docker-compose](https://github.com/docker/compose)/[podman-compose](https://github.com/containers/podman-compose)), then `compose-viz` is a great choice for you.
(back to top)
@@ -62,20 +66,40 @@
## Getting Started
+### Prerequisities
+
+#### Graphviz
+
+If you want to generate PNG (which is the default option), you need to install [Graphviz](https://graphviz.org/download/).
+
+### Installation
+
+#### Using `pip`
+
+`pip install compose-viz`
+
+#### Using `.whl`
+
+See [releases](https://github.com/compose-viz/compose-viz/releases).
+
### Usage
-`python3 compose-viz.py [OPTIONS] [input-file]`
+`cpv [OPTIONS] INPUT_PATH`
### Options
-| Option | Necessity | Description | Default Value |
-| ----------------------------- | --------- | ----------------- | --------------- |
-| `-o --output-file` | Optional | Output file path. | `./compose.png` |
-| `-m --output-format=DOT, PNG` | Optional | Output format. | PNG |
+| Option | Description |
+| ------------------------ | ------------------------------------------------------------------------------ |
+| `-o, --output-path` | Output path for the generated visualization file. [default: ./compose-viz.png] |
+| `-m, --format [PNG,DOT]` | Output format for the generated visualization file. [default: PNG] |
+| `-v, --version` | Show the version of compose-viz. |
+| `--help` | Show help and exit. |
### Example
-`python3 compose-viz.py docker-compose.yaml`
+`cpv -o .\examples\full-stack-node-app\compose-viz.png .\examples\full-stack-node-app\docker-compose.yml`
+
+[Here](https://github.com/compose-viz/compose-viz/blob/main/examples/full-stack-node-app/compose-viz.png) is the result.
(back to top)
@@ -83,7 +107,7 @@
## Roadmap
-- [ ] Support [podman-compose](https://github.com/containers/podman-compose).
+- [ ] Support more vizualization components.
See the [open issues](https://github.com/compose-viz/compose-viz/issues)
for a full list of proposed features (and known issues).
diff --git a/compose_viz/__main__.py b/compose_viz/__main__.py
index 4065f3b..8ade738 100644
--- a/compose_viz/__main__.py
+++ b/compose_viz/__main__.py
@@ -1,5 +1,4 @@
from compose_viz.cli import start_cli
-
if __name__ == "__main__":
start_cli()
diff --git a/compose_viz/cli.py b/compose_viz/cli.py
index d2bf1d3..e028d70 100644
--- a/compose_viz/cli.py
+++ b/compose_viz/cli.py
@@ -1,7 +1,10 @@
from enum import Enum
-import typer
from typing import Optional
+
+import typer
+
from compose_viz import __app_name__, __version__
+from compose_viz.graph import Graph
from compose_viz.parser import Parser
@@ -27,26 +30,26 @@ def _version_callback(value: bool) -> None:
@app.callback()
def compose_viz(
input_path: str,
- output_path: Optional[str] = typer.Option(
- None,
- "--output_path",
+ output_path: str = typer.Option(
+ "./compose-viz.png",
+ "--output-path",
"-o",
- help="Output path for the generated visualization.",
+ help="Output path for the generated visualization file.",
),
format: VisualizationFormats = typer.Option(
"PNG",
"--format",
"-m",
- help="Output format for the generated visualization.",
+ help="Output format for the generated visualization file.",
),
_: Optional[bool] = typer.Option(
None,
"--version",
"-v",
- help="Show the version of compose_viz.",
+ help="Show the version of compose-viz.",
callback=_version_callback,
is_eager=True,
- )
+ ),
) -> None:
parser = Parser()
compose = parser.parse(input_path)
@@ -54,8 +57,10 @@ def compose_viz(
if compose:
typer.echo(f"Successfully parsed {input_path}")
+ Graph(compose, output_path).render(format)
+
raise typer.Exit()
def start_cli() -> None:
- app(prog_name=__app_name__)
+ app(prog_name="cpv")
diff --git a/compose_viz/compose.py b/compose_viz/compose.py
index 6c65a8a..03e8f74 100644
--- a/compose_viz/compose.py
+++ b/compose_viz/compose.py
@@ -1,10 +1,12 @@
from typing import List
+
from compose_viz.service import Service
class Compose:
def __init__(self, services: List[Service]) -> None:
- self.services = services
+ self._services = services
- def extract_networks(self) -> List[str]:
- raise NotImplementedError
+ @property
+ def services(self):
+ return self._services
diff --git a/compose_viz/extends.py b/compose_viz/extends.py
new file mode 100644
index 0000000..9948aa0
--- /dev/null
+++ b/compose_viz/extends.py
@@ -0,0 +1,15 @@
+from typing import Optional
+
+
+class Extends:
+ def __init__(self, service_name: str, from_file: Optional[str] = None):
+ self._service_name = service_name
+ self._from_file = from_file
+
+ @property
+ def service_name(self):
+ return self._service_name
+
+ @property
+ def from_file(self):
+ return self._from_file
diff --git a/compose_viz/graph.py b/compose_viz/graph.py
new file mode 100644
index 0000000..15516ca
--- /dev/null
+++ b/compose_viz/graph.py
@@ -0,0 +1,92 @@
+from typing import Optional
+
+import graphviz
+
+from compose_viz.compose import Compose
+
+
+def apply_vertex_style(type) -> dict:
+ style = {
+ "service": {
+ "shape": "component",
+ },
+ "volume": {
+ "shape": "folder",
+ },
+ "network": {
+ "shape": "pentagon",
+ },
+ "port": {
+ "shape": "circle",
+ },
+ }
+
+ return style[type]
+
+
+def apply_edge_style(type) -> dict:
+ style = {
+ "ports": {
+ "style": "solid",
+ "dir": "both",
+ },
+ "links": {
+ "style": "solid",
+ },
+ "volumes": {
+ "style": "dashed",
+ "dir": "both",
+ },
+ "depends_on": {
+ "style": "dotted",
+ },
+ "extends": {
+ "dir": "both",
+ "arrowhead": "inv",
+ "arrowtail": "dot",
+ },
+ }
+
+ return style[type]
+
+
+class Graph:
+ def __init__(self, compose: Compose, filename: str) -> None:
+ self.dot = graphviz.Digraph()
+ self.dot.attr("graph", background="#ffffff", pad="0.5", ratio="fill")
+ self.compose = compose
+ self.filename = filename
+
+ def validate_name(self, name: str) -> str:
+ # graphviz does not allow ':' in node name
+ transTable = name.maketrans({":": ""})
+ return name.translate(transTable)
+
+ def add_vertex(self, name: str, type: str, lable: Optional[str] = None) -> None:
+ self.dot.node(self.validate_name(name), lable, **apply_vertex_style(type))
+
+ def add_edge(self, head: str, tail: str, type: str, lable: Optional[str] = None) -> None:
+ self.dot.edge(self.validate_name(head), self.validate_name(tail), lable, **apply_edge_style(type))
+
+ def render(self, format: str, cleanup: bool = True) -> None:
+ for service in self.compose.services:
+ if service.image is not None:
+ self.add_vertex(service.name, "service", lable=f"{service.name}\n({service.image})")
+ if service.extends is not None:
+ self.add_vertex(service.name, "service", lable=f"{service.name}\n")
+ self.add_edge(service.extends.service_name, service.name, "extends")
+ for network in service.networks:
+ self.add_vertex(network, "network", lable=f"net:{network}")
+ self.add_edge(service.name, network, "links")
+ for volume in service.volumes:
+ self.add_vertex(volume.source, "volume")
+ self.add_edge(service.name, volume.source, "volumes", lable=volume.target)
+ for port in service.ports:
+ self.add_vertex(port.host_port, "port", lable=port.host_port)
+ self.add_edge(port.host_port, service.name, "ports", lable=port.container_port)
+ for link in service.links:
+ self.add_edge(link.split(":")[0], service.name, "links", link.split(":")[1])
+ for depends_on in service.depends_on:
+ self.add_edge(service.name, depends_on, "depends_on")
+
+ self.dot.render(outfile=self.filename, format=format, cleanup=cleanup)
diff --git a/compose_viz/parser.py b/compose_viz/parser.py
index 1f44bbc..757d3ed 100644
--- a/compose_viz/parser.py
+++ b/compose_viz/parser.py
@@ -1,4 +1,12 @@
-from compose_viz.compose import Compose
+import re
+from typing import Dict, List, Optional
+
+from ruamel.yaml import YAML
+
+from compose_viz.compose import Compose, Service
+from compose_viz.extends import Extends
+from compose_viz.port import Port, Protocol
+from compose_viz.volume import Volume, VolumeType
class Parser:
@@ -6,5 +14,216 @@ class Parser:
pass
def parse(self, file_path: str) -> Compose:
- # validate input file using `docker-compose config -q sys.argv[1]` first
- raise NotImplementedError
+ # load the yaml file
+ with open(file_path, "r") as f:
+ try:
+ yaml = YAML(typ="safe", pure=True)
+ yaml_data = yaml.load(f)
+ except Exception as e:
+ raise RuntimeError(f"Error parsing file '{file_path}': {e}")
+ # validate the yaml file
+ if not yaml_data:
+ raise RuntimeError("Empty yaml file, aborting.")
+ if not yaml_data.get("services"):
+ raise RuntimeError("No services found, aborting.")
+
+ # parse services data into Service objects
+ services = self.parse_service_data(yaml_data["services"])
+
+ # create Compose object
+ compose = Compose(services)
+
+ return compose
+
+ def parse_service_data(self, services_yaml_data: Dict[str, dict]) -> List[Service]:
+ services: List[Service] = []
+ for service, service_name in zip(services_yaml_data.values(), services_yaml_data.keys()):
+
+ service_image: Optional[str] = None
+ if service.get("build"):
+ if type(service["build"]) is str:
+ service_image = f"build from '{service['build']}'"
+ elif type(service["build"]) is dict:
+ if service["build"].get("context") and service["build"].get("dockerfile"):
+ service_image = (
+ f"build from '{service['build']['context']}' using '{service['build']['dockerfile']}'"
+ )
+ elif service["build"].get("context"):
+ service_image = f"build from '{service['build']['context']}'"
+ if service.get("image"):
+ if service_image:
+ service_image += ", image: " + service["image"]
+ else:
+ service_image = service["image"]
+
+ service_networks: List[str] = []
+ if service.get("networks"):
+ if type(service["networks"]) is list:
+ service_networks = service["networks"]
+ elif type(service["networks"]) is dict:
+ service_networks = list(service["networks"].keys())
+
+ service_extends: Optional[Extends] = None
+ if service.get("extends"):
+ assert type(service["extends"]) is dict, "Invalid extends format, aborting."
+ assert service["extends"]["service"], "Missing extends service, aborting."
+ extend_service_name = str(service["extends"]["service"])
+
+ extend_from_file: Optional[str] = None
+ if service["extends"].get("file"):
+ assert service["extends"]["file"], "Missing extends file, aborting."
+ extend_from_file = str(service["extends"]["file"])
+
+ service_extends = Extends(service_name=extend_service_name, from_file=extend_from_file)
+
+ service_ports: List[Port] = []
+ if service.get("ports"):
+ assert type(service["ports"]) is list
+ for port_data in service["ports"]:
+ if type(port_data) is dict:
+ # define a nested function to limit variable scope
+ def long_syntax():
+ assert type(port_data) is dict
+ assert port_data["target"]
+
+ container_port: str = str(port_data["target"])
+ host_port: str = ""
+ protocol: Protocol = Protocol.any
+
+ if port_data.get("published"):
+ host_port = str(port_data["published"])
+ else:
+ host_port = container_port
+
+ if port_data.get("host_ip"):
+ host_ip = str(port_data["host_ip"])
+ host_port = f"{host_ip}:{host_port}"
+ else:
+ host_port = f"0.0.0.0:{host_port}"
+
+ if port_data.get("protocol"):
+ protocol = Protocol[str(port_data["protocol"])]
+
+ assert host_port, "Error parsing port, aborting."
+
+ service_ports.append(
+ Port(
+ host_port=host_port,
+ container_port=container_port,
+ protocol=protocol,
+ )
+ )
+
+ long_syntax()
+ elif type(port_data) is str:
+ # ports that needs to parse using regex:
+ # - "3000"
+ # - "3000-3005"
+ # - "8000:8000"
+ # - "9090-9091:8080-8081"
+ # - "49100:22"
+ # - "127.0.0.1:8001:8001"
+ # - "127.0.0.1:5000-5010:5000-5010"
+ # - "6060:6060/udp"
+
+ def short_syntax():
+ assert type(port_data) is str
+ regex = r"(?P\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}:)?((?P\d+(\-\d+)?):)?((?P\d+(\-\d+)?))?(/(?P\w+))?" # noqa: E501
+ match = re.match(regex, port_data)
+ if match:
+ host_ip: Optional[str] = match.group("host_ip")
+ host_port: Optional[str] = match.group("host_port")
+ container_port: Optional[str] = match.group("container_port")
+ protocol: Optional[str] = match.group("protocol")
+
+ assert container_port, "Invalid port format, aborting."
+
+ if container_port and not host_port:
+ host_port = container_port
+
+ if host_ip:
+ host_port = f"{host_ip}{host_port}"
+ else:
+ host_port = f"0.0.0.0:{host_port}"
+
+ assert host_port, "Error while parsing port, aborting."
+
+ if protocol:
+ service_ports.append(
+ Port(
+ host_port=host_port,
+ container_port=container_port,
+ protocol=Protocol[protocol],
+ )
+ )
+ else:
+ service_ports.append(
+ Port(
+ host_port=host_port,
+ container_port=container_port,
+ )
+ )
+
+ short_syntax()
+
+ service_depends_on: List[str] = []
+ if service.get("depends_on"):
+ if type(service["depends_on"]) is list:
+ for depends_on in service["depends_on"]:
+ service_depends_on.append(str(depends_on))
+ elif type(service["depends_on"]) is dict:
+ service_depends_on = list(service["depends_on"].keys())
+
+ service_volumes: List[Volume] = []
+ if service.get("volumes"):
+ assert type(service["volumes"]) is list
+ for volume_data in service["volumes"]:
+ if type(volume_data) is dict:
+ assert volume_data["source"] and volume_data["target"], "Invalid volume input, aborting."
+
+ volume_source: str = str(volume_data["source"])
+ volume_target: str = str(volume_data["target"])
+ volume_type: VolumeType = VolumeType.volume
+
+ if volume_data.get("type"):
+ volume_type = VolumeType[str(volume_data["type"])]
+
+ service_volumes.append(Volume(source=volume_source, target=volume_target, type=volume_type))
+ elif type(volume_data) is str:
+ assert ":" in volume_data, "Invalid volume input, aborting."
+
+ spilt_data = volume_data.split(":")
+ if len(spilt_data) == 2:
+ service_volumes.append(Volume(source=spilt_data[0], target=spilt_data[1]))
+ elif len(spilt_data) == 3:
+ service_volumes.append(
+ Volume(source=spilt_data[0], target=spilt_data[1], access_mode=spilt_data[2])
+ )
+
+ service_links: List[str] = []
+ if service.get("links"):
+ service_links = service["links"]
+
+ services.append(
+ Service(
+ name=service_name,
+ image=service_image,
+ networks=service_networks,
+ extends=service_extends,
+ ports=service_ports,
+ depends_on=service_depends_on,
+ volumes=service_volumes,
+ links=service_links,
+ )
+ )
+ # Service print debug
+ # print("--------------------")
+ # print("Service name: {}".format(service_name))
+ # print("image: {}".format(service_image))
+ # print("networks: {}".format(service_networks))
+ # print("image: {}".format(service_image))
+ # print("extends: {}".format(service_extends))
+ # print("ports: {}".format(service_ports))
+ # print("depends: {}".format(service_depends_on))
+
+ return services
diff --git a/compose_viz/port.py b/compose_viz/port.py
new file mode 100644
index 0000000..d64fb5c
--- /dev/null
+++ b/compose_viz/port.py
@@ -0,0 +1,26 @@
+from enum import Enum
+
+
+class Protocol(str, Enum):
+ tcp = "tcp"
+ udp = "udp"
+ any = "any"
+
+
+class Port:
+ def __init__(self, host_port: str, container_port: str, protocol: Protocol = Protocol.any):
+ self._host_port = host_port
+ self._container_port = container_port
+ self._protocol = protocol
+
+ @property
+ def host_port(self):
+ return self._host_port
+
+ @property
+ def container_port(self):
+ return self._container_port
+
+ @property
+ def protocol(self):
+ return self._protocol
diff --git a/compose_viz/service.py b/compose_viz/service.py
index 6e42483..b1bf84b 100644
--- a/compose_viz/service.py
+++ b/compose_viz/service.py
@@ -1,13 +1,59 @@
-from typing import List
+from typing import List, Optional
+
+from compose_viz.extends import Extends
+from compose_viz.port import Port
+from compose_viz.volume import Volume
class Service:
- def __init__(self, name: str, image: str, ports: List[str] = [], networks: List[str] = [], volumes: List[str] = [], depends_on: List[str] = [], links: List[str] = [], extends: List[str] = []) -> None:
- self.name = name
- self.image = image
- self.ports = ports
- self.networks = networks
- self.volumes = volumes
- self.depends_on = depends_on
- self.links = links
- self.extends = extends
+ def __init__(
+ self,
+ name: str,
+ image: Optional[str] = None,
+ ports: List[Port] = [],
+ networks: List[str] = [],
+ volumes: List[Volume] = [],
+ depends_on: List[str] = [],
+ links: List[str] = [],
+ extends: Optional[Extends] = None,
+ ) -> None:
+ self._name = name
+ self._image = image
+ self._ports = ports
+ self._networks = networks
+ self._volumes = volumes
+ self._depends_on = depends_on
+ self._links = links
+ self._extends = extends
+
+ @property
+ def name(self):
+ return self._name
+
+ @property
+ def image(self):
+ return self._image
+
+ @property
+ def ports(self):
+ return self._ports
+
+ @property
+ def networks(self):
+ return self._networks
+
+ @property
+ def volumes(self):
+ return self._volumes
+
+ @property
+ def depends_on(self):
+ return self._depends_on
+
+ @property
+ def links(self):
+ return self._links
+
+ @property
+ def extends(self):
+ return self._extends
diff --git a/compose_viz/volume.py b/compose_viz/volume.py
new file mode 100644
index 0000000..192ed97
--- /dev/null
+++ b/compose_viz/volume.py
@@ -0,0 +1,32 @@
+from enum import Enum
+
+
+class VolumeType(str, Enum):
+ volume = "volume"
+ bind = "bind"
+ tmpfs = "tmpfs"
+ npipe = "npipe"
+
+
+class Volume:
+ def __init__(self, source: str, target: str, type: VolumeType = VolumeType.volume, access_mode: str = "rw"):
+ self._source = source
+ self._target = target
+ self._type = type
+ self._access_mode = access_mode
+
+ @property
+ def source(self):
+ return self._source
+
+ @property
+ def target(self):
+ return self._target
+
+ @property
+ def type(self):
+ return self._type
+
+ @property
+ def access_mode(self):
+ return self._access_mode
diff --git a/examples/full-stack-node-app/compose-viz.png b/examples/full-stack-node-app/compose-viz.png
new file mode 100644
index 0000000..1b0976b
Binary files /dev/null and b/examples/full-stack-node-app/compose-viz.png differ
diff --git a/examples/full-stack-node-app/docker-compose.yml b/examples/full-stack-node-app/docker-compose.yml
new file mode 100644
index 0000000..b21887f
--- /dev/null
+++ b/examples/full-stack-node-app/docker-compose.yml
@@ -0,0 +1,87 @@
+version: "3.9"
+
+services:
+ node:
+ build:
+ context: .
+ dockerfile: Dockerfile.node
+
+ api:
+ image: "awesome/api"
+ extends:
+ service: node
+ build:
+ args:
+ PACKAGE_PATH: api
+ WORKING_DIR: /usr/src/
+ expose:
+ - 8000
+ ports:
+ - 8000:8000
+ environment:
+ - NODE_ENV=development
+ volumes:
+ - ./api:/usr/src
+ depends_on:
+ - db
+ - adminer
+ - redis
+ networks:
+ - front-tier
+ - back-tier
+ command: ["npm", "start"]
+
+ frontend:
+ extends:
+ service: node
+ build:
+ args:
+ PACKAGE_PATH: frontend
+ WORKING_DIR: /usr/src/
+ expose:
+ - 3000
+ ports:
+ - 3000:3000
+ volumes:
+ - ./frontend:/usr/src
+ depends_on:
+ - api
+ networks:
+ - front-tier
+ command: ["npm", "start"]
+
+ db:
+ image: "awesome/db"
+ extends:
+ service: postgres
+ file: postgres.yml
+ restart: always
+ networks:
+ - back-tier
+ volumes:
+ - "db-data:/data"
+ - type: bind
+ source: /var/run/postgres/postgres.sock
+ target: /var/run/postgres/postgres.sock
+
+ redis:
+ image: "awesome/redis"
+ restart: always
+ networks:
+ - back-tier
+ expose:
+ - 6379
+
+ adminer:
+ image: "awesome/adminer"
+ networks:
+ - back-tier
+ ports:
+ - 8080:8080
+
+volumes:
+ db-data:
+
+networks:
+ front-tier:
+ back-tier:
diff --git a/examples/full-stack-node-app/postgres.yml b/examples/full-stack-node-app/postgres.yml
new file mode 100644
index 0000000..cb6c45e
--- /dev/null
+++ b/examples/full-stack-node-app/postgres.yml
@@ -0,0 +1,5 @@
+version: "3.9"
+
+services:
+ postgres:
+ image: awesome/postgres
diff --git a/examples/non-normative/compose-viz.png b/examples/non-normative/compose-viz.png
new file mode 100644
index 0000000..805094a
Binary files /dev/null and b/examples/non-normative/compose-viz.png differ
diff --git a/tests/in/docker-compose.yaml b/examples/non-normative/docker-compose.yml
similarity index 95%
rename from tests/in/docker-compose.yaml
rename to examples/non-normative/docker-compose.yml
index 3f94ba7..2ed41b9 100644
--- a/tests/in/docker-compose.yaml
+++ b/examples/non-normative/docker-compose.yml
@@ -9,10 +9,9 @@ services:
image: awesome/monitoring
networks:
- admin
-
+
backend:
- image: awesome/backend
networks:
back-tier:
aliases:
diff --git a/examples/voting-app/compose-viz.png b/examples/voting-app/compose-viz.png
new file mode 100644
index 0000000..64486c2
Binary files /dev/null and b/examples/voting-app/compose-viz.png differ
diff --git a/examples/voting-app/docker-compose.yml b/examples/voting-app/docker-compose.yml
new file mode 100644
index 0000000..be21988
--- /dev/null
+++ b/examples/voting-app/docker-compose.yml
@@ -0,0 +1,89 @@
+# https://github.com/docker/labs/blob/master/beginner/chapters/votingapp.md
+
+version: "3.9"
+
+services:
+ redis:
+ image: redis:alpine
+ ports:
+ - "6379"
+ networks:
+ - frontend
+ deploy:
+ replicas: 2
+ update_config:
+ parallelism: 2
+ delay: 10s
+ restart_policy:
+ condition: on-failure
+ db:
+ image: postgres:9.4
+ volumes:
+ - db-data:/var/lib/postgresql/data
+ networks:
+ - backend
+ deploy:
+ placement:
+ constraints: [node.role == manager]
+ vote:
+ image: dockersamples/examplevotingapp_vote:before
+ ports:
+ - 5000:80
+ networks:
+ - frontend
+ depends_on:
+ - redis
+ deploy:
+ replicas: 2
+ update_config:
+ parallelism: 2
+ restart_policy:
+ condition: on-failure
+ result:
+ image: dockersamples/examplevotingapp_result:before
+ ports:
+ - 5001:80
+ networks:
+ - backend
+ depends_on:
+ - db
+ deploy:
+ replicas: 1
+ update_config:
+ parallelism: 2
+ delay: 10s
+ restart_policy:
+ condition: on-failure
+ worker:
+ image: dockersamples/examplevotingapp_worker
+ networks:
+ - frontend
+ - backend
+ deploy:
+ mode: replicated
+ replicas: 1
+ labels: [APP=VOTING]
+ restart_policy:
+ condition: on-failure
+ delay: 10s
+ max_attempts: 3
+ window: 120s
+ placement:
+ constraints: [node.role == manager]
+ visualizer:
+ image: dockersamples/visualizer
+ ports:
+ - "8080:8080"
+ stop_grace_period: 1m30s
+ volumes:
+ - /var/run/docker.sock:/var/run/docker.sock
+ deploy:
+ placement:
+ constraints: [node.role == manager]
+
+networks:
+ frontend:
+ backend:
+
+volumes:
+ db-data:
diff --git a/poetry.lock b/poetry.lock
index 381ddcb..6043af8 100644
--- a/poetry.lock
+++ b/poetry.lock
@@ -20,6 +20,14 @@ docs = ["furo", "sphinx", "zope.interface", "sphinx-notfound-page"]
tests = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "mypy", "pytest-mypy-plugins", "zope.interface", "cloudpickle"]
tests_no_zope = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "mypy", "pytest-mypy-plugins", "cloudpickle"]
+[[package]]
+name = "cfgv"
+version = "3.3.1"
+description = "Validate configuration and produce human readable error messages."
+category = "dev"
+optional = false
+python-versions = ">=3.6.1"
+
[[package]]
name = "click"
version = "8.1.3"
@@ -39,6 +47,64 @@ category = "main"
optional = false
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
+[[package]]
+name = "coverage"
+version = "6.3.3"
+description = "Code coverage measurement for Python"
+category = "dev"
+optional = false
+python-versions = ">=3.7"
+
+[package.dependencies]
+tomli = {version = "*", optional = true, markers = "extra == \"toml\""}
+
+[package.extras]
+toml = ["tomli"]
+
+[[package]]
+name = "distlib"
+version = "0.3.4"
+description = "Distribution utilities"
+category = "dev"
+optional = false
+python-versions = "*"
+
+[[package]]
+name = "filelock"
+version = "3.7.0"
+description = "A platform independent file lock."
+category = "dev"
+optional = false
+python-versions = ">=3.7"
+
+[package.extras]
+docs = ["furo (>=2021.8.17b43)", "sphinx (>=4.1)", "sphinx-autodoc-typehints (>=1.12)"]
+testing = ["covdefaults (>=1.2.0)", "coverage (>=4)", "pytest (>=4)", "pytest-cov", "pytest-timeout (>=1.4.2)"]
+
+[[package]]
+name = "graphviz"
+version = "0.20"
+description = "Simple Python interface for Graphviz"
+category = "main"
+optional = false
+python-versions = ">=3.7"
+
+[package.extras]
+dev = ["tox (>=3)", "flake8", "pep8-naming", "wheel", "twine"]
+docs = ["sphinx (>=4)", "sphinx-autodoc-typehints", "sphinx-rtd-theme"]
+test = ["pytest (>=7)", "pytest-mock (>=3)", "mock (>=4)", "pytest-cov", "coverage"]
+
+[[package]]
+name = "identify"
+version = "2.5.0"
+description = "File identification library for Python"
+category = "dev"
+optional = false
+python-versions = ">=3.7"
+
+[package.extras]
+license = ["ukkonen"]
+
[[package]]
name = "iniconfig"
version = "1.1.1"
@@ -47,6 +113,14 @@ category = "dev"
optional = false
python-versions = "*"
+[[package]]
+name = "nodeenv"
+version = "1.6.0"
+description = "Node.js virtual environment builder"
+category = "dev"
+optional = false
+python-versions = "*"
+
[[package]]
name = "packaging"
version = "21.3"
@@ -58,6 +132,18 @@ python-versions = ">=3.6"
[package.dependencies]
pyparsing = ">=2.0.2,<3.0.5 || >3.0.5"
+[[package]]
+name = "platformdirs"
+version = "2.5.2"
+description = "A small Python module for determining appropriate platform-specific dirs, e.g. a \"user data dir\"."
+category = "dev"
+optional = false
+python-versions = ">=3.7"
+
+[package.extras]
+docs = ["furo (>=2021.7.5b38)", "proselint (>=0.10.2)", "sphinx-autodoc-typehints (>=1.12)", "sphinx (>=4)"]
+test = ["appdirs (==1.4.4)", "pytest-cov (>=2.7)", "pytest-mock (>=3.6)", "pytest (>=6)"]
+
[[package]]
name = "pluggy"
version = "1.0.0"
@@ -70,6 +156,22 @@ python-versions = ">=3.6"
dev = ["pre-commit", "tox"]
testing = ["pytest", "pytest-benchmark"]
+[[package]]
+name = "pre-commit"
+version = "2.19.0"
+description = "A framework for managing and maintaining multi-language pre-commit hooks."
+category = "dev"
+optional = false
+python-versions = ">=3.7"
+
+[package.dependencies]
+cfgv = ">=2.0.0"
+identify = ">=1.0.0"
+nodeenv = ">=0.11.1"
+pyyaml = ">=5.1"
+toml = "*"
+virtualenv = ">=20.0.8"
+
[[package]]
name = "py"
version = "1.11.0"
@@ -110,6 +212,68 @@ tomli = ">=1.0.0"
[package.extras]
testing = ["argcomplete", "hypothesis (>=3.56)", "mock", "nose", "pygments (>=2.7.2)", "requests", "xmlschema"]
+[[package]]
+name = "pytest-cov"
+version = "3.0.0"
+description = "Pytest plugin for measuring coverage."
+category = "dev"
+optional = false
+python-versions = ">=3.6"
+
+[package.dependencies]
+coverage = {version = ">=5.2.1", extras = ["toml"]}
+pytest = ">=4.6"
+
+[package.extras]
+testing = ["fields", "hunter", "process-tests", "six", "pytest-xdist", "virtualenv"]
+
+[[package]]
+name = "pyyaml"
+version = "6.0"
+description = "YAML parser and emitter for Python"
+category = "main"
+optional = false
+python-versions = ">=3.6"
+
+[[package]]
+name = "ruamel.yaml"
+version = "0.17.21"
+description = "ruamel.yaml is a YAML parser/emitter that supports roundtrip preservation of comments, seq/map flow style, and map key order"
+category = "main"
+optional = false
+python-versions = ">=3"
+
+[package.dependencies]
+"ruamel.yaml.clib" = {version = ">=0.2.6", markers = "platform_python_implementation == \"CPython\" and python_version < \"3.11\""}
+
+[package.extras]
+docs = ["ryd"]
+jinja2 = ["ruamel.yaml.jinja2 (>=0.2)"]
+
+[[package]]
+name = "ruamel.yaml.clib"
+version = "0.2.6"
+description = "C version of reader, parser and emitter for ruamel.yaml derived from libyaml"
+category = "main"
+optional = false
+python-versions = ">=3.5"
+
+[[package]]
+name = "six"
+version = "1.16.0"
+description = "Python 2 and 3 compatibility utilities"
+category = "dev"
+optional = false
+python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*"
+
+[[package]]
+name = "toml"
+version = "0.10.2"
+description = "Python Library for Tom's Obvious, Minimal Language"
+category = "dev"
+optional = false
+python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*"
+
[[package]]
name = "tomli"
version = "2.0.1"
@@ -135,10 +299,28 @@ dev = ["autoflake (>=1.3.1,<2.0.0)", "flake8 (>=3.8.3,<4.0.0)"]
doc = ["mkdocs (>=1.1.2,<2.0.0)", "mkdocs-material (>=8.1.4,<9.0.0)", "mdx-include (>=1.4.1,<2.0.0)"]
test = ["shellingham (>=1.3.0,<2.0.0)", "pytest (>=4.4.0,<5.4.0)", "pytest-cov (>=2.10.0,<3.0.0)", "coverage (>=5.2,<6.0)", "pytest-xdist (>=1.32.0,<2.0.0)", "pytest-sugar (>=0.9.4,<0.10.0)", "mypy (==0.910)", "black (>=22.3.0,<23.0.0)", "isort (>=5.0.6,<6.0.0)"]
+[[package]]
+name = "virtualenv"
+version = "20.14.1"
+description = "Virtual Python Environment builder"
+category = "dev"
+optional = false
+python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,>=2.7"
+
+[package.dependencies]
+distlib = ">=0.3.1,<1"
+filelock = ">=3.2,<4"
+platformdirs = ">=2,<3"
+six = ">=1.9.0,<2"
+
+[package.extras]
+docs = ["proselint (>=0.10.2)", "sphinx (>=3)", "sphinx-argparse (>=0.2.5)", "sphinx-rtd-theme (>=0.4.3)", "towncrier (>=21.3)"]
+testing = ["coverage (>=4)", "coverage-enable-subprocess (>=1)", "flaky (>=3)", "pytest (>=4)", "pytest-env (>=0.6.2)", "pytest-freezegun (>=0.4.1)", "pytest-mock (>=2)", "pytest-randomly (>=1)", "pytest-timeout (>=1)", "packaging (>=20.0)"]
+
[metadata]
lock-version = "1.1"
python-versions = "^3.9"
-content-hash = "a80ea7abd86b8e5579a192dfa02a55d2219a3a1850bad12da89c30aa42e99156"
+content-hash = "e1b68a4c83f398e841e6f38823ea18f3cb27b0b251689d0398ae39c03ddc4a47"
[metadata.files]
atomicwrites = [
@@ -149,6 +331,10 @@ attrs = [
{file = "attrs-21.4.0-py2.py3-none-any.whl", hash = "sha256:2d27e3784d7a565d36ab851fe94887c5eccd6a463168875832a1be79c82828b4"},
{file = "attrs-21.4.0.tar.gz", hash = "sha256:626ba8234211db98e869df76230a137c4c40a12d72445c45d5f5b716f076e2fd"},
]
+cfgv = [
+ {file = "cfgv-3.3.1-py2.py3-none-any.whl", hash = "sha256:c6a0883f3917a037485059700b9e75da2464e6c27051014ad85ba6aaa5884426"},
+ {file = "cfgv-3.3.1.tar.gz", hash = "sha256:f5a830efb9ce7a445376bb66ec94c638a9787422f96264c98edc6bdeed8ab736"},
+]
click = [
{file = "click-8.1.3-py3-none-any.whl", hash = "sha256:bb4d8133cb15a609f44e8213d9b391b0809795062913b383c62be0ee95b1db48"},
{file = "click-8.1.3.tar.gz", hash = "sha256:7682dc8afb30297001674575ea00d1814d808d6a36af415a82bd481d37ba7b8e"},
@@ -157,18 +343,89 @@ colorama = [
{file = "colorama-0.4.4-py2.py3-none-any.whl", hash = "sha256:9f47eda37229f68eee03b24b9748937c7dc3868f906e8ba69fbcbdd3bc5dc3e2"},
{file = "colorama-0.4.4.tar.gz", hash = "sha256:5941b2b48a20143d2267e95b1c2a7603ce057ee39fd88e7329b0c292aa16869b"},
]
+coverage = [
+ {file = "coverage-6.3.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:df32ee0f4935a101e4b9a5f07b617d884a531ed5666671ff6ac66d2e8e8246d8"},
+ {file = "coverage-6.3.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:75b5dbffc334e0beb4f6c503fb95e6d422770fd2d1b40a64898ea26d6c02742d"},
+ {file = "coverage-6.3.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:114944e6061b68a801c5da5427b9173a0dd9d32cd5fcc18a13de90352843737d"},
+ {file = "coverage-6.3.3-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2ab88a01cd180b5640ccc9c47232e31924d5f9967ab7edd7e5c91c68eee47a69"},
+ {file = "coverage-6.3.3-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ad8f9068f5972a46d50fe5f32c09d6ee11da69c560fcb1b4c3baea246ca4109b"},
+ {file = "coverage-6.3.3-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:4cd696aa712e6cd16898d63cf66139dc70d998f8121ab558f0e1936396dbc579"},
+ {file = "coverage-6.3.3-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:c1a9942e282cc9d3ed522cd3e3cab081149b27ea3bda72d6f61f84eaf88c1a63"},
+ {file = "coverage-6.3.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:c06455121a089252b5943ea682187a4e0a5cf0a3fb980eb8e7ce394b144430a9"},
+ {file = "coverage-6.3.3-cp310-cp310-win32.whl", hash = "sha256:cb5311d6ccbd22578c80028c5e292a7ab9adb91bd62c1982087fad75abe2e63d"},
+ {file = "coverage-6.3.3-cp310-cp310-win_amd64.whl", hash = "sha256:6d4a6f30f611e657495cc81a07ff7aa8cd949144e7667c5d3e680d73ba7a70e4"},
+ {file = "coverage-6.3.3-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:79bf405432428e989cad7b8bc60581963238f7645ae8a404f5dce90236cc0293"},
+ {file = "coverage-6.3.3-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:338c417613f15596af9eb7a39353b60abec9d8ce1080aedba5ecee6a5d85f8d3"},
+ {file = "coverage-6.3.3-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:db094a6a4ae6329ed322a8973f83630b12715654c197dd392410400a5bfa1a73"},
+ {file = "coverage-6.3.3-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1414e8b124611bf4df8d77215bd32cba6e3425da8ce9c1f1046149615e3a9a31"},
+ {file = "coverage-6.3.3-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:93b16b08f94c92cab88073ffd185070cdcb29f1b98df8b28e6649145b7f2c90d"},
+ {file = "coverage-6.3.3-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:fbc86ae8cc129c801e7baaafe3addf3c8d49c9c1597c44bdf2d78139707c3c62"},
+ {file = "coverage-6.3.3-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:b5ba058610e8289a07db2a57bce45a1793ec0d3d11db28c047aae2aa1a832572"},
+ {file = "coverage-6.3.3-cp37-cp37m-win32.whl", hash = "sha256:8329635c0781927a2c6ae068461e19674c564e05b86736ab8eb29c420ee7dc20"},
+ {file = "coverage-6.3.3-cp37-cp37m-win_amd64.whl", hash = "sha256:e5af1feee71099ae2e3b086ec04f57f9950e1be9ecf6c420696fea7977b84738"},
+ {file = "coverage-6.3.3-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:e814a4a5a1d95223b08cdb0f4f57029e8eab22ffdbae2f97107aeef28554517e"},
+ {file = "coverage-6.3.3-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:61f4fbf3633cb0713437291b8848634ea97f89c7e849c2be17a665611e433f53"},
+ {file = "coverage-6.3.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3401b0d2ed9f726fadbfa35102e00d1b3547b73772a1de5508ef3bdbcb36afe7"},
+ {file = "coverage-6.3.3-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8586b177b4407f988731eb7f41967415b2197f35e2a6ee1a9b9b561f6323c8e9"},
+ {file = "coverage-6.3.3-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:892e7fe32191960da559a14536768a62e83e87bbb867e1b9c643e7e0fbce2579"},
+ {file = "coverage-6.3.3-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:afb03f981fadb5aed1ac6e3dd34f0488e1a0875623d557b6fad09b97a942b38a"},
+ {file = "coverage-6.3.3-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:cbe91bc84be4e5ef0b1480d15c7b18e29c73bdfa33e07d3725da7d18e1b0aff2"},
+ {file = "coverage-6.3.3-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:91502bf27cbd5c83c95cfea291ef387469f2387508645602e1ca0fd8a4ba7548"},
+ {file = "coverage-6.3.3-cp38-cp38-win32.whl", hash = "sha256:c488db059848702aff30aa1d90ef87928d4e72e4f00717343800546fdbff0a94"},
+ {file = "coverage-6.3.3-cp38-cp38-win_amd64.whl", hash = "sha256:ceb6534fcdfb5c503affb6b1130db7b5bfc8a0f77fa34880146f7a5c117987d0"},
+ {file = "coverage-6.3.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:cc692c9ee18f0dd3214843779ba6b275ee4bb9b9a5745ba64265bce911aefd1a"},
+ {file = "coverage-6.3.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:462105283de203df8de58a68c1bb4ba2a8a164097c2379f664fa81d6baf94b81"},
+ {file = "coverage-6.3.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cc972d829ad5ef4d4c5fcabd2bbe2add84ce8236f64ba1c0c72185da3a273130"},
+ {file = "coverage-6.3.3-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:06f54765cdbce99901871d50fe9f41d58213f18e98b170a30ca34f47de7dd5e8"},
+ {file = "coverage-6.3.3-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7835f76a081787f0ca62a53504361b3869840a1620049b56d803a8cb3a9eeea3"},
+ {file = "coverage-6.3.3-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:6f5fee77ec3384b934797f1873758f796dfb4f167e1296dc00f8b2e023ce6ee9"},
+ {file = "coverage-6.3.3-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:baa8be8aba3dd1e976e68677be68a960a633a6d44c325757aefaa4d66175050f"},
+ {file = "coverage-6.3.3-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:4d06380e777dd6b35ee936f333d55b53dc4a8271036ff884c909cf6e94be8b6c"},
+ {file = "coverage-6.3.3-cp39-cp39-win32.whl", hash = "sha256:f8cabc5fd0091976ab7b020f5708335033e422de25e20ddf9416bdce2b7e07d8"},
+ {file = "coverage-6.3.3-cp39-cp39-win_amd64.whl", hash = "sha256:9c9441d57b0963cf8340268ad62fc83de61f1613034b79c2b1053046af0c5284"},
+ {file = "coverage-6.3.3-pp36.pp37.pp38-none-any.whl", hash = "sha256:d522f1dc49127eab0bfbba4e90fa068ecff0899bbf61bf4065c790ddd6c177fe"},
+ {file = "coverage-6.3.3.tar.gz", hash = "sha256:2781c43bffbbec2b8867376d4d61916f5e9c4cc168232528562a61d1b4b01879"},
+]
+distlib = [
+ {file = "distlib-0.3.4-py2.py3-none-any.whl", hash = "sha256:6564fe0a8f51e734df6333d08b8b94d4ea8ee6b99b5ed50613f731fd4089f34b"},
+ {file = "distlib-0.3.4.zip", hash = "sha256:e4b58818180336dc9c529bfb9a0b58728ffc09ad92027a3f30b7cd91e3458579"},
+]
+filelock = [
+ {file = "filelock-3.7.0-py3-none-any.whl", hash = "sha256:c7b5fdb219b398a5b28c8e4c1893ef5f98ece6a38c6ab2c22e26ec161556fed6"},
+ {file = "filelock-3.7.0.tar.gz", hash = "sha256:b795f1b42a61bbf8ec7113c341dad679d772567b936fbd1bf43c9a238e673e20"},
+]
+graphviz = [
+ {file = "graphviz-0.20-py3-none-any.whl", hash = "sha256:62c5f48bcc534a45b4588c548ff75e419c1f1f3a33d31a91796ae80a7f581e4a"},
+ {file = "graphviz-0.20.zip", hash = "sha256:76bdfb73f42e72564ffe9c7299482f9d72f8e6cb8d54bce7b48ab323755e9ba5"},
+]
+identify = [
+ {file = "identify-2.5.0-py2.py3-none-any.whl", hash = "sha256:3acfe15a96e4272b4ec5662ee3e231ceba976ef63fd9980ed2ce9cc415df393f"},
+ {file = "identify-2.5.0.tar.gz", hash = "sha256:c83af514ea50bf2be2c4a3f2fb349442b59dc87284558ae9ff54191bff3541d2"},
+]
iniconfig = [
{file = "iniconfig-1.1.1-py2.py3-none-any.whl", hash = "sha256:011e24c64b7f47f6ebd835bb12a743f2fbe9a26d4cecaa7f53bc4f35ee9da8b3"},
{file = "iniconfig-1.1.1.tar.gz", hash = "sha256:bc3af051d7d14b2ee5ef9969666def0cd1a000e121eaea580d4a313df4b37f32"},
]
+nodeenv = [
+ {file = "nodeenv-1.6.0-py2.py3-none-any.whl", hash = "sha256:621e6b7076565ddcacd2db0294c0381e01fd28945ab36bcf00f41c5daf63bef7"},
+ {file = "nodeenv-1.6.0.tar.gz", hash = "sha256:3ef13ff90291ba2a4a7a4ff9a979b63ffdd00a464dbe04acf0ea6471517a4c2b"},
+]
packaging = [
{file = "packaging-21.3-py3-none-any.whl", hash = "sha256:ef103e05f519cdc783ae24ea4e2e0f508a9c99b2d4969652eed6a2e1ea5bd522"},
{file = "packaging-21.3.tar.gz", hash = "sha256:dd47c42927d89ab911e606518907cc2d3a1f38bbd026385970643f9c5b8ecfeb"},
]
+platformdirs = [
+ {file = "platformdirs-2.5.2-py3-none-any.whl", hash = "sha256:027d8e83a2d7de06bbac4e5ef7e023c02b863d7ea5d079477e722bb41ab25788"},
+ {file = "platformdirs-2.5.2.tar.gz", hash = "sha256:58c8abb07dcb441e6ee4b11d8df0ac856038f944ab98b7be6b27b2a3c7feef19"},
+]
pluggy = [
{file = "pluggy-1.0.0-py2.py3-none-any.whl", hash = "sha256:74134bbf457f031a36d68416e1509f34bd5ccc019f0bcc952c7b909d06b37bd3"},
{file = "pluggy-1.0.0.tar.gz", hash = "sha256:4224373bacce55f955a878bf9cfa763c1e360858e330072059e10bad68531159"},
]
+pre-commit = [
+ {file = "pre_commit-2.19.0-py2.py3-none-any.whl", hash = "sha256:10c62741aa5704faea2ad69cb550ca78082efe5697d6f04e5710c3c229afdd10"},
+ {file = "pre_commit-2.19.0.tar.gz", hash = "sha256:4233a1e38621c87d9dda9808c6606d7e7ba0e087cd56d3fe03202a01d2919615"},
+]
py = [
{file = "py-1.11.0-py2.py3-none-any.whl", hash = "sha256:607c53218732647dff4acdfcd50cb62615cedf612e72d1724fb1a0cc6405b378"},
{file = "py-1.11.0.tar.gz", hash = "sha256:51c75c4126074b472f746a24399ad32f6053d1b34b68d2fa41e558e6f4a98719"},
@@ -181,6 +438,84 @@ pytest = [
{file = "pytest-7.1.2-py3-none-any.whl", hash = "sha256:13d0e3ccfc2b6e26be000cb6568c832ba67ba32e719443bfe725814d3c42433c"},
{file = "pytest-7.1.2.tar.gz", hash = "sha256:a06a0425453864a270bc45e71f783330a7428defb4230fb5e6a731fde06ecd45"},
]
+pytest-cov = [
+ {file = "pytest-cov-3.0.0.tar.gz", hash = "sha256:e7f0f5b1617d2210a2cabc266dfe2f4c75a8d32fb89eafb7ad9d06f6d076d470"},
+ {file = "pytest_cov-3.0.0-py3-none-any.whl", hash = "sha256:578d5d15ac4a25e5f961c938b85a05b09fdaae9deef3bb6de9a6e766622ca7a6"},
+]
+pyyaml = [
+ {file = "PyYAML-6.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d4db7c7aef085872ef65a8fd7d6d09a14ae91f691dec3e87ee5ee0539d516f53"},
+ {file = "PyYAML-6.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9df7ed3b3d2e0ecfe09e14741b857df43adb5a3ddadc919a2d94fbdf78fea53c"},
+ {file = "PyYAML-6.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:77f396e6ef4c73fdc33a9157446466f1cff553d979bd00ecb64385760c6babdc"},
+ {file = "PyYAML-6.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a80a78046a72361de73f8f395f1f1e49f956c6be882eed58505a15f3e430962b"},
+ {file = "PyYAML-6.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:f84fbc98b019fef2ee9a1cb3ce93e3187a6df0b2538a651bfb890254ba9f90b5"},
+ {file = "PyYAML-6.0-cp310-cp310-win32.whl", hash = "sha256:2cd5df3de48857ed0544b34e2d40e9fac445930039f3cfe4bcc592a1f836d513"},
+ {file = "PyYAML-6.0-cp310-cp310-win_amd64.whl", hash = "sha256:daf496c58a8c52083df09b80c860005194014c3698698d1a57cbcfa182142a3a"},
+ {file = "PyYAML-6.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:897b80890765f037df3403d22bab41627ca8811ae55e9a722fd0392850ec4d86"},
+ {file = "PyYAML-6.0-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:50602afada6d6cbfad699b0c7bb50d5ccffa7e46a3d738092afddc1f9758427f"},
+ {file = "PyYAML-6.0-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:48c346915c114f5fdb3ead70312bd042a953a8ce5c7106d5bfb1a5254e47da92"},
+ {file = "PyYAML-6.0-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:98c4d36e99714e55cfbaaee6dd5badbc9a1ec339ebfc3b1f52e293aee6bb71a4"},
+ {file = "PyYAML-6.0-cp36-cp36m-win32.whl", hash = "sha256:0283c35a6a9fbf047493e3a0ce8d79ef5030852c51e9d911a27badfde0605293"},
+ {file = "PyYAML-6.0-cp36-cp36m-win_amd64.whl", hash = "sha256:07751360502caac1c067a8132d150cf3d61339af5691fe9e87803040dbc5db57"},
+ {file = "PyYAML-6.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:819b3830a1543db06c4d4b865e70ded25be52a2e0631ccd2f6a47a2822f2fd7c"},
+ {file = "PyYAML-6.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:473f9edb243cb1935ab5a084eb238d842fb8f404ed2193a915d1784b5a6b5fc0"},
+ {file = "PyYAML-6.0-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0ce82d761c532fe4ec3f87fc45688bdd3a4c1dc5e0b4a19814b9009a29baefd4"},
+ {file = "PyYAML-6.0-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:231710d57adfd809ef5d34183b8ed1eeae3f76459c18fb4a0b373ad56bedcdd9"},
+ {file = "PyYAML-6.0-cp37-cp37m-win32.whl", hash = "sha256:c5687b8d43cf58545ade1fe3e055f70eac7a5a1a0bf42824308d868289a95737"},
+ {file = "PyYAML-6.0-cp37-cp37m-win_amd64.whl", hash = "sha256:d15a181d1ecd0d4270dc32edb46f7cb7733c7c508857278d3d378d14d606db2d"},
+ {file = "PyYAML-6.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:0b4624f379dab24d3725ffde76559cff63d9ec94e1736b556dacdfebe5ab6d4b"},
+ {file = "PyYAML-6.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:213c60cd50106436cc818accf5baa1aba61c0189ff610f64f4a3e8c6726218ba"},
+ {file = "PyYAML-6.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9fa600030013c4de8165339db93d182b9431076eb98eb40ee068700c9c813e34"},
+ {file = "PyYAML-6.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:277a0ef2981ca40581a47093e9e2d13b3f1fbbeffae064c1d21bfceba2030287"},
+ {file = "PyYAML-6.0-cp38-cp38-win32.whl", hash = "sha256:d4eccecf9adf6fbcc6861a38015c2a64f38b9d94838ac1810a9023a0609e1b78"},
+ {file = "PyYAML-6.0-cp38-cp38-win_amd64.whl", hash = "sha256:1e4747bc279b4f613a09eb64bba2ba602d8a6664c6ce6396a4d0cd413a50ce07"},
+ {file = "PyYAML-6.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:055d937d65826939cb044fc8c9b08889e8c743fdc6a32b33e2390f66013e449b"},
+ {file = "PyYAML-6.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:e61ceaab6f49fb8bdfaa0f92c4b57bcfbea54c09277b1b4f7ac376bfb7a7c174"},
+ {file = "PyYAML-6.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d67d839ede4ed1b28a4e8909735fc992a923cdb84e618544973d7dfc71540803"},
+ {file = "PyYAML-6.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cba8c411ef271aa037d7357a2bc8f9ee8b58b9965831d9e51baf703280dc73d3"},
+ {file = "PyYAML-6.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:40527857252b61eacd1d9af500c3337ba8deb8fc298940291486c465c8b46ec0"},
+ {file = "PyYAML-6.0-cp39-cp39-win32.whl", hash = "sha256:b5b9eccad747aabaaffbc6064800670f0c297e52c12754eb1d976c57e4f74dcb"},
+ {file = "PyYAML-6.0-cp39-cp39-win_amd64.whl", hash = "sha256:b3d267842bf12586ba6c734f89d1f5b871df0273157918b0ccefa29deb05c21c"},
+ {file = "PyYAML-6.0.tar.gz", hash = "sha256:68fb519c14306fec9720a2a5b45bc9f0c8d1b9c72adf45c37baedfcd949c35a2"},
+]
+"ruamel.yaml" = [
+ {file = "ruamel.yaml-0.17.21-py3-none-any.whl", hash = "sha256:742b35d3d665023981bd6d16b3d24248ce5df75fdb4e2924e93a05c1f8b61ca7"},
+ {file = "ruamel.yaml-0.17.21.tar.gz", hash = "sha256:8b7ce697a2f212752a35c1ac414471dc16c424c9573be4926b56ff3f5d23b7af"},
+]
+"ruamel.yaml.clib" = [
+ {file = "ruamel.yaml.clib-0.2.6-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:6e7be2c5bcb297f5b82fee9c665eb2eb7001d1050deaba8471842979293a80b0"},
+ {file = "ruamel.yaml.clib-0.2.6-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:221eca6f35076c6ae472a531afa1c223b9c29377e62936f61bc8e6e8bdc5f9e7"},
+ {file = "ruamel.yaml.clib-0.2.6-cp310-cp310-win32.whl", hash = "sha256:1070ba9dd7f9370d0513d649420c3b362ac2d687fe78c6e888f5b12bf8bc7bee"},
+ {file = "ruamel.yaml.clib-0.2.6-cp310-cp310-win_amd64.whl", hash = "sha256:77df077d32921ad46f34816a9a16e6356d8100374579bc35e15bab5d4e9377de"},
+ {file = "ruamel.yaml.clib-0.2.6-cp35-cp35m-macosx_10_6_intel.whl", hash = "sha256:cfdb9389d888c5b74af297e51ce357b800dd844898af9d4a547ffc143fa56751"},
+ {file = "ruamel.yaml.clib-0.2.6-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:7b2927e92feb51d830f531de4ccb11b320255ee95e791022555971c466af4527"},
+ {file = "ruamel.yaml.clib-0.2.6-cp35-cp35m-win32.whl", hash = "sha256:ada3f400d9923a190ea8b59c8f60680c4ef8a4b0dfae134d2f2ff68429adfab5"},
+ {file = "ruamel.yaml.clib-0.2.6-cp35-cp35m-win_amd64.whl", hash = "sha256:de9c6b8a1ba52919ae919f3ae96abb72b994dd0350226e28f3686cb4f142165c"},
+ {file = "ruamel.yaml.clib-0.2.6-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:d67f273097c368265a7b81e152e07fb90ed395df6e552b9fa858c6d2c9f42502"},
+ {file = "ruamel.yaml.clib-0.2.6-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:72a2b8b2ff0a627496aad76f37a652bcef400fd861721744201ef1b45199ab78"},
+ {file = "ruamel.yaml.clib-0.2.6-cp36-cp36m-win32.whl", hash = "sha256:9efef4aab5353387b07f6b22ace0867032b900d8e91674b5d8ea9150db5cae94"},
+ {file = "ruamel.yaml.clib-0.2.6-cp36-cp36m-win_amd64.whl", hash = "sha256:846fc8336443106fe23f9b6d6b8c14a53d38cef9a375149d61f99d78782ea468"},
+ {file = "ruamel.yaml.clib-0.2.6-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:0847201b767447fc33b9c235780d3aa90357d20dd6108b92be544427bea197dd"},
+ {file = "ruamel.yaml.clib-0.2.6-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:78988ed190206672da0f5d50c61afef8f67daa718d614377dcd5e3ed85ab4a99"},
+ {file = "ruamel.yaml.clib-0.2.6-cp37-cp37m-win32.whl", hash = "sha256:a49e0161897901d1ac9c4a79984b8410f450565bbad64dbfcbf76152743a0cdb"},
+ {file = "ruamel.yaml.clib-0.2.6-cp37-cp37m-win_amd64.whl", hash = "sha256:bf75d28fa071645c529b5474a550a44686821decebdd00e21127ef1fd566eabe"},
+ {file = "ruamel.yaml.clib-0.2.6-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:a32f8d81ea0c6173ab1b3da956869114cae53ba1e9f72374032e33ba3118c233"},
+ {file = "ruamel.yaml.clib-0.2.6-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:7f7ecb53ae6848f959db6ae93bdff1740e651809780822270eab111500842a84"},
+ {file = "ruamel.yaml.clib-0.2.6-cp38-cp38-win32.whl", hash = "sha256:89221ec6d6026f8ae859c09b9718799fea22c0e8da8b766b0b2c9a9ba2db326b"},
+ {file = "ruamel.yaml.clib-0.2.6-cp38-cp38-win_amd64.whl", hash = "sha256:31ea73e564a7b5fbbe8188ab8b334393e06d997914a4e184975348f204790277"},
+ {file = "ruamel.yaml.clib-0.2.6-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:dc6a613d6c74eef5a14a214d433d06291526145431c3b964f5e16529b1842bed"},
+ {file = "ruamel.yaml.clib-0.2.6-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:1866cf2c284a03b9524a5cc00daca56d80057c5ce3cdc86a52020f4c720856f0"},
+ {file = "ruamel.yaml.clib-0.2.6-cp39-cp39-win32.whl", hash = "sha256:3fb9575a5acd13031c57a62cc7823e5d2ff8bc3835ba4d94b921b4e6ee664104"},
+ {file = "ruamel.yaml.clib-0.2.6-cp39-cp39-win_amd64.whl", hash = "sha256:825d5fccef6da42f3c8eccd4281af399f21c02b32d98e113dbc631ea6a6ecbc7"},
+ {file = "ruamel.yaml.clib-0.2.6.tar.gz", hash = "sha256:4ff604ce439abb20794f05613c374759ce10e3595d1867764dd1ae675b85acbd"},
+]
+six = [
+ {file = "six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"},
+ {file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"},
+]
+toml = [
+ {file = "toml-0.10.2-py2.py3-none-any.whl", hash = "sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b"},
+ {file = "toml-0.10.2.tar.gz", hash = "sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f"},
+]
tomli = [
{file = "tomli-2.0.1-py3-none-any.whl", hash = "sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc"},
{file = "tomli-2.0.1.tar.gz", hash = "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f"},
@@ -189,3 +524,7 @@ typer = [
{file = "typer-0.4.1-py3-none-any.whl", hash = "sha256:e8467f0ebac0c81366c2168d6ad9f888efdfb6d4e1d3d5b4a004f46fa444b5c3"},
{file = "typer-0.4.1.tar.gz", hash = "sha256:5646aef0d936b2c761a10393f0384ee6b5c7fe0bb3e5cd710b17134ca1d99cff"},
]
+virtualenv = [
+ {file = "virtualenv-20.14.1-py2.py3-none-any.whl", hash = "sha256:e617f16e25b42eb4f6e74096b9c9e37713cf10bf30168fb4a739f3fa8f898a3a"},
+ {file = "virtualenv-20.14.1.tar.gz", hash = "sha256:ef589a79795589aada0c1c5b319486797c03b67ac3984c48c669c0e4f50df3a5"},
+]
diff --git a/pyproject.toml b/pyproject.toml
index 3b7de75..5a686d1 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -1,7 +1,7 @@
[tool.poetry]
name = "compose-viz"
version = "0.1.0"
-description = "A docker-compose/podman-compose graph visualization tool that allows you to gernerate graph in DOT format or PNG."
+description = "A compose file visualization tool that supports compose-spec and allows you to gernerate graph in DOT format or PNG."
authors = ["Xyphuz Wu "]
readme = "README.md"
license = "MIT"
@@ -14,9 +14,15 @@ include = [
[tool.poetry.dependencies]
python = "^3.9"
typer = "^0.4.1"
+PyYAML = "^6.0"
+graphviz = "^0.20"
+"ruamel.yaml" = "^0.17.21"
[tool.poetry.dev-dependencies]
pytest = "^7.1.2"
+pre-commit = "^2.19.0"
+coverage = "^6.3.3"
+pytest-cov = "^3.0.0"
[build-system]
requires = ["poetry-core>=1.0.0"]
diff --git a/tests/in/000010.yaml b/tests/in/000010.yaml
deleted file mode 100644
index 1bb47e2..0000000
--- a/tests/in/000010.yaml
+++ /dev/null
@@ -1,11 +0,0 @@
-services:
- base:
- image: busybox
- user: root
- common:
- image: busybox
- extends:
- service: base
- cli:
- extends:
- service: common
\ No newline at end of file
diff --git a/tests/in/000011.yaml b/tests/in/000011.yaml
deleted file mode 100644
index 2336456..0000000
--- a/tests/in/000011.yaml
+++ /dev/null
@@ -1,30 +0,0 @@
-services:
- frontend:
- image: awesome/webapp
- networks:
- - front-tier
- - back-tier
-
- monitoring:
- image: awesome/monitoring
- networks:
- - admin
- extends:
- service: frontend
-
- backend:
- image: awesome/backend
- networks:
- back-tier:
- aliases:
- - database
- admin:
- aliases:
- - mysql
- extends:
- service: frontend
-
-networks:
- front-tier:
- back-tier:
- admin:
\ No newline at end of file
diff --git a/tests/in/000100.yaml b/tests/in/000100.yaml
deleted file mode 100644
index 34c09b1..0000000
--- a/tests/in/000100.yaml
+++ /dev/null
@@ -1,8 +0,0 @@
-version: "3.9"
-services:
- web:
- build: .
- ports:
- - "8000:5000"
- redis:
- image: "redis:alpine"
\ No newline at end of file
diff --git a/tests/in/000101.yaml b/tests/in/000101.yaml
deleted file mode 100644
index 6d1ce63..0000000
--- a/tests/in/000101.yaml
+++ /dev/null
@@ -1,32 +0,0 @@
-services:
- frontend:
- image: awesome/webapp
- ports:
- - "8000:5000"
- networks:
- - front-tier
- - back-tier
-
- monitoring:
- image: awesome/monitoring
- ports:
- - "8000:5001"
- networks:
- - admin
-
- backend:
- image: awesome/backend
- ports:
- - "8000:5010"
- networks:
- back-tier:
- aliases:
- - database
- admin:
- aliases:
- - mysql
-
-networks:
- front-tier:
- back-tier:
- admin:
\ No newline at end of file
diff --git a/tests/in/000110.yaml b/tests/in/000110.yaml
deleted file mode 100644
index 9ac40e0..0000000
--- a/tests/in/000110.yaml
+++ /dev/null
@@ -1,17 +0,0 @@
-services:
- frontend:
- image: awesome/webapp
- ports:
- - "8000:5000"
-
- monitoring:
- image: awesome/monitoring
- extends:
- service: frontend
-
- backend:
- image: awesome/backend
- extends:
- service: frontend
- ports:
- - "8000:5001"
\ No newline at end of file
diff --git a/tests/in/000111.yaml b/tests/in/000111.yaml
deleted file mode 100644
index 42fab6c..0000000
--- a/tests/in/000111.yaml
+++ /dev/null
@@ -1,34 +0,0 @@
-services:
- frontend:
- image: awesome/webapp
- networks:
- - front-tier
- - back-tier
- ports:
- - "8000:5000"
-
- monitoring:
- image: awesome/monitoring
- networks:
- - admin
- extends:
- service: frontend
-
- backend:
- image: awesome/backend
- networks:
- back-tier:
- aliases:
- - database
- admin:
- aliases:
- - mysql
- extends:
- service: frontend
- ports:
- - "8000:5001"
-
-networks:
- front-tier:
- back-tier:
- admin:
\ No newline at end of file
diff --git a/tests/in/001000.yaml b/tests/in/001000.yaml
deleted file mode 100644
index 51adbed..0000000
--- a/tests/in/001000.yaml
+++ /dev/null
@@ -1,10 +0,0 @@
-services:
- web:
- build: .
- depends_on:
- - db
- - redis
- redis:
- image: redis
- db:
- image: postgres
\ No newline at end of file
diff --git a/tests/in/001001.yaml b/tests/in/001001.yaml
deleted file mode 100644
index 37a10e0..0000000
--- a/tests/in/001001.yaml
+++ /dev/null
@@ -1,29 +0,0 @@
-services:
- frontend:
- image: awesome/webapp
- networks:
- - front-tier
- - back-tier
- depends_on:
- - monitoring
- - backend
-
- monitoring:
- image: awesome/monitoring
- networks:
- - admin
-
- backend:
- image: awesome/backend
- networks:
- back-tier:
- aliases:
- - database
- admin:
- aliases:
- - mysql
-
-networks:
- front-tier:
- back-tier:
- admin:
\ No newline at end of file
diff --git a/tests/in/001010.yaml b/tests/in/001010.yaml
deleted file mode 100644
index a598e6b..0000000
--- a/tests/in/001010.yaml
+++ /dev/null
@@ -1,12 +0,0 @@
-services:
- web:
- build: .
- depends_on:
- - db
- - redis
- extends:
- service: redis
- redis:
- image: redis
- db:
- image: postgres
\ No newline at end of file
diff --git a/tests/in/001011.yaml b/tests/in/001011.yaml
deleted file mode 100644
index 7ef900c..0000000
--- a/tests/in/001011.yaml
+++ /dev/null
@@ -1,31 +0,0 @@
-services:
- frontend:
- image: awesome/webapp
- networks:
- - front-tier
- - back-tier
- depends_on:
- - monitoring
- - backend
- extends:
- service: backend
-
- monitoring:
- image: awesome/monitoring
- networks:
- - admin
-
- backend:
- image: awesome/backend
- networks:
- back-tier:
- aliases:
- - database
- admin:
- aliases:
- - mysql
-
-networks:
- front-tier:
- back-tier:
- admin:
\ No newline at end of file
diff --git a/tests/in/001100.yaml b/tests/in/001100.yaml
deleted file mode 100644
index e3219c8..0000000
--- a/tests/in/001100.yaml
+++ /dev/null
@@ -1,17 +0,0 @@
-services:
- frontend:
- image: awesome/webapp
- ports:
- - "8000:5000"
-
- monitoring:
- image: awesome/monitoring
- depends_on:
- - backend
- ports:
- - "8000:5010"
-
- backend:
- image: awesome/backend
- ports:
- - "8000:5001"
\ No newline at end of file
diff --git a/tests/in/001101.yaml b/tests/in/001101.yaml
deleted file mode 100644
index 417fe7f..0000000
--- a/tests/in/001101.yaml
+++ /dev/null
@@ -1,30 +0,0 @@
-services:
- frontend:
- image: awesome/webapp
- networks:
- - front-tier
- - back-tier
-
- monitoring:
- image: awesome/monitoring
- networks:
- - admin
- depends_on:
- - backend
- ports:
- - "8000:5010"
-
- backend:
- image: awesome/backend
- networks:
- back-tier:
- aliases:
- - database
- admin:
- aliases:
- - mysql
-
-networks:
- front-tier:
- back-tier:
- admin:
\ No newline at end of file
diff --git a/tests/in/001110.yaml b/tests/in/001110.yaml
deleted file mode 100644
index 187d4b9..0000000
--- a/tests/in/001110.yaml
+++ /dev/null
@@ -1,21 +0,0 @@
-services:
- frontend:
- image: awesome/webapp
- ports:
- - "8000:5000"
-
- monitoring:
- image: awesome/monitoring
- depends_on:
- - backend
- extends:
- service: frontend
- ports:
- - "8000:5010"
-
- backend:
- image: awesome/backend
- extends:
- service: frontend
- ports:
- - "8000:5001"
\ No newline at end of file
diff --git a/tests/in/001111.yaml b/tests/in/001111.yaml
deleted file mode 100644
index 332ed69..0000000
--- a/tests/in/001111.yaml
+++ /dev/null
@@ -1,32 +0,0 @@
-services:
- frontend:
- image: awesome/webapp
- networks:
- - front-tier
- - back-tier
-
- monitoring:
- image: awesome/monitoring
- networks:
- - admin
- depends_on:
- - backend
- extends:
- service: frontend
- ports:
- - "8000:5010"
-
- backend:
- image: awesome/backend
- networks:
- back-tier:
- aliases:
- - database
- admin:
- aliases:
- - mysql
-
-networks:
- front-tier:
- back-tier:
- admin:
\ No newline at end of file
diff --git a/tests/in/010000.yaml b/tests/in/010000.yaml
deleted file mode 100644
index b1c39d7..0000000
--- a/tests/in/010000.yaml
+++ /dev/null
@@ -1,15 +0,0 @@
-services:
- backend:
- image: awesome/backend
- volumes:
- - type: volume
- source: db-data
- target: /data
- volume:
- nocopy: true
- - type: bind
- source: /var/run/postgres/postgres.sock
- target: /var/run/postgres/postgres.sock
-
-volumes:
- db-data:
\ No newline at end of file
diff --git a/tests/in/010001.yaml b/tests/in/010001.yaml
deleted file mode 100644
index 74bd2ea..0000000
--- a/tests/in/010001.yaml
+++ /dev/null
@@ -1,37 +0,0 @@
-services:
- frontend:
- image: awesome/webapp
- networks:
- - front-tier
- - back-tier
-
- monitoring:
- image: awesome/monitoring
- networks:
- - admin
-
- backend:
- image: awesome/backend
- networks:
- back-tier:
- aliases:
- - database
- admin:
- aliases:
- - mysql
- volumes:
- - type: volume
- source: db-data
- target: /data
- volume:
- nocopy: true
- - type: bind
- source: /var/run/postgres/postgres.sock
- target: /var/run/postgres/postgres.sock
-
-networks:
- front-tier:
- back-tier:
- admin:
-volumes:
- db-data:
\ No newline at end of file
diff --git a/tests/in/010010.yaml b/tests/in/010010.yaml
deleted file mode 100644
index bd14c8f..0000000
--- a/tests/in/010010.yaml
+++ /dev/null
@@ -1,13 +0,0 @@
-services:
- common:
- image: busybox
- volumes:
- - common-volume:/var/lib/backup/data:rw
- cli:
- extends:
- service: common
- volumes:
- - cli-volume:/var/lib/backup/data:ro
-volumes:
- common-volume:
- cli-volume:
\ No newline at end of file
diff --git a/tests/in/010011.yaml b/tests/in/010011.yaml
deleted file mode 100644
index fad0350..0000000
--- a/tests/in/010011.yaml
+++ /dev/null
@@ -1,39 +0,0 @@
-services:
- frontend:
- image: awesome/webapp
- networks:
- - front-tier
- - back-tier
-
- monitoring:
- image: awesome/monitoring
- networks:
- - admin
-
- backend:
- image: awesome/backend
- networks:
- back-tier:
- aliases:
- - database
- admin:
- aliases:
- - mysql
- volumes:
- - type: volume
- source: db-data
- target: /data
- volume:
- nocopy: true
- - type: bind
- source: /var/run/postgres/postgres.sock
- target: /var/run/postgres/postgres.sock
- extends:
- service: monitoring
-
-networks:
- front-tier:
- back-tier:
- admin:
-volumes:
- db-data:
\ No newline at end of file
diff --git a/tests/in/010100.yaml b/tests/in/010100.yaml
deleted file mode 100644
index de377ee..0000000
--- a/tests/in/010100.yaml
+++ /dev/null
@@ -1,16 +0,0 @@
-services:
- backend:
- image: awesome/backend
- volumes:
- - type: volume
- source: db-data
- target: /data
- volume:
- nocopy: true
- - type: bind
- source: /var/run/postgres/postgres.sock
- target: /var/run/postgres/postgres.sock
- ports:
- - "8000:5000"
-volumes:
- db-data:
\ No newline at end of file
diff --git a/tests/in/010101.yaml b/tests/in/010101.yaml
deleted file mode 100644
index 86a28b1..0000000
--- a/tests/in/010101.yaml
+++ /dev/null
@@ -1,39 +0,0 @@
-services:
- frontend:
- image: awesome/webapp
- networks:
- - front-tier
- - back-tier
-
- monitoring:
- image: awesome/monitoring
- networks:
- - admin
-
- backend:
- image: awesome/backend
- networks:
- back-tier:
- aliases:
- - database
- admin:
- aliases:
- - mysql
- volumes:
- - type: volume
- source: db-data
- target: /data
- volume:
- nocopy: true
- - type: bind
- source: /var/run/postgres/postgres.sock
- target: /var/run/postgres/postgres.sock
- ports:
- - "8000:5000"
-
-networks:
- front-tier:
- back-tier:
- admin:
-volumes:
- db-data:
\ No newline at end of file
diff --git a/tests/in/010110.yaml b/tests/in/010110.yaml
deleted file mode 100644
index ef63463..0000000
--- a/tests/in/010110.yaml
+++ /dev/null
@@ -1,24 +0,0 @@
-services:
- frontend:
- image: awesome/webapp
-
- monitoring:
- image: awesome/monitoring
-
- backend:
- image: awesome/backend
- volumes:
- - type: volume
- source: db-data
- target: /data
- volume:
- nocopy: true
- - type: bind
- source: /var/run/postgres/postgres.sock
- target: /var/run/postgres/postgres.sock
- extends:
- service: monitoring
- ports:
- - "8000:5000"
-volumes:
- db-data:
\ No newline at end of file
diff --git a/tests/in/010111.yaml b/tests/in/010111.yaml
deleted file mode 100644
index b3d9888..0000000
--- a/tests/in/010111.yaml
+++ /dev/null
@@ -1,41 +0,0 @@
-services:
- frontend:
- image: awesome/webapp
- networks:
- - front-tier
- - back-tier
-
- monitoring:
- image: awesome/monitoring
- networks:
- - admin
-
- backend:
- image: awesome/backend
- networks:
- back-tier:
- aliases:
- - database
- admin:
- aliases:
- - mysql
- volumes:
- - type: volume
- source: db-data
- target: /data
- volume:
- nocopy: true
- - type: bind
- source: /var/run/postgres/postgres.sock
- target: /var/run/postgres/postgres.sock
- extends:
- service: monitoring
- ports:
- - "8000:5000"
-
-networks:
- front-tier:
- back-tier:
- admin:
-volumes:
- db-data:
\ No newline at end of file
diff --git a/tests/in/011000.yaml b/tests/in/011000.yaml
deleted file mode 100644
index 4ef3c19..0000000
--- a/tests/in/011000.yaml
+++ /dev/null
@@ -1,22 +0,0 @@
-services:
- frontend:
- image: awesome/webapp
-
- monitoring:
- image: awesome/monitoring
- depends_on:
- - backend
- volumes:
- - type: volume
- source: db-data
- target: /data
- volume:
- nocopy: true
- - type: bind
- source: /var/run/postgres/postgres.sock
- target: /var/run/postgres/postgres.sock
-
- backend:
- image: awesome/backend
-volumes:
- db-data:
\ No newline at end of file
diff --git a/tests/in/011001.yaml b/tests/in/011001.yaml
deleted file mode 100644
index 023ab8c..0000000
--- a/tests/in/011001.yaml
+++ /dev/null
@@ -1,40 +0,0 @@
-services:
- frontend:
- image: awesome/webapp
- networks:
- - front-tier
- - back-tier
-
- monitoring:
- image: awesome/monitoring
- networks:
- - admin
-
-
- backend:
- image: awesome/backend
- networks:
- back-tier:
- aliases:
- - database
- admin:
- aliases:
- - mysql
- volumes:
- - type: volume
- source: db-data
- target: /data
- volume:
- nocopy: true
- - type: bind
- source: /var/run/postgres/postgres.sock
- target: /var/run/postgres/postgres.sock
- depends_on:
- - monitoring
-
-networks:
- front-tier:
- back-tier:
- admin:
-volumes:
- db-data:
\ No newline at end of file
diff --git a/tests/in/011010.yaml b/tests/in/011010.yaml
deleted file mode 100644
index a65c17e..0000000
--- a/tests/in/011010.yaml
+++ /dev/null
@@ -1,26 +0,0 @@
-services:
- frontend:
- image: awesome/webapp
-
- monitoring:
- image: awesome/monitoring
-
-
- backend:
- image: awesome/backend
- volumes:
- - type: volume
- source: db-data
- target: /data
- volume:
- nocopy: true
- - type: bind
- source: /var/run/postgres/postgres.sock
- target: /var/run/postgres/postgres.sock
- depends_on:
- - monitoring
- extends:
- service: frontend
-
-volumes:
- db-data:
\ No newline at end of file
diff --git a/tests/in/011011.yaml b/tests/in/011011.yaml
deleted file mode 100644
index 67bfde1..0000000
--- a/tests/in/011011.yaml
+++ /dev/null
@@ -1,42 +0,0 @@
-services:
- frontend:
- image: awesome/webapp
- networks:
- - front-tier
- - back-tier
-
- monitoring:
- image: awesome/monitoring
- networks:
- - admin
-
-
- backend:
- image: awesome/backend
- networks:
- back-tier:
- aliases:
- - database
- admin:
- aliases:
- - mysql
- volumes:
- - type: volume
- source: db-data
- target: /data
- volume:
- nocopy: true
- - type: bind
- source: /var/run/postgres/postgres.sock
- target: /var/run/postgres/postgres.sock
- depends_on:
- - monitoring
- extends:
- service: frontend
-
-networks:
- front-tier:
- back-tier:
- admin:
-volumes:
- db-data:
\ No newline at end of file
diff --git a/tests/in/011100.yaml b/tests/in/011100.yaml
deleted file mode 100644
index dbebc65..0000000
--- a/tests/in/011100.yaml
+++ /dev/null
@@ -1,28 +0,0 @@
-services:
- frontend:
- image: awesome/webapp
- ports:
- - "8000:5000"
-
- monitoring:
- image: awesome/monitoring
- depends_on:
- - backend
- ports:
- - "8000:5010"
- volumes:
- - type: volume
- source: db-data
- target: /data
- volume:
- nocopy: true
- - type: bind
- source: /var/run/postgres/postgres.sock
- target: /var/run/postgres/postgres.sock
-
- backend:
- image: awesome/backend
- ports:
- - "8000:5001"
-volumes:
- db-data:
\ No newline at end of file
diff --git a/tests/in/011101.yaml b/tests/in/011101.yaml
deleted file mode 100644
index dd833f4..0000000
--- a/tests/in/011101.yaml
+++ /dev/null
@@ -1,74 +0,0 @@
-services:
- vote:
- build: ./
- # use python rather than gunicorn for local dev
- command: python app.py
- depends_on:
- redis:
- condition: service_healthy
- volumes:
- - app
- ports:
- - "5000:80"
- networks:
- - front-tier
- - back-tier
-
- result:
- build: ./
- # use nodemon rather than node for local dev
- command: nodemon server.js
- depends_on:
- db:
- condition: service_healthy
- volumes:
- - app
- ports:
- - "5001:80"
- - "5858:5858"
- networks:
- - front-tier
- - back-tier
-
- worker:
- build:
- context: ./
- depends_on:
- redis:
- condition: service_healthy
- db:
- condition: service_healthy
- networks:
- - back-tier
-
- redis:
- image: redis:5.0-alpine3.10
- volumes:
- - "./healthchecks:/healthchecks"
- healthcheck:
- test: /healthchecks/redis.sh
- interval: "5s"
- ports: ["6379"]
- networks:
- - back-tier
-
- db:
- image: postgres:9.4
- environment:
- POSTGRES_USER: "postgres"
- POSTGRES_PASSWORD: "postgres"
- volumes:
- - "db-data:/var/lib/postgresql/data"
- - "./healthchecks:/healthchecks"
- healthcheck:
- test: /healthchecks/postgres.sh
- interval: "5s"
- networks:
- - back-tier
-
-volumes:
- db-data:
-
-networks:
- front-tier:
- back-tier:
diff --git a/tests/in/011110.yaml b/tests/in/011110.yaml
deleted file mode 100644
index 97f8c0d..0000000
--- a/tests/in/011110.yaml
+++ /dev/null
@@ -1,30 +0,0 @@
-services:
- frontend:
- image: awesome/webapp
-
-
- monitoring:
- image: awesome/monitoring
-
-
-
- backend:
- image: awesome/backend
- volumes:
- - type: volume
- source: db-data
- target: /data
- volume:
- nocopy: true
- - type: bind
- source: /var/run/postgres/postgres.sock
- target: /var/run/postgres/postgres.sock
- depends_on:
- - monitoring
- extends:
- service: frontend
- ports:
- - "8000:5010"
-
-volumes:
- db-data:
\ No newline at end of file
diff --git a/tests/in/011111.yaml b/tests/in/011111.yaml
deleted file mode 100644
index 797150d..0000000
--- a/tests/in/011111.yaml
+++ /dev/null
@@ -1,44 +0,0 @@
-services:
- frontend:
- image: awesome/webapp
- networks:
- - front-tier
- - back-tier
-
- monitoring:
- image: awesome/monitoring
- networks:
- - admin
-
-
- backend:
- image: awesome/backend
- networks:
- back-tier:
- aliases:
- - database
- admin:
- aliases:
- - mysql
- volumes:
- - type: volume
- source: db-data
- target: /data
- volume:
- nocopy: true
- - type: bind
- source: /var/run/postgres/postgres.sock
- target: /var/run/postgres/postgres.sock
- depends_on:
- - monitoring
- extends:
- service: frontend
- ports:
- - "8000:5010"
-
-networks:
- front-tier:
- back-tier:
- admin:
-volumes:
- db-data:
\ No newline at end of file
diff --git a/tests/in/100001.yaml b/tests/in/100001.yaml
deleted file mode 100644
index 82a5fa2..0000000
--- a/tests/in/100001.yaml
+++ /dev/null
@@ -1,31 +0,0 @@
-services:
- frontend:
- image: awesome/webapp
- networks:
- - front-tier
- - back-tier
-
- monitoring:
- image: awesome/monitoring
- networks:
- - admin
-
-
- backend:
- image: awesome/backend
- networks:
- back-tier:
- aliases:
- - database
- admin:
- aliases:
- - mysql
- links:
- - "db:database"
- db:
- image: postgres
-
-networks:
- front-tier:
- back-tier:
- admin:
\ No newline at end of file
diff --git a/tests/in/100010.yaml b/tests/in/100010.yaml
deleted file mode 100644
index 5f72c8a..0000000
--- a/tests/in/100010.yaml
+++ /dev/null
@@ -1,16 +0,0 @@
-services:
- frontend:
- image: awesome/webapp
-
- monitoring:
- image: awesome/monitoring
-
-
- backend:
- image: awesome/backend
- extends:
- service: frontend
- links:
- - "db:database"
- db:
- image: postgres
diff --git a/tests/in/100011.yaml b/tests/in/100011.yaml
deleted file mode 100644
index bbd4fd2..0000000
--- a/tests/in/100011.yaml
+++ /dev/null
@@ -1,33 +0,0 @@
-services:
- frontend:
- image: awesome/webapp
- networks:
- - front-tier
- - back-tier
-
- monitoring:
- image: awesome/monitoring
- networks:
- - admin
-
-
- backend:
- image: awesome/backend
- networks:
- back-tier:
- aliases:
- - database
- admin:
- aliases:
- - mysql
- extends:
- service: frontend
- links:
- - "db:database"
- db:
- image: postgres
-
-networks:
- front-tier:
- back-tier:
- admin:
\ No newline at end of file
diff --git a/tests/in/100100.yaml b/tests/in/100100.yaml
deleted file mode 100644
index 4640d65..0000000
--- a/tests/in/100100.yaml
+++ /dev/null
@@ -1,16 +0,0 @@
-services:
- frontend:
- image: awesome/webapp
-
- monitoring:
- image: awesome/monitoring
-
-
- backend:
- image: awesome/backend
- ports:
- - "8000:5010"
- links:
- - "db:database"
- db:
- image: postgres
diff --git a/tests/in/100101.yaml b/tests/in/100101.yaml
deleted file mode 100644
index bf6cac2..0000000
--- a/tests/in/100101.yaml
+++ /dev/null
@@ -1,33 +0,0 @@
-services:
- frontend:
- image: awesome/webapp
- networks:
- - front-tier
- - back-tier
-
- monitoring:
- image: awesome/monitoring
- networks:
- - admin
-
-
- backend:
- image: awesome/backend
- networks:
- back-tier:
- aliases:
- - database
- admin:
- aliases:
- - mysql
- ports:
- - "8000:5010"
- links:
- - "db:database"
- db:
- image: postgres
-
-networks:
- front-tier:
- back-tier:
- admin:
\ No newline at end of file
diff --git a/tests/in/100110.yaml b/tests/in/100110.yaml
deleted file mode 100644
index 34b31ca..0000000
--- a/tests/in/100110.yaml
+++ /dev/null
@@ -1,18 +0,0 @@
-services:
- frontend:
- image: awesome/webapp
-
- monitoring:
- image: awesome/monitoring
-
-
- backend:
- image: awesome/backend
- extends:
- service: frontend
- ports:
- - "8000:5010"
- links:
- - "db:database"
- db:
- image: postgres
diff --git a/tests/in/100111.yaml b/tests/in/100111.yaml
deleted file mode 100644
index 3c718f8..0000000
--- a/tests/in/100111.yaml
+++ /dev/null
@@ -1,35 +0,0 @@
-services:
- frontend:
- image: awesome/webapp
- networks:
- - front-tier
- - back-tier
-
- monitoring:
- image: awesome/monitoring
- networks:
- - admin
-
-
- backend:
- image: awesome/backend
- networks:
- back-tier:
- aliases:
- - database
- admin:
- aliases:
- - mysql
- extends:
- service: frontend
- ports:
- - "8000:5010"
- links:
- - "db:database"
- db:
- image: postgres
-
-networks:
- front-tier:
- back-tier:
- admin:
\ No newline at end of file
diff --git a/tests/in/101000.yaml b/tests/in/101000.yaml
deleted file mode 100644
index 72d5f8c..0000000
--- a/tests/in/101000.yaml
+++ /dev/null
@@ -1,16 +0,0 @@
-services:
- frontend:
- image: awesome/webapp
-
- monitoring:
- image: awesome/monitoring
-
-
- backend:
- image: awesome/backend
- depends_on:
- - monitoring
- links:
- - "db:database"
- db:
- image: postgres
diff --git a/tests/in/101001.yaml b/tests/in/101001.yaml
deleted file mode 100644
index a01f7ae..0000000
--- a/tests/in/101001.yaml
+++ /dev/null
@@ -1,33 +0,0 @@
-services:
- frontend:
- image: awesome/webapp
- networks:
- - front-tier
- - back-tier
-
- monitoring:
- image: awesome/monitoring
- networks:
- - admin
-
-
- backend:
- image: awesome/backend
- networks:
- back-tier:
- aliases:
- - database
- admin:
- aliases:
- - mysql
- depends_on:
- - monitoring
- links:
- - "db:database"
- db:
- image: postgres
-
-networks:
- front-tier:
- back-tier:
- admin:
\ No newline at end of file
diff --git a/tests/in/101010.yaml b/tests/in/101010.yaml
deleted file mode 100644
index ec3ee66..0000000
--- a/tests/in/101010.yaml
+++ /dev/null
@@ -1,18 +0,0 @@
-services:
- frontend:
- image: awesome/webapp
-
- monitoring:
- image: awesome/monitoring
-
-
- backend:
- image: awesome/backend
- depends_on:
- - monitoring
- links:
- - "db:database"
- extends:
- service: frontend
- db:
- image: postgres
diff --git a/tests/in/101011.yaml b/tests/in/101011.yaml
deleted file mode 100644
index 3b42bb5..0000000
--- a/tests/in/101011.yaml
+++ /dev/null
@@ -1,35 +0,0 @@
-services:
- frontend:
- image: awesome/webapp
- networks:
- - front-tier
- - back-tier
-
- monitoring:
- image: awesome/monitoring
- networks:
- - admin
-
-
- backend:
- image: awesome/backend
- networks:
- back-tier:
- aliases:
- - database
- admin:
- aliases:
- - mysql
- depends_on:
- - monitoring
- extends:
- service: frontend
- links:
- - "db:database"
- db:
- image: postgres
-
-networks:
- front-tier:
- back-tier:
- admin:
\ No newline at end of file
diff --git a/tests/in/101100.yaml b/tests/in/101100.yaml
deleted file mode 100644
index b08993e..0000000
--- a/tests/in/101100.yaml
+++ /dev/null
@@ -1,18 +0,0 @@
-services:
- frontend:
- image: awesome/webapp
-
- monitoring:
- image: awesome/monitoring
-
-
- backend:
- image: awesome/backend
- depends_on:
- - monitoring
- ports:
- - "8000:5010"
- links:
- - "db:database"
- db:
- image: postgres
diff --git a/tests/in/101101.yaml b/tests/in/101101.yaml
deleted file mode 100644
index 8b521fe..0000000
--- a/tests/in/101101.yaml
+++ /dev/null
@@ -1,35 +0,0 @@
-services:
- frontend:
- image: awesome/webapp
- networks:
- - front-tier
- - back-tier
-
- monitoring:
- image: awesome/monitoring
- networks:
- - admin
-
-
- backend:
- image: awesome/backend
- networks:
- back-tier:
- aliases:
- - database
- admin:
- aliases:
- - mysql
- depends_on:
- - monitoring
- ports:
- - "8000:5010"
- links:
- - "db:database"
- db:
- image: postgres
-
-networks:
- front-tier:
- back-tier:
- admin:
\ No newline at end of file
diff --git a/tests/in/101110.yaml b/tests/in/101110.yaml
deleted file mode 100644
index 6428644..0000000
--- a/tests/in/101110.yaml
+++ /dev/null
@@ -1,21 +0,0 @@
-services:
- frontend:
- image: awesome/webapp
-
- monitoring:
- image: awesome/monitoring
-
-
-
- backend:
- image: awesome/backend
- depends_on:
- - monitoring
- extends:
- service: frontend
- ports:
- - "8000:5010"
- links:
- - "db:database"
- db:
- image: postgres
diff --git a/tests/in/101111.yaml b/tests/in/101111.yaml
deleted file mode 100644
index ce845ca..0000000
--- a/tests/in/101111.yaml
+++ /dev/null
@@ -1,37 +0,0 @@
-services:
- frontend:
- image: awesome/webapp
- networks:
- - front-tier
- - back-tier
-
- monitoring:
- image: awesome/monitoring
- networks:
- - admin
-
-
- backend:
- image: awesome/backend
- networks:
- back-tier:
- aliases:
- - database
- admin:
- aliases:
- - mysql
- depends_on:
- - monitoring
- extends:
- service: frontend
- ports:
- - "8000:5010"
- links:
- - "db:database"
- db:
- image: postgres
-
-networks:
- front-tier:
- back-tier:
- admin:
\ No newline at end of file
diff --git a/tests/in/110000.yaml b/tests/in/110000.yaml
deleted file mode 100644
index b07f8ff..0000000
--- a/tests/in/110000.yaml
+++ /dev/null
@@ -1,24 +0,0 @@
-services:
- frontend:
- image: awesome/webapp
-
- monitoring:
- image: awesome/monitoring
- volumes:
- - type: volume
- source: db-data
- target: /data
- volume:
- nocopy: true
- - type: bind
- source: /var/run/postgres/postgres.sock
- target: /var/run/postgres/postgres.sock
- links:
- - "db:database"
-
- backend:
- image: awesome/backend
- db:
- image: postgres
-volumes:
- db-data:
\ No newline at end of file
diff --git a/tests/in/110001.yaml b/tests/in/110001.yaml
deleted file mode 100644
index 6475de2..0000000
--- a/tests/in/110001.yaml
+++ /dev/null
@@ -1,42 +0,0 @@
-services:
- frontend:
- image: awesome/webapp
- networks:
- - front-tier
- - back-tier
-
- monitoring:
- image: awesome/monitoring
- networks:
- - admin
-
-
- backend:
- image: awesome/backend
- networks:
- back-tier:
- aliases:
- - database
- admin:
- aliases:
- - mysql
- volumes:
- - type: volume
- source: db-data
- target: /data
- volume:
- nocopy: true
- - type: bind
- source: /var/run/postgres/postgres.sock
- target: /var/run/postgres/postgres.sock
- links:
- - "db:database"
- db:
- image: postgres
-
-networks:
- front-tier:
- back-tier:
- admin:
-volumes:
- db-data:
\ No newline at end of file
diff --git a/tests/in/110010.yaml b/tests/in/110010.yaml
deleted file mode 100644
index 297dddd..0000000
--- a/tests/in/110010.yaml
+++ /dev/null
@@ -1,28 +0,0 @@
-services:
- frontend:
- image: awesome/webapp
-
- monitoring:
- image: awesome/monitoring
-
-
- backend:
- image: awesome/backend
- volumes:
- - type: volume
- source: db-data
- target: /data
- volume:
- nocopy: true
- - type: bind
- source: /var/run/postgres/postgres.sock
- target: /var/run/postgres/postgres.sock
- extends:
- service: frontend
- links:
- - "db:database"
- db:
- image: postgres
-
-volumes:
- db-data:
\ No newline at end of file
diff --git a/tests/in/110011.yaml b/tests/in/110011.yaml
deleted file mode 100644
index 8778ea3..0000000
--- a/tests/in/110011.yaml
+++ /dev/null
@@ -1,44 +0,0 @@
-services:
- frontend:
- image: awesome/webapp
- networks:
- - front-tier
- - back-tier
-
- monitoring:
- image: awesome/monitoring
- networks:
- - admin
-
-
- backend:
- image: awesome/backend
- networks:
- back-tier:
- aliases:
- - database
- admin:
- aliases:
- - mysql
- volumes:
- - type: volume
- source: db-data
- target: /data
- volume:
- nocopy: true
- - type: bind
- source: /var/run/postgres/postgres.sock
- target: /var/run/postgres/postgres.sock
- extends:
- service: frontend
- links:
- - "db:database"
- db:
- image: postgres
-
-networks:
- front-tier:
- back-tier:
- admin:
-volumes:
- db-data:
\ No newline at end of file
diff --git a/tests/in/110100.yaml b/tests/in/110100.yaml
deleted file mode 100644
index 9ef067c..0000000
--- a/tests/in/110100.yaml
+++ /dev/null
@@ -1,28 +0,0 @@
-services:
- frontend:
- image: awesome/webapp
-
- monitoring:
- image: awesome/monitoring
-
-
- backend:
- image: awesome/backend
- volumes:
- - type: volume
- source: db-data
- target: /data
- volume:
- nocopy: true
- - type: bind
- source: /var/run/postgres/postgres.sock
- target: /var/run/postgres/postgres.sock
- ports:
- - "8000:5010"
- links:
- - "db:database"
- db:
- image: postgres
-
-volumes:
- db-data:
\ No newline at end of file
diff --git a/tests/in/110101.yaml b/tests/in/110101.yaml
deleted file mode 100644
index 2a8000c..0000000
--- a/tests/in/110101.yaml
+++ /dev/null
@@ -1,44 +0,0 @@
-services:
- frontend:
- image: awesome/webapp
- networks:
- - front-tier
- - back-tier
-
- monitoring:
- image: awesome/monitoring
- networks:
- - admin
-
-
- backend:
- image: awesome/backend
- networks:
- back-tier:
- aliases:
- - database
- admin:
- aliases:
- - mysql
- volumes:
- - type: volume
- source: db-data
- target: /data
- volume:
- nocopy: true
- - type: bind
- source: /var/run/postgres/postgres.sock
- target: /var/run/postgres/postgres.sock
- ports:
- - "8000:5010"
- links:
- - "db:database"
- db:
- image: postgres
-
-networks:
- front-tier:
- back-tier:
- admin:
-volumes:
- db-data:
\ No newline at end of file
diff --git a/tests/in/110110.yaml b/tests/in/110110.yaml
deleted file mode 100644
index 72f8a30..0000000
--- a/tests/in/110110.yaml
+++ /dev/null
@@ -1,30 +0,0 @@
-services:
- frontend:
- image: awesome/webapp
-
- monitoring:
- image: awesome/monitoring
-
-
- backend:
- image: awesome/backend
- volumes:
- - type: volume
- source: db-data
- target: /data
- volume:
- nocopy: true
- - type: bind
- source: /var/run/postgres/postgres.sock
- target: /var/run/postgres/postgres.sock
- extends:
- service: frontend
- ports:
- - "8000:5010"
- links:
- - "db:database"
- db:
- image: postgres
-
-volumes:
- db-data:
\ No newline at end of file
diff --git a/tests/in/110111.yaml b/tests/in/110111.yaml
deleted file mode 100644
index 0bda8ef..0000000
--- a/tests/in/110111.yaml
+++ /dev/null
@@ -1,46 +0,0 @@
-services:
- frontend:
- image: awesome/webapp
- networks:
- - front-tier
- - back-tier
-
- monitoring:
- image: awesome/monitoring
- networks:
- - admin
-
-
- backend:
- image: awesome/backend
- networks:
- back-tier:
- aliases:
- - database
- admin:
- aliases:
- - mysql
- volumes:
- - type: volume
- source: db-data
- target: /data
- volume:
- nocopy: true
- - type: bind
- source: /var/run/postgres/postgres.sock
- target: /var/run/postgres/postgres.sock
- extends:
- service: frontend
- ports:
- - "8000:5010"
- links:
- - "db:database"
- db:
- image: postgres
-
-networks:
- front-tier:
- back-tier:
- admin:
-volumes:
- db-data:
\ No newline at end of file
diff --git a/tests/in/111000.yaml b/tests/in/111000.yaml
deleted file mode 100644
index 24483e4..0000000
--- a/tests/in/111000.yaml
+++ /dev/null
@@ -1,26 +0,0 @@
-services:
- frontend:
- image: awesome/webapp
-
- monitoring:
- image: awesome/monitoring
- depends_on:
- - backend
- volumes:
- - type: volume
- source: db-data
- target: /data
- volume:
- nocopy: true
- - type: bind
- source: /var/run/postgres/postgres.sock
- target: /var/run/postgres/postgres.sock
- links:
- - "db:database"
-
- backend:
- image: awesome/backend
- db:
- image: postgres
-volumes:
- db-data:
\ No newline at end of file
diff --git a/tests/in/111001.yaml b/tests/in/111001.yaml
deleted file mode 100644
index b6acfed..0000000
--- a/tests/in/111001.yaml
+++ /dev/null
@@ -1,44 +0,0 @@
-services:
- frontend:
- image: awesome/webapp
- networks:
- - front-tier
- - back-tier
-
- monitoring:
- image: awesome/monitoring
- networks:
- - admin
-
-
- backend:
- image: awesome/backend
- networks:
- back-tier:
- aliases:
- - database
- admin:
- aliases:
- - mysql
- volumes:
- - type: volume
- source: db-data
- target: /data
- volume:
- nocopy: true
- - type: bind
- source: /var/run/postgres/postgres.sock
- target: /var/run/postgres/postgres.sock
- depends_on:
- - monitoring
- links:
- - "db:database"
- db:
- image: postgres
-
-networks:
- front-tier:
- back-tier:
- admin:
-volumes:
- db-data:
\ No newline at end of file
diff --git a/tests/in/111010.yaml b/tests/in/111010.yaml
deleted file mode 100644
index fcd05b9..0000000
--- a/tests/in/111010.yaml
+++ /dev/null
@@ -1,30 +0,0 @@
-services:
- frontend:
- image: awesome/webapp
-
- monitoring:
- image: awesome/monitoring
-
-
- backend:
- image: awesome/backend
- volumes:
- - type: volume
- source: db-data
- target: /data
- volume:
- nocopy: true
- - type: bind
- source: /var/run/postgres/postgres.sock
- target: /var/run/postgres/postgres.sock
- depends_on:
- - monitoring
- extends:
- service: frontend
- links:
- - "db:database"
- db:
- image: postgres
-
-volumes:
- db-data:
\ No newline at end of file
diff --git a/tests/in/111011.yaml b/tests/in/111011.yaml
deleted file mode 100644
index 1fbf1b1..0000000
--- a/tests/in/111011.yaml
+++ /dev/null
@@ -1,46 +0,0 @@
-services:
- frontend:
- image: awesome/webapp
- networks:
- - front-tier
- - back-tier
-
- monitoring:
- image: awesome/monitoring
- networks:
- - admin
-
-
- backend:
- image: awesome/backend
- networks:
- back-tier:
- aliases:
- - database
- admin:
- aliases:
- - mysql
- volumes:
- - type: volume
- source: db-data
- target: /data
- volume:
- nocopy: true
- - type: bind
- source: /var/run/postgres/postgres.sock
- target: /var/run/postgres/postgres.sock
- depends_on:
- - monitoring
- extends:
- service: frontend
- links:
- - "db:database"
- db:
- image: postgres
-
-networks:
- front-tier:
- back-tier:
- admin:
-volumes:
- db-data:
\ No newline at end of file
diff --git a/tests/in/111100.yaml b/tests/in/111100.yaml
deleted file mode 100644
index 23c650b..0000000
--- a/tests/in/111100.yaml
+++ /dev/null
@@ -1,30 +0,0 @@
-services:
- frontend:
- image: awesome/webapp
-
- monitoring:
- image: awesome/monitoring
-
-
- backend:
- image: awesome/backend
- volumes:
- - type: volume
- source: db-data
- target: /data
- volume:
- nocopy: true
- - type: bind
- source: /var/run/postgres/postgres.sock
- target: /var/run/postgres/postgres.sock
- depends_on:
- - monitoring
- ports:
- - "8000:5010"
- links:
- - "db:database"
- db:
- image: postgres
-
-volumes:
- db-data:
\ No newline at end of file
diff --git a/tests/in/111101.yaml b/tests/in/111101.yaml
deleted file mode 100644
index 79ebf03..0000000
--- a/tests/in/111101.yaml
+++ /dev/null
@@ -1,46 +0,0 @@
-services:
- frontend:
- image: awesome/webapp
- networks:
- - front-tier
- - back-tier
-
- monitoring:
- image: awesome/monitoring
- networks:
- - admin
-
-
- backend:
- image: awesome/backend
- networks:
- back-tier:
- aliases:
- - database
- admin:
- aliases:
- - mysql
- volumes:
- - type: volume
- source: db-data
- target: /data
- volume:
- nocopy: true
- - type: bind
- source: /var/run/postgres/postgres.sock
- target: /var/run/postgres/postgres.sock
- depends_on:
- - monitoring
- ports:
- - "8000:5010"
- links:
- - "db:database"
- db:
- image: postgres
-
-networks:
- front-tier:
- back-tier:
- admin:
-volumes:
- db-data:
\ No newline at end of file
diff --git a/tests/in/111110.yaml b/tests/in/111110.yaml
deleted file mode 100644
index 4250938..0000000
--- a/tests/in/111110.yaml
+++ /dev/null
@@ -1,31 +0,0 @@
-services:
- frontend:
- image: awesome/webapp
- monitoring:
- image: awesome/monitoring
-
-
- backend:
- image: awesome/backend
- volumes:
- - type: volume
- source: db-data
- target: /data
- volume:
- nocopy: true
- - type: bind
- source: /var/run/postgres/postgres.sock
- target: /var/run/postgres/postgres.sock
- depends_on:
- - monitoring
- extends:
- service: frontend
- ports:
- - "8000:5010"
- links:
- - "db:database"
- db:
- image: postgres
-
-volumes:
- db-data:
\ No newline at end of file
diff --git a/tests/in/111111.yaml b/tests/in/111111.yaml
deleted file mode 100644
index e506c33..0000000
--- a/tests/in/111111.yaml
+++ /dev/null
@@ -1,48 +0,0 @@
-services:
- frontend:
- image: awesome/webapp
- networks:
- - front-tier
- - back-tier
-
- monitoring:
- image: awesome/monitoring
- networks:
- - admin
-
-
- backend:
- image: awesome/backend
- networks:
- back-tier:
- aliases:
- - database
- admin:
- aliases:
- - mysql
- volumes:
- - type: volume
- source: db-data
- target: /data
- volume:
- nocopy: true
- - type: bind
- source: /var/run/postgres/postgres.sock
- target: /var/run/postgres/postgres.sock
- depends_on:
- - monitoring
- extends:
- service: frontend
- ports:
- - "8000:5010"
- links:
- - "db:database"
- db:
- image: postgres
-
-networks:
- front-tier:
- back-tier:
- admin:
-volumes:
- db-data:
\ No newline at end of file
diff --git a/tests/test_cli.py b/tests/test_cli.py
index 3f427b5..bfcdbcd 100644
--- a/tests/test_cli.py
+++ b/tests/test_cli.py
@@ -1,13 +1,34 @@
-from typer.testing import CliRunner
-from compose_viz import cli
+import os
+import pytest
+from typer.testing import CliRunner
+
+from compose_viz import cli
runner = CliRunner()
-def test_cli():
- input_path = "tests/in/000001.yaml"
- result = runner.invoke(cli.app, [input_path])
+@pytest.mark.parametrize(
+ "test_file_path",
+ [
+ "tests/ymls/builds/docker-compose.yml",
+ "tests/ymls/depends_on/docker-compose.yml",
+ "tests/ymls/extends/docker-compose.yml",
+ "tests/ymls/links/docker-compose.yml",
+ "tests/ymls/networks/docker-compose.yml",
+ "tests/ymls/ports/docker-compose.yml",
+ "tests/ymls/volumes/docker-compose.yml",
+ "examples/full-stack-node-app/docker-compose.yml",
+ "examples/non-normative/docker-compose.yml",
+ ],
+)
+def test_cli(test_file_path: str) -> None:
+ input_path = f"{test_file_path}"
+ output_path = "compose-viz-test.png"
+ result = runner.invoke(cli.app, ["-o", output_path, input_path])
assert result.exit_code == 0
assert f"Successfully parsed {input_path}\n" in result.stdout
+ assert os.path.exists(output_path)
+
+ os.remove(output_path)
diff --git a/tests/test_extends.py b/tests/test_extends.py
new file mode 100644
index 0000000..ad7e4c5
--- /dev/null
+++ b/tests/test_extends.py
@@ -0,0 +1,28 @@
+import pytest
+
+from compose_viz.extends import Extends
+
+
+def test_extend_init_normal() -> None:
+ try:
+ e = Extends(service_name="frontend", from_file="tests/ymls/others/empty.yaml")
+
+ assert e.service_name == "frontend"
+ assert e.from_file == "tests/ymls/others/empty.yaml"
+ except Exception as e:
+ assert False, e
+
+
+def test_extend_init_without_from_file() -> None:
+ try:
+ e = Extends(service_name="frontend")
+
+ assert e.service_name == "frontend"
+ assert e.from_file is None
+ except Exception as e:
+ assert False, e
+
+
+def test_extend_init_without_service_name() -> None:
+ with pytest.raises(TypeError):
+ Extends(from_file="tests/ymls/others/empty.yaml") # type: ignore
diff --git a/tests/test_module.py b/tests/test_module.py
new file mode 100644
index 0000000..136b7a9
--- /dev/null
+++ b/tests/test_module.py
@@ -0,0 +1,5 @@
+import os
+
+
+def test_module():
+ assert os.system("python -m compose_viz") == 0
diff --git a/tests/test_parse_file.py b/tests/test_parse_file.py
index bfc6b4a..f384789 100644
--- a/tests/test_parse_file.py
+++ b/tests/test_parse_file.py
@@ -1,28 +1,266 @@
-from compose_viz.parser import Parser
+import pytest
+
from compose_viz.compose import Compose
+from compose_viz.extends import Extends
+from compose_viz.parser import Parser
+from compose_viz.port import Port, Protocol
from compose_viz.service import Service
+from compose_viz.volume import Volume, VolumeType
-def test_parse_file():
- expected: Compose = Compose([
- Service(
- name='frontend',
- image='awesome/webapp',
- networks=['front-tier', 'back-tier'],
+@pytest.mark.parametrize(
+ "test_file_path, expected",
+ [
+ (
+ "builds/docker-compose",
+ Compose(
+ services=[
+ Service(
+ name="frontend",
+ image="build from './frontend', image: awesome/frontend",
+ ),
+ Service(
+ name="backend",
+ image="build from 'backend' using '../backend.Dockerfile'",
+ ),
+ Service(
+ name="db",
+ image="build from './db'",
+ ),
+ ],
+ ),
),
- Service(
- name='monitoring',
- image='awesome/monitoring',
- networks=['admin'],
+ (
+ "depends_on/docker-compose",
+ Compose(
+ services=[
+ Service(
+ name="frontend",
+ image="awesome/frontend",
+ depends_on=[
+ "db",
+ "redis",
+ ],
+ ),
+ Service(
+ name="backend",
+ image="awesome/backend",
+ depends_on=[
+ "db",
+ "redis",
+ ],
+ ),
+ Service(
+ name="db",
+ image="mysql",
+ ),
+ Service(
+ name="redis",
+ image="redis",
+ ),
+ ],
+ ),
),
- Service(
- name='backend',
- image='awesome/backend',
- networks=['back-tier', 'admin'],
+ (
+ "extends/docker-compose",
+ Compose(
+ services=[
+ Service(
+ name="base",
+ image="alpine:latest",
+ ),
+ Service(
+ name="derive_from_base",
+ image="alpine:edge",
+ extends=Extends(
+ service_name="base",
+ ),
+ ),
+ Service(
+ name="derive_from_file",
+ extends=Extends(
+ service_name="web",
+ from_file="web.yml",
+ ),
+ ),
+ ],
+ ),
),
- ])
-
+ (
+ "links/docker-compose",
+ Compose(
+ services=[
+ Service(
+ name="frontend",
+ image="awesome/frontend",
+ links=[
+ "db:database",
+ ],
+ ),
+ Service(
+ name="db",
+ image="mysql",
+ ),
+ ],
+ ),
+ ),
+ (
+ "networks/docker-compose",
+ Compose(
+ services=[
+ Service(
+ name="frontend",
+ image="awesome/frontend",
+ networks=[
+ "front-tier",
+ "back-tier",
+ ],
+ ),
+ Service(
+ name="monitoring",
+ image="awesome/monitoring",
+ networks=[
+ "admin",
+ ],
+ ),
+ Service(
+ name="backend",
+ image="awesome/backend",
+ networks=[
+ "back-tier",
+ "admin",
+ ],
+ ),
+ ],
+ ),
+ ),
+ (
+ "ports/docker-compose",
+ Compose(
+ services=[
+ Service(
+ name="frontend",
+ image="awesome/frontend",
+ ports=[
+ Port(
+ host_port="0.0.0.0:3000",
+ container_port="3000",
+ ),
+ Port(
+ host_port="0.0.0.0:3000-3005",
+ container_port="3000-3005",
+ ),
+ Port(
+ host_port="0.0.0.0:9090-9091",
+ container_port="8080-8081",
+ ),
+ Port(
+ host_port="0.0.0.0:49100",
+ container_port="22",
+ ),
+ Port(
+ host_port="127.0.0.1:8001",
+ container_port="8001",
+ ),
+ Port(
+ host_port="127.0.0.1:5000-5010",
+ container_port="5000-5010",
+ ),
+ Port(
+ host_port="0.0.0.0:6060",
+ container_port="6060",
+ protocol=Protocol.udp,
+ ),
+ Port(
+ host_port="127.0.0.1:8080",
+ container_port="80",
+ protocol=Protocol.tcp,
+ ),
+ Port(
+ host_port="0.0.0.0:443",
+ container_port="443",
+ ),
+ ],
+ ),
+ ],
+ ),
+ ),
+ (
+ "volumes/docker-compose",
+ Compose(
+ services=[
+ Service(
+ name="backend",
+ image="awesome/backend",
+ volumes=[
+ Volume(
+ source="./data",
+ target="/data",
+ ),
+ Volume(
+ source="/var/run/postgres/postgres.sock",
+ target="/var/run/postgres/postgres.sock",
+ type=VolumeType.bind,
+ ),
+ ],
+ ),
+ Service(
+ name="common",
+ image="busybox",
+ volumes=[
+ Volume(
+ source="common-volume",
+ target="/var/lib/backup/data",
+ ),
+ ],
+ ),
+ Service(
+ name="cli",
+ extends=Extends(
+ service_name="common",
+ ),
+ volumes=[
+ Volume(
+ source="cli-volume",
+ target="/var/lib/backup/data",
+ access_mode="ro,z",
+ ),
+ ],
+ ),
+ ],
+ ),
+ ),
+ ],
+)
+def test_parse_file(test_file_path: str, expected: Compose) -> None:
parser = Parser()
- actual = parser.parse('tests/in/000001.yaml')
+ actual = parser.parse(f"tests/ymls/{test_file_path}.yml")
- assert actual == expected
+ assert len(actual.services) == len(expected.services)
+
+ for actual_service, expected_service in zip(actual.services, expected.services):
+ assert actual_service.name == expected_service.name
+ assert actual_service.image == expected_service.image
+
+ assert len(actual_service.ports) == len(expected_service.ports)
+ for actual_port, expected_port in zip(actual_service.ports, expected_service.ports):
+ assert actual_port.host_port == expected_port.host_port
+ assert actual_port.container_port == expected_port.container_port
+ assert actual_port.protocol == expected_port.protocol
+
+ assert actual_service.networks == expected_service.networks
+
+ assert len(actual_service.volumes) == len(expected_service.volumes)
+ for actual_volume, expected_volume in zip(actual_service.volumes, expected_service.volumes):
+ assert actual_volume.source == expected_volume.source
+ assert actual_volume.target == expected_volume.target
+ assert actual_volume.type == expected_volume.type
+
+ assert actual_service.depends_on == expected_service.depends_on
+ assert actual_service.links == expected_service.links
+
+ assert (actual_service.extends is not None) == (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.from_file == expected_service.extends.from_file
diff --git a/tests/test_parser.py b/tests/test_parser.py
new file mode 100644
index 0000000..0e3d33f
--- /dev/null
+++ b/tests/test_parser.py
@@ -0,0 +1,18 @@
+import pytest
+
+from compose_viz.parser import Parser
+
+
+def test_parser_error_parsing_file() -> None:
+ with pytest.raises(RuntimeError, match=r"Error parsing file 'tests/ymls/others/invalid.yml'.*"):
+ Parser().parse("tests/ymls/others/invalid.yml")
+
+
+def test_parser_invalid_yaml() -> None:
+ with pytest.raises(RuntimeError, match=r"Empty yaml file, aborting."):
+ Parser().parse("tests/ymls/others/empty.yml")
+
+
+def test_parser_no_services_found() -> None:
+ with pytest.raises(RuntimeError, match=r"No services found, aborting."):
+ Parser().parse("tests/ymls/others/no-services.yml")
diff --git a/tests/test_port.py b/tests/test_port.py
new file mode 100644
index 0000000..159c92c
--- /dev/null
+++ b/tests/test_port.py
@@ -0,0 +1,23 @@
+from compose_viz.port import Port, Protocol
+
+
+def test_port_init_normal() -> None:
+ try:
+ p = Port(host_port="8080", container_port="80")
+
+ assert p.host_port == "8080"
+ assert p.container_port == "80"
+ assert p.protocol == Protocol.any
+ except Exception as e:
+ assert False, e
+
+
+def test_port_with_protocol() -> None:
+ try:
+ p = Port(host_port="8080", container_port="80", protocol=Protocol.udp)
+
+ assert p.host_port == "8080"
+ assert p.container_port == "80"
+ assert p.protocol == Protocol.udp
+ except Exception as e:
+ assert False, e
diff --git a/tests/test_version.py b/tests/test_version.py
index 1d2abd6..27ecc67 100644
--- a/tests/test_version.py
+++ b/tests/test_version.py
@@ -1,11 +1,11 @@
from typer.testing import CliRunner
-from compose_viz import cli, __app_name__, __version__
+from compose_viz import __app_name__, __version__, cli
runner = CliRunner()
-def test_version():
+def test_version() -> None:
result = runner.invoke(cli.app, ["--version"])
assert result.exit_code == 0
diff --git a/tests/test_volume.py b/tests/test_volume.py
new file mode 100644
index 0000000..b3651ec
--- /dev/null
+++ b/tests/test_volume.py
@@ -0,0 +1,37 @@
+from compose_viz.volume import Volume, VolumeType
+
+
+def test_volume_init_normal() -> None:
+ try:
+ v = Volume(source="./foo", target="./bar")
+
+ assert v.source == "./foo"
+ assert v.target == "./bar"
+ assert v.type == VolumeType.volume
+ assert v.access_mode == "rw"
+ except Exception as e:
+ assert False, e
+
+
+def test_volume_with_type() -> None:
+ try:
+ v = Volume(source="./foo", target="./bar", type=VolumeType.bind)
+
+ assert v.source == "./foo"
+ assert v.target == "./bar"
+ assert v.type == VolumeType.bind
+ assert v.access_mode == "rw"
+ except Exception as e:
+ assert False, e
+
+
+def test_volume_with_access_mode() -> None:
+ try:
+ v = Volume(source="./foo", target="./bar", access_mode="ro,z")
+
+ assert v.source == "./foo"
+ assert v.target == "./bar"
+ assert v.type == VolumeType.volume
+ assert v.access_mode == "ro,z"
+ except Exception as e:
+ assert False, e
diff --git a/tests/ymls/builds/docker-compose.yml b/tests/ymls/builds/docker-compose.yml
new file mode 100644
index 0000000..fff4866
--- /dev/null
+++ b/tests/ymls/builds/docker-compose.yml
@@ -0,0 +1,15 @@
+version: "3.9"
+
+services:
+ frontend:
+ image: awesome/frontend
+ build: ./frontend
+
+ backend:
+ build:
+ context: backend
+ dockerfile: ../backend.Dockerfile
+
+ db:
+ build:
+ context: ./db
diff --git a/tests/ymls/depends_on/docker-compose.yml b/tests/ymls/depends_on/docker-compose.yml
new file mode 100644
index 0000000..e949719
--- /dev/null
+++ b/tests/ymls/depends_on/docker-compose.yml
@@ -0,0 +1,19 @@
+version: "3.9"
+
+services:
+ frontend:
+ image: awesome/frontend
+ depends_on:
+ db:
+ condition: service_healthy
+ redis:
+ condition: service_started
+ backend:
+ image: awesome/backend
+ depends_on:
+ - db
+ - redis
+ db:
+ image: mysql
+ redis:
+ image: redis
diff --git a/tests/ymls/extends/docker-compose.yml b/tests/ymls/extends/docker-compose.yml
new file mode 100644
index 0000000..caf2a1e
--- /dev/null
+++ b/tests/ymls/extends/docker-compose.yml
@@ -0,0 +1,14 @@
+version: "3.9"
+
+services:
+ base:
+ image: alpine:latest
+ tty: true
+ derive_from_base:
+ image: alpine:edge
+ extends:
+ service: base
+ derive_from_file:
+ extends:
+ file: web.yml
+ service: web
diff --git a/tests/ymls/extends/web.yml b/tests/ymls/extends/web.yml
new file mode 100644
index 0000000..1676371
--- /dev/null
+++ b/tests/ymls/extends/web.yml
@@ -0,0 +1,5 @@
+version: "3.9"
+
+services:
+ web:
+ image: awesome/web
diff --git a/tests/in/100000.yaml b/tests/ymls/links/docker-compose.yml
similarity index 53%
rename from tests/in/100000.yaml
rename to tests/ymls/links/docker-compose.yml
index c214153..cac78e9 100644
--- a/tests/in/100000.yaml
+++ b/tests/ymls/links/docker-compose.yml
@@ -1,9 +1,9 @@
version: "3.9"
-services:
- web:
- build: .
+services:
+ frontend:
+ image: awesome/frontend
links:
- "db:database"
db:
- image: postgres
\ No newline at end of file
+ image: mysql
diff --git a/tests/in/000001.yaml b/tests/ymls/networks/docker-compose.yml
similarity index 86%
rename from tests/in/000001.yaml
rename to tests/ymls/networks/docker-compose.yml
index 1683eba..9210e39 100644
--- a/tests/in/000001.yaml
+++ b/tests/ymls/networks/docker-compose.yml
@@ -1,6 +1,8 @@
+version: "3.9"
+
services:
frontend:
- image: awesome/webapp
+ image: awesome/frontend
networks:
- front-tier
- back-tier
@@ -23,4 +25,4 @@ services:
networks:
front-tier:
back-tier:
- admin:
\ No newline at end of file
+ admin:
diff --git a/tests/ymls/others/empty.yml b/tests/ymls/others/empty.yml
new file mode 100644
index 0000000..e69de29
diff --git a/tests/ymls/others/invalid.yml b/tests/ymls/others/invalid.yml
new file mode 100644
index 0000000..8e7a231
--- /dev/null
+++ b/tests/ymls/others/invalid.yml
@@ -0,0 +1,3 @@
+what-is-this:
+ "an invalid yaml"
+ "test purpose"
diff --git a/tests/ymls/others/no-services.yml b/tests/ymls/others/no-services.yml
new file mode 100644
index 0000000..a0bf4a1
--- /dev/null
+++ b/tests/ymls/others/no-services.yml
@@ -0,0 +1,3 @@
+what-is-this:
+ - "a yaml file without services"
+ - "test purpose"
diff --git a/tests/ymls/ports/docker-compose.yml b/tests/ymls/ports/docker-compose.yml
new file mode 100644
index 0000000..642a975
--- /dev/null
+++ b/tests/ymls/ports/docker-compose.yml
@@ -0,0 +1,18 @@
+version: "3.9"
+
+services:
+ frontend:
+ image: awesome/frontend
+ ports:
+ - "3000"
+ - "3000-3005"
+ - "9090-9091:8080-8081"
+ - "49100:22"
+ - "127.0.0.1:8001:8001"
+ - "127.0.0.1:5000-5010:5000-5010"
+ - "6060:6060/udp"
+ - target: 80
+ host_ip: 127.0.0.1
+ published: 8080
+ protocol: tcp
+ - target: 443
diff --git a/tests/ymls/volumes/docker-compose.yml b/tests/ymls/volumes/docker-compose.yml
new file mode 100644
index 0000000..4edb77c
--- /dev/null
+++ b/tests/ymls/volumes/docker-compose.yml
@@ -0,0 +1,23 @@
+version: "3.9"
+
+services:
+ backend:
+ image: awesome/backend
+ volumes:
+ - "./data:/data"
+ - type: bind
+ source: /var/run/postgres/postgres.sock
+ target: /var/run/postgres/postgres.sock
+ common:
+ image: busybox
+ volumes:
+ - common-volume:/var/lib/backup/data:rw,z
+ cli:
+ extends:
+ service: common
+ volumes:
+ - cli-volume:/var/lib/backup/data:ro
+
+volumes:
+ common-volume:
+ cli-volume: