WIP: create packages from templates

Now, packages are actually created.
This commit is contained in:
Matthias Koefferlein 2017-03-23 23:46:25 +01:00
parent 4a90479187
commit 10b09c3575
5 changed files with 182 additions and 48 deletions

View File

@ -29,6 +29,7 @@
#include <QFileInfo>
#include <QDir>
#include <QResource>
namespace lay
{
@ -215,8 +216,91 @@ Salt::remove_grain (const SaltGrain &grain)
}
}
namespace
{
/**
* @brief A helper class required because directory traversal is not supported by QResource directly
* This class supports resource file trees and extraction of a tree from the latter
*/
class ResourceDir
: public QResource
{
public:
using QResource::isFile;
/**
* @brief Constructor
* Creates a resource representing a resource tree.
*/
ResourceDir (const QString &path)
: QResource (path)
{
// .. nothing yet ..
}
/**
* @brief Writes the resource tree to the target directory
* Returns false on error - see log in this case.
*/
bool copy_to (const QDir &target)
{
if (isDir ()) {
QStringList templ_dir = children ();
for (QStringList::const_iterator t = templ_dir.begin (); t != templ_dir.end (); ++t) {
ResourceDir child_res (fileName () + QString::fromUtf8 ("/") + *t);
if (child_res.isFile ()) {
QFile file (target.absoluteFilePath (*t));
if (! file.open (QIODevice::WriteOnly)) {
tl::error << QObject::tr ("Unable to open target file for writing: %1").arg (target.absoluteFilePath (*t));
return false;
}
QByteArray data;
if (child_res.isCompressed ()) {
data = qUncompress ((const unsigned char *)child_res.data (), (int)child_res.size ());
} else {
data = QByteArray ((const char *)child_res.data (), (int)child_res.size ());
}
file.write (data);
file.close ();
} else {
QFileInfo child_dir (target.absoluteFilePath (*t));
if (! child_dir.exists ()) {
if (! target.mkdir (*t)) {
tl::error << QObject::tr ("Unable to create target directory: %1").arg (child_dir.path ());
return false;
}
} else if (! child_dir.isDir ()) {
tl::error << QObject::tr ("Unable to create target directory (is a file already): %1").arg (child_dir.path ());
return false;
}
if (! child_res.copy_to (QDir (target.absoluteFilePath (*t)))) {
return false;
}
}
}
}
return true;
}
};
}
bool
Salt::create_grain (const SaltGrain &templ, SaltGrain &target, SaltDownloadManager &download_manager)
Salt::create_grain (const SaltGrain &templ, SaltGrain &target, SaltDownloadManager *download_manager)
{
tl_assert (!m_root.is_empty ());
@ -243,15 +327,23 @@ Salt::create_grain (const SaltGrain &templ, SaltGrain &target, SaltDownloadManag
// change down to the desired target location and create the directory structure while doing so
std::vector<std::string> name_parts = tl::split (target.name (), "/");
for (std::vector<std::string>::const_iterator n = name_parts.begin (); n != name_parts.end (); ++n) {
QDir subdir (target_dir.filePath (tl::to_qstring (*n)));
if (! subdir.exists ()) {
QFileInfo subdir (target_dir.filePath (tl::to_qstring (*n)));
if (subdir.exists () && ! subdir.isDir ()) {
throw tl::Exception (tl::to_string (tr ("Unable to create target directory '%1' for installing package - is already a file").arg (subdir.path ())));
} else if (! subdir.exists ()) {
if (! target_dir.mkdir (tl::to_qstring (*n))) {
throw tl::Exception (tl::to_string (tr ("Unable to create target directory '%1' for installing package").arg (subdir.path ())));
}
if (! target_dir.cd (tl::to_qstring (*n))) {
throw tl::Exception (tl::to_string (tr ("Unable to change to target directory '%1' for installing package").arg (subdir.path ())));
}
} else {
if (! target_dir.cd (tl::to_qstring (*n))) {
throw tl::Exception (tl::to_string (tr ("Unable to change to target directory '%1' for installing package").arg (subdir.path ())));
}
}
}
} catch (tl::Exception &ex) {
@ -261,30 +353,46 @@ Salt::create_grain (const SaltGrain &templ, SaltGrain &target, SaltDownloadManag
bool res = true;
std::string target_name = target.name ();
target = templ;
target.set_path (tl::to_string (target_dir.absolutePath ()));
target.set_name (target_name);
if (! templ.path ().empty ()) {
// if the template represents an actual folder, use the files from there
tl::info << QObject::tr ("Copying package from '%1' to '%2' ..").arg (tl::to_qstring (templ.path ())).arg (tl::to_qstring (target.path ()));
res = tl::cp_dir_recursive (templ.path (), target.path ());
if (templ.path ()[0] != ':') {
// if the template represents an actual folder, use the files from there
tl::info << QObject::tr ("Copying package from '%1' to '%2' ..").arg (tl::to_qstring (templ.path ())).arg (tl::to_qstring (target.path ()));
res = tl::cp_dir_recursive (templ.path (), target.path ());
} else {
// if the template represents a resource path, use the files from there
tl::info << QObject::tr ("Installing package from resource '%1' to '%2' ..").arg (tl::to_qstring (templ.path ())).arg (tl::to_qstring (target.path ()));
res = ResourceDir (tl::to_qstring (templ.path ())).copy_to (QDir (tl::to_qstring (target.path ())));
}
} else if (! templ.url ().empty ()) {
tl_assert (download_manager != 0);
// otherwise download from the URL
tl::info << QObject::tr ("Downloading package from '%1' to '%2' ..").arg (tl::to_qstring (templ.url ())).arg (tl::to_qstring (target.path ()));
res = download_manager.download (templ.url (), target.path ());
res = download_manager->download (templ.url (), target.path ());
}
if (res) {
tl::info << QObject::tr ("Package '%1' installed").arg (tl::to_qstring (target.name ()));
target.set_installed_time (QDateTime::currentDateTime ());
target.save ();
// NOTE: this is a bit brute force .. we could as well try to insert the new grain into the existing structure
refresh ();
} else {
tl::warn << QObject::tr ("Failed to install package '%1' - removing files ..").arg (tl::to_qstring (target.name ()));

View File

@ -152,7 +152,7 @@ public:
*
* Returns true, if the package could be created successfully.
*/
bool create_grain (const SaltGrain &templ, SaltGrain &target, SaltDownloadManager &download_manager);
bool create_grain (const SaltGrain &templ, SaltGrain &target, SaltDownloadManager *download_manager = 0);
signals:
/**

View File

@ -226,7 +226,7 @@ SaltGrain::valid_name (const std::string &n)
// this captures the cases where the extractor skips blanks
// TODO: the extractor should have a "non-blank-skipping" mode
return s == n;
return res == n;
}
bool

View File

@ -25,6 +25,7 @@
#include "laySalt.h"
#include "ui_SaltGrainTemplateSelectionDialog.h"
#include "tlString.h"
#include "tlExceptions.h"
#include <QAbstractItemModel>
#include <QAbstractTextDocumentLayout>
@ -152,7 +153,7 @@ public:
void update ()
{
// @@@
reset ();
}
public:
@ -230,8 +231,8 @@ class SaltGrainTemplateSelectionDialog
: public QDialog, private Ui::SaltGrainTemplateSelectionDialog
{
public:
SaltGrainTemplateSelectionDialog (QWidget *parent)
: QDialog (parent)
SaltGrainTemplateSelectionDialog (QWidget *parent, lay::Salt *salt)
: QDialog (parent), mp_salt (salt)
{
Ui::SaltGrainTemplateSelectionDialog::setupUi (this);
@ -249,11 +250,14 @@ public:
SaltGrain *g = model->grain_from_index (salt_view->currentIndex ());
tl_assert (g != 0);
g->set_name (tl::to_string (name_edit->text ().simplified ()));
return *g;
}
std::string name () const
{
return tl::to_string (name_edit->text ());
}
void accept ()
{
name_alert->clear ();
@ -263,12 +267,23 @@ public:
} else if (! SaltGrain::valid_name (name)) {
name_alert->error () << tr ("Name is not valid (must be composed of letters, digits or underscores.\nGroups and names need to be separated with slashes.");
} else {
// check, if this name does not exist yet
for (Salt::flat_iterator g = mp_salt->begin_flat (); g != mp_salt->end_flat (); ++g) {
if ((*g)->name () == name) {
name_alert->error () << tr ("A package with this name already exists");
return;
}
}
QDialog::accept ();
}
}
private:
lay::Salt m_salt_templates;
lay::Salt *mp_salt;
};
// --------------------------------------------------------------------------------------
@ -310,11 +325,6 @@ SaltManagerDialog::SaltManagerDialog (QWidget *parent)
connect (mp_salt, SIGNAL (collections_changed ()), this, SLOT (salt_changed ()));
// select the first grain
if (model->rowCount (QModelIndex ()) > 0) {
salt_view->setCurrentIndex (model->index (0, 0, QModelIndex ()));
}
salt_changed ();
connect (salt_view->selectionModel (), SIGNAL (currentChanged (const QModelIndex &, const QModelIndex &)), this, SLOT (current_changed ()));
@ -334,40 +344,41 @@ SaltManagerDialog::edit_properties ()
}
}
// @@@
namespace
{
/**
* @brief A helper class required because directory traversal is not supported by QResource directly
*/
class OpenResource
: public QResource
{
public:
using QResource::isDir;
using QResource::isFile;
using QResource::children;
OpenResource (const QString &path)
: QResource (path)
{
// .. nothing yet ..
}
};
}
// @@@
void
SaltManagerDialog::create_grain ()
{
SaltGrainTemplateSelectionDialog temp_dialog (this);
BEGIN_PROTECTED
SaltGrainTemplateSelectionDialog temp_dialog (this, mp_salt);
if (temp_dialog.exec ()) {
// @@@
SaltGrain target;
target.set_name (temp_dialog.name ());
if (mp_salt->create_grain (temp_dialog.templ (), target)) {
// select the new one
SaltModel *model = dynamic_cast <SaltModel *> (salt_view->model ());
if (model) {
for (int i = model->rowCount (QModelIndex ()); i > 0; ) {
--i;
QModelIndex index = model->index (i, 0, QModelIndex ());
SaltGrain *g = model->grain_from_index (index);
if (g && g->name () == target.name ()) {
salt_view->setCurrentIndex (index);
break;
}
}
}
} else {
throw tl::Exception (tl::to_string (tr ("Initialization of new package failed - see log window (File/Log Viewer) for details")));
}
}
END_PROTECTED
}
void
@ -399,11 +410,20 @@ SaltManagerDialog::salt_changed ()
m_current_changed_enabled = true;
if (mp_salt->is_empty ()) {
list_stack->setCurrentIndex (1);
details_frame->hide ();
} else {
list_stack->setCurrentIndex (0);
details_frame->show ();
// select the first grain
if (model->rowCount (QModelIndex ()) > 0) {
salt_view->setCurrentIndex (model->index (0, 0, QModelIndex ()));
}
}
current_changed ();

View File

@ -162,6 +162,13 @@ TEST (2)
EXPECT_EQ (lay::SaltGrain::valid_version ("\t1 . 2.\n3"), true);
EXPECT_EQ (lay::SaltGrain::valid_version ("x"), false);
EXPECT_EQ (lay::SaltGrain::valid_version ("1.2x"), false);
EXPECT_EQ (lay::SaltGrain::valid_name (""), false);
EXPECT_EQ (lay::SaltGrain::valid_name ("x"), true);
EXPECT_EQ (lay::SaltGrain::valid_name ("x1"), true);
EXPECT_EQ (lay::SaltGrain::valid_name ("x1 "), false);
EXPECT_EQ (lay::SaltGrain::valid_name ("x$1"), false);
EXPECT_EQ (lay::SaltGrain::valid_name ("x/y"), true);
EXPECT_EQ (lay::SaltGrain::valid_name ("x_y"), true);
EXPECT_EQ (lay::SaltGrain::compare_versions ("", ""), 0);
EXPECT_EQ (lay::SaltGrain::compare_versions ("1", "2"), -1);
EXPECT_EQ (lay::SaltGrain::compare_versions ("1", ""), 1);
@ -185,7 +192,6 @@ TEST (2)
EXPECT_EQ (lay::SaltGrain::compare_versions ("991", "990"), 1);
}
TEST (3)
{
const QString grain_spec_file = QString::fromUtf8 ("grain.xml");