mirror of
https://github.com/xcat2/xcat-core.git
synced 2026-08-04 00:16:59 +00:00
036bb24c87
* refactor(packaging): share precise systemd state detection * test(packaging): cover precise systemd state detection * test(packaging): allow explicit Debian init mode * fix(packaging): honor explicit Debian init targets * test(packaging): cover Debian init target detection * fix(packaging): add Debian xcatd init state helper * test(packaging): cover Debian xcatd init state helper * test(packaging): allow delegated SysV registration * fix(packaging): preserve Debian xcatd conffile lifecycle * test(packaging): mirror explicit Debian init mode * test(packaging): cover Debian xcatd conffile lifecycle * fix(packaging): contain init state file umask * test(packaging): cover init state permissions * fix(packaging): contain preinstall context umask * test(packaging): cover preinstall umask containment * fix(packaging): detect all systemd enablement links * test(packaging): cover all systemd enablement links * test(packaging): allow shared purge state path * refactor(packaging): reuse Debian init state path * fix(packaging): detect runtime systemd masks * test(packaging): cover runtime systemd masks * test(packaging): model Debian SysV registration * refactor(packaging): reuse shared init state detection * test(packaging): enforce shared Debian state probes * test(packaging): allow explicit unregistered masks * fix(packaging): preserve Debian SysV registration state * test(packaging): cover Debian SysV registration states * fix(packaging): clean failed Debian state writes * test(packaging): cover failed Debian state writes * fix(packaging): retain unregistered systemd provenance * test(packaging): cover unregistered systemd upgrades * fix(packaging): fail closed on shared state errors * test(packaging): cover shared state detector failures * test(packaging): mirror native xcatd runlevels * fix(packaging): recover rejected SysV layouts * test(packaging): cover rejected SysV layouts * test(packaging): cover SysV rebuild retries * refactor(packaging): reuse systemctl readiness guard * test(packaging): enforce shared systemctl guard
1244 lines
56 KiB
Perl
1244 lines
56 KiB
Perl
#!/usr/bin/env perl
|
|
use strict;
|
|
use warnings;
|
|
|
|
use File::Copy qw(copy);
|
|
use File::Path qw(make_path);
|
|
use File::Spec;
|
|
use File::Temp qw(tempdir);
|
|
use FindBin;
|
|
use Test::More;
|
|
|
|
my $repo_root = File::Spec->catdir( $FindBin::Bin, '..', '..' );
|
|
my $state_helper = File::Spec->catfile(
|
|
$repo_root, 'xCAT-server', 'debian', 'xcatd-init-state'
|
|
);
|
|
my $compat_helper = File::Spec->catfile(
|
|
$repo_root, 'xCAT-server', 'share', 'xcat', 'scripts', 'xcatd-init-compat'
|
|
);
|
|
my $template = File::Spec->catfile(
|
|
$repo_root, 'xCAT-server', 'etc', 'init.d', 'xcatd'
|
|
);
|
|
|
|
sub write_file {
|
|
my ( $path, $contents, $mode ) = @_;
|
|
open( my $fh, '>', $path ) or die "Unable to write $path: $!";
|
|
print {$fh} $contents;
|
|
close($fh);
|
|
chmod $mode, $path if defined $mode;
|
|
}
|
|
|
|
sub read_file {
|
|
my ($path) = @_;
|
|
open( my $fh, '<', $path ) or die "Unable to read $path: $!";
|
|
my $contents = do { local $/; <$fh> };
|
|
close($fh);
|
|
return $contents;
|
|
}
|
|
|
|
sub path_mode {
|
|
my ($path) = @_;
|
|
return ( stat($path) )[2] & 07777;
|
|
}
|
|
|
|
sub stage_root {
|
|
my $root = tempdir( CLEANUP => 1 );
|
|
my $scripts = File::Spec->catdir(
|
|
$root, 'opt', 'xcat', 'share', 'xcat', 'scripts'
|
|
);
|
|
my $fake_bin = File::Spec->catdir( $root, 'test-bin' );
|
|
make_path( $scripts, $fake_bin, File::Spec->catdir( $root, 'sbin' ) );
|
|
copy( $template, File::Spec->catfile( $scripts, 'xcatd' ) )
|
|
or die "Unable to stage xcatd template: $!";
|
|
copy( $compat_helper, File::Spec->catfile( $scripts, 'xcatd-init-compat' ) )
|
|
or die "Unable to stage xcatd compatibility helper: $!";
|
|
chmod 0755, File::Spec->catfile( $scripts, 'xcatd-init-compat' );
|
|
|
|
write_file(
|
|
File::Spec->catfile( $fake_bin, 'ucf' ),
|
|
<<'SH', 0755
|
|
#!/bin/sh
|
|
set -eu
|
|
root=${XCAT_COMPAT_ROOT:?}
|
|
[ ! -f "$root/fail-ucf" ] || exit 9
|
|
new_file=$1
|
|
destination=$2
|
|
if [ ! -e "$destination" ] && [ ! -L "$destination" ]; then
|
|
mkdir -p "$(dirname "$destination")"
|
|
cp "$new_file" "$destination"
|
|
chmod 755 "$destination"
|
|
fi
|
|
if [ -f "$root/mutate-ucf" ]; then
|
|
printf '%s\n' '# ucf mutation' >> "$destination"
|
|
fi
|
|
SH
|
|
);
|
|
write_file(
|
|
File::Spec->catfile( $fake_bin, 'ucfr' ),
|
|
<<'SH', 0755
|
|
#!/bin/sh
|
|
set -eu
|
|
root=${XCAT_COMPAT_ROOT:?}
|
|
[ ! -f "$root/fail-ucfr" ] || exit 8
|
|
exit 0
|
|
SH
|
|
);
|
|
write_file(
|
|
File::Spec->catfile( $fake_bin, 'update-rc.d' ),
|
|
<<'SH', 0755
|
|
#!/bin/sh
|
|
set -eu
|
|
root=${XCAT_COMPAT_ROOT:?}
|
|
remove_links()
|
|
{
|
|
rm -f "$root"/etc/rc?.d/[SK]??xcatd
|
|
}
|
|
registered()
|
|
{
|
|
for link in "$root"/etc/rc?.d/[SK]??xcatd; do
|
|
if [ -e "$link" ] || [ -L "$link" ]; then
|
|
return 0
|
|
fi
|
|
done
|
|
return 1
|
|
}
|
|
create_default_links()
|
|
{
|
|
for runlevel in 3 4 5; do
|
|
mkdir -p "$root/etc/rc$runlevel.d"
|
|
ln -sfn ../init.d/xcatd "$root/etc/rc$runlevel.d/S01xcatd"
|
|
done
|
|
}
|
|
defaults()
|
|
{
|
|
registered && return 0
|
|
create_default_links
|
|
}
|
|
toggle_links()
|
|
{
|
|
new_prefix=$1
|
|
old_prefix=$2
|
|
found=no
|
|
for runlevel in 3 4 5; do
|
|
for link in "$root/etc/rc$runlevel.d"/[SK]??xcatd; do
|
|
if [ -e "$link" ] || [ -L "$link" ]; then
|
|
found=yes
|
|
name=${link##*/}
|
|
case "$name" in
|
|
"$old_prefix"??xcatd)
|
|
sequence=${name#?}
|
|
mv -f "$link" \
|
|
"$root/etc/rc$runlevel.d/${new_prefix}${sequence}"
|
|
;;
|
|
esac
|
|
fi
|
|
done
|
|
done
|
|
[ "$found" = yes ]
|
|
}
|
|
case "${1:-}:${2:-}" in
|
|
-f:xcatd)
|
|
[ "${3:-}" = remove ]
|
|
remove_links
|
|
;;
|
|
xcatd:defaults)
|
|
defaults
|
|
;;
|
|
xcatd:disable)
|
|
toggle_links K S
|
|
[ ! -f "$root/fail-update-rc-disable" ] || exit 9
|
|
;;
|
|
xcatd:enable)
|
|
toggle_links S K
|
|
;;
|
|
*) exit 2 ;;
|
|
esac
|
|
SH
|
|
);
|
|
write_file(
|
|
File::Spec->catfile( $fake_bin, 'systemctl' ),
|
|
<<'SH', 0755
|
|
#!/bin/sh
|
|
set -eu
|
|
root=${XCAT_COMPAT_ROOT:?}
|
|
case "${1:-}" in
|
|
--root=*) shift ;;
|
|
esac
|
|
[ "${1:-}" = is-enabled ] || exit 0
|
|
if [ -r "$root/systemctl-state" ]; then
|
|
status=$(sed -n '1p' "$root/systemctl-state")
|
|
else
|
|
status=unknown
|
|
fi
|
|
printf '%s\n' "$status"
|
|
[ "$status" = enabled ] && exit 0
|
|
exit 1
|
|
SH
|
|
);
|
|
write_file(
|
|
File::Spec->catfile( $fake_bin, 'mv' ),
|
|
<<'SH', 0755
|
|
#!/bin/sh
|
|
set -eu
|
|
root=${XCAT_COMPAT_ROOT:?}
|
|
destination=
|
|
for argument in "$@"; do
|
|
destination=$argument
|
|
done
|
|
if [ -f "$root/fail-stash-move" ] &&
|
|
[ "$destination" = "$root/var/lib/xcat/xcatd-init-state/xcatd" ]; then
|
|
exit 7
|
|
fi
|
|
exec /bin/mv "$@"
|
|
SH
|
|
);
|
|
|
|
return $root;
|
|
}
|
|
|
|
sub run_script {
|
|
my ( $root, $script, @arguments ) = @_;
|
|
local $ENV{XCAT_COMPAT_ROOT} = $root;
|
|
local $ENV{XCATROOT} = '/opt/xcat';
|
|
local $ENV{PATH} = File::Spec->catdir( $root, 'test-bin' ) . ':/usr/bin:/bin';
|
|
system( '/bin/sh', $script, @arguments );
|
|
return $? >> 8;
|
|
}
|
|
|
|
sub run_state {
|
|
my ( $root, @arguments ) = @_;
|
|
return run_script( $root, $state_helper, @arguments );
|
|
}
|
|
|
|
sub run_compat {
|
|
my ( $root, @arguments ) = @_;
|
|
return run_script( $root, $compat_helper, @arguments );
|
|
}
|
|
|
|
sub run_update_rc_status {
|
|
my ( $root, @arguments ) = @_;
|
|
my $update_rc = File::Spec->catfile( $root, 'test-bin', 'update-rc.d' );
|
|
return run_script( $root, $update_rc, @arguments );
|
|
}
|
|
|
|
sub run_update_rc {
|
|
my ( $root, @arguments ) = @_;
|
|
my $status = run_update_rc_status( $root, @arguments );
|
|
die "Fake update-rc.d failed with status $status" if $status;
|
|
}
|
|
|
|
sub live_init {
|
|
my ($root) = @_;
|
|
return File::Spec->catfile( $root, 'etc', 'init.d', 'xcatd' );
|
|
}
|
|
|
|
sub state_dir {
|
|
my ($root) = @_;
|
|
return File::Spec->catdir( $root, 'var', 'lib', 'xcat', 'xcatd-init-state' );
|
|
}
|
|
|
|
sub staged_compat_helper {
|
|
my ($root) = @_;
|
|
return File::Spec->catfile(
|
|
$root, 'opt', 'xcat', 'share', 'xcat', 'scripts',
|
|
'xcatd-init-compat'
|
|
);
|
|
}
|
|
|
|
sub old_systemd_marker {
|
|
my ($root) = @_;
|
|
return File::Spec->catfile( $root, 'var', 'lib', 'xcat', 'xcatd-systemd-mode' );
|
|
}
|
|
|
|
sub state_value {
|
|
my ( $root, $field ) = @_;
|
|
my $state = read_file( File::Spec->catfile( state_dir($root), 'state' ) );
|
|
return $1 if $state =~ /^\Q$field\E=(.+)$/m;
|
|
return '';
|
|
}
|
|
|
|
sub set_init_target {
|
|
my ( $root, $target ) = @_;
|
|
my $init = File::Spec->catfile( $root, 'sbin', 'init' );
|
|
unlink($init) if -e $init || -l $init;
|
|
symlink( $target, $init ) or die "Unable to stage init target: $!";
|
|
}
|
|
|
|
sub set_systemd_state {
|
|
my ( $root, $state ) = @_;
|
|
write_file( File::Spec->catfile( $root, 'systemctl-state' ), "$state\n" );
|
|
}
|
|
|
|
sub rc_link {
|
|
my ($root) = @_;
|
|
return File::Spec->catfile( $root, 'etc', 'rc3.d', 'S01xcatd' );
|
|
}
|
|
|
|
sub rc_kill_link {
|
|
my ($root) = @_;
|
|
return File::Spec->catfile( $root, 'etc', 'rc3.d', 'K01xcatd' );
|
|
}
|
|
|
|
sub registration_link_count {
|
|
my ($root) = @_;
|
|
my @links = glob( File::Spec->catfile(
|
|
$root, 'etc', 'rc?.d', '[SK]??xcatd'
|
|
) );
|
|
return scalar @links;
|
|
}
|
|
|
|
my $update_rc_root = stage_root();
|
|
is( run_update_rc_status( $update_rc_root, 'xcatd', 'enable' ), 1,
|
|
'native enable rejects an unregistered service' );
|
|
is( registration_link_count($update_rc_root), 0,
|
|
'failed native enable does not invent registration links' );
|
|
run_update_rc( $update_rc_root, 'xcatd', 'defaults' );
|
|
is( registration_link_count($update_rc_root), 3,
|
|
'native defaults creates only the three declared start links' );
|
|
for my $runlevel ( 3, 4, 5 ) {
|
|
ok( -l File::Spec->catfile(
|
|
$update_rc_root, 'etc', "rc$runlevel.d", 'S01xcatd'
|
|
),
|
|
"native defaults creates the runlevel $runlevel start link" );
|
|
}
|
|
unlink File::Spec->catfile( $update_rc_root, 'etc', 'rc4.d', 'S01xcatd' );
|
|
unlink File::Spec->catfile( $update_rc_root, 'etc', 'rc5.d', 'S01xcatd' );
|
|
run_update_rc( $update_rc_root, 'xcatd', 'defaults' );
|
|
is( registration_link_count($update_rc_root), 1,
|
|
'native defaults preserves a partial existing registration' );
|
|
run_update_rc( $update_rc_root, 'xcatd', 'disable' );
|
|
ok( -l rc_kill_link($update_rc_root),
|
|
'native disable converts only the existing start link' );
|
|
is( registration_link_count($update_rc_root), 1,
|
|
'native disable does not synthesize missing runlevels' );
|
|
run_update_rc( $update_rc_root, '-f', 'xcatd', 'remove' );
|
|
my $priority_link = File::Spec->catfile(
|
|
$update_rc_root, 'etc', 'rc4.d', 'K17xcatd'
|
|
);
|
|
symlink( '../init.d/xcatd', $priority_link )
|
|
or die "Unable to stage custom-priority registration: $!";
|
|
run_update_rc( $update_rc_root, 'xcatd', 'enable' );
|
|
ok( -l File::Spec->catfile(
|
|
$update_rc_root, 'etc', 'rc4.d', 'S17xcatd'
|
|
),
|
|
'native enable preserves the existing link priority' );
|
|
run_update_rc( $update_rc_root, '-f', 'xcatd', 'remove' );
|
|
my $outside_link = File::Spec->catfile(
|
|
$update_rc_root, 'etc', 'rc2.d', 'S42xcatd'
|
|
);
|
|
make_path( File::Spec->catdir( $update_rc_root, 'etc', 'rc2.d' ) );
|
|
symlink( '../init.d/xcatd', $outside_link )
|
|
or die "Unable to stage outside-runlevel registration: $!";
|
|
is( run_update_rc_status( $update_rc_root, 'xcatd', 'disable' ), 1,
|
|
'native disable rejects links outside the declared start runlevels' );
|
|
ok( -l $outside_link,
|
|
'a rejected native toggle preserves the outside-runlevel link' );
|
|
|
|
my $round_trip_root = stage_root();
|
|
set_init_target( $round_trip_root, 'upstart' );
|
|
my $saved_umask = umask 0022;
|
|
my $fresh_configure_status =
|
|
run_state( $round_trip_root, 'configure-legacy', 'fresh' );
|
|
umask $saved_umask;
|
|
is( $fresh_configure_status, 0,
|
|
'fresh legacy configuration succeeds' );
|
|
is( path_mode( state_dir($round_trip_root) ), 0700,
|
|
'persistent init state remains private' );
|
|
is( path_mode( File::Spec->catfile( state_dir($round_trip_root), 'state' ) ),
|
|
0600, 'the init state file remains private' );
|
|
is( state_value( $round_trip_root, 'format' ), '2',
|
|
'new init state is written in registration-aware format 2' );
|
|
is( path_mode( File::Spec->catdir( $round_trip_root, 'etc', 'init.d' ) ),
|
|
0755, 'state writes do not leak their private umask into init directories' );
|
|
ok( -x live_init($round_trip_root),
|
|
'fresh legacy configuration materializes the init script' );
|
|
ok( -l rc_link($round_trip_root),
|
|
'fresh legacy configuration enables the init script' );
|
|
|
|
write_file( live_init($round_trip_root), "administrator customization\n", 0755 );
|
|
is( run_state( $round_trip_root, 'prepare-systemd', 'upgrade' ), 0,
|
|
'legacy to systemd preparation succeeds' );
|
|
is( state_value( $round_trip_root, 'content' ), 'stashed',
|
|
'a customized init script is stashed before removal' );
|
|
is( state_value( $round_trip_root, 'enabled' ), 'yes',
|
|
'SysV enablement is captured before removal' );
|
|
set_init_target( $round_trip_root, '../lib/systemd/systemd' );
|
|
is( run_compat( $round_trip_root, 'configure', '--explicit-target' ), 0,
|
|
'systemd compatibility configuration succeeds' );
|
|
is( run_state( $round_trip_root, 'commit-systemd' ), 0,
|
|
'systemd transition state commits' );
|
|
ok( !-e live_init($round_trip_root),
|
|
'systemd mode omits the live legacy script' );
|
|
like( read_file( File::Spec->catfile( state_dir($round_trip_root), 'xcatd' ) ),
|
|
qr/administrator customization/,
|
|
'the exact customized script remains in the durable stash' );
|
|
|
|
set_systemd_state( $round_trip_root, 'enabled' );
|
|
set_init_target( $round_trip_root, 'upstart' );
|
|
is( run_state( $round_trip_root, 'configure-legacy', 'upgrade' ), 0,
|
|
'systemd to legacy restoration succeeds' );
|
|
is( read_file( live_init($round_trip_root) ), "administrator customization\n",
|
|
'the customized init script survives a mode round trip' );
|
|
ok( -l rc_link($round_trip_root),
|
|
'systemd enablement maps back to SysV registration' );
|
|
is( state_value( $round_trip_root, 'mode' ), 'legacy',
|
|
'the completed restoration records legacy mode' );
|
|
|
|
unlink( live_init($round_trip_root) )
|
|
or die "Unable to stage administrator deletion: $!";
|
|
is( run_state( $round_trip_root, 'prepare-systemd', 'upgrade' ), 0,
|
|
'deleted legacy state prepares for systemd' );
|
|
is( state_value( $round_trip_root, 'content' ), 'deleted',
|
|
'administrator deletion is recorded separately from package omission' );
|
|
set_init_target( $round_trip_root, '../lib/systemd/systemd' );
|
|
is( run_state( $round_trip_root, 'commit-systemd' ), 0,
|
|
'deleted state commits in systemd mode' );
|
|
set_systemd_state( $round_trip_root, 'disabled' );
|
|
set_init_target( $round_trip_root, 'upstart' );
|
|
is( run_state( $round_trip_root, 'configure-legacy', 'upgrade' ), 0,
|
|
'deleted state returns to legacy mode' );
|
|
ok( !-e live_init($round_trip_root),
|
|
'administrator deletion survives a mode round trip' );
|
|
ok( !-e rc_link($round_trip_root),
|
|
'a missing legacy script is not registered' );
|
|
|
|
my $disabled_root = stage_root();
|
|
set_init_target( $disabled_root, 'upstart' );
|
|
is( run_state( $disabled_root, 'configure-legacy', 'fresh' ), 0,
|
|
'disabled fixture starts in legacy mode' );
|
|
run_update_rc( $disabled_root, 'xcatd', 'disable' );
|
|
ok( -l rc_kill_link($disabled_root),
|
|
'disabled fixture remains registered through a kill link' );
|
|
write_file( live_init($disabled_root), "disabled customization\n", 0755 );
|
|
is( run_state( $disabled_root, 'prepare-systemd', 'upgrade' ), 0,
|
|
'disabled legacy fixture prepares for systemd' );
|
|
is( state_value( $disabled_root, 'enabled' ), 'no',
|
|
'disabled SysV state is captured' );
|
|
set_init_target( $disabled_root, '../lib/systemd/systemd' );
|
|
is( run_compat( $disabled_root, 'configure', '--explicit-target' ), 0,
|
|
'disabled systemd fixture removes the live script' );
|
|
is( run_state( $disabled_root, 'commit-systemd' ), 0,
|
|
'disabled systemd state commits' );
|
|
set_systemd_state( $disabled_root, 'disabled' );
|
|
set_init_target( $disabled_root, 'upstart' );
|
|
is( run_state( $disabled_root, 'configure-legacy', 'upgrade' ), 0,
|
|
'disabled systemd fixture returns to legacy' );
|
|
is( read_file( live_init($disabled_root) ), "disabled customization\n",
|
|
'disabled customization is restored' );
|
|
ok( !-e rc_link($disabled_root),
|
|
'disabled systemd state stays disabled under SysV' );
|
|
ok( -l rc_kill_link($disabled_root),
|
|
'registered-disabled SysV state restores its kill link' );
|
|
run_update_rc( $disabled_root, 'xcatd', 'enable' );
|
|
ok( -l rc_link($disabled_root) && !-e rc_kill_link($disabled_root),
|
|
'a restored registered-disabled service can be enabled later' );
|
|
|
|
my $outside_disabled_root = stage_root();
|
|
set_init_target( $outside_disabled_root, 'upstart' );
|
|
is( run_state( $outside_disabled_root, 'configure-legacy', 'fresh' ), 0,
|
|
'outside-link fixture starts in legacy mode' );
|
|
run_update_rc( $outside_disabled_root, 'xcatd', 'disable' );
|
|
write_file( live_init($outside_disabled_root),
|
|
"outside-link customization\n", 0755 );
|
|
is( run_state( $outside_disabled_root, 'prepare-systemd', 'upgrade' ), 0,
|
|
'outside-link fixture prepares for systemd' );
|
|
set_init_target( $outside_disabled_root, '../lib/systemd/systemd' );
|
|
is( run_compat( $outside_disabled_root, 'configure', '--explicit-target' ), 0,
|
|
'outside-link fixture configures systemd mode' );
|
|
is( run_state( $outside_disabled_root, 'commit-systemd' ), 0,
|
|
'outside-link systemd state commits' );
|
|
set_systemd_state( $outside_disabled_root, 'disabled' );
|
|
run_update_rc( $outside_disabled_root, '-f', 'xcatd', 'remove' );
|
|
my $outside_disabled_link = File::Spec->catfile(
|
|
$outside_disabled_root, 'etc', 'rc2.d', 'K42xcatd'
|
|
);
|
|
make_path( File::Spec->catdir( $outside_disabled_root, 'etc', 'rc2.d' ) );
|
|
symlink( '../init.d/xcatd', $outside_disabled_link )
|
|
or die "Unable to stage rejected disabled registration: $!";
|
|
set_init_target( $outside_disabled_root, 'upstart' );
|
|
my $disable_failure = File::Spec->catfile(
|
|
$outside_disabled_root, 'fail-update-rc-disable'
|
|
);
|
|
write_file( $disable_failure, "fail rebuilt disable\n", 0644 );
|
|
is( run_state( $outside_disabled_root, 'configure-legacy', 'upgrade' ), 9,
|
|
'a failed rebuilt disable propagates from restoration' );
|
|
is( state_value( $outside_disabled_root, 'mode' ), 'transition-legacy',
|
|
'a failed rebuilt disable leaves retryable transition state' );
|
|
ok( -e File::Spec->catfile( state_dir($outside_disabled_root), 'xcatd' ),
|
|
'a failed rebuilt disable retains the durable script stash' );
|
|
unlink($disable_failure)
|
|
or die "Unable to clear rebuilt-disable failure: $!";
|
|
is( run_state( $outside_disabled_root, 'configure-legacy', 'upgrade' ), 0,
|
|
'a rejected outside-runlevel layout is rebuilt during restoration' );
|
|
ok( !-e $outside_disabled_link,
|
|
'the unusable outside-runlevel link is removed' );
|
|
is( registration_link_count($outside_disabled_root), 3,
|
|
'the rejected layout becomes a complete registered-disabled layout' );
|
|
ok( -l rc_kill_link($outside_disabled_root),
|
|
'the rebuilt layout contains a declared-runlevel kill link' );
|
|
is( state_value( $outside_disabled_root, 'enabled' ), 'no',
|
|
'the rebuilt layout retains registered-disabled state' );
|
|
|
|
my $unregistered_root = stage_root();
|
|
set_init_target( $unregistered_root, 'upstart' );
|
|
is( run_state( $unregistered_root, 'configure-legacy', 'fresh' ), 0,
|
|
'unregistered fixture starts in legacy mode' );
|
|
run_update_rc( $unregistered_root, '-f', 'xcatd', 'remove' );
|
|
write_file( live_init($unregistered_root), "unregistered customization\n", 0755 );
|
|
is( run_state( $unregistered_root, 'prepare-systemd', 'upgrade' ), 0,
|
|
'unregistered legacy fixture prepares for systemd' );
|
|
is( state_value( $unregistered_root, 'enabled' ), 'unregistered',
|
|
'an absent SysV registration is distinct from registered-disabled' );
|
|
set_init_target( $unregistered_root, '../lib/systemd/systemd' );
|
|
is( run_compat( $unregistered_root, 'configure', '--explicit-target' ), 0,
|
|
'unregistered systemd fixture removes the live script' );
|
|
is( run_state( $unregistered_root, 'commit-systemd' ), 0,
|
|
'unregistered systemd state commits' );
|
|
set_systemd_state( $unregistered_root, 'disabled' );
|
|
is( run_state( $unregistered_root, 'prepare-systemd', 'upgrade' ), 0,
|
|
'unregistered state survives a same-systemd upgrade preparation' );
|
|
is( state_value( $unregistered_root, 'enabled' ), 'unregistered',
|
|
'same-systemd disablement does not invent registered-disabled provenance' );
|
|
is( run_state( $unregistered_root, 'commit-systemd' ), 0,
|
|
'unregistered same-systemd upgrade commits' );
|
|
set_init_target( $unregistered_root, 'upstart' );
|
|
is( run_state( $unregistered_root, 'configure-legacy', 'upgrade' ), 0,
|
|
'unregistered systemd fixture returns to legacy' );
|
|
is( read_file( live_init($unregistered_root) ),
|
|
"unregistered customization\n",
|
|
'unregistered customization is restored' );
|
|
is( registration_link_count($unregistered_root), 0,
|
|
'unregistered state returns without start or kill links' );
|
|
is( state_value( $unregistered_root, 'enabled' ), 'unregistered',
|
|
'completed state retains unregistered metadata' );
|
|
|
|
my $rpm_layout_root = stage_root();
|
|
set_init_target( $rpm_layout_root, 'upstart' );
|
|
is( run_state( $rpm_layout_root, 'configure-legacy', 'fresh' ), 0,
|
|
'RPM-layout fixture starts in legacy mode' );
|
|
run_update_rc( $rpm_layout_root, '-f', 'xcatd', 'remove' );
|
|
my $rpm_runlevel_dir =
|
|
File::Spec->catdir( $rpm_layout_root, 'etc', 'rc.d', 'rc3.d' );
|
|
make_path($rpm_runlevel_dir);
|
|
symlink( '/etc/init.d/xcatd',
|
|
File::Spec->catfile( $rpm_runlevel_dir, 'S85xcatd' ) )
|
|
or die "Unable to stage RPM-only registration: $!";
|
|
is( run_state( $rpm_layout_root, 'prepare-systemd', 'upgrade' ), 0,
|
|
'RPM-only registration prepares for a Debian systemd transition' );
|
|
is( state_value( $rpm_layout_root, 'enabled' ), 'unregistered',
|
|
'RPM-only links do not become Debian SysV enablement evidence' );
|
|
|
|
my $missing_update_rc_root = stage_root();
|
|
unlink( File::Spec->catfile(
|
|
$missing_update_rc_root, 'test-bin', 'update-rc.d'
|
|
) )
|
|
or die "Unable to remove fake update-rc.d: $!";
|
|
set_init_target( $missing_update_rc_root, 'upstart' );
|
|
is( run_state( $missing_update_rc_root, 'configure-legacy', 'fresh' ), 0,
|
|
'fresh legacy configuration tolerates a missing update-rc.d' );
|
|
ok( -x live_init($missing_update_rc_root),
|
|
'missing registration tooling does not prevent script materialization' );
|
|
is( registration_link_count($missing_update_rc_root), 0,
|
|
'missing registration tooling leaves runlevel links untouched' );
|
|
|
|
my $failed_stash_root = stage_root();
|
|
set_init_target( $failed_stash_root, 'upstart' );
|
|
is( run_state( $failed_stash_root, 'configure-legacy', 'fresh' ), 0,
|
|
'failed-stash fixture starts in legacy mode' );
|
|
write_file( live_init($failed_stash_root), "failed-stash customization\n", 0755 );
|
|
write_file( File::Spec->catfile( $failed_stash_root, 'fail-stash-move' ),
|
|
"fail\n" );
|
|
isnt( run_state( $failed_stash_root, 'prepare-systemd', 'upgrade' ), 0,
|
|
'a failed atomic stash move is reported' );
|
|
my @failed_stash_temps = glob( File::Spec->catfile(
|
|
state_dir($failed_stash_root), 'xcatd.tmp.*'
|
|
) );
|
|
is( scalar @failed_stash_temps, 0,
|
|
'a failed atomic stash move removes its temporary file' );
|
|
is( read_file( live_init($failed_stash_root) ),
|
|
"failed-stash customization\n",
|
|
'a failed stash leaves the live customization untouched' );
|
|
is( state_value( $failed_stash_root, 'mode' ), 'legacy',
|
|
'a failed stash does not advance durable transition state' );
|
|
|
|
my $missing_helper_root = stage_root();
|
|
unlink( staged_compat_helper($missing_helper_root) )
|
|
or die "Unable to remove staged compatibility helper: $!";
|
|
isnt( run_state( $missing_helper_root, 'prepare-systemd', 'fresh' ), 0,
|
|
'fresh systemd preparation fails without the shared detector' );
|
|
ok( !-e File::Spec->catfile( state_dir($missing_helper_root), 'state' ),
|
|
'missing shared detection cannot advance durable systemd state' );
|
|
ok( !-e live_init($missing_helper_root),
|
|
'missing shared detection cannot materialize legacy content' );
|
|
|
|
my $failing_helper_root = stage_root();
|
|
write_file( staged_compat_helper($failing_helper_root),
|
|
"#!/bin/sh\nexit 7\n", 0755 );
|
|
set_init_target( $failing_helper_root, 'upstart' );
|
|
isnt( run_state( $failing_helper_root, 'configure-legacy', 'fresh' ), 0,
|
|
'fresh legacy configuration propagates shared detector failure' );
|
|
ok( !-e File::Spec->catfile( state_dir($failing_helper_root), 'state' ),
|
|
'failing shared detection cannot advance durable legacy state' );
|
|
ok( !-e live_init($failing_helper_root),
|
|
'failing shared detection cannot materialize the init script' );
|
|
is( registration_link_count($failing_helper_root), 0,
|
|
'failing shared detection cannot create registration links' );
|
|
|
|
my $malformed_systemd_helper_root = stage_root();
|
|
write_file( staged_compat_helper($malformed_systemd_helper_root),
|
|
"#!/bin/sh\nprintf '%s\\n' malformed\n", 0755 );
|
|
isnt( run_state( $malformed_systemd_helper_root, 'prepare-systemd', 'fresh' ),
|
|
0, 'successful malformed systemd detection fails closed' );
|
|
ok( !-e File::Spec->catfile(
|
|
state_dir($malformed_systemd_helper_root), 'state'
|
|
),
|
|
'malformed systemd detection cannot advance durable state' );
|
|
ok( !-e live_init($malformed_systemd_helper_root),
|
|
'malformed systemd detection cannot materialize legacy content' );
|
|
is( registration_link_count($malformed_systemd_helper_root), 0,
|
|
'malformed systemd detection cannot create registration links' );
|
|
|
|
my $malformed_legacy_helper_root = stage_root();
|
|
set_init_target( $malformed_legacy_helper_root, 'upstart' );
|
|
is( run_state( $malformed_legacy_helper_root, 'configure-legacy', 'fresh' ), 0,
|
|
'malformed legacy fixture starts in stable legacy mode' );
|
|
write_file( live_init($malformed_legacy_helper_root),
|
|
"malformed helper survivor\n", 0755 );
|
|
my $legacy_state_before = read_file( File::Spec->catfile(
|
|
state_dir($malformed_legacy_helper_root), 'state'
|
|
) );
|
|
my $legacy_content_before = read_file( live_init($malformed_legacy_helper_root) );
|
|
my $legacy_links_before = registration_link_count($malformed_legacy_helper_root);
|
|
write_file( staged_compat_helper($malformed_legacy_helper_root), <<'SH', 0755 );
|
|
#!/bin/sh
|
|
case "${1:-}" in
|
|
systemd-state) printf '%s\n' disabled ;;
|
|
debian-legacy-state) printf '%s\n' malformed ;;
|
|
*) exit 7 ;;
|
|
esac
|
|
SH
|
|
isnt( run_state( $malformed_legacy_helper_root, 'configure-legacy', 'upgrade' ),
|
|
0, 'successful malformed legacy detection fails closed' );
|
|
is( read_file( File::Spec->catfile(
|
|
state_dir($malformed_legacy_helper_root), 'state'
|
|
) ),
|
|
$legacy_state_before, 'malformed legacy detection preserves durable state' );
|
|
is( read_file( live_init($malformed_legacy_helper_root) ),
|
|
$legacy_content_before, 'malformed legacy detection preserves live content' );
|
|
is( registration_link_count($malformed_legacy_helper_root),
|
|
$legacy_links_before, 'malformed legacy detection preserves registration' );
|
|
|
|
my $legacy_reinstall_root = stage_root();
|
|
set_init_target( $legacy_reinstall_root, 'upstart' );
|
|
is( run_state( $legacy_reinstall_root, 'configure-legacy', 'fresh' ), 0,
|
|
'legacy reinstall fixture starts enabled' );
|
|
run_update_rc( $legacy_reinstall_root, '-f', 'xcatd', 'remove' );
|
|
is( run_state( $legacy_reinstall_root, 'configure-legacy', 'fresh' ), 0,
|
|
'legacy remove and reinstall configures as fresh' );
|
|
ok( -l rc_link($legacy_reinstall_root),
|
|
'legacy remove and reinstall restores default enablement' );
|
|
|
|
my $masked_transition_root = stage_root();
|
|
set_init_target( $masked_transition_root, 'upstart' );
|
|
is( run_state( $masked_transition_root, 'configure-legacy', 'fresh' ), 0,
|
|
'masked transition fixture starts enabled in legacy mode' );
|
|
my $masked_transition_unit_dir =
|
|
File::Spec->catdir( $masked_transition_root, 'etc', 'systemd', 'system' );
|
|
make_path($masked_transition_unit_dir);
|
|
symlink( '/dev/null',
|
|
File::Spec->catfile( $masked_transition_unit_dir, 'xcatd.service' ) )
|
|
or die "Unable to stage transition mask: $!";
|
|
is( run_state( $masked_transition_root, 'prepare-systemd', 'upgrade' ), 0,
|
|
'enabled legacy state prepares against a systemd mask' );
|
|
is( state_value( $masked_transition_root, 'enabled' ), 'unregistered',
|
|
'an explicit systemd mask overrides legacy registration' );
|
|
is( state_value( $masked_transition_root, 'origin' ), 'systemd',
|
|
'masked transition suppresses postinst enablement mutation' );
|
|
|
|
my $late_mask_root = stage_root();
|
|
set_init_target( $late_mask_root, 'upstart' );
|
|
is( run_state( $late_mask_root, 'configure-legacy', 'fresh' ), 0,
|
|
'late-mask fixture starts enabled in legacy mode' );
|
|
is( run_state( $late_mask_root, 'prepare-systemd', 'upgrade' ), 0,
|
|
'late-mask fixture begins a systemd transition' );
|
|
my $late_mask_unit_dir =
|
|
File::Spec->catdir( $late_mask_root, 'etc', 'systemd', 'system' );
|
|
make_path($late_mask_unit_dir);
|
|
symlink( '/dev/null',
|
|
File::Spec->catfile( $late_mask_unit_dir, 'xcatd.service' ) )
|
|
or die "Unable to stage mask during transition: $!";
|
|
is( run_state( $late_mask_root, 'prepare-systemd', 'upgrade' ), 0,
|
|
'systemd transition retries after a mask is added' );
|
|
is( state_value( $late_mask_root, 'enabled' ), 'unregistered',
|
|
'a newly added mask overrides preserved transition enablement' );
|
|
is( state_value( $late_mask_root, 'origin' ), 'systemd',
|
|
'a newly added mask suppresses retry enablement mutation' );
|
|
|
|
my $fresh_systemd_root = stage_root();
|
|
set_systemd_state( $fresh_systemd_root, 'disabled' );
|
|
is( run_state( $fresh_systemd_root, 'prepare-systemd', 'fresh' ), 0,
|
|
'fresh systemd preparation succeeds for a newly unpacked disabled unit' );
|
|
is( state_value( $fresh_systemd_root, 'enabled' ), 'yes',
|
|
'fresh systemd installs remain enabled by default' );
|
|
is( state_value( $fresh_systemd_root, 'origin' ), 'fresh',
|
|
'fresh systemd state records a fresh transition' );
|
|
|
|
for my $link_case (
|
|
[ 'persistent wants', qw(etc wants) ],
|
|
[ 'persistent requires', qw(etc requires) ],
|
|
[ 'runtime wants', qw(run wants) ],
|
|
[ 'runtime requires', qw(run requires) ],
|
|
) {
|
|
my ( $label, $scope, $relation ) = @{$link_case};
|
|
my $link_root = stage_root();
|
|
my $link_dir = File::Spec->catdir(
|
|
$link_root, $scope, 'systemd', 'system',
|
|
"multi-user.target.$relation"
|
|
);
|
|
make_path($link_dir);
|
|
symlink(
|
|
'/usr/lib/systemd/system/xcatd.service',
|
|
File::Spec->catfile( $link_dir, 'xcatd.service' )
|
|
) or die "Unable to stage $label link: $!";
|
|
set_systemd_state( $link_root, 'unknown' );
|
|
is( run_state( $link_root, 'prepare-systemd', 'upgrade' ), 0,
|
|
"$label enablement prepares successfully" );
|
|
is( state_value( $link_root, 'enabled' ), 'yes',
|
|
"$label enablement is preserved" );
|
|
}
|
|
|
|
my $masked_root = stage_root();
|
|
my $masked_unit_dir = File::Spec->catdir( $masked_root, 'etc', 'systemd', 'system' );
|
|
make_path($masked_unit_dir);
|
|
symlink( '/dev/null', File::Spec->catfile( $masked_unit_dir, 'xcatd.service' ) )
|
|
or die "Unable to stage masked unit: $!";
|
|
make_path( File::Spec->catdir( $masked_root, 'var', 'lib', 'xcat' ) );
|
|
write_file( old_systemd_marker($masked_root), "marker\n" );
|
|
is( run_state( $masked_root, 'prepare-systemd', 'upgrade' ), 0,
|
|
'masked systemd preparation succeeds' );
|
|
is( state_value( $masked_root, 'content' ), 'package-default',
|
|
'an obsolete package conffile marker retains package omission semantics' );
|
|
is( state_value( $masked_root, 'enabled' ), 'unregistered',
|
|
'a masked unit is not treated as boot-enabled' );
|
|
|
|
my $fresh_masked_root = stage_root();
|
|
my $fresh_masked_unit_dir =
|
|
File::Spec->catdir( $fresh_masked_root, 'etc', 'systemd', 'system' );
|
|
make_path($fresh_masked_unit_dir);
|
|
symlink( '/dev/null',
|
|
File::Spec->catfile( $fresh_masked_unit_dir, 'xcatd.service' ) )
|
|
or die "Unable to stage fresh masked unit: $!";
|
|
is( run_state( $fresh_masked_root, 'prepare-systemd', 'fresh' ), 0,
|
|
'fresh masked systemd preparation succeeds' );
|
|
is( state_value( $fresh_masked_root, 'enabled' ), 'unregistered',
|
|
'a pre-existing mask is not overridden on fresh install' );
|
|
|
|
my $runtime_masked_root = stage_root();
|
|
my $runtime_masked_unit_dir =
|
|
File::Spec->catdir( $runtime_masked_root, 'run', 'systemd', 'system' );
|
|
make_path($runtime_masked_unit_dir);
|
|
symlink( '/dev/null',
|
|
File::Spec->catfile( $runtime_masked_unit_dir, 'xcatd.service' ) )
|
|
or die "Unable to stage runtime masked unit: $!";
|
|
set_systemd_state( $runtime_masked_root, 'unknown' );
|
|
is( run_state( $runtime_masked_root, 'prepare-systemd', 'fresh' ), 0,
|
|
'runtime masked systemd preparation succeeds' );
|
|
is( state_value( $runtime_masked_root, 'enabled' ), 'unregistered',
|
|
'a runtime mask is not treated as enabled offline' );
|
|
is( state_value( $runtime_masked_root, 'origin' ), 'systemd',
|
|
'a runtime mask overrides fresh-install enablement' );
|
|
|
|
my $pending_deleted_root = stage_root();
|
|
make_path( state_dir($pending_deleted_root) );
|
|
write_file( File::Spec->catfile( state_dir($pending_deleted_root), 'pending-deleted' ), "\n" );
|
|
write_file( File::Spec->catfile( state_dir($pending_deleted_root), 'pending-enabled' ), "no\n" );
|
|
set_systemd_state( $pending_deleted_root, 'enabled' );
|
|
is( run_state( $pending_deleted_root, 'prepare-systemd', 'upgrade' ), 0,
|
|
'explicit preinst deletion evidence prepares successfully' );
|
|
is( state_value( $pending_deleted_root, 'content' ), 'deleted',
|
|
'a still-owned missing conffile remains an administrator deletion' );
|
|
is( state_value( $pending_deleted_root, 'enabled' ), 'yes',
|
|
'existing native-systemd enablement survives legacy deletion evidence' );
|
|
is( state_value( $pending_deleted_root, 'origin' ), 'legacy',
|
|
'explicit conffile deletion retains legacy provenance' );
|
|
|
|
my $pending_enabled_root = stage_root();
|
|
make_path( state_dir($pending_enabled_root) );
|
|
write_file(
|
|
File::Spec->catfile( state_dir($pending_enabled_root), 'pending-xcatd' ),
|
|
"legacy content\n", 0755
|
|
);
|
|
write_file(
|
|
File::Spec->catfile( state_dir($pending_enabled_root), 'pending-enabled' ),
|
|
"yes\n"
|
|
);
|
|
set_systemd_state( $pending_enabled_root, 'disabled' );
|
|
is( run_state( $pending_enabled_root, 'prepare-systemd', 'upgrade' ), 0,
|
|
'enabled legacy evidence prepares with disabled systemd state' );
|
|
is( state_value( $pending_enabled_root, 'enabled' ), 'yes',
|
|
'first migration preserves enablement from either service manager' );
|
|
|
|
my $both_disabled_root = stage_root();
|
|
make_path( state_dir($both_disabled_root) );
|
|
write_file(
|
|
File::Spec->catfile( state_dir($both_disabled_root), 'pending-xcatd' ),
|
|
"legacy content\n", 0755
|
|
);
|
|
write_file(
|
|
File::Spec->catfile( state_dir($both_disabled_root), 'pending-enabled' ),
|
|
"no\n"
|
|
);
|
|
set_systemd_state( $both_disabled_root, 'disabled' );
|
|
is( run_state( $both_disabled_root, 'prepare-systemd', 'upgrade' ), 0,
|
|
'fully disabled first migration prepares successfully' );
|
|
is( state_value( $both_disabled_root, 'enabled' ), 'no',
|
|
'first migration remains disabled when both managers are disabled' );
|
|
|
|
my $masked_pending_root = stage_root();
|
|
make_path(
|
|
state_dir($masked_pending_root),
|
|
File::Spec->catdir( $masked_pending_root, 'etc', 'systemd', 'system' )
|
|
);
|
|
write_file(
|
|
File::Spec->catfile( state_dir($masked_pending_root), 'pending-xcatd' ),
|
|
"legacy content\n", 0755
|
|
);
|
|
write_file(
|
|
File::Spec->catfile( state_dir($masked_pending_root), 'pending-enabled' ),
|
|
"yes\n"
|
|
);
|
|
symlink( '/dev/null',
|
|
File::Spec->catfile( $masked_pending_root, 'etc', 'systemd', 'system',
|
|
'xcatd.service' ) )
|
|
or die "Unable to stage masked first migration: $!";
|
|
is( run_state( $masked_pending_root, 'prepare-systemd', 'upgrade' ), 0,
|
|
'masked first migration prepares successfully' );
|
|
is( state_value( $masked_pending_root, 'enabled' ), 'unregistered',
|
|
'an explicit systemd mask wins over stale legacy enablement' );
|
|
|
|
my $obsolete_disabled_root = stage_root();
|
|
make_path( File::Spec->catdir( $obsolete_disabled_root, 'var', 'lib', 'xcat' ) );
|
|
write_file( old_systemd_marker($obsolete_disabled_root), "marker\n" );
|
|
set_systemd_state( $obsolete_disabled_root, 'disabled' );
|
|
is( run_state( $obsolete_disabled_root, 'prepare-systemd', 'upgrade' ), 0,
|
|
'disabled obsolete-conffile migration prepares successfully' );
|
|
is( state_value( $obsolete_disabled_root, 'content' ), 'package-default',
|
|
'package-driven omission is not confused with administrator deletion' );
|
|
is( state_value( $obsolete_disabled_root, 'enabled' ), 'no',
|
|
'disabled markerless systemd state remains disabled' );
|
|
is( run_state( $obsolete_disabled_root, 'commit-systemd' ), 0,
|
|
'disabled markerless systemd state commits' );
|
|
set_init_target( $obsolete_disabled_root, 'upstart' );
|
|
is( run_state( $obsolete_disabled_root, 'configure-legacy', 'upgrade' ), 0,
|
|
'disabled package omission returns to legacy mode' );
|
|
ok( -x live_init($obsolete_disabled_root),
|
|
'package omission restores the packaged legacy script' );
|
|
ok( !-e rc_link($obsolete_disabled_root),
|
|
'disabled package omission remains off under SysV' );
|
|
ok( -l rc_kill_link($obsolete_disabled_root),
|
|
'native systemd disablement maps to registered-disabled SysV state' );
|
|
|
|
my $same_systemd_root = stage_root();
|
|
set_systemd_state( $same_systemd_root, 'disabled' );
|
|
is( run_state( $same_systemd_root, 'prepare-systemd', 'fresh' ), 0,
|
|
'same-systemd fixture prepares as fresh' );
|
|
is( run_state( $same_systemd_root, 'commit-systemd' ), 0,
|
|
'same-systemd fixture commits its initial state' );
|
|
set_systemd_state( $same_systemd_root, 'enabled' );
|
|
is( run_state( $same_systemd_root, 'prepare-systemd', 'upgrade' ), 0,
|
|
'same-systemd upgrade preparation succeeds' );
|
|
is( state_value( $same_systemd_root, 'origin' ), 'systemd',
|
|
'same-systemd upgrades are distinguishable from mode changes' );
|
|
|
|
my $reinstall_state_root = stage_root();
|
|
set_init_target( $reinstall_state_root, 'upstart' );
|
|
is( run_state( $reinstall_state_root, 'configure-legacy', 'fresh' ), 0,
|
|
'reinstall-state fixture starts in legacy mode' );
|
|
write_file( live_init($reinstall_state_root), "reinstall customization\n", 0755 );
|
|
is( run_state( $reinstall_state_root, 'prepare-systemd', 'upgrade' ), 0,
|
|
'reinstall-state fixture stashes its customization' );
|
|
set_init_target( $reinstall_state_root, '../lib/systemd/systemd' );
|
|
is( run_compat( $reinstall_state_root, 'configure', '--explicit-target' ), 0,
|
|
'reinstall-state fixture enters systemd mode' );
|
|
is( run_state( $reinstall_state_root, 'commit-systemd' ), 0,
|
|
'reinstall-state fixture commits durable systemd state' );
|
|
write_file(
|
|
File::Spec->catfile( state_dir($reinstall_state_root), 'pending-deleted' ),
|
|
"stale\n"
|
|
);
|
|
write_file(
|
|
File::Spec->catfile( state_dir($reinstall_state_root), 'pending-enabled' ),
|
|
"no\n"
|
|
);
|
|
set_systemd_state( $reinstall_state_root, 'disabled' );
|
|
is( run_state( $reinstall_state_root, 'prepare-systemd', 'fresh' ), 0,
|
|
'remove and reinstall preparation uses durable state' );
|
|
is( state_value( $reinstall_state_root, 'content' ), 'stashed',
|
|
'stale fallback deletion evidence cannot replace a durable stash' );
|
|
is( state_value( $reinstall_state_root, 'enabled' ), 'yes',
|
|
'a genuine reinstall restores the default enabled state' );
|
|
is( read_file( File::Spec->catfile( state_dir($reinstall_state_root), 'xcatd' ) ),
|
|
"reinstall customization\n",
|
|
'remove and reinstall preserves the exact administrator customization' );
|
|
|
|
my $stale_pending_root = stage_root();
|
|
set_init_target( $stale_pending_root, 'upstart' );
|
|
is( run_state( $stale_pending_root, 'configure-legacy', 'fresh' ), 0,
|
|
'stale-pending fixture starts in legacy mode' );
|
|
write_file( live_init($stale_pending_root), "current live content\n", 0755 );
|
|
write_file(
|
|
File::Spec->catfile( state_dir($stale_pending_root), 'pending-xcatd' ),
|
|
"stale pending content\n", 0755
|
|
);
|
|
is( run_state( $stale_pending_root, 'prepare-systemd', 'upgrade' ), 0,
|
|
'stable legacy state ignores stale pending evidence' );
|
|
is( read_file( File::Spec->catfile( state_dir($stale_pending_root), 'xcatd' ) ),
|
|
"current live content\n",
|
|
'current live content wins over stale pending content' );
|
|
ok( !-e File::Spec->catfile( state_dir($stale_pending_root), 'pending-xcatd' ),
|
|
'successful systemd preparation clears stale pending evidence' );
|
|
|
|
my $pending_cleanup_root = stage_root();
|
|
make_path( state_dir($pending_cleanup_root) );
|
|
for my $pending_name (qw(pending-xcatd pending-deleted pending-enabled)) {
|
|
write_file(
|
|
File::Spec->catfile( state_dir($pending_cleanup_root), $pending_name ),
|
|
"stale\n", 0755
|
|
);
|
|
}
|
|
set_init_target( $pending_cleanup_root, 'upstart' );
|
|
is( run_state( $pending_cleanup_root, 'configure-legacy', 'fresh' ), 0,
|
|
'legacy convergence succeeds with stale pending evidence' );
|
|
for my $pending_name (qw(pending-xcatd pending-deleted pending-enabled)) {
|
|
ok( !-e File::Spec->catfile( state_dir($pending_cleanup_root), $pending_name ),
|
|
"legacy convergence clears $pending_name" );
|
|
}
|
|
|
|
my $legacy_retry_root = stage_root();
|
|
set_init_target( $legacy_retry_root, 'upstart' );
|
|
is( run_state( $legacy_retry_root, 'configure-legacy', 'fresh' ), 0,
|
|
'same-mode retry fixture starts in legacy mode' );
|
|
run_update_rc( $legacy_retry_root, 'xcatd', 'disable' );
|
|
is( run_state( $legacy_retry_root, 'configure-legacy', 'upgrade' ), 0,
|
|
'disabled same-mode state is recorded' );
|
|
set_systemd_state( $legacy_retry_root, 'enabled' );
|
|
write_file( live_init($legacy_retry_root), "same-mode original\n", 0755 );
|
|
write_file( File::Spec->catfile( $legacy_retry_root, 'mutate-ucf' ), "mutate\n" );
|
|
write_file( File::Spec->catfile( $legacy_retry_root, 'fail-ucfr' ), "fail\n" );
|
|
isnt( run_state( $legacy_retry_root, 'configure-legacy', 'upgrade' ), 0,
|
|
'same-mode failure after UCF mutation is reported' );
|
|
is( state_value( $legacy_retry_root, 'origin' ), 'legacy',
|
|
'failed same-mode configuration retains its legacy origin' );
|
|
is( read_file( File::Spec->catfile( state_dir($legacy_retry_root), 'xcatd' ) ),
|
|
"same-mode original\n",
|
|
'same-mode failure retains an exact transactional stash' );
|
|
unlink( File::Spec->catfile( $legacy_retry_root, 'mutate-ucf' ) )
|
|
or die "Unable to clear same-mode UCF mutation: $!";
|
|
unlink( File::Spec->catfile( $legacy_retry_root, 'fail-ucfr' ) )
|
|
or die "Unable to clear same-mode ucfr failure: $!";
|
|
is( run_state( $legacy_retry_root, 'configure-legacy', 'upgrade' ), 0,
|
|
'same-mode legacy retry succeeds' );
|
|
is( read_file( live_init($legacy_retry_root) ), "same-mode original\n",
|
|
'same-mode retry restores exact pre-failure content' );
|
|
ok( !-e rc_link($legacy_retry_root),
|
|
'same-mode retry does not import stale systemd enablement' );
|
|
|
|
my $admin_disabled_retry_root = stage_root();
|
|
set_init_target( $admin_disabled_retry_root, 'upstart' );
|
|
is( run_state( $admin_disabled_retry_root, 'configure-legacy', 'fresh' ), 0,
|
|
'admin-disabled retry fixture starts enabled' );
|
|
run_update_rc( $admin_disabled_retry_root, 'xcatd', 'disable' );
|
|
write_file(
|
|
File::Spec->catfile( $admin_disabled_retry_root, 'fail-ucfr' ),
|
|
"fail\n"
|
|
);
|
|
isnt( run_state( $admin_disabled_retry_root, 'configure-legacy', 'upgrade' ), 0,
|
|
'same-mode upgrade fails after current disablement is sampled' );
|
|
is( state_value( $admin_disabled_retry_root, 'enabled' ), 'no',
|
|
'failed same-mode upgrade records current disabled rc state' );
|
|
unlink( File::Spec->catfile( $admin_disabled_retry_root, 'fail-ucfr' ) )
|
|
or die "Unable to clear admin-disabled retry failure: $!";
|
|
is( run_state( $admin_disabled_retry_root, 'configure-legacy', 'upgrade' ), 0,
|
|
'admin-disabled same-mode upgrade retries' );
|
|
ok( !-e rc_link($admin_disabled_retry_root),
|
|
'retry does not undo a current administrator disablement' );
|
|
|
|
my $admin_enabled_retry_root = stage_root();
|
|
set_init_target( $admin_enabled_retry_root, 'upstart' );
|
|
is( run_state( $admin_enabled_retry_root, 'configure-legacy', 'fresh' ), 0,
|
|
'admin-enabled retry fixture starts enabled' );
|
|
run_update_rc( $admin_enabled_retry_root, 'xcatd', 'disable' );
|
|
is( run_state( $admin_enabled_retry_root, 'configure-legacy', 'upgrade' ), 0,
|
|
'disabled baseline is recorded' );
|
|
make_path( File::Spec->catdir( $admin_enabled_retry_root, 'etc', 'rc2.d' ) );
|
|
symlink( '../init.d/xcatd', rc_link($admin_enabled_retry_root) )
|
|
or die "Unable to stage current administrator enablement: $!";
|
|
write_file(
|
|
File::Spec->catfile( $admin_enabled_retry_root, 'fail-ucfr' ),
|
|
"fail\n"
|
|
);
|
|
isnt( run_state( $admin_enabled_retry_root, 'configure-legacy', 'upgrade' ), 0,
|
|
'same-mode upgrade fails after current enablement is sampled' );
|
|
is( state_value( $admin_enabled_retry_root, 'enabled' ), 'yes',
|
|
'failed same-mode upgrade records current enabled rc state' );
|
|
unlink( File::Spec->catfile( $admin_enabled_retry_root, 'fail-ucfr' ) )
|
|
or die "Unable to clear admin-enabled retry failure: $!";
|
|
is( run_state( $admin_enabled_retry_root, 'configure-legacy', 'upgrade' ), 0,
|
|
'admin-enabled same-mode upgrade retries' );
|
|
ok( -l rc_link($admin_enabled_retry_root),
|
|
'retry does not undo a current administrator enablement' );
|
|
|
|
my $markerless_enabled_root = stage_root();
|
|
set_systemd_state( $markerless_enabled_root, 'enabled' );
|
|
set_init_target( $markerless_enabled_root, 'upstart' );
|
|
is( run_state( $markerless_enabled_root, 'configure-legacy', 'upgrade' ), 0,
|
|
'enabled markerless migration succeeds' );
|
|
ok( -x live_init($markerless_enabled_root),
|
|
'strong systemd enablement evidence restores the packaged script' );
|
|
ok( -l rc_link($markerless_enabled_root),
|
|
'strong systemd enablement evidence restores SysV registration' );
|
|
|
|
my $markerless_unknown_root = stage_root();
|
|
set_systemd_state( $markerless_unknown_root, 'unknown' );
|
|
set_init_target( $markerless_unknown_root, 'upstart' );
|
|
is( run_state( $markerless_unknown_root, 'configure-legacy', 'upgrade' ), 0,
|
|
'ambiguous markerless migration converges safely' );
|
|
ok( !-e live_init($markerless_unknown_root),
|
|
'ambiguous absence is not silently resurrected' );
|
|
ok( !-e rc_link($markerless_unknown_root),
|
|
'ambiguous enablement defaults to disabled' );
|
|
|
|
my $legacy_target_obsolete_root = stage_root();
|
|
make_path( File::Spec->catdir( $legacy_target_obsolete_root, 'var', 'lib', 'xcat' ) );
|
|
write_file( old_systemd_marker($legacy_target_obsolete_root), "marker\n" );
|
|
set_systemd_state( $legacy_target_obsolete_root, 'disabled' );
|
|
set_init_target( $legacy_target_obsolete_root, 'upstart' );
|
|
is( run_state( $legacy_target_obsolete_root, 'configure-legacy', 'upgrade' ), 0,
|
|
'obsolete conffile migrates directly to a legacy target' );
|
|
ok( -x live_init($legacy_target_obsolete_root),
|
|
'direct legacy migration restores package-driven omission' );
|
|
ok( !-e rc_link($legacy_target_obsolete_root),
|
|
'direct legacy migration preserves disabled systemd state' );
|
|
ok( -l rc_kill_link($legacy_target_obsolete_root),
|
|
'direct legacy migration records disabled systemd state with kill links' );
|
|
|
|
my $legacy_target_live_root = stage_root();
|
|
make_path( File::Spec->catdir( $legacy_target_live_root, 'etc', 'init.d' ) );
|
|
write_file( live_init($legacy_target_live_root), "live first-migration content\n", 0755 );
|
|
set_systemd_state( $legacy_target_live_root, 'enabled' );
|
|
set_init_target( $legacy_target_live_root, 'upstart' );
|
|
is( run_state( $legacy_target_live_root, 'configure-legacy', 'upgrade' ), 0,
|
|
'live conffile migrates directly to a legacy target' );
|
|
is( read_file( live_init($legacy_target_live_root) ),
|
|
"live first-migration content\n",
|
|
'direct legacy migration preserves live administrator content' );
|
|
ok( -l rc_link($legacy_target_live_root),
|
|
'native systemd enablement maps to SysV on first legacy migration' );
|
|
|
|
my $legacy_target_deleted_root = stage_root();
|
|
make_path( state_dir($legacy_target_deleted_root) );
|
|
write_file(
|
|
File::Spec->catfile( state_dir($legacy_target_deleted_root), 'pending-deleted' ),
|
|
"deleted\n"
|
|
);
|
|
set_systemd_state( $legacy_target_deleted_root, 'enabled' );
|
|
set_init_target( $legacy_target_deleted_root, 'upstart' );
|
|
is( run_state( $legacy_target_deleted_root, 'configure-legacy', 'upgrade' ), 0,
|
|
'deleted conffile migrates directly to a legacy target' );
|
|
ok( !-e live_init($legacy_target_deleted_root),
|
|
'direct legacy migration preserves administrator deletion' );
|
|
ok( !-e rc_link($legacy_target_deleted_root),
|
|
'deleted conffile is never registered under SysV' );
|
|
is( registration_link_count($legacy_target_deleted_root), 0,
|
|
'deleted conffile gains neither start nor kill links' );
|
|
|
|
my $backup_root = stage_root();
|
|
make_path( File::Spec->catdir( $backup_root, 'etc', 'init.d' ) );
|
|
write_file( live_init($backup_root) . '.dpkg-bak', "recoverable customization\n", 0755 );
|
|
set_systemd_state( $backup_root, 'disabled' );
|
|
set_init_target( $backup_root, 'upstart' );
|
|
is( run_state( $backup_root, 'configure-legacy', 'upgrade' ), 0,
|
|
'dpkg backup migration succeeds' );
|
|
is( read_file( live_init($backup_root) ), "recoverable customization\n",
|
|
'a dpkg conffile backup is recovered through ucf' );
|
|
|
|
my $origin_reversal_root = stage_root();
|
|
set_init_target( $origin_reversal_root, 'upstart' );
|
|
is( run_state( $origin_reversal_root, 'configure-legacy', 'fresh' ), 0,
|
|
'origin-reversal fixture starts in legacy mode' );
|
|
write_file( live_init($origin_reversal_root), "origin customization\n", 0755 );
|
|
is( run_state( $origin_reversal_root, 'prepare-systemd', 'upgrade' ), 0,
|
|
'origin-reversal fixture begins a systemd transition' );
|
|
is( state_value( $origin_reversal_root, 'origin' ), 'legacy',
|
|
'legacy origin is recorded before systemd enablement runs' );
|
|
write_file( File::Spec->catfile( $origin_reversal_root, 'mutate-ucf' ), "mutate\n" );
|
|
write_file( File::Spec->catfile( $origin_reversal_root, 'fail-ucfr' ), "fail\n" );
|
|
isnt( run_state( $origin_reversal_root, 'configure-legacy', 'upgrade' ), 0,
|
|
'reversed pre-enable transition can fail during legacy restoration' );
|
|
is( state_value( $origin_reversal_root, 'origin' ), 'legacy',
|
|
'failed reversal preserves the original legacy provenance' );
|
|
set_init_target( $origin_reversal_root, '../lib/systemd/systemd' );
|
|
is( run_state( $origin_reversal_root, 'prepare-systemd', 'upgrade' ), 0,
|
|
'failed reversal can return toward systemd' );
|
|
is( state_value( $origin_reversal_root, 'origin' ), 'legacy',
|
|
'second systemd attempt still requires legacy-origin enablement' );
|
|
is( state_value( $origin_reversal_root, 'enabled' ), 'yes',
|
|
'second systemd attempt retains intended enablement' );
|
|
|
|
my $registration_retry_root = stage_root();
|
|
set_init_target( $registration_retry_root, 'upstart' );
|
|
is( run_state( $registration_retry_root, 'configure-legacy', 'fresh' ), 0,
|
|
'registration retry fixture starts enabled in legacy mode' );
|
|
is( run_state( $registration_retry_root, 'prepare-systemd', 'upgrade' ), 0,
|
|
'registration retry fixture begins a systemd transition' );
|
|
run_update_rc( $registration_retry_root, '-f', 'xcatd', 'remove' );
|
|
write_file( File::Spec->catfile( $registration_retry_root, 'fail-ucfr' ), "fail\n" );
|
|
isnt( run_state( $registration_retry_root, 'configure-legacy', 'upgrade' ), 0,
|
|
'legacy reversal fails before registration is restored' );
|
|
unlink( File::Spec->catfile( $registration_retry_root, 'fail-ucfr' ) )
|
|
or die "Unable to clear registration retry failure: $!";
|
|
is( run_state( $registration_retry_root, 'configure-legacy', 'upgrade' ), 0,
|
|
'legacy reversal retries successfully' );
|
|
ok( -l rc_link($registration_retry_root),
|
|
'transition retry restores missing SysV registration' );
|
|
|
|
my $retry_root = stage_root();
|
|
set_init_target( $retry_root, 'upstart' );
|
|
is( run_state( $retry_root, 'configure-legacy', 'fresh' ), 0,
|
|
'retry fixture starts in legacy mode' );
|
|
write_file( live_init($retry_root), "retry customization\n", 0755 );
|
|
is( run_state( $retry_root, 'prepare-systemd', 'upgrade' ), 0,
|
|
'retry fixture is durably stashed' );
|
|
set_init_target( $retry_root, '../lib/systemd/systemd' );
|
|
is( run_compat( $retry_root, 'configure', '--explicit-target' ), 0,
|
|
'retry fixture enters systemd mode' );
|
|
is( run_state( $retry_root, 'commit-systemd' ), 0,
|
|
'retry fixture commits systemd state' );
|
|
set_systemd_state( $retry_root, 'enabled' );
|
|
set_init_target( $retry_root, 'upstart' );
|
|
write_file( File::Spec->catfile( $retry_root, 'mutate-ucf' ), "mutate\n" );
|
|
write_file( File::Spec->catfile( $retry_root, 'fail-ucfr' ), "fail\n" );
|
|
isnt( run_state( $retry_root, 'configure-legacy', 'upgrade' ), 0,
|
|
'a failure after ucf mutation reports failure' );
|
|
ok( -e File::Spec->catfile( state_dir($retry_root), 'xcatd' ),
|
|
'failed restoration retains the durable stash' );
|
|
is( read_file( File::Spec->catfile( state_dir($retry_root), 'xcatd' ) ),
|
|
"retry customization\n",
|
|
'partial restoration cannot overwrite the original stash' );
|
|
is( state_value( $retry_root, 'mode' ), 'transition-legacy',
|
|
'failed restoration remains explicitly transitional' );
|
|
unlink( File::Spec->catfile( $retry_root, 'mutate-ucf' ) )
|
|
or die "Unable to clear injected ucf mutation: $!";
|
|
unlink( File::Spec->catfile( $retry_root, 'fail-ucfr' ) )
|
|
or die "Unable to clear injected ucfr failure: $!";
|
|
is( run_state( $retry_root, 'configure-legacy', 'upgrade' ), 0,
|
|
'a repeated configure completes the interrupted transition' );
|
|
is( read_file( live_init($retry_root) ), "retry customization\n",
|
|
'retry preserves the customized content' );
|
|
|
|
my $reversal_retry_root = stage_root();
|
|
set_init_target( $reversal_retry_root, 'upstart' );
|
|
is( run_state( $reversal_retry_root, 'configure-legacy', 'fresh' ), 0,
|
|
'reversal retry fixture starts in legacy mode' );
|
|
write_file( live_init($reversal_retry_root), "reversal customization\n", 0755 );
|
|
is( run_state( $reversal_retry_root, 'prepare-systemd', 'upgrade' ), 0,
|
|
'reversal retry fixture is durably stashed' );
|
|
set_init_target( $reversal_retry_root, '../lib/systemd/systemd' );
|
|
is( run_compat( $reversal_retry_root, 'configure', '--explicit-target' ), 0,
|
|
'reversal retry fixture enters systemd mode' );
|
|
is( run_state( $reversal_retry_root, 'commit-systemd' ), 0,
|
|
'reversal retry fixture commits systemd state' );
|
|
set_systemd_state( $reversal_retry_root, 'enabled' );
|
|
set_init_target( $reversal_retry_root, 'upstart' );
|
|
write_file( File::Spec->catfile( $reversal_retry_root, 'mutate-ucf' ), "mutate\n" );
|
|
write_file( File::Spec->catfile( $reversal_retry_root, 'fail-ucfr' ), "fail\n" );
|
|
isnt( run_state( $reversal_retry_root, 'configure-legacy', 'upgrade' ), 0,
|
|
'reversal retry fixture fails after UCF mutation' );
|
|
set_init_target( $reversal_retry_root, '../lib/systemd/systemd' );
|
|
is( run_state( $reversal_retry_root, 'prepare-systemd', 'upgrade' ), 0,
|
|
'a reversed transition prepares for systemd again' );
|
|
is( read_file(
|
|
File::Spec->catfile( state_dir($reversal_retry_root), 'xcatd' )
|
|
),
|
|
"reversal customization\n",
|
|
'reversing a failed restoration cannot overwrite the durable stash' );
|
|
is( run_compat( $reversal_retry_root, 'configure', '--explicit-target' ), 0,
|
|
'reversed transition removes the partial live script' );
|
|
is( run_state( $reversal_retry_root, 'commit-systemd' ), 0,
|
|
'reversed transition commits systemd state' );
|
|
unlink( File::Spec->catfile( $reversal_retry_root, 'mutate-ucf' ) )
|
|
or die "Unable to clear reversed UCF mutation: $!";
|
|
unlink( File::Spec->catfile( $reversal_retry_root, 'fail-ucfr' ) )
|
|
or die "Unable to clear reversed ucfr failure: $!";
|
|
set_init_target( $reversal_retry_root, 'upstart' );
|
|
is( run_state( $reversal_retry_root, 'configure-legacy', 'upgrade' ), 0,
|
|
'reversed transition can later return to legacy mode' );
|
|
is( read_file( live_init($reversal_retry_root) ), "reversal customization\n",
|
|
'reversed transition eventually restores exact administrator content' );
|
|
|
|
my $default_retry_root = stage_root();
|
|
set_systemd_state( $default_retry_root, 'disabled' );
|
|
is( run_state( $default_retry_root, 'prepare-systemd', 'fresh' ), 0,
|
|
'package-default retry fixture prepares as a fresh systemd install' );
|
|
is( run_state( $default_retry_root, 'commit-systemd' ), 0,
|
|
'package-default retry fixture commits systemd state' );
|
|
set_systemd_state( $default_retry_root, 'enabled' );
|
|
set_init_target( $default_retry_root, 'upstart' );
|
|
write_file( File::Spec->catfile( $default_retry_root, 'mutate-ucf' ), "mutate\n" );
|
|
write_file( File::Spec->catfile( $default_retry_root, 'fail-ucfr' ), "fail\n" );
|
|
isnt( run_state( $default_retry_root, 'configure-legacy', 'upgrade' ), 0,
|
|
'package-default restoration can fail after materialization' );
|
|
is( state_value( $default_retry_root, 'content' ), 'package-default',
|
|
'partial package materialization is not promoted to customization' );
|
|
unlink( File::Spec->catfile( $default_retry_root, 'mutate-ucf' ) )
|
|
or die "Unable to clear package-default UCF mutation: $!";
|
|
unlink( File::Spec->catfile( $default_retry_root, 'fail-ucfr' ) )
|
|
or die "Unable to clear package-default ucfr failure: $!";
|
|
is( run_state( $default_retry_root, 'configure-legacy', 'upgrade' ), 0,
|
|
'package-default restoration retries successfully' );
|
|
is( read_file( live_init($default_retry_root) ), read_file($template),
|
|
'package-default retry rematerializes the exact packaged template' );
|
|
ok( !-e File::Spec->catfile( state_dir($default_retry_root), 'xcatd' ),
|
|
'package-default retry does not create a durable customization stash' );
|
|
|
|
my $format1_root = stage_root();
|
|
make_path( state_dir($format1_root) );
|
|
write_file( File::Spec->catfile( state_dir($format1_root), 'state' ),
|
|
"format=1\nmode=systemd\ncontent=package-default\nenabled=no\norigin=unknown\n" );
|
|
set_systemd_state( $format1_root, 'disabled' );
|
|
set_init_target( $format1_root, '../lib/systemd/systemd' );
|
|
is( run_state( $format1_root, 'prepare-systemd', 'upgrade' ), 0,
|
|
'format-1 disabled state survives a same-systemd upgrade preparation' );
|
|
is( state_value( $format1_root, 'enabled' ), 'unregistered',
|
|
'same-systemd preparation retains recovered format-1 provenance' );
|
|
is( run_state( $format1_root, 'commit-systemd' ), 0,
|
|
'recovered format-1 same-systemd state commits' );
|
|
set_init_target( $format1_root, 'upstart' );
|
|
is( run_state( $format1_root, 'configure-legacy', 'upgrade' ), 0,
|
|
'format-1 disabled state converges safely' );
|
|
is( state_value( $format1_root, 'format' ), '2',
|
|
'format-1 state is rewritten in registration-aware format 2' );
|
|
is( state_value( $format1_root, 'enabled' ), 'unregistered',
|
|
'format-1 no preserves its historical remove-all-links behavior' );
|
|
is( registration_link_count($format1_root), 0,
|
|
'format-1 no does not invent registered-disabled metadata' );
|
|
|
|
my $malformed_root = stage_root();
|
|
make_path( state_dir($malformed_root), File::Spec->catdir( $malformed_root, 'etc', 'init.d' ) );
|
|
write_file( live_init($malformed_root), "must survive\n", 0755 );
|
|
write_file( File::Spec->catfile( state_dir($malformed_root), 'state' ),
|
|
"format=9\nmode=legacy\ncontent=active\nenabled=yes\n" );
|
|
isnt( run_state( $malformed_root, 'prepare-systemd', 'upgrade' ), 0,
|
|
'malformed persistent state fails closed' );
|
|
is( read_file( live_init($malformed_root) ), "must survive\n",
|
|
'malformed state cannot delete the live init script' );
|
|
|
|
write_file( File::Spec->catfile( state_dir($malformed_root), 'state' ),
|
|
"format=1\nmode=legacy\ncontent=active\nenabled=yes\n" );
|
|
isnt( run_state( $malformed_root, 'prepare-systemd', 'upgrade' ), 0,
|
|
'incomplete persistent state fails closed' );
|
|
is( read_file( live_init($malformed_root) ), "must survive\n",
|
|
'incomplete state cannot delete the live init script' );
|
|
|
|
done_testing();
|