xref: /freebsd/sbin/bectl/tests/bectl_test.sh (revision 18f21f0355481283ceef0ec10e99554f44c205c2)
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	# Test standard creation, creation of a snapshot, and creation from a
126	# snapshot.
127	atf_check bectl -r ${zpool}/ROOT create -e default default2
128	atf_check bectl -r ${zpool}/ROOT create default2@test_snap
129	atf_check bectl -r ${zpool}/ROOT create -e default2@test_snap default3
130
131	# Test standard creation, creation of a snapshot, and creation from a
132	# snapshot for recursive boot environments.
133	atf_check bectl -r ${zpool}/ROOT create -r -e default recursive
134	atf_check bectl -r ${zpool}/ROOT create -r recursive@test_snap
135	atf_check bectl -r ${zpool}/ROOT create -r -e recursive@test_snap recursive-snap
136
137	# Test that non-recursive boot environments have no child datasets.
138	atf_check -e not-empty -s not-exit:0 \
139		zfs list "${zpool}/ROOT/default2/usr"
140	atf_check -e not-empty -s not-exit:0 \
141		zfs list "${zpool}/ROOT/default3/usr"
142
143	# Test that recursive boot environments have child datasets.
144	atf_check -o not-empty \
145		zfs list "${zpool}/ROOT/recursive/usr"
146	atf_check -o not-empty \
147		zfs list "${zpool}/ROOT/recursive-snap/usr"
148}
149bectl_create_cleanup()
150{
151	bectl_cleanup $(get_zpool_name)
152}
153
154atf_test_case bectl_destroy cleanup
155bectl_destroy_head()
156{
157
158	atf_set "descr" "Check bectl destroy"
159	atf_set "require.user" root
160}
161bectl_destroy_body()
162{
163	if [ "$(atf_config_get ci false)" = "true" ] && \
164		[ "$(uname -p)" = "i386" ]; then
165		atf_skip "https://bugs.freebsd.org/249055"
166	fi
167
168	if [ "$(atf_config_get ci false)" = "true" ] && \
169		[ "$(uname -p)" = "armv7" ]; then
170		atf_skip "https://bugs.freebsd.org/249229"
171	fi
172
173	cwd=$(realpath .)
174	zpool=$(make_zpool_name)
175	disk=${cwd}/disk.img
176	mount=${cwd}/mnt
177	root=${mount}/root
178
179	bectl_create_setup ${zpool} ${disk} ${mount}
180	atf_check bectl -r ${zpool}/ROOT create -e default default2
181	atf_check -o not-empty zfs get mountpoint ${zpool}/ROOT/default2
182	atf_check -e ignore bectl -r ${zpool}/ROOT destroy default2
183	atf_check -e not-empty -s not-exit:0 zfs get mountpoint ${zpool}/ROOT/default2
184
185	# Test origin snapshot deletion when the snapshot to be destroyed
186	# belongs to a mounted dataset, see PR 236043.
187	atf_check mkdir -p ${root}
188	atf_check -o not-empty bectl -r ${zpool}/ROOT mount default ${root}
189	atf_check bectl -r ${zpool}/ROOT create -e default default3
190	atf_check bectl -r ${zpool}/ROOT destroy -o default3
191	atf_check bectl -r ${zpool}/ROOT unmount default
192
193	# create two be from the same parent and destroy the parent
194	atf_check bectl -r ${zpool}/ROOT create -e default default2
195	atf_check bectl -r ${zpool}/ROOT create -e default default3
196	atf_check bectl -r ${zpool}/ROOT destroy default
197	atf_check bectl -r ${zpool}/ROOT destroy default2
198	atf_check bectl -r ${zpool}/ROOT rename default3 default
199
200	# Create a BE, have it be the parent for another and repeat, then start
201	# deleting environments.  Arbitrarily chose default3 as the first.
202	# Sleeps are required to prevent conflicting snapshots- libbe will
203	# use the time with a serial at the end as needed to prevent collisions,
204	# but as BEs get promoted the snapshot names will convert and conflict
205	# anyways.  libbe should perhaps consider adding something extra to the
206	# default name to prevent collisions like this, but the default name
207	# includes down to the second and creating BEs this rapidly is perhaps
208	# uncommon enough.
209	atf_check bectl -r ${zpool}/ROOT create -e default default2
210	sleep 1
211	atf_check bectl -r ${zpool}/ROOT create -e default2 default3
212	sleep 1
213	atf_check bectl -r ${zpool}/ROOT create -e default3 default4
214	atf_check bectl -r ${zpool}/ROOT destroy default3
215	atf_check bectl -r ${zpool}/ROOT destroy default2
216	atf_check bectl -r ${zpool}/ROOT destroy default4
217
218	# Create two BEs, then create an unrelated snapshot on the originating
219	# BE and destroy it.  We shouldn't have promoted the second BE, and it's
220	# only possible to tell if we promoted it by making sure we didn't
221	# demote the first BE at some point -- if we did, it's origin will no
222	# longer be empty.
223	atf_check bectl -r ${zpool}/ROOT create -e default default2
224	atf_check bectl -r ${zpool}/ROOT create default@test
225
226	atf_check bectl -r ${zpool}/ROOT destroy default@test
227	atf_check -o inline:"-\n" zfs get -Ho value origin ${zpool}/ROOT/default
228	atf_check bectl -r ${zpool}/ROOT destroy default2
229
230	# As observed by beadm, if we explicitly try to destroy a snapshot that
231	# leads to clones, we shouldn't have allowed it.
232	atf_check bectl -r ${zpool}/ROOT create default@test
233	atf_check bectl -r ${zpool}/ROOT create -e default@test default2
234
235	atf_check -e  not-empty -s not-exit:0 bectl -r ${zpool}/ROOT destroy \
236	    default@test
237}
238bectl_destroy_cleanup()
239{
240
241	bectl_cleanup $(get_zpool_name)
242}
243
244atf_test_case bectl_export_import cleanup
245bectl_export_import_head()
246{
247
248	atf_set "descr" "Check bectl export and import"
249	atf_set "require.user" root
250}
251bectl_export_import_body()
252{
253	if [ "$(atf_config_get ci false)" = "true" ] && \
254		[ "$(uname -p)" = "i386" ]; then
255		atf_skip "https://bugs.freebsd.org/249055"
256	fi
257
258	if [ "$(atf_config_get ci false)" = "true" ] && \
259		[ "$(uname -p)" = "armv7" ]; then
260		atf_skip "https://bugs.freebsd.org/249229"
261	fi
262
263	cwd=$(realpath .)
264	zpool=$(make_zpool_name)
265	disk=${cwd}/disk.img
266	mount=${cwd}/mnt
267
268	bectl_create_setup ${zpool} ${disk} ${mount}
269	atf_check -o save:exported bectl -r ${zpool}/ROOT export default
270	atf_check -x "bectl -r ${zpool}/ROOT import default2 < exported"
271	atf_check -o not-empty zfs get mountpoint ${zpool}/ROOT/default2
272	atf_check -e ignore bectl -r ${zpool}/ROOT destroy default2
273	atf_check -e not-empty -s not-exit:0 zfs get mountpoint \
274	    ${zpool}/ROOT/default2
275}
276bectl_export_import_cleanup()
277{
278
279	bectl_cleanup $(get_zpool_name)
280}
281
282atf_test_case bectl_list cleanup
283bectl_list_head()
284{
285
286	atf_set "descr" "Check bectl list"
287	atf_set "require.user" root
288}
289bectl_list_body()
290{
291	if [ "$(atf_config_get ci false)" = "true" ] && \
292		[ "$(uname -p)" = "i386" ]; then
293		atf_skip "https://bugs.freebsd.org/249055"
294	fi
295
296	if [ "$(atf_config_get ci false)" = "true" ] && \
297		[ "$(uname -p)" = "armv7" ]; then
298		atf_skip "https://bugs.freebsd.org/249229"
299	fi
300
301	cwd=$(realpath .)
302	zpool=$(make_zpool_name)
303	disk=${cwd}/disk.img
304	mount=${cwd}/mnt
305
306	bectl_create_setup ${zpool} ${disk} ${mount}
307	# Test the list functionality, including that BEs come and go away
308	# as they're created and destroyed.  Creation and destruction tests
309	# use the 'zfs' utility to verify that they're actually created, so
310	# these are just light tests that 'list' is picking them up.
311	atf_check -o save:list.out bectl -r ${zpool}/ROOT list
312	atf_check -o not-empty grep 'default' list.out
313	atf_check bectl -r ${zpool}/ROOT create -e default default2
314	atf_check -o save:list.out bectl -r ${zpool}/ROOT list
315	atf_check -o not-empty grep 'default2' list.out
316	atf_check -e ignore bectl -r ${zpool}/ROOT destroy default2
317	atf_check -o save:list.out bectl -r ${zpool}/ROOT list
318	atf_check -s not-exit:0 grep 'default2' list.out
319	# XXX TODO: Formatting checks
320}
321bectl_list_cleanup()
322{
323
324	bectl_cleanup $(get_zpool_name)
325}
326
327atf_test_case bectl_mount cleanup
328bectl_mount_head()
329{
330
331	atf_set "descr" "Check bectl mount/unmount"
332	atf_set "require.user" root
333}
334bectl_mount_body()
335{
336	if [ "$(atf_config_get ci false)" = "true" ] && \
337		[ "$(uname -p)" = "i386" ]; then
338		atf_skip "https://bugs.freebsd.org/249055"
339	fi
340
341	if [ "$(atf_config_get ci false)" = "true" ] && \
342		[ "$(uname -p)" = "armv7" ]; then
343		atf_skip "https://bugs.freebsd.org/249229"
344	fi
345
346	cwd=$(realpath .)
347	zpool=$(make_zpool_name)
348	disk=${cwd}/disk.img
349	mount=${cwd}/mnt
350	root=${mount}/root
351
352	bectl_create_deep_setup ${zpool} ${disk} ${mount}
353	atf_check mkdir -p ${root}
354	# Test unmount first...
355	atf_check -o not-empty bectl -r ${zpool}/ROOT mount default ${root}
356	atf_check -o not-empty -x "mount | grep '^${zpool}/ROOT/default'"
357	atf_check bectl -r ${zpool}/ROOT unmount default
358	atf_check -s not-exit:0 -x "mount | grep '^${zpool}/ROOT/default'"
359	# Then umount!
360	atf_check -o not-empty bectl -r ${zpool}/ROOT mount default ${root}
361	atf_check -o not-empty -x "mount | grep '^${zpool}/ROOT/default'"
362	atf_check bectl -r ${zpool}/ROOT umount default
363	atf_check -s not-exit:0 -x "mount | grep '^${zpool}/ROOT/default'"
364}
365bectl_mount_cleanup()
366{
367
368	bectl_cleanup $(get_zpool_name)
369}
370
371atf_test_case bectl_rename cleanup
372bectl_rename_head()
373{
374
375	atf_set "descr" "Check bectl rename"
376	atf_set "require.user" root
377}
378bectl_rename_body()
379{
380	if [ "$(atf_config_get ci false)" = "true" ] && \
381		[ "$(uname -p)" = "i386" ]; then
382		atf_skip "https://bugs.freebsd.org/249055"
383	fi
384
385	if [ "$(atf_config_get ci false)" = "true" ] && \
386		[ "$(uname -p)" = "armv7" ]; then
387		atf_skip "https://bugs.freebsd.org/249229"
388	fi
389
390	cwd=$(realpath .)
391	zpool=$(make_zpool_name)
392	disk=${cwd}/disk.img
393	mount=${cwd}/mnt
394
395	bectl_create_setup ${zpool} ${disk} ${mount}
396	atf_check bectl -r ${zpool}/ROOT rename default default2
397	atf_check -o not-empty zfs get mountpoint ${zpool}/ROOT/default2
398	atf_check -e not-empty -s not-exit:0 zfs get mountpoint \
399	    ${zpool}/ROOT/default
400}
401bectl_rename_cleanup()
402{
403
404	bectl_cleanup $(get_zpool_name)
405}
406
407atf_test_case bectl_jail cleanup
408bectl_jail_head()
409{
410
411	atf_set "descr" "Check bectl rename"
412	atf_set "require.user" root
413	atf_set "require.progs" jail
414}
415bectl_jail_body()
416{
417	if [ "$(atf_config_get ci false)" = "true" ] && \
418		[ "$(uname -p)" = "i386" ]; then
419		atf_skip "https://bugs.freebsd.org/249055"
420	fi
421
422	if [ "$(atf_config_get ci false)" = "true" ] && \
423		[ "$(uname -p)" = "armv7" ]; then
424		atf_skip "https://bugs.freebsd.org/249229"
425	fi
426
427	cwd=$(realpath .)
428	zpool=$(make_zpool_name)
429	disk=${cwd}/disk.img
430	mount=${cwd}/mnt
431	root=${mount}/root
432
433	if [ ! -f /rescue/rescue ]; then
434		atf_skip "This test requires a rescue binary"
435	fi
436	bectl_create_deep_setup ${zpool} ${disk} ${mount}
437	# Prepare our minimal BE... plop a rescue binary into it
438	atf_check mkdir -p ${root}
439	atf_check -o ignore bectl -r ${zpool}/ROOT mount default ${root}
440	atf_check mkdir -p ${root}/rescue
441	atf_check cp /rescue/rescue ${root}/rescue/rescue
442	atf_check bectl -r ${zpool}/ROOT umount default
443
444	# Prepare some more boot environments
445	atf_check -o empty -s exit:0 bectl -r ${zpool}/ROOT create -e default target
446	atf_check -o empty -s exit:0 bectl -r ${zpool}/ROOT create -e default 1234
447
448	# Attempt to unjail a BE with numeric name; jail_getid at one point
449	# did not validate that the input was a valid jid before returning the
450	# jid.
451	atf_check -o empty -s exit:0 bectl -r ${zpool}/ROOT jail -b 1234
452	atf_check -o empty -s exit:0 bectl -r ${zpool}/ROOT unjail 1234
453
454	# When a jail name is not explicit, it should match the jail id.
455	atf_check -o empty -s exit:0 bectl -r ${zpool}/ROOT jail -b -o jid=233637 default
456	atf_check -o inline:"233637\n" -s exit:0 -x "jls -j 233637 name"
457	atf_check -o empty -s exit:0 bectl -r ${zpool}/ROOT unjail default
458
459	# Basic command-mode tests, with and without jail cleanup
460	atf_check -o inline:"rescue\nusr\n" bectl -r ${zpool}/ROOT \
461	    jail default /rescue/rescue ls -1
462	atf_check -o inline:"rescue\nusr\n" bectl -r ${zpool}/ROOT \
463	    jail -Uo path=${root} default /rescue/rescue ls -1
464	atf_check [ -f ${root}/rescue/rescue ]
465	atf_check bectl -r ${zpool}/ROOT ujail default
466
467	# Batch mode tests
468	atf_check bectl -r ${zpool}/ROOT jail -bo path=${root} default
469	atf_check -o not-empty -x "jls | grep -F \"${root}\""
470	atf_check bectl -r ${zpool}/ROOT ujail default
471	atf_check -s not-exit:0 -x "jls | grep -F \"${root}\""
472	# 'unjail' naming
473	atf_check bectl -r ${zpool}/ROOT jail -b default
474	atf_check bectl -r ${zpool}/ROOT unjail default
475	atf_check -s not-exit:0 -x "jls | grep -F \"${root}\""
476	# 'unjail' by BE name. Force bectl to lookup jail id by the BE name.
477	atf_check -o empty -s exit:0 bectl -r ${zpool}/ROOT jail -b default
478	atf_check -o empty -s exit:0 bectl -r ${zpool}/ROOT jail -b -o name=bectl_test target
479	atf_check -o empty -s exit:0 bectl -r ${zpool}/ROOT unjail target
480	atf_check -o empty -s exit:0 bectl -r ${zpool}/ROOT unjail default
481	# cannot unjail an unjailed BE (by either command name)
482	atf_check -e ignore -s not-exit:0 bectl -r ${zpool}/ROOT ujail default
483	atf_check -e ignore -s not-exit:0 bectl -r ${zpool}/ROOT unjail default
484
485	# set+unset
486	atf_check bectl -r ${zpool}/ROOT jail -b -o path=${root} -u path default
487	# Ensure that it didn't mount at ${root}
488	atf_check -s not-exit:0 -x "mount | grep -F '${root}'"
489	atf_check bectl -r ${zpool}/ROOT ujail default
490}
491
492# If a test has failed, it's possible that the boot environment hasn't
493# been 'unjail'ed. We want to remove the jail before 'bectl_cleanup'
494# attempts to destroy the zpool.
495bectl_jail_cleanup()
496{
497	if [ "$(atf_config_get ci false)" = "true" ] && \
498		[ "$(uname -p)" = "i386" ]; then
499		atf_skip "https://bugs.freebsd.org/249055"
500	fi
501
502	if [ "$(atf_config_get ci false)" = "true" ] && \
503		[ "$(uname -p)" = "armv7" ]; then
504		atf_skip "https://bugs.freebsd.org/249229"
505	fi
506
507	zpool=$(get_zpool_name)
508	for bootenv in "default" "target" "1234"; do
509		# mountpoint of the boot environment
510		mountpoint="$(bectl -r ${zpool}/ROOT list -H | grep ${bootenv} | awk '{print $3}')"
511
512		# see if any jail paths match the boot environment mountpoint
513		jailid="$(jls | grep ${mountpoint} | awk '{print $1}')"
514
515		if [ -z "$jailid" ]; then
516		       continue;
517		fi
518		jail -r ${jailid}
519	done;
520
521	bectl_cleanup ${zpool}
522}
523
524atf_init_test_cases()
525{
526	atf_add_test_case bectl_create
527	atf_add_test_case bectl_destroy
528	atf_add_test_case bectl_export_import
529	atf_add_test_case bectl_list
530	atf_add_test_case bectl_mount
531	atf_add_test_case bectl_rename
532	atf_add_test_case bectl_jail
533}
534