IMPORTANT: This system is designed for Python scripts and bots only. Do NOT use GUI libraries like
tkinter,PyQt,wxPython, or any graphical libraries as they will cause execution failures in the serverless environment.
- ποΈ Modular Architecture: Complete codebase refactoring with separated routes, services, models, and utilities
- π¦ Distributed Structure: Code organized by endpoints for better maintainability and scalability
- π Secure Authentication System: JWT-based authentication with random credentials generated at startup
- π Parameterized Program Execution: Configure command-line parameters for each program
- π Enhanced Statistics: Detailed execution breakdown by status and program name
- π³ LibreOffice Integration: Base image now includes LibreOffice suite for document processing
- π External Scripts Support: Mount external directories as volumes for scripts/bots outside the project
- π§ Improved Base Image: Upgraded pip and added comprehensive LibreOffice packages
- π Better Logging: Enhanced parameter logging and execution tracking
- π‘οΈ Endpoint Protection: All API endpoints now require authentication
- Code Organization: Separated concerns into routes/, services/, models/, and utils/ directories
- Maintainability: Each endpoint has its own route file for easier maintenance
- Scalability: Easy to add new endpoints and services without modifying core files
- Configuration Management: Added parameters field to program configuration
- Multi-language Parameter Support: Parameters work with Python, Node.js, and Shell scripts
- Security Enhancement: Credentials shown only once at startup for security
- Resource Optimization: Updated CPU limits and memory configuration
- Error Handling: Better validation and error messages for parameters
- Documentation: Comprehensive examples and usage guides
- Port Consistency: Fixed port configuration across all files (8001)
- Service Name Alignment: Corrected service names in scripts and documentation
- Path Configuration: Fixed hardcoded paths and environment variable usage
- Resource Limits: Aligned CPU limits between configuration and Docker Compose
- Authentication Flow: Streamlined login process and token management
- Overview
- Architecture
- Installation
- Authentication
- Configuration
- Parameterized Execution
- Custom Docker Images
- Usage
- API Endpoints
- Container Monitoring
- Actions System
- Log Management
- Troubleshooting
PyExecutorHub is a serverless execution platform that allows you to run Python scripts and bots in isolated Docker containers. It provides a REST API for program execution, monitoring, and management with advanced container monitoring capabilities.
- π Secure Authentication: JWT-based authentication with random credentials
- π Parameterized Execution: Configure command-line parameters for each program
- ποΈ Isolated Execution: Each program runs in its own Docker container
- π³ Custom Docker Images: Use specific images for different runtime requirements
- π Real-time Monitoring: Track execution status and detailed statistics
- π Container Monitoring: View logs by Docker image and active containers
- βοΈ Flexible Configuration: Easy setup and customization
- π§ Actions System: Pre and post-execution hooks
- π Multi-language Support: Python, Node.js, and shell scripts
- β‘ Concurrent Control: Prevent system overload with execution limits
- π LibreOffice Integration: Document processing capabilities
βββββββββββββββββββ ββββββββββββββββββββ βββββββββββββββββββ
β Client/API βββββΆβ FastAPI Server βββββΆβ Docker Engine β
β (HTTP/REST) β β (Port 8000) β β (Containers) β
βββββββββββββββββββ ββββββββββββββββββββ βββββββββββββββββββ
β
βΌ
ββββββββββββββββββββ
β Program Files β
β (Scripts/Bots) β
ββββββββββββββββββββ
The codebase is now organized in a modular structure for better maintainability:
PyExecutorHub/
βββ api.py # Main FastAPI application entry point
βββ routes/ # API route modules (organized by endpoint)
β βββ auth.py # Authentication routes (/auth)
β βββ programs.py # Program management routes (/programs)
β βββ executions.py # Execution routes (/executions)
β βββ containers.py # Container monitoring routes (/containers)
β βββ images.py # Docker image routes (/images)
βββ services/ # Business logic services
β βββ auth_service.py # Authentication service
β βββ config_service.py # Configuration management
β βββ execution_service.py # Program execution logic
β βββ execution_storage.py # Execution storage management
βββ models/ # Pydantic models
β βββ auth_models.py # Authentication models
β βββ execution_models.py # Execution models
β βββ program_models.py # Program models
βββ utils/ # Utility modules
β βββ docker_utils.py # Docker operations
β βββ log_formatter.py # Log formatting utilities
βββ scripts/ # Example scripts
βββ bots/ # Example bots
βββ actions/ # Pre/post execution hooks
βββ config.yaml # Program configuration
βββ docker-compose.yml # Docker orchestration
- β Separation of Concerns: Each module has a single responsibility
- β Easy Maintenance: Changes to one endpoint don't affect others
- β Scalability: Add new endpoints without modifying core files
- β Testability: Each module can be tested independently
- β Reusability: Services and utilities can be reused across routes
- Docker and Docker Compose
- Python 3.11+
- Git
# Clone the repository
git clone https://github.com/simetb/PyExecutorHub.git
cd PyExecutorHub
# Copy environment configuration
cp env.example .env
# Edit .env with your settings
nano .env
# Start the system (builds both base image and API)
docker compose up -d --build
# Or use the installer script
chmod +x install.sh
./install.shUse the provided installer script for a complete setup:
# Make script executable
chmod +x install.sh
# Run installer
./install.shThe installer will:
- β Check system requirements
- β Configure environment
- β Build base image with LibreOffice
- β Build and start API service
- β Test the installation
- β Show usage instructions
Edit .env file:
# Project configuration
HOST_PROJECT_DIR=/path/to/your/project
MAX_EXECUTIONS=50
EXECUTION_TIMEOUT=300
# Resource configuration
MEMORY_LIMIT=1g
CPU_LIMIT=0.5
# Network configuration
API_HOST=0.0.0.0
API_PORT=8001PyExecutorHub now includes a secure authentication system that generates random credentials at startup and uses JWT tokens for API access.
- π Random Credentials: Username and password generated automatically at startup
- π« JWT Tokens: Secure token-based authentication for API access
- β° Token Expiration: Tokens expire after 24 hours for security
- π‘οΈ Endpoint Protection: All API endpoints require authentication
- π One-time Display: Credentials shown only once at startup
docker compose up -d --buildThe system will display credentials at startup:
π CREDENCIALES DE USO
============================================================
π€ Usuario: abc123def
π Password: xY9#mK2$pL8
============================================================
π‘ Usa estas credenciales para hacer login en /auth/login
π Las credenciales solo se muestran una vez por seguridad
============================================================
curl -X POST http://localhost:8001/auth/login \
-H "Content-Type: application/json" \
-d '{"username": "abc123def", "password": "xY9#mK2$pL8"}'Response:
{
"access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"token_type": "bearer",
"expires_in": 86400,
"username": "abc123def"
}# Get programs list
curl -H "Authorization: Bearer YOUR_TOKEN" http://localhost:8001/programs
# Execute a program
curl -X POST http://localhost:8001/execute \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{"program_id": "example_script"}'- POST
/auth/login - Body:
{"username": "string", "password": "string"} - Response: JWT token and user information
- Credentials are generated once at startup and shown only once
- No endpoint to retrieve credentials after startup for security
- Tokens expire after 24 hours and must be renewed
- All API endpoints require authentication except
/healthand/auth/login
The system uses a config.yaml file to define available scripts and bots. This file is mounted as a volume, allowing you to modify the configuration without rebuilding the container.
scripts:
# Example 1: Basic script without custom Docker image
example_script:
id: "example_script"
name: "Example Script"
path: "scripts/example_script" # Relative path
description: "Example script without custom Docker image"
enabled: true
main_file: "main.py"
# docker_image: Not specified, uses default
# parameters: Not specified, runs without parameters
# Example 2: Script with custom Docker image
script_with_custom_image:
id: "script_with_custom_image"
name: "Script with Custom Docker Image"
path: "scripts/custom_script"
description: "Script that uses a custom Docker image"
enabled: true
main_file: "run.py"
docker_image: "custom-image:latest" # Custom Docker image
# Example 3: Script with parameters
script_with_parameters:
id: "script_with_parameters"
name: "Script with Parameters"
path: "scripts/parameterized_script"
description: "Script that accepts command-line parameters"
enabled: true
main_file: "main.py"
parameters: "--process proceso_2 --verbose" # Command-line parameters
# Example 4: External script (requires volume mount)
external_script:
id: "external_script"
name: "External Script"
path: "/docker/scripts/external_script" # Absolute path
description: "Script located outside the project directory"
enabled: true
main_file: "run.py"
docker_image: "external-script:latest"
bots:
example_bot:
id: "example_bot"
name: "Example Bot"
path: "bots/example_bot"
description: "Example bot"
enabled: true
main_file: "run.py"
parameters: "--mode production --verbose"
# General system configuration
settings:
docker_image: "pyexecutorhub-base" # Default image for programs without custom image
timeout_seconds: 300 # 5 minutes default
max_concurrent_executions: 5
memory_limit: "1g" # Recommended: 1GB for most cases
cpu_limit: "0.5" # CPU limit per container (50% of one core)id: Unique identifier for the programname: Descriptive namepath: Path to program directory (relative or absolute)description: Program descriptionenabled:trueorfalseto enable/disablemain_file: Main file to execute (main.py,run.py,app.js, etc.)
docker_image: Custom Docker image (if not specified, uses default)parameters: Command-line parameters to pass to the programpath_docker_compose_run: Path to docker-compose file for docker compose execution (alternative to docker run)
PyExecutorHub supports executing programs using docker compose up instead of the standard docker run method. This is useful for complex multi-container applications or when you need to orchestrate multiple services.
Add path_docker_compose_run to your program configuration:
scripts:
docker_compose_script:
id: "docker_compose_script"
name: "Docker Compose Script"
path: "scripts/docker_compose_script"
description: "Script that uses docker compose up"
enabled: true
path_docker_compose_run: "docker-compose.yml" # Path to docker-compose fileWhen path_docker_compose_run is present:
- β
The system executes
docker compose -f <path> upinstead ofdocker run - β
docker_imageandmain_fileare ignored (not used) - β The docker-compose file is executed in its directory context
- β
Services are automatically cleaned up with
docker compose downafter execution - β All logs and output are captured and returned
The path_docker_compose_run can be:
- Relative: Relative to the program's
pathdirectorypath: "scripts/my_script" path_docker_compose_run: "docker-compose.yml" # Resolves to scripts/my_script/docker-compose.yml
- Absolute: Full path to the docker-compose file
path: "scripts/my_script" path_docker_compose_run: "/docker/scripts/my_script/docker-compose.yml" # Absolute path
1. Directory structure:
scripts/
βββ docker_compose_script/
βββ docker-compose.yml
βββ app/
βββ main.py
2. docker-compose.yml:
version: '3.8'
services:
app:
build: .
environment:
- PROGRAM_ID=${PROGRAM_ID}
- EXECUTION_ID=${EXECUTION_ID}
volumes:
- ./app:/app3. config.yaml:
scripts:
docker_compose_script:
id: "docker_compose_script"
name: "Docker Compose Script"
path: "scripts/docker_compose_script"
description: "Multi-container application"
enabled: true
path_docker_compose_run: "docker-compose.yml" # Relative to path- Automatic Cleanup: The system automatically runs
docker compose downafter execution to clean up services - Timeout Handling: If execution times out, cleanup is still attempted
- Environment Variables:
PROGRAM_IDandEXECUTION_IDare available in the docker-compose context - Concurrent Limits: Docker compose executions count towards the concurrent execution limit
- Logs: All output from
docker compose upis captured and returned in the execution logs
If your scripts or bots are located outside the PyExecutorHub project directory, you need to mount them as volumes in docker-compose.yml.
Use an absolute path in your configuration:
scripts:
external_script:
id: "external_script"
name: "External Script"
path: "/docker/scripts/external_script" # Absolute path
description: "Script located outside project"
enabled: true
main_file: "run.py"
docker_image: "external-script:latest"Add the volume mount in the pyexecutorhub-api service:
services:
pyexecutorhub-api:
# ... other configuration ...
volumes:
- .:/project:ro
- ./config.yaml:/app/config.yaml:ro
- /var/run/docker.sock:/var/run/docker.sock
- /usr/bin/docker:/usr/bin/docker:ro
- /docker/scripts:/docker/scripts:ro # Mount external scripts directory- Read-Only Mount: Use
:roflag for security (read-only access) - Path Consistency: The path in
config.yamlmust match the volume mount path - Permissions: Ensure the Docker container has read permissions for the mounted directory
- Multiple Directories: You can mount multiple external directories:
volumes:
- /docker/scripts:/docker/scripts:ro
- /external/bots:/external/bots:ro
- /custom/path:/custom/path:ro1. Directory structure on host:
/docker/scripts/
βββ extractcredentialsfromemail/
β βββ run.py
β βββ .env
βββ photovalidator/
βββ run.py
βββ requirements.txt
2. config.yaml:
scripts:
extractcredentialsfromemail:
id: "1"
name: "Extractor de creds"
path: "/docker/scripts/extractcredentialsfromemail" # Absolute path
description: "Script scrapping de Emails"
enabled: true
main_file: "run.py"
docker_image: "extractcredentialsfromemail:latest"
photovalidator:
id: "18"
name: "Screenshot Validator"
path: "/docker/scripts/photovalidator" # Absolute path
description: "Valida la imagen"
enabled: true
main_file: "run.py"
docker_image: "photovalidator:latest"3. docker-compose.yml:
services:
pyexecutorhub-api:
volumes:
- .:/project:ro
- ./config.yaml:/app/config.yaml:ro
- /var/run/docker.sock:/var/run/docker.sock
- /usr/bin/docker:/usr/bin/docker:ro
- /docker/scripts:/docker/scripts:ro # External scripts volumeThe config.yaml file is mounted as a volume, which means:
- β No rebuild required: Changes to the configuration file are reflected immediately
- β Live updates: Add, modify, or remove programs without restarting the container
- β Persistent changes: Configuration changes survive container restarts
- β Easy management: Edit the file directly on the host system
To add a new program:
- Edit
config.yamland add your program configuration - If using external scripts, ensure the volume is mounted in
docker-compose.yml - The new program will be available immediately via the API
- No container restart or rebuild needed
PyExecutorHub now supports parameterized program execution, allowing you to configure command-line parameters for each program in the configuration file.
Add a parameters field to your program configuration:
scripts:
example_script:
id: "example_script"
name: "Example Script"
path: "scripts/example_script"
description: "Example script with parameters"
enabled: true
main_file: "main.py"
parameters: "--process proceso_2 --verbose" # Optional parameters
bots:
data_processor:
id: "data_processor"
name: "Data Processor"
path: "bots/data_processor"
description: "Process data with custom parameters"
enabled: true
main_file: "run.py"
parameters: "--input data.csv --output results.json --format json"When a program is executed, the system automatically appends the configured parameters to the command:
Without parameters:
python main.pyWith parameters:
python main.py --process proceso_2 --verboseParameters work with different file types:
parameters: "--verbose --output /tmp/result.txt"
# Executes: python main.py --verbose --output /tmp/result.txtparameters: "--mode production --port 8080"
# Executes: node main.js --mode production --port 8080parameters: "--config /etc/app.conf --debug"
# Executes: bash script.sh --config /etc/app.conf --debugmy_script:
parameters: "--verbose"api_client:
parameters: "--host api.example.com --port 8080 --timeout 30"data_processor:
parameters: "--input data.csv --output results.json --format json --compress"Your programs can access command-line arguments using standard methods:
#!/usr/bin/env python3
import sys
import argparse
def main():
parser = argparse.ArgumentParser(description='My Program')
parser.add_argument('--process', help='Process name')
parser.add_argument('--verbose', action='store_true', help='Verbose output')
args = parser.parse_args()
print(f"Process: {args.process}")
print(f"Verbose: {args.verbose}")
return 0
if __name__ == "__main__":
sys.exit(main())#!/usr/bin/env node
const args = process.argv.slice(2);
const processName = args.find(arg => arg.startsWith('--process'))?.split('=')[1];
const verbose = args.includes('--verbose');
console.log(`Process: ${processName}`);
console.log(`Verbose: ${verbose}`);The system logs parameter usage for debugging:
π Program parameters: --process proceso_2 --verbose
π³ Docker command: ... python main.py --process proceso_2 --verbose ...
- Use descriptive parameter names for clarity
- Document parameters in your program's help text
- Validate parameters in your program code
- Use consistent naming across similar programs
- Test parameters before production deployment
Each script or bot can specify its own Docker image, allowing for:
- Different runtime environments (Python, Node.js, etc.)
- Optimized images for specific use cases
- Pre-installed dependencies for faster execution
- Custom configurations for different requirements
Add docker_image to your program configuration:
my_script:
id: "my_script"
name: "My Script"
path: "scripts/my_script"
docker_image: "python:3.11-slim" # Custom image
main_file: "main.py"docker_image: "python:3.11-slim"
docker_image: "python:3.11-alpine"
docker_image: "python:3.12"docker_image: "node:18-alpine"
docker_image: "node:20-slim"
docker_image: "node:latest"docker_image: "ubuntu:22.04"
docker_image: "debian:bullseye-slim"
docker_image: "alpine:latest"- Must support shell commands (bash/sh)
- Should have the required runtime (python, node, etc.)
- Must be available locally or pullable from Docker Hub
- Should be optimized for your use case
#!/usr/bin/env python3
import os
import sys
def main():
print(f"Python version: {sys.version}")
print(f"Running in container: {os.path.exists('/.dockerenv')}")
return 0
if __name__ == "__main__":
sys.exit(main())#!/usr/bin/env node
const os = require('os');
function main() {
console.log(`Node.js version: ${process.version}`);
console.log(`Platform: ${process.platform}`);
process.exit(0);
}
main();The default pyexecutorhub-base image includes:
- Python 3.11 with latest pip
- MariaDB driver for database connections
- System dependencies for common Python packages
- LibreOffice Writer - Document processing
- LibreOffice Calc - Spreadsheet operations
- LibreOffice Impress - Presentation creation
- LibreOffice Draw - Vector graphics
- LibreOffice Math - Formula editing
- LibreOffice Base - Database management
#!/usr/bin/env python3
import subprocess
import os
def convert_document():
"""Convert a document using LibreOffice"""
input_file = "/workspace/document.docx"
output_dir = "/workspace/output"
# Create output directory
os.makedirs(output_dir, exist_ok=True)
# Convert document to PDF
cmd = [
"libreoffice",
"--headless",
"--convert-to", "pdf",
"--outdir", output_dir,
input_file
]
result = subprocess.run(cmd, capture_output=True, text=True)
if result.returncode == 0:
print("β
Document converted successfully")
else:
print(f"β Conversion failed: {result.stderr}")
return result.returncode
if __name__ == "__main__":
exit(convert_document())# Build only the base image with LibreOffice
docker compose build pyexecutorhub-base# Build only the API service
docker compose build pyexecutorhub-api# Build both base image and API service
docker compose build# Rebuild and start all services
docker compose up -d --build- Use specific versions instead of
latest - Choose lightweight images when possible
- Test your images before production
- Document image requirements in your code
- Use multi-stage builds for complex setups
- Leverage LibreOffice for document processing tasks
- Build base image first when making changes to dependencies
curl -X POST http://localhost:8001/auth/login \
-H "Content-Type: application/json" \
-d '{"username": "YOUR_USERNAME", "password": "YOUR_PASSWORD"}'curl -H "Authorization: Bearer YOUR_TOKEN" http://localhost:8001/programscurl -X POST http://localhost:8001/execute \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{"program_id": "example_script"}'curl -H "Authorization: Bearer YOUR_TOKEN" http://localhost:8001/executions/{execution_id}curl -H "Authorization: Bearer YOUR_TOKEN" http://localhost:8001/executions/stats# 1. Login
TOKEN=$(curl -s -X POST http://localhost:8001/auth/login \
-H "Content-Type: application/json" \
-d '{"username": "abc123def", "password": "xY9#mK2$pL8"}' | jq -r '.access_token')
# 2. List programs
curl -H "Authorization: Bearer $TOKEN" http://localhost:8001/programs
# 3. Execute program with parameters
curl -X POST http://localhost:8001/execute \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"program_id": "example_script"}'
# 4. Check status
curl -H "Authorization: Bearer $TOKEN" http://localhost:8001/executions/{execution_id}scripts/
βββ test_script/ # Test script (included)
β βββ main.py # Main script file
β βββ Dockerfile # Docker image definition
β βββ .env # Environment variables
β βββ env.example # Environment variables template
βββ my_script/
β βββ main.py # Main script file
β βββ requirements.txt # Python dependencies
β βββ env.example # Environment variables template
bots/
βββ my_bot/
β βββ run.py # Main bot file
β βββ requirements.txt # Python dependencies
β βββ env.example # Environment variables template
POST /auth/login- Login to get JWT tokenGET /health- Health check (no authentication required)
GET /- API informationGET /programs- List all programs with parametersPOST /execute- Execute a program with parametersGET /executions- List all executionsGET /executions/{id}- Get execution status
GET /executions/info- Execution statisticsGET /executions/stats- Detailed statistics by status and programGET /executions/concurrent- Concurrent execution informationDELETE /executions/cleanup- Cleanup finished executions
GET /containers/logs/{image_name}- Get logs from containers by Docker imageGET /containers/active- Get all active containersGET /executions/{execution_id}/logs- Get detailed execution logs with timing
GET /images/available- Get all available Docker images from configurationGET /images/search/{image_name}- Search for specific Docker images in configuration
All protected endpoints require the following header:
Authorization: Bearer YOUR_JWT_TOKEN
A simple test script is included to verify PyExecutorHub functionality:
Location: scripts/test_script/
Image: test-script:latest
Main File: main.py
# Build the test image
cd scripts/test_script && docker build -t test-script:latest .
# Test manually
docker run --rm -e MY_NAME=Temis test-script:latest
# Test with PyExecutorHub
curl -X POST http://localhost:8001/execute \
-H "Content-Type: application/json" \
-d '{"program_id": "test_script"}'==================================================
π PyExecutorHub Test Script
==================================================
β° Execution Time: 2025-08-05 09:52:10
π Program ID: test_script
π Execution ID: 0e14651b-9ed4-43f7-a5db-f6f5e917fb66
π Hola mundo Temis!
==================================================
π³ Running inside Docker container
π Python version: 3.11.13
π No custom parameters provided
==================================================
β
Test script completed successfully!
==================================================
Advanced container monitoring capabilities allow you to track and debug running containers by Docker image and get real-time logs.
View logs from all containers running a specific Docker image:
# Get logs from all containers using python:3.11-slim
curl http://localhost:8001/containers/logs/python:3.11-slim
# Response
{
"image_name": "python:3.11-slim",
"containers": [
{
"container_id": "abc123def456",
"stdout": "Program output...",
"stderr": "",
"logs_lines": 15
}
],
"total_containers": 1,
"message": "Found 1 running containers for image: python:3.11-slim"
}Monitor all currently running containers:
# Get all active containers
curl http://localhost:8001/containers/active
# Response
{
"total_containers": 3,
"containers": [
{
"container_id": "abc123def456",
"image": "python:3.11-slim",
"status": "Up 2 minutes",
"name": "friendly_container_name"
},
{
"container_id": "def456ghi789",
"image": "node:18-alpine",
"status": "Up 1 minute",
"name": "node_container"
}
]
}Track concurrent executions and prevent system overload:
# Get concurrent execution information
curl http://localhost:8001/executions/concurrent
# Response
{
"concurrent_count": 2,
"max_concurrent": 5,
"running_executions": [
{
"execution_id": "uuid-1",
"program_id": "my_script",
"start_time": "2024-01-01T10:00:00",
"duration_seconds": 45.2
}
]
}Get detailed execution information with timing:
# Get detailed execution logs
curl -H "Authorization: Bearer YOUR_TOKEN" http://localhost:8001/executions/{execution_id}/logs
# Response
{
"execution_id": "uuid-1",
"program_id": "my_script",
"status": "completed",
"start_time": "2024-01-01T10:00:00",
"end_time": "2024-01-01T10:00:45",
"duration_seconds": 45.2,
"output": "Program output...",
"error": "",
"output_lines": 10,
"error_lines": 0
}Get comprehensive execution statistics with program breakdown:
# Get detailed statistics
curl -H "Authorization: Bearer YOUR_TOKEN" http://localhost:8001/executions/stats
# Response
{
"total": 395,
"by_status": {
"completed": [
{"total": 350, "name": "example_script"},
{"total": 200, "name": "data_processor"},
{"total": 150, "name": "test_script"}
],
"failed": [
{"total": 4, "name": "example_script"},
{"total": 2, "name": "data_processor"}
],
"timeout": [
{"total": 1, "name": "long_running_script"}
],
"running": [
{"total": 2, "name": "example_script"}
],
"queued": [
{"total": 1, "name": "data_processor"}
]
}
}Manage and monitor Docker images used by the system. Get information about available images from the configuration and search for specific ones.
Get all Docker images configured in the system:
# Get all available images from configuration
curl http://localhost:8001/images/available
# Response
{
"total_images": 2,
"images": [
{
"repository": "test-script",
"tag": "latest",
"image_id": "config",
"size": "N/A",
"created_at": "N/A",
"full_name": "test-script:latest",
"source": "script",
"script_id": "test_script"
},
{
"repository": "pyexecutorhub-base",
"tag": "latest",
"image_id": "config",
"size": "N/A",
"created_at": "N/A",
"full_name": "pyexecutorhub-base",
"source": "default",
"description": "Default image for programs without custom image"
}
]
}Search for specific Docker images in configuration:
# Search for test-script images
curl http://localhost:8001/images/search/test-script
# Response
{
"search_term": "test-script",
"total_images": 1,
"images": [
{
"repository": "test-script",
"tag": "latest",
"image_id": "config",
"size": "N/A",
"created_at": "N/A",
"full_name": "test-script:latest",
"source": "script",
"script_id": "test_script"
}
]
}The system automatically removes containers after execution using the --rm flag:
- β Automatic cleanup: Containers are removed after execution
- β No accumulation: Prevents container buildup
- β Resource management: Frees up system resources
- β Clean environment: Each execution starts fresh
# Good - Specific version
docker_image: "python:3.11-slim"
# Avoid - Latest tag
docker_image: "python:latest"# Remove unused images
docker image prune -f
# Remove all unused images (including untagged)
docker image prune -a -f# Check image sizes
docker images --format "table {{.Repository}}\t{{.Tag}}\t{{.Size}}"
# Find large images
docker images --format "table {{.Repository}}\t{{.Tag}}\t{{.Size}}" | sort -k3 -hrActions are Python scripts that run before and after program execution, providing hooks for:
- Pre-execution setup (database connections, file preparation)
- Post-execution cleanup (log processing, notifications)
- Custom workflows (data validation, reporting)
actions/
βββ act_before.py # Pre-execution actions
βββ act_after.py # Post-execution actions
βββ requirements.txt # Action dependencies
βββ README.md # Documentation
Actions have access to:
PROGRAM_ID- Current program IDEXECUTION_ID- Current execution IDEXIT_CODE- Program exit code (post-execution only)- All custom parameters as
PARAM_*variables
# act_before.py
import os
import logging
def main():
program_id = os.getenv('PROGRAM_ID')
execution_id = os.getenv('EXECUTION_ID')
logging.info(f"Starting execution {execution_id} for program {program_id}")
# Your pre-execution logic here
print("π§ Pre-execution actions completed")
if __name__ == "__main__":
main()# act_after.py
import os
import requests
def main():
exit_code = os.getenv('EXIT_CODE', '0')
program_id = os.getenv('PROGRAM_ID')
if exit_code == '0':
print("β
Program completed successfully")
# Send success notification
else:
print(f"β Program failed with exit code {exit_code}")
# Send failure notification
if __name__ == "__main__":
main()Logs are automatically formatted for better readability:
- Line breaks are properly displayed
- Escape sequences are handled correctly
- Output and error streams are separated
# Get execution status with logs
curl http://localhost:8000/executions/{execution_id}
# Example response
{
"execution_id": "abc123",
"program_id": "my_script",
"status": "completed",
"output": "Program started\nProcessing data...\nCompleted successfully",
"error": ""
}- INFO: General execution information
- WARNING: Non-critical issues
- ERROR: Execution failures
- DEBUG: Detailed debugging information
Set up cron jobs for automated program execution:
# Execute bot every 8 hours
0 */8 * * * /path/to/execute_bot.sh 6#!/bin/bash
PROGRAM_ID=$1
if [ -z "$PROGRAM_ID" ]; then
echo "Usage: $0 <program_id>"
exit 1
fi
curl -X POST http://localhost:8000/execute \
-H "Content-Type: application/json" \
-d "{\"program_id\": \"$PROGRAM_ID\"}" \
-s > /dev/null
echo "Bot $PROGRAM_ID execution triggered at $(date)"When migrating to a new server:
# Update cron jobs
crontab -e
# Change paths from old system to new
# Old: /apps/serverless-executor/execute_bot.sh
# New: /apps/PyExecutorHub/execute_bot.shThe system automatically validates and manages Docker images:
# Error when image doesn't exist
β Docker image 'python:3.11-slim' not found and could not be pulled.
Please ensure the image exists locally or is available in a registry.- β Local Check: First verifies if image exists locally
- β Auto Pull: Attempts to download from Docker Hub if not found
- β Timeout: 5-minute timeout for image downloads
- β Error Handling: Clear error messages with instructions
Smart detection of main program files:
main.py(default)run.py(alternative)app.py(alternative)index.py(alternative)
# Error when main file is missing
β Main program file 'main.py' not found in /path/to/scripts/mi_script.
Please ensure the file exists or update the configuration.Prevents system overload with configurable limits:
settings:
max_concurrent_executions: 5 # Maximum concurrent executions# Error when concurrent limit is reached
β Maximum concurrent executions (5) reached.
Please wait for some executions to complete.- Image not found locally
- Failed to pull from registry
- Invalid image name
- Main file not found
- Invalid file path
- Missing dependencies
- Concurrent limit exceeded
- Timeout exceeded
- Resource limits reached
- Docker daemon not available
- Insufficient permissions
- Network connectivity issues
# List local images
docker images
# Check specific image
docker images python:3.11-slim
# Pull image manually
docker pull python:3.11-slim# Check program directory
ls -la scripts/mi_script/
# Verify main file exists
ls -la scripts/mi_script/main.py
# Check alternative files
ls -la scripts/mi_script/run.py# Check current concurrent executions
curl http://localhost:8000/executions/concurrent
# View all executions
curl http://localhost:8000/executions# Check if API is running
curl http://localhost:8001/health
# Get credentials from logs
docker compose logs pyexecutorhub-api | grep -A 5 "CREDENCIALES DE USO"
# Test login
curl -X POST http://localhost:8001/auth/login \
-H "Content-Type: application/json" \
-d '{"username": "YOUR_USERNAME", "password": "YOUR_PASSWORD"}'# Login first
TOKEN=$(curl -s -X POST http://localhost:8001/auth/login \
-H "Content-Type: application/json" \
-d '{"username": "YOUR_USERNAME", "password": "YOUR_PASSWORD"}' | jq -r '.access_token')
# Check if program exists
curl -H "Authorization: Bearer $TOKEN" http://localhost:8001/programs
# Verify configuration
cat config.yaml# Check execution status
curl -H "Authorization: Bearer $TOKEN" http://localhost:8001/executions/{execution_id}
# View container logs
docker logs pyexecutorhub-api# Check if parameters are configured
curl -H "Authorization: Bearer $TOKEN" http://localhost:8001/programs | jq '.[] | {id, name, parameters}'
# Check execution logs for parameter usage
docker compose logs pyexecutorhub-api | grep "Program parameters"# Check available images
docker images
# Pull missing images
docker pull python:3.11-slim- Check API health:
curl http://localhost:8001/health - Get credentials:
docker compose logs pyexecutorhub-api | grep -A 5 "CREDENCIALES" - Login and get token: Use the credentials to get a JWT token
- Verify configuration: Review
config.yamlfor parameters - Check Docker:
docker ps -a - View logs:
docker logs pyexecutorhub-api - Test manually: Run program directly in container
- Use lightweight images (alpine, slim)
- Optimize dependencies (only required packages)
- Set appropriate timeouts for your use case
- Monitor resource usage regularly
#!/usr/bin/env python3
"""
Test Script for PyExecutorHub
Simple script to test environment variables and Docker execution
"""
import os
import sys
from datetime import datetime
def main():
"""Main function that reads environment variables and displays a message"""
# Get environment variables
program_id = os.getenv('PROGRAM_ID', 'unknown')
execution_id = os.getenv('EXECUTION_ID', 'unknown')
my_name = os.getenv('MY_NAME', 'World')
# Get current timestamp
current_time = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
print("=" * 50)
print("π PyExecutorHub Test Script")
print("=" * 50)
print(f"β° Execution Time: {current_time}")
print(f"π Program ID: {program_id}")
print(f"π Execution ID: {execution_id}")
print(f"π Hola mundo {my_name}!")
print("=" * 50)
# Check if running in Docker
if os.path.exists('/.dockerenv'):
print("π³ Running inside Docker container")
else:
print("π» Running on host system")
# Display Python version
print(f"π Python version: {sys.version}")
# Check if we have any parameters
param_count = 0
for key, value in os.environ.items():
if key.startswith('PARAM_'):
param_count += 1
print(f"π Parameter {key}: {value}")
if param_count == 0:
print("π No custom parameters provided")
print("=" * 50)
print("β
Test script completed successfully!")
print("=" * 50)
return 0
if __name__ == "__main__":
sys.exit(main())#!/usr/bin/env python3
import os
import sys
def main():
print("Hello from PyExecutorHub!")
print(f"Program ID: {os.getenv('PROGRAM_ID')}")
print(f"Execution ID: {os.getenv('EXECUTION_ID')}")
return 0
if __name__ == "__main__":
sys.exit(main())#!/usr/bin/env python3
import os
import sys
def main():
# Access custom parameters
param1 = os.getenv('PARAM_PARAM1', 'default')
param2 = os.getenv('PARAM_PARAM2', 'default')
print(f"Parameter 1: {param1}")
print(f"Parameter 2: {param2}")
return 0
if __name__ == "__main__":
sys.exit(main())#!/usr/bin/env python3
import os
import sys
import mariadb
def main():
try:
# Connect to database
conn = mariadb.connect(
host=os.getenv('DB_HOST'),
user=os.getenv('DB_USER'),
password=os.getenv('DB_PASS'),
database=os.getenv('DB_NAME')
)
# Your bot logic here
print("Bot executed successfully")
conn.close()
return 0
except Exception as e:
print(f"Error: {e}")
return 1
if __name__ == "__main__":
sys.exit(main())See CONTRIBUTING.md for guidelines.
This project is licensed under the MIT License - see the LICENSE file for details.
For support and questions:
- Documentation: Check this README and the actions README
- Issues: Report bugs and feature requests on GitHub
- Examples: Review the example scripts and bots in the repository
PyExecutorHub - Making Python execution simple, scalable, and reliable! π