Commit 1419af0a authored by blam's avatar blam

[vault] Remove everything except unit helpers. This removes the gittin...

[vault] Remove everything except unit helpers. This removes the gittin dependency. Contributes to JB#36999

The git-related features are no longer required, so remove them and
avoid the gittin dependency. Keep unit.hpp/cpp in the devel library
as it is still required to build backup/restore units.

Also build with qmake and rename unit.hpp to unit.h for consistency
with other Sailfish middleware libraries.
parent 82ad5540
PROJECT(VAULT)
cmake_minimum_required(VERSION 2.8.8)
message(STATUS "Tools go to ${TOOLS_DIR}")
set(prefix ${CMAKE_INSTALL_PREFIX})
IF(NOT DEFINED VERSION OR VERSION STREQUAL "")
message(FATAL_ERROR "Define VERSION")
ENDIF(NOT DEFINED VERSION OR VERSION STREQUAL "")
IF(NOT DEFINED LONG_VERSION OR LONG_VERSION STREQUAL "")
set(LONG_VERSION ${VERSION})
ENDIF(NOT DEFINED LONG_VERSION OR LONG_VERSION STREQUAL "")
message(STATUS "Version ${VERSION}")
message(STATUS "Long version is ${LONG_VERSION}")
find_package(PkgConfig REQUIRED)
find_package(Qt5Core REQUIRED)
find_package(Cor REQUIRED)
set(CMAKE_CXX_FLAGS
"${CMAKE_CXX_FLAGS} -fPIC -W -Wall -Wextra -g -O2 -std=c++0x")
#-Wno-psabi is to remove next g++ warning/note:
#the mangling of 'va_list' has changed in GCC 4.4
set(CMAKE_CXX_FLAGS
"${CMAKE_CXX_FLAGS} -Wno-psabi")
# fix for gcc 4.6 specific compiling/linking issues, no dwarf-4
set(CMAKE_CXX_FLAGS
"${CMAKE_CXX_FLAGS} -gdwarf-3"
)
include_directories(
${CMAKE_CURRENT_SOURCE_DIR}/include
${CMAKE_CURRENT_SOURCE_DIR}/src
)
pkg_check_modules(QTAROUND qtaround REQUIRED)
include_directories(
${COR_INCLUDE_DIRS}
${QTAROUND_INCLUDE_DIRS}
)
link_directories(
${COR_LIBRARY_DIRS}
${QTAROUND_LIBRARY_DIRS}
)
add_subdirectory(src)
add_subdirectory(examples)
add_subdirectory(tests)
add_subdirectory(qml/Vault)
add_subdirectory(tools)
configure_install_pkgconfig(vault-unit)
install(
DIRECTORY include/vault
DESTINATION include
FILES_MATCHING
PATTERN "*.hpp"
PATTERN "*.h"
)
* vault
Set of tools to perform backup/restoration of the data. This is the
rewrite of [[https://github.com/nemomobile/the-vault][the-vault]] in C++.
It uses git for data and metadata management. Binaries are stored
separately now inside .git repository. Some ideas are borrowed from
git-annex but this framework has more permissive license.
** Rationale
Application developers ordinary understand backup as a process of
performing a snapshots of an application state and restoration as a
replacement of application state with a previously saved state. So,
the common strategy of backup is to freeze application workflow, make
a snapshot of application data and continue to work. Restoration is
the reverse process.
This is the bad practice: imagine, application database is damaged as
a result of some bug in application code (this is becomes more and
more common) and initially there is no any visible changes in
application behaviour, user performs backup, but later user notices
issues in application/system behaviour and want to restore its state
from backup was done eariler. But because database was corrupted
before, restoring data from this backup will result in the same issues
to reappear soon. Also, maybe user added some data in the mean time,
so restoration also will result in this data will be lost. Finally,
user still has damaged database while one lost data he/she added in
the meantime.
The proper and flexible way to backup/restore application data is to
work with exporting/importing structured data and saving/restoring
opaque data. So, like in relational databases world application data
can be separated:
- data (structured) can be represented in human-readable form
(e.g. for relational database this is SQL, for contacts database it
can be vCard files, maybe, EXIF information from images, MP3 tags
etc.). Also small binary files can be put here
- opaque, binary data aka blobs (e.g. images, videos, audio files, can
be also something like PDF files (potentially can be exported but it
is hard to do it and can result in some data loss), maybe documents
in binary formats etc.
So, application communication with backup world is described in terms
of the following operations: export/import/clear
** Backup API
Each application should register path to executable to be invoked for
......@@ -63,18 +17,3 @@ Options:
- --action -- which action should be executed. Possible values are:
import, export, clear.
** TODO Examples
** Planned features
- Huge files should be detected and managed automatically in many
cases.
- It looks like it is possible to improve usability replacing separate
storage for BLOBs (.git/blobs) with git submodule with
pack.windowmemory set to finite value - multiply of device RAM and
splitting BLOBs to smaller chunks. So, it will be possible to clone
repo and submodule using standard git w/o any enhancements.
add_executable(use_cutes test_cutes.cpp)
qt5_use_modules(use_cutes Core)
target_link_libraries(use_cutes qtaround)
import QtQuick 2.0
import NemoMobile.Vault 1.1
QtObject {
property Vault vault: Vault {
onDone: vaultOperationDone(operation, data)
onProgress: vaultOperationProgress(operation, data)
onError: vaultOperationError(operation, error)
onData: vaultData(id, context)
}
// event handlers
function vaultOperationDone(operation, data) {
console.log("vault operation", operation, "done")
var action
switch (operation) {
case Vault.Connect:
break
case Vault.Maintenance:
break
case Vault.Backup:
break
case Vault.Restore:
break
case Vault.RemoveSnapshot:
break
case Vault.ExportSnapshot:
console.log("snapshot exporting is done"
, data.rc, data.snapshot, data.dst
, data.stdout, data.stderr)
break
case Vault.ExportImportPrepare:
break
case Vault.ExportImportExecute:
break
default:
break
}
}
function vaultData(operation, context) {
switch (operation) {
case Vault.SnapshotUnits:
break
case Vault.Snapshots:
break
case Vault.Units:
break
default:
break
}
}
function vaultOperationProgress(operation, data) {
switch (operation) {
case Vault.Backup:
break
case Vault.Restore:
break
case Vault.ExportImportExecute:
break
default:
break
}
}
function vaultOperationError(operation, error) {
console.log("vault operation", operation, "error")
switch (operation) {
case Vault.Connect:
break
case Vault.Backup:
break
case Vault.Restore:
break
case Vault.RemoveSnapshot:
break
case Vault.ExportImportPrepare:
break
case Vault.ExportImportExecute:
break
case Vault.ExportSnapshot:
console.log("error exporting snapshot"
, error.rc, error.snapshot, error.dst
, error.stdout, error.stderr)
default:
break
}
}
}
{
"name": "Accounts",
"group": "organizer",
"icon": "icon-launcher-accounts",
"script": "/usr/share/jolla-vault/units/accounts.js",
"aux" : {
"a" : 1,
"b" : "c"
},
"array" : [ "x1", "y2" ]
}
#include <qtaround/debug.hpp>
#include <qtaround/util.hpp>
#include <qtaround/os.hpp>
#include <qtaround/subprocess.hpp>
#include <qtaround/json.hpp>
#include <QCoreApplication>
#include <QDebug>
namespace os = qtaround::os;
namespace error = qtaround::error;
namespace sys = qtaround::sys;
namespace json = qtaround::json;
namespace debug = qtaround::debug;
int main(int argc, char *argv[])
{
QCoreApplication app(argc, argv);
try {
qDebug() << os::path::exists(".")
<< os::mkdir("./tmp1", {{"parent", true}})
<< os::mkdir("./tmp2/tmp3", {{"parent", true}})
<< os::mkdir("tmp4")
;
debug::print(1, map({{"w", 2}}));
debug::debug("w", "e");
debug::error("error", "more info", 1);
auto v = json::read("data.json");
debug::print(v, get(v, "name"), get(v, "array", 0));
// is movable
auto v2 = std::move(v);
debug::print(v2);
QVariantMap options_info
= {{"data_dir", map({{"short", "d"}, {"long", "dir"}
, {"required", true}, {"has_param", true}})}
, {"bin_dir", map({{"short", "b"}, {"long", "bin-dir"}
, {"required", true}, {"has_param", true}})}
, {"home", map({{"short", "H"}, {"long", "home-dir"}
, {"required", true}, {"has_param", true}})}
, {"action", map({{"short", "a"}, {"long", "action"}
, {"required", true}, {"has_param", true}})}};
sys::getopt(options_info);
} catch(error::Error const &e) {
qDebug() << "E:" << e.m;
} catch (std::exception const &e) {
qDebug() << "E:" << e.what();
}
return 0;
}
#ifndef _VAULT_CONFIG_HPP_
#define _VAULT_CONFIG_HPP_
/**
* @file config.hpp
* @brief Vault configuration
* @author Giulio Camuffo <giulio.camuffo@jollamobile.com>
* @copyright (C) 2014 Jolla Ltd.
* @par License: LGPL 2.1 http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html
*/
#include <QString>
#include <QMap>
#include <QVariantMap>
#include <qtaround/os.hpp>
namespace Gittin {
class Repo;
}
namespace vault { namespace config {
namespace {
namespace os = qtaround::os;
const QString prefix = ".f8b52b7481393a3e6ade051ecfb549fa";
static inline QString units_path(QString const &vaultDir)
{
return os::path::join(vaultDir, ".units");
}
}
class Unit
{
public:
Unit();
explicit Unit(const QVariantMap &data);
Unit &read(const QString &fname);
ssize_t write(const QString &fname);
bool update(const QVariantMap &src);
QString name() const;
QString script() const;
inline QVariantMap data() const { return m_data; }
bool isLocal() const { return is(m_data.value("local", QVariant(false))); }
private:
QVariantMap m_data;
};
class Config
{
public:
explicit Config(const QString &unitsDir);
~Config();
void load();
bool set(const QVariantMap &data);
QString rm(const QString &name);
void setUnitsDir(const QString &unitsDir);
QMap<QString, Unit> units() const;
QString path(const QString &fname) const;
QString root() const;
private:
QString m_unitsDir;
QMap<QString, Unit> m_units;
};
Config *global();
class Vault
{
public:
explicit Vault(Gittin::Repo *vcs);
~Vault();
bool set(const QVariantMap &data);
bool rm(const QString &name);
bool update(const QMap<QString, Unit> &src);
bool update(const QVariantMap &src);
QMap<QString, Unit> units() const;
private:
Config m_config;
Gittin::Repo *m_vcs;
};
}}
#endif // _VAULT_CONFIG_HPP_
#ifndef _VAULT_VAULT_HPP_
#define _VAULT_VAULT_HPP_
/**
* @file vault.hpp
* @brief Vault management API
* @author Giulio Camuffo <giulio.camuffo@jollamobile.com>
* @copyright (C) 2014 Jolla Ltd.
* @par License: LGPL 2.1 http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html
*/
#include <qtaround/os.hpp>
#include <functional>
#include <QString>
#include <gittin/repo.hpp>
#include <vault/config.hpp>
namespace vault {
enum class File { Message, VersionTree, VersionRepo, State, Lock
, Last_ = Lock };
QString fileName(File);
class Snapshot
{
public:
explicit Snapshot(const Gittin::Tag &commit);
explicit Snapshot(Gittin::Repo *repo, const QString &name);
inline Gittin::Tag tag() const { return m_tag; }
QString name() const;
void remove();
private:
Gittin::Tag m_tag;
};
typedef std::shared_ptr<qtaround::os::FileLock> Lock;
typedef std::weak_ptr<qtaround::os::FileLock> Barrier;
class Vault
{
public:
struct Result {
QStringList succededUnits;
QStringList failedUnits;
Result() {}
Result(Result &&from)
: succededUnits(std::move(from.succededUnits))
, failedUnits(std::move(from.failedUnits))
{}
Result(Result const &) = delete;
Result & operator = (Result const &) = delete;
};
struct UnitPath {
QString path;
QString bin;
QString data;
bool exists() const;
};
typedef std::function<void (const QString &, const QString &)> ProgressCallback;
Vault(const QString &path);
bool init(const QVariantMap &config = QVariantMap());
Result backup(const QString &home, const QStringList &units, const QString &message, const ProgressCallback &callback = nullptr);
Result restore(const Snapshot &snapshot, const QString &home, const QStringList &units, const ProgressCallback &callback = nullptr);
Result restore(const QString &snapshot, const QString &home, const QStringList &units, const ProgressCallback &callback = nullptr);
bool clear(const QVariantMap &options);
QList<Snapshot> snapshots() const;
QList<QString> units(QString const & snapshotName) const;
Snapshot snapshot(const QByteArray &tag) const;
QString notes(const QString &snapshotName);
bool exists() const;
bool isInvalid();
config::Vault config();
UnitPath unitPath(const QString &name) const;
inline QString root() const { return m_path; }
void registerConfig(const QVariantMap &config);
void unregisterUnit(const QString &unit);
bool writeFile(const QString &file, const QString &content);
static int execute(const QVariantMap &options);
bool ensureValid();
void reset(const QByteArray &treeish = QByteArray());
Lock lock() const;
void resetLastSnapshot();
std::tuple<QString, bool, QString> backupUnit(const QString &, const QString &);
QString tagSnapshot(const QString &message);
std::tuple<QString, bool, QString> restoreUnit
(const QString &, const QString &, const QString &);
std::tuple<int, QString, QString> exportSnapshot(QString const &, QString const&);
private:
bool setState(const QString &state);
bool backupUnit(const QString &home, const QString &unit, const ProgressCallback &callback);
bool restoreUnit(const QString &home, const QString &unit, const ProgressCallback &callback);
void checkout(const QString &);
void resetMaster();
void setup(const QVariantMap *config);
QString absolutePath(QString const &) const;
QString readFile(const QString &relPath);
void setVersion(File, int);
int getVersion(File);
const QString m_path;
const QString m_blobStorage;
Gittin::Repo m_vcs;
config::Vault m_config;
mutable Barrier m_barrier;
};
}
#endif // _VAULT_VAULT_HPP_
TEMPLATE = lib
TARGET = vault
TARGET = $$qtLibraryTarget($$TARGET)
TARGETPATH = $$[QT_INSTALL_LIBS]
CONFIG += create_pc create_prl no_install_prl link_pkgconfig
PKGCONFIG += qtaround
MOC_DIR = $$OUT_PWD/.moc
OBJECTS_DIR = $$OUT_PWD/.obj
RCC_DIR = $$OUT_PWD/.rcc
SOURCES += unit.cpp
HEADERS += unit.h
develheaders.path = /usr/include/vault
develheaders.files = unit.h
target.path = $$[QT_INSTALL_LIBS]
pkgconfig.files = $$TARGET.pc
pkgconfig.path = $$target.path/pkgconfig
QMAKE_PKGCONFIG_NAME = lib$$TARGET
QMAKE_PKGCONFIG_DESCRIPTION = Vault development files
QMAKE_PKGCONFIG_LIBDIR = $$target.path
QMAKE_PKGCONFIG_INCDIR = $$develheaders.path
QMAKE_PKGCONFIG_DESTDIR = pkgconfig
QMAKE_PKGCONFIG_REQUIRES = Qt5Core
QMAKE_PKGCONFIG_VERSION = $$VERSION
INSTALLS += target develheaders pkgconfig
......@@ -6,7 +6,7 @@
* @par License: LGPL 2.1 http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html
*/
#include <vault/unit.hpp>
#include "unit.h"
#include <qtaround/util.hpp>
#include <qtaround/os.hpp>
......@@ -27,6 +27,8 @@ namespace vault { namespace unit {
static const unsigned current_version = 1;
static const QString default_preserve = "mode,ownership,timestamps";
static const QString config_prefix = ".f8b52b7481393a3e6ade051ecfb549fa";
namespace {
QVariantMap options_info
= {{"dir", map({{"short", "d"}, {"long", "dir"}
......@@ -52,7 +54,7 @@ typedef QList<QVariantMap> list_type;
class Version {
public:
Version(QString const &root)
: fname(os::path::join(root, vault::config::prefix + ".unit.version"))
: fname(os::path::join(root, config_prefix + ".unit.version"))
{}
unsigned get()
{
......@@ -131,7 +133,7 @@ private:
static QString get_link_info_fname(QString const &root)
{
return os::path::join(root, vault::config::prefix + ".links");
return os::path::join(root, config_prefix + ".links");
}
Version version(QString const &root)
......
......@@ -8,9 +8,10 @@
* @par License: LGPL 2.1 http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html
*/
#include <vault/config.hpp>
#include <memory>
#include <QVariantMap>
namespace qtaround { namespace sys { class GetOpt; }}
namespace vault { namespace unit {
......
set(CMAKE_INCLUDE_CURRENT_DIR ON)
set(CMAKE_AUTOMOC TRUE)
find_package(Qt5Qml REQUIRED)
add_library(vault-declarative SHARED plugin.cpp vault.cpp)
qt5_use_modules(vault-declarative Qml)
target_link_libraries(vault-declarative vault-core vault-transfer)
set_target_properties(vault-declarative PROPERTIES
SOVERSION 0
VERSION ${VERSION}
)
install(TARGETS vault-declarative DESTINATION ${DST_LIB}/qt5/qml/NemoMobile/Vault)
install(FILES qmldir DESTINATION ${DST_LIB}/qt5/qml/NemoMobile/Vault)
#include <QtQml/qqmlextensionplugin.h>
#include <QtQml/qqml.h>
#include <QtQml/qqmlengine.h>
#include <QtQml/qqmlcomponent.h>
#include "vault.hpp"
QT_BEGIN_NAMESPACE
class VaultPlugin : public QQmlExtensionPlugin
{
Q_OBJECT
Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QQmlExtensionInterface/1.0")
public:
virtual void registerTypes(const char *uri)
{
Q_ASSERT(QLatin1String(uri) == QLatin1String("NemoMobile.Vault"));
qmlRegisterType<Vault>(uri, 1, 0, "Vault");
qmlRegisterType<Vault>(uri, 1, 1, "Vault");
}
void initializeEngine(QQmlEngine *engine, const char *uri)
{
Q_UNUSED(uri);
Q_UNUSED(engine);
}
};
QT_END_NAMESPACE
#include "plugin.moc"
module NemoMobile.Vault
plugin vault-declarative
This diff is collapsed.
#ifndef QML_VAULT_H
#define QML_VAULT_H
#include <QObject>
#include <QThread>
#include <QStringList>
#include <QVariantMap>
class QJSValue;
class Worker;
class Vault : public QObject
{
Q_OBJECT
Q_PROPERTY(QString root READ root WRITE setRoot NOTIFY rootChanged)
Q_PROPERTY(QString backupHome READ backupHome WRITE setBackupHome NOTIFY backupHomeChanged)
public:
enum ImportExportAction {
Export,
Import
};
Q_ENUMS(ImportExportAction);
enum Operation {
Connect,
Backup,
Restore,
RemoveSnapshot,
ExportImportPrepare,
ExportImportExecute,
Data,
Maintenance,
ExportSnapshot
}
Q_ENUMS(Operation);
enum DataType {
SnapshotUnits
}
Q_ENUMS(DataType);
explicit Vault(QObject *parent = nullptr);
~Vault();
QString root() const;
QString backupHome() const;
void setRoot(const QString &root);
void setBackupHome(const QString &home);
Q_INVOKABLE void connectVault(bool reconnect);
Q_INVOKABLE void startBackup(const QString &message, const QStringList &units);
Q_INVOKABLE void startRestore(const QString &snapshot, const QStringList &units);
Q_INVOKABLE QStringList snapshots() const;
Q_INVOKABLE QVariantMap units() const;
Q_INVOKABLE void resetHead();
Q_INVOKABLE void removeSnapshot(const QString &name);
Q_INVOKABLE void exportImportPrepare(ImportExportAction action, const QString &path);
Q_INVOKABLE void exportImportExecute();