seppo

Crates.ioseppo
lib.rsseppo
version0.1.0
created_at2025-12-27 19:00:18.663369+00
updated_at2025-12-27 19:00:18.663369+00
descriptionKubernetes testing framework with multi-cloud cluster management
homepage
repositoryhttps://github.com/yairfalse/seppo
max_upload_size
id2007675
size520,160
Yair Etziony (yairfalse)

documentation

README

SEPPO

Kubernetes SDK for Rust. No YAML. No CLI. Just code.

License Rust

This is a learning project - exploring Kubernetes SDK patterns in Rust.

use seppo::Context;

let ctx = Context::new().await?;
ctx.apply(&deployment).await?;
ctx.wait_ready("deployment/myapp").await?;

Why

The problem: Kubernetes tooling is fragmented. You write YAML, shell out to kubectl, maintain bash scripts, and wrestle with templating engines. Your "infrastructure as code" isn't really code—it's configuration files with code-like aspirations.

The vision: What if Kubernetes operations were just... code? Real code. In your language. With types, IDE support, and compile-time checks.

Seppo makes Kubernetes a library call:

// This is your deployment. No YAML.
let deployment = DeploymentFixture::new("myapp")
    .image("myapp:v1")
    .replicas(3)
    .port(8080)
    .build();

ctx.apply(&deployment).await?;
ctx.wait_ready("deployment/myapp").await?;

No YAML. No kubectl. No templating. Just Rust.


What We Want to Achieve

  1. Escape YAML — Define resources in code, not configuration files
  2. Native SDK — First-class Rust library, not a CLI wrapper
  3. Full kubectl coverage — Everything kubectl does, but programmatic
  4. Ecosystem integration — Import from Helm/Kustomize as migration path, then stay in code
  5. Multi-language — Same concepts in Rust (Seppo) and Go (Ilmari)

The goal: you should never need to write YAML again.


How It Works

┌──────────────────┐     ┌─────────────────┐     ┌──────────────┐
│  Context::new()  │────▶│     Context     │────▶│  Kubernetes  │
│  (your code)     │     │   (namespace)   │     │ (any cluster)│
└──────────────────┘     └─────────────────┘     └──────────────┘
         │                       │
         │                       ├── apply()      → create resources
         │                       ├── get/list()   → read resources
         │                       ├── delete()     → remove resources
         │                       ├── wait_ready() → poll until ready
         │                       ├── port_forward() → port forward
         │                       ├── exec()       → run commands
         │                       └── logs()       → stream logs

Each Context manages a namespace. Resources are created via kube-rs. The optional #[seppo::test] macro adds automatic cleanup and failure diagnostics.


Install

[dependencies]
seppo = "0.1"

Requires a Kubernetes cluster (Kind, Minikube, EKS, GKE, etc.)


Usage

Standalone

use seppo::Context;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let ctx = Context::new().await?;

    ctx.apply(&my_deployment).await?;
    ctx.wait_ready("deployment/myapp").await?;

    // Your operations here...

    ctx.cleanup().await?;
    Ok(())
}

With Test Macro

use seppo::Context;

#[seppo::test]
async fn test_my_app(ctx: Context) {
    ctx.apply(&deployment).await?;
    ctx.wait_ready("deployment/myapp").await?;

    let pf = ctx.port_forward("svc/myapp", 8080).await?;
    assert!(pf.get("/health").await?.contains("ok"));
}
// Cleanup automatic on success, diagnostics on failure

Resource Builders

use seppo::deployment;

let deploy = deployment("myapp")
    .image("nginx:latest")
    .replicas(3)
    .port(80)
    .env("LOG_LEVEL", "debug")
    .label("tier", "frontend")
    .build();

ctx.apply(&deploy).await?;

Deploy Stacks

use seppo::stack;

let s = stack()
    .service("frontend")
        .image("fe:v1")
        .replicas(2)
        .port(80)
    .service("backend")
        .image("be:v1")
        .replicas(3)
        .port(8080)
    .service("db")
        .image("postgres:15")
        .port(5432)
    .build();

ctx.up(&s).await?;

Async Conditions

use seppo::eventually;

eventually(|| async {
    let pods: Vec<Pod> = ctx.list().await?;
    Ok(pods.len() >= 3)
})
.timeout(Duration::from_secs(60))
.await?;

Failure Diagnostics

When tests fail, Seppo keeps the namespace and dumps diagnostics:

━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
  SEPPO TEST FAILED
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

  Namespace: seppo-test-a1b2c3d4 (kept for debugging)

─── Pod Logs ───────────────────────────────────────────────────
[myapp-xyz123]
  ERROR: Connection refused to postgres:5432

─── Events ─────────────────────────────────────────────────────
  • 10:42:00  Pod/myapp  Scheduled  Assigned to node-1
  • 10:42:01  Pod/myapp  BackOff    Image pull error

─── Debug ──────────────────────────────────────────────────────
  kubectl -n seppo-test-a1b2c3d4 get all
  kubectl delete ns seppo-test-a1b2c3d4
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

Features

Category Features
Core Context, apply/get/list/delete, wait_ready
Network Port forwarding, exec, logs
Builders DeploymentFixture, PodFixture, ServiceFixture, Stack
Testing #[seppo::test], eventually/consistently, assertions
Diagnostics Pod logs, events, failure reports
Providers Kind, Minikube, existing clusters
Assertions Semantic assertions on pods, deployments, services

Roadmap

Phase Goal
0 Rebrand as SDK, expose Context::new() standalone
1 Complete primitives: patch, watch, copy, diff
2 Helm/Kustomize import, migration CLI
3 Operational: scale, rollback, restart, drain
4 Multi-cluster, impersonation, audit

Naming

Seppo (Finnish smith god from Kalevala) - Part of a Finnish tool naming theme:

  • SEPPO (smith) - Kubernetes SDK for Rust
  • ILMARI (smith) - Kubernetes SDK for Go
  • SYKLI (cycle) - CI orchestrator
  • NOPEA (fast) - GitOps controller
  • KULTA (gold) - Progressive delivery
  • RAUTA (iron) - Gateway API controller

License

Apache-2.0

Commit count: 0

cargo fmt