Script 'mail_helper' called by obssrc Hello community, here is the log from the commit of package transactional-update for openSUSE:Factory checked in at 2024-07-17 15:14:03 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/transactional-update (Old) and /work/SRC/openSUSE:Factory/.transactional-update.new.17339 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "transactional-update" Wed Jul 17 15:14:03 2024 rev:109 rq:1188045 version:4.7.0 Changes: -------- --- /work/SRC/openSUSE:Factory/transactional-update/transactional-update.changes 2024-05-09 12:07:29.417039699 +0200 +++ /work/SRC/openSUSE:Factory/.transactional-update.new.17339/transactional-update.changes 2024-07-17 15:14:34.728813590 +0200 @@ -1,0 +2,23 @@ +Tue Jul 16 18:12:53 UTC 2024 - Ignaz Forster <ifors...@suse.com> + +- It seems it's taking a longer time until the tests will be + adopted to the new reboot behavior. Disable soft-reboot for now + to unblock the regular transactional-update update. + +------------------------------------------------------------------- +Thu Jul 4 15:22:40 UTC 2024 - Ignaz Forster <ifors...@suse.com> + +- Version 4.7.0 + - Add plugin mechanism + It's now possible to hook into API functions with custom + plugins; see doc/tukit-plugins.md for details. + [gh#openSUSE/transactional-update#122] + - Fix missing libdir replacement for status command + +------------------------------------------------------------------- +Fri Jun 14 10:06:31 UTC 2024 - Ignaz Forster <ifors...@suse.com> + +- Enable soft-reboot by default again as announced in + https://microos.opensuse.org/blog/2024-06-13-soft-reboot/ + +------------------------------------------------------------------- Old: ---- transactional-update-4.6.8.tar.gz New: ---- transactional-update-4.7.0.tar.gz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ transactional-update.spec ++++++ --- /var/tmp/diff_new_pack.4KoXAy/_old 2024-07-17 15:14:35.292834049 +0200 +++ /var/tmp/diff_new_pack.4KoXAy/_new 2024-07-17 15:14:35.296834193 +0200 @@ -26,7 +26,7 @@ %{!?_distconfdir: %global _distconfdir %{_prefix}%{_sysconfdir}} Name: transactional-update -Version: 4.6.8 +Version: 4.7.0 Release: 0 Summary: Transactional Updates with btrfs and snapshots License: GPL-2.0-or-later AND LGPL-2.1-or-later @@ -170,6 +170,9 @@ sed -i 's/^UPDATE_METHOD=.*/UPDATE_METHOD=up/' etc/transactional-update.conf %endif +# Enable soft-reboot by default +sed -i 's/^REBOOT_ALLOW_SOFT_REBOOT=.*/REBOOT_ALLOW_SOFT_REBOOT=false/' etc/tukit.conf + %install %make_install ++++++ transactional-update-4.6.8.tar.gz -> transactional-update-4.7.0.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/transactional-update-4.6.8/NEWS new/transactional-update-4.7.0/NEWS --- old/transactional-update-4.6.8/NEWS 2024-05-07 14:12:47.000000000 +0200 +++ new/transactional-update-4.7.0/NEWS 2024-07-04 17:18:17.000000000 +0200 @@ -1,6 +1,13 @@ transactional-update NEWS -- history of user-visible changes. -Copyright (C) 2016-2023 Thorsten Kukuk, Ignaz Forster et al. +Copyright (C) 2016-2024 Thorsten Kukuk, Ignaz Forster et al. + +Version 4.7.0 (2024-07-04) +* Add plugin mechanism + It's now possible to hook into API functions with custom plugins; see + doc/tukit-plugins.md for details. + [gh#openSUSE/transactional-update#122] +* Fix missing libdir replacement for status command Version 4.6.8 (2024-05-07) * tukit: Properly handle overlay syncing failures: If the system would not be diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/transactional-update-4.6.8/configure.ac new/transactional-update-4.7.0/configure.ac --- old/transactional-update-4.6.8/configure.ac 2024-05-07 14:12:47.000000000 +0200 +++ new/transactional-update-4.7.0/configure.ac 2024-07-04 17:18:17.000000000 +0200 @@ -1,11 +1,11 @@ dnl Process this file with autoconf to produce a configure script. -AC_INIT(transactional-update, 4.6.8) +AC_INIT(transactional-update, 4.7.0) # Increase on any interface change and reset revision -LIBTOOL_CURRENT=5 +LIBTOOL_CURRENT=6 # On interface change increase if backwards compatible, reset otherwise -LIBTOOL_AGE=1 +LIBTOOL_AGE=2 # Increase on *any* C/C++ library code change, reset at interface change -LIBTOOL_REVISION=3 +LIBTOOL_REVISION=0 AC_CANONICAL_SYSTEM AM_INIT_AUTOMAKE([foreign]) AC_CONFIG_FILES([tukit.pc]) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/transactional-update-4.6.8/doc/tukit-plugins.md new/transactional-update-4.7.0/doc/tukit-plugins.md --- old/transactional-update-4.6.8/doc/tukit-plugins.md 1970-01-01 01:00:00.000000000 +0100 +++ new/transactional-update-4.7.0/doc/tukit-plugins.md 2024-07-04 17:18:17.000000000 +0200 @@ -0,0 +1,115 @@ +# Tukit plugins + +## Motivation + +Sometimes it is useful to inspect the content of a transaction in +certain points. For example, before `transactional-update dup` we +want to add new files into the system, and after the update we want to +collect the list of new packages. + +With a plugin system we can provide scripts that can be executed +before or after each action supported by `transactional-update`, like +package installation, creation of new `initrd`, etc. This can work +for most of the use cases, but there are certain tasks that cannot be +done at this level. + +One example of those tasks is inspecting the `/run` directory after a +full system upgrade to look for files that can signalize a condition, +like the one that will trigger the `initrd` creation in a +post-transaction scriptlet. The `/run` directory that is alive inside +the new transaction is different from the one running in the host, but +in both cases it is a `tmpfs` filesystem. This means that every time +that it is unmounted we lost the information stored in there. Sadly, +every time that `transactional-update` calls `tukit` to realize an +action inside the snapshot, those directories are mounted and +unmounted. + +The solution for this is to have the plugins at the `tukit` level. + +## Directories and shadowing + +The plugins can be installed in two different places: + +* `/usr/lib/tukit/plugins`: place for plugins that comes from packages +* `/etc/tukit/plugins`: for user defined plugins + +The plugins in `/etc` can shadow the ones from `/usr` using the same +name. For example, if the plugin `get_status` is in both places with +the executable attribute, `tukit` will use the code from `/etc`, +shadowing the one from the packages. + +One variation of shadowing is when the plugin in `/etc` is a soft link +to `/dev/null`. This will be used as a mark to completely disable +this plugin, and would not be called by `tukit`. + +The plugins in `/etc` will be called before the ones in `/usr` but the +user should not depend on the calling order. + +## Stages + +The actions are based on the low-level API of libtukit, not of the +user API level. This means that some verbs like `tukit execute <cmd>` +will be presented as several of those low level actions: create +snapshot, execute command, keep snapshot. + +Some actions will trigger a plugin call before and after the action +itself, depending if it makes sense in the context of this action. + +The next table summarizes the action, the stage and different +parameters sent to the plugin. + +| Action | Stage | Parameters | Notes | +|----------|-------|-----------------------------------|-------| +| init | -pre | | | +| | -post | path, snapshot\_id | | +| resume | -pre | snapshot\_id | | +| | -post | path, snapshot\_id | | +| execute | -pre | path, snapshot\_id, action params | | +| | -post | path, snapshot\_id, action params | | +| callExt | -pre | path, snapshot\_id, action params | [1] | +| | -post | path, snapshot\_id, action params | | +| finalize | -pre | path, snapshot\_id | | +| | -post | snapshot\_id, [discarded] | [2] | +| abort | -post | snapshot\_id | [3] | +| keep | -pre | path, snapshot\_id | | +| | -post | snapshot\_id | | +| reboot | -pre | | | + +[1] The {} placeholder gets expanded in the arguments passed to the +plugin + +[2] If the snapshot is discarded, the second parameter for the -post +is "discarded" + +[3] abort-pre cannot be captured from the libtukit level + + +## Example + +```bash +#!/bin/bash + +exec_pre() { + local path="$1"; shift + local snapshot_id="$1"; shift + local cmd="$@" + + # The live snapshot is in "$path", and the future closed snapshot in + # "/.snapshots/${snapshot_id}/snapshot + + mkdir -p /var/lib/report + echo "${snapshot_id}: $cmd" >> /var/lib/report/all_commands +} + +declare -A commands + +commands['execute-pre']=exec_pre +commands['callExt-pre']=exec_pre + +cmd="$1" +shift +[ -n "$cmd" ] || cmd=help +if [ "${#commands[$cmd]}" -gt 0 ]; then + ${commands[$cmd]} "$@" +fi +``` diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/transactional-update-4.6.8/lib/Makefile.am new/transactional-update-4.7.0/lib/Makefile.am --- old/transactional-update-4.6.8/lib/Makefile.am 2024-05-07 14:12:47.000000000 +0200 +++ new/transactional-update-4.7.0/lib/Makefile.am 2024-07-04 17:18:17.000000000 +0200 @@ -3,14 +3,14 @@ libtukit_la_SOURCES=Transaction.cpp \ SnapshotManager.cpp Snapshot/Snapper.cpp \ Mount.cpp Overlay.cpp Reboot.cpp Configuration.cpp \ - Util.cpp Supplement.cpp Bindings/CBindings.cpp + Util.cpp Supplement.cpp Plugins.cpp Bindings/CBindings.cpp publicheadersdir=$(includedir)/tukit publicheaders_HEADERS=Transaction.hpp \ SnapshotManager.hpp Reboot.hpp \ Bindings/libtukit.h noinst_HEADERS=Snapshot/Snapper.hpp Snapshot.hpp \ Mount.hpp Overlay.hpp Log.hpp Configuration.hpp \ - Util.hpp Supplement.hpp Exceptions.hpp + Util.hpp Supplement.hpp Exceptions.hpp Plugins.hpp libtukit_la_CPPFLAGS=-DPREFIX=\"$(prefix)\" -DCONFDIR=\"$(sysconfdir)\" $(ECONF_CFLAGS) $(LIBMOUNT_CFLAGS) $(SELINUX_CFLAGS) libtukit_la_LDFLAGS=$(ECONF_LIBS) $(LIBMOUNT_LIBS) $(SELINUX_LIBS) \ -version-info $(LIBTOOL_CURRENT):$(LIBTOOL_REVISION):$(LIBTOOL_AGE) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/transactional-update-4.6.8/lib/Plugins.cpp new/transactional-update-4.7.0/lib/Plugins.cpp --- old/transactional-update-4.6.8/lib/Plugins.cpp 1970-01-01 01:00:00.000000000 +0100 +++ new/transactional-update-4.7.0/lib/Plugins.cpp 2024-07-04 17:18:17.000000000 +0200 @@ -0,0 +1,91 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* SPDX-FileCopyrightText: 2024 SUSE LLC */ + +/* Plugin mechanism for tukit */ + +#include "Exceptions.hpp" +#include "Log.hpp" +#include "Plugins.hpp" +#include "Util.hpp" +#include <set> +#include <unistd.h> + +namespace TransactionalUpdate { + +using namespace std; + +Plugins::Plugins(TransactionalUpdate::Transaction* transaction): transaction(transaction) { + set<string> plugins_set{}; + + const filesystem::path plugins_dir{filesystem::path(CONFDIR)/"tukit"/"plugins"}; + const filesystem::path system_plugins_dir{filesystem::path(PREFIX)/"lib"/"tukit"/"plugins"}; + + for (auto d: {plugins_dir, system_plugins_dir}) { + if (!filesystem::exists(d)) + continue; + + for (auto const& dir_entry: filesystem::directory_iterator{d}) { + auto path = dir_entry.path(); + auto filename = dir_entry.path().filename(); + + // Plugins can be shadowed, so a plugin in /etc can + // replace one from /usr/lib + if (plugins_set.count(filename) != 0) + continue; + + // If is a symlink to /dev/null, ignore and shadow it + if (filesystem::is_symlink(path) && filesystem::read_symlink(path) == "/dev/null") { + plugins_set.insert(filename); + continue; + } + + // If the plugin is not executable, ignore it + if (!(filesystem::is_regular_file(path) && (access(path.c_str(), X_OK) == 0))) + continue; + + tulog.info("Found plugin ", path); + plugins.push_back(path); + plugins_set.insert(filename); + } + } +} + +Plugins::~Plugins() { + plugins.clear(); +} + +void Plugins::run(string stage, string args) { + std::string output; + + for (auto& p: plugins) { + std::string cmd = p.string() + " " + stage; + if (!args.empty()) + cmd.append(" " + args); + + try { + output = Util::exec(cmd); + if (!output.empty()) + tulog.info("Output of plugin ", p, ": ", output); + } catch (const ExecutionException &e) { + // An error in the plugin should not discard the transaction + tulog.error("ERROR: Plugin ", p, " failed with ", e.what()); + } + } +} + +void Plugins::run(string stage, char* argv[]) { + std::string args; + + if (transaction != nullptr) + args.append(transaction->getBindDir().string() + " " + transaction->getSnapshot()); + + int i = 0; + while (argv != nullptr && argv[i]) { + args.append(" "); + args.append(argv[i++]); + } + + run(stage, args); +} + +} // namespace TransactionalUpdate diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/transactional-update-4.6.8/lib/Plugins.hpp new/transactional-update-4.7.0/lib/Plugins.hpp --- old/transactional-update-4.6.8/lib/Plugins.hpp 1970-01-01 01:00:00.000000000 +0100 +++ new/transactional-update-4.7.0/lib/Plugins.hpp 2024-07-04 17:18:17.000000000 +0200 @@ -0,0 +1,29 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* SPDX-FileCopyrightText: 2024 SUSE LLC */ + +/* Plugin mechanism for tukit */ + +#ifndef T_U_PLUGINS_H +#define T_U_PLUGINS_H + +#include "Transaction.hpp" +#include <filesystem> +#include <string> +#include <vector> + +namespace TransactionalUpdate { + +class Plugins { +public: + Plugins(TransactionalUpdate::Transaction* transaction); + virtual ~Plugins(); + void run(std::string stage, std::string args); + void run(std::string stage, char* argv[]); +protected: + TransactionalUpdate::Transaction* transaction; + std::vector<std::filesystem::path> plugins; +}; + +} // namespace TransactionalUpdate + +#endif // T_U_PLUGINS_H diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/transactional-update-4.6.8/lib/Reboot.cpp new/transactional-update-4.7.0/lib/Reboot.cpp --- old/transactional-update-4.6.8/lib/Reboot.cpp 2024-05-07 14:12:47.000000000 +0200 +++ new/transactional-update-4.7.0/lib/Reboot.cpp 2024-07-04 17:18:17.000000000 +0200 @@ -9,6 +9,7 @@ #include "Configuration.hpp" #include "Exceptions.hpp" #include "Log.hpp" +#include "Plugins.hpp" #include "Snapshot.hpp" #include "SnapshotManager.hpp" #include "Util.hpp" @@ -77,6 +78,8 @@ } void Reboot::reboot() { + TransactionalUpdate::Plugins plugins{nullptr}; + plugins.run("reboot-pre", nullptr); Util::exec(command); } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/transactional-update-4.6.8/lib/Transaction.cpp new/transactional-update-4.7.0/lib/Transaction.cpp --- old/transactional-update-4.6.8/lib/Transaction.cpp 2024-05-07 14:12:47.000000000 +0200 +++ new/transactional-update-4.7.0/lib/Transaction.cpp 2024-07-04 17:18:17.000000000 +0200 @@ -12,6 +12,7 @@ #include "Log.hpp" #include "Mount.hpp" #include "Overlay.hpp" +#include "Plugins.hpp" #include "SnapshotManager.hpp" #include "Snapshot.hpp" #include "Supplement.hpp" @@ -80,6 +81,8 @@ if (isInitialized() && !getSnapshot().empty() && fs::exists(getRoot())) { tulog.info("Discarding snapshot ", pImpl->snapshot->getUid(), "."); pImpl->snapshot->abort(); + TransactionalUpdate::Plugins plugins{nullptr}; + plugins.run("abort-post", pImpl->snapshot->getUid()); } } catch (const std::exception &e) { tulog.error("ERROR: ", e.what()); @@ -98,6 +101,10 @@ return pImpl->snapshot->getRoot(); } +fs::path Transaction::getBindDir() { + return pImpl->bindDir; +} + void Transaction::impl::snapMount() { if (unshare(CLONE_NEWNS) < 0) { throw std::runtime_error{"Creating new mount namespace failed: " + std::string(strerror(errno))}; @@ -256,6 +263,9 @@ } void Transaction::init(std::string base, std::optional<std::string> description) { + TransactionalUpdate::Plugins plugins{nullptr}; + plugins.run("init-pre", nullptr); + if (base == "active") base = pImpl->snapshotMgr->getCurrent(); else if (base == "default") @@ -291,9 +301,15 @@ // Flag file to indicate this snapshot was initialized with discard flag std::ofstream output(getRoot() / "discardIfNoChange"); } + + TransactionalUpdate::Plugins plugins_with_transaction{this}; + plugins_with_transaction.run("init-post", nullptr); } void Transaction::resume(std::string id) { + TransactionalUpdate::Plugins plugins{nullptr}; + plugins.run("resume-pre", id); + pImpl->snapshot = pImpl->snapshotMgr->open(id); if (! pImpl->snapshot->isInProgress()) { pImpl->snapshot.reset(); @@ -304,6 +320,9 @@ if (fs::exists(getRoot() / "discardIfNoChange")) { pImpl->discardIfNoChange = true; } + + TransactionalUpdate::Plugins plugins_with_transaction{this}; + plugins_with_transaction.run("resume-post", nullptr); } void Transaction::setDiscardIfUnchanged(bool discard) { @@ -452,7 +471,11 @@ } int Transaction::execute(char* argv[], std::string* output) { - return this->pImpl->runCommand(argv, true, output); + TransactionalUpdate::Plugins plugins{this}; + plugins.run("execute-pre", argv); + int status = this->pImpl->runCommand(argv, true, output); + plugins.run("execute-post", argv); + return status; } int Transaction::callExt(char* argv[], std::string* output) { @@ -466,7 +489,12 @@ s.replace(pos, from.size(), this->pImpl->bindDir); argv[i] = strdup(s.c_str()); } - return this->pImpl->runCommand(argv, false, output); + + TransactionalUpdate::Plugins plugins{this}; + plugins.run("callExt-pre", argv); + int status = this->pImpl->runCommand(argv, false, output); + plugins.run("callExt-post", argv); + return status; } void Transaction::sendSignal(int signal) { @@ -478,6 +506,9 @@ } void Transaction::finalize() { + TransactionalUpdate::Plugins plugins{this}; + plugins.run("finalize-pre", nullptr); + sync(); if (pImpl->discardIfNoChange && ((inotifyFd != 0 && pImpl->inotifyRead() == 0) || @@ -511,6 +542,8 @@ Util::exec("rsync --archive --inplace --xattrs --acls --exclude 'fstab' --delete --quiet '" + this->pImpl->bindDir.native() + "/etc/' " + targetRoot.native() + "/etc"); } + TransactionalUpdate::Plugins plugins_without_transaction{nullptr}; + plugins_without_transaction.run("finalize-post", pImpl->snapshot->getUid() + " " + "discarded"); return; } if (fs::exists(getRoot() / "discardIfNoChange")) { @@ -531,15 +564,26 @@ pImpl->snapshot->setDefault(); tulog.info("New default snapshot is #" + pImpl->snapshot->getUid() + " (" + std::string(pImpl->snapshot->getRoot()) + ")."); + std::string id = pImpl->snapshot->getUid(); pImpl->snapshot.reset(); + + TransactionalUpdate::Plugins plugins_without_transaction{nullptr}; + plugins_without_transaction.run("finalize-post", id); } void Transaction::keep() { + TransactionalUpdate::Plugins plugins{this}; + plugins.run("keep-pre", nullptr); + sync(); if (fs::exists(pImpl->snapshot->getRoot() / "discardIfNoChange") && (inotifyFd != 0 && pImpl->inotifyRead() > 0)) { tulog.debug("Snapshot was changed, removing discard flagfile."); fs::remove(pImpl->snapshot->getRoot() / "discardIfNoChange"); } pImpl->supplements.cleanup(); + std::string id = pImpl->snapshot->getUid(); pImpl->snapshot.reset(); + + TransactionalUpdate::Plugins plugins_without_transaction{nullptr}; + plugins_without_transaction.run("keep-post", id); } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/transactional-update-4.6.8/lib/Transaction.hpp new/transactional-update-4.7.0/lib/Transaction.hpp --- old/transactional-update-4.6.8/lib/Transaction.hpp 2024-05-07 14:12:47.000000000 +0200 +++ new/transactional-update-4.7.0/lib/Transaction.hpp 2024-07-04 17:18:17.000000000 +0200 @@ -167,6 +167,10 @@ * root path as long as Transaction's destructor hasn't been called. */ std::filesystem::path getRoot(); + + friend class Plugins; +protected: + std::filesystem::path getBindDir(); private: class impl; std::unique_ptr<impl> pImpl; diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/transactional-update-4.6.8/sbin/Makefile.am new/transactional-update-4.7.0/sbin/Makefile.am --- old/transactional-update-4.6.8/sbin/Makefile.am 2024-05-07 14:12:47.000000000 +0200 +++ new/transactional-update-4.7.0/sbin/Makefile.am 2024-07-04 17:18:17.000000000 +0200 @@ -12,6 +12,7 @@ do_subst = sed -e 's,[@]sysconfdir[@],$(sysconfdir),g' \ -e 's,[@]prefix[@],$(prefix),g' \ + -e 's,[@]libdir[@],$(libdir),g' \ -e 's,[@]VERSION[@],$(VERSION),g' transactional-update: transactional-update.in Makefile diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/transactional-update-4.6.8/tukit/main.cpp new/transactional-update-4.7.0/tukit/main.cpp --- old/transactional-update-4.6.8/tukit/main.cpp 2024-05-07 14:12:47.000000000 +0200 +++ new/transactional-update-4.7.0/tukit/main.cpp 2024-07-04 17:18:17.000000000 +0200 @@ -14,7 +14,7 @@ try { TUKit ta{argc, argv}; } catch (int e) { - return e; + return e; } catch (const exception &e) { cerr << "ERROR: " << e.what() << endl; return 1;