Your browser sucks, JavaScript disabled! Gunnar's console needs JavaScript and WebAssembly. Git over SSH on port 2222 does not.

nordisk/knut

crates/knut-pipelines/src/bin/knut-spark-server.rs

//! `knut-spark-server` — bring a local Spark 4.x **Connect + SDP pipelines**
//! server up/down **entirely from Rust**, over podman's REST API.
//!
//! This binary is a thin CLI wrapper over the reusable [`knut_pipelines::server`]
//! library module ([`SparkServer`]): all the container lifecycle logic lives in
//! the lib so downstream crates (e.g. korp's demo orchestrator) can bring Spark
//! up from Rust too. The zero-shell charter is the lib's: no `sh`, no
//! `podman`/`docker` CLI, no [`std::process::Command`] — the container lifecycle
//! goes through the Docker/podman HTTP API, routed THROUGH draupnir's ONE OCI
//! engine (no second bollard engine in knut).
//!
//! Gated behind the `server` feature so the default build stays lean:
//! ```bash
//! cargo run -p knut-pipelines --features server --bin knut-spark-server -- up
//! ```
//!
//! Subcommands:
//! - `up` — pull the image if absent, create+start a detached container
//!   publishing 15002, wait for the connect-server ready line, print
//!   `ready: sc://localhost:15002`, and RETURN (server stays running).
//! - `stop` — stop + remove the container.
//! - `logs` — stream the server logs.
//! - `status` — report running / port.

use clap::{Parser, Subcommand};
use knut_pipelines::server::{ServerStatus, SparkServer};

#[derive(Parser)]
#[command(
    name = "knut-spark-server",
    about = "Bring a local Spark Connect + SDP pipelines server up/down via podman's REST API (pure Rust, zero-shell)",
    // `v<pkg> (<git-sha>)`, stamped pure-fs by build.rs → `knut-spark-server v<ver> (<hash>)`.
    version = knut_pipelines::VERSION_LINE
)]
struct Cli {
    #[command(subcommand)]
    command: Command,
}

#[derive(Subcommand)]
enum Command {
    /// Pull (if absent), create and start the server detached; wait until ready.
    Up,
    /// Stop and remove the server container.
    Stop,
    /// Stream the server's logs (follow).
    Logs,
    /// Report whether the server is running, and on which port.
    Status,
}

#[tokio::main]
async fn main() -> std::process::ExitCode {
    let cli = Cli::parse();
    // `connect` prints the podman-socket prerequisite hint on failure.
    let server = match SparkServer::connect() {
        Ok(s) => s,
        Err(_) => return std::process::ExitCode::FAILURE,
    };
    let result = match cli.command {
        Command::Up => match server.up().await {
            Ok(url) => {
                println!("ready:  {url}");
                println!(
                    "drive:  knutpipe pipeline {url} --spec crates/knut-pipelines/examples/pipeline.json --storage file:///tmp/knut_pipeline"
                );
                Ok(())
            }
            Err(e) => Err(e),
        },
        Command::Stop => server.stop().await.map(|()| println!("stopped {}", NAME)),
        Command::Logs => server.logs().await,
        Command::Status => match server.status().await {
            Ok(ServerStatus::Running { url }) => {
                println!("running: {NAME}  ·  {url}");
                Ok(())
            }
            Ok(ServerStatus::Stopped) => {
                println!("stopped: {NAME} exists but is not running");
                Ok(())
            }
            Ok(ServerStatus::Absent) => {
                println!("absent:  no {NAME} container (run `up` to start one)");
                Ok(())
            }
            Err(e) => Err(e),
        },
    };
    match result {
        Ok(()) => std::process::ExitCode::SUCCESS,
        Err(e) => {
            eprintln!("error: {e}");
            std::process::ExitCode::FAILURE
        }
    }
}

/// The managed container name, for the CLI's status/stop messages (mirrors the
/// lib default).
const NAME: &str = knut_pipelines::server::DEFAULT_NAME;