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