Add ability to trigger updates from the web (#31)

* add external process spawner and update test cases

* pass no_report to handlers

* provide service api endpoints

* do not spawn process for single architecture run

* pass no report to handlers

* make _call method of handlers public and also simplify process spawn

* move update under add

* implement actions from web page

* clear logging & improve l&f
This commit is contained in:
2021-09-10 00:33:35 +03:00
committed by GitHub
parent 18de70154e
commit 98eb93c27a
101 changed files with 1417 additions and 295 deletions

View File

@ -2,7 +2,7 @@
keys = root,builder,build_details,http
[handlers]
keys = console_handler,build_file_handler,file_handler,http_handler,syslog_handler
keys = console_handler,syslog_handler
[formatters]
keys = generic_format,syslog_format
@ -13,24 +13,6 @@ level = DEBUG
formatter = generic_format
args = (sys.stderr,)
[handler_file_handler]
class = logging.handlers.RotatingFileHandler
level = DEBUG
formatter = generic_format
args = ("/var/log/ahriman/ahriman.log", "a", 20971520, 20)
[handler_build_file_handler]
class = logging.handlers.RotatingFileHandler
level = DEBUG
formatter = generic_format
args = ("/var/log/ahriman/build.log", "a", 20971520, 20)
[handler_http_handler]
class = logging.handlers.RotatingFileHandler
level = DEBUG
formatter = generic_format
args = ("/var/log/ahriman/http.log", "a", 20971520, 20)
[handler_syslog_handler]
class = logging.handlers.SysLogHandler
level = DEBUG

View File

@ -5,11 +5,7 @@
<meta name="viewport" content="width=device-width, initial-scale=1">
<script src="https://kit.fontawesome.com/0d6d6d5226.js" crossorigin="anonymous"></script>
<link href="https://unpkg.com/bootstrap-table@1.18.3/dist/bootstrap-table.min.css" rel="stylesheet">
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.0/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-KyZXEAg3QhqLMpG8r+8fhAXLRk2vvoC2f3B09zVXn8CA5QIVfZOJ3BCsw2P0p/We" crossorigin="anonymous">
{% include "style.jinja2" %}
{% include "utils/style.jinja2" %}
</head>
<body>
@ -26,20 +22,46 @@
</div>
<div class="container">
<table id="packages" class="table table-striped table-hover" cellspacing="0"
data-toggle="table"
data-pagination="true"
data-page-siz="10"
<div id="toolbar">
{% if not auth_enabled or auth_username is not none %}
<button id="add" class="btn btn-primary" data-bs-toggle="modal" data-bs-target="#addForm">
<i class="fa fa-plus"></i> Add
</button>
<button id="update" class="btn btn-secondary" onclick="updatePackages()" disabled>
<i class="fa fa-play"></i> Update
</button>
<button id="remove" class="btn btn-danger" onclick="removePackages()" disabled>
<i class="fa fa-trash"></i> Remove
</button>
{% endif %}
</div>
<table id="packages" class="table table-striped table-hover"
data-click-to-select="true"
data-export-options='{"fileName": "packages"}'
data-page-list="[10, 25, 50, 100, all]"
data-page-size="10"
data-pagination="true"
data-resizable="true"
data-search="true"
data-show-columns="true"
data-show-columns-search="true"
data-show-columns-toggle-all="true"
data-show-export="true"
data-sortable="true">
data-show-fullscreen="true"
data-show-search-clear-button="true"
data-sortable="true"
data-sort-reset="true"
data-toggle="table"
data-toolbar="#toolbar">
<thead class="table-primary">
<tr>
<th data-sortable="true">package base</th>
<th data-sortable="true">packages</th>
<th data-checkbox="true"></th>
<th data-sortable="true" data-switchable="false">package base</th>
<th data-sortable="true">version</th>
<th data-sortable="true">packages</th>
<th data-sortable="true" data-visible="false">groups</th>
<th data-sortable="true" data-visible="false">licenses</th>
<th data-sortable="true">last update</th>
<th data-sortable="true">status</th>
</tr>
@ -48,10 +70,13 @@
<tbody>
{% if authorized %}
{% for package in packages %}
<tr>
<tr data-package-base="{{ package.base }}">
<td data-checkbox="true"></td>
<td><a href="{{ package.web_url }}" title="{{ package.base }}">{{ package.base }}</a></td>
<td>{{ package.packages|join("<br>"|safe) }}</td>
<td>{{ package.version }}</td>
<td>{{ package.packages|join("<br>"|safe) }}</td>
<td>{{ package.groups|join("<br>"|safe) }}</td>
<td>{{ package.licenses|join("<br>"|safe) }}</td>
<td>{{ package.timestamp }}</td>
<td class="table-{{ package.status_color }}">{{ package.status }}</td>
</tr>
@ -77,45 +102,23 @@
{% if auth_username is none %}
<button type="button" class="btn btn-link" data-bs-toggle="modal" data-bs-target="#loginForm" style="text-decoration: none">login</button>
{% else %}
<form action="/logout" method="post">
<button type="submit" class="btn btn-link" style="text-decoration: none">logout ({{ auth_username }})</button>
<form action="/user-api/v1/logout" method="post">
<button class="btn btn-link" style="text-decoration: none">logout ({{ auth_username }})</button>
</form>
{% endif %}
{% endif %}
</footer>
</div>
<div id="loginForm" tabindex="-1" role="dialog" class="modal fade">
<div class="modal-dialog modal-login">
<div class="modal-content">
<form action="/login" method="post">
<div class="modal-header">
<h4 class="modal-title">login</h4>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="close"></button>
</div>
<div class="modal-body">
<div class="form-group">
<label for="username" class="col-form-label">username</label>
<input id="username" type="text" class="form-control" placeholder="enter username" name="username" required>
</div>
<div class="form-group">
<label for="password" class="col-form-label">password</label>
<input id="password" type="password" class="form-control" placeholder="enter username" name="password" required>
</div>
</div>
<div class="modal-footer">
<input type="submit" class="btn btn-primary" value="Login">
</div>
</form>
</div>
</div>
</div>
{% if auth_enabled %}
{% include "build-status/login-modal.jinja2" %}
{% endif %}
<script src="https://cdn.jsdelivr.net/npm/jquery/dist/jquery.min.js"></script>
<script src="https://unpkg.com/tableexport.jquery.plugin/tableExport.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.1.0/dist/js/bootstrap.bundle.min.js" integrity="sha384-U1DAWAznBHeqEIlVSCgzq+c9gqGAJn5c/t99JyeKa9xxaYpSvHU5awsuZVVFIhvj" crossorigin="anonymous"></script>
<script src="https://unpkg.com/bootstrap-table@1.18.3/dist/bootstrap-table.min.js"></script>
<script src="https://unpkg.com/bootstrap-table@1.18.3/dist/extensions/export/bootstrap-table-export.min.js"></script>
{% include "build-status/package-actions-modals.jinja2" %}
{% include "utils/bootstrap-scripts.jinja2" %}
{% include "build-status/package-actions-script.jinja2" %}
</body>

View File

@ -0,0 +1,29 @@
<div id="loginForm" tabindex="-1" role="dialog" class="modal fade">
<div class="modal-dialog" role="document">
<div class="modal-content">
<form action="/user-api/v1/login" method="post">
<div class="modal-header">
<h4 class="modal-title">Login</h4>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="close"></button>
</div>
<div class="modal-body">
<div class="form-group row">
<label for="username" class="col-sm-2 col-form-label">Username</label>
<div class="col-sm-10">
<input id="username" type="text" class="form-control" placeholder="enter username" name="username" required>
</div>
</div>
<div class="form-group row">
<label for="password" class="col-sm-2 col-form-label">Password</label>
<div class="col-sm-10">
<input id="password" type="password" class="form-control" placeholder="enter password" name="password" required>
</div>
</div>
</div>
<div class="modal-footer">
<button class="btn btn-primary">Login</button>
</div>
</form>
</div>
</div>
</div>

View File

@ -0,0 +1,57 @@
<div id="addForm" tabindex="-1" role="dialog" class="modal fade">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<h4 class="modal-title">Add new packages</h4>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="close"></button>
</div>
<div class="modal-body">
<div class="form-group row">
<label for="package" class="col-sm-2 col-form-label">Package</label>
<div class="col-sm-10">
<input id="package" type="text" list="knownPackages" class="form-control" placeholder="AUR package" name="package" required>
<datalist id="knownPackages"></datalist>
</div>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Close</button>
<button type="button" class="btn btn-primary" data-bs-dismiss="modal" onclick="addPackages()">Add</button>
</div>
</div>
</div>
</div>
<div id="failedForm" tabindex="-1" role="dialog" class="modal fade">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header bg-danger">
<h4 class="modal-title">Failed</h4>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="close"></button>
</div>
<div class="modal-body">
<p>Packages update has failed.</p>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-primary" data-bs-dismiss="modal">Close</button>
</div>
</div>
</div>
</div>
<div id="successForm" tabindex="-1" role="dialog" class="modal fade">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header bg-success">
<h4 class="modal-title">Success</h4>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="close"></button>
</div>
<div class="modal-body">
<p>Packages update has been run.</p>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-primary" data-bs-dismiss="modal">Close</button>
</div>
</div>
</div>
</div>

View File

@ -0,0 +1,76 @@
<script>
const $remove = $("#remove")
const $update = $("#update")
const $table = $("#packages")
$table.on("check.bs.table uncheck.bs.table check-all.bs.table uncheck-all.bs.table",
function () {
$remove.prop("disabled", !$table.bootstrapTable("getSelections").length)
$update.prop("disabled", !$table.bootstrapTable("getSelections").length)
})
const $successForm = $("#successForm")
$successForm.on("hidden.bs.modal", function() { window.location.reload(); })
const $failedForm = $("#failedForm")
$failedForm.on("hidden.bs.modal", function() { window.location.reload(); })
const $package = $("#package")
const $knownPackages = $("#knownPackages")
$package.keyup(function () {
const $this = $(this);
clearTimeout($this.data("timeout"));
$this.data("timeout", setTimeout($.proxy(function () {
const $value = $package.val();
$.ajax({
url: "/service-api/v1/search",
data: {"for": $value},
type: "GET",
dataType: "json",
success: function (resp) {
const $options = resp.map(function (pkg) {
const $option = document.createElement("option");
$option.value = pkg;
return $option;
});
$knownPackages.empty().append($options);
$this.focus()
},
})
}, this), 500));
})
function doPackageAction($uri, $packages) {
if ($packages.length === 0)
return;
$.ajax({
url: $uri,
data: JSON.stringify({packages: $packages}),
type: "POST",
contentType: "application/json",
success: function (_) { $successForm.modal("show"); },
error: function (_) { $failedForm.modal("show"); },
})
}
function getSelection() {
return $.map($table.bootstrapTable("getSelections"), function(row) {
return row._data["package-base"];
})
}
function addPackages() {
const $packages = [$package.val()]
doPackageAction("/service-api/v1/add", $packages);
}
function removePackages() { doPackageAction("/service-api/v1/remove", getSelection()); }
function updatePackages() { doPackageAction("/service-api/v1/add", getSelection()); }
$(function () {
$table.bootstrapTable("uncheckAll");
})
</script>

View File

@ -6,15 +6,13 @@
<meta name="viewport" content="width=device-width, initial-scale=1">
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.0/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-KyZXEAg3QhqLMpG8r+8fhAXLRk2vvoC2f3B09zVXn8CA5QIVfZOJ3BCsw2P0p/We" crossorigin="anonymous">
{% include "style.jinja2" %}
{% include "utils/style.jinja2" %}
</head>
<body>
<div class="container">
<table id="packages" class="table table-striped" cellspacing="0">
<table id="packages" class="table table-striped">
<thead class="table-primary">
<tr>
<th>package</th>

View File

@ -5,11 +5,7 @@
<meta name="viewport" content="width=device-width, initial-scale=1">
<script src="https://kit.fontawesome.com/0d6d6d5226.js" crossorigin="anonymous"></script>
<link href="https://unpkg.com/bootstrap-table@1.18.3/dist/bootstrap-table.min.css" rel="stylesheet">
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.0/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-KyZXEAg3QhqLMpG8r+8fhAXLRk2vvoC2f3B09zVXn8CA5QIVfZOJ3BCsw2P0p/We" crossorigin="anonymous">
{% include "style.jinja2" %}
{% include "utils/style.jinja2" %}
</head>
<body>
@ -30,19 +26,32 @@ SigLevel = Database{% if has_repo_signed %}Required{% else %}Never{% endif %} Pa
</div>
<div class="container">
<table id="packages" class="table table-striped table-hover" cellspacing="0"
data-toggle="table"
data-pagination="true"
data-page-siz="10"
<table id="packages" class="table table-striped table-hover"
data-export-options='{"fileName": "packages"}'
data-page-list="[10, 25, 50, 100, all]"
data-page-size="10"
data-pagination="true"
data-resizable="true"
data-search="true"
data-show-columns="true"
data-show-columns-search="true"
data-show-columns-toggle-all="true"
data-show-export="true"
data-sortable="true">
data-show-fullscreen="true"
data-show-search-clear-button="true"
data-sortable="true"
data-sort-reset="true"
data-toggle="table">
<thead class="table-primary">
<tr>
<th data-sortable="true">package</th>
<th data-sortable="true" data-switchable="false">package</th>
<th data-sortable="true">version</th>
<th data-sortable="true" data-visible="false">architecture</th>
<th data-sortable="true" data-visible="false">description</th>
<th data-sortable="true" data-visible="false">upstream url</th>
<th data-sortable="true" data-visible="false">licenses</th>
<th data-sortable="true" data-visible="false">groups</th>
<th data-sortable="true" data-visible="false">depends</th>
<th data-sortable="true">archive size</th>
<th data-sortable="true">installed size</th>
<th data-sortable="true">build date</th>
@ -54,6 +63,12 @@ SigLevel = Database{% if has_repo_signed %}Required{% else %}Never{% endif %} Pa
<tr>
<td><a href="{{ link_path }}/{{ package.filename }}" title="{{ package.name }}">{{ package.name }}</a></td>
<td>{{ package.version }}</td>
<td>{{ package.architecture }}</td>
<td>{{ package.description }}</td>
<td><a href="{{ package.url }}" title="{{ package.name }} upstream url">{{ package.url }}</a></td>
<td>{{ package.licenses|join("<br>"|safe) }}</td>
<td>{{ package.groups|join("<br>"|safe) }}</td>
<td>{{ package.depends|join("<br>"|safe) }}</td>
<td>{{ package.archive_size }}</td>
<td>{{ package.installed_size }}</td>
<td>{{ package.build_date }}</td>
@ -73,11 +88,7 @@ SigLevel = Database{% if has_repo_signed %}Required{% else %}Never{% endif %} Pa
</footer>
</div>
<script src="https://cdn.jsdelivr.net/npm/jquery/dist/jquery.min.js"></script>
<script src="https://unpkg.com/tableexport.jquery.plugin/tableExport.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.1.0/dist/js/bootstrap.bundle.min.js" integrity="sha384-U1DAWAznBHeqEIlVSCgzq+c9gqGAJn5c/t99JyeKa9xxaYpSvHU5awsuZVVFIhvj" crossorigin="anonymous"></script>
<script src="https://unpkg.com/bootstrap-table@1.18.3/dist/bootstrap-table.min.js"></script>
<script src="https://unpkg.com/bootstrap-table@1.18.3/dist/extensions/export/bootstrap-table-export.min.js"></script>
{% include "utils/bootstrap-scripts.jinja2" %}
</body>

View File

@ -1 +0,0 @@
<style></style>

View File

@ -0,0 +1,12 @@
<script src="https://cdn.jsdelivr.net/npm/jquery/dist/jquery.min.js"></script>
<script src="https://unpkg.com/tableexport.jquery.plugin/tableExport.min.js"></script>
<script src="https://unpkg.com/jquery-resizable-columns@0.2.3/dist/jquery.resizableColumns.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.1.0/dist/js/bootstrap.bundle.min.js" integrity="sha384-U1DAWAznBHeqEIlVSCgzq+c9gqGAJn5c/t99JyeKa9xxaYpSvHU5awsuZVVFIhvj" crossorigin="anonymous"></script>
<script src="https://unpkg.com/bootstrap-table@1.18.3/dist/bootstrap-table.min.js"></script>
<script src="https://unpkg.com/bootstrap-table@1.18.3/dist/extensions/export/bootstrap-table-export.min.js"></script>
<script src="https://unpkg.com/bootstrap-table@1.18.3/dist/extensions/resizable/bootstrap-table-resizable.js"></script>

View File

@ -0,0 +1,9 @@
<script src="https://kit.fontawesome.com/0d6d6d5226.js" crossorigin="anonymous"></script>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.0/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-KyZXEAg3QhqLMpG8r+8fhAXLRk2vvoC2f3B09zVXn8CA5QIVfZOJ3BCsw2P0p/We" crossorigin="anonymous">
<link href="https://unpkg.com/bootstrap-table@1.18.3/dist/bootstrap-table.min.css" rel="stylesheet">
<link href="https://unpkg.com/jquery-resizable-columns@0.2.3/dist/jquery.resizableColumns.css" rel="stylesheet">
<style>
</style>