Skip to main content

A Python client for Enroot

Project description

enroot-py

A Python client library for Enroot, providing a docker-py inspired interface for managing Enroot containers and images.

Overview

enroot-py offers a Pythonic API to interact with Enroot, NVIDIA's container runtime for HPC environments. It provides a familiar interface similar to docker-py, making it easy to manage containers, pull images, and execute commands within Enroot containers.

Features

  • Image Management: Pull, list, and retrieve Docker images converted to Enroot's .sqsh format
  • Container Operations: Create, start, stop, remove, and execute commands in containers
  • Port Mapping: Automatic port allocation and mapping for containerized services
  • Environment Variables: Pass environment variables to containers
  • Resource Limits: CPU and memory limits via systemd-run (when available)
  • Docker Compatibility: Automatically imports Docker images if not found locally

Prerequisites

  • Python 3.8 or higher
  • Enroot installed and available in your PATH
  • Linux operating system (Enroot is Linux-only)

Installation

This library is not yet packaged on PyPI. To use it, clone the repository and add it to your PYTHONPATH:

git clone https://github.com/your-username/enroot-py.git
cd enroot-py
export PYTHONPATH=$PYTHONPATH:$(pwd)

Alternatively, you can install it in development mode:

pip install -e .

Quick Start

Creating a Client

from enroot.client import from_env

client = from_env()

# Check if Enroot is available
if client.ping():
    print("Enroot is running!")

Working with Images

Pulling an Image

Pull a Docker image and convert it to Enroot's .sqsh format:

# Pull with explicit tag
image = client.images.pull("ubuntu", "20.04")
print(f"Pulled image: {image.id}")

# Pull latest tag (default)
image = client.images.pull("python", "3.9")

# Pull from a custom registry
image = client.images.pull("my-image", "latest", registry_host="registry.example.com")

Listing Images

images = client.images.list()
for image in images:
    print(f"Image: {image.id}, Tags: {image.tags}")

Getting a Specific Image

image = client.images.get("ubuntu+20.04")  # Use '+' instead of '/' or ':'
print(f"Found image: {image.id}")

Working with Containers

Starting a Container

container = client.containers.run(
    "ubuntu:20.04",              # Image name (will be imported if needed)
    command="sleep 3600",        # Command to run
    name="my-container",         # Optional container name
    detach=True,                 # Required: must be True
    ports={"8080/tcp": "8080"},  # Port mapping (container_port: host_port)
    environment={                # Environment variables
        "MY_VAR": "my_value",
        "DEBUG": "1"
    },
    cpu_count=2.0,              # Optional: CPU limit (requires systemd-run)
    mem_limit="2G"              # Optional: Memory limit (requires systemd-run)
)
print(f"Started container: {container.name}")
print(f"Status: {container.status}")

Note: detach=True is required. Non-detached mode is not currently supported.

Using Local .sqsh Files

You can also use local .sqsh files directly:

container = client.containers.run(
    "/path/to/image.sqsh",
    command="bash",
    detach=True
)

Listing Containers

containers = client.containers.list()
for container in containers:
    print(f"- {container.name} ({container.status})")

Getting a Container

container = client.containers.get("my-container")
print(f"Container status: {container.status}")
print(f"Container attributes: {container.attrs}")

Executing Commands in a Container

result = container.exec_run("echo 'Hello from container!'")
print(f"Exit code: {result.exit_code}")
print(f"Output: {result.output}")

# Execute with a list of arguments
result = container.exec_run(["ls", "-la", "/tmp"])

Stopping and Removing Containers

# Stop (kill) a container
container.kill()

# Remove a container
container.remove()

# Force remove a container
container.remove(force=True)

Checking Container Status

# Reload status from Enroot
container.reload()

# Get current status
status = container.status  # "running" or "exited"

# Get container attributes (including port mappings)
attrs = container.attrs

Configuration

Environment Variables

  • ENROOT_HOME: Override the default Enroot home directory (default: ~/.cache/enroot)
  • ENROOT_IMAGES_PATH: Override where pulled .sqsh images are stored (default: <ENROOT_HOME>/images)
  • ENROOT_DEBUG: Set to "1" to enable debug logging (shows Enroot command execution)
  • ENROOT_ASYNC: Set to "1" to make the synchronous run_enroot helper transparently use the async backend when no event loop is already running
  • XDG_CACHE_HOME: Used for cache directory if ENROOT_HOME is not set

Port Mapping

When you specify ports in containers.run(), the library automatically allocates a free port on the host and maps it to the container port. The allocated port is available in the container's attrs:

container = client.containers.run(
    "nginx",
    detach=True,
    ports={"80/tcp": "80"}
)

# Access port mapping
port_mapping = container.attrs["NetworkSettings"]["Ports"]
# The actual host port is stored here

The allocated port is also set as the PORT environment variable inside the container.

Resource Limits

CPU and memory limits are applied using systemd-run when available:

container = client.containers.run(
    "my-image",
    detach=True,
    cpu_count=1.5,      # 1.5 CPUs
    mem_limit="512M"    # 512 MB memory limit
)

Note: Resource limits require systemd-run to be available. If it's not found, a warning is issued and the limits are ignored.

API Reference

EnrootClient

Main client class for interacting with Enroot.

  • ping() -> bool: Check if Enroot is available
  • images: Access to image operations (Images instance)
  • containers: Access to container operations (Containers instance)

Images

Image management operations.

  • pull(repository: str, tag: str | None = None, registry_host: str = "", directory: str | None = None) -> Image: Pull and import a Docker image. When registry_host is empty (the default), Enroot uses its built-in default registry; pass e.g. "registry.example.com" to override.
  • list() -> List[Image]: List all available images
  • get(ref: str) -> Image: Get a specific image by reference

Containers

Container management operations.

  • run(image, command=None, name=None, timeout=120, detach=False, remove=False, ports=None, mount=None, environment=None, cpu_count=None, mem_limit=None, cpus=None, **_) -> Container: Create and start a container (detach=True is required).
  • create(image, command=None, name=None, ports=None, mount=None, environment=None, cpu_count=None, mem_limit=None, cpus=None, timeout=120, **_) -> Container: Create a container without starting it (similar to docker create). The optional command is stashed and used as the default for Container.start(...).
  • create_async(...) -> Container: Coroutine variant of create.
  • list() -> List[Container]: List all containers
  • get(ident: str) -> Container: Get a container by name

Container

Represents a single container instance.

  • name: str / id: str / short_id: str: Container identifiers (Enroot uses the name as the id).
  • status: str: Container status ("created", "running", or "exited").
  • attrs: Dict: Container attributes (similar to Docker API).
  • pids: List[int]: Cached in-container PIDs from enroot list --fancy.
  • reload() / reload_async(): Refresh state from enroot list.
  • start(command=None, environment=None, mount=None, cpu_count=None, mem_limit=None, cpus=None, timeout=30, **_): Start a container previously created with containers.create(...).
  • start_async(...): Async variant of start.
  • stop(timeout=10, **_): Gracefully stop and remove the container.
  • kill(**_) / kill_async(timeout=10, **_): Forcefully stop and remove.
  • remove(force=False, **_): Remove the container via enroot remove.
  • exec_run(cmd, *, stdout=True, stderr=True, demux=False, workdir=None, user=None, environment=None, detach=False, mem_limit=None, mem_guard_limit=None, mem_guard_poll_interval=1.0, timeout=None, **_) -> ExecResult: Execute a command in the container. Supports host-side timeout (raises enroot.errors.TimeoutError on exit 124), prlimit-based mem_limit, and psutil-based RSS cap mem_guard_limit (raises enroot.errors.MemGuardError on exit 137).
  • exec_run_async(...) -> ExecResult: Async variant of exec_run.
  • copy_to(local_path, container_path) / put_archive(path, data): Copy files into a running container.

Image

Represents a container image.

  • id: str: Image ID (path to .sqsh file)
  • tags: List[str]: Image tags

Limitations

  • Detached Mode Only: The detach parameter must be True. Non-detached containers are not supported.
  • No Logs: The logs() method returns an empty bytes object. Enroot doesn't provide a built-in logging mechanism.
  • Resource Limits: CPU and memory limits require systemd-run to be available on the system.
  • Port Mapping: All ports are mapped to a single automatically-allocated free port. Individual port mappings are not fully supported.

Testing

The test suite uses pytest. Install the dev extras and run it:

pip install -e ".[dev]"
pytest

Slow integration checks that pull large images are gated behind the slow marker:

pytest -m slow

Contributing

Contributions are welcome! Please feel free to submit a Pull Request.

License

MIT License (see pyproject.toml for details)

Related Projects

  • Enroot - The underlying container runtime
  • docker-py - The inspiration for this library's API design

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

enroot_py-0.1.1.tar.gz (32.3 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

enroot_py-0.1.1-py3-none-any.whl (28.8 kB view details)

Uploaded Python 3

File details

Details for the file enroot_py-0.1.1.tar.gz.

File metadata

  • Download URL: enroot_py-0.1.1.tar.gz
  • Upload date:
  • Size: 32.3 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.12.12

File hashes

Hashes for enroot_py-0.1.1.tar.gz
Algorithm Hash digest
SHA256 2458b7a579bdc1e6a69107dc3d1040ad86cd33c8611b37d2b61a4507d060d4f4
MD5 02cbf56215143b7d6fe450fad26da428
BLAKE2b-256 9efc8b9ae7231e1166890cfb9017a37133f882391912eb1212827bc257873f5a

See more details on using hashes here.

File details

Details for the file enroot_py-0.1.1-py3-none-any.whl.

File metadata

  • Download URL: enroot_py-0.1.1-py3-none-any.whl
  • Upload date:
  • Size: 28.8 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.12.12

File hashes

Hashes for enroot_py-0.1.1-py3-none-any.whl
Algorithm Hash digest
SHA256 336d7451fd065ab78980a5f334fe7533cae4034f729ded0eff5c3fe8166d70ca
MD5 d590388624982f8bcbd65dcab02bfad7
BLAKE2b-256 e6f7c9640ef35c7de73fa425d1e0ee6fc02764e4b177edb3c3f1b2df2dccc843

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page