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} 347bectl_jail_body() 348{ 349 cwd=$(realpath .) 350 zpool=$(make_zpool_name) 351 disk=${cwd}/disk.img 352 mount=${cwd}/mnt 353 root=${mount}/root 354 355 if [ ! -f /rescue/rescue ]; then 356 atf_skip "This test requires a rescue binary" 357 fi 358 bectl_create_deep_setup ${zpool} ${disk} ${mount} 359 # Prepare our minimal BE... plop a rescue binary into it 360 atf_check mkdir -p ${root} 361 atf_check -o ignore bectl -r ${zpool}/ROOT mount default ${root} 362 atf_check mkdir -p ${root}/rescue 363 atf_check cp /rescue/rescue ${root}/rescue/rescue 364 atf_check bectl -r ${zpool}/ROOT umount default 365 366 # Prepare some more boot environments 367 atf_check -o empty -s exit:0 bectl -r ${zpool}/ROOT create -e default target 368 atf_check -o empty -s exit:0 bectl -r ${zpool}/ROOT create -e default 1234 369 370 # Attempt to unjail a BE with numeric name; jail_getid at one point 371 # did not validate that the input was a valid jid before returning the 372 # jid. 373 atf_check -o empty -s exit:0 bectl -r ${zpool}/ROOT jail -b 1234 374 atf_check -o empty -s exit:0 bectl -r ${zpool}/ROOT unjail 1234 375 376 # When a jail name is not explicit, it should match the jail id. 377 atf_check -o empty -s exit:0 bectl -r ${zpool}/ROOT jail -b -o jid=233637 default 378 atf_check -o inline:"233637\n" -s exit:0 -x "jls -j 233637 name" 379 atf_check -o empty -s exit:0 bectl -r ${zpool}/ROOT unjail default 380 381 # Basic command-mode tests, with and without jail cleanup 382 atf_check -o inline:"rescue\nusr\n" bectl -r ${zpool}/ROOT \ 383 jail default /rescue/rescue ls -1 384 atf_check -o inline:"rescue\nusr\n" bectl -r ${zpool}/ROOT \ 385 jail -Uo path=${root} default /rescue/rescue ls -1 386 atf_check [ -f ${root}/rescue/rescue ] 387 atf_check bectl -r ${zpool}/ROOT ujail default 388 389 # Batch mode tests 390 atf_check bectl -r ${zpool}/ROOT jail -bo path=${root} default 391 atf_check -o not-empty -x "jls | grep -F \"${root}\"" 392 atf_check bectl -r ${zpool}/ROOT ujail default 393 atf_check -s not-exit:0 -x "jls | grep -F \"${root}\"" 394 # 'unjail' naming 395 atf_check bectl -r ${zpool}/ROOT jail -b default 396 atf_check bectl -r ${zpool}/ROOT unjail default 397 atf_check -s not-exit:0 -x "jls | grep -F \"${root}\"" 398 # 'unjail' by BE name. Force bectl to lookup jail id by the BE name. 399 atf_check -o empty -s exit:0 bectl -r ${zpool}/ROOT jail -b default 400 atf_check -o empty -s exit:0 bectl -r ${zpool}/ROOT jail -b -o name=bectl_test target 401 atf_check -o empty -s exit:0 bectl -r ${zpool}/ROOT unjail target 402 atf_check -o empty -s exit:0 bectl -r ${zpool}/ROOT unjail default 403 # cannot unjail an unjailed BE (by either command name) 404 atf_check -e ignore -s not-exit:0 bectl -r ${zpool}/ROOT ujail default 405 atf_check -e ignore -s not-exit:0 bectl -r ${zpool}/ROOT unjail default 406 407 # set+unset 408 atf_check bectl -r ${zpool}/ROOT jail -b -o path=${root} -u path default 409 # Ensure that it didn't mount at ${root} 410 atf_check -s not-exit:0 -x "mount | grep -F '${root}'" 411 atf_check bectl -r ${zpool}/ROOT ujail default 412} 413 414# If a test has failed, it's possible that the boot environment hasn't 415# been 'unjail'ed. We want to remove the jail before 'bectl_cleanup' 416# attempts to destroy the zpool. 417bectl_jail_cleanup() 418{ 419 zpool=$(get_zpool_name) 420 for bootenv in "default" "target" "1234"; do 421 # mountpoint of the boot environment 422 mountpoint="$(bectl -r ${zpool}/ROOT list -H | grep ${bootenv} | awk '{print $3}')" 423 424 # see if any jail paths match the boot environment mountpoint 425 jailid="$(jls | grep ${mountpoint} | awk '{print $1}')" 426 427 if [ -z "$jailid" ]; then 428 continue; 429 fi 430 jail -r ${jailid} 431 done; 432 433 bectl_cleanup ${zpool} 434} 435 436atf_init_test_cases() 437{ 438 atf_add_test_case bectl_create 439 atf_add_test_case bectl_destroy 440 atf_add_test_case bectl_export_import 441 atf_add_test_case bectl_list 442 atf_add_test_case bectl_mount 443 atf_add_test_case bectl_rename 444 atf_add_test_case bectl_jail 445} 446