Skip to content

Commit 0691eb6

Browse files
committed
keylimectl: Implement singleton for config and clients
Signed-off-by: Anderson Toshiyuki Sasaki <[email protected]>
1 parent fb37c73 commit 0691eb6

File tree

9 files changed

+465
-262
lines changed

9 files changed

+465
-262
lines changed

keylimectl/src/client/factory.rs

Lines changed: 160 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,160 @@
1+
// SPDX-License-Identifier: Apache-2.0
2+
// Copyright 2025 Keylime Authors
3+
4+
//! Client factory for caching client instances
5+
//!
6+
//! This module provides a factory pattern for creating and caching client
7+
//! instances (VerifierClient, RegistrarClient). Each client is created once
8+
//! per command execution and reused to avoid redundant API version detection.
9+
//!
10+
//! The factory uses `std::sync::OnceLock` to cache clients. Since keylimectl
11+
//! is single-threaded (one command per execution), this provides efficient
12+
//! caching. Note that `OnceLock` can only be initialized once per process
13+
//! lifetime, which is perfect for our use case.
14+
15+
use crate::client::{registrar::RegistrarClient, verifier::VerifierClient};
16+
use crate::config::singleton::get_config;
17+
use crate::error::KeylimectlError;
18+
use std::sync::OnceLock;
19+
20+
static VERIFIER_CLIENT: OnceLock<VerifierClient> = OnceLock::new();
21+
static VERIFIER_CLIENT_OVERRIDE: OnceLock<VerifierClient> = OnceLock::new();
22+
static REGISTRAR_CLIENT: OnceLock<RegistrarClient> = OnceLock::new();
23+
24+
/// Get or create the verifier client
25+
///
26+
/// This function returns a cached verifier client if one exists, or creates
27+
/// a new one if this is the first call. The client is cached for the duration
28+
/// of the process (which is typically one command execution for keylimectl).
29+
///
30+
/// # Errors
31+
///
32+
/// Returns an error if the client cannot be created (e.g., network issues,
33+
/// invalid configuration, or API version detection failure).
34+
///
35+
/// # Examples
36+
///
37+
/// ```rust,ignore
38+
/// use keylimectl::client::factory;
39+
///
40+
/// let verifier = factory::get_verifier().await?;
41+
/// let agents = verifier.list_agents(None).await?;
42+
/// ```
43+
pub async fn get_verifier() -> Result<&'static VerifierClient, KeylimectlError>
44+
{
45+
if let Some(client) = VERIFIER_CLIENT.get() {
46+
return Ok(client);
47+
}
48+
49+
// Create and initialize the client
50+
let config = get_config();
51+
let client = VerifierClient::builder().config(config).build().await?;
52+
53+
// Try to set it (might fail if another task beat us to it, which is fine)
54+
match VERIFIER_CLIENT.set(client) {
55+
Ok(()) => Ok(VERIFIER_CLIENT.get().unwrap()),
56+
Err(client) => {
57+
// Another task already set it, return the existing one
58+
// But this shouldn't happen in single-threaded keylimectl
59+
drop(client);
60+
Ok(VERIFIER_CLIENT.get().unwrap())
61+
}
62+
}
63+
}
64+
65+
/// Get or create the verifier client with API version override
66+
///
67+
/// This function is used for push-model where we need to force API v3.0
68+
/// for verifier requests while still benefiting from version detection.
69+
///
70+
/// # Arguments
71+
///
72+
/// * `api_version` - The API version to use (e.g., "3.0")
73+
///
74+
/// # Errors
75+
///
76+
/// Returns an error if the client cannot be created.
77+
///
78+
/// # Examples
79+
///
80+
/// ```rust,ignore
81+
/// use keylimectl::client::factory;
82+
///
83+
/// // For push-model operations
84+
/// let verifier = factory::get_verifier_with_override("3.0").await?;
85+
/// ```
86+
pub async fn get_verifier_with_override(
87+
api_version: &str,
88+
) -> Result<&'static VerifierClient, KeylimectlError> {
89+
if let Some(client) = VERIFIER_CLIENT_OVERRIDE.get() {
90+
return Ok(client);
91+
}
92+
93+
// Create and initialize the client with override
94+
let config = get_config();
95+
let client = VerifierClient::builder()
96+
.config(config)
97+
.override_api_version(api_version)
98+
.build()
99+
.await?;
100+
101+
// Try to set it
102+
match VERIFIER_CLIENT_OVERRIDE.set(client) {
103+
Ok(()) => Ok(VERIFIER_CLIENT_OVERRIDE.get().unwrap()),
104+
Err(client) => {
105+
drop(client);
106+
Ok(VERIFIER_CLIENT_OVERRIDE.get().unwrap())
107+
}
108+
}
109+
}
110+
111+
/// Get or create the registrar client
112+
///
113+
/// This function returns a cached registrar client if one exists, or creates
114+
/// a new one if this is the first call. The client is cached for the duration
115+
/// of the process.
116+
///
117+
/// # Errors
118+
///
119+
/// Returns an error if the client cannot be created.
120+
///
121+
/// # Examples
122+
///
123+
/// ```rust,ignore
124+
/// use keylimectl::client::factory;
125+
///
126+
/// let registrar = factory::get_registrar().await?;
127+
/// let agent_data = registrar.get_agent("agent-uuid").await?;
128+
/// ```
129+
pub async fn get_registrar(
130+
) -> Result<&'static RegistrarClient, KeylimectlError> {
131+
if let Some(client) = REGISTRAR_CLIENT.get() {
132+
return Ok(client);
133+
}
134+
135+
// Create and initialize the client
136+
let config = get_config();
137+
let client = RegistrarClient::builder().config(config).build().await?;
138+
139+
// Try to set it
140+
match REGISTRAR_CLIENT.set(client) {
141+
Ok(()) => Ok(REGISTRAR_CLIENT.get().unwrap()),
142+
Err(client) => {
143+
drop(client);
144+
Ok(REGISTRAR_CLIENT.get().unwrap())
145+
}
146+
}
147+
}
148+
149+
#[cfg(test)]
150+
mod tests {
151+
// Note: These tests are limited because we can't easily reset OnceLock
152+
// in unit tests (it's designed to be set once per process lifetime).
153+
// Integration tests would be better for testing the factory pattern.
154+
155+
#[test]
156+
fn test_factory_exists() {
157+
// Just verify the module compiles and functions are callable
158+
// No assertions needed - compilation success is the test
159+
}
160+
}

keylimectl/src/client/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,5 +6,6 @@
66
pub mod agent;
77
pub mod base;
88
pub mod error;
9+
pub mod factory;
910
pub mod registrar;
1011
pub mod verifier;

0 commit comments

Comments
 (0)