use super::*;
impl RdsService {
pub(super) async fn describe_db_log_files(
&self,
request: &AwsRequest,
) -> Result<AwsResponse, AwsServiceError> {
let db_instance_identifier = required_query_param(request, "DBInstanceIdentifier")?;
let filename_contains = optional_query_param(request, "FilenameContains");
let file_last_written =
optional_query_param(request, "FileLastWritten").and_then(|s| s.parse::<i64>().ok());
let file_size =
optional_query_param(request, "FileSize").and_then(|s| s.parse::<i64>().ok());
let engine = {
let accounts = self.state.read();
let state = accounts
.get(&request.account_id)
.ok_or_else(|| db_instance_not_found(&db_instance_identifier))?;
let instance = state
.instances
.get(&db_instance_identifier)
.ok_or_else(|| db_instance_not_found(&db_instance_identifier))?;
instance.engine.clone()
};
let now_millis = Utc::now().timestamp_millis();
let candidates: Vec<(String, i64, i64)> = match engine.as_str() {
"mysql" | "mariadb" => vec![
("error/mysql-error.log".to_string(), now_millis, 1024),
("slowquery/mysql-slowquery.log".to_string(), now_millis, 512),
],
_ => vec![
("error/postgres.log".to_string(), now_millis, 1024),
("trace/postgres-trace.log".to_string(), now_millis, 512),
],
};
let filtered: Vec<(String, i64, i64)> = candidates
.into_iter()
.filter(|(name, written, size)| {
if let Some(needle) = &filename_contains {
if !name.contains(needle) {
return false;
}
}
if let Some(min_written) = file_last_written {
if *written / 1000 <= min_written {
return false;
}
}
if let Some(min_size) = file_size {
if *size < min_size {
return false;
}
}
true
})
.collect();
let details: String = filtered
.iter()
.map(|(name, written, size)| {
format!(
"<DescribeDBLogFilesDetails>\
<LogFileName>{}</LogFileName>\
<LastWritten>{}</LastWritten>\
<Size>{}</Size>\
</DescribeDBLogFilesDetails>",
xml_escape(name),
written,
size,
)
})
.collect();
Ok(AwsResponse::xml(
StatusCode::OK,
query_response_xml(
"DescribeDBLogFiles",
RDS_NS,
&format!("<DescribeDBLogFiles>{details}</DescribeDBLogFiles>"),
&request.request_id,
),
))
}
pub(super) async fn download_db_log_file_portion(
&self,
request: &AwsRequest,
) -> Result<AwsResponse, AwsServiceError> {
let db_instance_identifier = required_query_param(request, "DBInstanceIdentifier")?;
let log_file_name = required_query_param(request, "LogFileName")?;
let _marker = optional_query_param(request, "Marker").unwrap_or_else(|| "0".to_string());
let _number_of_lines = optional_query_param(request, "NumberOfLines")
.and_then(|s| s.parse::<i64>().ok())
.unwrap_or(0);
let engine = {
let accounts = self.state.read();
let state = accounts
.get(&request.account_id)
.ok_or_else(|| db_instance_not_found(&db_instance_identifier))?;
let instance = state
.instances
.get(&db_instance_identifier)
.ok_or_else(|| db_instance_not_found(&db_instance_identifier))?;
instance.engine.clone()
};
let known_synthetic = matches!(
(engine.as_str(), log_file_name.as_str()),
("mysql" | "mariadb", "error/mysql-error.log")
| ("mysql" | "mariadb", "slowquery/mysql-slowquery.log")
| (_, "error/postgres.log")
| (_, "trace/postgres-trace.log")
);
let container_path = map_log_file_to_container_path(&engine, &log_file_name);
let log_data = if let Some(runtime) = self.runtime.as_ref() {
match runtime
.read_log_file(&db_instance_identifier, &container_path)
.await
{
Ok(bytes) => Some(bytes),
Err(RuntimeError::Unavailable) => None,
Err(RuntimeError::ContainerStartFailed(_)) if known_synthetic => Some(Vec::new()),
Err(RuntimeError::ContainerStartFailed(message)) => {
return Err(AwsServiceError::aws_error(
StatusCode::NOT_FOUND,
"DBLogFileNotFoundFault",
format!("DBLogFile {log_file_name} not found: {message}"),
));
}
}
} else if known_synthetic {
Some(Vec::new())
} else {
None
};
let log_data = match log_data {
Some(bytes) => bytes,
None => {
return Err(AwsServiceError::aws_error(
StatusCode::NOT_FOUND,
"DBLogFileNotFoundFault",
format!("DBLogFile {log_file_name} not found"),
))
}
};
let payload = String::from_utf8_lossy(&log_data).into_owned();
let total_bytes = payload.len();
Ok(AwsResponse::xml(
StatusCode::OK,
query_response_xml(
"DownloadDBLogFilePortion",
RDS_NS,
&format!(
"<LogFileData>{}</LogFileData>\
<Marker>{}</Marker>\
<AdditionalDataPending>false</AdditionalDataPending>",
xml_escape(&payload),
total_bytes,
),
&request.request_id,
),
))
}
}