mirror of
https://github.com/Cian-H/I-Form_Server_Node_Deployer.git
synced 2025-12-22 22:22:02 +00:00
Full type coverage with mypy
This commit is contained in:
17
poetry.lock
generated
17
poetry.lock
generated
@@ -328,6 +328,21 @@ websocket-client = ">=0.32.0"
|
|||||||
[package.extras]
|
[package.extras]
|
||||||
ssh = ["paramiko (>=2.4.3)"]
|
ssh = ["paramiko (>=2.4.3)"]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "docker-stubs"
|
||||||
|
version = "0.1.0"
|
||||||
|
description = "Stubs package for the Python Docker API."
|
||||||
|
optional = false
|
||||||
|
python-versions = "^3.8"
|
||||||
|
files = []
|
||||||
|
develop = false
|
||||||
|
|
||||||
|
[package.source]
|
||||||
|
type = "git"
|
||||||
|
url = "https://github.com/rdozier-work/docker-stubs"
|
||||||
|
reference = "HEAD"
|
||||||
|
resolved_reference = "9de7906804ae912f1d644c97b617ac77e784fca8"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "executing"
|
name = "executing"
|
||||||
version = "2.0.1"
|
version = "2.0.1"
|
||||||
@@ -1092,4 +1107,4 @@ h11 = ">=0.9.0,<1"
|
|||||||
[metadata]
|
[metadata]
|
||||||
lock-version = "2.0"
|
lock-version = "2.0"
|
||||||
python-versions = "^3.12"
|
python-versions = "^3.12"
|
||||||
content-hash = "c74d1dcf762e5266d41c81b62bef444f065124fede9b3a34176cd9a6b93d9c2b"
|
content-hash = "0da96ff6654ff4c33b776829e34f78ba2725a0de30980ec5eaab86a8b7abbead"
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ black = "^23.10.1"
|
|||||||
snoop = "^0.4.3"
|
snoop = "^0.4.3"
|
||||||
pytest = "^7.4.3"
|
pytest = "^7.4.3"
|
||||||
mypy = "^1.6.1"
|
mypy = "^1.6.1"
|
||||||
|
docker-stubs = {git = "https://github.com/rdozier-work/docker-stubs"}
|
||||||
|
|
||||||
[tool.poetry.scripts]
|
[tool.poetry.scripts]
|
||||||
node_deployer = "node_deployer.__main__:main"
|
node_deployer = "node_deployer.__main__:main"
|
||||||
|
|||||||
@@ -81,8 +81,11 @@ def convert_json_via_fuelignition(
|
|||||||
bytestream = io.BytesIO(b"".join(chunk for chunk in filestream))
|
bytestream = io.BytesIO(b"".join(chunk for chunk in filestream))
|
||||||
bytestream.seek(0)
|
bytestream.seek(0)
|
||||||
tar = tarfile.open(fileobj=bytestream)
|
tar = tarfile.open(fileobj=bytestream)
|
||||||
|
container_image = tar.extractfile(tar.getmembers()[0].name)
|
||||||
|
if container_image is None:
|
||||||
|
raise Exception("Failed to extract image from tarfile")
|
||||||
with open(host_image_path, "wb+") as f:
|
with open(host_image_path, "wb+") as f:
|
||||||
f.write(tar.extractfile(tar.getmembers()[0].name).read())
|
f.write(container_image.read())
|
||||||
|
|
||||||
|
|
||||||
def build_fuelignition() -> docker.models.images.Image:
|
def build_fuelignition() -> docker.models.images.Image:
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from types import SimpleNamespace
|
from types import SimpleNamespace
|
||||||
|
from typing import Union
|
||||||
|
|
||||||
import docker
|
import docker
|
||||||
import tomllib
|
import tomllib
|
||||||
@@ -9,7 +10,7 @@ CLIENT = docker.from_env(version="auto")
|
|||||||
MAX_PORT: int = 65535
|
MAX_PORT: int = 65535
|
||||||
PROJECT_ROOT: Path = Path(__file__).parent.parent.parent.absolute()
|
PROJECT_ROOT: Path = Path(__file__).parent.parent.parent.absolute()
|
||||||
|
|
||||||
type ConfigLabel = str | list[str]
|
ConfigLabel = Union[str, list[str]] # After PEP695 support: type ConfigLabel = str | list[str]
|
||||||
|
|
||||||
|
|
||||||
class Config(SimpleNamespace):
|
class Config(SimpleNamespace):
|
||||||
|
|||||||
@@ -1,19 +1,20 @@
|
|||||||
from fnmatch import fnmatch
|
from fnmatch import fnmatch
|
||||||
import ipaddress
|
import ipaddress
|
||||||
from typing import Annotated
|
from typing import Annotated, Optional, Union
|
||||||
|
|
||||||
from docker.types import Mount
|
from docker.types import Mount
|
||||||
import typer
|
import typer
|
||||||
from typing import Tuple
|
|
||||||
|
|
||||||
from .config import config
|
|
||||||
from .cli import cli_spinner
|
from .cli import cli_spinner
|
||||||
|
from .config import config
|
||||||
from .create_img import create_img
|
from .create_img import create_img
|
||||||
from .debug import debug_guard
|
from .debug import debug_guard
|
||||||
from .utils import ensure_build_dir
|
from .utils import ensure_build_dir
|
||||||
|
|
||||||
|
|
||||||
type IPAddress = ipaddress.IPv4Address | ipaddress.IPv6Address
|
# When PEP695 is supported this line should be:
|
||||||
|
# type IPAddress = ipaddress.IPv4Address | ipaddress.IPv6Address
|
||||||
|
IPAddress = Union[ipaddress.IPv4Address, ipaddress.IPv6Address]
|
||||||
|
|
||||||
|
|
||||||
def filter_validation_response(response: str) -> str:
|
def filter_validation_response(response: str) -> str:
|
||||||
@@ -60,18 +61,18 @@ def validation_result() -> str:
|
|||||||
)
|
)
|
||||||
if config.CLEANUP_IMAGES:
|
if config.CLEANUP_IMAGES:
|
||||||
image.remove(force=True)
|
image.remove(force=True)
|
||||||
return response
|
return response.decode()
|
||||||
|
|
||||||
|
|
||||||
@cli_spinner(description="Validating ignition image", total=None)
|
@cli_spinner(description="Validating ignition image", total=None)
|
||||||
def validate() -> Tuple[bool, str]:
|
def validate() -> tuple[bool, str]:
|
||||||
"""Validates the ignition image
|
"""Validates the ignition image
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
Tuple[bool, str]: A tuple containing a boolean indicating whether
|
tuple[bool, str]: A tuple containing a boolean indicating whether
|
||||||
the validation was successful and the response from the validation
|
the validation was successful and the response from the validation
|
||||||
"""
|
"""
|
||||||
response = validation_result().decode()
|
response = validation_result()
|
||||||
response = filter_validation_response(response)
|
response = filter_validation_response(response)
|
||||||
return (not bool(response), response)
|
return (not bool(response), response)
|
||||||
|
|
||||||
@@ -96,7 +97,7 @@ def write_disk(disk: str) -> None:
|
|||||||
@ensure_build_dir
|
@ensure_build_dir
|
||||||
def create_ignition_disk(
|
def create_ignition_disk(
|
||||||
disk: Annotated[
|
disk: Annotated[
|
||||||
str,
|
Optional[str],
|
||||||
typer.Option(
|
typer.Option(
|
||||||
"--disk",
|
"--disk",
|
||||||
"-d",
|
"-d",
|
||||||
@@ -114,7 +115,7 @@ def create_ignition_disk(
|
|||||||
),
|
),
|
||||||
] = "node",
|
] = "node",
|
||||||
password: Annotated[
|
password: Annotated[
|
||||||
str,
|
Optional[str],
|
||||||
typer.Option(
|
typer.Option(
|
||||||
"--password",
|
"--password",
|
||||||
"-p",
|
"-p",
|
||||||
@@ -125,7 +126,7 @@ def create_ignition_disk(
|
|||||||
),
|
),
|
||||||
] = None,
|
] = None,
|
||||||
switch_ip: Annotated[
|
switch_ip: Annotated[
|
||||||
IPAddress,
|
Optional[IPAddress],
|
||||||
typer.Option(
|
typer.Option(
|
||||||
"--switch-ip",
|
"--switch-ip",
|
||||||
"-ip",
|
"-ip",
|
||||||
@@ -146,7 +147,7 @@ def create_ignition_disk(
|
|||||||
),
|
),
|
||||||
] = 4789,
|
] = 4789,
|
||||||
swarm_token: Annotated[
|
swarm_token: Annotated[
|
||||||
str,
|
Optional[str],
|
||||||
typer.Option(
|
typer.Option(
|
||||||
"--swarm-token",
|
"--swarm-token",
|
||||||
"-t",
|
"-t",
|
||||||
@@ -194,6 +195,10 @@ def create_ignition_disk(
|
|||||||
Raises:
|
Raises:
|
||||||
typer.Exit: Exit CLI if the ignition image is invalid
|
typer.Exit: Exit CLI if the ignition image is invalid
|
||||||
"""
|
"""
|
||||||
|
# Guard against the user specifying no disk
|
||||||
|
if disk is None:
|
||||||
|
raise typer.BadParameter("No disk specified")
|
||||||
|
|
||||||
create_img(
|
create_img(
|
||||||
hostname = hostname,
|
hostname = hostname,
|
||||||
password = password,
|
password = password,
|
||||||
|
|||||||
@@ -1,18 +1,19 @@
|
|||||||
import ipaddress
|
import ipaddress
|
||||||
import json
|
import json
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import Annotated
|
from typing import Annotated, Optional, Union
|
||||||
|
|
||||||
import typer
|
import typer
|
||||||
|
|
||||||
from .config import config
|
|
||||||
from .autoignition import json_to_img
|
from .autoignition import json_to_img
|
||||||
from .cli import cli_spinner
|
from .cli import cli_spinner
|
||||||
|
from .config import config
|
||||||
from .debug import debug_guard
|
from .debug import debug_guard
|
||||||
from .utils import ensure_build_dir
|
from .utils import ensure_build_dir
|
||||||
|
|
||||||
|
# When PEP695 is supported this line should be:
|
||||||
type IPAddress = ipaddress.IPv4Address | ipaddress.IPv6Address
|
# type IPAddress = ipaddress.IPv4Address | ipaddress.IPv6Address
|
||||||
|
IPAddress = Union[ipaddress.IPv4Address, ipaddress.IPv6Address]
|
||||||
|
|
||||||
|
|
||||||
def load_template() -> dict:
|
def load_template() -> dict:
|
||||||
@@ -99,7 +100,7 @@ def create_img(
|
|||||||
),
|
),
|
||||||
] = "node",
|
] = "node",
|
||||||
password: Annotated[
|
password: Annotated[
|
||||||
str,
|
Optional[str],
|
||||||
typer.Option(
|
typer.Option(
|
||||||
"--password",
|
"--password",
|
||||||
"-p",
|
"-p",
|
||||||
@@ -110,7 +111,7 @@ def create_img(
|
|||||||
),
|
),
|
||||||
] = None,
|
] = None,
|
||||||
switch_ip: Annotated[
|
switch_ip: Annotated[
|
||||||
IPAddress,
|
Optional[IPAddress],
|
||||||
typer.Option(
|
typer.Option(
|
||||||
"--switch-ip",
|
"--switch-ip",
|
||||||
"-ip",
|
"-ip",
|
||||||
@@ -131,7 +132,7 @@ def create_img(
|
|||||||
),
|
),
|
||||||
] = 4789,
|
] = 4789,
|
||||||
swarm_token: Annotated[
|
swarm_token: Annotated[
|
||||||
str,
|
Optional[str],
|
||||||
typer.Option(
|
typer.Option(
|
||||||
"--swarm-token",
|
"--swarm-token",
|
||||||
"-t",
|
"-t",
|
||||||
@@ -194,10 +195,13 @@ def create_img(
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# Guards against the user not specifying a password
|
||||||
|
if password is None:
|
||||||
|
raise typer.BadParameter("Password must be specified")
|
||||||
|
|
||||||
# Create ignition configuration
|
# Create ignition configuration
|
||||||
ignition_config = load_template()
|
|
||||||
ignition_config = apply_ignition_settings(
|
ignition_config = apply_ignition_settings(
|
||||||
ignition_config,
|
load_template(),
|
||||||
hostname,
|
hostname,
|
||||||
password,
|
password,
|
||||||
swarm_config,
|
swarm_config,
|
||||||
|
|||||||
@@ -1,12 +1,14 @@
|
|||||||
|
from typing import Any, Dict
|
||||||
|
|
||||||
import typer
|
import typer
|
||||||
|
|
||||||
from .config import config
|
|
||||||
from .autoignition import json_to_img
|
from .autoignition import json_to_img
|
||||||
|
from .config import config
|
||||||
from .create_disk import create_ignition_disk
|
from .create_disk import create_ignition_disk
|
||||||
from .create_img import create_img
|
from .create_img import create_img
|
||||||
|
|
||||||
|
|
||||||
cmd_params = {
|
cmd_params: Dict[Any, Any] = {
|
||||||
"no_args_is_help": True,
|
"no_args_is_help": True,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -15,9 +17,19 @@ app = typer.Typer(
|
|||||||
**cmd_params,
|
**cmd_params,
|
||||||
)
|
)
|
||||||
|
|
||||||
app.command(**cmd_params)(create_img)
|
# Register commands
|
||||||
app.command(**cmd_params)(create_ignition_disk)
|
app.command(
|
||||||
app.command(**cmd_params)(json_to_img)
|
help = str(create_ignition_disk.__doc__).split("Args:")[0].strip(),
|
||||||
|
**cmd_params
|
||||||
|
)(create_ignition_disk)
|
||||||
|
app.command(
|
||||||
|
help = str(create_img.__doc__).split("Args:")[0].strip(),
|
||||||
|
**cmd_params
|
||||||
|
)(create_img)
|
||||||
|
app.command(
|
||||||
|
help = str(json_to_img.__doc__).split("Args:")[0].strip(),
|
||||||
|
**cmd_params
|
||||||
|
)(json_to_img)
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
config.update_config("cli")
|
config.update_config("cli")
|
||||||
|
|||||||
Reference in New Issue
Block a user