[API CHANGE] Add a new "pkg_install_verify" action to sysadm/pkg

Add an "action" = "pkg_install_verify" option to the sysadm/pkg API call.
REQUIRED: "pkg_origins" - string or array of pkg origins
	"repo" - remote repository that the package will be installed from.
----------

REST Request (example):
-------------------------------
PUT /sysadm/pkg
{
   "repo" : "trueos-major",
   "action" : "pkg_install_verify",
   "pkg_origins" : [
      "www/qupzilla-qt5-webkit"
   ]
}

WebSocket Request:
-------------------------------
{
   "name" : "pkg",
   "namespace" : "sysadm",
   "id" : "fooid",
   "args" : {
      "action" : "pkg_install_verify",
      "pkg_origins" : [
         "www/qupzilla-qt5-webkit"
      ],
      "repo" : "trueos-major"
   }
}

Response:
-------------------------------
{
  "args": {
    "pkg_install_verify": {
      "conflicts": [],
      "install": {
        "qupzilla-qt5-webkit": {
          "comment": "Web browser based on WebKit engine and Qt Framework",
          "flatsize": "13380132",
          "name": "qupzilla-qt5-webkit",
          "origin": "www/qupzilla-qt5-webkit",
          "pkgsize": "2574460",
          "version": "1.8.9_3"
        }
      }
    }
  },
  "id": "fooid",
  "name": "response",
  "namespace": "sysadm"
}
This commit is contained in:
Ken Moore
2017-08-01 13:08:51 -04:00
parent 09c41edaca
commit 00251895a3
3 changed files with 121 additions and 12 deletions

View File

@@ -865,20 +865,20 @@ RestOutputStruct::ExitCode WebSocket::EvaluateSysadmPkgRequest(const QJsonValue
if(in_args.toObject().value("pkg_origins").isString()){ pkgs << in_args.toObject().value("pkg_origins").toString(); }
else if(in_args.toObject().value("pkg_origins").isArray()){ pkgs = JsonArrayToStringList(in_args.toObject().value("pkg_origins").toArray()); }
}
//Parse the action and perform accordingly
if(act=="pkg_info"){
//OPTIONAL: "pkg_origins" OR "category"
//OPTIONAL: "repo"
//OPTIONAL: "result" = "full" or "simple" (Default: "simple")
bool fullresults = false;
bool fullresults = false;
if(in_args.toObject().contains("result")){ fullresults = (in_args.toObject().value("result").toString()=="full"); }
//Now run the info fetch routine
QJsonObject info = sysadm::PKG::pkg_info(pkgs, repo, cat, fullresults);
if(!info.isEmpty()){ out->insert("pkg_info",info); }
else{ return RestOutputStruct::NOCONTENT; }
}else if(act=="pkg_search" && in_args.toObject().contains("search_term")){
//REQUIRED: "search_term" (string to search for)
//OPTIONAL: "repo"
@@ -899,22 +899,26 @@ RestOutputStruct::ExitCode WebSocket::EvaluateSysadmPkgRequest(const QJsonValue
}else{
return RestOutputStruct::NOCONTENT;
}
}else if(act=="list_categories"){
//OPTIONAL: "repo"
QJsonArray cats = sysadm::PKG::list_categories(repo);
if(!cats.isEmpty()){ out->insert("list_categories", cats); }
else{ return RestOutputStruct::NOCONTENT; }
}else if(act=="list_repos"){
QJsonArray repos = sysadm::PKG::list_repos();
if(!repos.isEmpty()){ out->insert("list_repos", repos); }
else{ return RestOutputStruct::NOCONTENT; }
}else if(act=="pkg_install" && !pkgs.isEmpty() ){
//REQUIRED: "pkg_origins"
//OPTIONAL: "repo" (pkg will determine the best repo to use if not supplied)
out->insert("pkg_install", sysadm::PKG::pkg_install(pkgs,repo));
}else if(act=="pkg_install_verify"){
//REQUIRED: "pkg_origins", "repo"
out->insert("pkg_install_verify", sysadm::PKG::evaluateInstall(pkgs,repo) );
}else if(act=="pkg_remove" && !pkgs.isEmpty() ){
//REQUIRED: "pkg_origins"
//OPTIONAL: "recursive"="true" or "false" (default: "true")
@@ -923,10 +927,10 @@ RestOutputStruct::ExitCode WebSocket::EvaluateSysadmPkgRequest(const QJsonValue
out->insert("pkg_remove", sysadm::PKG::pkg_remove(pkgs, recursive));
}else if(act=="pkg_lock" && !pkgs.isEmpty() ){
//REQUIRED: "pkg_origins"
out->insert("pkg_lock", sysadm::PKG::pkg_lock(pkgs));
out->insert("pkg_lock", sysadm::PKG::pkg_lock(pkgs));
}else if(act=="pkg_unlock" && !pkgs.isEmpty() ){
//REQUIRED: "pkg_origins"
out->insert("pkg_unlock", sysadm::PKG::pkg_unlock(pkgs));
out->insert("pkg_unlock", sysadm::PKG::pkg_unlock(pkgs));
}else if(act=="pkg_update"){
//OPTIONAL: "force" = ["true"/"false"] (default: "false")
bool force = false;
@@ -944,7 +948,7 @@ RestOutputStruct::ExitCode WebSocket::EvaluateSysadmPkgRequest(const QJsonValue
//unknown action
return RestOutputStruct::BADREQUEST;
}
return RestOutputStruct::OK;
}

View File

@@ -14,6 +14,20 @@ using namespace sysadm;
// ==================
// INLINE FUNCTIONS
// ==================
inline QStringList ids_from_origins(QStringList origins, QSqlDatabase DB){
QSqlQuery q("SELECT id FROM packages WHERE origin IN ('"+origins.join("', '")+"')",DB);
QStringList out;
while(q.next()){ out << q.value("id").toString(); }
return out;
}
inline QStringList ids_from_names(QStringList names, QSqlDatabase DB){
QSqlQuery q("SELECT id FROM packages WHERE name IN ('"+names.join("', '")+"')",DB);
QStringList out;
while(q.next()){ out << q.value("id").toString(); }
return out;
}
//Get annotation variable/values
inline void annotations_from_ids(QStringList var_ids, QStringList val_ids, QJsonObject *out, QSqlDatabase DB){
//Note: Both input lists *must* be the same length (one variable for one value)
@@ -47,7 +61,7 @@ inline QStringList origins_from_package_ids(QStringList ids, QSqlDatabase DB){
while(q.next()){ out << q.value("origin").toString(); }
return out;
}
//Generic ID's -> Names function (known databases: users, groups, licenses, shlibs, categories )
//Generic ID's -> Names function (known databases: users, groups, licenses, shlibs, categories, packages )
inline QStringList names_from_ids(QStringList ids, QString db, QSqlDatabase DB){
QSqlQuery q("SELECT name FROM "+db+" WHERE id IN ('"+ids.join("', '")+"')",DB);
QStringList out;
@@ -68,6 +82,26 @@ inline QStringList requires_from_ids(QStringList ids, QSqlDatabase DB){
while(q.next()){ out << q.value("require").toString(); }
return out;
}
//conflict ID's from package ID's
inline QStringList conflicts_from_ids(QStringList ids, QSqlDatabase DB){
QSqlQuery q("SELECT conflict_id FROM pkg_conflicts WHERE package_id IN ('"+ids.join("', '")+"')", DB);
QStringList out;
while(q.next()){ out << q.value("conflict_id").toString(); }
qDebug() << "Last Conflict detection Error:" << q.lastError().text();
return out;
}
//dependencies from package ID's
inline QStringList depends_from_ids(QStringList ids, QSqlDatabase DB){
//Note: This returns package names, not ID's
QSqlQuery q("SELECT name FROM deps WHERE package_id IN ('"+ids.join("', '")+"')", DB);
QStringList out;
while(q.next()){ out << q.value("name").toString(); }
return out;
}
inline QString getRepoFile(QString repo){
if(repo=="local"){ return "/var/db/pkg/local.sqlite"; }
else{ return ("/var/db/pkg/repo-"+repo+".sqlite"); }
@@ -204,7 +238,7 @@ QJsonObject PKG::pkg_info(QStringList origins, QString repo, QString category, b
QSqlQuery q15("SELECT require_id FROM pkg_requires WHERE package_id = '"+id+"'", DB);
tmpList.clear();
while(q15.next()){ tmpList << q15.value("require_id").toString(); }
if(!tmpList.isEmpty()){ info.insert("requires", QJsonArray::fromStringList(requires_from_ids(tmpList, DB)) ); }
if(!tmpList.isEmpty()){ info.insert("requires", QJsonArray::fromStringList(requires_from_ids(tmpList, DB)) ); }\
//Now insert this information into the main object
retObj.insert(origin,info);
} //end loop over pkg matches
@@ -350,6 +384,75 @@ QJsonArray PKG::list_repos(bool updated){
return QJsonArray::fromStringList(found);
}
QJsonObject PKG::evaluateInstall(QStringList origins, QString repo){
qDebug() << "Verify Install:" << origins << repo;
QJsonObject out;
if(repo=="local" || origins.isEmpty()){ return out; } //nothing to do
QString dbconn = openDB(repo);
QString ldbconn = openDB("local");
if(!dbconn.isEmpty()){
QSqlDatabase DB = QSqlDatabase::database(dbconn);
QSqlDatabase LDB = QSqlDatabase::database(ldbconn);
if(!DB.isOpen() || !LDB.isOpen()){ return out; } //could not open DB (file missing?)
//First get the list of all packages which need to be installed (ID's) from the remote database
QStringList toInstall_id;
QStringList tmp = names_from_ids( ids_from_origins(origins, DB), "packages", DB);
//qDebug() << " - Initial names:" << tmp;
while(!tmp.isEmpty()){
QStringList ids = ids_from_names(tmp, DB);
for(int i=0; i<ids.length(); i++){
if(toInstall_id.contains(ids[i])){ ids.removeAt(i); i--; } //remove any duplicate/evaluated ID's
}
if(ids.isEmpty()){ break; } //stop the loop - found the last round of dependencies
toInstall_id << ids; //add these to the list which are going to get installed
tmp = depends_from_ids(ids, DB); //now get the depdendencies of these packages
//qDebug() << " - Iteration names:" << tmp;
}
//Now go through and remove any packages from the list which are already installed locally
QStringList names = names_from_ids(toInstall_id, "packages", DB); //same order
//qDebug() << " - Total Names:" << names;
QStringList local_names = names_from_ids( ids_from_names(names, LDB), "packages", LDB);
//qDebug() << " - Local Names:" << local_names;
for(int i=0; i<local_names.length(); i++){
names.removeAll(local_names[i]);
}
qDebug() << " - Filtered Names:" << names;
toInstall_id = ids_from_names(names, DB); //now get the shorter/filtered list of ID's (remote)
//qDebug() << " - Filtered ID's:" << toInstall_id;
//Get the list of conflicting packages which are already installed
QStringList conflict_ids = conflicts_from_ids(toInstall_id, DB); //also get the list of any conflicts for these packages
conflict_ids.removeDuplicates();
QStringList conflict_names = names_from_ids(conflict_ids, "packages", DB);
qDebug() << " - Conflicts (remote):" << conflict_ids << conflict_names;
out.insert("conflicts", QJsonArray::fromStringList(names_from_ids( ids_from_names(conflict_names, LDB), "packages", LDB) ) );
//Now assemble all the information about the packages (remote database)
QJsonObject install;
//qDebug() << "Perform Query";
QSqlQuery qi("SELECT * FROM packages WHERE id IN ('"+toInstall_id.join("', '")+"')", DB);
while(qi.next()){
QJsonObject obj;
obj.insert( "name", qi.value("name").toString());
obj.insert( "origin", qi.value("origin").toString());
obj.insert( "pkgsize", qi.value("pkgsize").toString());
obj.insert( "flatsize", qi.value("flatsize").toString());
obj.insert( "version", qi.value("version").toString());
obj.insert( "comment", qi.value("comment").toString());
install.insert(qi.value("name").toString(), obj);
}
qDebug() << "Final Install Object:" << install;
//qDebug() << "Last Query Error:" << qi.lastError().text();
//Add the info to the output object and close the databases
out.insert("install", install);
DB.close();
LDB.close();
} //force DB out of scope
QSqlDatabase::removeDatabase(dbconn);
QSqlDatabase::removeDatabase(ldbconn);
return out;
}
//=================
//pkg modification routines (dispatcher events for notifications)
//=================

View File

@@ -24,6 +24,8 @@ public:
static QStringList pkg_search(QString repo, QString searchterm, QStringList searchexcludes, QString category = "");
static QJsonArray list_categories(QString repo);
static QJsonArray list_repos(bool updated = false);
static QJsonObject evaluateInstall(QStringList origins, QString repo); //evaluate what will be done if these packages are installed
//pkg modification routines (dispatcher events for notifications)
static QJsonObject pkg_install(QStringList origins, QString repo);