Skip to content

threefoldtech/zinit-client

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

18 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Zinit Client

Rust CI Rust Examples Code Coverage Security Scan

A Rust client library for interacting with the Zinit service manager.

Features

  • Complete API coverage for all Zinit operations
  • Robust error handling with custom error types
  • Automatic reconnection on socket errors
  • Retry mechanisms for transient failures
  • Async/await support using Tokio
  • Strongly typed service states and responses
  • Efficient log streaming

Installation

Add this to your Cargo.toml:

[dependencies]
zinit-client = "0.2.0"

Building

To build the library, you need Rust and Cargo installed. Then run:

# Build the library
cargo build

# Build with optimizations
cargo build --release

# Build the examples
cargo build --examples

Usage

use zinit_client::{ZinitClient, Result};

#[tokio::main]
async fn main() -> Result<()> {
    // Create a client with default configuration
    let client = ZinitClient::new("/var/run/zinit.sock");
    
    // List all services
    let services = client.list().await?;
    println!("Services: {:?}", services);
    
    // Start a service
    client.start("nginx").await?;
    
    // Get service status
    let status = client.status("nginx").await?;
    println!("Nginx status: {:?}", status);
    
    // Stream logs
    let mut logs = client.logs(true, Some("nginx")).await?;
    while let Some(log) = logs.next().await {
        println!("{}: {}", log?.timestamp, log?.message);
    }
    
    Ok(())
}

Service CRUD Operations

The client supports creating, reading, updating, and deleting service configurations:

use serde_json::json;
use zinit_client::{ZinitClient, Result};

#[tokio::main]
async fn main() -> Result<()> {
    let client = ZinitClient::new("/var/run/zinit.sock");

    // Create a new service
    let service_config = json!({
        "exec": "/usr/bin/my-app",
        "args": ["--port", "8080"],
        "env": {
            "PORT": "8080",
            "ENV": "production"
        },
        "oneshot": false,
        "working_dir": "/opt/my-app"
    });

    client.create_service("my-app", service_config).await?;

    // Get service configuration
    let config = client.get_service("my-app").await?;
    println!("Service config: {}", serde_json::to_string_pretty(&config)?);

    // Delete a service (stops it first if running)
    client.delete_service("my-app").await?;

    Ok(())
}

Configuration

You can customize the client behavior using ClientConfig:

use zinit_client::{ZinitClient, ClientConfig};
use std::time::Duration;

let config = ClientConfig {
    socket_path: "/var/run/zinit.sock".into(),
    connection_timeout: Duration::from_secs(5),
    operation_timeout: Duration::from_secs(30),
    max_retries: 3,
    retry_delay: Duration::from_millis(100),
    max_retry_delay: Duration::from_secs(5),
    retry_jitter: true,
};

let client = ZinitClient::with_config(config);

Examples

See the examples directory for more usage examples:

Running Examples

To run the examples, you need a running Zinit instance. The examples will try to connect to Zinit at the default socket path (/var/run/zinit.sock).

# Basic usage example (requires Zinit)
cargo run --example basic_usage

# Comprehensive service management with CRUD operations (interactive, requires Zinit)
cargo run --example service_management

# Log streaming example (interactive, requires Zinit)
cargo run --example log_streaming

# Mock server demo (doesn't require Zinit)
cargo run --example mock_server_demo

If you want to use a different socket path, you'll need to modify the examples. Open the example file and change the socket path in the ZinitClient::new() call:

// Change this line
let client = ZinitClient::new("/var/run/zinit.sock");

// To use a custom socket path
let client = ZinitClient::new("/path/to/your/zinit.sock");

Running Without Zinit

If you don't have Zinit running, you can still test the client by using the mock server provided in the tests directory. The mock server simulates a Zinit instance for testing purposes.

Here's how to use it:

use std::path::PathBuf;
use tempfile::tempdir;
use zinit_client::ZinitClient;
use zinit_client::tests::MockZinitServer;

#[tokio::main]
async fn main() -> Result<()> {
    // Create a temporary directory for the socket
    let temp_dir = tempdir().expect("Failed to create temp dir");
    let socket_path = temp_dir.path().join("mock-zinit.sock");
    
    // Create and start the mock server
    let mut server = MockZinitServer::new(&socket_path).await;
    server.start().await.expect("Failed to start mock server");
    
    // Add some mock services
    server.add_service(MockService {
        name: "test-service".to_string(),
        pid: 1001,
        state: MockServiceState::Running,
        target: MockServiceTarget::Up,
        after: HashMap::new(),
    });
    
    // Create a client to connect to the mock server
    let client = ZinitClient::new(&socket_path);
    
    // Use the client as normal
    let services = client.list().await?;
    println!("Services: {:?}", services);
    
    // Stop the mock server when done
    server.stop().await;
    
    Ok(())
}

Note: The examples assume Zinit is running and listening on the default socket path (/var/run/zinit.sock). If your Zinit instance is using a different socket path, you'll need to modify the examples accordingly.

Testing

The library includes both integration tests and a mock server for testing without requiring a real Zinit instance.

Running Tests

# Run all tests
cargo test

# Run a specific test
cargo test test_client_reconnection

All tests use the mock server, so they can be run without requiring a real Zinit instance. The mock server simulates a Zinit instance for testing purposes, allowing for reliable and reproducible tests.

CI/CD

This project uses GitHub Actions for continuous integration and delivery:

  • Rust CI: Builds the project, runs tests, and checks code formatting and linting
  • Rust Examples: Builds and runs all examples to ensure they work correctly
  • Code Coverage: Generates code coverage reports and uploads them to Codecov
  • Security Scan: Performs security audits on dependencies using cargo-audit and cargo-deny
  • Publish: Automatically publishes the crate to crates.io when a new release is created

All workflows run on every push to any branch and on all pull requests.

License

See LICENSE file for details.

About

A Rust client library for interacting with the Zinit https://github.com/threefoldtech/zinit service manager.

Topics

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Languages