# Docker Container Manager

This guide demonstrates how to manage Docker containers using the official `docker` SDK for Python. It allows you to automate container lifecycle management (listing, running, stopping, removing) from a Python script.

**Modules Used:**
*   `docker`: The official Python library for the Docker Engine API.
*   [[programming/python/modules/argparse-module|argparse]]: To handle command-line arguments.

## Prerequisites

1.  **Docker Desktop** (or Docker Engine) must be installed and running.
2.  The user running the script must have permission to access the Docker socket (on Linux, add user to `docker` group).

## Installation

```bash
pip install docker
```

## The Code

Save this as `docker_manager.py`.

```python
import docker
import argparse
import sys

def get_client():
    try:
        # Connects to the Docker daemon using default socket or environment variables
        return docker.from_env()
    except docker.errors.DockerException as e:
        print(f"Error connecting to Docker: {e}")
        print("Ensure Docker is running.")
        return None

def list_containers(show_all=False):
    client = get_client()
    if not client: return

    containers = client.containers.list(all=show_all)
    if not containers:
        print("No containers found.")
        return

    print(f"{'ID':<12} {'Image':<25} {'Status':<10} {'Name'}")
    print("-" * 65)
    for c in containers:
        # Short ID
        cid = c.short_id
        # Image tags might be a list, take the first one or ID
        image = c.image.tags[0] if c.image.tags else c.image.id[:12]
        # Truncate image name if too long
        if len(image) > 23: image = image[:22] + "..."
        
        print(f"{cid:<12} {image:<25} {c.status:<10} {c.name}")

def run_container(image, name=None, detach=True, ports=None):
    client = get_client()
    if not client: return

    print(f"Pulling and running '{image}'...")
    try:
        # Parse ports if provided (e.g., ["8080:80"]) -> {80: 8080}
        # Note: docker-py expects {container_port: host_port}
        port_bindings = {}
        if ports:
            for p in ports:
                host, container = p.split(':')
                port_bindings[container] = host

        container = client.containers.run(
            image,
            name=name,
            detach=detach,
            ports=port_bindings
        )
        print(f"Container started: {container.name} ({container.short_id})")
    except docker.errors.ImageNotFound:
        print(f"Error: Image '{image}' not found locally or on registry.")
    except docker.errors.APIError as e:
        print(f"Docker API Error: {e}")

def stop_container(container_id):
    client = get_client()
    if not client: return

    try:
        container = client.containers.get(container_id)
        print(f"Stopping {container.name}...")
        container.stop()
        print("Stopped.")
    except docker.errors.NotFound:
        print(f"Error: Container '{container_id}' not found.")

def remove_container(container_id, force=False):
    client = get_client()
    if not client: return

    try:
        container = client.containers.get(container_id)
        print(f"Removing {container.name}...")
        container.remove(force=force)
        print("Removed.")
    except docker.errors.NotFound:
        print(f"Error: Container '{container_id}' not found.")
    except docker.errors.APIError as e:
        print(f"Error: {e}")

if __name__ == "__main__":
    parser = argparse.ArgumentParser(description="Docker Container Manager")
    subparsers = parser.add_subparsers(dest="command", help="Command to run")

    # List
    list_parser = subparsers.add_parser("list", help="List containers")
    list_parser.add_argument("-a", "--all", action="store_true", help="Show all containers (default shows just running)")

    # Run
    run_parser = subparsers.add_parser("run", help="Run a container")
    run_parser.add_argument("image", help="Image name (e.g., nginx)")
    run_parser.add_argument("--name", help="Container name")
    run_parser.add_argument("-p", "--ports", action="append", help="Port mapping host:container (e.g., 8080:80)")

    # Stop
    stop_parser = subparsers.add_parser("stop", help="Stop a container")
    stop_parser.add_argument("id", help="Container ID or Name")

    # Remove
    rm_parser = subparsers.add_parser("rm", help="Remove a container")
    rm_parser.add_argument("id", help="Container ID or Name")
    rm_parser.add_argument("-f", "--force", action="store_true", help="Force removal (kill if running)")

    args = parser.parse_args()

    if args.command == "list":
        list_containers(args.all)
    elif args.command == "run":
        run_container(args.image, args.name, ports=args.ports)
    elif args.command == "stop":
        stop_container(args.id)
    elif args.command == "rm":
        remove_container(args.id, args.force)
    else:
        parser.print_help()
```

## Usage

```bash
# Run an Nginx server on port 8080
python docker_manager.py run nginx --name my-web-server -p 8080:80

# List running containers
python docker_manager.py list

# Stop the container
python docker_manager.py stop my-web-server

# Remove the container
python docker_manager.py rm my-web-server
```

[[programming/python/python]]