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