Skip to content

Commit

Permalink
systemd: Show package updates status on front page
Browse files Browse the repository at this point in the history
Show a summary indicator on the system front page whether the host has
available package updates.

https://bugzilla.redhat.com/show_bug.cgi?id=1495543
Fixes #7758
Closes #8822
  • Loading branch information
martinpitt committed Mar 17, 2018
1 parent 924f7d5 commit f451186
Show file tree
Hide file tree
Showing 3 changed files with 201 additions and 7 deletions.
86 changes: 86 additions & 0 deletions pkg/systemd/host.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,11 @@
* along with Cockpit; If not, see <http://www.gnu.org/licenses/>.
*/

require("polyfills.js");
var $ = require("jquery");
var cockpit = require("cockpit");
var machine_info = require("machine-info.es6");
var packagekit = require("packagekit.es6");

var Mustache = require("mustache");
var plot = require("plot");
Expand Down Expand Up @@ -196,6 +198,9 @@ PageServer.prototype = {
this.server_time = null;
this.client = null;
this.hostname_proxy = null;
this.os_updates = null; // packagekit.Enum.INFO_* → #count
this.os_updates_icon = document.getElementById("system_information_updates_icon");
this.unregistered = false;
},

getTitle: function() {
Expand Down Expand Up @@ -369,6 +374,87 @@ PageServer.prototype = {

$(pmlogger_service).on('changed', refresh_pmlogger_state);
refresh_pmlogger_state();

// if cockpit component "page" is available, set element content to a link to it, otherwise just text
function set_page_link(element_sel, page, text) {
if (cockpit.manifests[page]) {
var link = document.createElement("a");
link.innerHTML = text;
link.addEventListener("click", function() { cockpit.jump("/" + page); });
$(element_sel).html(link);
} else {
$(element_sel).text(text);
}
}

function refresh_os_updates_state() {
self.os_updates_icon.className = ""; // hide spinner

// if system is unregistered, always show that
if (self.unregistered) {
self.os_updates_icon.className = "pficon pficon-warning-triangle-o";
set_page_link("#system_information_updates_text", "subscriptions", _("System Not Registered"));
return;
}

var infos = Object.keys(self.os_updates || {}).sort();
if (infos.length === 0) {
$("#system_information_updates_text").text(_("System Up To Date"));
return;
}

// show highest severity level
var severity = infos[infos.length - 1];
var text;
self.os_updates_icon.className = packagekit.getSeverityIcon(severity);
if (severity == packagekit.Enum.INFO_SECURITY)
text = _("Security Updates Available");
else if (severity >= packagekit.Enum.INFO_NORMAL)
text = _("Bug Fix Updates Available");
else
text = _("Enhancement Updates Available");

set_page_link("#system_information_updates_text", "updates", text);
}

function check_for_updates() {
self.os_updates = null;

packagekit.cancellableTransaction("GetUpdates", [0],
function (data) {
// we are getting progress, so PackageKit works; show spinner
if (self.os_updates === null) {
self.os_updates = {};
$("#system_information_updates_text").text(_("Checking for updates…"));
self.os_updates_icon.className = "spinner spinner-xs spinner-inline";
}
},
{
Package: function (info) {
// HACK: dnf backend yields wrong severity (https://bugs.freedesktop.org/show_bug.cgi?id=101070)
if (info < packagekit.Enum.INFO_LOW || info > packagekit.Enum.INFO_SECURITY)
info = packagekit.Enum.INFO_NORMAL;
self.os_updates[info] = (self.os_updates[info] || 0) + 1;
}
})
.then(refresh_os_updates_state)
.catch(function (ex) {
// if PackageKit is not available, hide the table line
console.warn("Checking for available updates failed:", ex.toString());
self.os_updates_icon.className = "";
$("#system_information_updates_text").toggle(false);
});
}

// check for updates now and on page switches, in case they get applied on /updates or terminal
$(cockpit).on("visibilitychange", check_for_updates);
check_for_updates();

// check for unregistered system
packagekit.watchRedHatSubscription(function (subscribed) {
self.unregistered = !subscribed;
refresh_os_updates_state();
});
},

enter: function() {
Expand Down
7 changes: 7 additions & 0 deletions pkg/systemd/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
<link href="system.css" rel="stylesheet">
<script src="../base1/jquery.js"></script>
<script src="../base1/cockpit.js"></script>
<script src="../manifests.js"></script>
<script src="../*/po.js"></script>
</head>
<body hidden>
Expand Down Expand Up @@ -99,6 +100,12 @@
<td translatable="yes">Operating System</td>
<td id="system_information_os_text"></td>
</tr>
<tr id="system_information_updates">
<td>
<span id="system_information_updates_icon" hidden></span>
</td>
<td id="system_information_updates_text"></td>
</tr>
<tr id="system-ssh-keys">
<td translatable="yes">Secure Shell Keys</td>
<td><a id="system-ssh-keys-link" translatable="yes" data-toggle="modal"
Expand Down
115 changes: 108 additions & 7 deletions test/verify/check-packagekit
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
# along with Cockpit; If not, see <http://www.gnu.org/licenses/>.

import os
import time

import parent
from testlib import *
Expand Down Expand Up @@ -85,15 +86,39 @@ class TestUpdates(PackageCase):

return row

def wait_checking_updates(self):
'''Wait until spinner is gone from #system_information_updates_icon for 3 s'''

self.browser.wait_present("#system_information_updates_icon")
good_count = 0
for retry in range(60):
classes = self.browser.attr("#system_information_updates_icon", "class")
if classes is None or "spinner" in classes:
good_count = 0
else:
good_count += 1
if good_count >= 3:
return
time.sleep(1)
else:
self.fail("Timed out waiting for updates spinner to go away")

def testBasic(self):
# no security updates, no changelogs
b = self.browser
m = self.machine

m.start_cockpit()
b.login_and_go("/updates")

# no repositories at all, thus no updates
b.login_and_go("/system")
if m.image != "rhel-7-4":
# status on /system front page: no repos at all, thus no updates
self.wait_checking_updates()
b.wait_text("#system_information_updates_text", "System Up To Date")
self.assertEqual(b.attr("#system_information_updates_icon", "class"), "")

# no updates on the Software Updates page
b.go("/updates")
b.enter_page("/updates")
b.wait_present(".content-header-extra td button")
b.wait_in_text("#state", "No updates pending")
b.wait_present(".content-header-extra td.text-right span")
Expand Down Expand Up @@ -127,6 +152,17 @@ class TestUpdates(PackageCase):
self.check_nth_update(1, "chocolate", "2.0-2")
self.check_nth_update(2, "vanilla", "1.0-2")

if m.image != "rhel-7-4":
# updates are shown on system page
b.go("/system")
b.enter_page("/system")
self.wait_checking_updates()
b.wait_text("#system_information_updates_text", "Bug Fix Updates Available")
self.assertIn("fa-bug", b.attr("#system_information_updates_icon", "class"))
# should be a link, click on it to go to /updates
b.click("#system_information_updates_text a")
b.enter_page("/updates")

# old versions are still installed
m.execute("test -f /stamp-vanilla-1.0-1 && test -f /stamp-chocolate-2.0-1")

Expand Down Expand Up @@ -182,6 +218,14 @@ class TestUpdates(PackageCase):
# new versions are now installed
m.execute("test -f /stamp-vanilla-1.0-2 && test -f /stamp-chocolate-2.0-2")

if m.image != "rhel-7-4":
# system page has current state as well
b.go("/system")
b.enter_page("/system")
self.wait_checking_updates()
b.wait_text("#system_information_updates_text", "System Up To Date")
self.assertEqual(b.attr("#system_information_updates_icon", "class"), "")

@skipImage("Ubuntu 16.04 PackageKit does not support changelogs", "ubuntu-1604")
def testInfoSecurity(self):
b = self.browser
Expand Down Expand Up @@ -263,6 +307,20 @@ class TestUpdates(PackageCase):
self.assertEqual(b.text(sel + " .listing-ct-body em"), "more") # *more*
self.assertEqual(b.attr(sel + " .listing-ct-body a:first-of-type", "href"), "http://unicorn.example.com")

if m.image != "rhel-7-4":
# updates are shown on system page
b.go("/system")
b.enter_page("/system")
self.wait_checking_updates()
# PackageKit's dnf backend mis-parses severity (https://bugs.freedesktop.org/show_bug.cgi?id=101070)
# and PackageKit's deb backend does not know about severity at all, so only test this on RPM
if self.backend == 'yum':
b.wait_text("#system_information_updates_text", "Security Updates Available")
self.assertIn("security", b.attr("#system_information_updates_icon", "class"))
# should be a link, click on it to go to back to /updates
b.click("#system_information_updates_text a")
b.enter_page("/updates")

# install only security updates
self.assertEqual(b.text("#app .container-fluid button.btn-default"), "Install Security Updates")
b.click("#app .container-fluid button.btn-default")
Expand Down Expand Up @@ -588,6 +646,14 @@ class TestUpdates(PackageCase):
b.wait_present("#app pre")
b.wait_in_text("#app pre", "PackageKit is not installed")

if m.image != "rhel-7-4":
# update status on front page should be invisible
b.go("/system")
b.enter_page("/system")
b.wait_present("#system_information_updates_text")
b.wait_not_visible("#system_information_updates_text")
self.assertEqual(b.attr("#system_information_updates_icon", "class"), "")


@skipImage("Image uses OSTree", "continuous-atomic", "fedora-atomic", "rhel-atomic")
@skipImage("No subscriptions", "centos-7", "debian-stable", "debian-testing", "fedora-26", "fedora-27", "fedora-i386", "fedora-testing", "ubuntu-1604", "ubuntu-stable")
Expand Down Expand Up @@ -632,7 +698,16 @@ class TestUpdatesSubscriptions(PackageCase):

# fresh machine, no updates available; by default our rhel-* images are not registered
m.start_cockpit()
b.login_and_go("/updates")
b.login_and_go("/system")
if m.image != "rhel-7-4":
# show unregistered status on system front page
b.wait_present("#system_information_updates_text")
b.wait_in_text("#system_information_updates_text", "Not Registered")
self.assertIn("triangle", b.attr("#system_information_updates_icon", "class"))

# and also on software updates page
b.go("/updates")
b.enter_page("/updates")

# empty state visible in main area
b.wait_present(".container-fluid div.blank-slate-pf button")
Expand All @@ -655,6 +730,14 @@ class TestUpdatesSubscriptions(PackageCase):
b.wait_present(".container-fluid div.blank-slate-pf")
b.wait_in_text(".container-fluid div.blank-slate-pf", "up to date")

if m.image != "rhel-7-4":
# same on system page
b.go("/system")
b.enter_page("/system")
b.wait_present("#system_information_updates_text")
b.wait_text("#system_information_updates_text", "System Up To Date")
self.assertEqual(b.attr("#system_information_updates_icon", "class"), "")

def testAvailableUpdates(self):
m = self.machine
b = self.browser
Expand All @@ -664,11 +747,21 @@ class TestUpdatesSubscriptions(PackageCase):
self.createPackage("vanilla", "1.0", "2")
self.enableRepo()

# by default our rhel-* images are not registered; should show available update and unregistered warning, but no
# other action buttons
m.start_cockpit()
b.login_and_go("/updates")

b.login_and_go("/system")
if m.image != "rhel-7-4":
# by default our rhel-* images are not registered; show warning on system page
b.wait_present("#system_information_updates_text")
b.wait_in_text("#system_information_updates_text", "Not Registered")
self.assertIn("triangle", b.attr("#system_information_updates_icon", "class"))
# should be a link leading to subscriptions page
b.click("#system_information_updates_text a")
b.enter_page("/subscriptions")

# /updates should show available update and unregistered warning, but no other action buttons
b.go("/updates")
b.enter_page("/updates")
b.wait_present(".alert-warning")
b.wait_in_text(".alert-warning", "subscribe")
b.wait_present("table.listing-ct")
Expand Down Expand Up @@ -702,6 +795,14 @@ class TestUpdatesSubscriptions(PackageCase):
b.wait_present("#app .container-fluid button")
self.assertEqual(b.text("#app .container-fluid button"), "Install All Updates")

if m.image != "rhel-7-4":
# show available updates on system page too
b.go("/system")
b.enter_page("/system")
b.wait_present("#system_information_updates_text")
b.wait_text("#system_information_updates_text", "Bug Fix Updates Available")
self.assertIn("fa-bug", b.attr("#system_information_updates_icon", "class"))


@skipImage("Image uses OSTree", "continuous-atomic", "fedora-atomic", "rhel-atomic")
class TestAutoUpdates(PackageCase):
Expand Down

0 comments on commit f451186

Please sign in to comment.