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 -R ${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