Initial support for automatic updates
This patch introduces a new `AutomaticUpdatePolicy` configuration. This was a long time coming for rpm-ostree, given that its update model makes it extremely apt for such a feature. The config supports a `check` mode, which should be very useful to Atomic Workstation users, as well as a `reboot` mode, which could be used in its present form in simple single node Atomic Host situations. There is still a lot of work to be done, including integrating advisories, and supporting a `deploy` mode. This feature hopefully will be leveraged as well by higher-level projects like GNOME Software and Cockpit. Closes: #1147 Approved by: cgwalters
This commit is contained in:
parent
3e9c6cf230
commit
51fb641305
@ -74,15 +74,26 @@ librpmostreed_la_LIBADD = \
|
||||
dbusconf_DATA = $(srcdir)/src/daemon/org.projectatomic.rpmostree1.conf
|
||||
dbusconfdir = ${sysconfdir}/dbus-1/system.d
|
||||
|
||||
systemdunit_in_files = $(srcdir)/src/daemon/rpm-ostreed.service.in
|
||||
systemdunit_DATA = $(systemdunit_in_files:.service.in=.service)
|
||||
systemdunit_service_in_files = \
|
||||
$(srcdir)/src/daemon/rpm-ostreed.service.in \
|
||||
$(srcdir)/src/daemon/rpm-ostreed-automatic.service.in \
|
||||
$(NULL)
|
||||
|
||||
systemdunit_service_files = $(systemdunit_service_in_files:.service.in=.service)
|
||||
systemdunit_timer_files = $(srcdir)/src/daemon/rpm-ostreed-automatic.timer
|
||||
|
||||
systemdunit_DATA = \
|
||||
$(systemdunit_service_files) \
|
||||
$(systemdunit_timer_files) \
|
||||
$(NULL)
|
||||
|
||||
systemdunitdir = $(prefix)/lib/systemd/system/
|
||||
if BUILDOPT_ASAN
|
||||
daemon_asan_options = -e s,@SYSTEMD_ENVIRON\@,Environment=ASAN_OPTIONS=detect_leaks=false,
|
||||
else
|
||||
daemon_asan_options = -e /@SYSTEMD_ENVIRON\@/d
|
||||
endif
|
||||
$(systemdunit_DATA): Makefile
|
||||
$(systemdunit_service_files): Makefile
|
||||
$(SED_SUBST) $(daemon_asan_options) $@.in > $@
|
||||
|
||||
# We keep this stub script around to have SELinux labeling work,
|
||||
@ -119,10 +130,11 @@ EXTRA_DIST += \
|
||||
$(polkit_policy_DATA) \
|
||||
$(sysconf_DATA) \
|
||||
$(service_in_files) \
|
||||
$(systemdunit_in_files) \
|
||||
$(systemdunit_service_in_files) \
|
||||
$(systemdunit_timer_files) \
|
||||
$(NULL)
|
||||
|
||||
CLEANFILES += \
|
||||
$(service_DATA) \
|
||||
$(systemdunit_DATA) \
|
||||
$(systemdunit_service_files) \
|
||||
$(NULL)
|
||||
|
@ -62,6 +62,8 @@ librpmostreepriv_la_SOURCES = \
|
||||
src/libpriv/rpmostree-editor.h \
|
||||
src/libpriv/libsd-locale-util.c \
|
||||
src/libpriv/libsd-locale-util.h \
|
||||
src/libpriv/libsd-time-util.c \
|
||||
src/libpriv/libsd-time-util.h \
|
||||
src/libpriv/rpmostree-libarchive-input-stream.c \
|
||||
src/libpriv/rpmostree-libarchive-input-stream.h \
|
||||
$(NULL)
|
||||
|
@ -18,7 +18,7 @@ if BUILDOPT_ASAN
|
||||
AM_TESTS_ENVIRONMENT += BUILDOPT_ASAN=yes ASAN_OPTIONS=detect_leaks=false
|
||||
endif
|
||||
|
||||
testbin_cppflags = $(AM_CPPFLAGS) -I $(srcdir)/src/libpriv -I $(srcdir)/libglnx -I $(srcdir)/tests/common
|
||||
testbin_cppflags = $(AM_CPPFLAGS) -I $(srcdir)/src/lib -I $(srcdir)/src/libpriv -I $(srcdir)/libglnx -I $(srcdir)/tests/common
|
||||
testbin_cflags = $(AM_CFLAGS) $(PKGDEP_RPMOSTREE_CFLAGS)
|
||||
testbin_ldadd = $(PKGDEP_RPMOSTREE_LIBS) librpmostree-1.la librpmostreepriv.la
|
||||
|
||||
@ -56,10 +56,18 @@ uninstalled_test_scripts = \
|
||||
tests/check/test-ucontainer.sh \
|
||||
$(NULL)
|
||||
|
||||
uninstalled_test_extra_programs = dbus-run-session
|
||||
uninstalled_test_extra_programs = \
|
||||
inject-pkglist \
|
||||
dbus-run-session \
|
||||
$(NULL)
|
||||
|
||||
dbus_run_session_SOURCES = tests/utils/dbus-run-session.c
|
||||
|
||||
inject_pkglist_CPPFLAGS = $(testbin_cppflags)
|
||||
inject_pkglist_CFLAGS = $(testbin_cflags)
|
||||
inject_pkglist_LDADD = $(testbin_ldadd) libtest.la
|
||||
inject_pkglist_SOURCES = tests/utils/inject-pkglist.c
|
||||
|
||||
check-local:
|
||||
@echo " *** NOTE ***"
|
||||
@echo " *** NOTE ***"
|
||||
@ -78,7 +86,7 @@ vmsync:
|
||||
fi; \
|
||||
env $(BASE_TESTS_ENVIRONMENT) ./tests/vmcheck/sync.sh
|
||||
|
||||
vmoverlay:
|
||||
vmoverlay: inject-pkglist
|
||||
@set -e; if [ -z "$(SKIP_VMOVERLAY)" ]; then \
|
||||
if [ -z "$(SKIP_INSTALL)" ]; then \
|
||||
env $(BASE_TESTS_ENVIRONMENT) ./tests/vmcheck/install.sh; \
|
||||
|
@ -67,12 +67,27 @@ Boston, MA 02111-1307, USA.
|
||||
</para>
|
||||
|
||||
<variablelist>
|
||||
<varlistentry>
|
||||
<term><varname>AutomaticUpdatePolicy=</varname></term>
|
||||
|
||||
<listitem>
|
||||
<para>Controls the automatic update policy. Currently "none" or "check".
|
||||
"none" disables automatic updates. "check" downloads just enough metadata to check
|
||||
for updates and display them in <command>rpm-ostree status</command>. Defaults to
|
||||
"none".</para>
|
||||
|
||||
<para>Automatic updates enablement and frequency are controlled by the
|
||||
<command>rpm-ostreed-automatic.timer</command> unit. <!-- XXX: needs man page -->
|
||||
See <citerefentry><refentrytitle>systemd.timer</refentrytitle><manvolnum>5</manvolnum></citerefentry>
|
||||
for more information on how to control systemd timers.</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
<varlistentry>
|
||||
<term><varname>IdleExitTimeout=</varname></term>
|
||||
|
||||
<listitem>
|
||||
<para>Controls the time in seconds of inactivity before the daemon exits. Use 0 to
|
||||
disable auto-exit.</para>
|
||||
disable auto-exit. Defaults to 60.</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
<!--
|
||||
|
@ -26,6 +26,7 @@
|
||||
#include <glib-unix.h>
|
||||
#include <gio/gunixoutputstream.h>
|
||||
#include <json-glib/json-glib.h>
|
||||
#include <libdnf/libdnf.h>
|
||||
|
||||
#include "rpmostree-builtins.h"
|
||||
#include "rpmostree-libbuiltin.h"
|
||||
@ -34,9 +35,13 @@
|
||||
#include "rpmostree-core.h"
|
||||
#include "rpmostree-rpm-util.h"
|
||||
#include "libsd-locale-util.h"
|
||||
#include "libsd-time-util.h"
|
||||
|
||||
#include <libglnx.h>
|
||||
|
||||
#define RPMOSTREE_AUTOMATIC_SERVICE_OBJPATH \
|
||||
"/org/freedesktop/systemd1/unit/rpm_2dostreed_2dautomatic_2eservice"
|
||||
|
||||
static gboolean opt_pretty;
|
||||
static gboolean opt_verbose;
|
||||
static gboolean opt_json;
|
||||
@ -167,8 +172,70 @@ gv_nevra_to_evr (GString *buffer,
|
||||
PKG_NEVRA_FLAGS_EPOCH_VERSION_RELEASE);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
get_last_auto_update_run (GDBusConnection *connection,
|
||||
char **out_last_run,
|
||||
gboolean *out_fail,
|
||||
GCancellable *cancellable,
|
||||
GError **error)
|
||||
{
|
||||
GLNX_AUTO_PREFIX_ERROR ("Querying systemd for last auto-update run", error);
|
||||
|
||||
g_autoptr(GDBusProxy) unit_proxy =
|
||||
g_dbus_proxy_new_sync (connection, G_DBUS_PROXY_FLAGS_DO_NOT_CONNECT_SIGNALS, NULL,
|
||||
"org.freedesktop.systemd1", RPMOSTREE_AUTOMATIC_SERVICE_OBJPATH,
|
||||
"org.freedesktop.systemd1.Unit", cancellable, error);
|
||||
if (!unit_proxy)
|
||||
return FALSE;
|
||||
|
||||
g_autoptr(GVariant) state_val =
|
||||
g_dbus_proxy_get_cached_property (unit_proxy, "ActiveState");
|
||||
|
||||
/* let's not error out if we can't msg systemd (e.g. bad sepol); just mark as unknown */
|
||||
if (state_val == NULL)
|
||||
{
|
||||
*out_fail = FALSE;
|
||||
*out_last_run = g_strdup ("unknown");
|
||||
return TRUE; /* NB early return */
|
||||
}
|
||||
|
||||
const char *state = g_variant_get_string (state_val, NULL);
|
||||
if (g_str_equal (state, "failed"))
|
||||
{
|
||||
*out_fail = TRUE;
|
||||
return TRUE; /* NB early return */
|
||||
}
|
||||
|
||||
g_autoptr(GDBusProxy) service_proxy =
|
||||
g_dbus_proxy_new_sync (connection, G_DBUS_PROXY_FLAGS_DO_NOT_CONNECT_SIGNALS, NULL,
|
||||
"org.freedesktop.systemd1", RPMOSTREE_AUTOMATIC_SERVICE_OBJPATH,
|
||||
"org.freedesktop.systemd1.Service", cancellable, error);
|
||||
if (!service_proxy)
|
||||
return FALSE;
|
||||
|
||||
g_autoptr(GVariant) t_val =
|
||||
g_dbus_proxy_get_cached_property (service_proxy, "ExecMainExitTimestamp");
|
||||
|
||||
g_autofree char *last_run = NULL;
|
||||
if (t_val)
|
||||
{
|
||||
guint64 t = g_variant_get_uint64 (t_val);
|
||||
if (t > 0)
|
||||
{
|
||||
char time_rel[FORMAT_TIMESTAMP_RELATIVE_MAX] = "";
|
||||
libsd_format_timestamp_relative (time_rel, sizeof(time_rel), t);
|
||||
last_run = g_strdup (time_rel);
|
||||
}
|
||||
}
|
||||
|
||||
*out_fail = FALSE;
|
||||
*out_last_run = g_steal_pointer (&last_run);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
print_daemon_state (RPMOSTreeSysroot *sysroot_proxy,
|
||||
GBusType bus_type,
|
||||
GCancellable *cancellable,
|
||||
GError **error)
|
||||
{
|
||||
@ -177,7 +244,38 @@ print_daemon_state (RPMOSTreeSysroot *sysroot_proxy,
|
||||
cancellable, error))
|
||||
return FALSE;
|
||||
|
||||
const char *policy = rpmostree_sysroot_get_automatic_update_policy (sysroot_proxy);
|
||||
|
||||
g_print ("State: %s", txn_proxy ? "busy" : "idle");
|
||||
if (g_str_equal (policy, "none"))
|
||||
g_print ("; auto updates disabled\n");
|
||||
else
|
||||
{
|
||||
g_print ("; auto updates enabled ");
|
||||
|
||||
/* don't try to get info from systemd if we're not on the system bus */
|
||||
if (bus_type != G_BUS_TYPE_SYSTEM)
|
||||
g_print ("(%s)\n", policy);
|
||||
else
|
||||
{
|
||||
gboolean failed;
|
||||
g_autofree char *last_run = NULL;
|
||||
|
||||
GDBusConnection *connection =
|
||||
g_dbus_proxy_get_connection (G_DBUS_PROXY (sysroot_proxy));
|
||||
if (!get_last_auto_update_run (connection, &last_run, &failed, cancellable, error))
|
||||
return FALSE;
|
||||
|
||||
if (failed)
|
||||
g_print ("(%s; %s%slast run failed%s%s)\n", policy,
|
||||
get_red_start (), get_bold_start (), get_bold_end (), get_red_end ());
|
||||
else if (last_run)
|
||||
/* e.g. "last check 4h 32min ago" */
|
||||
g_print ("(%s; last run %s)\n", policy, last_run);
|
||||
else
|
||||
g_print ("(%s; no runs since boot)\n", policy);
|
||||
}
|
||||
}
|
||||
|
||||
if (txn_proxy)
|
||||
{
|
||||
@ -575,6 +673,7 @@ rpmostree_builtin_status (int argc,
|
||||
glnx_unref_object RPMOSTreeSysroot *sysroot_proxy = NULL;
|
||||
_cleanup_peer_ GPid peer_pid = 0;
|
||||
|
||||
GBusType bus_type;
|
||||
if (!rpmostree_option_context_parse (context,
|
||||
option_entries,
|
||||
&argc, &argv,
|
||||
@ -582,7 +681,7 @@ rpmostree_builtin_status (int argc,
|
||||
cancellable,
|
||||
NULL, NULL,
|
||||
&sysroot_proxy,
|
||||
&peer_pid, NULL,
|
||||
&peer_pid, &bus_type,
|
||||
error))
|
||||
return FALSE;
|
||||
|
||||
@ -597,6 +696,9 @@ rpmostree_builtin_status (int argc,
|
||||
return FALSE;
|
||||
|
||||
g_autoptr(GVariant) deployments = rpmostree_sysroot_dup_deployments (sysroot_proxy);
|
||||
g_autoptr(GVariant) cached_update = NULL;
|
||||
if (rpmostree_os_get_has_cached_update_rpm_diff (os_proxy))
|
||||
cached_update = rpmostree_os_dup_cached_update (os_proxy);
|
||||
|
||||
if (opt_json || opt_jsonpath)
|
||||
{
|
||||
@ -610,6 +712,13 @@ rpmostree_builtin_status (int argc,
|
||||
JsonNode *txn_node =
|
||||
txn ? json_gvariant_serialize (txn) : json_node_new (JSON_NODE_NULL);
|
||||
json_builder_add_value (builder, txn_node);
|
||||
json_builder_set_member_name (builder, "cached-update");
|
||||
JsonNode *cached_update_node;
|
||||
if (cached_update)
|
||||
cached_update_node = json_gvariant_serialize (cached_update);
|
||||
else
|
||||
cached_update_node = json_node_new (JSON_NODE_NULL);
|
||||
json_builder_add_value (builder, cached_update_node);
|
||||
json_builder_end_object (builder);
|
||||
|
||||
JsonNode *json_root = json_builder_get_root (builder);
|
||||
@ -638,11 +747,21 @@ rpmostree_builtin_status (int argc,
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!print_daemon_state (sysroot_proxy, cancellable, error))
|
||||
if (!print_daemon_state (sysroot_proxy, bus_type, cancellable, error))
|
||||
return FALSE;
|
||||
|
||||
if (!print_deployments (sysroot_proxy, deployments, cancellable, error))
|
||||
return FALSE;
|
||||
|
||||
const char *policy = rpmostree_sysroot_get_automatic_update_policy (sysroot_proxy);
|
||||
gboolean auto_updates_enabled = (!g_str_equal (policy, "none"));
|
||||
if (cached_update && auto_updates_enabled)
|
||||
{
|
||||
g_print ("\n");
|
||||
if (!rpmostree_print_cached_update (cached_update, opt_verbose,
|
||||
cancellable, error))
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
|
@ -39,6 +39,7 @@ static gboolean opt_check;
|
||||
static gboolean opt_upgrade_unchanged_exit_77;
|
||||
static gboolean opt_cache_only;
|
||||
static gboolean opt_download_only;
|
||||
static char *opt_automatic;
|
||||
|
||||
/* "check-diff" is deprecated, replaced by "preview" */
|
||||
static GOptionEntry option_entries[] = {
|
||||
@ -51,6 +52,7 @@ static GOptionEntry option_entries[] = {
|
||||
{ "cache-only", 'C', 0, G_OPTION_ARG_NONE, &opt_cache_only, "Do not download latest ostree and RPM data", NULL },
|
||||
{ "download-only", 0, 0, G_OPTION_ARG_NONE, &opt_download_only, "Just download latest ostree and RPM data, don't deploy", NULL },
|
||||
{ "upgrade-unchanged-exit-77", 0, 0, G_OPTION_ARG_NONE, &opt_upgrade_unchanged_exit_77, "If no upgrade is available, exit 77", NULL },
|
||||
{ "trigger-automatic-update-policy", 0, G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_NONE, &opt_automatic, "For automated use only; triggered by automatic timer", NULL },
|
||||
{ NULL }
|
||||
};
|
||||
|
||||
@ -112,13 +114,29 @@ rpmostree_builtin_upgrade (int argc,
|
||||
|
||||
g_autoptr(GVariant) previous_deployment = rpmostree_os_dup_default_deployment (os_proxy);
|
||||
|
||||
if (opt_preview || opt_check)
|
||||
const gboolean check_or_preview = (opt_check || opt_preview);
|
||||
if (opt_automatic || check_or_preview)
|
||||
{
|
||||
if (!rpmostree_os_call_download_update_rpm_diff_sync (os_proxy,
|
||||
GVariantDict dict;
|
||||
g_variant_dict_init (&dict, NULL);
|
||||
g_variant_dict_insert (&dict, "mode", "s", check_or_preview ? "check" : "auto");
|
||||
g_autoptr(GVariant) options = g_variant_ref_sink (g_variant_dict_end (&dict));
|
||||
|
||||
gboolean auto_updates_enabled;
|
||||
if (!rpmostree_os_call_automatic_update_trigger_sync (os_proxy,
|
||||
options,
|
||||
&auto_updates_enabled,
|
||||
&transaction_address,
|
||||
cancellable,
|
||||
error))
|
||||
return FALSE;
|
||||
|
||||
if (!auto_updates_enabled)
|
||||
{
|
||||
/* print something for the benefit of the journal */
|
||||
g_print ("Automatic updates are not enabled; exiting...\n");
|
||||
return TRUE; /* Note early return */
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -168,27 +186,24 @@ rpmostree_builtin_upgrade (int argc,
|
||||
error))
|
||||
return FALSE;
|
||||
|
||||
if (opt_preview || opt_check)
|
||||
if (check_or_preview)
|
||||
{
|
||||
g_autoptr(GVariant) result = NULL;
|
||||
g_autoptr(GVariant) details = NULL;
|
||||
g_autoptr(GVariant) cached_update = NULL;
|
||||
if (rpmostree_os_get_has_cached_update_rpm_diff (os_proxy))
|
||||
cached_update = rpmostree_os_dup_cached_update (os_proxy);
|
||||
|
||||
if (!rpmostree_os_call_get_cached_update_rpm_diff_sync (os_proxy,
|
||||
"",
|
||||
&result,
|
||||
&details,
|
||||
cancellable,
|
||||
error))
|
||||
return FALSE;
|
||||
|
||||
if (g_variant_n_children (result) == 0)
|
||||
if (!cached_update)
|
||||
{
|
||||
g_print ("No updates available.\n");
|
||||
invocation->exit_code = RPM_OSTREE_EXIT_UNCHANGED;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
if (!opt_check)
|
||||
rpmostree_print_package_diffs (result);
|
||||
else
|
||||
{
|
||||
/* preview --> verbose (i.e. we want the diff) */
|
||||
if (!rpmostree_print_cached_update (cached_update, opt_preview,
|
||||
cancellable, error))
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
else if (!opt_reboot)
|
||||
{
|
||||
|
@ -20,14 +20,18 @@
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <signal.h>
|
||||
#include <sys/socket.h>
|
||||
#include <systemd/sd-login.h>
|
||||
|
||||
#include <glib-unix.h>
|
||||
#include <libglnx.h>
|
||||
|
||||
#include "rpmostree-dbus-helpers.h"
|
||||
#include "rpmostree-builtins.h"
|
||||
#include "rpmostree-libbuiltin.h"
|
||||
#include "libglnx.h"
|
||||
#include <sys/socket.h>
|
||||
#include "glib-unix.h"
|
||||
#include <signal.h>
|
||||
#include <systemd/sd-login.h>
|
||||
#include "rpmostree-util.h"
|
||||
#include "rpmostree-rpm-util.h"
|
||||
|
||||
void
|
||||
rpmostree_cleanup_peer (GPid *peer_pid)
|
||||
@ -1154,3 +1158,119 @@ rpmostree_update_deployment (RPMOSTreeOS *os_proxy,
|
||||
cancellable,
|
||||
error);
|
||||
}
|
||||
|
||||
static void
|
||||
append_to_summary (GString *summary,
|
||||
const char *type,
|
||||
guint n)
|
||||
{
|
||||
if (n == 0)
|
||||
return;
|
||||
if (summary->len > 0)
|
||||
g_string_append (summary, ", ");
|
||||
g_string_append_printf (summary, "%u %s", n, type);
|
||||
}
|
||||
|
||||
/* this is used by both `status` and `upgrade --check/--preview` */
|
||||
gboolean
|
||||
rpmostree_print_cached_update (GVariant *cached_update,
|
||||
gboolean verbose,
|
||||
GCancellable *cancellable,
|
||||
GError **error)
|
||||
{
|
||||
GLNX_AUTO_PREFIX_ERROR ("Retrieving cached update", error);
|
||||
|
||||
g_auto(GVariantDict) dict;
|
||||
g_variant_dict_init (&dict, cached_update);
|
||||
|
||||
/* let's just extract 📤 all the info ahead of time */
|
||||
|
||||
const char *checksum;
|
||||
if (!g_variant_dict_lookup (&dict, "checksum", "&s", &checksum))
|
||||
return glnx_throw (error, "Missing \"checksum\" key");
|
||||
|
||||
const char *version;
|
||||
if (!g_variant_dict_lookup (&dict, "version", "&s", &version))
|
||||
version= NULL;
|
||||
|
||||
g_autofree char *timestamp = NULL;
|
||||
{ guint64 t;
|
||||
if (!g_variant_dict_lookup (&dict, "timestamp", "t", &t))
|
||||
t = 0;
|
||||
timestamp = rpmostree_timestamp_str_from_unix_utc (t);
|
||||
}
|
||||
|
||||
gboolean gpg_enabled;
|
||||
if (!g_variant_dict_lookup (&dict, "gpg-enabled", "b", &gpg_enabled))
|
||||
gpg_enabled = FALSE;
|
||||
|
||||
g_autoptr(GVariant) signatures =
|
||||
g_variant_dict_lookup_value (&dict, "signatures", G_VARIANT_TYPE ("av"));
|
||||
|
||||
gboolean is_new_checksum;
|
||||
g_assert (g_variant_dict_lookup (&dict, "ref-has-new-commit", "b", &is_new_checksum));
|
||||
|
||||
g_autoptr(GVariant) rpm_diff =
|
||||
g_variant_dict_lookup_value (&dict, "rpm-diff", G_VARIANT_TYPE ("a{sv}"));
|
||||
|
||||
/* and now we can print 🖨️ things! */
|
||||
|
||||
g_print ("Available update:\n");
|
||||
|
||||
/* add the long keys here */
|
||||
const guint max_key_len = MAX (strlen ("Downgraded"),
|
||||
strlen ("GPGSignature"));
|
||||
|
||||
if (is_new_checksum)
|
||||
{
|
||||
rpmostree_print_timestamp_version (version, timestamp, max_key_len);
|
||||
rpmostree_print_kv ("Commit", max_key_len, checksum);
|
||||
if (gpg_enabled)
|
||||
rpmostree_print_gpg_info (signatures, verbose, max_key_len);
|
||||
}
|
||||
|
||||
if (rpm_diff)
|
||||
{
|
||||
g_auto(GVariantDict) rpm_diff_dict;
|
||||
g_variant_dict_init (&rpm_diff_dict, rpm_diff);
|
||||
|
||||
g_autoptr(GVariant) upgraded =
|
||||
_rpmostree_vardict_lookup_value_required (&rpm_diff_dict, "upgraded",
|
||||
G_VARIANT_TYPE ("a(us(ss)(ss))"), error);
|
||||
if (!upgraded)
|
||||
return FALSE;
|
||||
|
||||
g_autoptr(GVariant) downgraded =
|
||||
_rpmostree_vardict_lookup_value_required (&rpm_diff_dict, "downgraded",
|
||||
G_VARIANT_TYPE ("a(us(ss)(ss))"), error);
|
||||
if (!downgraded)
|
||||
return FALSE;
|
||||
|
||||
g_autoptr(GVariant) removed =
|
||||
_rpmostree_vardict_lookup_value_required (&rpm_diff_dict, "removed",
|
||||
G_VARIANT_TYPE ("a(usss)"), error);
|
||||
if (!removed)
|
||||
return FALSE;
|
||||
|
||||
g_autoptr(GVariant) added =
|
||||
_rpmostree_vardict_lookup_value_required (&rpm_diff_dict, "added",
|
||||
G_VARIANT_TYPE ("a(usss)"), error);
|
||||
if (!added)
|
||||
return FALSE;
|
||||
|
||||
if (verbose)
|
||||
rpmostree_variant_diff_print_formatted (max_key_len,
|
||||
upgraded, downgraded, removed, added);
|
||||
else
|
||||
{
|
||||
g_autoptr(GString) diff_summary = g_string_new (NULL);
|
||||
append_to_summary (diff_summary, "upgraded", g_variant_n_children (upgraded));
|
||||
append_to_summary (diff_summary, "downgraded", g_variant_n_children (downgraded));
|
||||
append_to_summary (diff_summary, "removed", g_variant_n_children (removed));
|
||||
append_to_summary (diff_summary, "added", g_variant_n_children (added));
|
||||
rpmostree_print_kv ("Diff", max_key_len, diff_summary->str);
|
||||
}
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
@ -122,3 +122,9 @@ rpmostree_update_deployment (RPMOSTreeOS *os_proxy,
|
||||
char **out_transaction_address,
|
||||
GCancellable *cancellable,
|
||||
GError **error);
|
||||
|
||||
gboolean
|
||||
rpmostree_print_cached_update (GVariant *cached_update,
|
||||
gboolean verbose,
|
||||
GCancellable *cancellable,
|
||||
GError **error);
|
||||
|
@ -50,6 +50,9 @@
|
||||
<method name="ReloadConfig">
|
||||
</method>
|
||||
|
||||
<!-- none, check, reboot -->
|
||||
<property name="AutomaticUpdatePolicy" type="s" access="read"/>
|
||||
|
||||
<method name="CreateOSName">
|
||||
<arg type="s" name="name"/>
|
||||
<arg type="o" name="result" direction="out"/>
|
||||
@ -77,12 +80,32 @@
|
||||
'origin' (type 's')
|
||||
'signatures' (type 'av')
|
||||
'gpg-enabled' (type 'b')
|
||||
'ref-has-new-commit' (type 'b')
|
||||
TRUE if 'checksum' refers to a new commit we're not booted in.
|
||||
'rpm-diff' (type 'a{sv}')
|
||||
'upgraded' (type 'a(us(ss)(ss))')
|
||||
'downgraded' (type 'a(us(ss)(ss))')
|
||||
'removed' (type 'a(usss)')
|
||||
'added' (type 'a(usss)')
|
||||
-->
|
||||
<property name="CachedUpdate" type="a{sv}" access="read"/>
|
||||
<property name="HasCachedUpdateRpmDiff" type="b" access="read"/>
|
||||
|
||||
<!-- NONE, DIFF, PREPARE, REBOOT -->
|
||||
<property name="AutomaticUpdatePolicy" type="s" access="read"/>
|
||||
<!-- Available options:
|
||||
"mode" (type 's')
|
||||
One of auto, none, check, reboot. Defaults to
|
||||
auto, which follows configured policy (available in
|
||||
AutomaticUpdatePolicy property).
|
||||
|
||||
If automatic updates are not enabled, @enabled will be FALSE and
|
||||
@transaction_address will be the empty string.
|
||||
-->
|
||||
<method name="AutomaticUpdateTrigger">
|
||||
<arg type="a{sv}" name="options" direction="in"/>
|
||||
<arg type="b" name="enabled" direction="out"/>
|
||||
<arg type="s" name="transaction_address" direction="out"/>
|
||||
</method>
|
||||
|
||||
<property name="Name" type="s" access="read"/>
|
||||
|
||||
<method name="GetDeploymentsRpmDiff">
|
||||
|
8
src/daemon/rpm-ostreed-automatic.service.in
Normal file
8
src/daemon/rpm-ostreed-automatic.service.in
Normal file
@ -0,0 +1,8 @@
|
||||
[Unit]
|
||||
Description=RPM-OSTree Automatic Update
|
||||
Documentation=man:rpm-ostree(1) man:rpm-ostreed.conf(5)
|
||||
ConditionPathExists=/run/ostree-booted
|
||||
|
||||
[Service]
|
||||
Type=oneshot
|
||||
ExecStart=@bindir@/rpm-ostree upgrade --automatic
|
11
src/daemon/rpm-ostreed-automatic.timer
Normal file
11
src/daemon/rpm-ostreed-automatic.timer
Normal file
@ -0,0 +1,11 @@
|
||||
[Unit]
|
||||
Description=RPM-OSTree Automatic Update Trigger
|
||||
Documentation=man:rpm-ostree(1) man:rpm-ostreed.conf(5)
|
||||
ConditionPathExists=/run/ostree-booted
|
||||
|
||||
[Timer]
|
||||
OnBootSec=1h
|
||||
OnUnitInactiveSec=1d
|
||||
|
||||
[Install]
|
||||
WantedBy=timers.target
|
@ -3,4 +3,5 @@
|
||||
# For option meanings, see rpm-ostreed.conf(5).
|
||||
|
||||
[Daemon]
|
||||
#AutomaticUpdatePolicy=none
|
||||
#IdleExitTimeout=60
|
||||
|
@ -22,6 +22,7 @@
|
||||
#include "rpmostreed-sysroot.h"
|
||||
#include "rpmostreed-types.h"
|
||||
#include "rpmostreed-utils.h"
|
||||
#include "rpmostree-util.h"
|
||||
|
||||
#include <libglnx.h>
|
||||
#include <systemd/sd-journal.h>
|
||||
@ -61,8 +62,9 @@ struct _RpmostreedDaemon {
|
||||
RpmostreedSysroot *sysroot;
|
||||
gchar *sysroot_path;
|
||||
|
||||
/* we only have one setting for now, so let's just keep it in the main struct */
|
||||
/* we only have two settings for now, so let's just keep it in the main struct */
|
||||
guint idle_exit_timeout;
|
||||
RpmostreedAutomaticUpdatePolicy auto_update_policy;
|
||||
|
||||
GDBusConnection *connection;
|
||||
GDBusObjectManagerServer *object_manager;
|
||||
@ -312,6 +314,17 @@ maybe_load_config_keyfile (GKeyFile **out_keyfile,
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static char*
|
||||
get_config_str (GKeyFile *keyfile,
|
||||
const char *key,
|
||||
const char *default_val)
|
||||
{
|
||||
g_autofree char *val = NULL;
|
||||
if (keyfile)
|
||||
val = g_key_file_get_string (keyfile, DAEMON_CONFIG_GROUP, key, NULL);
|
||||
return g_steal_pointer (&val) ?: g_strdup (default_val);
|
||||
}
|
||||
|
||||
static guint64
|
||||
get_config_uint64 (GKeyFile *keyfile,
|
||||
const char *key,
|
||||
@ -330,6 +343,20 @@ get_config_uint64 (GKeyFile *keyfile,
|
||||
return default_val;
|
||||
}
|
||||
|
||||
RpmostreedAutomaticUpdatePolicy
|
||||
rpmostreed_get_automatic_update_policy (RpmostreedDaemon *self)
|
||||
{
|
||||
return self->auto_update_policy;
|
||||
}
|
||||
|
||||
/* in-place version of g_ascii_strdown */
|
||||
static inline void
|
||||
ascii_strdown_inplace (char *str)
|
||||
{
|
||||
for (char *c = str; *c; c++)
|
||||
*c = g_ascii_tolower (*c);
|
||||
}
|
||||
|
||||
gboolean
|
||||
rpmostreed_daemon_reload_config (RpmostreedDaemon *self,
|
||||
gboolean *out_changed,
|
||||
@ -343,12 +370,32 @@ rpmostreed_daemon_reload_config (RpmostreedDaemon *self,
|
||||
* follow-up requests are more responsive */
|
||||
guint64 idle_exit_timeout = get_config_uint64 (config, "IdleExitTimeout", 60);
|
||||
|
||||
/* default to off for now; we will change it to "check" in a later release */
|
||||
RpmostreedAutomaticUpdatePolicy auto_update_policy =
|
||||
RPMOSTREED_AUTOMATIC_UPDATE_POLICY_NONE;
|
||||
|
||||
g_autofree char *auto_update_policy_str =
|
||||
get_config_str (config, "AutomaticUpdatePolicy", NULL);
|
||||
if (auto_update_policy_str)
|
||||
{
|
||||
ascii_strdown_inplace (auto_update_policy_str);
|
||||
if (!rpmostree_str_to_auto_update_policy (auto_update_policy_str,
|
||||
&auto_update_policy, error))
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/* don't update changed for this; it's contained to RpmostreedDaemon so no other objects
|
||||
* need to be reloaded if it changes */
|
||||
self->idle_exit_timeout = idle_exit_timeout;
|
||||
|
||||
gboolean changed = FALSE;
|
||||
|
||||
changed = changed || (self->auto_update_policy != auto_update_policy);
|
||||
|
||||
self->auto_update_policy = auto_update_policy;
|
||||
|
||||
if (out_changed)
|
||||
*out_changed = FALSE;
|
||||
*out_changed = changed;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
|
@ -19,6 +19,7 @@
|
||||
#pragma once
|
||||
|
||||
#include "rpmostreed-types.h"
|
||||
#include "rpmostree-util.h"
|
||||
|
||||
#define RPMOSTREED_TYPE_DAEMON (rpmostreed_daemon_get_type ())
|
||||
#define RPMOSTREED_DAEMON(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), RPMOSTREED_TYPE_DAEMON, RpmostreedDaemon))
|
||||
@ -50,3 +51,6 @@ void rpmostreed_daemon_unpublish (RpmostreedDaemon *self,
|
||||
gboolean rpmostreed_daemon_reload_config (RpmostreedDaemon *self,
|
||||
gboolean *out_changed,
|
||||
GError **error);
|
||||
|
||||
RpmostreedAutomaticUpdatePolicy
|
||||
rpmostreed_get_automatic_update_policy (RpmostreedDaemon *self);
|
||||
|
@ -18,15 +18,19 @@
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <systemd/sd-journal.h>
|
||||
#include <libglnx.h>
|
||||
|
||||
#include "rpmostreed-deployment-utils.h"
|
||||
#include "rpmostree-origin.h"
|
||||
#include "rpmostree-util.h"
|
||||
#include "rpmostree-rpm-util.h"
|
||||
#include "rpmostree-sysroot-core.h"
|
||||
#include "rpmostree-core.h"
|
||||
#include "rpmostree-package-variants.h"
|
||||
#include "rpmostreed-utils.h"
|
||||
#include "rpmostreed-errors.h"
|
||||
|
||||
#include <libglnx.h>
|
||||
|
||||
/* Get a currently unique (for this host) identifier for the
|
||||
* deployment; TODO - adding the deployment timestamp would make it
|
||||
* persistently unique, needs API in libostree.
|
||||
@ -187,7 +191,7 @@ variant_add_metadata_attribute (GVariantDict *dict,
|
||||
|
||||
static void
|
||||
variant_add_commit_details (GVariantDict *dict,
|
||||
const char *prefix,
|
||||
const char *prefix,
|
||||
GVariant *commit)
|
||||
{
|
||||
g_autoptr(GVariant) metadata = NULL;
|
||||
@ -437,3 +441,454 @@ rpmostreed_commit_generate_cached_details_variant (OstreeDeployment *deployment,
|
||||
|
||||
return g_variant_ref_sink (g_variant_dict_end (&dict));
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
gboolean initialized;
|
||||
GPtrArray *upgraded;
|
||||
GPtrArray *downgraded;
|
||||
GPtrArray *removed;
|
||||
GPtrArray *added;
|
||||
} RpmDiff;
|
||||
|
||||
static void
|
||||
rpm_diff_init (RpmDiff *diff)
|
||||
{
|
||||
g_assert (!diff->initialized);
|
||||
diff->upgraded = g_ptr_array_new_with_free_func ((GDestroyNotify)g_variant_unref);
|
||||
diff->downgraded = g_ptr_array_new_with_free_func ((GDestroyNotify)g_variant_unref);
|
||||
diff->removed = g_ptr_array_new_with_free_func ((GDestroyNotify)g_variant_unref);
|
||||
diff->added = g_ptr_array_new_with_free_func ((GDestroyNotify)g_variant_unref);
|
||||
diff->initialized = TRUE;
|
||||
}
|
||||
|
||||
static void
|
||||
rpm_diff_clear (RpmDiff *diff)
|
||||
{
|
||||
if (!diff->initialized)
|
||||
return;
|
||||
g_clear_pointer (&diff->upgraded, (GDestroyNotify)g_ptr_array_unref);
|
||||
g_clear_pointer (&diff->downgraded, (GDestroyNotify)g_ptr_array_unref);
|
||||
g_clear_pointer (&diff->removed, (GDestroyNotify)g_ptr_array_unref);
|
||||
g_clear_pointer (&diff->added, (GDestroyNotify)g_ptr_array_unref);
|
||||
diff->initialized = FALSE;
|
||||
}
|
||||
|
||||
G_DEFINE_AUTO_CLEANUP_CLEAR_FUNC (RpmDiff, rpm_diff_clear);
|
||||
|
||||
static GVariant*
|
||||
single_pkg_variant_new (RpmOstreePkgTypes type,
|
||||
RpmOstreePackage *pkg)
|
||||
{
|
||||
return g_variant_ref_sink (
|
||||
g_variant_new ("(usss)", type,
|
||||
rpm_ostree_package_get_name (pkg),
|
||||
rpm_ostree_package_get_evr (pkg),
|
||||
rpm_ostree_package_get_arch (pkg)));
|
||||
}
|
||||
|
||||
static GVariant*
|
||||
modified_pkg_variant_new (RpmOstreePkgTypes type,
|
||||
RpmOstreePackage *pkg_old,
|
||||
RpmOstreePackage *pkg_new)
|
||||
{
|
||||
const char *name_old = rpm_ostree_package_get_name (pkg_old);
|
||||
const char *name_new = rpm_ostree_package_get_name (pkg_new);
|
||||
g_assert_cmpstr (name_old, ==, name_new);
|
||||
return g_variant_ref_sink (
|
||||
g_variant_new ("(us(ss)(ss))", type, name_old,
|
||||
rpm_ostree_package_get_evr (pkg_old),
|
||||
rpm_ostree_package_get_arch (pkg_old),
|
||||
rpm_ostree_package_get_evr (pkg_new),
|
||||
rpm_ostree_package_get_arch (pkg_new)));
|
||||
}
|
||||
|
||||
static GVariant*
|
||||
modified_dnfpkg_variant_new (RpmOstreePkgTypes type,
|
||||
RpmOstreePackage *pkg_old,
|
||||
DnfPackage *pkg_new)
|
||||
{
|
||||
const char *name_old = rpm_ostree_package_get_name (pkg_old);
|
||||
const char *name_new = dnf_package_get_name (pkg_new);
|
||||
g_assert_cmpstr (name_old, ==, name_new);
|
||||
return g_variant_ref_sink (
|
||||
g_variant_new ("(us(ss)(ss))", type, name_old,
|
||||
rpm_ostree_package_get_evr (pkg_old),
|
||||
rpm_ostree_package_get_arch (pkg_old),
|
||||
dnf_package_get_evr (pkg_new),
|
||||
dnf_package_get_arch (pkg_new)));
|
||||
}
|
||||
|
||||
static void
|
||||
rpm_diff_add_base_db_diff (RpmDiff *diff,
|
||||
/* element-type RpmOstreePackage */
|
||||
GPtrArray *removed,
|
||||
GPtrArray *added,
|
||||
GPtrArray *modified_old,
|
||||
GPtrArray *modified_new)
|
||||
{
|
||||
g_assert_cmpuint (modified_old->len, ==, modified_new->len);
|
||||
|
||||
RpmOstreePkgTypes type = RPM_OSTREE_PKG_TYPE_BASE;
|
||||
for (guint i = 0; i < removed->len; i++)
|
||||
g_ptr_array_add (diff->removed, single_pkg_variant_new (type, removed->pdata[i]));
|
||||
for (guint i = 0; i < added->len; i++)
|
||||
g_ptr_array_add (diff->added, single_pkg_variant_new (type, added->pdata[i]));
|
||||
for (guint i = 0; i < modified_old->len; i++)
|
||||
{
|
||||
RpmOstreePackage *old_pkg = modified_old->pdata[i];
|
||||
RpmOstreePackage *new_pkg = modified_new->pdata[i];
|
||||
if (rpm_ostree_package_cmp (old_pkg, new_pkg) < 0)
|
||||
g_ptr_array_add (diff->upgraded,
|
||||
modified_pkg_variant_new (type, old_pkg, new_pkg));
|
||||
else
|
||||
g_ptr_array_add (diff->downgraded,
|
||||
modified_pkg_variant_new (type, old_pkg, new_pkg));
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
rpm_diff_add_layered_diff (RpmDiff *diff,
|
||||
RpmOstreePackage *old_pkg,
|
||||
DnfPackage *new_pkg)
|
||||
{
|
||||
/* add to upgraded; layered pkgs only go up */
|
||||
RpmOstreePkgTypes type = RPM_OSTREE_PKG_TYPE_LAYER;
|
||||
g_ptr_array_add (diff->upgraded, modified_dnfpkg_variant_new (type, old_pkg, new_pkg));
|
||||
}
|
||||
|
||||
static int
|
||||
sort_pkgvariant_by_name (gconstpointer pkga_pp,
|
||||
gconstpointer pkgb_pp)
|
||||
{
|
||||
GVariant *pkg_a = *((GVariant**)pkga_pp);
|
||||
GVariant *pkg_b = *((GVariant**)pkgb_pp);
|
||||
|
||||
const char *pkgname_a;
|
||||
g_variant_get_child (pkg_a, 1, "&s", &pkgname_a);
|
||||
const char *pkgname_b;
|
||||
g_variant_get_child (pkg_b, 1, "&s", &pkgname_b);
|
||||
|
||||
return strcmp (pkgname_a, pkgname_b);
|
||||
}
|
||||
static GVariant*
|
||||
array_to_variant_new (const char *format, GPtrArray *array)
|
||||
{
|
||||
if (array->len == 0)
|
||||
return g_variant_new (format, NULL);
|
||||
|
||||
/* make doubly sure it's sorted */
|
||||
g_ptr_array_sort (array, sort_pkgvariant_by_name);
|
||||
|
||||
g_auto(GVariantBuilder) builder;
|
||||
g_variant_builder_init (&builder, G_VARIANT_TYPE_ARRAY);
|
||||
for (guint i = 0; i < array->len; i++)
|
||||
g_variant_builder_add_value (&builder, array->pdata[i]);
|
||||
return g_variant_builder_end (&builder);
|
||||
}
|
||||
|
||||
static GVariant*
|
||||
rpm_diff_variant_new (RpmDiff *diff)
|
||||
{
|
||||
g_assert (diff->initialized);
|
||||
g_auto(GVariantDict) dict;
|
||||
g_variant_dict_init (&dict, NULL);
|
||||
g_variant_dict_insert_value (&dict, "upgraded",
|
||||
array_to_variant_new ("a(us(ss)(ss))", diff->upgraded));
|
||||
g_variant_dict_insert_value (&dict, "downgraded",
|
||||
array_to_variant_new ("a(us(ss)(ss))", diff->downgraded));
|
||||
g_variant_dict_insert_value (&dict, "removed",
|
||||
array_to_variant_new ("a(usss)", diff->removed));
|
||||
g_variant_dict_insert_value (&dict, "added",
|
||||
array_to_variant_new ("a(usss)", diff->added));
|
||||
return g_variant_dict_end (&dict);
|
||||
}
|
||||
|
||||
static DnfPackage*
|
||||
find_newer_package (DnfSack *sack,
|
||||
RpmOstreePackage *pkg)
|
||||
{
|
||||
hy_autoquery HyQuery query = hy_query_create (sack);
|
||||
hy_query_filter (query, HY_PKG_NAME, HY_EQ, rpm_ostree_package_get_name (pkg));
|
||||
hy_query_filter (query, HY_PKG_EVR, HY_GT, rpm_ostree_package_get_evr (pkg));
|
||||
hy_query_filter (query, HY_PKG_ARCH, HY_NEQ, "src");
|
||||
hy_query_filter_latest (query, TRUE);
|
||||
g_autoptr(GPtrArray) new_pkgs = hy_query_run (query);
|
||||
if (new_pkgs->len == 0)
|
||||
return NULL; /* canonicalize to NULL */
|
||||
g_ptr_array_sort (new_pkgs, (GCompareFunc)rpmostree_pkg_array_compare);
|
||||
return g_object_ref (new_pkgs->pdata[new_pkgs->len-1]);
|
||||
}
|
||||
|
||||
/* For all layered pkgs, check if there are newer versions in the rpmmd. Add diff to
|
||||
* @rpm_diff, and all new pkgs in @out_newer_packages (these are used later for advisories).
|
||||
* */
|
||||
static gboolean
|
||||
rpmmd_diff (OstreeSysroot *sysroot,
|
||||
/* these are just to avoid refetching them */
|
||||
OstreeRepo *repo,
|
||||
OstreeDeployment *deployment,
|
||||
const char *base_checksum,
|
||||
DnfSack *sack,
|
||||
RpmDiff *rpm_diff,
|
||||
GPtrArray **out_newer_packages,
|
||||
GError **error)
|
||||
{
|
||||
/* Note here that we *don't* actually use layered_pkgs; we want to look at all the RPMs
|
||||
* installed, whereas the layered pkgs (actually patterns) just represent top-level
|
||||
* entries. IOW, we want to run through all layered RPMs, which include deps of
|
||||
* layered_pkgs. */
|
||||
|
||||
g_autoptr(GPtrArray) all_layered_pkgs = NULL;
|
||||
const char *layered_checksum = ostree_deployment_get_csum (deployment);
|
||||
RpmOstreeDbDiffExtFlags flags = RPM_OSTREE_DB_DIFF_EXT_ALLOW_NOENT;
|
||||
if (!rpm_ostree_db_diff_ext (repo, base_checksum, layered_checksum, flags, NULL,
|
||||
&all_layered_pkgs, NULL, NULL, NULL, error))
|
||||
return FALSE;
|
||||
|
||||
/* XXX: need to filter out local pkgs; though we still want to check for advisories --
|
||||
* maybe we should do this in status.c instead? */
|
||||
|
||||
if (all_layered_pkgs == NULL || /* -> older layer before we injected pkglist metadata */
|
||||
all_layered_pkgs->len == 0) /* -> no layered pkgs, e.g. override remove only */
|
||||
{
|
||||
*out_newer_packages = NULL;
|
||||
return TRUE; /* note early return */
|
||||
}
|
||||
|
||||
/* for each layered pkg, check if there's a newer version available (in reality, there may
|
||||
* be other new pkgs that need to be layered or some pkgs that no longer need to, but we
|
||||
* won't find out until we have the full commit available -- XXX: we could go the extra
|
||||
* effort and use the rpmdb of new_checksum if we already have it somehow, though that's
|
||||
* probably not the common case */
|
||||
|
||||
g_autoptr(GPtrArray) newer_packages =
|
||||
g_ptr_array_new_with_free_func ((GDestroyNotify)g_object_unref);
|
||||
for (guint i = 0; i < all_layered_pkgs->len; i++)
|
||||
{
|
||||
RpmOstreePackage *pkg = all_layered_pkgs->pdata[i];
|
||||
g_autoptr(DnfPackage) newer_pkg = find_newer_package (sack, pkg);
|
||||
if (!newer_pkg)
|
||||
continue;
|
||||
|
||||
g_ptr_array_add (newer_packages, g_object_ref (newer_pkg));
|
||||
rpm_diff_add_layered_diff (rpm_diff, pkg, newer_pkg);
|
||||
}
|
||||
|
||||
/* canonicalize to NULL if there's nothing new */
|
||||
if (newer_packages->len == 0)
|
||||
g_clear_pointer (&newer_packages, (GDestroyNotify)g_ptr_array_unref);
|
||||
|
||||
*out_newer_packages = g_steal_pointer (&newer_packages);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
get_cached_rpmmd_sack (OstreeSysroot *sysroot,
|
||||
OstreeRepo *repo,
|
||||
OstreeDeployment *deployment,
|
||||
DnfSack **out_sack,
|
||||
GError **error)
|
||||
{
|
||||
/* we don't need the full force of the core ctx here; we just want a DnfContext so that it
|
||||
* can load the repos and deal with releasever for us */
|
||||
g_autoptr(DnfContext) ctx = dnf_context_new ();
|
||||
|
||||
/* We have to point to the same source root for releasever to hit the right cache: an
|
||||
* interesting point here is that if there's a newer $releasever pending (i.e. 'deploy'
|
||||
* auto update policy), we'll still be using the previous releasever -- this is OK though,
|
||||
* we should be special-casing these rebases later re. how to display them; at least
|
||||
* status already shows endoflife. See also deploy_transaction_execute(). */
|
||||
g_autofree char *deployment_root = rpmostree_get_deployment_root (sysroot, deployment);
|
||||
dnf_context_set_source_root (ctx, deployment_root);
|
||||
g_autofree char *reposdir = g_build_filename (deployment_root, "etc/yum.repos.d", NULL);
|
||||
dnf_context_set_repo_dir (ctx, reposdir);
|
||||
dnf_context_set_cache_dir (ctx, RPMOSTREE_CORE_CACHEDIR RPMOSTREE_DIR_CACHE_REPOMD);
|
||||
dnf_context_set_solv_dir (ctx, RPMOSTREE_CORE_CACHEDIR RPMOSTREE_DIR_CACHE_SOLV);
|
||||
|
||||
if (!dnf_context_setup (ctx, NULL, error))
|
||||
return FALSE;
|
||||
|
||||
/* add the repos but strictly from cache; we should have already *just* checked &
|
||||
* refreshed metadata as part of the DeployTransaction; but we gracefully handle bad cache
|
||||
* too (e.g. if we start using the new dnf_context_clean_cache() on rebases?) */
|
||||
GPtrArray *repos = dnf_context_get_repos (ctx);
|
||||
|
||||
/* need a new DnfSackAddFlags flag for dnf_sack_add_repos to say "don't fallback to
|
||||
* updating if cache invalid/absent"; for now, just do it ourselves */
|
||||
GPtrArray *cached_enabled_repos = g_ptr_array_new ();
|
||||
for (guint i = 0; i < repos->len; i++)
|
||||
{
|
||||
DnfRepo *repo = repos->pdata[i];
|
||||
if ((dnf_repo_get_enabled (repo) & DNF_REPO_ENABLED_PACKAGES) == 0)
|
||||
continue;
|
||||
|
||||
/* TODO: We need to expand libdnf here to somehow do a dnf_repo_check() without it
|
||||
* triggering a download if there's no cache at all. Here, we just physically check
|
||||
* for the location. */
|
||||
const char *location = dnf_repo_get_location (repo);
|
||||
if (!glnx_fstatat_allow_noent (AT_FDCWD, location, NULL, 0, error))
|
||||
return FALSE;
|
||||
if (errno == ENOENT)
|
||||
continue;
|
||||
|
||||
g_autoptr(GError) local_error = NULL;
|
||||
g_autoptr(DnfState) state = dnf_state_new ();
|
||||
if (!dnf_repo_check (repo, G_MAXUINT, state, &local_error))
|
||||
sd_journal_print (LOG_WARNING, "Couldn't load cache for repo %s: %s",
|
||||
dnf_repo_get_id (repo), local_error->message);
|
||||
else
|
||||
g_ptr_array_add (cached_enabled_repos, repo);
|
||||
}
|
||||
|
||||
g_autoptr(DnfSack) sack = NULL;
|
||||
if (cached_enabled_repos->len > 0)
|
||||
{
|
||||
/* Set up our own sack and point it to the solv cache. TODO: We could've used the sack
|
||||
* from dnf_context_setup_sack(), but we need to extend libdnf to specify flags like
|
||||
* UPDATEINFO beforehand. Otherwise we have to add_repos() twice which almost double
|
||||
* startup time. */
|
||||
sack = dnf_sack_new ();
|
||||
dnf_sack_set_cachedir (sack, RPMOSTREE_CORE_CACHEDIR RPMOSTREE_DIR_CACHE_SOLV);
|
||||
if (!dnf_sack_setup (sack, DNF_SACK_SETUP_FLAG_MAKE_CACHE_DIR, error))
|
||||
return FALSE;
|
||||
|
||||
/* we still use add_repos rather than add_repo separately above because it does nice
|
||||
* things like process excludes */
|
||||
g_autoptr(DnfState) state = dnf_state_new ();
|
||||
if (!dnf_sack_add_repos (sack, cached_enabled_repos, G_MAXUINT,
|
||||
DNF_SACK_ADD_FLAG_UPDATEINFO, state, error))
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
*out_sack = g_steal_pointer (&sack);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/* The variant returned by this function is backwards compatible with the one returned by
|
||||
* rpmostreed_commit_generate_cached_details_variant(). However, it also includes a base
|
||||
* tree db diff, layered pkgs diff, state, advisories, etc... Also, it will happily return
|
||||
* NULL if no updates are available. */
|
||||
gboolean
|
||||
rpmostreed_update_generate_variant (OstreeSysroot *sysroot,
|
||||
OstreeDeployment *deployment,
|
||||
OstreeRepo *repo,
|
||||
GVariant **out_update,
|
||||
GError **error)
|
||||
{
|
||||
/* We try to minimize I/O in this function. We're in the daemon startup path, and thus
|
||||
* directly contribute to lag from a cold `rpm-ostree status`. Anyway, as a principle we
|
||||
* shouldn't do long-running operations outside of transactions. */
|
||||
|
||||
g_autoptr(RpmOstreeOrigin) origin = rpmostree_origin_parse_deployment (deployment, error);
|
||||
if (!origin)
|
||||
return FALSE;
|
||||
|
||||
const char *refspec = rpmostree_origin_get_refspec (origin);
|
||||
{ RpmOstreeRefspecType refspectype = RPMOSTREE_REFSPEC_TYPE_OSTREE;
|
||||
const char *refspec_data;
|
||||
if (!rpmostree_refspec_classify (refspec, &refspectype, &refspec_data, error))
|
||||
return FALSE;
|
||||
|
||||
/* we don't support jigdo-based origins yet */
|
||||
if (refspectype != RPMOSTREE_REFSPEC_TYPE_OSTREE)
|
||||
{
|
||||
*out_update = NULL;
|
||||
return TRUE; /* NB: early return */
|
||||
}
|
||||
|
||||
/* just skip over "ostree://" so we can talk with libostree without thinking about it */
|
||||
refspec = refspec_data;
|
||||
}
|
||||
|
||||
/* let's start with the ostree side of things */
|
||||
|
||||
g_autofree char *new_checksum = NULL;
|
||||
if (!ostree_repo_resolve_rev_ext (repo, refspec, TRUE, 0, &new_checksum, error))
|
||||
return FALSE;
|
||||
|
||||
const char *current_checksum = ostree_deployment_get_csum (deployment);
|
||||
gboolean is_layered;
|
||||
g_autofree char *current_checksum_owned = NULL;
|
||||
if (!rpmostree_deployment_get_layered_info (repo, deployment, &is_layered,
|
||||
¤t_checksum_owned, NULL, NULL, NULL,
|
||||
error))
|
||||
return FALSE;
|
||||
if (is_layered)
|
||||
current_checksum = current_checksum_owned;
|
||||
|
||||
/* Graciously handle rev no longer in repo; e.g. mucking around with rebase/rollback; we
|
||||
* still want to do the rpm-md phase. In that case, just use the current csum. */
|
||||
gboolean is_new_checksum = FALSE;
|
||||
if (!new_checksum)
|
||||
new_checksum = g_strdup (current_checksum);
|
||||
else
|
||||
is_new_checksum = !g_str_equal (new_checksum, current_checksum);
|
||||
|
||||
g_autoptr(GVariant) commit = NULL;
|
||||
if (!ostree_repo_load_commit (repo, new_checksum, &commit, NULL, error))
|
||||
return FALSE;
|
||||
|
||||
g_auto(GVariantDict) dict;
|
||||
g_variant_dict_init (&dict, NULL);
|
||||
|
||||
/* first get all the traditional/backcompat stuff */
|
||||
if (!add_all_commit_details_to_vardict (deployment, repo, refspec,
|
||||
new_checksum, commit, &dict, error))
|
||||
return FALSE;
|
||||
|
||||
/* This may seem trivial, but it's important to keep the final variant as self-contained
|
||||
* and "diff-based" as possible, since it'll be available as a D-Bus property. This makes
|
||||
* it easier to consume for UIs like GNOME Software and Cockpit. */
|
||||
g_variant_dict_insert (&dict, "ref-has-new-commit", "b", is_new_checksum);
|
||||
|
||||
g_auto(RpmDiff) rpm_diff = {0, };
|
||||
rpm_diff_init (&rpm_diff);
|
||||
|
||||
/* we'll need this later for advisories, so just keep it around */
|
||||
g_autoptr(GPtrArray) ostree_modified_new = NULL;
|
||||
if (is_new_checksum)
|
||||
{
|
||||
g_autoptr(GPtrArray) removed = NULL;
|
||||
g_autoptr(GPtrArray) added = NULL;
|
||||
g_autoptr(GPtrArray) modified_old = NULL;
|
||||
|
||||
/* Note we allow_noent here; we'll just skip over the rpm diff if there's no data */
|
||||
RpmOstreeDbDiffExtFlags flags = RPM_OSTREE_DB_DIFF_EXT_ALLOW_NOENT;
|
||||
if (!rpm_ostree_db_diff_ext (repo, current_checksum, new_checksum, flags, &removed,
|
||||
&added, &modified_old, &ostree_modified_new, NULL, error))
|
||||
return FALSE;
|
||||
|
||||
/* check if allow_noent kicked in */
|
||||
if (removed)
|
||||
rpm_diff_add_base_db_diff (&rpm_diff, removed, added,
|
||||
modified_old, ostree_modified_new);
|
||||
}
|
||||
|
||||
/* now we look at the rpm-md side */
|
||||
|
||||
/* first we try to set up a sack (NULL --> no cache available) */
|
||||
g_autoptr(DnfSack) sack = NULL;
|
||||
if (!get_cached_rpmmd_sack (sysroot, repo, deployment, &sack, error))
|
||||
return FALSE;
|
||||
|
||||
g_autoptr(GPtrArray) rpmmd_modified_new = NULL;
|
||||
|
||||
GHashTable *layered_pkgs = rpmostree_origin_get_packages (origin);
|
||||
/* check that it's actually layered (i.e. the requests are not all just dormant) */
|
||||
if (sack && is_layered && g_hash_table_size (layered_pkgs) > 0)
|
||||
{
|
||||
if (!rpmmd_diff (sysroot, repo, deployment, current_checksum, sack, &rpm_diff,
|
||||
&rpmmd_modified_new, error))
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
g_variant_dict_insert (&dict, "rpm-diff", "@a{sv}", rpm_diff_variant_new (&rpm_diff));
|
||||
|
||||
/* but if there are no updates, then just ditch the whole thing and return NULL */
|
||||
if (is_new_checksum || rpmmd_modified_new)
|
||||
*out_update = g_variant_ref_sink (g_variant_dict_end (&dict));
|
||||
else
|
||||
*out_update = NULL;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
@ -44,3 +44,9 @@ GVariant * rpmostreed_commit_generate_cached_details_variant (OstreeDeploym
|
||||
OstreeRepo *repo,
|
||||
const gchar *refspec,
|
||||
GError **error);
|
||||
gboolean
|
||||
rpmostreed_update_generate_variant (OstreeSysroot *sysroot,
|
||||
OstreeDeployment *deployment,
|
||||
OstreeRepo *repo,
|
||||
GVariant **out_update,
|
||||
GError **error);
|
||||
|
@ -20,6 +20,7 @@
|
||||
#include "ostree.h"
|
||||
|
||||
#include <libglnx.h>
|
||||
#include <systemd/sd-journal.h>
|
||||
|
||||
#include "rpmostreed-sysroot.h"
|
||||
#include "rpmostreed-daemon.h"
|
||||
@ -119,7 +120,9 @@ os_authorize_method (GDBusInterfaceSkeleton *interface,
|
||||
{
|
||||
g_ptr_array_add (actions, "org.projectatomic.rpmostree1.deploy");
|
||||
}
|
||||
else if (g_strcmp0 (method_name, "Upgrade") == 0)
|
||||
/* unite these for now; it could make sense at least to make "check" its own action */
|
||||
else if (g_strcmp0 (method_name, "Upgrade") == 0 ||
|
||||
g_strcmp0 (method_name, "AutomaticUpdateTrigger") == 0)
|
||||
{
|
||||
g_ptr_array_add (actions, "org.projectatomic.rpmostree1.upgrade");
|
||||
}
|
||||
@ -713,6 +716,20 @@ start_deployment_txn (GDBusMethodInvocation *invocation,
|
||||
cancellable, error);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
refresh_cached_update (RpmostreedOS*, GError **error);
|
||||
|
||||
static void
|
||||
on_auto_update_done (RpmostreedTransaction *transaction, RpmostreedOS *self)
|
||||
{
|
||||
g_autoptr(GError) local_error = NULL;
|
||||
if (!refresh_cached_update (self, &local_error))
|
||||
{
|
||||
sd_journal_print (LOG_WARNING, "Failed to refresh CachedUpdate property: %s",
|
||||
local_error->message);
|
||||
}
|
||||
}
|
||||
|
||||
typedef void (*InvocationCompleter)(RPMOSTreeOS*,
|
||||
GDBusMethodInvocation*,
|
||||
GUnixFDList*,
|
||||
@ -772,8 +789,15 @@ os_merge_or_start_deployment_txn (RPMOSTreeOS *interface,
|
||||
fd_list,
|
||||
&local_error);
|
||||
if (transaction)
|
||||
rpmostreed_transaction_monitor_add (self->transaction_monitor,
|
||||
transaction);
|
||||
rpmostreed_transaction_monitor_add (self->transaction_monitor, transaction);
|
||||
|
||||
/* For the AutomaticUpdateTrigger "check" and "download" cases, we want to make sure
|
||||
* we refresh CachedUpdate after; "deploy" will do this through sysroot_changed */
|
||||
const char *method_name = g_dbus_method_invocation_get_method_name (invocation);
|
||||
if (g_str_equal (method_name, "AutomaticUpdateTrigger") &&
|
||||
(default_flags & (RPMOSTREE_TRANSACTION_DEPLOY_FLAG_DOWNLOAD_ONLY |
|
||||
RPMOSTREE_TRANSACTION_DEPLOY_FLAG_DOWNLOAD_METADATA_ONLY)))
|
||||
g_signal_connect (transaction, "closed", G_CALLBACK (on_auto_update_done), self);
|
||||
}
|
||||
|
||||
if (transaction)
|
||||
@ -892,6 +916,75 @@ os_handle_update_deployment (RPMOSTreeOS *interface,
|
||||
rpmostree_os_complete_update_deployment);
|
||||
}
|
||||
|
||||
/* compat shim for call completer */
|
||||
static void automatic_update_trigger_completer (RPMOSTreeOS *os,
|
||||
GDBusMethodInvocation *invocation,
|
||||
GUnixFDList *dummy,
|
||||
const gchar *address)
|
||||
{ /* enabled */
|
||||
rpmostree_os_complete_automatic_update_trigger (os, invocation, TRUE, address);
|
||||
}
|
||||
|
||||
|
||||
/* we make this a separate method to keep the D-Bus API clean, but the actual
|
||||
* implementation is done by our dear friend deploy_transaction_execute(). ❤️
|
||||
*/
|
||||
|
||||
static gboolean
|
||||
os_handle_automatic_update_trigger (RPMOSTreeOS *interface,
|
||||
GDBusMethodInvocation *invocation,
|
||||
GVariant *arg_options)
|
||||
{
|
||||
g_auto(GVariantDict) dict;
|
||||
g_variant_dict_init (&dict, arg_options);
|
||||
const char *mode = vardict_lookup_ptr (&dict, "mode", "&s") ?: "auto";
|
||||
g_autoptr(GError) local_error = NULL;
|
||||
GError **error = &local_error;
|
||||
|
||||
RpmostreedAutomaticUpdatePolicy autoupdate_policy;
|
||||
if (g_str_equal (mode, "auto"))
|
||||
autoupdate_policy = rpmostreed_get_automatic_update_policy (rpmostreed_daemon_get ());
|
||||
else
|
||||
{
|
||||
if (!rpmostree_str_to_auto_update_policy (mode, &autoupdate_policy, error))
|
||||
{
|
||||
g_dbus_method_invocation_take_error (invocation, g_steal_pointer (&local_error));
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
/* Now we translate policy into flags the deploy transaction understands. But avoid
|
||||
* starting it at all if we're not even on. The benefit of this approach is that we keep
|
||||
* the Deploy transaction simpler. */
|
||||
|
||||
RpmOstreeTransactionDeployFlags dfault = 0;
|
||||
switch (autoupdate_policy)
|
||||
{
|
||||
case RPMOSTREED_AUTOMATIC_UPDATE_POLICY_NONE:
|
||||
{
|
||||
/* NB: we return the empty string here rather than NULL, because gdbus converts this
|
||||
* to a gvariant, which doesn't support NULL strings */ /* enabled */
|
||||
rpmostree_os_complete_automatic_update_trigger (interface, invocation, FALSE, "");
|
||||
return TRUE;
|
||||
}
|
||||
case RPMOSTREED_AUTOMATIC_UPDATE_POLICY_CHECK:
|
||||
dfault = RPMOSTREE_TRANSACTION_DEPLOY_FLAG_DOWNLOAD_METADATA_ONLY;
|
||||
break;
|
||||
default:
|
||||
g_assert_not_reached ();
|
||||
}
|
||||
|
||||
return os_merge_or_start_deployment_txn (
|
||||
interface,
|
||||
invocation,
|
||||
dfault,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
automatic_update_trigger_completer);
|
||||
}
|
||||
|
||||
|
||||
static gboolean
|
||||
os_handle_rollback (RPMOSTreeOS *interface,
|
||||
GDBusMethodInvocation *invocation,
|
||||
@ -1596,6 +1689,39 @@ out:
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
refresh_cached_update (RpmostreedOS *self, GError **error)
|
||||
{
|
||||
const char *name = rpmostree_os_get_name (RPMOSTREE_OS (self));
|
||||
OstreeSysroot *sysroot = rpmostreed_sysroot_get_root (rpmostreed_sysroot_get ());
|
||||
OstreeRepo *repo = ostree_sysroot_repo (sysroot);
|
||||
|
||||
/* if we're not handling the system sysroot, then let's just skip all this (e.g. `make
|
||||
* check` tests) */
|
||||
const char *sysroot_path = gs_file_get_path_cached (ostree_sysroot_get_path (sysroot));
|
||||
if (!g_str_equal (sysroot_path, "/"))
|
||||
return TRUE;
|
||||
|
||||
/* Note here we're *not* using rpmostree_syscore_get_origin_merge_deployment(): cached
|
||||
* updates are always relative to the booted/merge deployment; e.g. we still want to be
|
||||
* able to show details about a pending deployment. */
|
||||
g_autoptr(OstreeDeployment) merge_deployment =
|
||||
ostree_sysroot_get_merge_deployment (sysroot, name);
|
||||
|
||||
g_autoptr(GVariant) cached_update = NULL;
|
||||
|
||||
if (!rpmostreed_update_generate_variant (sysroot, merge_deployment, repo,
|
||||
&cached_update, error))
|
||||
return FALSE;
|
||||
|
||||
rpmostree_os_set_cached_update (RPMOSTREE_OS (self), cached_update);
|
||||
|
||||
/* for backwards compatibility */
|
||||
gboolean has_cached_updates = (cached_update != NULL);
|
||||
rpmostree_os_set_has_cached_update_rpm_diff (RPMOSTREE_OS (self), has_cached_updates);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
rpmostreed_os_load_internals (RpmostreedOS *self, GError **error)
|
||||
{
|
||||
@ -1603,16 +1729,12 @@ rpmostreed_os_load_internals (RpmostreedOS *self, GError **error)
|
||||
|
||||
OstreeDeployment *booted = NULL; /* owned by sysroot */
|
||||
g_autofree gchar* booted_id = NULL;
|
||||
glnx_unref_object OstreeDeployment *merge_deployment = NULL; /* transfered */
|
||||
|
||||
g_autoptr(GPtrArray) deployments = NULL;
|
||||
OstreeSysroot *ot_sysroot;
|
||||
OstreeRepo *ot_repo;
|
||||
GVariant *booted_variant = NULL;
|
||||
GVariant *default_variant = NULL;
|
||||
GVariant *rollback_variant = NULL;
|
||||
g_autoptr(GVariant) cached_update = NULL;
|
||||
gboolean has_cached_updates = FALSE;
|
||||
|
||||
name = rpmostree_os_get_name (RPMOSTREE_OS (self));
|
||||
g_debug ("loading %s", name);
|
||||
@ -1661,25 +1783,6 @@ rpmostreed_os_load_internals (RpmostreedOS *self, GError **error)
|
||||
}
|
||||
}
|
||||
|
||||
merge_deployment = ostree_sysroot_get_merge_deployment (ot_sysroot, name);
|
||||
if (merge_deployment)
|
||||
{
|
||||
g_autoptr(RpmOstreeOrigin) origin = NULL;
|
||||
|
||||
/* Don't fail here for unknown origin types */
|
||||
origin = rpmostree_origin_parse_deployment (merge_deployment, NULL);
|
||||
if (origin)
|
||||
{
|
||||
cached_update = rpmostreed_commit_generate_cached_details_variant (merge_deployment,
|
||||
ot_repo,
|
||||
rpmostree_origin_get_refspec (origin),
|
||||
error);
|
||||
if (!cached_update)
|
||||
return FALSE;
|
||||
has_cached_updates = cached_update != NULL;
|
||||
}
|
||||
}
|
||||
|
||||
if (!booted_variant)
|
||||
booted_variant = rpmostreed_deployment_generate_blank_variant ();
|
||||
rpmostree_os_set_booted_deployment (RPMOSTREE_OS (self),
|
||||
@ -1695,10 +1798,10 @@ rpmostreed_os_load_internals (RpmostreedOS *self, GError **error)
|
||||
rpmostree_os_set_rollback_deployment (RPMOSTREE_OS (self),
|
||||
rollback_variant);
|
||||
|
||||
rpmostree_os_set_cached_update (RPMOSTREE_OS (self), cached_update);
|
||||
rpmostree_os_set_has_cached_update_rpm_diff (RPMOSTREE_OS (self),
|
||||
has_cached_updates);
|
||||
g_dbus_interface_skeleton_flush(G_DBUS_INTERFACE_SKELETON (self));
|
||||
if (!refresh_cached_update (self, error))
|
||||
return FALSE;
|
||||
|
||||
g_dbus_interface_skeleton_flush (G_DBUS_INTERFACE_SKELETON (self));
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
@ -1706,6 +1809,7 @@ rpmostreed_os_load_internals (RpmostreedOS *self, GError **error)
|
||||
static void
|
||||
rpmostreed_os_iface_init (RPMOSTreeOSIface *iface)
|
||||
{
|
||||
iface->handle_automatic_update_trigger = os_handle_automatic_update_trigger;
|
||||
iface->handle_cleanup = os_handle_cleanup;
|
||||
iface->handle_get_deployment_boot_config = os_handle_get_deployment_boot_config;
|
||||
iface->handle_kernel_args = os_handle_kernel_args;
|
||||
|
@ -409,6 +409,20 @@ handle_unregister_client (RPMOSTreeSysroot *object,
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
reset_config_properties (RpmostreedSysroot *self,
|
||||
GError **error)
|
||||
{
|
||||
RpmostreedDaemon *daemon = rpmostreed_daemon_get ();
|
||||
|
||||
RpmostreedAutomaticUpdatePolicy policy = rpmostreed_get_automatic_update_policy (daemon);
|
||||
const char *policy_str = rpmostree_auto_update_policy_to_str (policy, NULL);
|
||||
g_assert (policy_str);
|
||||
rpmostree_sysroot_set_automatic_update_policy (RPMOSTREE_SYSROOT (self), policy_str);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
handle_reload_config (RPMOSTreeSysroot *object,
|
||||
GDBusMethodInvocation *invocation)
|
||||
@ -417,7 +431,11 @@ handle_reload_config (RPMOSTreeSysroot *object,
|
||||
g_autoptr(GError) local_error = NULL;
|
||||
GError **error = &local_error;
|
||||
|
||||
if (!rpmostreed_daemon_reload_config (rpmostreed_daemon_get (), NULL, error))
|
||||
gboolean changed = FALSE;
|
||||
if (!rpmostreed_daemon_reload_config (rpmostreed_daemon_get (), &changed, error))
|
||||
goto out;
|
||||
|
||||
if (changed && !reset_config_properties (self, error))
|
||||
goto out;
|
||||
|
||||
if (!rpmostreed_sysroot_reload (self, error))
|
||||
@ -742,6 +760,9 @@ rpmostreed_sysroot_populate (RpmostreedSysroot *self,
|
||||
if (!sysroot_populate_deployments_unlocked (self, NULL, error))
|
||||
return FALSE;
|
||||
|
||||
if (!reset_config_properties (self, error))
|
||||
return FALSE;
|
||||
|
||||
if (self->monitor == NULL)
|
||||
{
|
||||
const char *sysroot_path = gs_file_get_path_cached (ostree_sysroot_get_path (self->ot_sysroot));
|
||||
|
@ -176,6 +176,9 @@ package_diff_transaction_execute (RpmostreedTransaction *transaction,
|
||||
GCancellable *cancellable,
|
||||
GError **error)
|
||||
{
|
||||
/* XXX: we should just unify this with deploy_transaction_execute to take advantage of the
|
||||
* new pkglist metadata when possible */
|
||||
|
||||
PackageDiffTransaction *self = (PackageDiffTransaction *) transaction;
|
||||
RpmOstreeSysrootUpgraderFlags upgrader_flags = 0;
|
||||
|
||||
@ -598,6 +601,10 @@ deploy_transaction_execute (RpmostreedTransaction *transaction,
|
||||
/* Mainly for the `install` and `override` commands */
|
||||
const gboolean no_pull_base =
|
||||
((self->flags & RPMOSTREE_TRANSACTION_DEPLOY_FLAG_NO_PULL_BASE) > 0);
|
||||
/* Used to background check for updates; this essentially means downloading the minimum
|
||||
* amount of metadata only to check if there's an upgrade */
|
||||
const gboolean download_metadata_only =
|
||||
((self->flags & RPMOSTREE_TRANSACTION_DEPLOY_FLAG_DOWNLOAD_METADATA_ONLY) > 0);
|
||||
|
||||
RpmOstreeSysrootUpgraderFlags upgrader_flags = 0;
|
||||
if (self->flags & RPMOSTREE_TRANSACTION_DEPLOY_FLAG_ALLOW_DOWNGRADE)
|
||||
@ -605,6 +612,11 @@ deploy_transaction_execute (RpmostreedTransaction *transaction,
|
||||
if (dry_run)
|
||||
upgrader_flags |= RPMOSTREE_SYSROOT_UPGRADER_FLAGS_DRY_RUN;
|
||||
|
||||
/* DOWNLOAD_METADATA_ONLY isn't directly exposed at the D-Bus API level, so we shouldn't
|
||||
* ever run into these conflicting options */
|
||||
if (download_metadata_only)
|
||||
g_assert (!(no_pull_base || cache_only || download_only));
|
||||
|
||||
if (cache_only)
|
||||
{
|
||||
/* practically, we could unite those two into a single flag, though it's nice to be
|
||||
@ -701,8 +713,11 @@ deploy_transaction_execute (RpmostreedTransaction *transaction,
|
||||
else
|
||||
g_string_append (txn_title, "upgrade");
|
||||
|
||||
/* so users know we were probably fired by the automated timer when looking at status */
|
||||
if (cache_only)
|
||||
g_string_append (txn_title, " (cache only)");
|
||||
else if (download_metadata_only)
|
||||
g_string_append (txn_title, " (check only)");
|
||||
else if (download_only)
|
||||
g_string_append (txn_title, " (download only)");
|
||||
|
||||
@ -865,7 +880,11 @@ deploy_transaction_execute (RpmostreedTransaction *transaction,
|
||||
{
|
||||
gboolean base_changed;
|
||||
|
||||
if (!rpmostree_sysroot_upgrader_pull_base (upgrader, NULL, 0, progress,
|
||||
OstreeRepoPullFlags flags = OSTREE_REPO_PULL_FLAGS_NONE;
|
||||
if (download_metadata_only)
|
||||
flags |= OSTREE_REPO_PULL_FLAGS_COMMIT_ONLY;
|
||||
|
||||
if (!rpmostree_sysroot_upgrader_pull_base (upgrader, NULL, flags, progress,
|
||||
&base_changed, cancellable, error))
|
||||
return FALSE;
|
||||
|
||||
@ -933,6 +952,48 @@ deploy_transaction_execute (RpmostreedTransaction *transaction,
|
||||
changed = TRUE;
|
||||
}
|
||||
|
||||
if (download_metadata_only)
|
||||
{
|
||||
/* We have to short-circuit the usual path here; we already downloaded the ostree
|
||||
* metadata, so now we just need to update the rpmmd data (but only if we actually
|
||||
* have pkgs layered). This is still just a heuristic, since e.g. an InactiveRequest
|
||||
* may in fact become active in the new base, but we don't have the full tree. */
|
||||
|
||||
/* XXX: in jigdo mode we'll want to do this unconditionally */
|
||||
if (g_hash_table_size (rpmostree_origin_get_packages (origin)) > 0)
|
||||
{
|
||||
/* XXX: dedupe a bit more with RefreshMd path */
|
||||
g_autoptr(RpmOstreeContext) ctx =
|
||||
rpmostree_context_new_system (repo, cancellable, error);
|
||||
|
||||
/* Note here that we use the cfg merge deployment for releasever: the download
|
||||
* metadata only path is currently used only by the auto-update checker, and there
|
||||
* we want to show updates/vulnerabilities relative to the *booted* releasever.
|
||||
* Anyway, given that we don't yet do etc merges on boot, it shouldn't be too
|
||||
* common for users to stay long on e.g. f26 when they have f27 already deployed
|
||||
* and ready to reboot into. */
|
||||
g_autoptr(OstreeDeployment) cfg_merge_deployment =
|
||||
ostree_sysroot_get_merge_deployment (sysroot, self->osname);
|
||||
|
||||
g_autofree char *source_root =
|
||||
rpmostree_get_deployment_root (sysroot, cfg_merge_deployment);
|
||||
if (!rpmostree_context_setup (ctx, NULL, source_root, NULL, cancellable, error))
|
||||
return FALSE;
|
||||
|
||||
/* we always want to force a refetch of the metadata */
|
||||
dnf_context_set_cache_age (rpmostree_context_get_dnf (ctx), 0);
|
||||
|
||||
/* point libdnf to our repos dir */
|
||||
rpmostree_context_configure_from_deployment (ctx, sysroot, cfg_merge_deployment);
|
||||
|
||||
if (!rpmostree_context_download_metadata (ctx, cancellable, error))
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/* Note early return */
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
RpmOstreeSysrootUpgraderLayeringType layering_type;
|
||||
gboolean layering_changed = FALSE;
|
||||
if (!rpmostree_sysroot_upgrader_prep_layering (upgrader, &layering_type, &layering_changed,
|
||||
|
@ -19,6 +19,7 @@
|
||||
#pragma once
|
||||
|
||||
#include "rpmostreed-types.h"
|
||||
#include "rpmostreed-daemon.h"
|
||||
|
||||
#include <gio/gunixfdlist.h>
|
||||
|
||||
@ -56,6 +57,7 @@ typedef enum {
|
||||
RPMOSTREE_TRANSACTION_DEPLOY_FLAG_NO_OVERRIDES = (1 << 6),
|
||||
RPMOSTREE_TRANSACTION_DEPLOY_FLAG_CACHE_ONLY = (1 << 7),
|
||||
RPMOSTREE_TRANSACTION_DEPLOY_FLAG_DOWNLOAD_ONLY = (1 << 8),
|
||||
RPMOSTREE_TRANSACTION_DEPLOY_FLAG_DOWNLOAD_METADATA_ONLY = (1 << 9),
|
||||
} RpmOstreeTransactionDeployFlags;
|
||||
|
||||
|
||||
|
128
src/libpriv/libsd-time-util.c
Normal file
128
src/libpriv/libsd-time-util.c
Normal file
@ -0,0 +1,128 @@
|
||||
/***
|
||||
This file was originally part of systemd.
|
||||
|
||||
Copyright 2010 Lennart Poettering
|
||||
|
||||
systemd is free software; you can redistribute it and/or modify it
|
||||
under the terms of the GNU Lesser General Public License as published by
|
||||
the Free Software Foundation; either version 2.1 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
systemd 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
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with systemd; If not, see <http://www.gnu.org/licenses/>.
|
||||
***/
|
||||
|
||||
#include <glib.h>
|
||||
#include <sys/time.h>
|
||||
|
||||
#include "libsd-time-util.h"
|
||||
|
||||
static clockid_t map_clock_id(clockid_t c) {
|
||||
|
||||
/* Some more exotic archs (s390, ppc, …) lack the "ALARM" flavour of the clocks. Thus, clock_gettime() will
|
||||
* fail for them. Since they are essentially the same as their non-ALARM pendants (their only difference is
|
||||
* when timers are set on them), let's just map them accordingly. This way, we can get the correct time even on
|
||||
* those archs. */
|
||||
|
||||
switch (c) {
|
||||
|
||||
case CLOCK_BOOTTIME_ALARM:
|
||||
return CLOCK_BOOTTIME;
|
||||
|
||||
case CLOCK_REALTIME_ALARM:
|
||||
return CLOCK_REALTIME;
|
||||
|
||||
default:
|
||||
return c;
|
||||
}
|
||||
}
|
||||
|
||||
static usec_t timespec_load(const struct timespec *ts) {
|
||||
g_assert(ts);
|
||||
|
||||
if (ts->tv_sec < 0 || ts->tv_nsec < 0)
|
||||
return USEC_INFINITY;
|
||||
|
||||
if ((usec_t) ts->tv_sec > (UINT64_MAX - (ts->tv_nsec / NSEC_PER_USEC)) / USEC_PER_SEC)
|
||||
return USEC_INFINITY;
|
||||
|
||||
return
|
||||
(usec_t) ts->tv_sec * USEC_PER_SEC +
|
||||
(usec_t) ts->tv_nsec / NSEC_PER_USEC;
|
||||
}
|
||||
|
||||
static usec_t now(clockid_t clock_id) {
|
||||
struct timespec ts;
|
||||
|
||||
g_assert_cmpint (clock_gettime(map_clock_id(clock_id), &ts), ==, 0);
|
||||
|
||||
return timespec_load(&ts);
|
||||
}
|
||||
|
||||
char *libsd_format_timestamp_relative(char *buf, size_t l, usec_t t) {
|
||||
const char *s;
|
||||
usec_t n, d;
|
||||
|
||||
if (t <= 0 || t == USEC_INFINITY)
|
||||
return NULL;
|
||||
|
||||
n = now(CLOCK_REALTIME);
|
||||
if (n > t) {
|
||||
d = n - t;
|
||||
s = "ago";
|
||||
} else {
|
||||
d = t - n;
|
||||
s = "left";
|
||||
}
|
||||
|
||||
if (d >= USEC_PER_YEAR)
|
||||
snprintf(buf, l, USEC_FMT " years " USEC_FMT " months %s",
|
||||
d / USEC_PER_YEAR,
|
||||
(d % USEC_PER_YEAR) / USEC_PER_MONTH, s);
|
||||
else if (d >= USEC_PER_MONTH)
|
||||
snprintf(buf, l, USEC_FMT " months " USEC_FMT " days %s",
|
||||
d / USEC_PER_MONTH,
|
||||
(d % USEC_PER_MONTH) / USEC_PER_DAY, s);
|
||||
else if (d >= USEC_PER_WEEK)
|
||||
snprintf(buf, l, USEC_FMT " weeks " USEC_FMT " days %s",
|
||||
d / USEC_PER_WEEK,
|
||||
(d % USEC_PER_WEEK) / USEC_PER_DAY, s);
|
||||
else if (d >= 2*USEC_PER_DAY)
|
||||
snprintf(buf, l, USEC_FMT " days %s", d / USEC_PER_DAY, s);
|
||||
else if (d >= 25*USEC_PER_HOUR)
|
||||
snprintf(buf, l, "1 day " USEC_FMT "h %s",
|
||||
(d - USEC_PER_DAY) / USEC_PER_HOUR, s);
|
||||
else if (d >= 6*USEC_PER_HOUR)
|
||||
snprintf(buf, l, USEC_FMT "h %s",
|
||||
d / USEC_PER_HOUR, s);
|
||||
else if (d >= USEC_PER_HOUR)
|
||||
snprintf(buf, l, USEC_FMT "h " USEC_FMT "min %s",
|
||||
d / USEC_PER_HOUR,
|
||||
(d % USEC_PER_HOUR) / USEC_PER_MINUTE, s);
|
||||
else if (d >= 5*USEC_PER_MINUTE)
|
||||
snprintf(buf, l, USEC_FMT "min %s",
|
||||
d / USEC_PER_MINUTE, s);
|
||||
else if (d >= USEC_PER_MINUTE)
|
||||
snprintf(buf, l, USEC_FMT "min " USEC_FMT "s %s",
|
||||
d / USEC_PER_MINUTE,
|
||||
(d % USEC_PER_MINUTE) / USEC_PER_SEC, s);
|
||||
else if (d >= USEC_PER_SEC)
|
||||
snprintf(buf, l, USEC_FMT "s %s",
|
||||
d / USEC_PER_SEC, s);
|
||||
else if (d >= USEC_PER_MSEC)
|
||||
snprintf(buf, l, USEC_FMT "ms %s",
|
||||
d / USEC_PER_MSEC, s);
|
||||
else if (d > 0)
|
||||
snprintf(buf, l, USEC_FMT"us %s",
|
||||
d, s);
|
||||
else
|
||||
snprintf(buf, l, "now");
|
||||
|
||||
buf[l-1] = 0;
|
||||
return buf;
|
||||
}
|
67
src/libpriv/libsd-time-util.h
Normal file
67
src/libpriv/libsd-time-util.h
Normal file
@ -0,0 +1,67 @@
|
||||
#pragma once
|
||||
|
||||
/***
|
||||
This file was originally part of systemd.
|
||||
|
||||
Copyright 2010 Lennart Poettering
|
||||
|
||||
systemd is free software; you can redistribute it and/or modify it
|
||||
under the terms of the GNU Lesser General Public License as published by
|
||||
the Free Software Foundation; either version 2.1 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
systemd 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
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with systemd; If not, see <http://www.gnu.org/licenses/>.
|
||||
***/
|
||||
|
||||
#include <inttypes.h>
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <time.h>
|
||||
|
||||
typedef uint64_t usec_t;
|
||||
typedef uint64_t nsec_t;
|
||||
|
||||
#define PRI_NSEC PRIu64
|
||||
#define PRI_USEC PRIu64
|
||||
#define NSEC_FMT "%" PRI_NSEC
|
||||
#define USEC_FMT "%" PRI_USEC
|
||||
|
||||
#define USEC_INFINITY ((usec_t) -1)
|
||||
#define NSEC_INFINITY ((nsec_t) -1)
|
||||
|
||||
#define MSEC_PER_SEC 1000ULL
|
||||
#define USEC_PER_SEC ((usec_t) 1000000ULL)
|
||||
#define USEC_PER_MSEC ((usec_t) 1000ULL)
|
||||
#define NSEC_PER_SEC ((nsec_t) 1000000000ULL)
|
||||
#define NSEC_PER_MSEC ((nsec_t) 1000000ULL)
|
||||
#define NSEC_PER_USEC ((nsec_t) 1000ULL)
|
||||
|
||||
#define USEC_PER_MINUTE ((usec_t) (60ULL*USEC_PER_SEC))
|
||||
#define NSEC_PER_MINUTE ((nsec_t) (60ULL*NSEC_PER_SEC))
|
||||
#define USEC_PER_HOUR ((usec_t) (60ULL*USEC_PER_MINUTE))
|
||||
#define NSEC_PER_HOUR ((nsec_t) (60ULL*NSEC_PER_MINUTE))
|
||||
#define USEC_PER_DAY ((usec_t) (24ULL*USEC_PER_HOUR))
|
||||
#define NSEC_PER_DAY ((nsec_t) (24ULL*NSEC_PER_HOUR))
|
||||
#define USEC_PER_WEEK ((usec_t) (7ULL*USEC_PER_DAY))
|
||||
#define NSEC_PER_WEEK ((nsec_t) (7ULL*NSEC_PER_DAY))
|
||||
#define USEC_PER_MONTH ((usec_t) (2629800ULL*USEC_PER_SEC))
|
||||
#define NSEC_PER_MONTH ((nsec_t) (2629800ULL*NSEC_PER_SEC))
|
||||
#define USEC_PER_YEAR ((usec_t) (31557600ULL*USEC_PER_SEC))
|
||||
#define NSEC_PER_YEAR ((nsec_t) (31557600ULL*NSEC_PER_SEC))
|
||||
|
||||
/* We assume a maximum timezone length of 6. TZNAME_MAX is not defined on Linux, but glibc internally initializes this
|
||||
* to 6. Let's rely on that. */
|
||||
#define FORMAT_TIMESTAMP_MAX (3+1+10+1+8+1+6+1+6+1)
|
||||
#define FORMAT_TIMESTAMP_WIDTH 28 /* when outputting, assume this width */
|
||||
#define FORMAT_TIMESTAMP_RELATIVE_MAX 256
|
||||
#define FORMAT_TIMESPAN_MAX 64
|
||||
|
||||
char *libsd_format_timestamp_relative(char *buf, size_t l, usec_t t);
|
@ -46,10 +46,6 @@
|
||||
#define RPMOSTREE_MESSAGE_PKG_REPOS SD_ID128_MAKE(0e,ea,67,9b,bf,a3,4d,43,80,2d,ec,99,b2,74,eb,e7)
|
||||
#define RPMOSTREE_MESSAGE_PKG_IMPORT SD_ID128_MAKE(df,8b,b5,4f,04,fa,47,08,ac,16,11,1b,bf,4b,a3,52)
|
||||
|
||||
#define RPMOSTREE_DIR_CACHE_REPOMD "repomd"
|
||||
#define RPMOSTREE_DIR_CACHE_SOLV "solv"
|
||||
#define RPMOSTREE_DIR_LOCK "lock"
|
||||
|
||||
static OstreeRepo * get_pkgcache_repo (RpmOstreeContext *self);
|
||||
|
||||
/* Given a string, look for ostree:// or rojig:// prefix and
|
||||
|
@ -27,6 +27,10 @@
|
||||
#include "libglnx.h"
|
||||
|
||||
#define RPMOSTREE_CORE_CACHEDIR "/var/cache/rpm-ostree/"
|
||||
#define RPMOSTREE_DIR_CACHE_REPOMD "repomd"
|
||||
#define RPMOSTREE_DIR_CACHE_SOLV "solv"
|
||||
#define RPMOSTREE_DIR_LOCK "lock"
|
||||
|
||||
/* See http://lists.rpm.org/pipermail/rpm-maint/2017-October/006681.html */
|
||||
#define RPMOSTREE_RPMDB_LOCATION "usr/share/rpm"
|
||||
#define RPMOSTREE_SYSIMAGE_DIR "usr/lib/sysimage"
|
||||
|
@ -143,7 +143,7 @@ static char *
|
||||
pkg_nevra_strdup (Header h1)
|
||||
{
|
||||
return rpmostree_header_custom_nevra_strdup (h1, PKG_NEVRA_FLAGS_NAME |
|
||||
PKG_NEVRA_FLAGS_EPOCH_VERSION_RELEASE |
|
||||
PKG_NEVRA_FLAGS_EVR |
|
||||
PKG_NEVRA_FLAGS_ARCH);
|
||||
}
|
||||
|
||||
@ -893,9 +893,9 @@ rpmostree_get_refts_for_commit (OstreeRepo *repo,
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static gint
|
||||
pkg_array_compare (DnfPackage **p_pkg1,
|
||||
DnfPackage **p_pkg2)
|
||||
gint
|
||||
rpmostree_pkg_array_compare (DnfPackage **p_pkg1,
|
||||
DnfPackage **p_pkg2)
|
||||
{
|
||||
return dnf_package_cmp (*p_pkg1, *p_pkg2);
|
||||
}
|
||||
@ -915,7 +915,7 @@ rpmostree_sighandler_reset_cleanup (RpmSighandlerResetCleanup *cleanup)
|
||||
static void
|
||||
print_pkglist (GPtrArray *pkglist)
|
||||
{
|
||||
g_ptr_array_sort (pkglist, (GCompareFunc) pkg_array_compare);
|
||||
g_ptr_array_sort (pkglist, (GCompareFunc) rpmostree_pkg_array_compare);
|
||||
|
||||
for (guint i = 0; i < pkglist->len; i++)
|
||||
{
|
||||
@ -1124,7 +1124,7 @@ GPtrArray*
|
||||
rpmostree_sack_get_sorted_packages (DnfSack *sack)
|
||||
{
|
||||
g_autoptr(GPtrArray) pkglist = rpmostree_sack_get_packages (sack);
|
||||
g_ptr_array_sort (pkglist, (GCompareFunc)pkg_array_compare);
|
||||
g_ptr_array_sort (pkglist, (GCompareFunc)rpmostree_pkg_array_compare);
|
||||
return g_steal_pointer (&pkglist);
|
||||
}
|
||||
|
||||
|
@ -114,6 +114,10 @@ rpmostree_get_refts_for_commit (OstreeRepo *repo,
|
||||
GCancellable *cancellable,
|
||||
GError **error);
|
||||
|
||||
gint
|
||||
rpmostree_pkg_array_compare (DnfPackage **p_pkg1,
|
||||
DnfPackage **p_pkg2);
|
||||
|
||||
void
|
||||
rpmostree_print_transaction (DnfContext *context);
|
||||
|
||||
|
@ -30,7 +30,7 @@
|
||||
#include "rpmostree-util.h"
|
||||
#include "rpmostree-origin.h"
|
||||
#include "rpmostree-output.h"
|
||||
#include "rpmostree.h"
|
||||
#include "libsd-locale-util.h"
|
||||
#include "libglnx.h"
|
||||
|
||||
#define RPMOSTREE_OLD_PKGCACHE_DIR "extensions/rpmostree/pkgcache"
|
||||
@ -777,7 +777,7 @@ rpmostree_diff_print_formatted (GPtrArray *removed,
|
||||
{
|
||||
gboolean first;
|
||||
|
||||
g_assert (modified_old->len == modified_new->len);
|
||||
g_assert_cmpuint (modified_old->len, ==, modified_new->len);
|
||||
|
||||
first = TRUE;
|
||||
for (guint i = 0; i < modified_old->len; i++)
|
||||
@ -842,6 +842,50 @@ rpmostree_diff_print_formatted (GPtrArray *removed,
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
variant_diff_print_modified (guint max_key_len,
|
||||
GVariant *modified,
|
||||
const char *type)
|
||||
{
|
||||
guint n = g_variant_n_children (modified);
|
||||
for (guint i = 0; i < n; i++)
|
||||
{
|
||||
const char *name, *evr_old, *evr_new;
|
||||
g_variant_get_child (modified, i, "(u&s(&ss)(&ss))",
|
||||
NULL, &name, &evr_old, NULL, &evr_new, NULL);
|
||||
g_print (" %*s%s %s %s -> %s\n", max_key_len, i == 0 ? type : "", i == 0 ? ":" : " ",
|
||||
name, evr_old, evr_new);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
variant_diff_print_singles (guint max_key_len,
|
||||
GVariant *singles,
|
||||
const char *type)
|
||||
{
|
||||
guint n = g_variant_n_children (singles);
|
||||
for (guint i = 0; i < n; i++)
|
||||
{
|
||||
const char *name, *evr, *arch;
|
||||
g_variant_get_child (singles, i, "(u&s&s&s)", NULL, &name, &evr, &arch);
|
||||
g_print (" %*s%s %s-%s.%s\n", max_key_len, i == 0 ? type : "", i == 0 ? ":" : " ",
|
||||
name, evr, arch);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
rpmostree_variant_diff_print_formatted (guint max_key_len,
|
||||
GVariant *upgraded,
|
||||
GVariant *downgraded,
|
||||
GVariant *removed,
|
||||
GVariant *added)
|
||||
{
|
||||
variant_diff_print_modified (max_key_len, upgraded, "Upgraded");
|
||||
variant_diff_print_modified (max_key_len, downgraded, "Downgraded");
|
||||
variant_diff_print_singles (max_key_len, removed, "Removed");
|
||||
variant_diff_print_singles (max_key_len, added, "Added");
|
||||
}
|
||||
|
||||
static int
|
||||
pkg_cmp_end (RpmOstreePackage *a, RpmOstreePackage *b)
|
||||
{
|
||||
@ -957,3 +1001,33 @@ rpmostree_variant_bsearch_str (GVariant *array,
|
||||
*out_pos = imid;
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
const char*
|
||||
rpmostree_auto_update_policy_to_str (RpmostreedAutomaticUpdatePolicy policy,
|
||||
GError **error)
|
||||
{
|
||||
switch (policy)
|
||||
{
|
||||
case RPMOSTREED_AUTOMATIC_UPDATE_POLICY_NONE:
|
||||
return "none";
|
||||
case RPMOSTREED_AUTOMATIC_UPDATE_POLICY_CHECK:
|
||||
return "check";
|
||||
default:
|
||||
return glnx_null_throw (error, "Invalid policy value %u", policy);
|
||||
}
|
||||
}
|
||||
|
||||
gboolean
|
||||
rpmostree_str_to_auto_update_policy (const char *str,
|
||||
RpmostreedAutomaticUpdatePolicy *out_policy,
|
||||
GError **error)
|
||||
{
|
||||
g_assert (str);
|
||||
if (g_str_equal (str, "none") || g_str_equal (str, "off"))
|
||||
*out_policy = RPMOSTREED_AUTOMATIC_UPDATE_POLICY_NONE;
|
||||
else if (g_str_equal (str, "check"))
|
||||
*out_policy = RPMOSTREED_AUTOMATIC_UPDATE_POLICY_CHECK;
|
||||
else
|
||||
return glnx_throw (error, "Invalid value for AutomaticUpdatePolicy: '%s'", str);
|
||||
return TRUE;
|
||||
}
|
||||
|
@ -26,6 +26,7 @@
|
||||
#include <ostree.h>
|
||||
#include <libdnf/libdnf.h>
|
||||
#include "libglnx.h"
|
||||
#include "rpmostree.h"
|
||||
|
||||
#define _N(single, plural, n) ( (n) == 1 ? (single) : (plural) )
|
||||
#define _NS(n) _N("", "s", n)
|
||||
@ -99,6 +100,13 @@ rpmostree_diff_print_formatted (GPtrArray *removed,
|
||||
GPtrArray *modified_old,
|
||||
GPtrArray *modified_new);
|
||||
|
||||
void
|
||||
rpmostree_variant_diff_print_formatted (guint max_key_len,
|
||||
GVariant *upgraded,
|
||||
GVariant *downgraded,
|
||||
GVariant *removed,
|
||||
GVariant *added);
|
||||
|
||||
void
|
||||
rpmostree_diff_print (GPtrArray *removed,
|
||||
GPtrArray *added,
|
||||
@ -191,3 +199,24 @@ gboolean
|
||||
rpmostree_variant_bsearch_str (GVariant *array,
|
||||
const char *str,
|
||||
int *out_pos);
|
||||
|
||||
/* these are kept here for easier sharing with the client */
|
||||
|
||||
typedef enum {
|
||||
RPMOSTREED_AUTOMATIC_UPDATE_POLICY_NONE,
|
||||
RPMOSTREED_AUTOMATIC_UPDATE_POLICY_CHECK,
|
||||
} RpmostreedAutomaticUpdatePolicy;
|
||||
|
||||
const char*
|
||||
rpmostree_auto_update_policy_to_str (RpmostreedAutomaticUpdatePolicy policy,
|
||||
GError **error);
|
||||
|
||||
gboolean
|
||||
rpmostree_str_to_auto_update_policy (const char *str,
|
||||
RpmostreedAutomaticUpdatePolicy *out_policy,
|
||||
GError **error);
|
||||
|
||||
typedef enum {
|
||||
RPM_OSTREE_PKG_TYPE_BASE,
|
||||
RPM_OSTREE_PKG_TYPE_LAYER,
|
||||
} RpmOstreePkgTypes;
|
||||
|
@ -487,20 +487,21 @@ $files
|
||||
EOF
|
||||
(cd $test_tmpdir/yumrepo/specs &&
|
||||
rpmbuild -ba $name.spec \
|
||||
--define "_topdir $PWD" \
|
||||
--define "_sourcedir $PWD" \
|
||||
--define "_specdir $PWD" \
|
||||
--define "_builddir $PWD/.build" \
|
||||
--define "_srcrpmdir $PWD" \
|
||||
--define "_rpmdir $test_tmpdir/yumrepo/packages" \
|
||||
--define "_buildrootdir $PWD")
|
||||
(cd yumrepo && createrepo_c --no-database .)
|
||||
if test '!' -f yumrepo.repo; then
|
||||
cat > yumrepo.repo.tmp << EOF
|
||||
(cd $test_tmpdir/yumrepo && createrepo_c --no-database .)
|
||||
if test '!' -f $test_tmpdir/yumrepo.repo; then
|
||||
cat > $test_tmpdir/yumrepo.repo.tmp << EOF
|
||||
[test-repo]
|
||||
name=test-repo
|
||||
baseurl=file:///$PWD/yumrepo
|
||||
EOF
|
||||
mv yumrepo.repo{.tmp,}
|
||||
mv $test_tmpdir/yumrepo.repo{.tmp,}
|
||||
fi
|
||||
}
|
||||
|
||||
|
@ -48,7 +48,7 @@ vm_raw_rsync() {
|
||||
vm_rsync() {
|
||||
if ! test -f .vagrant/using_sshfs; then
|
||||
pushd ${topsrcdir}
|
||||
vm_raw_rsync --exclude .git/ . $VM:/var/roothome/sync
|
||||
vm_raw_rsync --delete --exclude .git/ . $VM:/var/roothome/sync
|
||||
popd
|
||||
fi
|
||||
}
|
||||
|
99
tests/utils/inject-pkglist.c
Normal file
99
tests/utils/inject-pkglist.c
Normal file
@ -0,0 +1,99 @@
|
||||
/*
|
||||
Given a ref, read its pkglist, inject it in a new commit that is for our
|
||||
purposes identical to the one the ref is pointing to, then reset the ref to that
|
||||
commit. Essentially, we replace the tip with a copy, except that it has the
|
||||
pkglist metadata.
|
||||
|
||||
This is used by tests that test features that require the new pkglist metadata
|
||||
and is also really useful for debugging.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <err.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <glib-unix.h>
|
||||
|
||||
#include "libglnx.h"
|
||||
#include "rpmostree-rpm-util.h"
|
||||
|
||||
static gboolean
|
||||
impl (const char *repo_path,
|
||||
const char *refspec,
|
||||
GError **error)
|
||||
{
|
||||
g_autofree char *remote = NULL;
|
||||
g_autofree char *ref = NULL;
|
||||
if (!ostree_parse_refspec (refspec, &remote, &ref, error))
|
||||
return FALSE;
|
||||
|
||||
g_autoptr(OstreeRepo) repo = ostree_repo_open_at (AT_FDCWD, repo_path, NULL, error);
|
||||
if (!repo)
|
||||
return FALSE;
|
||||
|
||||
g_autofree char *checksum = NULL;
|
||||
if (!ostree_repo_resolve_rev (repo, refspec, FALSE, &checksum, error))
|
||||
return FALSE;
|
||||
|
||||
g_autoptr(GVariant) commit = NULL;
|
||||
if (!ostree_repo_load_commit (repo, checksum, &commit, NULL, error))
|
||||
return FALSE;
|
||||
|
||||
g_autoptr(GVariant) meta = g_variant_get_child_value (commit, 0);
|
||||
g_autoptr(GVariantDict) meta_dict = g_variant_dict_new (meta);
|
||||
if (g_variant_dict_contains (meta_dict, "rpmostree.rpmdb.pkglist"))
|
||||
{
|
||||
g_print ("Refspec '%s' already has pkglist metadata; exiting.\n", refspec);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/* just an easy way to checkout the rpmdb */
|
||||
g_autoptr(RpmOstreeRefSack) rsack =
|
||||
rpmostree_get_refsack_for_commit (repo, checksum, NULL, error);
|
||||
if (!rsack)
|
||||
return FALSE;
|
||||
g_assert (rsack->tmpdir.initialized);
|
||||
|
||||
g_autoptr(GVariant) pkglist = NULL;
|
||||
if (!rpmostree_create_rpmdb_pkglist_variant (rsack->tmpdir.fd, ".", &pkglist, NULL, error))
|
||||
return FALSE;
|
||||
|
||||
g_variant_dict_insert_value (meta_dict, "rpmostree.rpmdb.pkglist", pkglist);
|
||||
g_autoptr(GVariant) new_meta = g_variant_ref_sink (g_variant_dict_end (meta_dict));
|
||||
|
||||
g_autoptr(GFile) root = NULL;
|
||||
if (!ostree_repo_read_commit (repo, checksum, &root, NULL, NULL, error))
|
||||
return FALSE;
|
||||
|
||||
g_autofree char *new_checksum = NULL;
|
||||
g_autofree char *parent = ostree_commit_get_parent (commit);
|
||||
if (!ostree_repo_write_commit (repo, parent, "", "", new_meta, OSTREE_REPO_FILE (root),
|
||||
&new_checksum, NULL, error))
|
||||
return FALSE;
|
||||
|
||||
if (!ostree_repo_set_ref_immediate (repo, remote, ref, new_checksum, NULL, error))
|
||||
return FALSE;
|
||||
|
||||
g_print("%s => %s\n", refspec, new_checksum);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
int
|
||||
main (int argc, char *argv[])
|
||||
{
|
||||
if (argc != 3)
|
||||
errx (EXIT_FAILURE, "Usage: %s <repo> <refspec>", argv[0]);
|
||||
|
||||
const char *repo_path = argv[1];
|
||||
const char *refspec = argv[2];
|
||||
|
||||
g_autoptr(GError) local_error = NULL;
|
||||
if (!impl (repo_path, refspec, &local_error))
|
||||
errx (EXIT_FAILURE, "%s", local_error->message);
|
||||
g_assert (local_error == NULL);
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
@ -12,6 +12,26 @@ if test -z "${INSIDE_VM:-}"; then
|
||||
fi
|
||||
|
||||
vm_rsync
|
||||
|
||||
# ✀✀✀ BEGIN selinux-policy hack (part 1) for
|
||||
# https://github.com/fedora-selinux/selinux-policy-contrib/pull/45
|
||||
selhack=selinux-tmp-hack
|
||||
if ! vm_cmd sesearch -A -s init_t -t install_t -c dbus | grep -q allow; then
|
||||
echo "Activating selinux-tmp-hack"
|
||||
d=$(mktemp -d)
|
||||
cat > $d/$selhack.te << 'EOF'
|
||||
policy_module(selinux-tmp-hack, 1.0.0)
|
||||
gen_require(`
|
||||
type install_t;
|
||||
')
|
||||
init_dbus_chat(install_t)
|
||||
EOF
|
||||
make -C $d -f /usr/share/selinux/devel/Makefile $selhack.pp
|
||||
vm_send /var/roothome/sync $d/$selhack.pp
|
||||
rm -rf $d
|
||||
fi
|
||||
# ✀✀✀ END selinux-policy hack ✀✀✀
|
||||
|
||||
vm_cmd env INSIDE_VM=1 /var/roothome/sync/tests/vmcheck/overlay.sh
|
||||
vm_reboot
|
||||
exit 0
|
||||
@ -54,6 +74,20 @@ INSTTREE=/var/roothome/sync/insttree
|
||||
rsync -rlv $INSTTREE/usr/ vmcheck/usr/
|
||||
rsync -rlv $INSTTREE/etc/ vmcheck/usr/etc/
|
||||
|
||||
## ✀✀✀ BEGIN selinux-policy hack (part 2) for
|
||||
## https://github.com/fedora-selinux/selinux-policy-contrib/pull/45
|
||||
selhack=selinux-tmp-hack
|
||||
pp=/var/roothome/sync/$selhack.pp
|
||||
if [ -f $pp ]; then
|
||||
seld=usr/share/selinux/packages/$selhack
|
||||
mkdir -p vmcheck/$seld
|
||||
cp $pp vmcheck/$seld
|
||||
mkdir vmcheck/var/tmp # bwrap wrapper will mount tmpfs there
|
||||
/var/roothome/sync/scripts/bwrap-script-shell.sh /ostree/repo/tmp/vmcheck \
|
||||
semodule -v -n -i /$seld/$selhack.pp
|
||||
fi
|
||||
## ✀✀✀ END selinux-policy hack ✀✀✀
|
||||
|
||||
# ✀✀✀ BEGIN hack to get --keep-metadata
|
||||
if ! ostree commit --help | grep -q -e --keep-metadata; then
|
||||
# this is fine, rsync doesn't modify in place
|
||||
@ -63,7 +97,14 @@ if ! ostree commit --help | grep -q -e --keep-metadata; then
|
||||
fi
|
||||
# ✀✀✀ END hack to get --keep-metadata ✀✀✀
|
||||
|
||||
# if the commit already has pkglist metadata (i.e. the tree was composed with at
|
||||
# least v2018.1), make sure it gets preserved, because it's useful for playing
|
||||
# around (but note it's not a requirement for our tests)
|
||||
commit_opts=
|
||||
if ostree show $commit --raw | grep -q rpmostree.rpmdb.pkglist; then
|
||||
commit_opts="${commit_opts} --keep-metadata=rpmostree.rpmdb.pkglist"
|
||||
fi
|
||||
|
||||
source_opt= # make this its own var since it contains spaces
|
||||
if [ $origin != vmcheck ]; then
|
||||
source_title="${origin}"
|
||||
@ -82,4 +123,5 @@ fi
|
||||
ostree commit --parent=$commit -b vmcheck --consume --no-bindings \
|
||||
--link-checkout-speedup ${commit_opts} "${source_opt}" \
|
||||
--selinux-policy=vmcheck --tree=dir=vmcheck
|
||||
|
||||
ostree admin deploy vmcheck
|
||||
|
186
tests/vmcheck/test-autoupdate.sh
Executable file
186
tests/vmcheck/test-autoupdate.sh
Executable file
@ -0,0 +1,186 @@
|
||||
#!/bin/bash
|
||||
#
|
||||
# Copyright (C) 2018 Jonathan Lebon
|
||||
#
|
||||
# This library is free software; you can redistribute it and/or
|
||||
# modify it under the terms of the GNU Lesser General Public
|
||||
# License as published by the Free Software Foundation; either
|
||||
# version 2 of the License, or (at your option) any later version.
|
||||
#
|
||||
# This library 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
|
||||
# Lesser General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Lesser General Public
|
||||
# License along with this library; if not, write to the
|
||||
# Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
||||
# Boston, MA 02111-1307, USA.
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
. ${commondir}/libtest.sh
|
||||
. ${commondir}/libvm.sh
|
||||
|
||||
set -x
|
||||
|
||||
# first, let's make sure the timer is disabled so it doesn't mess up with our
|
||||
# tests
|
||||
vm_cmd systemctl disable --now rpm-ostreed-automatic.timer
|
||||
|
||||
# Really testing this like a user requires a remote ostree server setup.
|
||||
# Let's start by setting up the repo.
|
||||
REMOTE_OSTREE=/ostree/repo/tmp/vmcheck-remote
|
||||
vm_cmd mkdir -p $REMOTE_OSTREE
|
||||
vm_cmd ostree init --repo=$REMOTE_OSTREE --mode=archive
|
||||
vm_start_httpd ostree_server $REMOTE_OSTREE 8888
|
||||
|
||||
# We need to build up a history on the server. Rather than wasting time
|
||||
# composing trees for real, we just use client package layering to create new
|
||||
# trees that we then "lift" into the server before cleaning them up client-side.
|
||||
|
||||
# steal a commit from the system repo and make a branch out of it
|
||||
lift_commit() {
|
||||
checksum=$1; shift
|
||||
branch=$1; shift
|
||||
vm_cmd ostree pull-local --repo=$REMOTE_OSTREE --disable-fsync \
|
||||
/ostree/repo $checksum
|
||||
vm_cmd ostree --repo=$REMOTE_OSTREE refs $branch --delete
|
||||
vm_cmd ostree --repo=$REMOTE_OSTREE refs $checksum --create=$branch
|
||||
}
|
||||
|
||||
# use a previously stolen commit to create an update on our vmcheck branch,
|
||||
# complete with version string and pkglist metadata
|
||||
create_update() {
|
||||
branch=$1; shift
|
||||
vm_cmd ostree commit --repo=$REMOTE_OSTREE -b vmcheck \
|
||||
--tree=ref=$branch --add-metadata-string=version=$branch --fsync=no
|
||||
# avoid libtool wrapper here since we're running on the VM and it would try to
|
||||
# cd to topsrcdir/use gcc; libs are installed anyway
|
||||
vm_cmd /var/roothome/sync/.libs/inject-pkglist $REMOTE_OSTREE vmcheck
|
||||
}
|
||||
|
||||
# (delete ref but don't prune for easier debugging)
|
||||
vm_cmd ostree refs --repo=$REMOTE_OSTREE vmcheck --delete
|
||||
|
||||
# now let's build some pkgs that we'll jury-rig into a base update
|
||||
# this whole block can be commented out for a speed-up when iterating locally
|
||||
vm_build_rpm base-pkg-foo version 1.4 release 7
|
||||
vm_build_rpm base-pkg-bar
|
||||
vm_build_rpm base-pkg-baz version 1.1 release 1
|
||||
vm_rpmostree install base-pkg-{foo,bar,baz}
|
||||
lift_commit $(vm_get_pending_csum) v1
|
||||
vm_rpmostree cleanup -p
|
||||
rm -rf $test_tmpdir/yumrepo
|
||||
vm_build_rpm base-pkg-foo version 1.4 release 8 # upgraded
|
||||
vm_build_rpm base-pkg-bar version 0.9 release 3 # downgraded
|
||||
vm_build_rpm base-pkg-boo version 3.7 release 2.11 # added
|
||||
vm_rpmostree install base-pkg-{foo,bar,boo}
|
||||
lift_commit $(vm_get_pending_csum) v2
|
||||
vm_rpmostree cleanup -p
|
||||
|
||||
# ok, we're done with prep, now let's rebase on the first revision and install a
|
||||
# layered package
|
||||
create_update v1
|
||||
vm_cmd ostree remote add vmcheckmote --no-gpg-verify http://localhost:8888/
|
||||
vm_build_rpm layered-cake version 2.1 release 3
|
||||
vm_rpmostree rebase vmcheckmote:vmcheck --install layered-cake
|
||||
vm_reboot
|
||||
vm_rpmostree status -v
|
||||
vm_assert_status_jq \
|
||||
".deployments[0][\"origin\"] == \"vmcheckmote:vmcheck\"" \
|
||||
".deployments[0][\"version\"] == \"v1\"" \
|
||||
'.deployments[0]["packages"]|length == 1' \
|
||||
'.deployments[0]["packages"]|index("layered-cake") >= 0'
|
||||
echo "ok prep"
|
||||
|
||||
# start it up again since we rebooted
|
||||
vm_start_httpd ostree_server $REMOTE_OSTREE 8888
|
||||
|
||||
change_policy() {
|
||||
policy=$1; shift
|
||||
vm_cmd cp /usr/etc/rpm-ostreed.conf /etc
|
||||
cat > tmp.sh << EOF
|
||||
echo -e "[Daemon]\nAutomaticUpdatePolicy=$policy" > /etc/rpm-ostreed.conf
|
||||
EOF
|
||||
vm_cmdfile tmp.sh
|
||||
vm_rpmostree reload
|
||||
}
|
||||
|
||||
# make sure that off means off
|
||||
change_policy off
|
||||
vm_rpmostree status | grep 'auto updates disabled'
|
||||
vm_rpmostree upgrade --trigger-automatic-update-policy > out.txt
|
||||
assert_file_has_content out.txt "Automatic updates are not enabled; exiting"
|
||||
echo "ok disabled"
|
||||
|
||||
# ok, let's test out check
|
||||
change_policy check
|
||||
vm_rpmostree status | grep 'auto updates enabled (check'
|
||||
|
||||
# build an *older version* and check that we don't report an update
|
||||
vm_build_rpm layered-cake version 2.1 release 2
|
||||
vm_rpmostree upgrade --trigger-automatic-update-policy
|
||||
vm_rpmostree status -v > out.txt
|
||||
assert_not_file_has_content out.txt "Available update"
|
||||
|
||||
# build a *newer version* and check that we report an update
|
||||
vm_build_rpm layered-cake version 2.1 release 4
|
||||
vm_rpmostree upgrade --trigger-automatic-update-policy
|
||||
vm_rpmostree status > out.txt
|
||||
assert_file_has_content out.txt "Available update"
|
||||
assert_file_has_content out.txt "Diff: 1 upgraded"
|
||||
vm_rpmostree status -v > out.txt
|
||||
assert_file_has_content out.txt "Upgraded: layered-cake 2.1-3 -> 2.1-4"
|
||||
# make sure we don't report ostree-based stuff somehow
|
||||
! grep -A999 'Available update' out.txt | grep "Version"
|
||||
! grep -A999 'Available update' out.txt | grep "Timestamp"
|
||||
! grep -A999 'Available update' out.txt | grep "Commit"
|
||||
echo "ok check mode layered only"
|
||||
|
||||
# ok now let's add ostree updates in the picture
|
||||
create_update v2
|
||||
vm_rpmostree upgrade --trigger-automatic-update-policy
|
||||
|
||||
# make sure we only pulled down the commit metadata
|
||||
if vm_cmd ostree checkout vmcheckmote:vmcheck --subpath /usr/share/rpm; then
|
||||
assert_not_reached "Was able to checkout /usr/share/rpm?"
|
||||
fi
|
||||
|
||||
assert_update() {
|
||||
vm_assert_status_jq \
|
||||
'.["cached-update"]["origin"] == "vmcheckmote:vmcheck"' \
|
||||
'.["cached-update"]["version"] == "v2"' \
|
||||
'.["cached-update"]["ref-has-new-commit"] == true' \
|
||||
'.["cached-update"]["gpg-enabled"] == false'
|
||||
|
||||
# we could assert more json here, though how it's presented to users is
|
||||
# important, and implicitly tests the json
|
||||
vm_rpmostree status > out.txt
|
||||
assert_file_has_content out.txt 'Diff: 2 upgraded, 1 downgraded, 1 removed, 1 added'
|
||||
|
||||
vm_rpmostree status -v > out.txt
|
||||
assert_file_has_content out.txt 'Upgraded: base-pkg-foo 1.4-7 -> 1.4-8'
|
||||
assert_file_has_content out.txt " layered-cake 2.1-3 -> 2.1-4"
|
||||
assert_file_has_content out.txt 'Downgraded: base-pkg-bar 1.0-1 -> 0.9-3'
|
||||
assert_file_has_content out.txt 'Removed: base-pkg-baz-1.1-1.x86_64'
|
||||
assert_file_has_content out.txt 'Added: base-pkg-boo-3.7-2.11.x86_64'
|
||||
}
|
||||
|
||||
assert_update
|
||||
echo "ok check mode ostree"
|
||||
|
||||
assert_default_deployment_is_update() {
|
||||
vm_assert_status_jq \
|
||||
'.deployments[0]["origin"] == "vmcheckmote:vmcheck"' \
|
||||
'.deployments[0]["version"] == "v2"' \
|
||||
'.deployments[0]["packages"]|length == 1' \
|
||||
'.deployments[0]["packages"]|index("layered-cake") >= 0'
|
||||
vm_rpmostree db list $(vm_get_pending_csum) > list.txt
|
||||
assert_file_has_content list.txt 'layered-cake-2.1-4.x86_64'
|
||||
}
|
||||
|
||||
# now let's upgrade and check that it matches what we expect
|
||||
vm_rpmostree upgrade
|
||||
assert_default_deployment_is_update
|
||||
echo "ok upgrade"
|
@ -76,6 +76,8 @@ if vm_cmd test -f /etc/rpm-ostreed.conf; then
|
||||
fi
|
||||
vm_cmd cp -f /usr/etc/rpm-ostreed.conf /etc
|
||||
|
||||
vm_cmd ostree remote delete --if-exists vmcheckmote
|
||||
|
||||
origdir=$(pwd)
|
||||
echo -n '' > ${LOG}
|
||||
|
||||
@ -186,6 +188,7 @@ for tf in $(find . -name 'test-*.sh' | sort); do
|
||||
# and clean up any leftovers from our tmp
|
||||
osname=$(vm_get_booted_deployment_info osname)
|
||||
vm_cmd rm -rf /ostree/deploy/$osname/var/tmp/vmcheck
|
||||
vm_cmd ostree remote delete --if-exists vmcheckmote
|
||||
done
|
||||
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user