tuwunel_service/storage/provider/
s3.rs1use std::{sync::Arc, time::Duration};
8
9pub use object_store::{GetResult, GetResultPayload, PutPayload, PutResult};
15use object_store::{aws::AmazonS3Builder, client::ClientOptions, signer::Signer};
16use tuwunel_core::{
17 Result,
18 config::{StorageProvider, StorageProviderS3},
19 debug, debug_info, error, trace,
20 version::user_agent,
21};
22
23use super::Provider;
24
25#[tracing::instrument(name = "new", level = "info", skip_all, err)]
31pub(in super::super) fn new(
32 args: &crate::Args<'_>,
33 name: &str,
34 config: &StorageProviderS3,
35) -> Result<Option<(String, Arc<Provider>)>> {
36 if config.url.is_none() && config.bucket.is_none() {
38 debug!(?name, "s3_provider.bucket not set. This configuration will be skipped");
39 return Ok(None);
40 }
41
42 let mut builder = AmazonS3Builder::from_env().with_client_options(
43 ClientOptions::new()
44 .with_user_agent(user_agent().try_into()?)
45 .with_pool_max_idle_per_host(args.server.config.request_idle_per_host.into())
46 .with_pool_idle_timeout(Duration::from_secs(args.server.config.request_idle_timeout)),
47 );
48
49 if let Some(url) = config.url.clone() {
50 builder = builder.with_url(url);
51 }
52
53 if let Some(region) = config.region.clone() {
54 builder = builder.with_region(region);
55 }
56
57 if let Some(bucket) = config.bucket.clone() {
58 builder = builder.with_bucket_name(bucket);
59 }
60
61 if let Some(key) = config.key.clone() {
62 builder = builder.with_access_key_id(key);
63 }
64
65 if let Some(secret) = config.secret.clone() {
66 builder = builder.with_secret_access_key(secret);
67 }
68
69 if let Some(kms) = config.kms.clone() {
70 builder = builder.with_ssec_encryption(kms);
71 }
72
73 if let Some(token) = config.token.clone() {
74 builder = builder.with_token(token);
75 }
76
77 if let Some(endpoint) = config.endpoint.clone() {
78 builder = builder.with_endpoint(endpoint);
79 }
80
81 if let Some(use_bucket_key) = config.use_bucket_key {
82 builder = builder.with_bucket_key(use_bucket_key);
83 }
84
85 if let Some(use_https) = config.use_https {
86 builder = builder.with_allow_http(!use_https);
87 }
88
89 if let Some(use_signatures) = config.use_signatures {
90 builder = builder.with_skip_signature(!use_signatures);
91 }
92
93 if let Some(use_payload_signatures) = config.use_payload_signatures {
94 builder = builder.with_unsigned_payload(!use_payload_signatures);
95 }
96
97 if let Some(use_vhost_request) = config.use_vhost_request {
98 builder = builder.with_virtual_hosted_style_request(use_vhost_request);
99 }
100
101 trace!(?name, ?config, "Initializing S3...");
102
103 let client = builder
104 .build()
105 .inspect_err(|e| error!("Failed to configure S3 storage client: {e}"))?;
106
107 debug_info!(
108 credentials = ?client.credentials(),
109 "Started S3 storage client."
110 );
111
112 #[allow(clippy::allow_attributes, clippy::redundant_clone)] let signer: Arc<dyn Signer> = Arc::new(client.clone());
114
115 let provider = Provider {
116 name: name.to_owned(),
117 base_path: config.base_path.clone().map(Into::into),
118 config: StorageProvider::s3(Box::new(config.clone())),
119 startup_check: config.startup_check,
120 services: args.services.clone(),
121 provider: Box::new(client),
122 signer: Some(signer),
123 };
124
125 Ok(Some((name.to_owned(), Arc::new(provider))))
126}