LogoContainerPub

Python Podman Client

Python-based Podman API client for container operations

Python Podman Client#

The Python Podman Client (podman_client.py) is a command-line tool that provides JSON-based communication between the Dart backend and Podman API.

Overview#

Location: tools/podman_client/podman_client.py

Purpose:

  • Bridge between Dart backend and Podman API
  • Structured JSON communication
  • Direct Podman Python SDK usage
  • Container lifecycle management

Architecture:

Dart BackendPodmanPyRuntimePython ClientPodman APIContainers

Features#

1. JSON Communication#

All commands return structured JSON responses:

Success Response:

{
  "success": true,
  "data": {
    "container_id": "abc123",
    "status": "exited",
    "stdout": {...}
  }
}

Error Response:

{
  "success": false,
  "error": "Container execution failed",
  "exit_code": 1
}

2. Socket-Based Connection#

Connects to Podman via Unix socket:

--socket /run/podman/podman.sock

Configurable via:

  • Command-line argument: --socket /path/to/socket
  • Environment variable: PODMAN_SOCKET_PATH
  • Default: /run/podman/podman.sock

3. Container Operations#

Full container lifecycle management:

  • Build images
  • Run containers
  • Kill containers
  • Remove containers
  • List containers
  • Inspect containers

4. Image Management#

Complete image operations:

  • Build images
  • Remove images
  • List images
  • Inspect images
  • Check existence
  • Prune unused images

Command Reference#

System Commands#

version

Get Podman version information.

python3 podman_client.py --socket /run/podman/podman.sock version

Response:

{
  "success": true,
  "data": "5.0.0"
}

ping

Test connection to Podman API.

python3 podman_client.py --socket /run/podman/podman.sock ping

Response:

{
  "success": true,
  "data": "Pong"
}

info

Get system information.

python3 podman_client.py --socket /run/podman/podman.sock info

With format:

python3 podman_client.py --socket /run/podman/podman.sock info --format ""

Image Commands#

build

Build a container image from Dockerfile.

python3 podman_client.py --socket /run/podman/podman.sock build . \
  --tag myapp:latest \
  --file Dockerfile \
  --platform linux/amd64

Options:

  • --tag, -t: Image tag (e.g., myapp:latest)
  • --file, -f: Dockerfile name (default: Dockerfile)
  • --build-arg: Build arguments (format: KEY=VALUE)
  • --platform: Target platform (e.g., linux/amd64, linux/arm64)
  • --no-cache: Do not use cache when building
  • --force: Force rebuild even if image exists

Response:

{
  "success": true,
  "data": {
    "image_id": "sha256:abc123...",
    "tags": ["myapp:latest"],
    "platform": "linux/amd64",
    "logs": ["Step 1/5 : FROM dart:stable", ...]
  }
}

images

List all images.

python3 podman_client.py --socket /run/podman/podman.sock images

With all images:

python3 podman_client.py --socket /run/podman/podman.sock images --all

Response:

{
  "success": true,
  "data": [
    {
      "id": "sha256:abc123...",
      "tags": ["myapp:latest"],
      "size": 1234567890,
      "created": "2024-01-06T00:00:00Z",
      "digest": "sha256:def456..."
    }
  ]
}

exists

Check if an image exists.

python3 podman_client.py --socket /run/podman/podman.sock exists myapp:latest

Response:

{
  "success": true,
  "data": true
}

inspect

Inspect an image.

python3 podman_client.py --socket /run/podman/podman.sock inspect myapp:latest

With format:

python3 podman_client.py --socket /run/podman/podman.sock inspect myapp:latest \
  --format ""

Response:

{
  "success": true,
  "data": {
    "Id": "sha256:abc123...",
    "Size": 1234567890,
    "Architecture": "amd64",
    "Os": "linux"
  }
}

rmi

Remove an image.

python3 podman_client.py --socket /run/podman/podman.sock rmi myapp:latest

With force:

python3 podman_client.py --socket /run/podman/podman.sock rmi myapp:latest --force

Response:

{
  "success": true,
  "data": {
    "image": "myapp:latest",
    "message": "Image deleted successfully"
  }
}

prune

Remove unused images.

python3 podman_client.py --socket /run/podman/podman.sock prune

Response:

{
  "success": true,
  "data": {
    "images_deleted": ["sha256:abc123..."],
    "space_reclaimed": 1234567890,
    "message": "Images pruned successfully"
  }
}

Container Commands#

run

Run a container from an image.

python3 podman_client.py --socket /run/podman/podman.sock run myapp:latest \
  --name mycontainer \
  --env KEY=VALUE \
  --mount /host/path:/container/path:Z,rshared \
  --memory 20m \
  --cpus 0.5 \
  --timeout 10

Options:

  • --name: Container name
  • --entrypoint: Entrypoint to run in container
  • --detach, -d: Run container in background (default: true)
  • --port, -p: Port mapping (format: HOST:CONTAINER)
  • --env, -e: Environment variables (format: KEY=VALUE)
  • --volume, -v: Volume mapping (format: HOST:CONTAINER)
  • --mount, -m: Mount mapping (format: source:target:relabel,propagation)
  • --run-command, -c: Command to run in container
  • --no-auto-remove: Do not automatically remove container after exit
  • --network-mode: Network mode (default: none)
  • --memory: Memory limit (default: 20m)
  • --memory-swap: Memory swap limit (default: 20m)
  • --cpus: Number of CPUs (default: 0.5)
  • --timeout: Timeout in seconds (default: 5)
  • --workdir, -w: Working directory inside the container

Mount Format:

source:target:relabel,propagation,size
  • source: Host path
  • target: Container path
  • relabel: SELinux relabel (e.g., Z, z)
  • propagation: Mount propagation (e.g., rshared, shared, private)
  • size: Optional size limit

Response:

{
  "success": true,
  "data": {
    "container_id": "abc123",
    "name": "mycontainer",
    "status": "exited",
    "exit_code": 0,
    "image": ["myapp:latest"],
    "auto_remove": true,
    "stdout": {
      "statusCode": 200,
      "body": { "result": "success" }
    }
  }
}

ps

List containers.

python3 podman_client.py --socket /run/podman/podman.sock ps

With all containers:

python3 podman_client.py --socket /run/podman/podman.sock ps --all

Response:

{
  "success": true,
  "data": [
    {
      "id": "abc123",
      "name": "mycontainer",
      "status": "exited",
      "image": ["myapp:latest"],
      "created": "2024-01-06T00:00:00Z"
    }
  ]
}

kill

Kill a running container.

python3 podman_client.py --socket /run/podman/podman.sock kill mycontainer

With signal:

python3 podman_client.py --socket /run/podman/podman.sock kill mycontainer --signal SIGTERM

Response:

{
  "success": true,
  "data": {
    "container_id": "mycontainer",
    "message": "Container killed with signal SIGKILL"
  }
}

rm

Remove a container.

python3 podman_client.py --socket /run/podman/podman.sock rm mycontainer

With force:

python3 podman_client.py --socket /run/podman/podman.sock rm mycontainer --force

Response:

{
  "success": true,
  "data": {
    "container_id": "mycontainer",
    "message": "Container deleted successfully"
  }
}

Implementation Details#

PodmanCLI Class#

Location: tools/podman_client/podman_client.py

Key Methods:

class PodmanCLI:
    def __init__(self, socket_path: str):
        """Initialize with Podman socket path"""

    def connect(self) -> bool:
        """Connect to Podman API"""

    def build_image(self, context_path: str, tag: str, ...):
        """Build container image"""

    def run_container(self, image: str, name: str, ...):
        """Run container with options"""

    def kill_container(self, container_id: str, signal: str):
        """Kill running container"""

    def delete_container(self, container_id: str, force: bool):
        """Delete container"""

    def delete_image(self, image_tag: str, force: bool):
        """Delete image"""

Response Formatting#

Success Output:

def _output_success(self, data: Any):
    print(json.dumps({"success": True, "data": data}))

Error Output:

def _output_error(self, message: str):
    print(json.dumps({"success": False, "error": message}), file=sys.stderr)

Format String Support#

Supports Go-style template formatting:

def format_inspect(self, attrs, format_str):
    """Replaces  with values from attributes"""
    patterns = re.findall(r'\{\{\s*\.(.*?)\s*\}\}', format_str)
    # ... replace patterns with actual values

Example:

python3 podman_client.py inspect myapp --format ""
# Output: {"success": true, "data": "1234567890"}

Integration with Dart Backend#

PodmanPyRuntime#

Location: dart_cloud_backend/lib/services/docker/podman_py_runtime.dart

Execution Flow:

Future<Map<String, dynamic>> _executePythonCommand(
  List<String> args, {
  Duration? timeout,
}) async {
  final fullArgs = [
    _pythonClientPath,
    '--socket',
    _socketPath,
    ...args,
  ];

  final process = await Process.start(_pythonExecutable, fullArgs);

  // Capture stdout/stderr
  // Parse JSON response
  // Handle timeout
  // Return structured result
}

Example Usage#

Build Image:

final result = await _executePythonCommand([
  'build',
  contextDir,
  '--tag',
  imageTag,
  '--file',
  dockerfilePath,
]);

if (result['success'] == true) {
  final data = result['data'] as Map<String, dynamic>;
  final logs = data['logs'] as List<dynamic>? ?? [];
  // Process build logs
}

Run Container:

final result = await _executePythonCommand([
  'run',
  imageTag,
  '--name',
  containerName,
  '--mount',
  'functions_data:/app/functions:Z,rshared',
  '--memory',
  '20m',
  '--cpus',
  '0.5',
  '--timeout',
  timeout.inSeconds.toString(),
]);

if (result['success'] == true) {
  final data = result['data'] as Map<String, dynamic>;
  final containerId = data['container_id'];
  final stdout = data['stdout'];
  // Process container output
}

Configuration#

Socket Path#

Default:

/run/podman/podman.sock

Custom via Command Line:

python3 podman_client.py --socket /custom/path/podman.sock <command>

Custom via Environment:

export PODMAN_SOCKET_PATH=/custom/path/podman.sock

Python Executable#

Default: python3

Custom in PodmanPyRuntime:

final runtime = PodmanPyRuntime(
  pythonExecutable: '/usr/bin/python3.11',
);

Client Path#

Default: podman_client.py (relative to backend)

Custom in PodmanPyRuntime:

final runtime = PodmanPyRuntime(
  pythonClientPath: '/path/to/podman_client.py',
);

Dependencies#

Python Requirements#

podman>=5.0.0  # Podman Python SDK

Installation:

pip3 install podman

System Requirements#

  • Python 3.x
  • Podman installed and running
  • Podman socket enabled

Troubleshooting#

Connection Failed#

Error:

{
  "success": false,
  "error": "Failed to connect to Podman socket: ..."
}

Solution:

# Check socket exists
ls -la /run/podman/podman.sock

# Start Podman socket
podman system service --time=0 unix:///run/podman/podman.sock

# Test connection
python3 podman_client.py --socket /run/podman/podman.sock ping

Python SDK Not Found#

Error:

ModuleNotFoundError: No module named 'podman'

Solution:

# Install Podman SDK
pip3 install podman

# Verify installation
python3 -c "import podman; print(podman.__version__)"

JSON Parse Error#

Error:

{
  "success": false,
  "error": "Failed to parse JSON response: ..."
}

Solution:

# Test Python client directly
python3 podman_client.py --socket /run/podman/podman.sock version

# Should output valid JSON
# {"success": true, "data": "5.0.0"}

Timeout Issues#

Error:

{
  "success": false,
  "error": "Container 'name' exceeded timeout of 5s and was killed"
}

Solution:

# Increase timeout
python3 podman_client.py run myapp --timeout 30

# Or in Dart backend
final result = await _executePythonCommand(
  args,
  timeout: Duration(seconds: 30),
);

Best Practices#

1. Error Handling#

Always check success field in response:

result = json.loads(output)
if result['success']:
    data = result['data']
    # Process data
else:
    error = result['error']
    # Handle error

2. Timeout Configuration#

Set appropriate timeouts for operations:

# Short timeout for quick operations
python3 podman_client.py run myapp --timeout 5

# Longer timeout for complex functions
python3 podman_client.py run myapp --timeout 30

3. Resource Limits#

Always specify resource limits:

python3 podman_client.py run myapp \
  --memory 20m \
  --memory-swap 50m \
  --cpus 0.5

4. Mount Configuration#

Use proper SELinux labels and propagation:

# SELinux label for container access
--mount /host/path:/container/path:Z,rshared

# Z = private label
# z = shared label
# rshared = recursive shared propagation

5. Network Isolation#

Use network isolation for security:

python3 podman_client.py run myapp --network-mode none

See Also#