CLOS-6911: remove SysV runlevel links that shadow a real unit after the upgrade - #72
CLOS-6911: remove SysV runlevel links that shadow a real unit after the upgrade#72prilr wants to merge 4 commits into
Conversation
Review bodyRequesting changes for one blocking issue: the actor's The fix is a one-token move to the After stage plus a test adjustment, both verified locally on this branch: the 28 existing tests pass before the change, and the AST-based phase test is the only one that has to change. Everything else is green here: unit tests, flake8, and both CI checks on a2f8344. Inline comment:
|
`systemctl start mariadb.service` failed on a freshly converted CloudLinux 9
host while mysqld was demonstrably running and serving queries. The journal
explains it:
systemd-sysv-generator: SysV service '/etc/rc.d/init.d/mysql' lacks a
native systemd unit file. Automatically generating a unit file for
compatibility.
systemd[1]: Starting LSB: start and stop MariaDB...
/etc/rc.d/init.d/mysql: line 276: log_success_msg: command not found
cl-MariaDB103-server ships /etc/rc.d/init.d/mysql in its EL9 build as well as
its EL8 one, and the runlevel links chkconfig created on the source
(/etc/rc.d/rc[2-5].d/S64mysql, rc[016].d/K36mysql) belong to no package - so
nothing removes them during the upgrade. On the target the sysv-generator turns
them back into an LSB compatibility unit that starts mysqld_safe outside
mariadb.service, and the real unit can then never take the datadir. The init
script is itself broken there: log_success_msg came from redhat-lsb-core, which
EL9 does not have.
leapp's systemd state transition cannot see any of this - it works on units,
and these are files under /etc/rc.d/rc*.d.
Verified on the host: removing the seven links and rebooting put MariaDB back
under mariadb.service.
The actor only touches a service that has a native '<name>.service' on the
target to take over; where there is none, the init script is the only way that
service runs and its links are left alone. When a start link existed but the
native unit is disabled, the unit is enabled first, so the service keeps
starting at boot - and if that enable fails, the links stay, since a service
started by nothing at all would be worse than the shadowing.
Not Plesk-specific: any CloudLinux 8 host with the cl-MariaDB stack carries
these links. It stays latent for cPanel, DirectAdmin and nopanel only because
nothing there starts the service by name.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Worked-On: cl-aiworkspaces
This entry was folded into 0.20.0-11, which has since been released - the v0.20.0-11.cloudlinux tag is at 4a4202a and the build has gone out. Adding to a shipped stanza puts a line in a changelog customers already have, describing a fix their package does not contain. The tag predated the original commit by twelve days; the mistake was reasoning past it because the ELevate repository was still serving 0.20.0-10 at the time, which showed what was deployed rather than what had been cut. So the entry moves to a new 0.20.0-12 stanza and the spec Release goes to 12. CLOS-6911 Worked-On: cl-aiworkspaces
…m files
Two defects, both found by driving a CloudLinux 8 + Plesk conversion end to
end. The actor removed the links, reported that it had, and MariaDB still came
up outside its unit:
CGroup: /system.slice/mysql.service
|- 1585 /usr/bin/sh /usr/bin/mysqld_safe
mariadb.service: Unit process 1585 (mysqld_safe) remains running
Failed to start MariaDB database server.
Phase. FirstBootPhase runs after systemd-sysv-generator has already read the
links and started the service, so the removal only takes effect one boot later
- and the conversion's finish stage fails before that boot happens. Moved to
FinalizationPhase, which runs in the upgrade initramfs against the mounted
target root, before the new system boots at all. It is where leapp's own
SetSystemdServicesState applies unit states, for the same reason.
Detection. The actor asked `systemctl list-unit-files` whether a native
<name>.service existed. On a booted system that listing includes generator
output - a unit generated FROM the very link being removed - so the check was
circular and answered yes for any SysV service with an init script. It also
made the enable step actively harmful: cl-MariaDB103-server ships no
mysql.service, so `systemctl enable mysql.service` reported "not a native
service, redirecting to systemd-sysv-install" and called chkconfig, which
re-creates the links.
Units are now read from files in /usr/lib/systemd/system and
/etc/systemd/system - never /run/systemd, where the generator writes - and a
unit provides both its own name and every name in its [Install] Alias.
mariadb.service carries "Alias=mysql.service", which is how the SysV name
reaches the real unit. That unit is what gets enabled.
Fixing only the phase would have broken it the other way: with no boot yet
there is no generator output, no mysql.service appears, and the actor would
silently leave the links alone in exactly the case it exists for.
Verified against the real converted filesystem: mysql -> mariadb.service,
drwebd -> drwebd.service, network -> no real unit, links left. Each guard
mutation-checked.
Worked-On: cl-aiworkspaces
Review catch, and the conversion I had called a verification actually shows it
happening. This actor and set_systemd_services_state both ran in Finalization
Main, where leapp orders actors only by produce/consume edges - and this one
consumes nothing and produces only a Report, so nothing pinned the two:
21:38:55.163 remove_stale_sysv_links: systemctl enable mariadb.service
21:38:55.891 set_systemd_services_state: systemctl disable mariadb.service
The EL9 cl-MariaDB103-server %posttrans enables mariadb.service during the RPM
transaction; the transition sees it enabled against a disable preset and emits
to_disable=[mariadb.service]. Applying that removes the WantedBy link and both
alias symlinks - which is also what let the generator build a mysql.service from
the init script in the first place. So with the links already unlinked, nothing
started the database at boot: the first-boot journal shows MariaDB starting only
when Plesk's finish stage enabled and started it, four hours later.
That is why the end state of a converted host was not evidence: Plesk repairs
the unit, so the ticket symptom disappears while the actor's own effect had been
undone. FinalizationPhase.After runs strictly after the Main stage, the same
pattern as createfinishmarker and removeresumeservice.
The AST-based phase test could not have caught this - it compared the declared
tag by name, so the correct change would have made it fail while a tag imported
from the wrong module would have passed it. Replaced with the loaded actor's
real tags via the snactor fixtures, in a component_test_* file: phase tags come
from the repository rather than the framework, so importing them at module scope
in test_removestalesysvlinks.py would have made all 26 unit tests require a
loaded repository, which no other test_*.py in this repo does.
Worked-On: cl-aiworkspaces
7315e9b to
f1ac70f
Compare
|
Review findings confirmed, and fixed in the follow-up. |
What
Adds
RemoveStaleSysvLinks, a Finalization-phase actor that removes SysV runlevel links left over from the source system where the target provides a real systemd unit for the same service.Why
A package can ship both an init script and a systemd unit. The runlevel links
chkconfigcreated on the source system belong to no package, so nothing removes them during the upgrade. On the target,systemd-sysv-generatorturns such a link back into a unit that contests with the real one.cl-MariaDB103-serveris the case this was written for. It ships/etc/init.d/mysqlon EL9 as well, the EL8 links survive, and MariaDB ends up started bymysqld_safeoutsidemariadb.service:The init script is itself broken on EL9, where
log_success_msgno longer exists.leapp's own systemd state transition cannot see any of this. It works on units, and these are files under
/etc/rc.d/rc*.d- the same blind spot as CLOS-4518, which was the.timerhalf of_SYSTEMCTL_CMD_OPTIONS = ['--type=service', ...]. This is the SysV half.Two important aspects
It has to run before the target boots. On FirstBoot the generator has already read the links and started the service - PID 1585 above is early boot. The removal then only takes effect on the second boot, and a conversion whose finish stage fails never reaches it.
FinalizationPhaseruns in the upgrade initramfs against the mounted target root, which is where leapp's ownSetSystemdServicesStateapplies unit states for the same reason."Is there a real unit?" must not be asked of a booted system.
systemctl list-unit-filesincludes generator output - a unit generated from the link being removed - so the check is circular and answers yes for any SysV service with an init script. It also made the enable step actively harmful:cl-MariaDB103-serverships nomysql.service, sosystemctl enable mysql.servicereported "not a native service, redirecting to systemd-sysv-install" and calledchkconfig, which re-creates the links.Units are therefore read from files in
/usr/lib/systemd/systemand/etc/systemd/system- never/run/systemd, where the generator writes - and a unit provides its own name plus every name in its[Install] Alias.mariadb.servicecarriesAlias=mysql.service, which is how the SysV name reaches the real unit, and that unit is what gets enabled.Fixing only the phase would break it the other way: with no boot yet there is no generator output, no
mysql.serviceappears, and the actor silently leaves the links alone in exactly the case it exists for.Behavior
Scope
Narrower than it may look.
Only cl-MariaDB103 ships one, so only hosts running it are affected. That is not a rare configuration: EL8's default
mariadbmodule stream is 10.3 and MySQL Governor'sautofollows it, so hosts arrive on cl-MariaDB103 without choosing it, and leapp maps the stream 1:1 across the upgrade.Notes