1#![deny(missing_docs)]
2use async_trait::async_trait;
9use layer0::secret::SecretSource;
10use neuron_auth::AuthProvider;
11use neuron_secret::{SecretError, SecretLease, SecretResolver};
12use std::sync::Arc;
13
14pub struct K8sResolver {
16 _auth: Arc<dyn AuthProvider>,
17}
18
19impl K8sResolver {
20 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}