Skip to content

Commit 9623c76

Browse files
committed
test cases: Add test case for wrap-file with sftp
1 parent a0c1295 commit 9623c76

File tree

8 files changed

+159
-1
lines changed

8 files changed

+159
-1
lines changed
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
foo.tar.gz: foo/foo.c foo/foo.h foo/meson.build
2+
tar -c -z -f $@ $^
363 Bytes
Binary file not shown.
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
#include "foo.h"
2+
3+
int foo_double(int x) {
4+
return 2 * x;
5+
}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
#ifndef FOO_H
2+
#define FOO_H
3+
4+
int foo_double(int x);
5+
6+
#endif
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
project('foo', 'c')
2+
3+
libfoo = library('foo', ['foo.c', 'foo.h'])
4+
5+
libfoo_dep = declare_dependency(include_directories: '.', link_with: libfoo)
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
#include "foo.h"
2+
3+
int main(int argc, char **argv) {
4+
(void)foo_double(argc);
5+
return 0;
6+
}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
project('proj', 'c')
2+
3+
libfoo_proj = subproject('foo')
4+
libfoo_dep = libfoo_proj.get_variable('libfoo_dep')
5+
6+
executable('doubler', 'doubler.c', dependencies: [libfoo_dep])

unittests/allplatformstests.py

Lines changed: 129 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,13 @@
1313
import pickle
1414
import zipfile, tarfile
1515
import sys
16+
import socket
17+
import stat
18+
import hashlib
1619
from unittest import mock, SkipTest, skipIf, skipUnless, expectedFailure
1720
from contextlib import contextmanager
1821
from glob import glob
19-
from pathlib import (PurePath, Path)
22+
from pathlib import (PurePath, Path, PureWindowsPath)
2023
import typing as T
2124

2225
import mesonbuild.mlog
@@ -5373,3 +5376,128 @@ def test_fortran_new_module_in_dep(self) -> None:
53735376
output = entry['output']
53745377

53755378
self.build(output, extra_args=['-j1'])
5379+
5380+
@skipIfNoExecutable('sshd')
5381+
@skipIfNoExecutable('sftp')
5382+
# Not tested on Windows, since there is not yet an OpenSSH server available
5383+
def test_wrap_file_sftp(self) -> None:
5384+
testdir = Path(self.unit_test_dir) / '130 wrap file sftp'
5385+
5386+
def write_file(path, contents):
5387+
with open(path, 'w', encoding='utf-8') as f:
5388+
f.write(contents)
5389+
5390+
def read_file(path):
5391+
with open(path, 'r', encoding='utf-8') as f:
5392+
return f.read()
5393+
5394+
def generate_key(path):
5395+
subprocess.run(['ssh-keygen', '-f', path, '-N', ''], check=True)
5396+
os.chmod(path, stat.S_IREAD | stat.S_IWRITE)
5397+
with open(path.with_suffix('.pub'), 'r', encoding='utf-8') as f:
5398+
return f.read()
5399+
5400+
def find_free_port():
5401+
with socket.socket() as sock:
5402+
sock.bind(('', 0))
5403+
return sock.getsockname()[1]
5404+
5405+
def generate_wrap_file(tmpdir, ssh_server_port, user_key_path, host_public_key, source_hash):
5406+
if mesonbuild.environment.detect_msys2_arch():
5407+
user_key_path = to_msys_path(user_key_path)
5408+
(tmpdir / 'subprojects').mkdir()
5409+
write_file(tmpdir / 'subprojects' / 'foo.wrap',
5410+
textwrap.dedent(f'''\
5411+
[wrap-file]
5412+
directory = foo
5413+
5414+
source_url = sftp://localhost:{ssh_server_port}/foo.tar.gz
5415+
source_filename = foo.tar.gz
5416+
source_hash = {source_hash}
5417+
source_hostkey = {host_public_key}
5418+
source_identityfile = {user_key_path}
5419+
'''))
5420+
5421+
def generate_sshd_config(sshdir, user_public_key, ssh_server_port, sftpdir):
5422+
authorized_keys_path = sshdir / 'authorized_keys'
5423+
write_file(authorized_keys_path, user_public_key)
5424+
if mesonbuild.environment.detect_msys2_arch():
5425+
authorized_keys_path = to_msys_path(authorized_keys_path)
5426+
sftpdir = to_msys_path(sftpdir)
5427+
sshd_config_path = sshdir / 'sshd_config'
5428+
write_file(sshd_config_path,
5429+
textwrap.dedent(f'''\
5430+
ListenAddress localhost:{ssh_server_port}
5431+
PidFile "{sshdir / 'sshd_pid'}"
5432+
AuthorizedKeysFile "{authorized_keys_path}"
5433+
Subsystem sftp internal-sftp -d {sftpdir}
5434+
PasswordAuthentication no
5435+
StrictModes no
5436+
'''))
5437+
return sshd_config_path
5438+
5439+
def to_msys_path(path):
5440+
# Convert A:\b\c to /a/b/c
5441+
path = PureWindowsPath(str(path))
5442+
return f'/{path.drive[:1].lower()}/{path.relative_to(path.drive + "/").as_posix()}'
5443+
5444+
def start_sshd(config_path, host_key_path):
5445+
if mesonbuild.environment.detect_msys2_arch():
5446+
config_path = to_msys_path(config_path)
5447+
host_key_path = to_msys_path(host_key_path)
5448+
sshd_path = shutil.which('sshd')
5449+
sshd = subprocess.Popen([sshd_path, '-f', config_path, '-h', host_key_path, '-D'])
5450+
try:
5451+
sshd.wait(1)
5452+
return None
5453+
except subprocess.TimeoutExpired:
5454+
# It seems sshd started successfully
5455+
return sshd
5456+
5457+
def hash_file(path):
5458+
h = hashlib.sha256()
5459+
with open(path, 'rb') as f:
5460+
h.update(f.read())
5461+
return h.hexdigest()
5462+
5463+
# In Ubuntu Bionic, the privilege separation directory /run/sshd is
5464+
# created when the service is started. Since this has never happened in
5465+
# the docker images in the CI environment, create it manually.
5466+
if is_linux() and 'CI' in os.environ:
5467+
os.makedirs('/run/sshd', mode=0o755, exist_ok=True)
5468+
builddir = Path(self.builddir)
5469+
workdir = builddir / 'work'
5470+
sftpdir = builddir / 'sftp'
5471+
sshdir = builddir / 'ssh'
5472+
sftpdir.mkdir()
5473+
sshdir.mkdir()
5474+
shutil.copytree(testdir / 'top', workdir)
5475+
shutil.copy(testdir / 'foo.tar.gz', sftpdir)
5476+
source_hash = hash_file(testdir / 'foo.tar.gz')
5477+
host_key_path = sshdir / 'host_key'
5478+
user_key_path = sshdir / 'user_key'
5479+
host_public_key = generate_key(host_key_path)
5480+
user_public_key = generate_key(user_key_path)
5481+
5482+
# As there is no reliable way to avoid the port being taken between
5483+
# us finding a free port and starting the server, support a number
5484+
# of retries.
5485+
attempts = 0
5486+
while attempts < 3:
5487+
port = find_free_port()
5488+
sshd_config_path = generate_sshd_config(sshdir, user_public_key, port, sftpdir)
5489+
sshd = start_sshd(sshd_config_path, host_key_path)
5490+
if sshd is None:
5491+
print(f'Failed to start sshd, probably due to port being taken. Trying again.')
5492+
attempts += 1
5493+
continue
5494+
generate_wrap_file(workdir, port, user_key_path, host_public_key, source_hash)
5495+
try:
5496+
self.new_builddir() # Ensure builddir is not parent or workdir
5497+
self.init(str(workdir))
5498+
self.build()
5499+
return
5500+
finally:
5501+
sshd.terminate()
5502+
sshd.wait()
5503+
raise self.fail(f'Failed to start sshd after {attempts} attempts.')

0 commit comments

Comments
 (0)