Skip to main content

neuron_secret_k8s/
lib.rs

1#![deny(missing_docs)]
2//! Stub secret resolver for Kubernetes Secrets.
3//!
4//! This crate provides the correct trait impl shape for a Kubernetes secret resolver.
5//! The actual K8s API integration is not implemented — all resolve calls return
6//! `SecretError::BackendError`.
7
8use async_trait::async_trait;
9use layer0::secret::SecretSource;
10use neuron_auth::AuthProvider;
11use neuron_secret::{SecretError, SecretLease, SecretResolver};
12use std::sync::Arc;
13
14/// Stub resolver for Kubernetes Secrets.
15pub struct K8sResolver {
16    _auth: Arc<dyn AuthProvider>,
17}
18
19impl K8sResolver {
20    /// Create a new Kubernetes resolver (stub).
21    pub fn new(auth: Arc<dyn AuthProvider>) -> Self {
22        Self { _auth: auth }
23    }
24}
25
26#[async_trait]
27impl SecretResolver for K8sResolver {
28    async fn resolve(&self, source: &SecretSource) -> Result<SecretLease, SecretError> {
29        match source {
30            SecretSource::Kubernetes {
31                namespace,
32                name,
33                key,
34            } => Err(SecretError::BackendError(format!(
35                "K8sResolver is a stub — would resolve {namespace}/{name}/{key}"
36            ))),
37            _ => Err(SecretError::NoResolver("kubernetes".into())),
38        }
39    }
40}
41
42#[cfg(test)]
43mod tests {
44    use super::*;
45    use neuron_auth::{AuthError, AuthRequest, AuthToken};
46
47    struct StubAuth;
48    #[async_trait]
49    impl AuthProvider for StubAuth {
50        async fn provide(&self, _request: &AuthRequest) -> Result<AuthToken, AuthError> {
51            Ok(AuthToken::permanent(b"stub".to_vec()))
52        }
53    }
54
55    fn _assert_send_sync<T: Send + Sync>() {}
56
57    #[test]
58    fn object_safety() {
59        _assert_send_sync::<Box<dyn SecretResolver>>();
60        _assert_send_sync::<Arc<dyn SecretResolver>>();
61        let auth: Arc<dyn AuthProvider> = Arc::new(StubAuth);
62        let _: Arc<dyn SecretResolver> = Arc::new(K8sResolver::new(auth));
63    }
64
65    #[tokio::test]
66    async fn matches_k8s_source() {
67        let auth: Arc<dyn AuthProvider> = Arc::new(StubAuth);
68        let resolver = K8sResolver::new(auth);
69        let source = SecretSource::Kubernetes {
70            namespace: "default".into(),
71            name: "api-keys".into(),
72            key: "anthropic".into(),
73        };
74        let err = resolver.resolve(&source).await.unwrap_err();
75        assert!(matches!(err, SecretError::BackendError(_)));
76        assert!(err.to_string().contains("stub"));
77    }
78
79    #[tokio::test]
80    async fn rejects_wrong_source() {
81        let auth: Arc<dyn AuthProvider> = Arc::new(StubAuth);
82        let resolver = K8sResolver::new(auth);
83        let source = SecretSource::Vault {
84            mount: "secret".into(),
85            path: "data/key".into(),
86        };
87        let err = resolver.resolve(&source).await.unwrap_err();
88        assert!(matches!(err, SecretError::NoResolver(_)));
89    }
90}