xref: /freebsd/sbin/bectl/tests/bectl_test.sh (revision af23369a6deaaeb612ab266eb88b8bb8d560c322)
1#
2# SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3#
4# Copyright (c) 2018 Kyle Evans <kevans@FreeBSD.org>
5#
6# Redistribution and use in source and binary forms, with or without
7# modification, are permitted provided that the following conditions
8# are met:
9# 1. Redistributions of source code must retain the above copyright
10#    notice, this list of conditions and the following disclaimer.
11# 2. Redistributions in binary form must reproduce the above copyright
12#    notice, this list of conditions and the following disclaimer in the
13#    documentation and/or other materials provided with the distribution.
14#
15# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18# ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25# SUCH DAMAGE.
26#
27# $FreeBSD$
28
29ZPOOL_NAME_FILE=zpool_name
30get_zpool_name()
31{
32	cat $ZPOOL_NAME_FILE
33}
34make_zpool_name()
35{
36	mktemp -u bectl_test_XXXXXX > $ZPOOL_NAME_FILE
37	get_zpool_name
38}
39
40# Establishes a bectl_create zpool that can be used for some light testing; contains
41# a 'default' BE and not much else.
42bectl_create_setup()
43{
44	zpool=$1
45	disk=$2
46	mnt=$3
47
48	# Sanity check to make sure `make_zpool_name` succeeded
49	atf_check test -n "$zpool"
50
51	kldload -n -q zfs || atf_skip "ZFS module not loaded on the current system"
52	if ! getconf MIN_HOLE_SIZE "$(pwd)"; then
53		echo "getconf MIN_HOLE_SIZE $(pwd) failed; sparse files " \
54		    "probably not supported by file system"
55		mount
56		atf_skip "Test's work directory does not support sparse files;" \
57		    "try with a different TMPDIR?"
58	fi
59	atf_check mkdir -p ${mnt}
60	atf_check truncate -s 1G ${disk}
61	atf_check zpool create -R ${mnt} ${zpool} ${disk}
62	atf_check zfs create -o mountpoint=none ${zpool}/ROOT
63	atf_check zfs create -o mountpoint=/ -o canmount=noauto \
64	    ${zpool}/ROOT/default
65}
66bectl_create_deep_setup()
67{
68	zpool=$1
69	disk=$2
70	mnt=$3
71
72	# Sanity check to make sure `make_zpool_name` succeeded
73	atf_check test -n "$zpool"
74
75	bectl_create_setup ${zpool} ${disk} ${mnt}
76	atf_check mkdir -p ${root}
77	atf_check -o ignore bectl -r ${zpool}/ROOT mount default ${root}
78	atf_check mkdir -p ${root}/usr
79	atf_check zfs create -o mountpoint=/usr -o canmount=noauto \
80	    ${zpool}/ROOT/default/usr
81	atf_check -o ignore bectl -r ${zpool}/ROOT umount default
82}
83
84bectl_cleanup()
85{
86	zpool=$1
87	if [ -z "$zpool" ]; then
88		echo "Skipping cleanup; zpool not set up"
89	elif zpool get health ${zpool} >/dev/null 2>&1; then
90		zpool destroy -f ${zpool}
91	fi
92}
93
94atf_test_case bectl_create cleanup
95bectl_create_head()
96{
97
98	atf_set "descr" "Check the various forms of bectl create"
99	atf_set "require.user" root
100}
101bectl_create_body()
102{
103	if [ "$(atf_config_get ci false)" = "true" ] && \
104		[ "$(uname -p)" = "i386" ]; then
105		atf_skip "https://bugs.freebsd.org/249055"
106	fi
107
108	if [ "$(atf_config_get ci false)" = "true" ] && \
109		[ "$(uname -p)" = "armv7" ]; then
110		atf_skip "https://bugs.freebsd.org/249229"
111	fi
112
113	cwd=$(realpath .)
114	zpool=$(make_zpool_name)
115	disk=${cwd}/disk.img
116	mount=${cwd}/mnt
117
118	bectl_create_setup ${zpool} ${disk} ${mount}
119
120	# Create a child dataset that will be used to test creation
121	# of recursive and non-recursive boot environments.
122	atf_check zfs create -o mountpoint=/usr -o canmount=noauto \
123	    ${zpool}/ROOT/default/usr
124
125	# BE datasets with spaces are not bootable, PR 254441.
126	atf_check -e not-empty -s not-exit:0 \
127		bectl -r ${zpool}/ROOT create "foo bar"
128
129	# Test standard creation, creation of a snapshot, and creation from a
130	# snapshot.
131	atf_check bectl -r ${zpool}/ROOT create -e default default2
132	atf_check bectl -r ${zpool}/ROOT create default2@test_snap
133	atf_check bectl -r ${zpool}/ROOT create -e default2@test_snap default3
134
135	# Test standard creation, creation of a snapshot, and creation from a
136	# snapshot for recursive boot environments.
137	atf_check bectl -r ${zpool}/ROOT create -r -e default recursive
138	atf_check bectl -r ${zpool}/ROOT create -r recursive@test_snap
139	atf_check bectl -r ${zpool}/ROOT create -r -e recursive@test_snap recursive-snap
140
141	# Test that non-recursive boot environments have no child datasets.
142	atf_check -e not-empty -s not-exit:0 \
143		zfs list "${zpool}/ROOT/default2/usr"
144	atf_check -e not-empty -s not-exit:0 \
145		zfs list "${zpool}/ROOT/default3/usr"
146
147	# Test that recursive boot environments have child datasets.
148	atf_check -o not-empty \
149		zfs list "${zpool}/ROOT/recursive/usr"
150	atf_check -o not-empty \
151		zfs list "${zpool}/ROOT/recursive-snap/usr"
152}
153bectl_create_cleanup()
154{
155	bectl_cleanup $(get_zpool_name)
156}
157
158atf_test_case bectl_destroy cleanup
159bectl_destroy_head()
160{
161
162	atf_set "descr" "Check bectl destroy"
163	atf_set "require.user" root
164}
165bectl_destroy_body()
166{
167	if [ "$(atf_config_get ci false)" = "true" ] && \
168		[ "$(uname -p)" = "i386" ]; then
169		atf_skip "https://bugs.freebsd.org/249055"
170	fi
171
172	if [ "$(atf_config_get ci false)" = "true" ] && \
173		[ "$(uname -p)" = "armv7" ]; then
174		atf_skip "https://bugs.freebsd.org/249229"
175	fi
176
177	cwd=$(realpath .)
178	zpool=$(make_zpool_name)
179	disk=${cwd}/disk.img
180	mount=${cwd}/mnt
181	root=${mount}/root
182
183	bectl_create_setup ${zpool} ${disk} ${mount}
184	atf_check bectl -r ${zpool}/ROOT create -e default default2
185	atf_check -o not-empty zfs get mountpoint ${zpool}/ROOT/default2
186	atf_check -e ignore bectl -r ${zpool}/ROOT destroy default2
187	atf_check -e not-empty -s not-exit:0 zfs get mountpoint ${zpool}/ROOT/default2
188
189	# Test origin snapshot deletion when the snapshot to be destroyed
190	# belongs to a mounted dataset, see PR 236043.
191	atf_check mkdir -p ${root}
192	atf_check -o not-empty bectl -r ${zpool}/ROOT mount default ${root}
193	atf_check bectl -r ${zpool}/ROOT create -e default default3
194	atf_check bectl -r ${zpool}/ROOT destroy -o default3
195	atf_check bectl -r ${zpool}/ROOT unmount default
196
197	# create two be from the same parent and destroy the parent
198	atf_check bectl -r ${zpool}/ROOT create -e default default2
199	atf_check bectl -r ${zpool}/ROOT create -e default default3
200	atf_check bectl -r ${zpool}/ROOT destroy default
201	atf_check bectl -r ${zpool}/ROOT destroy default2
202	atf_check bectl -r ${zpool}/ROOT rename default3 default
203
204	# Create a BE, have it be the parent for another and repeat, then start
205	# deleting environments.  Arbitrarily chose default3 as the first.
206	# Sleeps are required to prevent conflicting snapshots- libbe will
207	# use the time with a serial at the end as needed to prevent collisions,
208	# but as BEs get promoted the snapshot names will convert and conflict
209	# anyways.  libbe should perhaps consider adding something extra to the
210	# default name to prevent collisions like this, but the default name
211	# includes down to the second and creating BEs this rapidly is perhaps
212	# uncommon enough.
213	atf_check bectl -r ${zpool}/ROOT create -e default default2
214	sleep 1
215	atf_check bectl -r ${zpool}/ROOT create -e default2 default3
216	sleep 1
217	atf_check bectl -r ${zpool}/ROOT create -e default3 default4
218	atf_check bectl -r ${zpool}/ROOT destroy default3
219	atf_check bectl -r ${zpool}/ROOT destroy default2
220	atf_check bectl -r ${zpool}/ROOT destroy default4
221
222	# Create two BEs, then create an unrelated snapshot on the originating
223	# BE and destroy it.  We shouldn't have promoted the second BE, and it's
224	# only possible to tell if we promoted it by making sure we didn't
225	# demote the first BE at some point -- if we did, it's origin will no
226	# longer be empty.
227	atf_check bectl -r ${zpool}/ROOT create -e default default2
228	atf_check bectl -r ${zpool}/ROOT create default@test
229
230	atf_check bectl -r ${zpool}/ROOT destroy default@test
231	atf_check -o inline:"-\n" zfs get -Ho value origin ${zpool}/ROOT/default
232	atf_check bectl -r ${zpool}/ROOT destroy default2
233
234	# As observed by beadm, if we explicitly try to destroy a snapshot that
235	# leads to clones, we shouldn't have allowed it.
236	atf_check bectl -r ${zpool}/ROOT create default@test
237	atf_check bectl -r ${zpool}/ROOT create -e default@test default2
238
239	atf_check -e  not-empty -s not-exit:0 bectl -r ${zpool}/ROOT destroy \
240	    default@test
241}
242bectl_destroy_cleanup()
243{
244
245	bectl_cleanup $(get_zpool_name)
246}
247
248atf_test_case bectl_export_import cleanup
249bectl_export_import_head()
250{
251
252	atf_set "descr" "Check bectl export and import"
253	atf_set "require.user" root
254}
255bectl_export_import_body()
256{
257	if [ "$(atf_config_get ci false)" = "true" ] && \
258		[ "$(uname -p)" = "i386" ]; then
259		atf_skip "https://bugs.freebsd.org/249055"
260	fi
261
262	if [ "$(atf_config_get ci false)" = "true" ] && \
263		[ "$(uname -p)" = "armv7" ]; then
264		atf_skip "https://bugs.freebsd.org/249229"
265	fi
266
267	cwd=$(realpath .)
268	zpool=$(make_zpool_name)
269	disk=${cwd}/disk.img
270	mount=${cwd}/mnt
271
272	bectl_create_setup ${zpool} ${disk} ${mount}
273	atf_check -o save:exported bectl -r ${zpool}/ROOT export default
274	atf_check -x "bectl -r ${zpool}/ROOT import default2 < exported"
275	atf_check -o not-empty zfs get mountpoint ${zpool}/ROOT/default2
276	atf_check -e ignore bectl -r ${zpool}/ROOT destroy default2
277	atf_check -e not-empty -s not-exit:0 zfs get mountpoint \
278	    ${zpool}/ROOT/default2
279}
280bectl_export_import_cleanup()
281{
282
283	bectl_cleanup $(get_zpool_name)
284}
285
286atf_test_case bectl_list cleanup
287bectl_list_head()
288{
289
290	atf_set "descr" "Check bectl list"
291	atf_set "require.user" root
292}
293bectl_list_body()
294{
295	if [ "$(atf_config_get ci false)" = "true" ] && \
296		[ "$(uname -p)" = "i386" ]; then
297		atf_skip "https://bugs.freebsd.org/249055"
298	fi
299
300	if [ "$(atf_config_get ci false)" = "true" ] && \
301		[ "$(uname -p)" = "armv7" ]; then
302		atf_skip "https://bugs.freebsd.org/249229"
303	fi
304
305	cwd=$(realpath .)
306	zpool=$(make_zpool_name)
307	disk=${cwd}/disk.img
308	mount=${cwd}/mnt
309
310	bectl_create_setup ${zpool} ${disk} ${mount}
311	# Test the list functionality, including that BEs come and go away
312	# as they're created and destroyed.  Creation and destruction tests
313	# use the 'zfs' utility to verify that they're actually created, so
314	# these are just light tests that 'list' is picking them up.
315	atf_check -o save:list.out bectl -r ${zpool}/ROOT list
316	atf_check -o not-empty grep 'default' list.out
317	atf_check bectl -r ${zpool}/ROOT create -e default default2
318	atf_check -o save:list.out bectl -r ${zpool}/ROOT list
319	atf_check -o not-empty grep 'default2' list.out
320	atf_check -e ignore bectl -r ${zpool}/ROOT destroy default2
321	atf_check -o save:list.out bectl -r ${zpool}/ROOT list
322	atf_check -s not-exit:0 grep 'default2' list.out
323	# XXX TODO: Formatting checks
324}
325bectl_list_cleanup()
326{
327
328	bectl_cleanup $(get_zpool_name)
329}
330
331atf_test_case bectl_mount cleanup
332bectl_mount_head()
333{
334
335	atf_set "descr" "Check bectl mount/unmount"
336	atf_set "require.user" root
337}
338bectl_mount_body()
339{
340	if [ "$(atf_config_get ci false)" = "true" ] && \
341		[ "$(uname -p)" = "i386" ]; then
342		atf_skip "https://bugs.freebsd.org/249055"
343	fi
344
345	if [ "$(atf_config_get ci false)" = "true" ] && \
346		[ "$(uname -p)" = "armv7" ]; then
347		atf_skip "https://bugs.freebsd.org/249229"
348	fi
349
350	cwd=$(realpath .)
351	zpool=$(make_zpool_name)
352	disk=${cwd}/disk.img
353	mount=${cwd}/mnt
354	root=${mount}/root
355
356	bectl_create_deep_setup ${zpool} ${disk} ${mount}
357	atf_check mkdir -p ${root}
358	# Test unmount first...
359	atf_check -o not-empty bectl -r ${zpool}/ROOT mount default ${root}
360	atf_check -o not-empty -x "mount | grep '^${zpool}/ROOT/default'"
361	atf_check bectl -r ${zpool}/ROOT unmount default
362	atf_check -s not-exit:0 -x "mount | grep '^${zpool}/ROOT/default'"
363	# Then umount!
364	atf_check -o not-empty bectl -r ${zpool}/ROOT mount default ${root}
365	atf_check -o not-empty -x "mount | grep '^${zpool}/ROOT/default'"
366	atf_check bectl -r ${zpool}/ROOT umount default
367	atf_check -s not-exit:0 -x "mount | grep '^${zpool}/ROOT/default'"
368}
369bectl_mount_cleanup()
370{
371
372	bectl_cleanup $(get_zpool_name)
373}
374
375atf_test_case bectl_rename cleanup
376bectl_rename_head()
377{
378
379	atf_set "descr" "Check bectl rename"
380	atf_set "require.user" root
381}
382bectl_rename_body()
383{
384	if [ "$(atf_config_get ci false)" = "true" ] && \
385		[ "$(uname -p)" = "i386" ]; then
386		atf_skip "https://bugs.freebsd.org/249055"
387	fi
388
389	if [ "$(atf_config_get ci false)" = "true" ] && \
390		[ "$(uname -p)" = "armv7" ]; then
391		atf_skip "https://bugs.freebsd.org/249229"
392	fi
393
394	cwd=$(realpath .)
395	zpool=$(make_zpool_name)
396	disk=${cwd}/disk.img
397	mount=${cwd}/mnt
398
399	bectl_create_setup ${zpool} ${disk} ${mount}
400	atf_check bectl -r ${zpool}/ROOT rename default default2
401	atf_check -o not-empty zfs get mountpoint ${zpool}/ROOT/default2
402	atf_check -e not-empty -s not-exit:0 zfs get mountpoint \
403	    ${zpool}/ROOT/default
404}
405bectl_rename_cleanup()
406{
407
408	bectl_cleanup $(get_zpool_name)
409}
410
411atf_test_case bectl_jail cleanup
412bectl_jail_head()
413{
414
415	atf_set "descr" "Check bectl rename"
416	atf_set "require.user" root
417	atf_set "require.progs" jail
418}
419bectl_jail_body()
420{
421	if [ "$(atf_config_get ci false)" = "true" ] && \
422		[ "$(uname -p)" = "i386" ]; then
423		atf_skip "https://bugs.freebsd.org/249055"
424	fi
425
426	if [ "$(atf_config_get ci false)" = "true" ] && \
427		[ "$(uname -p)" = "armv7" ]; then
428		atf_skip "https://bugs.freebsd.org/249229"
429	fi
430
431	cwd=$(realpath .)
432	zpool=$(make_zpool_name)
433	disk=${cwd}/disk.img
434	mount=${cwd}/mnt
435	root=${mount}/root
436
437	if [ ! -f /rescue/rescue ]; then
438		atf_skip "This test requires a rescue binary"
439	fi
440	bectl_create_deep_setup ${zpool} ${disk} ${mount}
441	# Prepare our minimal BE... plop a rescue binary into it
442	atf_check mkdir -p ${root}
443	atf_check -o ignore bectl -r ${zpool}/ROOT mount default ${root}
444	atf_check mkdir -p ${root}/rescue
445	atf_check cp /rescue/rescue ${root}/rescue/rescue
446	atf_check bectl -r ${zpool}/ROOT umount default
447
448	# Prepare some more boot environments
449	atf_check -o empty -s exit:0 bectl -r ${zpool}/ROOT create -e default target
450	atf_check -o empty -s exit:0 bectl -r ${zpool}/ROOT create -e default 1234
451
452	# Attempt to unjail a BE with numeric name; jail_getid at one point
453	# did not validate that the input was a valid jid before returning the
454	# jid.
455	atf_check -o empty -s exit:0 bectl -r ${zpool}/ROOT jail -b 1234
456	atf_check -o empty -s exit:0 bectl -r ${zpool}/ROOT unjail 1234
457
458	# When a jail name is not explicit, it should match the jail id.
459	atf_check -o empty -s exit:0 bectl -r ${zpool}/ROOT jail -b -o jid=233637 default
460	atf_check -o inline:"233637\n" -s exit:0 -x "jls -j 233637 name"
461	atf_check -o empty -s exit:0 bectl -r ${zpool}/ROOT unjail default
462
463	# Basic command-mode tests, with and without jail cleanup
464	atf_check -o inline:"rescue\nusr\n" bectl -r ${zpool}/ROOT \
465	    jail default /rescue/rescue ls -1
466	atf_check -o inline:"rescue\nusr\n" bectl -r ${zpool}/ROOT \
467	    jail -Uo path=${root} default /rescue/rescue ls -1
468	atf_check [ -f ${root}/rescue/rescue ]
469	atf_check bectl -r ${zpool}/ROOT ujail default
470
471	# Batch mode tests
472	atf_check bectl -r ${zpool}/ROOT jail -bo path=${root} default
473	atf_check -o not-empty -x "jls | grep -F \"${root}\""
474	atf_check bectl -r ${zpool}/ROOT ujail default
475	atf_check -s not-exit:0 -x "jls | grep -F \"${root}\""
476	# 'unjail' naming
477	atf_check bectl -r ${zpool}/ROOT jail -b default
478	atf_check bectl -r ${zpool}/ROOT unjail default
479	atf_check -s not-exit:0 -x "jls | grep -F \"${root}\""
480	# 'unjail' by BE name. Force bectl to lookup jail id by the BE name.
481	atf_check -o empty -s exit:0 bectl -r ${zpool}/ROOT jail -b default
482	atf_check -o empty -s exit:0 bectl -r ${zpool}/ROOT jail -b -o name=bectl_test target
483	atf_check -o empty -s exit:0 bectl -r ${zpool}/ROOT unjail target
484	atf_check -o empty -s exit:0 bectl -r ${zpool}/ROOT unjail default
485	# cannot unjail an unjailed BE (by either command name)
486	atf_check -e ignore -s not-exit:0 bectl -r ${zpool}/ROOT ujail default
487	atf_check -e ignore -s not-exit:0 bectl -r ${zpool}/ROOT unjail default
488
489	# set+unset
490	atf_check bectl -r ${zpool}/ROOT jail -b -o path=${root} -u path default
491	# Ensure that it didn't mount at ${root}
492	atf_check -s not-exit:0 -x "mount | grep -F '${root}'"
493	atf_check bectl -r ${zpool}/ROOT ujail default
494}
495
496# If a test has failed, it's possible that the boot environment hasn't
497# been 'unjail'ed. We want to remove the jail before 'bectl_cleanup'
498# attempts to destroy the zpool.
499bectl_jail_cleanup()
500{
501	if [ "$(atf_config_get ci false)" = "true" ] && \
502		[ "$(uname -p)" = "i386" ]; then
503		atf_skip "https://bugs.freebsd.org/249055"
504	fi
505
506	if [ "$(atf_config_get ci false)" = "true" ] && \
507		[ "$(uname -p)" = "armv7" ]; then
508		atf_skip "https://bugs.freebsd.org/249229"
509	fi
510
511	zpool=$(get_zpool_name)
512	for bootenv in "default" "target" "1234"; do
513		# mountpoint of the boot environment
514		mountpoint="$(bectl -r ${zpool}/ROOT list -H | grep ${bootenv} | awk '{print $3}')"
515
516		# see if any jail paths match the boot environment mountpoint
517		jailid="$(jls | grep ${mountpoint} | awk '{print $1}')"
518
519		if [ -z "$jailid" ]; then
520		       continue;
521		fi
522		jail -r ${jailid}
523	done;
524
525	bectl_cleanup ${zpool}
526}
527
528atf_init_test_cases()
529{
530	atf_add_test_case bectl_create
531	atf_add_test_case bectl_destroy
532	atf_add_test_case bectl_export_import
533	atf_add_test_case bectl_list
534	atf_add_test_case bectl_mount
535	atf_add_test_case bectl_rename
536	atf_add_test_case bectl_jail
537}
538