Verified Commit 97a46bf7 authored by Karel Koci's avatar Karel Koci 🤘

Add pkgsorter lua module

This module is for plan sorting. Will be used as replacement of current
algorithm.
parent cf33cd41
......@@ -336,7 +336,8 @@ critical::
consistent state or be able to at least fix it without access to
network. Other packages may stop working if the update is
interrupted at the wrong time (for example by a power outage), but
would be fixed by another finished updater run.
would be fixed by another finished updater run. For critical packages
`priority` field is ignored.
optional::
Set this to `true` to not fail if packages is not available from any configured
repository. Be aware that this has implications if form of possible removed
......
......@@ -34,7 +34,9 @@ libupdater_MODULES := \
util \
syscnf \
multiwrite \
logging
logging \
pkgsorter
ifdef COV
libupdater_MODULES += lcoverage.embed
endif
......
......@@ -28,6 +28,7 @@
#include "syscnf.h"
#include "uri_lua.h"
#include "picosat.h"
#include "pkgsorter.h"
#include <lua.h>
#include <lualib.h>
......@@ -1026,6 +1027,7 @@ struct interpreter *interpreter_create(struct events *events) {
syscnf_mod_init(L);
uri_mod_init(L);
picosat_mod_init(L);
pkgsorter_mod_init(L);
#ifdef COVERAGE
interpreter_load_coverage(result);
#endif
......
......@@ -298,7 +298,7 @@ The format of journal returned by recover is a table with records.
Each record contains `type` ‒ one of the types above, and `params` ‒
table with all the parameters stored with the record.
Pisocat
Picosat
-------
Picosat can be used trough module `picosat`. Here is briefly described
......@@ -328,6 +328,28 @@ object with variable you are interested in. It returns true or false.
It can also return nil if variable was added after `satisfiable` method
call.
PkgSorter
---------
This is module implementing tree sorting algorithm with priorities.
TODO document what priority for node and for edge means
You can create new instance by calling `pkgsorter.new` function.
It returns object with following methods:
node(name, priority):: Create new node with given priority.
edge(priority, from, to, reverse):: Add edge between two nodes.
Note that if you have edge from low priority node to higher priority
one then priority of such node is increased to be the same.
prune():: Prune edges forming cycles and return report about it.
TODO
isnode(name):: Check if we have node of given name.
iterator(root):: Returns iterator to go trough resulting order. You
have to call it after `prune` is called. As optional argument you
can specify root then instead of looking for all roots only given
one is expanded and iterated trough.
Others
------
......
This diff is collapsed.
/*
* Copyright 2017, CZ.NIC z.s.p.o. (http://www.nic.cz/)
*
* This file is part of the turris updater.
*
* Updater is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Updater is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Updater. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef UPDATER_PKGSORTER_H
#define UPDATER_PKGSORTER_H
#include <lua.h>
// Create orderer module and inject it to the lua state
void pkgsorter_mod_init(lua_State *L) __attribute__((nonnull));
#endif
......@@ -23,8 +23,8 @@
#include "events.h"
#include <stdlib.h>
#include <stdio.h>
#include <stdbool.h>
#include <stdio.h>
#include <alloca.h>
#include <unistd.h>
......
......@@ -32,7 +32,8 @@ LUA_TESTS := \
cleanup \
uri \
picosat \
updater
pkgsorter \
updater
ifdef COV
LUA_TESTS += coverage
......
--[[
Copyright 2017, CZ.NIC z.s.p.o. (http://www.nic.cz/)
This file is part of the turris updater.
Updater is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Updater is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Updater. If not, see <http://www.gnu.org/licenses/>.
]]--
require "lunit"
local table = table
local pkgsorter = require "pkgsorter"
module("pkgsort-tests", package.seeall, lunit.testcase)
function test_no_edges()
local ps = pkgsorter.new()
ps:node("t1", 2)
ps:node("t2", 3)
ps:node("t3", 1)
ps:node("t4", 4)
assert_table_equal({}, ps:prune())
local res = {}
for node in ps:iterator() do
table.insert(res, node)
end
assert_table_equal({"t4", "t2", "t1", "t3"}, res)
end
function test_iterator_root()
local ps = pkgsorter.new()
ps:node("t1", 1)
ps:node("t2", 2)
ps:node("t3", 3)
assert_table_equal({}, ps:prune())
local res = {}
for node in ps:iterator("t2") do
table.insert(res, node)
end
assert_table_equal({"t2"}, res)
end
function test_simple()
local ps = pkgsorter.new()
ps:node("t1", 1)
ps:node("t2", 2)
ps:node("t3", 3)
ps:edge(pkgsorter.DEPENDS, "t2", "t3")
ps:edge(pkgsorter.DEPENDS, "t3", "t1")
assert_table_equal({}, ps:prune())
local res = {}
for node in ps:iterator() do
table.insert(res, node)
end
assert_table_equal({"t1", "t3", "t2"}, res)
end
function test_rev_edge()
local ps = pkgsorter.new()
ps:node("t1", 1)
ps:node("t2", 2)
ps:node("t3", 3)
ps:edge(pkgsorter.DEPENDS, "t2", "t3")
ps:edge(pkgsorter.PROVIDES, "t3", "t1", true)
assert_table_equal({}, ps:prune())
local res = {}
for node in ps:iterator() do
table.insert(res, node)
end
assert_table_equal({"t3", "t1", "t2"}, res)
end
function test_edge_type_order()
local ps = pkgsorter.new()
ps:node("t1", 1)
ps:node("t2", 1)
ps:node("t3", 1)
ps:node("t4", 1)
ps:edge(pkgsorter.FORCE, "t1", "t2")
ps:edge(pkgsorter.DEPENDS, "t1", "t3")
ps:edge(pkgsorter.PROVIDES, "t1", "t4")
assert_table_equal({}, ps:prune())
local res = {}
for node in ps:iterator() do
table.insert(res, node)
end
assert_table_equal({"t2", "t3", "t4", "t1"}, res)
end
-- When edges has same type then we look to priority of target node
function test_priority_order()
local ps = pkgsorter.new()
ps:node("t1", 1)
ps:node("t2", 2)
ps:node("t3", 3)
ps:node("t4", 4)
ps:edge(pkgsorter.DEPENDS, "t1", "t2")
ps:edge(pkgsorter.DEPENDS, "t1", "t3")
ps:edge(pkgsorter.DEPENDS, "t1", "t4")
assert_table_equal({}, ps:prune())
local res = {}
for node in ps:iterator() do
table.insert(res, node)
end
assert_table_equal({"t4", "t3", "t2", "t1"}, res)
end
-- When some node with higher priority is underneath of less priority node then it's priority should be escalated.
function test_priority_elevation()
local ps = pkgsorter.new()
ps:node("t1", 1)
ps:node("t2", 2)
ps:node("t3", 3)
ps:edge(pkgsorter.DEPENDS, "t1", "t3")
assert_table_equal({}, ps:prune())
local res = {}
for node in ps:iterator() do
table.insert(res, node)
end
assert_table_equal({"t3", "t1", "t2"}, res)
end
function test_simple_prune()
local ps = pkgsorter.new()
ps:node("t1", 1)
ps:node("t2", 2)
ps:edge(pkgsorter.DEPENDS, "t1", "t2")
ps:edge(pkgsorter.FORCE, "t2", "t1")
assert_table_equal({{
["type"] = pkgsorter.DEPENDS,
from = "t1",
to = "t2",
cycle = {["t1"] = true, ["t2"] = true}
}}, ps:prune())
local res = {}
for node in ps:iterator() do
table.insert(res, node)
end
assert_table_equal({"t1", "t2"}, res)
end
--[[
Here are two potential cycles. One cuts edge t4->t2. Another one cuts t1->t2.
Why that is is little bit complicated. It's because edges t3->t4 and t2->t3 points
to nodes with higher priority (2) and only other node in given cycle is t4->t1
but that edge has higher type. So only solution is to cut t1->t2.
]]
function test_prune()
local ps = pkgsorter.new()
ps:node("t1", 1)
ps:node("t2", 1)
ps:node("t3", 2)
ps:node("t4", 2)
ps:edge(pkgsorter.DEPENDS, "t1", "t2")
ps:edge(pkgsorter.DEPENDS, "t2", "t3")
ps:edge(pkgsorter.DEPENDS, "t3", "t4")
ps:edge(pkgsorter.PROVIDES, "t4", "t2", true)
ps:edge(pkgsorter.FORCE, "t4", "t1")
assert_table_equal({
{
["type"] = pkgsorter.DEPENDS,
from = "t1",
to = "t2",
cycle = {["t1"] = true, ["t2"] = true, ["t3"] = true, ["t4"] = true}
},
{
["type"] = pkgsorter.PROVIDES,
from = "t4",
to = "t2",
cycle = {["t2"] = true, ["t3"] = true, ["t4"] = true}
},
}, ps:prune())
local res = {}
for node in ps:iterator() do
table.insert(res, node)
end
assert_table_equal({"t1", "t4", "t3", "t2"}, res)
end
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment