Skip to content

Commit 6e60111

Browse files
authored
Ephemeral server fixes (#878)
1 parent 865cd49 commit 6e60111

File tree

3 files changed

+67
-8
lines changed

3 files changed

+67
-8
lines changed

core/src/ephemeral_server/mod.rs

Lines changed: 64 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -270,6 +270,8 @@ pub enum EphemeralExe {
270270
version: EphemeralExeVersion,
271271
/// Destination directory or the user temp directory if none set.
272272
dest_dir: Option<String>,
273+
/// How long to cache the download for. None means forever.
274+
ttl: Option<Duration>,
273275
},
274276
}
275277

@@ -309,7 +311,11 @@ impl EphemeralExe {
309311
}
310312
Ok(path)
311313
}
312-
EphemeralExe::CachedDownload { version, dest_dir } => {
314+
EphemeralExe::CachedDownload {
315+
version,
316+
dest_dir,
317+
ttl,
318+
} => {
313319
let dest_dir = dest_dir
314320
.as_ref()
315321
.map(PathBuf::from)
@@ -334,8 +340,7 @@ impl EphemeralExe {
334340
dest.display()
335341
);
336342

337-
// If it already exists, skip
338-
if dest.exists() {
343+
if dest.exists() && remove_file_past_ttl(ttl, &dest)? {
339344
return Ok(dest);
340345
}
341346

@@ -379,6 +384,7 @@ impl EphemeralExe {
379384
&info.archive_url,
380385
Path::new(&info.file_to_extract),
381386
&dest,
387+
false,
382388
)
383389
.await?
384390
{
@@ -433,7 +439,7 @@ fn get_free_port(bind_ip: &str) -> io::Result<u16> {
433439
let (socket, _addr) = listen.accept()?;
434440

435441
// Explicitly drop the socket to close the connection from the listening side first
436-
std::mem::drop(socket);
442+
drop(socket);
437443
}
438444

439445
Ok(addr.port())
@@ -447,6 +453,7 @@ async fn lazy_download_exe(
447453
uri: &str,
448454
file_to_extract: &Path,
449455
dest: &Path,
456+
already_tried_cleaning_old: bool,
450457
) -> anyhow::Result<bool> {
451458
// If it already exists, do not extract
452459
if dest.exists() {
@@ -474,7 +481,16 @@ async fn lazy_download_exe(
474481
// This match only gets Ok if the file was downloaded and extracted to the
475482
// temporary path
476483
match file {
477-
Err(err) if err.kind() == std::io::ErrorKind::AlreadyExists => {
484+
Err(err) if err.kind() == io::ErrorKind::AlreadyExists => {
485+
// If the download lock file exists but is old, delete it and try again, since it may
486+
// have been left by an abandoned process.
487+
if !already_tried_cleaning_old
488+
&& temp_dest.metadata()?.modified()?.elapsed()?.as_secs() > 90
489+
{
490+
std::fs::remove_file(temp_dest)?;
491+
return Box::pin(lazy_download_exe(client, uri, file_to_extract, dest, true)).await;
492+
}
493+
478494
// Since it already exists, we'll try once a second for 20 seconds
479495
// to wait for it to be done, then return false so the caller can
480496
// try again.
@@ -530,7 +546,7 @@ async fn download_and_extract(
530546
// We have to map the error type to an io error
531547
let stream = resp
532548
.bytes_stream()
533-
.map(|item| item.map_err(|err| std::io::Error::new(std::io::ErrorKind::Other, err)));
549+
.map(|item| item.map_err(|err| io::Error::new(io::ErrorKind::Other, err)));
534550

535551
// Since our tar/zip impls use sync IO, we have to create a bridge and run
536552
// in a blocking closure.
@@ -574,12 +590,36 @@ async fn download_and_extract(
574590
.await?
575591
}
576592

593+
/// Remove the file if it's older than the TTL. Returns true if the current file can be re-used,
594+
/// returns false if it was removed or should otherwise be re-downloaded.
595+
fn remove_file_past_ttl(ttl: &Option<Duration>, dest: &PathBuf) -> Result<bool, anyhow::Error> {
596+
match ttl {
597+
None => return Ok(true),
598+
Some(ttl) => {
599+
if let Ok(mtime) = dest.metadata().and_then(|d| d.modified()) {
600+
if mtime.elapsed().unwrap_or_default().lt(ttl) {
601+
return Ok(true);
602+
} else {
603+
// Remove so we can re-download
604+
std::fs::remove_file(dest)?;
605+
}
606+
}
607+
// If we couldn't read the mtime something weird is probably up, so
608+
// re-download
609+
}
610+
}
611+
Ok(false)
612+
}
613+
577614
#[cfg(test)]
578615
mod tests {
579-
use super::get_free_port;
616+
use super::{get_free_port, remove_file_past_ttl};
580617
use std::{
581618
collections::HashSet,
619+
env::temp_dir,
620+
fs::File,
582621
net::{TcpListener, TcpStream},
622+
time::{Duration, SystemTime},
583623
};
584624

585625
#[test]
@@ -609,6 +649,22 @@ mod tests {
609649
}
610650
}
611651

652+
#[tokio::test]
653+
async fn respects_file_ttl() {
654+
let rand_fname = format!("{}", rand::random::<u64>());
655+
let temp_dir = temp_dir();
656+
657+
let dest_file_path = temp_dir.join(format!("core-test-{}", &rand_fname));
658+
let dest_file = File::create(&dest_file_path).unwrap();
659+
let set_time_to = SystemTime::now() - Duration::from_secs(100);
660+
dest_file.set_modified(set_time_to).unwrap();
661+
662+
remove_file_past_ttl(&Some(Duration::from_secs(60)), &dest_file_path).unwrap();
663+
664+
// file should be gone
665+
assert!(!dest_file_path.exists());
666+
}
667+
612668
fn try_listen_and_dial_on(host: &str, port: u16) -> std::io::Result<()> {
613669
let listener = TcpListener::bind((host, port))?;
614670
let _stream = TcpStream::connect((host, port))?;
@@ -617,7 +673,7 @@ mod tests {
617673
let (socket, _addr) = listener.accept()?;
618674

619675
// Explicitly drop the socket to close the connection from the listening side first
620-
std::mem::drop(socket);
676+
drop(socket);
621677

622678
Ok(())
623679
}

test-utils/src/lib.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -687,6 +687,8 @@ pub fn default_cached_download() -> EphemeralExe {
687687
sdk_version: "0.1.0".to_string(),
688688
},
689689
dest_dir: None,
690+
// 15 days
691+
ttl: Some(Duration::from_secs(60 * 60 * 24 * 15)),
690692
}
691693
}
692694

tests/integ_tests/ephemeral_server_tests.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,7 @@ fn fixed_cached_download(version: &str) -> EphemeralExe {
129129
EphemeralExe::CachedDownload {
130130
version: EphemeralExeVersion::Fixed(version.to_string()),
131131
dest_dir: None,
132+
ttl: None,
132133
}
133134
}
134135

0 commit comments

Comments
 (0)