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