-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy paths3.rs
138 lines (117 loc) · 3.83 KB
/
s3.rs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
use core::fmt::{self, Display};
use std::io::{self, Read, Seek, SeekFrom, Write};
use anyhow::Context;
use aws_sdk_s3::primitives::ByteStream;
use log::info;
use tempfile::tempfile;
use super::BundleLogsUploader;
/// Re-export the `aws-config` crate as a module so that the user
/// does not have to depend on the `aws-config` crate directly
pub mod aws_config {
pub use ::aws_config::*;
}
/// A logs uploader that uploads the logs to an S3 bucket and an optional prefix.
#[derive(Debug, Clone)]
pub struct S3LogsUploader {
config: Option<aws_config::SdkConfig>,
logs_upload_bucket: String,
logs_upload_prefix: Option<String>,
}
impl S3LogsUploader {
/// Creates a new `S3LogsUploader` instance
///
/// # Arguments
/// - `config` - The optional AWS SDK configuration
/// - `logs_upload_bucket` - The name of the S3 bucket to upload the logs to
/// - `logs_upload_prefix` - The optional prefix to use when uploading the logs
pub const fn new(
config: Option<aws_config::SdkConfig>,
logs_upload_bucket: String,
logs_upload_prefix: Option<String>,
) -> Self {
Self {
config,
logs_upload_bucket,
logs_upload_prefix,
}
}
}
impl BundleLogsUploader for S3LogsUploader {
async fn upload_logs<R>(
&mut self,
mut read: R,
id: Option<&str>,
name: &str,
) -> anyhow::Result<()>
where
R: Read + Seek,
{
if let Some(id) = id {
info!(
"About to upload logs `{name}.log.zip` for ID `{id}` to S3 bucket `{}`...",
BucketWithPrefix::new(&self.logs_upload_bucket, self.logs_upload_prefix.as_deref())
);
} else {
info!(
"About to uploads logs `{name}.log.zip` to S3 bucket `{}`...",
BucketWithPrefix::new(&self.logs_upload_bucket, self.logs_upload_prefix.as_deref())
);
}
let config = if let Some(config) = self.config.as_ref() {
config.clone()
} else {
aws_config::load_from_env().await
};
let client = aws_sdk_s3::Client::new(&config);
let key = self
.logs_upload_prefix
.as_deref()
.map(|prefix| format!("{prefix}/{name}.log.zip"))
.unwrap_or(format!("{name}.log.zip"));
read.seek(io::SeekFrom::Start(0))
.context("Saving the bundle log failed")?;
let mut temp_file = tempfile().context("Uploading the bundle log failed")?;
std::io::copy(&mut read, &mut temp_file).context("Uploading the bundle log failed")?;
temp_file
.flush()
.context("Uploading the bundle log failed")?;
temp_file
.seek(SeekFrom::Start(0))
.context("Uploading the bundle log failed")?;
client
.put_object()
.bucket(&self.logs_upload_bucket)
.key(key)
.body(
ByteStream::read_from()
.file(temp_file.into())
.build()
.await
.context("Uploading the bundle log failed")?,
)
.send()
.await
.context("Uploading the bundle log failed")?;
info!("Logs `{name}.log.zip` uploaded");
Ok(())
}
}
#[derive(Debug)]
struct BucketWithPrefix<'a> {
bucket: &'a str,
prefix: Option<&'a str>,
}
impl<'a> BucketWithPrefix<'a> {
const fn new(bucket: &'a str, prefix: Option<&'a str>) -> Self {
Self { bucket, prefix }
}
}
impl Display for BucketWithPrefix<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if let Some(prefix) = self.prefix {
write!(f, "{}/{}", self.bucket, prefix)
} else {
write!(f, "{}", self.bucket)
}
}
}