File add-container-snap.patch of Package transactional-update
From 09873858bc9200b8270dc21340a0e2f4b90affdd Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Dan=20=C4=8Cerm=C3=A1k?= <dcermak@suse.com>
Date: Fri, 22 Nov 2024 11:18:57 +0100
Subject: [PATCH] Add container-snap
---
etc/tukit.conf | 3 +
lib/Makefile.am | 4 +-
lib/Snapshot/ContainerSnap.cpp | 115 +++++++++++++++++++++++++++++++++
lib/Snapshot/ContainerSnap.hpp | 48 ++++++++++++++
lib/SnapshotManager.cpp | 4 ++
5 files changed, 172 insertions(+), 2 deletions(-)
create mode 100644 lib/Snapshot/ContainerSnap.cpp
create mode 100644 lib/Snapshot/ContainerSnap.hpp
diff --git a/etc/tukit.conf b/etc/tukit.conf
index f98b435a..90db9d35 100644
--- a/etc/tukit.conf
+++ b/etc/tukit.conf
@@ -12,6 +12,9 @@
# self-contained.
BINDDIRS[0]="/opt"
+# container-snap "snapshots"
+BINDDIRS[1]="/var/lib/container-snap"
+
# For "rebootmgr" and "systemd" reboot methods, allow them to use
# systemd's soft-reboot mechanism if /run/reboot-needed indicates this
# would be sufficient (see man 8 zypp-boot-plugin).
diff --git a/lib/Makefile.am b/lib/Makefile.am
index c6ac344b..99dfbb83 100644
--- a/lib/Makefile.am
+++ b/lib/Makefile.am
@@ -4,7 +4,7 @@
AUTOMAKE_OPTIONS = subdir-objects
lib_LTLIBRARIES = libtukit.la
libtukit_la_SOURCES=Transaction.cpp \
- SnapshotManager.cpp Snapshot/Snapper.cpp \
+ SnapshotManager.cpp Snapshot/Snapper.cpp Snapshot/ContainerSnap.cpp \
Mount.cpp Reboot.cpp Configuration.cpp \
Util.cpp Supplement.cpp Plugins.cpp Bindings/CBindings.cpp \
BlsEntry.cpp
@@ -12,7 +12,7 @@ publicheadersdir=$(includedir)/tukit
publicheaders_HEADERS=Transaction.hpp \
SnapshotManager.hpp Reboot.hpp \
Bindings/libtukit.h
-noinst_HEADERS=Snapshot/Snapper.hpp Snapshot.hpp \
+noinst_HEADERS=Snapshot/Snapper.hpp Snapshot/ContainerSnap.hpp Snapshot.hpp \
Mount.hpp Log.hpp Configuration.hpp \
Util.hpp Supplement.hpp Exceptions.hpp Plugins.hpp BlsEntry.hpp
libtukit_la_CPPFLAGS=-DPREFIX=\"$(prefix)\" -DCONFDIR=\"$(sysconfdir)\" $(ECONF_CFLAGS) $(LIBMOUNT_CFLAGS) $(SELINUX_CFLAGS)
diff --git a/lib/Snapshot/ContainerSnap.cpp b/lib/Snapshot/ContainerSnap.cpp
new file mode 100644
index 00000000..436230f2
--- /dev/null
+++ b/lib/Snapshot/ContainerSnap.cpp
@@ -0,0 +1,115 @@
+#include "ContainerSnap.hpp"
+#include "Util.hpp"
+#include <filesystem>
+#include <map>
+#include <memory>
+#include <sstream>
+#include <string>
+
+namespace TransactionalUpdate {
+
+static std::string callContainerSnap(std::string opts) {
+ std::string output;
+
+ output = Util::exec("container-snap " + opts);
+ Util::trim(output);
+ return output;
+}
+
+std::string ContainerSnap::getCurrent() {
+ return callContainerSnap("get-current");
+}
+
+std::string ContainerSnap::getDefault() {
+ return callContainerSnap("get-default");
+}
+
+void ContainerSnap::deleteSnap(std::string id) {
+ callContainerSnap("delete --id=" + id);
+}
+
+void ContainerSnap::rollbackTo(std::string id) {
+ callContainerSnap("switch " + id);
+}
+
+std::deque<std::map<std::string, std::string>>
+ContainerSnap::getList(std::string columns) {
+ std::deque<std::map<std::string, std::string>> snapshotList;
+
+ const auto output = callContainerSnap("list-snapshots");
+ // output looks like this:
+ // # snasphot-id,ro
+ // 43de4dcfccec5cd0b92c04afe1bbde645ff24bff5ff8845b73e82ae8bfd58e74,false
+ std::stringstream outputstream(output);
+ std::string line;
+
+ // consume header line
+ std::getline(outputstream, line);
+
+ while (std::getline(outputstream, line)) {
+ // Skip empty lines if any
+ if (line.empty()) {
+ continue;
+ }
+
+ std::stringstream linestream(line);
+ std::string id_value, ro_value;
+
+ // Extract snapshot-id
+ if (!std::getline(linestream, id_value, ',')) {
+ // line doesn't contain a comma, or is malformed => skip
+ continue;
+ }
+
+ // Extract ro state (rest of the line after the first comma)
+ std::getline(linestream, ro_value);
+
+ std::map<std::string, std::string> entry;
+ entry["id"] = id_value;
+ entry["ro"] = ro_value;
+
+ snapshotList.push_back(entry);
+ }
+
+ return snapshotList;
+}
+
+std::unique_ptr<Snapshot> ContainerSnap::open(std::string id) {
+ return std::make_unique<ContainerSnapshot>(ContainerSnapshot(id));
+};
+
+std::unique_ptr<Snapshot> ContainerSnap::create(std::string base,
+ std::string /*description*/) {
+ std::string new_snapshot_id = callContainerSnap("create-snapshot " + base);
+ return std::make_unique<ContainerSnapshot>(
+ ContainerSnapshot(new_snapshot_id));
+}
+
+std::filesystem::path ContainerSnapshot::getRoot() {
+ return std::filesystem::path(
+ callContainerSnap("get-root " + this->snapshotId));
+}
+
+void ContainerSnapshot::abort() {
+ ContainerSnap().deleteSnap(this->snapshotId);
+}
+
+void ContainerSnapshot::close() { this->inProgress = false; }
+
+bool ContainerSnapshot::isInProgress() { return this->inProgress; }
+
+bool ContainerSnapshot::isReadOnly() {
+ return callContainerSnap("get-readonly-state " + this->snapshotId) == "true";
+}
+
+void ContainerSnapshot::setDefault() {
+ callContainerSnap("switch " + this->snapshotId);
+}
+
+void ContainerSnapshot::setReadOnly(bool readonly) {
+ callContainerSnap("set-readonly-state " +
+ std::string(readonly ? "--readonly" : "--no-readonly") +
+ " " + this->snapshotId);
+}
+
+} // namespace TransactionalUpdate
diff --git a/lib/Snapshot/ContainerSnap.hpp b/lib/Snapshot/ContainerSnap.hpp
new file mode 100644
index 00000000..16d64c6b
--- /dev/null
+++ b/lib/Snapshot/ContainerSnap.hpp
@@ -0,0 +1,48 @@
+#pragma once
+
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+/* SPDX-FileCopyrightText: 2020 SUSE LLC */
+
+#include "Snapshot.hpp"
+#include "SnapshotManager.hpp"
+
+namespace TransactionalUpdate {
+
+class ContainerSnap : public SnapshotManager {
+public:
+ ~ContainerSnap() = default;
+
+ ContainerSnap() : SnapshotManager() {};
+ std::unique_ptr<Snapshot> create(std::string base,
+ std::string description) override;
+ virtual std::unique_ptr<Snapshot> open(std::string id) override;
+
+ /** Returns the list of snapshots, ignores the input columns (unsupported by
+ * container-snap)
+ */
+ std::deque<std::map<std::string, std::string>>
+ getList(std::string columns) override;
+ std::string getCurrent() override;
+ std::string getDefault() override;
+ void deleteSnap(std::string id) override;
+ void rollbackTo(std::string id) override;
+};
+
+class ContainerSnapshot : public Snapshot {
+private:
+ bool inProgress = true;
+
+public:
+ ~ContainerSnapshot() = default;
+
+ ContainerSnapshot(std::string snap) : Snapshot(snap) {};
+ void close() override;
+ void abort() override;
+ std::filesystem::path getRoot() override;
+ bool isInProgress() override;
+ bool isReadOnly() override;
+ void setDefault() override;
+ void setReadOnly(bool readonly) override;
+};
+
+} // namespace TransactionalUpdate
diff --git a/lib/SnapshotManager.cpp b/lib/SnapshotManager.cpp
index bf432f92..a0cdcfec 100644
--- a/lib/SnapshotManager.cpp
+++ b/lib/SnapshotManager.cpp
@@ -6,7 +6,9 @@
implementations can be found in the "Snapshot" directory
*/
+#include "Snapshot/ContainerSnap.hpp"
#include "Snapshot/Snapper.hpp"
+#include <memory>
using namespace std;
namespace TransactionalUpdate {
@@ -15,6 +17,8 @@ namespace TransactionalUpdate {
unique_ptr<SnapshotManager> SnapshotFactory::get() {
if (filesystem::exists("/usr/bin/snapper")) {
return make_unique<Snapper>();
+ } else if (filesystem::exists("/usr/bin/container-snap")) {
+ return make_unique<ContainerSnap>();
} else {
throw runtime_error{"No supported environment found."};
}