xref: /freebsd/sbin/bectl/tests/bectl_test.sh (revision 7f49ce7a0b5f0d501d233308d73ccb1bf191a68b)
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}
166bectl_destroy_cleanup()
167{
168
169	bectl_cleanup $(get_zpool_name)
170}
171
172atf_test_case bectl_export_import cleanup
173bectl_export_import_head()
174{
175
176	atf_set "descr" "Check bectl export and import"
177	atf_set "require.user" root
178}
179bectl_export_import_body()
180{
181	cwd=$(realpath .)
182	zpool=$(make_zpool_name)
183	disk=${cwd}/disk.img
184	mount=${cwd}/mnt
185
186	bectl_create_setup ${zpool} ${disk} ${mount}
187	atf_check -o save:exported bectl -r ${zpool}/ROOT export default
188	atf_check -x "bectl -r ${zpool}/ROOT import default2 < exported"
189	atf_check -o not-empty zfs get mountpoint ${zpool}/ROOT/default2
190	atf_check -e ignore bectl -r ${zpool}/ROOT destroy default2
191	atf_check -e not-empty -s not-exit:0 zfs get mountpoint \
192	    ${zpool}/ROOT/default2
193}
194bectl_export_import_cleanup()
195{
196
197	bectl_cleanup $(get_zpool_name)
198}
199
200atf_test_case bectl_list cleanup
201bectl_list_head()
202{
203
204	atf_set "descr" "Check bectl list"
205	atf_set "require.user" root
206}
207bectl_list_body()
208{
209	cwd=$(realpath .)
210	zpool=$(make_zpool_name)
211	disk=${cwd}/disk.img
212	mount=${cwd}/mnt
213
214	bectl_create_setup ${zpool} ${disk} ${mount}
215	# Test the list functionality, including that BEs come and go away
216	# as they're created and destroyed.  Creation and destruction tests
217	# use the 'zfs' utility to verify that they're actually created, so
218	# these are just light tests that 'list' is picking them up.
219	atf_check -o save:list.out bectl -r ${zpool}/ROOT list
220	atf_check -o not-empty grep 'default' list.out
221	atf_check bectl -r ${zpool}/ROOT create -e default default2
222	atf_check -o save:list.out bectl -r ${zpool}/ROOT list
223	atf_check -o not-empty grep 'default2' list.out
224	atf_check -e ignore bectl -r ${zpool}/ROOT destroy default2
225	atf_check -o save:list.out bectl -r ${zpool}/ROOT list
226	atf_check -s not-exit:0 grep 'default2' list.out
227	# XXX TODO: Formatting checks
228}
229bectl_list_cleanup()
230{
231
232	bectl_cleanup $(get_zpool_name)
233}
234
235atf_test_case bectl_mount cleanup
236bectl_mount_head()
237{
238
239	atf_set "descr" "Check bectl mount/unmount"
240	atf_set "require.user" root
241}
242bectl_mount_body()
243{
244	cwd=$(realpath .)
245	zpool=$(make_zpool_name)
246	disk=${cwd}/disk.img
247	mount=${cwd}/mnt
248	root=${mount}/root
249
250	bectl_create_deep_setup ${zpool} ${disk} ${mount}
251	atf_check mkdir -p ${root}
252	# Test unmount first...
253	atf_check -o not-empty bectl -r ${zpool}/ROOT mount default ${root}
254	atf_check -o not-empty -x "mount | grep '^${zpool}/ROOT/default'"
255	atf_check bectl -r ${zpool}/ROOT unmount default
256	atf_check -s not-exit:0 -x "mount | grep '^${zpool}/ROOT/default'"
257	# Then umount!
258	atf_check -o not-empty bectl -r ${zpool}/ROOT mount default ${root}
259	atf_check -o not-empty -x "mount | grep '^${zpool}/ROOT/default'"
260	atf_check bectl -r ${zpool}/ROOT umount default
261	atf_check -s not-exit:0 -x "mount | grep '^${zpool}/ROOT/default'"
262}
263bectl_mount_cleanup()
264{
265
266	bectl_cleanup $(get_zpool_name)
267}
268
269atf_test_case bectl_rename cleanup
270bectl_rename_head()
271{
272
273	atf_set "descr" "Check bectl rename"
274	atf_set "require.user" root
275}
276bectl_rename_body()
277{
278	cwd=$(realpath .)
279	zpool=$(make_zpool_name)
280	disk=${cwd}/disk.img
281	mount=${cwd}/mnt
282
283	bectl_create_setup ${zpool} ${disk} ${mount}
284	atf_check bectl -r ${zpool}/ROOT rename default default2
285	atf_check -o not-empty zfs get mountpoint ${zpool}/ROOT/default2
286	atf_check -e not-empty -s not-exit:0 zfs get mountpoint \
287	    ${zpool}/ROOT/default
288}
289bectl_rename_cleanup()
290{
291
292	bectl_cleanup $(get_zpool_name)
293}
294
295atf_test_case bectl_jail cleanup
296bectl_jail_head()
297{
298
299	atf_set "descr" "Check bectl rename"
300	atf_set "require.user" root
301}
302bectl_jail_body()
303{
304	cwd=$(realpath .)
305	zpool=$(make_zpool_name)
306	disk=${cwd}/disk.img
307	mount=${cwd}/mnt
308	root=${mount}/root
309
310	if [ ! -f /rescue/rescue ]; then
311		atf_skip "This test requires a rescue binary"
312	fi
313	bectl_create_deep_setup ${zpool} ${disk} ${mount}
314	# Prepare our minimal BE... plop a rescue binary into it
315	atf_check mkdir -p ${root}
316	atf_check -o ignore bectl -r ${zpool}/ROOT mount default ${root}
317	atf_check mkdir -p ${root}/rescue
318	atf_check cp /rescue/rescue ${root}/rescue/rescue
319	atf_check bectl -r ${zpool}/ROOT umount default
320
321	# Prepare some more boot environments
322	atf_check -o empty -s exit:0 bectl -r ${zpool}/ROOT create -e default target
323	atf_check -o empty -s exit:0 bectl -r ${zpool}/ROOT create -e default 1234
324
325	# Attempt to unjail a BE with numeric name; jail_getid at one point
326	# did not validate that the input was a valid jid before returning the
327	# jid.
328	atf_check -o empty -s exit:0 bectl -r ${zpool}/ROOT jail -b 1234
329	atf_check -o empty -s exit:0 bectl -r ${zpool}/ROOT unjail 1234
330
331	# When a jail name is not explicit, it should match the jail id.
332	atf_check -o empty -s exit:0 bectl -r ${zpool}/ROOT jail -b -o jid=233637 default
333	atf_check -o inline:"233637\n" -s exit:0 -x "jls -j 233637 name"
334	atf_check -o empty -s exit:0 bectl -r ${zpool}/ROOT unjail default
335
336	# Basic command-mode tests, with and without jail cleanup
337	atf_check -o inline:"rescue\nusr\n" bectl -r ${zpool}/ROOT \
338	    jail default /rescue/rescue ls -1
339	atf_check -o inline:"rescue\nusr\n" bectl -r ${zpool}/ROOT \
340	    jail -Uo path=${root} default /rescue/rescue ls -1
341	atf_check [ -f ${root}/rescue/rescue ]
342	atf_check bectl -r ${zpool}/ROOT ujail default
343
344	# Batch mode tests
345	atf_check bectl -r ${zpool}/ROOT jail -bo path=${root} default
346	atf_check -o not-empty -x "jls | grep -F \"${root}\""
347	atf_check bectl -r ${zpool}/ROOT ujail default
348	atf_check -s not-exit:0 -x "jls | grep -F \"${root}\""
349	# 'unjail' naming
350	atf_check bectl -r ${zpool}/ROOT jail -b default
351	atf_check bectl -r ${zpool}/ROOT unjail default
352	atf_check -s not-exit:0 -x "jls | grep -F \"${root}\""
353	# 'unjail' by BE name. Force bectl to lookup jail id by the BE name.
354	atf_check -o empty -s exit:0 bectl -r ${zpool}/ROOT jail -b default
355	atf_check -o empty -s exit:0 bectl -r ${zpool}/ROOT jail -b -o name=bectl_test target
356	atf_check -o empty -s exit:0 bectl -r ${zpool}/ROOT unjail target
357	atf_check -o empty -s exit:0 bectl -r ${zpool}/ROOT unjail default
358	# cannot unjail an unjailed BE (by either command name)
359	atf_check -e ignore -s not-exit:0 bectl -r ${zpool}/ROOT ujail default
360	atf_check -e ignore -s not-exit:0 bectl -r ${zpool}/ROOT unjail default
361
362	# set+unset
363	atf_check bectl -r ${zpool}/ROOT jail -b -o path=${root} -u path default
364	# Ensure that it didn't mount at ${root}
365	atf_check -s not-exit:0 -x "mount | grep -F '${root}'"
366	atf_check bectl -r ${zpool}/ROOT ujail default
367}
368
369# If a test has failed, it's possible that the boot environment hasn't
370# been 'unjail'ed. We want to remove the jail before 'bectl_cleanup'
371# attempts to destroy the zpool.
372bectl_jail_cleanup()
373{
374	zpool=$(get_zpool_name)
375	for bootenv in "default" "target" "1234"; do
376		# mountpoint of the boot environment
377		mountpoint="$(bectl -r ${zpool}/ROOT list -H | grep ${bootenv} | awk '{print $3}')"
378
379		# see if any jail paths match the boot environment mountpoint
380		jailid="$(jls | grep ${mountpoint} | awk '{print $1}')"
381
382		if [ -z "$jailid" ]; then
383		       continue;
384		fi
385		jail -r ${jailid}
386	done;
387
388	bectl_cleanup ${zpool}
389}
390
391atf_init_test_cases()
392{
393	atf_add_test_case bectl_create
394	atf_add_test_case bectl_destroy
395	atf_add_test_case bectl_export_import
396	atf_add_test_case bectl_list
397	atf_add_test_case bectl_mount
398	atf_add_test_case bectl_rename
399	atf_add_test_case bectl_jail
400}
401