-
-
Notifications
You must be signed in to change notification settings - Fork 0
refactor: Refactor into python #5
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,19 @@ | ||
| name: Docker Publish Dev | ||
|
|
||
| on: | ||
| workflow_dispatch: | ||
| push: | ||
| branches: | ||
| - dev | ||
|
|
||
| permissions: | ||
| packages: write | ||
|
|
||
| jobs: | ||
| call-reusable-workflow: | ||
| uses: JourneyDocker/github-workflows/.github/workflows/docker-publish.yml@main | ||
| with: | ||
| tag: dev | ||
| platforms: linux/amd64 | ||
| registry: github | ||
| secrets: inherit |
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
| @@ -1,7 +1,16 @@ | ||||||
| FROM alpine:3.22.2 | ||||||
| FROM python:3.14.1-alpine | ||||||
|
|
||||||
| # Install required packages | ||||||
| RUN apk add --no-cache curl jq tzdata | ||||||
| RUN apk add --no-cache curl tzdata procps | ||||||
|
|
||||||
| # Set working directory | ||||||
| WORKDIR /app | ||||||
|
|
||||||
| # Copy source code | ||||||
| COPY autoheal/ /app/autoheal/ | ||||||
|
|
||||||
| # Install Python dependencies | ||||||
| RUN pip install --no-cache-dir requests docker | ||||||
|
||||||
| RUN pip install --no-cache-dir requests docker | |
| RUN pip install --no-cache-dir requests==2.31.0 docker==7.0.0 |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,3 @@ | ||
| """Docker Autoheal - Monitor and automatically restart unhealthy Docker containers.""" | ||
|
|
||
| __version__ = "2.0.0" |
| Original file line number | Diff line number | Diff line change | ||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,55 @@ | ||||||||||||
| """Main entry point for Docker Autoheal.""" | ||||||||||||
|
|
||||||||||||
| import signal | ||||||||||||
| import sys | ||||||||||||
| import time | ||||||||||||
| import os | ||||||||||||
| import threading | ||||||||||||
| from .config import Config | ||||||||||||
| from .health_monitor import monitor_containers | ||||||||||||
| from .logging_utils import setup_logging | ||||||||||||
|
||||||||||||
| from .logging_utils import setup_logging | |
| from .logging_utils import setup_logging |
Copilot
AI
Dec 3, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The version attribute lookup uses __import__('autoheal').__version__, which will fail if the autoheal package doesn't have a __version__ attribute defined. Based on the autoheal/__init__.py file, __version__ is defined, but this approach is fragile. Consider importing it directly: from . import __version__ at the top of the file.
Copilot
AI
Dec 3, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The masking logic for sensitive URLs only applies to 'webhook_url' (case-sensitive check), but there are other potentially sensitive values like 'apprise_url' that should also be masked. Consider applying the masking logic to all URL-related configuration values.
Copilot
AI
Dec 3, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Index error: os.execvp(sys.argv[1], sys.argv[1:]) will fail when len(sys.argv) == 1 (no arguments provided). This code path is reached when the script is run without arguments or with arguments other than "autoheal". Add a check or error message for this case.
| # Exec other commands | |
| # Exec other commands | |
| if len(sys.argv) == 1: | |
| print("Error: No command provided to execute.", file=sys.stderr) | |
| sys.exit(1) |
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
| @@ -0,0 +1,56 @@ | ||||||
| """Configuration management for Docker Autoheal.""" | ||||||
|
|
||||||
| import os | ||||||
| from dataclasses import dataclass, field | ||||||
|
|
||||||
|
|
||||||
| @dataclass | ||||||
| class Config: | ||||||
| """Configuration class for environment variables.""" | ||||||
|
|
||||||
| docker_sock: str = field(default_factory=lambda: os.getenv("DOCKER_SOCK", "/var/run/docker.sock")) | ||||||
| curl_timeout: int = field(default_factory=lambda: int(os.getenv("CURL_TIMEOUT", "30"))) | ||||||
| webhook_url: str = field(default_factory=lambda: os.getenv("WEBHOOK_URL", "")) | ||||||
| webhook_json_entry: str = field(default_factory=lambda: os.getenv("WEBHOOK_JSON_ENTRY", "text")) | ||||||
|
||||||
| webhook_json_entry: str = field(default_factory=lambda: os.getenv("WEBHOOK_JSON_ENTRY", "text")) | |
| webhook_json_entry: str = field(default_factory=lambda: os.getenv("WEBHOOK_JSON_ENTRY") or os.getenv("WEBHOOK_JSON_KEY", "text")) |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,76 @@ | ||
| """Docker API interaction functions.""" | ||
|
|
||
| import docker | ||
| from .config import Config | ||
| from .logging_utils import get_logger | ||
|
|
||
| logger = get_logger() | ||
|
|
||
|
|
||
| def get_docker_client(config: Config) -> docker.DockerClient: | ||
| """Get Docker client.""" | ||
| kwargs = {} | ||
| if config.unix_sock: | ||
| kwargs["base_url"] = f"unix://{config.unix_sock}" | ||
| elif config.docker_sock.startswith("tcp://"): | ||
| kwargs["base_url"] = config.docker_sock | ||
| elif config.docker_sock.startswith("tcps://"): | ||
| tls_config = docker.tls.TLSConfig( | ||
| ca_cert=config.ca_cert, | ||
| client_cert=(config.client_cert, config.client_key), | ||
| verify=True | ||
| ) | ||
| kwargs["base_url"] = config.docker_sock | ||
| kwargs["tls"] = tls_config | ||
| else: | ||
| pass # default | ||
| try: | ||
| return docker.DockerClient(**kwargs) | ||
| except Exception as e: | ||
| logger.error(f"Failed to create Docker client: {e}") | ||
| raise | ||
|
Comment on lines
+10
to
+31
|
||
|
|
||
|
|
||
| def get_container_info(config: Config) -> list: | ||
| """Get list of unhealthy containers.""" | ||
| try: | ||
| client = get_docker_client(config) | ||
| filters = {"health": ["unhealthy"]} | ||
| if config.autoheal_container_label != "all": | ||
| filters["label"] = [f"{config.autoheal_container_label}=true"] | ||
| if config.autoheal_only_monitor_running: | ||
| filters["status"] = ["running"] | ||
| return client.containers.list(filters=filters) | ||
| except Exception as e: | ||
| logger.error(f"Failed to get container info: {e}") | ||
| return [] | ||
|
|
||
|
|
||
| def restart_container(container_id: str, timeout: int, config: Config) -> bool: | ||
| """Restart a container.""" | ||
| try: | ||
| client = get_docker_client(config) | ||
| container = client.containers.get(container_id) | ||
| container.restart(timeout=timeout) | ||
| return True | ||
| except docker.errors.APIError as e: | ||
| logger.error(f"Failed to restart container {container_id}: {e}") | ||
| return False | ||
| except Exception as e: | ||
| logger.error(f"Unexpected error restarting container {container_id}: {e}") | ||
| return False | ||
|
|
||
|
|
||
| def stop_container(container_id: str, timeout: int, config: Config) -> bool: | ||
| """Stop a container.""" | ||
| try: | ||
| client = get_docker_client(config) | ||
| container = client.containers.get(container_id) | ||
| container.stop(timeout=timeout) | ||
| return True | ||
| except docker.errors.APIError as e: | ||
| logger.error(f"Failed to stop container {container_id}: {e}") | ||
| return False | ||
| except Exception as e: | ||
| logger.error(f"Unexpected error stopping container {container_id}: {e}") | ||
| return False | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The package
curlis installed but not used by the Python application. It was needed in the shell script version for Docker API calls, but the Python version uses thedockerlibrary instead. Consider removing it unless it's needed for debugging purposes. Similarly,procpsis added but may not be necessary.