# lmrc-docker

> Part of the [LMRC Stack](https://gitlab.com/lemarco/lmrc-stack) - Infrastructure-as-Code toolkit for building production-ready Rust applications

[![Crates.io](https://img.shields.io/crates/v/lmrc-docker.svg)](https://crates.io/crates/lmrc-docker)
[![Documentation](https://docs.rs/lmrc-docker/badge.svg)](https://docs.rs/lmrc-docker)
[![License](https://img.shields.io/crates/l/lmrc-docker.svg)](https://github.com/lemarco/lmrc-docker)

An ergonomic, comprehensive Docker client library for Rust with fluent builder APIs for managing containers, images, networks, volumes, and registries.

## Features

- **Fluent Builder APIs**: Ergonomic, chainable methods for creating and configuring Docker resources
- **Full Docker API Coverage**: Complete support for containers, images, networks, and volumes
- **Registry Management**: Search, authenticate, and manage images across multiple registries (Docker Hub, GitHub Container Registry, custom registries)
- **Async/Await**: Built on Tokio for modern async Rust
- **Type-Safe**: Leverages Rust's type system for compile-time safety
- **Comprehensive Error Handling**: Detailed error types using `thiserror`
- **Well Documented**: Extensive documentation and examples

## Installation

Add this to your `Cargo.toml`:

```toml
[dependencies]
lmrc-docker = "0.1"
tokio = { version = "1.0", features = ["full"] }
```

## Quick Start

```rust
use lmrc_docker::{DockerClient, Result};

#[tokio::main]
async fn main() -> Result<()> {
    // Create a client
    let client = DockerClient::new()?;

    // Check Docker is available
    client.ping().await?;

    // Pull an image
    client.images().pull("nginx:alpine", None).await?;

    // Create and start a container
    let container = client.containers()
        .create("nginx:alpine")
        .name("my-nginx")
        .port(8080, 80, "tcp")
        .env("ENV", "production")
        .build()
        .await?;

    container.start().await?;
    println!("Container started: {}", container.id());

    // Stop and remove the container
    container.stop(Some(10)).await?;
    container.remove(false, false).await?;

    Ok(())
}
```

## Examples

### Container Management

```rust
use lmrc_docker::DockerClient;

let client = DockerClient::new()?;

// Create a container with advanced configuration
let container = client.containers()
    .create("redis:alpine")
    .name("my-redis")
    .port(6379, 6379, "tcp")
    .env("REDIS_PASSWORD", "secret")
    .restart_always()
    .memory_limit(512 * 1024 * 1024) // 512MB
    .build()
    .await?;

// Start the container
container.start().await?;

// View logs
let logs = container.logs(true, true, true, None).await?;
println!("Logs: {}", logs);

// Execute a command
let exec_id = container.exec(vec!["redis-cli".into(), "ping".into()], false).await?;

// Stop and remove
container.stop(Some(10)).await?;
container.remove(true, true).await?;
```

### Image Operations

```rust
use lmrc_docker::DockerClient;

let client = DockerClient::new()?;

// Pull an image
client.images().pull("alpine:latest", None).await?;

// Build an image from a Dockerfile
let image_id = client.images()
    .build("./my-app")
    .tag("my-app:latest")
    .dockerfile("Dockerfile")
    .cache_from(vec!["my-app:cache"])
    .execute()
    .await?;

// Push to registry (requires authentication)
client.images().push("username/my-app:latest", None).await?;

// List images
let images = client.images().list(false).await?;
for image in images {
    println!("Image: {:?}", image.repo_tags);
}

// Remove an image
client.images().remove("my-app:latest", false, false).await?;
```

### Registry Management

```rust
use lmrc_docker::{DockerClient, RegistryConfig};

let client = DockerClient::new()?;
let registry = client.registry();

// Search for images
let results = registry.search("nginx", Some(10)).await?;
for result in results {
    println!("{}: {} stars", result.name, result.star_count.unwrap_or(0));
}

// Authenticate with Docker Hub
let config = RegistryConfig::docker_hub(
    "username".to_string(),
    "password".to_string()
);
registry.login(&config).await?;

// Authenticate with GitHub Container Registry
let config = RegistryConfig::github(
    "username".to_string(),
    "github_token".to_string()
);
registry.login(&config).await?;

// Check if an image exists locally
let exists = registry.image_exists_locally("alpine:latest").await?;

// Tag an image
registry.tag_image("alpine:latest", "my-alpine", Some("v1.0")).await?;

// Export/Import images
let tar_data = registry.export_image("alpine:latest").await?;
let image_id = registry.import_image(tar_data, Some("imported:latest")).await?;

// Prune unused images
let reclaimed = registry.prune_images(true).await?;
println!("Reclaimed {} bytes", reclaimed);
```

### Network Management

```rust
use lmrc_docker::DockerClient;

let client = DockerClient::new()?;

// Create a network
let network = client.networks()
    .create("my-network")
    .driver("bridge")
    .await?;

// Connect a container to a network
network.connect("container-id", None).await?;

// Disconnect a container
network.disconnect("container-id", false).await?;

// List networks
let networks = client.networks().list().await?;

// Remove network
network.remove().await?;
```

### Volume Management

```rust
use lmrc_docker::DockerClient;

let client = DockerClient::new()?;

// Create a volume
let volume = client.volumes()
    .create("my-volume")
    .driver("local")
    .await?;

// List volumes
let volumes = client.volumes().list().await?;

// Remove volume
volume.remove(false).await?;

// Prune unused volumes
let pruned = client.volumes().prune().await?;
```

## Configuration

### Custom Docker Connection

```rust
use lmrc_docker::{DockerClient, DockerClientConfig};

// Connect via Unix socket
let client = DockerClient::connect_with_unix("/var/run/docker.sock")?;

// Connect via TCP
let client = DockerClient::connect_with_tcp("localhost:2375")?;

// Custom configuration
let config = DockerClientConfig::new()
    .timeout(60)
    .api_version("1.43");
let client = DockerClient::with_config(config)?;
```

### Environment Variables

The client respects the `DOCKER_HOST` environment variable:

```bash
export DOCKER_HOST=tcp://localhost:2375
export DOCKER_HOST=unix:///var/run/docker.sock
```

## Error Handling

The library uses a comprehensive error type system:

```rust
use lmrc_docker::{DockerClient, DockerError, Result};

match client.containers().get("unknown-container") {
    Ok(container) => println!("Found container"),
    Err(DockerError::ContainerNotFound(id)) => {
        eprintln!("Container {} not found", id);
    }
    Err(DockerError::Connection(msg)) => {
        eprintln!("Connection error: {}", msg);
    }
    Err(e) => eprintln!("Other error: {}", e),
}
```

## Architecture

The library is organized into modular components:

- **`client`**: Docker client configuration and connection management
- **`containers`**: Container lifecycle operations with `ContainerBuilder`
- **`images`**: Image operations with `ImageBuilder` for building images
- **`networks`**: Network creation and management
- **`volumes`**: Volume operations
- **`registry`**: Registry authentication, search, and image management
- **`error`**: Comprehensive error types

## Advanced Usage

### Direct Bollard Access

For operations not covered by the high-level API:

```rust
let client = DockerClient::new()?;
let bollard = client.inner();

// Use bollard directly
let info = bollard.info().await?;
```

### Streaming Logs

```rust
use futures_util::StreamExt;

let container = client.containers().get("my-container").await?;
let mut logs_stream = container.logs_stream(true, true, true, None).await?;

while let Some(log) = logs_stream.next().await {
    match log {
        Ok(line) => println!("{}", line),
        Err(e) => eprintln!("Error: {}", e),
    }
}
```

## Requirements

- Rust 1.70 or later
- Docker daemon running and accessible

## Examples

See the [examples](examples/) directory for complete working examples:

- [`basic_container.rs`](examples/basic_container.rs) - Basic container lifecycle
- [`build_image.rs`](examples/build_image.rs) - Building Docker images
- [`list_resources.rs`](examples/list_resources.rs) - Listing containers, images, networks, volumes
- [`registry_operations.rs`](examples/registry_operations.rs) - Registry management operations

Run examples with:

```bash
cargo run --example basic_container
cargo run --example registry_operations
```

## Testing

The library includes integration tests that require a running Docker daemon:

```bash
# Run tests
cargo test

# Run specific test
cargo test test_container_lifecycle
```

## Contributing

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

## License

Part of the LMRC Stack project. Licensed under either of:

- Apache License, Version 2.0 ([LICENSE-APACHE](../../LICENSE-APACHE) or <http://www.apache.org/licenses/LICENSE-2.0>)
- MIT license ([LICENSE-MIT](../../LICENSE-MIT) or <http://opensource.org/licenses/MIT>)

at your option.
## Acknowledgments

Built on top of [Bollard](https://github.com/fussybeaver/bollard), the excellent Docker daemon API for Rust.

## Related Projects

- [bollard](https://crates.io/crates/bollard) - Lower-level Docker API
- [shiplift](https://crates.io/crates/shiplift) - Alternative Docker API client
- [dockworker](https://crates.io/crates/dockworker) - Docker daemon API wrapper
