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."};
     }
openSUSE Build Service is sponsored by