Skip to content

Commit 0c23c69

Browse files
authored
Merge pull request #946 from yinkscss/fix/881-kubernetes-operator-sdk-framework-for-extensions
[881] [EPIC] Kubernetes Operator SDK Framework for Extensions
2 parents d4afe91 + de94410 commit 0c23c69

14 files changed

Lines changed: 830 additions & 0 deletions

Cargo.toml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -205,6 +205,10 @@ k8s-openapi = { version = "0.22", default-features = false, features = [
205205
"v1_30",
206206
] }
207207

208+
[[bin]]
209+
name = "stellar-scaffold"
210+
path = "src/bin/stellar-scaffold.rs"
211+
208212
[[bin]]
209213
name = "stellar-operator"
210214
path = "src/main.rs"

docs/operator-sdk-guide.md

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
# Operator SDK Developer Guide
2+
3+
Build custom operators and extensions for Stellar-K8s using the in-tree SDK.
4+
5+
## SDK Components
6+
7+
| Module | Purpose |
8+
|--------|---------|
9+
| `stellar_k8s::sdk` | Public SDK entry point |
10+
| `stellar_k8s::plugin_sdk` | Reconcile hooks and sidecar injectors |
11+
| `stellar_k8s::sdk::codegen` | CRD-to-controller stub generation |
12+
| `stellar_k8s::sdk::testing` | Mock contexts and test harness |
13+
14+
## Scaffold a New Controller
15+
16+
```bash
17+
cargo run --bin stellar-scaffold -- StellarMyResource --print
18+
```
19+
20+
## Example Operators
21+
22+
See `examples/operators/` for three reference implementations:
23+
24+
1. `metrics_exporter.rs` - ReconcileHook
25+
2. `log_shipper.rs` - SidecarInjector
26+
3. `registry_validator.rs` - Admission policy integration
27+
28+
## Register a Plugin
29+
30+
```rust
31+
use stellar_k8s::plugin_sdk::PluginRegistry;
32+
33+
let registry = std::sync::Arc::new(
34+
PluginRegistry::new().with_hook(MyHook)
35+
);
36+
```
37+
38+
## CI/CD Template
39+
40+
Use `.github/workflows/operator-extension.yml` as a starting point for extension CI.
41+
42+
## Tutorial
43+
44+
See `docs/tutorials/building-your-first-operator.md` for a step-by-step walkthrough.

docs/secret-management-guide.md

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
# Secret Management Guide
2+
3+
Declarative secret management via the `StellarSecret` CRD with dynamic credentials,
4+
automatic rotation, KMS encryption, and multi-backend support.
5+
6+
## Features
7+
8+
- Dynamic database credentials with TTL
9+
- Automatic rotation every 30 days (configurable via `720h` interval)
10+
- AWS KMS, Azure Key Vault, GCP KMS, and Vault backends
11+
- Complete audit logging with anomaly detection
12+
- Zero-downtime rotation with version history and rollback
13+
- Secret injection as environment variables or files
14+
15+
## Quick Start
16+
17+
```yaml
18+
apiVersion: stellar.org/v1alpha1
19+
kind: StellarSecret
20+
metadata:
21+
name: postgres-dynamic
22+
spec:
23+
secretName: postgres-creds
24+
backend: vault
25+
provider: Vault
26+
dynamic:
27+
enabled: true
28+
ttl: 1h
29+
rotation:
30+
interval: 720h
31+
zeroDowntime: true
32+
```
33+
34+
## Grafana Dashboard
35+
36+
Import `monitoring/grafana/stellar-secret-dashboard.json` for rotation metrics,
37+
sync drift, and access anomaly alerts.
38+
39+
## Compliance
40+
41+
Export audit trails via `stellar-operator export-compliance` for auditor review.

examples/operators/log_shipper.rs

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
//! Example sidecar injector operator using the Stellar-K8s SDK.
2+
3+
use async_trait::async_trait;
4+
use stellar_k8s::plugin_sdk::{InjectedSidecar, ReconcileContext, SidecarInjector};
5+
6+
pub struct LogShipperInjector;
7+
8+
#[async_trait]
9+
impl SidecarInjector for LogShipperInjector {
10+
fn name(&self) -> &str {
11+
"log-shipper"
12+
}
13+
14+
async fn sidecars(&self, _ctx: &ReconcileContext) -> Vec<InjectedSidecar> {
15+
vec![InjectedSidecar {
16+
name: "log-shipper".into(),
17+
image: "fluent/fluent-bit:3.0".into(),
18+
..Default::default()
19+
}]
20+
}
21+
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
//! Example reconcile hook operator using the Stellar-K8s SDK.
2+
3+
use async_trait::async_trait;
4+
use stellar_k8s::plugin_sdk::{HookResult, ReconcileContext, ReconcileHook};
5+
6+
pub struct MetricsExporterHook;
7+
8+
#[async_trait]
9+
impl ReconcileHook for MetricsExporterHook {
10+
fn name(&self) -> &str {
11+
"metrics-exporter"
12+
}
13+
14+
async fn pre_reconcile(&self, ctx: &ReconcileContext) -> HookResult {
15+
tracing::debug!(node = %ctx.node_name, "exporting pre-reconcile metrics");
16+
HookResult::Continue
17+
}
18+
}
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
//! Example admission policy hook using registry admission checks.
2+
3+
use stellar_k8s::controller::check_admission;
4+
use stellar_k8s::crd::stellar_registry::{StellarRegistry, VulnerabilitySummary};
5+
6+
pub fn validate_image(registry: &StellarRegistry, image: &str, signed: bool) -> Result<(), String> {
7+
check_admission(registry, image, signed, &VulnerabilitySummary::default())
8+
}
9+
10+
#[cfg(test)]
11+
mod tests {
12+
use super::*;
13+
use kube::core::ObjectMeta;
14+
use stellar_k8s::crd::stellar_registry::{
15+
AdmissionPolicy, RegistryMirror, ScanningConfig, SigningConfig, StellarRegistrySpec,
16+
};
17+
18+
fn sample_registry() -> StellarRegistry {
19+
StellarRegistry {
20+
metadata: ObjectMeta {
21+
name: Some("reg".into()),
22+
namespace: Some("stellar".into()),
23+
..Default::default()
24+
},
25+
spec: StellarRegistrySpec {
26+
endpoint: "registry.example.com".into(),
27+
scanning: ScanningConfig::default(),
28+
signing: SigningConfig {
29+
require_signature: false,
30+
..Default::default()
31+
},
32+
admission: AdmissionPolicy::default(),
33+
mirrors: vec![],
34+
garbage_collection: None,
35+
proxy: None,
36+
auto_patch: None,
37+
},
38+
status: None,
39+
}
40+
}
41+
42+
#[test]
43+
fn allows_signed_image() {
44+
assert!(validate_image(&sample_registry(), "app:v1", true).is_ok());
45+
}
46+
}
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
apiVersion: stellar.org/v1alpha1
2+
kind: StellarSecret
3+
metadata:
4+
name: postgres-dynamic
5+
namespace: stellar
6+
spec:
7+
secretName: postgres-creds
8+
backend: vault
9+
provider: Vault
10+
dynamic:
11+
enabled: true
12+
ttl: 1h
13+
database:
14+
host: postgres.stellar.svc
15+
database: horizon
16+
username: horizon
17+
role: readwrite
18+
rotation:
19+
interval: 720h
20+
zeroDowntime: true
21+
versionRetention: 5
22+
targets:
23+
- kind: envVar
24+
name: horizon
25+
key: DATABASE_URL
26+
audit:
27+
enabled: true
28+
sink: s3://audit-logs/secrets

src/bin/stellar-scaffold.rs

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
//! stellar-scaffold: CLI for scaffolding Stellar-K8s operators
2+
3+
use clap::Parser;
4+
use stellar_k8s::sdk::codegen::{generate_controller_stub, render_controller_source};
5+
6+
#[derive(Parser)]
7+
#[command(name = "stellar-scaffold")]
8+
#[command(about = "Scaffold a new Stellar-K8s operator controller from a CRD kind")]
9+
struct Args {
10+
/// CRD API group
11+
#[arg(long, default_value = "stellar.org")]
12+
group: String,
13+
14+
/// CRD API version
15+
#[arg(long, default_value = "v1alpha1")]
16+
version: String,
17+
18+
/// CRD Kind name (PascalCase)
19+
kind: String,
20+
21+
/// Print generated Rust source to stdout
22+
#[arg(long)]
23+
print: bool,
24+
}
25+
26+
fn main() {
27+
let args = Args::parse();
28+
let stub = generate_controller_stub(&args.group, &args.version, &args.kind);
29+
if args.print {
30+
print!("{}", render_controller_source(&stub));
31+
} else {
32+
println!("Controller stub: {}", stub.reconciler_fn);
33+
println!("Module: src/controller/{}.rs", stub.module_name);
34+
println!("Run with --print to emit reconciler source");
35+
}
36+
}

0 commit comments

Comments
 (0)