talos_rust_client/
connector.rs1use crate::error::{Error, Result};
4use tonic::transport::{Certificate, Channel, ClientTlsConfig, Endpoint, Identity};
5use tracing::{debug, instrument};
6
7#[derive(Debug, Clone)]
9pub struct TalosConnector {
10 endpoint: String,
11 ca_cert: Option<Vec<u8>>,
12 client_cert: Option<Vec<u8>>,
13 client_key: Option<Vec<u8>>,
14 server_name: Option<String>,
15}
16
17impl TalosConnector {
18 pub fn new(endpoint: impl Into<String>) -> Self {
23 Self {
24 endpoint: endpoint.into(),
25 ca_cert: None,
26 client_cert: None,
27 client_key: None,
28 server_name: None,
29 }
30 }
31
32 pub fn ca_pem(mut self, pem: Vec<u8>) -> Self {
34 self.ca_cert = Some(pem);
35 self
36 }
37
38 pub fn ca_pem_file(mut self, path: impl AsRef<std::path::Path>) -> Result<Self> {
40 let pem = std::fs::read(path)?;
41 self.ca_cert = Some(pem);
42 Ok(self)
43 }
44
45 pub fn cert_pem(mut self, pem: Vec<u8>) -> Self {
47 self.client_cert = Some(pem);
48 self
49 }
50
51 pub fn cert_pem_file(mut self, path: impl AsRef<std::path::Path>) -> Result<Self> {
53 let pem = std::fs::read(path)?;
54 self.client_cert = Some(pem);
55 Ok(self)
56 }
57
58 pub fn key_pem(mut self, pem: Vec<u8>) -> Self {
60 self.client_key = Some(pem);
61 self
62 }
63
64 pub fn key_pem_file(mut self, path: impl AsRef<std::path::Path>) -> Result<Self> {
66 let pem = std::fs::read(path)?;
67 self.client_key = Some(pem);
68 Ok(self)
69 }
70
71 pub fn server_name(mut self, name: impl Into<String>) -> Self {
73 self.server_name = Some(name.into());
74 self
75 }
76
77 #[instrument(skip(self))]
79 pub async fn connect(self) -> Result<Channel> {
80 debug!("Connecting to Talos API at {}", self.endpoint);
81
82 let ca_cert = self
84 .ca_cert
85 .ok_or_else(|| Error::MissingConfig("CA certificate".to_string()))?;
86 let client_cert = self
87 .client_cert
88 .ok_or_else(|| Error::MissingConfig("Client certificate".to_string()))?;
89 let client_key = self
90 .client_key
91 .ok_or_else(|| Error::MissingConfig("Client key".to_string()))?;
92
93 let ca = Certificate::from_pem(ca_cert);
95 let identity = Identity::from_pem(client_cert, client_key);
96
97 let mut tls_config = ClientTlsConfig::new().ca_certificate(ca).identity(identity);
99
100 if let Some(domain) = self.server_name {
102 tls_config = tls_config.domain_name(domain);
103 } else {
104 if let Ok(url) = url::Url::parse(&self.endpoint) {
106 if let Some(host) = url.host_str() {
107 tls_config = tls_config.domain_name(host);
108 }
109 }
110 }
111
112 let channel = Endpoint::from_shared(self.endpoint.clone())
113 .map_err(|e| Error::Other(format!("Invalid endpoint: {e}")))?
114 .tls_config(tls_config)
115 .map_err(|e| Error::TlsConfig(format!("Failed to set TLS config: {e}")))?
116 .connect()
117 .await?;
118
119 debug!("Successfully connected to Talos API");
120 Ok(channel)
121 }
122}