{"id":46609741,"url":"https://github.com/ctxswitch/kube-fake-client-rs","last_synced_at":"2026-03-07T18:08:41.806Z","repository":{"id":322037139,"uuid":"1087939364","full_name":"ctxswitch/kube-fake-client-rs","owner":"ctxswitch","description":"A fake Kubernetes client for testing operations Rust.  Based on the golang controller-runtime fake client with some extra sparkles :sparkles:.","archived":false,"fork":false,"pushed_at":"2026-03-02T06:08:54.000Z","size":657,"stargazers_count":3,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-03-02T07:49:02.430Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"Rust","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/ctxswitch.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2025-11-02T00:30:24.000Z","updated_at":"2026-03-02T06:08:57.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/ctxswitch/kube-fake-client-rs","commit_stats":null,"previous_names":["ctxswitch/kube-fake-client-rs"],"tags_count":6,"template":false,"template_full_name":null,"purl":"pkg:github/ctxswitch/kube-fake-client-rs","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ctxswitch%2Fkube-fake-client-rs","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ctxswitch%2Fkube-fake-client-rs/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ctxswitch%2Fkube-fake-client-rs/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ctxswitch%2Fkube-fake-client-rs/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/ctxswitch","download_url":"https://codeload.github.com/ctxswitch/kube-fake-client-rs/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ctxswitch%2Fkube-fake-client-rs/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":30225696,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-03-07T17:00:40.062Z","status":"ssl_error","status_checked_at":"2026-03-07T17:00:39.026Z","response_time":53,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.6:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2026-03-07T18:08:41.348Z","updated_at":"2026-03-07T18:08:41.800Z","avatar_url":"https://github.com/ctxswitch.png","language":"Rust","funding_links":[],"categories":[],"sub_categories":[],"readme":"# kube-fake-client\n\n[![CI](https://github.com/ctxswitch/kube-fake-client-rs/workflows/CI/badge.svg)](https://github.com/ctxswitch/kube-fake-client-rs/actions/workflows/ci.yml)\n[![Crates.io](https://img.shields.io/crates/v/kube-fake-client.svg)](https://crates.io/crates/kube-fake-client)\n[![Documentation](https://docs.rs/kube-fake-client/badge.svg)](https://docs.rs/kube-fake-client)\n[![codecov](https://img.shields.io/codecov/c/github/ctxswitch/kube-fake-client-rs)](https://app.codecov.io/gh/ctxswitch/kube-fake-client-rs)\n[![License](https://img.shields.io/badge/license-Apache--2.0-blue.svg)](LICENSE)\n\nIn-memory Kubernetes client for testing controllers and operators in Rust. Inspired by [controller-runtime's fake client](https://github.com/kubernetes-sigs/controller-runtime/tree/main/pkg/client/fake) from the Go ecosystem, this library provides a full-featured test client that mimics Kubernetes API behavior without requiring an actual cluster.\n\n## Features\n\n### Core Capabilities\n- **Full CRUD Operations** - Create, read, update, patch, and delete resources with complete `kube::Api\u003cK\u003e` compatibility\n- **Status Subresources** - Separate spec and status updates matching real Kubernetes behavior\n- **Resource Version Tracking** - Automatic versioning with conflict detection for optimistic concurrency\n- **Namespace Isolation** - Proper multi-namespace support with namespace-scoped and cluster-scoped resources\n\n### Advanced Features\n- **Label \u0026 Field Selectors** - Filter resources using standard Kubernetes selector syntax with custom indexing\n- **YAML Fixtures** - Load test data from files (single or multi-document YAML)\n- **Custom Resources (CRDs)** - First-class support for custom resource definitions\n- **Interceptors** - Inject custom behavior for error simulation, validation, and action tracking\n- **OpenAPI Schema Validation** - Optional runtime validation against Kubernetes OpenAPI specs (requires `validation` feature)\n\n### Developer Experience\n- **Drop-in Replacement** - Works seamlessly with existing `kube::Api\u003cK\u003e` code\n- **Type-Safe** - Leverages Rust's type system for compile-time safety\n- **Test-Friendly** - Designed specifically for unit and integration testing workflows\n\n## Installation Instructions\n\n### Basic Setup\n\nAdd `kube-fake-client` as a development dependency in your `Cargo.toml`:\n\n```toml\n[dev-dependencies]\nkube-fake-client = \"0.2\"\nkube = { version = \"3.0\", features = [\"client\", \"derive\"] }\nk8s-openapi = { version = \"0.27\", features = [\"v1_31\"] }\ntokio = { version = \"1.0\", features = [\"full\"] }\n```\n\n**Note**: By default, `kube-fake-client` uses Kubernetes API version 1.31 (`v1_31`). If you need a different version, see the Kubernetes Version Features section below.\n\n### Kubernetes Version Features\n\nThe library supports multiple Kubernetes API versions through feature flags. **Only one version feature should be enabled at a time**.\n\nAvailable versions:\n- `v1_31` (default) - Kubernetes 1.31 API\n- `v1_32` - Kubernetes 1.32 API\n- `v1_33` - Kubernetes 1.33 API\n- `v1_34` - Kubernetes 1.34 API\n- `v1_35` - Kubernetes 1.35 API\n\nTo use a specific version, disable default features and enable the desired version:\n\n```toml\n[dev-dependencies]\nkube-fake-client = { version = \"0.2\", default-features = false, features = [\"v1_31\"] }\nkube = { version = \"3.0\", features = [\"client\", \"derive\"] }\nk8s-openapi = { version = \"0.27\", features = [\"v1_31\"] }\ntokio = { version = \"1.0\", features = [\"full\"] }\n```\n\n**Important**: Make sure the k8s-openapi version feature matches the kube-fake-client version feature.\n\n### With OpenAPI Validation (Optional)\n\nTo enable runtime schema validation, add the `validation` feature:\n\n```toml\n[dev-dependencies]\nkube-fake-client = { version = \"0.2\", features = [\"validation\"] }\n\n# Or with a specific Kubernetes version\nkube-fake-client = { version = \"0.2\", default-features = false, features = [\"v1_32\", \"validation\"] }\n```\n\n### Dependencies Overview\n\nThe library requires:\n- **kube** - Kubernetes client library for Rust (for `Api\u003cK\u003e` types and traits)\n- **k8s-openapi** - Kubernetes API types (Pods, Deployments, etc.)\n- **tokio** - Async runtime (required for async test functions)\n\nAll other dependencies are managed internally by the library.\n\n## Usage\n\n### Basic Controller Testing\n\nTest a simple controller that adds labels to pods:\n\n```rust\nuse kube_fake_client::ClientBuilder;\nuse k8s_openapi::api::core::v1::Pod;\nuse kube::api::{Api, Patch, PatchParams};\nuse serde_json::json;\n\n// Controller that ensures pods have a \"managed-by\" label\nstruct PodController {\n    api: Api\u003cPod\u003e,\n}\n\nimpl PodController {\n    async fn reconcile(\u0026self, name: \u0026str) -\u003e Result\u003c(), Box\u003cdyn std::error::Error\u003e\u003e {\n        let pod = self.api.get(name).await?;\n\n        let needs_label = pod.metadata.labels.as_ref()\n            .and_then(|labels| labels.get(\"managed-by\"))\n            .is_none();\n\n        if needs_label {\n            let patch = json!({\n                \"metadata\": {\n                    \"labels\": {\n                        \"managed-by\": \"pod-controller\"\n                    }\n                }\n            });\n            self.api.patch(name, \u0026PatchParams::default(), \u0026Patch::Merge(\u0026patch)).await?;\n        }\n        Ok(())\n    }\n}\n\n#[tokio::test]\nasync fn test_controller_adds_label() -\u003e Result\u003c(), Box\u003cdyn std::error::Error\u003e\u003e {\n    // Create a pod without the managed-by label\n    let mut pod = Pod::default();\n    pod.metadata.name = Some(\"test-pod\".to_string());\n    pod.metadata.namespace = Some(\"default\".to_string());\n\n    // Build fake client with initial pod\n    let client = ClientBuilder::new()\n        .with_object(pod)\n        .build()\n        .await?;\n\n    let pods: Api\u003cPod\u003e = Api::namespaced(client, \"default\");\n    let controller = PodController { api: pods.clone() };\n\n    // Run controller reconciliation\n    controller.reconcile(\"test-pod\").await?;\n\n    // Verify the label was added\n    let updated = pods.get(\"test-pod\").await?;\n    assert_eq!(\n        updated.metadata.labels.as_ref().unwrap().get(\"managed-by\"),\n        Some(\u0026\"pod-controller\".to_string())\n    );\n\n    Ok(())\n}\n```\n\n### Status Subresource Testing\n\nTest controllers that update resource status separately from spec:\n\n```rust\nuse k8s_openapi::api::apps::v1::Deployment;\nuse kube::api::Api;\n\n#[tokio::test]\nasync fn test_status_update_isolation() -\u003e Result\u003c(), Box\u003cdyn std::error::Error\u003e\u003e {\n    let mut deployment = Deployment::default();\n    deployment.metadata.name = Some(\"my-app\".to_string());\n    deployment.metadata.namespace = Some(\"default\".to_string());\n\n    // Enable status subresource for Deployment\n    let client = ClientBuilder::new()\n        .with_object(deployment)\n        .with_status_subresource::\u003cDeployment\u003e()\n        .build()\n        .await?;\n\n    let api: Api\u003cDeployment\u003e = Api::namespaced(client, \"default\");\n\n    // Status updates don't affect spec, and vice versa\n    // (implementation details omitted for brevity)\n\n    Ok(())\n}\n```\n\n### Loading YAML Fixtures\n\nLoad test data from YAML files:\n\n```rust\n#[tokio::test]\nasync fn test_with_fixtures() -\u003e Result\u003c(), Box\u003cdyn std::error::Error\u003e\u003e {\n    let client = ClientBuilder::new()\n        .with_fixture_dir(\"tests/fixtures\")\n        .load_fixture(\"pods.yaml\")?\n        .load_fixture(\"deployments.yaml\")?\n        .build()\n        .await?;\n\n    let pods: Api\u003cPod\u003e = Api::namespaced(client, \"default\");\n    let pod_list = pods.list(\u0026Default::default()).await?;\n\n    assert!(!pod_list.items.is_empty());\n    Ok(())\n}\n```\n\n### Custom Resources (CRDs)\n\nTest operators that work with custom resources:\n\n```rust\nuse kube::CustomResource;\nuse schemars::JsonSchema;\nuse serde::{Deserialize, Serialize};\n\n#[derive(CustomResource, Clone, Debug, Deserialize, Serialize, JsonSchema)]\n#[kube(group = \"example.com\", version = \"v1\", kind = \"MyApp\", namespaced)]\npub struct MyAppSpec {\n    replicas: i32,\n    image: String,\n}\n\n#[tokio::test]\nasync fn test_custom_resource() -\u003e Result\u003c(), Box\u003cdyn std::error::Error\u003e\u003e {\n    let mut app = MyApp::new(\"my-app\", MyAppSpec {\n        replicas: 3,\n        image: \"nginx:latest\".to_string(),\n    });\n    app.metadata.namespace = Some(\"default\".to_string());\n\n    // Register the CRD with the fake client\n    let client = ClientBuilder::new()\n        .with_resource::\u003cMyApp\u003e()\n        .with_object(app)\n        .build()\n        .await?;\n\n    let api: Api\u003cMyApp\u003e = Api::namespaced(client, \"default\");\n    let retrieved = api.get(\"my-app\").await?;\n\n    assert_eq!(retrieved.spec.replicas, 3);\n    Ok(())\n}\n```\n\n### Error Injection with Interceptors\n\nSimulate API errors for testing error handling:\n\n```rust\nuse kube_fake_client::{ClientBuilder, interceptor, Error};\n\n#[tokio::test]\nasync fn test_error_handling() -\u003e Result\u003c(), Box\u003cdyn std::error::Error\u003e\u003e {\n    let client = ClientBuilder::new()\n        .with_interceptor_funcs(\n            interceptor::Funcs::new().create(|ctx| {\n                // Inject error for pods named \"trigger-error\"\n                if ctx.object.get(\"metadata\")\n                    .and_then(|m| m.get(\"name\"))\n                    .and_then(|n| n.as_str()) == Some(\"trigger-error\") {\n                    return Err(Error::Internal(\"simulated error\".into()));\n                }\n                Ok(None)\n            })\n        )\n        .build()\n        .await?;\n\n    let pods: Api\u003cPod\u003e = Api::namespaced(client, \"default\");\n\n    let mut pod = Pod::default();\n    pod.metadata.name = Some(\"trigger-error\".to_string());\n\n    // This create should fail due to interceptor\n    let result = pods.create(\u0026Default::default(), \u0026pod).await;\n    assert!(result.is_err());\n\n    Ok(())\n}\n```\n\n### Field Selectors\n\nFilter resources using field selectors:\n\n```rust\nuse kube::api::ListParams;\n\n#[tokio::test]\nasync fn test_field_selectors() -\u003e Result\u003c(), Box\u003cdyn std::error::Error\u003e\u003e {\n    // Create pods and setup client (omitted for brevity)\n\n    let pods: Api\u003cPod\u003e = Api::namespaced(client, \"default\");\n\n    // Filter by metadata.name (universally supported)\n    let filtered = pods\n        .list(\u0026ListParams::default().fields(\"metadata.name=my-pod\"))\n        .await?;\n\n    assert_eq!(filtered.items.len(), 1);\n    Ok(())\n}\n```\n\n### Examples\n\nThe [`examples/`](examples/) directory contains comprehensive examples demonstrating various patterns:\n\n- **[basic_usage.rs](examples/basic_usage.rs)** - CRUD operations, label/field selectors, namespaced and cluster-scoped resources\n- **[controller.rs](examples/controller.rs)** - Controller testing pattern with label management\n- **[custom_resource.rs](examples/custom_resource.rs)** - Working with custom resource definitions (CRDs)\n- **[status_controller.rs](examples/status_controller.rs)** - Status subresource handling and separation\n- **[fixture_loading.rs](examples/fixture_loading.rs)** - Loading test data from YAML files\n- **[interceptors.rs](examples/interceptors.rs)** - Error injection and custom behavior\n- **[schema_validations.rs](examples/schema_validations.rs)** - Runtime OpenAPI schema validation (requires `validation` feature)\n\n#### Running Examples\n\n```bash\n# Run a specific example\ncargo run --example basic_usage\ncargo run --example controller\ncargo run --example custom_resource\n\n# Run example with validation feature\ncargo run --example schema_validations --features validation\n\n# Run all examples\nfor example in basic_usage controller custom_resource fixture_loading \\\n               status_controller interceptors; do\n    cargo run --example $example\ndone\n```\n\n## Contributing\n\nContributions are welcome! This project aims to closely follow the behavior of [controller-runtime's fake client](https://github.com/kubernetes-sigs/controller-runtime) while providing an idiomatic Rust experience.\n\nPlease see [CONTRIBUTING.md](CONTRIBUTING.md) for:\n- Development setup\n- Code style guidelines\n- Testing requirements\n- Pull request process\n\n## License\n\nLicensed under the Apache License, Version 2.0. See [LICENSE](LICENSE) for details.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fctxswitch%2Fkube-fake-client-rs","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fctxswitch%2Fkube-fake-client-rs","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fctxswitch%2Fkube-fake-client-rs/lists"}