Skip to content

Commit

Permalink
Add better APIs for making shh:ed files executable
Browse files Browse the repository at this point in the history
  • Loading branch information
MarkusPettersson98 committed Dec 13, 2024
1 parent f716bfa commit 465ff15
Showing 1 changed file with 41 additions and 12 deletions.
53 changes: 41 additions & 12 deletions test/test-manager/src/vm/provision.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use crate::{
tests::config::BOOTSTRAP_SCRIPT,
};
use anyhow::{bail, Context, Result};
use ssh2::Session;
use ssh2::{File, Session};
use std::{
io::{self, Read},
net::{IpAddr, SocketAddr, TcpStream},
Expand Down Expand Up @@ -115,27 +115,27 @@ fn blocking_ssh(
let temp_dir = Path::new(remote_temp_dir);
// Transfer a test runner
let source = local_runner_dir.join("test-runner");
ssh_send_file(&session, &source, temp_dir)
ssh_send_file_with_opts(&session, &source, temp_dir, FileOpts { executable: true })
.with_context(|| format!("Failed to send '{source:?}' to remote"))?;

// Transfer connection-checker
let source = local_runner_dir.join("connection-checker");
ssh_send_file(&session, &source, temp_dir)
ssh_send_file_with_opts(&session, &source, temp_dir, FileOpts { executable: true })
.with_context(|| format!("Failed to send '{source:?}' to remote"))?;

// Transfer app packages
let source = &local_app_manifest.app_package_path;
ssh_send_file(&session, source, temp_dir)
ssh_send_file_with_opts(&session, source, temp_dir, FileOpts { executable: true })
.with_context(|| format!("Failed to send '{source:?}' to remote"))?;

if let Some(source) = &local_app_manifest.app_package_to_upgrade_from_path {
ssh_send_file(&session, source, temp_dir)
ssh_send_file_with_opts(&session, source, temp_dir, FileOpts { executable: true })
.with_context(|| format!("Failed to send '{source:?}' to remote"))?;
} else {
log::warn!("No previous app package to upgrade from to send to remote")
}
if let Some(source) = &local_app_manifest.gui_package_path {
ssh_send_file(&session, source, temp_dir)
ssh_send_file_with_opts(&session, source, temp_dir, FileOpts { executable: true })
.with_context(|| format!("Failed to send '{source:?}' to remote"))?;
} else {
log::warn!("No UI e2e test to send to remote")
Expand Down Expand Up @@ -182,13 +182,14 @@ fn blocking_ssh(
Ok(remote_dir.to_string())
}

/// Copy a `source` file to `dest_dir` in the test runner.
/// Copy a `source` file to `dest_dir` in the test runner with opts.
///
/// Returns the aboslute path in the test runner where the file is stored.
fn ssh_send_file<P: AsRef<Path> + Copy>(
/// Returns the absolute path in the test runner where the file is stored.
fn ssh_send_file_with_opts<P: AsRef<Path> + Copy>(
session: &Session,
source: P,
dest_dir: &Path,
opts: FileOpts,
) -> Result<PathBuf> {
let dest = dest_dir.join(
source
Expand All @@ -206,18 +207,46 @@ fn ssh_send_file<P: AsRef<Path> + Copy>(
let source = std::fs::read(source)
.with_context(|| format!("Failed to open file at {}", source.as_ref().display()))?;

ssh_write(session, &dest, &source[..])?;
let mut remote_file = ssh_write_with_opts(session, &dest, &source[..], opts)?;

Ok(dest)
}

/// Analogues to [`std::fs::write`], but over ssh!
fn ssh_write<P: AsRef<Path>>(session: &Session, dest: P, mut source: impl Read) -> Result<()> {
/// Create a new file with opts at location `dest` and write the content of `source` into it.
/// Returns a handle to the newly created file.
fn ssh_write_with_opts<P: AsRef<Path>>(
session: &Session,
dest: P,
mut source: impl Read,
opts: FileOpts,
) -> Result<File> {
let sftp = session.sftp()?;
let mut remote_file = sftp.create(dest.as_ref())?;

io::copy(&mut source, &mut remote_file).context("failed to write file")?;

if opts.executable {
make_executable(&mut remote_file)?;
};

Ok(remote_file)
}

/// Extra options that may be necessary to configure for files written to the test runner VM.
/// Used in conjunction with the `ssh_*_with_opts` functions.
#[derive(Clone, Copy, Debug, Default)]
struct FileOpts {
/// If file should be executable.
executable: bool,
}

fn make_executable(file: &mut File) -> Result<()> {
// Make sure that the script is executable!
let mut file_stat = file.stat()?;
// 0x111 is the executable bit for Owner/Group/Public
let perm = file_stat.perm.map(|perm| perm | 0x111).unwrap_or(0x111);
file_stat.perm = Some(perm);
file.setstat(file_stat)?;
Ok(())
}

Expand Down

0 comments on commit 465ff15

Please sign in to comment.