Skip to content

Commit 4f19fd8

Browse files
authored
Merge pull request #5173 from bartoldeman/bwrap-improved
Improve `--bwrap` to work with r/o filesystems (overlayfs) and `--subdir-{modules,software}`
2 parents 75fd22a + cd0267c commit 4f19fd8

2 files changed

Lines changed: 60 additions & 8 deletions

File tree

.github/workflows/end2end_bwrap.yml

Lines changed: 33 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ on: [push, pull_request]
55
jobs:
66
end2end:
77
name: End-to-end test (runner VM)
8-
runs-on: ubuntu-24.04
8+
runs-on: ubuntu-26.04
99

1010
steps:
1111
- name: Check out the repo
@@ -14,7 +14,7 @@ jobs:
1414
- name: Install system packages
1515
run: sudo apt-get install -y lmod bubblewrap
1616

17-
- name: Allow unprivileged user namespaces (Ubuntu 24.04)
17+
- name: Allow unprivileged user namespaces (Ubuntu 26.04)
1818
run: |
1919
sudo sysctl -w kernel.apparmor_restrict_unprivileged_userns=0
2020
sudo sysctl -w kernel.apparmor_restrict_unprivileged_unconfined=0
@@ -77,3 +77,34 @@ jobs:
7777
7878
grep 'no UnZip module dir found: OK' /tmp/out-UnZip-mod && grep 'no bzip2 module dir found: OK' /tmp/out-bzip2-mod && grep 'UnZip software dir in .* is empty' /tmp/out-UnZip-sw-dir && grep 'bzip2 software dir in .* is empty' /tmp/out-bzip2-sw-dir && ls -l /tmp/bwrap-installpath/modules/all/{bzip2/1.0.8.lua,UnZip/6.0.lua} && ls -l /tmp/bwrap-installpath/software/{UnZip,bzip2}/*/bin/* && ls -l /tmp/bwrap-installpath/software/{UnZip,bzip2}/*/easybuild/*log*
7979
EOF
80+
81+
- name: End-to-end test of installing UnZip with EasyBuild in bwrap namespace with overlay
82+
shell: bash
83+
run: |
84+
bash -l <<EOF
85+
cat /etc/os-release
86+
# bwrap version needs to be >=0.10 for --overlay and --overlay-src support
87+
bwrap --version
88+
89+
source /tmp/eb_env
90+
91+
# remove previously created install directories
92+
rm -r $HOME/.local/easybuild/software/* /tmp/bwrap-installpath
93+
94+
# make install directory read-only to force using overlay
95+
chmod -w $HOME/.local/easybuild/software
96+
97+
# test installation of UnZip + bzip2 dependency
98+
eb UnZip-6.0.eb --trace --robot --experimental --bwrap --bwrap-installpath /tmp/bwrap-installpath --filter-deps=binutils
99+
100+
# no module files should be created in configured install path (default: $HOME/.local/easybuild),
101+
# not for UnZip, nor for bzip2 dependency
102+
(test -d $HOME/.local/easybuild/modules/all/UnZip || echo 'no UnZip module dir found: OK') > /tmp/out-UnZip-mod
103+
(test -d $HOME/.local/easybuild/modules/all/bzip2 || echo 'no bzip2 module dir found: OK') > /tmp/out-bzip2-mod
104+
105+
# software subdirectories in configured install path should be empty
106+
(test -e $HOME/.local/easybuild/software/UnZip/6.0/* || echo 'UnZip software dir in $HOME/.local/easybuild is empty: OK') > /tmp/out-UnZip-sw-dir
107+
(test -e $HOME/.local/easybuild/software/bzip2/1.0.8/* || echo 'bzip2 software dir in $HOME/.local/easybuild is empty: OK') > /tmp/out-bzip2-sw-dir
108+
109+
grep 'no UnZip module dir found: OK' /tmp/out-UnZip-mod && grep 'no bzip2 module dir found: OK' /tmp/out-bzip2-mod && grep 'UnZip software dir in .* is empty' /tmp/out-UnZip-sw-dir && grep 'bzip2 software dir in .* is empty' /tmp/out-bzip2-sw-dir && ls -l /tmp/bwrap-installpath/modules/all/{bzip2/1.0.8.lua,UnZip/6.0.lua} && ls -l /tmp/bwrap-installpath/software/{UnZip,bzip2}/*/bin/* && ls -l /tmp/bwrap-installpath/software/{UnZip,bzip2}/*/easybuild/*log*
110+
EOF

easybuild/tools/bwrap.py

Lines changed: 27 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@
3535

3636
from easybuild.base import fancylogger
3737
from easybuild.tools.build_log import EasyBuildError, print_msg
38-
from easybuild.tools.config import install_path
38+
from easybuild.tools.config import install_path, ConfigurationVariables
3939
from easybuild.tools.filetools import mkdir, write_file
4040
from easybuild.tools.utilities import trace_msg
4141

@@ -47,6 +47,8 @@
4747
'bwrap_cmd': [],
4848
'bwrap_eb_options': [],
4949
'bwrap_installpath': '',
50+
'bwrap_installpath_software': '',
51+
'bwrap_installpath_modules': '',
5052
'installpath_modules': '',
5153
'installpath_software': '',
5254
'modules_to_install': set(),
@@ -107,23 +109,42 @@ def prepare_bwrap(bwrap_installpath):
107109

108110
set_bwrap_info('installpath_modules', install_path(typ='modules'))
109111

110-
bwrap_modules_installpath = os.path.join(bwrap_installpath, 'modules')
112+
variables = ConfigurationVariables()
113+
bwrap_installpath_software = os.path.join(bwrap_installpath, variables['subdir_software'])
114+
bwrap_installpath_modules = os.path.join(bwrap_installpath, variables['subdir_modules'])
115+
set_bwrap_info('bwrap_installpath_software', bwrap_installpath_software)
116+
set_bwrap_info('bwrap_installpath_modules', bwrap_installpath_modules)
111117

112118
bwrap_cmd = ['bwrap', '--dev-bind', '/', '/']
113119

114120
# bind mount all software directories
115121
for mod in sorted(get_bwrap_info('modules_to_install')):
116122
installdir = os.path.join(os.path.realpath(installpath_software), mod)
117-
bwrap_installdir = os.path.join(bwrap_installpath, 'software', mod)
118-
mkdir(installdir, parents=True)
123+
bwrap_installdir = os.path.join(bwrap_installpath_software, mod)
124+
use_overlayfs = False
125+
try:
126+
mkdir(installdir, parents=True)
127+
except EasyBuildError:
128+
# if we can't create the external installation directory, try to use overlayfs
129+
use_overlayfs = True
119130
mkdir(bwrap_installdir, parents=True)
120-
bwrap_cmd.extend(['--bind', bwrap_installdir, installdir])
131+
if use_overlayfs:
132+
bwrap_workdir = os.path.join(bwrap_installpath, 'workdir', mod)
133+
mkdir(bwrap_workdir, parents=True)
134+
# go up the tree until we find a directory that exists
135+
while not os.path.exists(installdir):
136+
installdir = os.path.dirname(installdir)
137+
bwrap_installdir = os.path.dirname(bwrap_installdir)
138+
opts = ['--overlay-src', installdir, '--overlay', bwrap_installdir, bwrap_workdir, installdir]
139+
else:
140+
opts = ['--bind', bwrap_installdir, installdir]
141+
bwrap_cmd.extend(opts)
121142

122143
set_bwrap_info('bwrap_cmd', bwrap_cmd)
123144
bwrap_cmd_str = ' '.join(bwrap_cmd)
124145

125146
# disable `--bwrap` to prepare for a real installation (in bwrap namespace)
126-
bwrap_eb_options = ['--disable-bwrap', f'--installpath-modules={bwrap_modules_installpath}']
147+
bwrap_eb_options = ['--disable-bwrap', f'--installpath-modules={bwrap_installpath_modules}']
127148
set_bwrap_info('bwrap_eb_options', bwrap_eb_options)
128149

129150
_log.info(f'Info needed for bwrap: {_bwrap_info}')

0 commit comments

Comments
 (0)