Skip to content

Commit 3085bea

Browse files
Add better APIs for making shh:ed files executable
1 parent 200df04 commit 3085bea

File tree

1 file changed

+41
-12
lines changed

1 file changed

+41
-12
lines changed

test/test-manager/src/vm/provision.rs

+41-12
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ use crate::{
44
tests::config::BOOTSTRAP_SCRIPT,
55
};
66
use anyhow::{bail, Context, Result};
7-
use ssh2::Session;
7+
use ssh2::{File, Session};
88
use std::{
99
io::{self, Read},
1010
net::{IpAddr, SocketAddr, TcpStream},
@@ -115,27 +115,27 @@ fn blocking_ssh(
115115
let temp_dir = Path::new(remote_temp_dir);
116116
// Transfer a test runner
117117
let source = local_runner_dir.join("test-runner");
118-
ssh_send_file(&session, &source, temp_dir)
118+
ssh_send_file_with_opts(&session, &source, temp_dir, FileOpts { executable: true })
119119
.with_context(|| format!("Failed to send '{source:?}' to remote"))?;
120120

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

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

131131
if let Some(source) = &local_app_manifest.app_package_to_upgrade_from_path {
132-
ssh_send_file(&session, source, temp_dir)
132+
ssh_send_file_with_opts(&session, source, temp_dir, FileOpts { executable: true })
133133
.with_context(|| format!("Failed to send '{source:?}' to remote"))?;
134134
} else {
135135
log::warn!("No previous app package to upgrade from to send to remote")
136136
}
137137
if let Some(source) = &local_app_manifest.gui_package_path {
138-
ssh_send_file(&session, source, temp_dir)
138+
ssh_send_file_with_opts(&session, source, temp_dir, FileOpts { executable: true })
139139
.with_context(|| format!("Failed to send '{source:?}' to remote"))?;
140140
} else {
141141
log::warn!("No UI e2e test to send to remote")
@@ -182,13 +182,14 @@ fn blocking_ssh(
182182
Ok(remote_dir.to_string())
183183
}
184184

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

209-
ssh_write(session, &dest, &source[..])?;
210+
ssh_write_with_opts(session, &dest, &source[..], opts)?;
210211

211212
Ok(dest)
212213
}
213214

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

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

228+
if opts.executable {
229+
make_executable(&mut remote_file)?;
230+
};
231+
232+
Ok(remote_file)
233+
}
234+
235+
/// Extra options that may be necessary to configure for files written to the test runner VM.
236+
/// Used in conjunction with the `ssh_*_with_opts` functions.
237+
#[derive(Clone, Copy, Debug, Default)]
238+
struct FileOpts {
239+
/// If file should be executable.
240+
executable: bool,
241+
}
242+
243+
fn make_executable(file: &mut File) -> Result<()> {
244+
// Make sure that the script is executable!
245+
let mut file_stat = file.stat()?;
246+
// 0x111 is the executable bit for Owner/Group/Public
247+
let perm = file_stat.perm.map(|perm| perm | 0x111).unwrap_or(0x111);
248+
file_stat.perm = Some(perm);
249+
file.setstat(file_stat)?;
221250
Ok(())
222251
}
223252

0 commit comments

Comments
 (0)