Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

return 404 errors when a file is not found in S3 #48

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 14 additions & 11 deletions server/src/api/public.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,17 +25,21 @@ pub(crate) async fn public_file_download(

let p = path.into_inner();

let not_found = || {
HttpError::for_client_error(
None,
StatusCode::NOT_FOUND,
"published file not found".into(),
)
};

/*
* Load the user from the database.
*/
let u = if let Some(au) = c.db.user_by_name(&p.username).or_500()? {
au.id
} else {
return Err(HttpError::for_client_error(
None,
StatusCode::NOT_FOUND,
"published file not found".into(),
));
return Err(not_found());
};

let pf = if let Some(pf) =
Expand All @@ -44,17 +48,16 @@ pub(crate) async fn public_file_download(
{
pf
} else {
return Err(HttpError::for_client_error(
None,
StatusCode::NOT_FOUND,
"published file not found".into(),
));
return Err(not_found());
};

let mut res = Response::builder();
res = res.header(CONTENT_TYPE, "application/octet-stream");

let fr = c.file_response(pf.job, pf.file).await.or_500()?;
let Some(fr) = c.file_response(pf.job, pf.file).await.or_500()? else {
return Err(not_found());
};

info!(
log,
"published file: user {} series {} version {} name {} is in the {}",
Expand Down
8 changes: 7 additions & 1 deletion server/src/api/user.rs
Original file line number Diff line number Diff line change
Expand Up @@ -152,7 +152,13 @@ pub(crate) async fn job_output_download(
let mut res = Response::builder();
res = res.header(CONTENT_TYPE, "application/octet-stream");

let fr = c.file_response(t.id, o.id).await.or_500()?;
let Some(fr) = c.file_response(t.id, o.id).await.or_500()? else {
return Err(HttpError::for_client_error(
None,
hyper::StatusCode::NOT_FOUND,
"output file not found".into(),
));
};
info!(
log,
"job {} output {} path {:?} is in the {}", t.id, o.id, o.path, fr.info
Expand Down
9 changes: 7 additions & 2 deletions server/src/api/worker.rs
Original file line number Diff line number Diff line change
Expand Up @@ -237,10 +237,15 @@ pub(crate) async fn worker_job_input_download(
let mut res = Response::builder();
res = res.header(CONTENT_TYPE, "application/octet-stream");

let fr = c
let Some(fr) = c
.file_response(i.other_job.unwrap_or(i.job), i.id.unwrap())
.await
.or_500()?;
.or_500()?
else {
return Err(HttpError::for_internal_error(
"input file not found".to_string(),
));
};
info!(
log,
"worker {} job {} input {} name {:?} is in the {}",
Expand Down
48 changes: 41 additions & 7 deletions server/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -619,15 +619,33 @@ impl Central {
&self,
job: JobId,
file: JobFileId,
) -> Result<FileResponse> {
) -> Result<Option<FileResponse>> {
let op = self.file_path(job, file)?;

Ok(if op.is_file() {
Ok(Some(if op.is_file() {
/*
* The file exists locally.
*/
let info = format!("local file system at {:?}", op);
let f = tokio::fs::File::open(op).await?;
let f = match tokio::fs::File::open(&op).await {
Ok(f) => f,
// The the path doesn't exist. Return `None` rather than an
// error, so that the API can respond with 404 rather than 500.
//
// N.B. that this isn't a particularly common case, since we
// just checked that the path exists and will only attempt to
// open it if it does. However, the file *could* have been
// removed since when we checked if it exists, so do the right
// thing in this case anyway.
Err(e) if e.kind() == std::io::ErrorKind::NotFound => {
return Ok(None);
}
Err(e) => {
return Err(e).with_context(|| {
format!("job {job} file {file}: failed to open {op:?}")
})
}
};
let md = f.metadata().await?;
assert!(md.is_file());
let fbs = FileBytesStream::new(f);
Expand All @@ -639,20 +657,36 @@ impl Central {
*/
let key = self.file_object_key(job, file);
let info = format!("object store at {}", key);
let obj = self
let res = self
.s3
.get_object()
.bucket(&self.config.storage.bucket)
.key(key)
.send()
.await?;

.await;
let obj = match res {
// The object doesn't exist. Return `None` rather than an error
// so that the API can respond with a 404 rather than 500.
Err(aws_smithy_http::result::SdkError::ServiceError(e))
if e.err().is_no_such_key() =>
{
return Ok(None);
}
Err(e) => {
return Err(e).with_context(|| {
format!(
"job {job} file {file}: failed to get file {op:?} from S3"
)
})
}
Ok(obj) => obj,
};
FileResponse {
info,
size: obj.content_length.try_into().unwrap(),
body: Body::wrap_stream(obj.body),
}
})
}))
}

fn complete_job(
Expand Down