Commit | Line | Data |
---|---|---|
b07014f5 | 1 | # GNU Guix --- Functional package management for GNU |
2520059b | 2 | # Copyright © 2018, 2019, 2020 Ludovic Courtès <ludo@gnu.org> |
4184998c | 3 | # Copyright © 2020 Eric Bavier <bavier@posteo.net> |
b07014f5 LC |
4 | # |
5 | # This file is part of GNU Guix. | |
6 | # | |
7 | # GNU Guix is free software; you can redistribute it and/or modify it | |
8 | # under the terms of the GNU General Public License as published by | |
9 | # the Free Software Foundation; either version 3 of the License, or (at | |
10 | # your option) any later version. | |
11 | # | |
12 | # GNU Guix is distributed in the hope that it will be useful, but | |
13 | # WITHOUT ANY WARRANTY; without even the implied warranty of | |
14 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
15 | # GNU General Public License for more details. | |
16 | # | |
17 | # You should have received a copy of the GNU General Public License | |
18 | # along with GNU Guix. If not, see <http://www.gnu.org/licenses/>. | |
19 | ||
20 | # | |
21 | # Test the 'guix pack --relocatable' using the external store, if any. | |
22 | # | |
23 | ||
24 | guix pack --version | |
25 | ||
26 | # 'guix pack --relocatable' requires a C compiler and libc.a, which our | |
27 | # bootstrap binaries don't provide. To make the test relatively inexpensive, | |
28 | # run it on the user's global store if possible, on the grounds that binaries | |
29 | # may already be there or can be built or downloaded inexpensively. | |
30 | ||
baab87ac | 31 | storedir="`guile -c '(use-modules (guix config))(display %storedir)'`" |
b07014f5 | 32 | localstatedir="`guile -c '(use-modules (guix config))(display %localstatedir)'`" |
baab87ac | 33 | NIX_STORE_DIR="$storedir" |
b07014f5 LC |
34 | GUIX_DAEMON_SOCKET="$localstatedir/guix/daemon-socket/socket" |
35 | export NIX_STORE_DIR GUIX_DAEMON_SOCKET | |
36 | ||
37 | if ! guile -c '(use-modules (guix)) (exit (false-if-exception (open-connection)))' | |
38 | then | |
39 | exit 77 | |
40 | fi | |
41 | ||
c6c0d5a2 LC |
42 | # Attempt to run the given command in a namespace where the store is |
43 | # invisible. This makes sure the presence of the store does not hide | |
44 | # problems. | |
45 | run_without_store () | |
46 | { | |
47 | if unshare -r true # Are user namespaces supported? | |
48 | then | |
49 | # Run that relocatable executable in a user namespace where we "erase" | |
50 | # the store by mounting an empty file system on top of it. That way, | |
51 | # we exercise the wrapper code that creates the user namespace and | |
52 | # bind-mounts the store. | |
53 | unshare -mrf sh -c 'mount -t tmpfs -o ro none "$NIX_STORE_DIR"; '"$*" | |
54 | else | |
55 | # Run the relocatable program in the current namespaces. This is a | |
56 | # weak test because we're going to access store items from the host | |
57 | # store. | |
a67b8247 | 58 | sh -c "$*" |
c6c0d5a2 LC |
59 | fi |
60 | } | |
b07014f5 | 61 | |
bfe82fe2 LC |
62 | # Wait for the given file to show up. Error out if it doesn't show up in a |
63 | # timely fashion. | |
64 | wait_for_file () | |
65 | { | |
66 | i=0 | |
67 | while ! test -f "$1" && test $i -lt 20 | |
68 | do | |
69 | sleep 0.3 | |
70 | i=`expr $i + 1` | |
71 | done | |
72 | test -f "$1" | |
73 | } | |
74 | ||
b07014f5 LC |
75 | test_directory="`mktemp -d`" |
76 | export test_directory | |
77 | trap 'chmod -Rf +w "$test_directory"; rm -rf "$test_directory"' EXIT | |
78 | ||
c6c0d5a2 | 79 | if unshare -r true |
8deb65c3 | 80 | then |
c6c0d5a2 LC |
81 | # Test the 'userns' execution engine. |
82 | tarball="`guix pack -R -S /Bin=bin sed`" | |
83 | (cd "$test_directory"; tar xvf "$tarball") | |
84 | ||
85 | run_without_store "$test_directory/Bin/sed" --version > "$test_directory/output" | |
86 | grep 'GNU sed' "$test_directory/output" | |
87 | ||
88 | # Same with an explicit engine. | |
89 | run_without_store GUIX_EXECUTION_ENGINE="userns" \ | |
90 | "$test_directory/Bin/sed" --version > "$test_directory/output" | |
91 | grep 'GNU sed' "$test_directory/output" | |
2520059b LC |
92 | |
93 | # Check whether the exit code is preserved. | |
d8934360 | 94 | ! run_without_store "$test_directory/Bin/sed" --does-not-exist |
c6c0d5a2 LC |
95 | |
96 | chmod -Rf +w "$test_directory"; rm -rf "$test_directory"/* | |
8deb65c3 | 97 | else |
c6c0d5a2 | 98 | echo "'userns' execution tests skipped" >&2 |
8deb65c3 | 99 | fi |
b908fcd8 | 100 | |
fde2aec3 LC |
101 | case "`uname -m`" in |
102 | x86_64|i?86) | |
103 | # Try '-RR' and PRoot. | |
104 | tarball="`guix pack -RR -S /Bin=bin sed`" | |
105 | tar tvf "$tarball" | grep /bin/proot | |
c088aa29 | 106 | (cd "$test_directory"; tar xf "$tarball") |
c6c0d5a2 | 107 | run_without_store GUIX_EXECUTION_ENGINE="proot" \ |
fde2aec3 LC |
108 | "$test_directory/Bin/sed" --version > "$test_directory/output" |
109 | grep 'GNU sed' "$test_directory/output" | |
64562321 LC |
110 | |
111 | # Now with fakechroot. | |
c6c0d5a2 | 112 | run_without_store GUIX_EXECUTION_ENGINE="fakechroot" \ |
64562321 LC |
113 | "$test_directory/Bin/sed" --version > "$test_directory/output" |
114 | grep 'GNU sed' "$test_directory/output" | |
4184998c | 115 | unset GUIX_EXECUTION_ENGINE |
64562321 | 116 | |
fde2aec3 | 117 | chmod -Rf +w "$test_directory"; rm -rf "$test_directory"/* |
c088aa29 LC |
118 | |
119 | if unshare -r true | |
120 | then | |
121 | # Check whether the store contains everything it should. Check | |
122 | # once when erasing $STORE_PARENT ("/gnu") and once when erasing | |
123 | # $NIX_STORE_DIR ("/gnu/store"). | |
124 | tarball="`guix pack -RR -S /bin=bin bash-minimal`" | |
125 | (cd "$test_directory"; tar xf "$tarball") | |
126 | ||
127 | STORE_PARENT="`dirname $NIX_STORE_DIR`" | |
128 | export STORE_PARENT | |
129 | ||
130 | for engine in userns proot fakechroot | |
131 | do | |
132 | for i in $(guix gc -R $(guix build bash-minimal | grep -v -e '-doc$')) | |
133 | do | |
134 | unshare -mrf sh -c "mount -t tmpfs none \"$NIX_STORE_DIR\"; GUIX_EXECUTION_ENGINE=$engine $test_directory/bin/sh -c 'echo $NIX_STORE_DIR/*'" | grep $(basename $i) | |
135 | unshare -mrf sh -c "mount -t tmpfs none \"$STORE_PARENT\"; GUIX_EXECUTION_ENGINE=$engine $test_directory/bin/sh -c 'echo $NIX_STORE_DIR/*'" | grep $(basename $i) | |
136 | done | |
137 | done | |
138 | ||
139 | chmod -Rf +w "$test_directory"; rm -rf "$test_directory"/* | |
140 | fi | |
fde2aec3 LC |
141 | ;; |
142 | *) | |
c6c0d5a2 | 143 | echo "skipping PRoot and Fakechroot tests" >&2 |
fde2aec3 LC |
144 | ;; |
145 | esac | |
146 | ||
bfe82fe2 LC |
147 | if unshare -r true |
148 | then | |
149 | # Check what happens if the wrapped binary forks and leaves child | |
150 | # processes behind, like a daemon. The root file system should remain | |
151 | # available to those child processes. See <https://bugs.gnu.org/44261>. | |
152 | cat > "$test_directory/manifest.scm" <<EOF | |
153 | (use-modules (guix)) | |
154 | ||
155 | (define daemon | |
156 | (program-file "daemon" | |
157 | #~(begin | |
158 | (use-modules (ice-9 match) | |
159 | (ice-9 ftw)) | |
160 | ||
161 | (call-with-output-file "parent-store" | |
162 | (lambda (port) | |
163 | (write (scandir (ungexp (%store-prefix))) | |
164 | port))) | |
165 | ||
166 | (match (primitive-fork) | |
167 | (0 (sigaction SIGHUP (const #t)) | |
168 | (call-with-output-file "pid" | |
169 | (lambda (port) | |
170 | (display (getpid) port))) | |
171 | (pause) | |
172 | (call-with-output-file "child-store" | |
173 | (lambda (port) | |
174 | (write (scandir (ungexp (%store-prefix))) | |
175 | port)))) | |
176 | (_ #t))))) | |
177 | ||
178 | (define package | |
179 | (computed-file "package" | |
180 | #~(let ((out (ungexp output))) | |
181 | (mkdir out) | |
182 | (mkdir (string-append out "/bin")) | |
183 | (symlink (ungexp daemon) | |
184 | (string-append out "/bin/daemon"))))) | |
185 | ||
186 | (manifest (list (manifest-entry | |
187 | (name "daemon") | |
188 | (version "0") | |
189 | (item package)))) | |
190 | EOF | |
191 | ||
192 | tarball="$(guix pack -S /bin=bin -R -m "$test_directory/manifest.scm")" | |
193 | (cd "$test_directory"; tar xf "$tarball") | |
194 | ||
195 | # Run '/bin/daemon', which forks, then wait for the child, send it SIGHUP | |
196 | # so that it dumps its view of the store, and make sure the child and | |
197 | # parent both see the same store contents. | |
198 | (cd "$test_directory"; run_without_store ./bin/daemon) | |
199 | wait_for_file "$test_directory/pid" | |
200 | kill -HUP $(cat "$test_directory/pid") | |
201 | wait_for_file "$test_directory/child-store" | |
202 | diff -u "$test_directory/parent-store" "$test_directory/child-store" | |
203 | ||
204 | chmod -Rf +w "$test_directory"; rm -rf "$test_directory"/* | |
205 | fi | |
206 | ||
b908fcd8 LC |
207 | # Ensure '-R' works with outputs other than "out". |
208 | tarball="`guix pack -R -S /share=share groff:doc`" | |
c088aa29 | 209 | (cd "$test_directory"; tar xf "$tarball") |
b908fcd8 | 210 | test -d "$test_directory/share/doc/groff/html" |
4184998c | 211 | chmod -Rf +w "$test_directory"; rm -rf "$test_directory"/* |
a5538922 LC |
212 | |
213 | # Ensure '-R' applies to propagated inputs. Failing to do that, it would fail | |
214 | # with a profile collision error in this case because 'python-scipy' | |
215 | # propagates 'python-numpy'. See <https://bugs.gnu.org/42510>. | |
216 | guix pack -RR python-numpy python-scipy --no-grafts -n | |
4184998c EB |
217 | |
218 | # Check that packages that mix executable and support files (e.g. git) in the | |
219 | # "binary" directories still work after wrapped. | |
220 | cat >"$test_directory/manifest.scm" <<'EOF' | |
221 | (use-modules (guix) (guix profiles) (guix search-paths) | |
222 | (gnu packages bootstrap)) | |
223 | (manifest | |
224 | (list (manifest-entry | |
225 | (name "test") (version "0") | |
226 | (item (file-union "test" | |
227 | `(("bin/hello" | |
228 | ,(program-file | |
229 | "hello" | |
230 | #~(begin | |
231 | (add-to-load-path (getenv "HELLO_EXEC_PATH")) | |
232 | (display (load-from-path "msg"))(newline)) | |
233 | #:guile %bootstrap-guile)) | |
234 | ("libexec/hello/msg" | |
235 | ,(plain-file "msg" "42"))))) | |
236 | (search-paths | |
237 | (list (search-path-specification | |
238 | (variable "HELLO_EXEC_PATH") | |
239 | (files '("libexec/hello")) | |
240 | (separator #f))))))) | |
241 | EOF | |
242 | tarball="`guix pack -RR -S /opt= -m $test_directory/manifest.scm`" | |
243 | (cd "$test_directory"; tar xvf "$tarball") | |
244 | ( export GUIX_PROFILE=$test_directory/opt | |
245 | . $GUIX_PROFILE/etc/profile | |
246 | run_without_store "$test_directory/opt/bin/hello" > "$test_directory/output" ) | |
247 | cat "$test_directory/output" | |
248 | test "`cat $test_directory/output`" = "42" |