From 46fa7190e75ef72681a2e421ad2906d4bbfe65b0 Mon Sep 17 00:00:00 2001 From: Jonathan Lebon Date: Thu, 12 Oct 2017 20:42:55 +0000 Subject: [PATCH] app: support full offline operations with --cache-only As Colin mentioned in #1035, the new `--cache-only` implemented only the rpmmd half of the story. Here we complete that story by also ensuring that when in cache-only mode, we don't download new ostree data nor new packages. We try to complete the requested operation with what we have. To do this, we add support for the same `SYNTHETIC` pull that was added in ostree[1] so that we don't actually pull, but still perform timestamp checking. On the pkgcache side, we disable all remote repos and instead insert all our cached RPMs into the `DnfSack`. Care is taken to still perform SHA256 verification for local pkg installs/replacements. [1] https://github.com/ostreedev/ostree/pull/642 --- man/rpm-ostree.xml | 23 ++++ src/app/rpmostree-builtin-deploy.c | 2 +- src/app/rpmostree-builtin-rebase.c | 2 +- src/app/rpmostree-builtin-upgrade.c | 2 +- src/app/rpmostree-dbus-helpers.c | 2 +- src/app/rpmostree-pkg-builtins.c | 2 +- src/daemon/org.projectatomic.rpmostree1.xml | 9 +- src/daemon/rpmostree-sysroot-upgrader.c | 18 ++-- src/daemon/rpmostree-sysroot-upgrader.h | 6 +- src/daemon/rpmostreed-os.c | 4 +- src/daemon/rpmostreed-transaction-types.c | 18 +++- src/daemon/rpmostreed-transaction-types.h | 2 +- src/libpriv/rpmostree-core.c | 114 ++++++++++++++++++-- src/libpriv/rpmostree-core.h | 3 + tests/vmcheck/test-basic.sh | 9 +- 15 files changed, 173 insertions(+), 43 deletions(-) diff --git a/man/rpm-ostree.xml b/man/rpm-ostree.xml index 06a424ffff..c756e0a142 100644 --- a/man/rpm-ostree.xml +++ b/man/rpm-ostree.xml @@ -182,6 +182,12 @@ Boston, MA 02111-1307, USA. inspect the RPM diff, but do not actually create a new deployment. + + + to perform the operation + without trying to download the target tree from the remote + nor the latest packages. + @@ -206,6 +212,11 @@ Boston, MA 02111-1307, USA. exit after printing the transaction rather than downloading the packages and creating a new deployment. + + + to perform the operation + without trying to download the latest packages. + @@ -265,6 +276,12 @@ Boston, MA 02111-1307, USA. to pick a remote name. + + to perform the rebase without + trying to download the target tree from the remote nor the + latest packages. + + @@ -350,6 +367,12 @@ Boston, MA 02111-1307, USA. available, without downloading it or performing a package-level diff. + + + to perform the upgrade without + trying to download the latest tree from the remote nor the + latest packages. + diff --git a/src/app/rpmostree-builtin-deploy.c b/src/app/rpmostree-builtin-deploy.c index 0b4fe13132..b54025aa94 100644 --- a/src/app/rpmostree-builtin-deploy.c +++ b/src/app/rpmostree-builtin-deploy.c @@ -40,7 +40,7 @@ static GOptionEntry option_entries[] = { * A --preview option would work for both commands if we wanted to * deprecate --check-diff. */ { "preview", 0, 0, G_OPTION_ARG_NONE, &opt_preview, "Just preview package differences", NULL }, - { "cache-only", 'C', 0, G_OPTION_ARG_NONE, &opt_cache_only, "Do not update repo metadata cache", NULL }, + { "cache-only", 'C', 0, G_OPTION_ARG_NONE, &opt_cache_only, "Do not download latest ostree and rpmmd data", NULL }, { NULL } }; diff --git a/src/app/rpmostree-builtin-rebase.c b/src/app/rpmostree-builtin-rebase.c index 9c71bb7e41..8096d75c04 100644 --- a/src/app/rpmostree-builtin-rebase.c +++ b/src/app/rpmostree-builtin-rebase.c @@ -43,7 +43,7 @@ static GOptionEntry option_entries[] = { { "remote", 'm', 0, G_OPTION_ARG_STRING, &opt_remote, "Rebase to current branch name using REMOTE; may also be combined with --branch", "REMOTE" }, { "reboot", 'r', 0, G_OPTION_ARG_NONE, &opt_reboot, "Initiate a reboot after rebase is finished", NULL }, { "skip-purge", 0, 0, G_OPTION_ARG_NONE, &opt_skip_purge, "Keep previous refspec after rebase", NULL }, - { "cache-only", 'C', 0, G_OPTION_ARG_NONE, &opt_cache_only, "Do not update repo metadata cache", NULL }, + { "cache-only", 'C', 0, G_OPTION_ARG_NONE, &opt_cache_only, "Do not download latest ostree and rpmmd data", NULL }, { NULL } }; diff --git a/src/app/rpmostree-builtin-upgrade.c b/src/app/rpmostree-builtin-upgrade.c index 1352c41781..29113e2951 100644 --- a/src/app/rpmostree-builtin-upgrade.c +++ b/src/app/rpmostree-builtin-upgrade.c @@ -47,7 +47,7 @@ static GOptionEntry option_entries[] = { { "check-diff", 0, G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_NONE, &opt_preview, "Check for upgrades and print package diff only", NULL }, { "preview", 0, 0, G_OPTION_ARG_NONE, &opt_preview, "Just preview package differences", NULL }, { "check", 0, 0, G_OPTION_ARG_NONE, &opt_check, "Just check if an upgrade is available", NULL }, - { "cache-only", 'C', 0, G_OPTION_ARG_NONE, &opt_cache_only, "Do not update repo metadata cache", NULL }, + { "cache-only", 'C', 0, G_OPTION_ARG_NONE, &opt_cache_only, "Do not download latest ostree and rpmmd data", NULL }, { "upgrade-unchanged-exit-77", 0, 0, G_OPTION_ARG_NONE, &opt_upgrade_unchanged_exit_77, "If no upgrade is available, exit 77", NULL }, { NULL } }; diff --git a/src/app/rpmostree-dbus-helpers.c b/src/app/rpmostree-dbus-helpers.c index c2548e8780..495f1a484b 100644 --- a/src/app/rpmostree-dbus-helpers.c +++ b/src/app/rpmostree-dbus-helpers.c @@ -1075,7 +1075,7 @@ rpmostree_get_options_variant (gboolean reboot, g_variant_dict_init (&dict, NULL); g_variant_dict_insert (&dict, "reboot", "b", reboot); g_variant_dict_insert (&dict, "allow-downgrade", "b", allow_downgrade); - g_variant_dict_insert (&dict, "rpmmd-cache-only", "b", cache_only); + g_variant_dict_insert (&dict, "cache-only", "b", cache_only); g_variant_dict_insert (&dict, "skip-purge", "b", skip_purge); g_variant_dict_insert (&dict, "no-pull-base", "b", no_pull_base); g_variant_dict_insert (&dict, "dry-run", "b", dry_run); diff --git a/src/app/rpmostree-pkg-builtins.c b/src/app/rpmostree-pkg-builtins.c index 53e115a456..1ea7f9423d 100644 --- a/src/app/rpmostree-pkg-builtins.c +++ b/src/app/rpmostree-pkg-builtins.c @@ -50,7 +50,7 @@ static GOptionEntry uninstall_option_entry[] = { static GOptionEntry install_option_entry[] = { { "uninstall", 0, 0, G_OPTION_ARG_STRING_ARRAY, &opt_uninstall, "Uninstall a package", "PKG" }, - { "cache-only", 'C', 0, G_OPTION_ARG_NONE, &opt_cache_only, "Do not update repo metadata cache", NULL }, + { "cache-only", 'C', 0, G_OPTION_ARG_NONE, &opt_cache_only, "Do not download latest ostree and rpmmd data", NULL }, { NULL } }; diff --git a/src/daemon/org.projectatomic.rpmostree1.xml b/src/daemon/org.projectatomic.rpmostree1.xml index c06ccb8bd2..4a71cd7b7e 100644 --- a/src/daemon/org.projectatomic.rpmostree1.xml +++ b/src/daemon/org.projectatomic.rpmostree1.xml @@ -255,13 +255,14 @@ Do not pull a base layer from the remote. Not valid if either "set-refspec" or "set-revision" is specified. "dry-run" (type 'b') - Stop short of deploying the new tree. If - layering packages, the pkg diff is printed. + Stop short of deploying the new tree. If layering packages, + the pkg diff is printed but packages are not downloaded or + imported. "no-overrides" (type 'b') Remove all active overrides. Not valid if any override modifiers are specified. - "rpmmd-cache-only" (type 'b') - Do not update rpmmd repo metadata cache. + "cache-only" (type 'b') + Do not update rpmmd repo metadata cache or ostree refspec. --> diff --git a/src/daemon/rpmostree-sysroot-upgrader.c b/src/daemon/rpmostree-sysroot-upgrader.c index cdaeed8981..95407be416 100644 --- a/src/daemon/rpmostree-sysroot-upgrader.c +++ b/src/daemon/rpmostree-sysroot-upgrader.c @@ -382,11 +382,13 @@ rpmostree_sysroot_upgrader_pull_base (RpmOstreeSysrootUpgrader *self, const gboolean allow_older = (self->flags & RPMOSTREE_SYSROOT_UPGRADER_FLAGS_ALLOW_OLDER) > 0; + const gboolean synthetic = + (self->flags & RPMOSTREE_SYSROOT_UPGRADER_FLAGS_SYNTHETIC_PULL) > 0; const char *override_commit = rpmostree_origin_get_override_commit (self->origin); g_assert (self->origin_merge_deployment); - if (origin_remote) + if (origin_remote && !synthetic) { g_autoptr(GVariantBuilder) optbuilder = g_variant_builder_new (G_VARIANT_TYPE ("a{sv}")); if (dir_to_pull && *dir_to_pull) @@ -428,8 +430,8 @@ rpmostree_sysroot_upgrader_pull_base (RpmOstreeSysrootUpgrader *self, gboolean changed = !g_str_equal (new_base_rev, self->base_revision); if (changed) { - /* check timestamps here too in case the commit was already pulled (or the - * refspec is local) */ + /* check timestamps here too in case the commit was already pulled, or the pull was + * synthetic, or the refspec is local */ if (!allow_older) { if (!ostree_sysroot_upgrader_check_timestamps (self->repo, self->base_revision, @@ -785,6 +787,10 @@ prepare_context_for_assembly (RpmOstreeSysrootUpgrader *self, return FALSE; rpmostree_context_set_sepolicy (self->ctx, sepolicy); + + if (self->flags & RPMOSTREE_SYSROOT_UPGRADER_FLAGS_PKGCACHE_ONLY) + rpmostree_context_set_pkgcache_only (self->ctx, TRUE); + return TRUE; } @@ -814,12 +820,6 @@ prep_local_assembly (RpmOstreeSysrootUpgrader *self, cancellable, error)) return FALSE; - if (self->flags & RPMOSTREE_SYSROOT_UPGRADER_FLAGS_RPMMD_CACHE_ONLY) - { - DnfContext *hifctx = rpmostree_context_get_hif (self->ctx); - dnf_context_set_cache_age (hifctx, G_MAXUINT); - } - g_autoptr(OstreeRepo) pkgcache_repo = NULL; if (!rpmostree_get_pkgcache_repo (self->repo, &pkgcache_repo, cancellable, error)) return FALSE; diff --git a/src/daemon/rpmostree-sysroot-upgrader.h b/src/daemon/rpmostree-sysroot-upgrader.h index 47308068e1..ad2c149cbf 100644 --- a/src/daemon/rpmostree-sysroot-upgrader.h +++ b/src/daemon/rpmostree-sysroot-upgrader.h @@ -41,7 +41,8 @@ G_DEFINE_AUTOPTR_CLEANUP_FUNC (RpmOstreeSysrootUpgrader, g_object_unref) * @RPMOSTREE_SYSROOT_UPGRADER_FLAGS_IGNORE_UNCONFIGURED: Do not error if the origin has an unconfigured-state key * @RPMOSTREE_SYSROOT_UPGRADER_FLAGS_ALLOW_OLDER: Do not error if the new deployment was composed earlier than the current deployment * @RPMOSTREE_SYSROOT_UPGRADER_FLAGS_DRY_RUN: Don't deploy new base. If layering packages, only print the transaction - * @RPMOSTREE_SYSROOT_UPGRADER_FLAGS_DRY_RUN: Don't update rpmmd cache. + * @RPMOSTREE_SYSROOT_UPGRADER_FLAGS_PKGCACHE_ONLY: Don't try to update cached packages. + * @RPMOSTREE_SYSROOT_UPGRADER_FLAGS_SYNTHETIC_PULL: Don't actually pull, just resolve ref and timestamp check * * Flags controlling operation of an #RpmOstreeSysrootUpgrader. */ @@ -50,7 +51,8 @@ typedef enum { RPMOSTREE_SYSROOT_UPGRADER_FLAGS_IGNORE_UNCONFIGURED = (1 << 1), RPMOSTREE_SYSROOT_UPGRADER_FLAGS_ALLOW_OLDER = (1 << 2), RPMOSTREE_SYSROOT_UPGRADER_FLAGS_DRY_RUN = (1 << 3), - RPMOSTREE_SYSROOT_UPGRADER_FLAGS_RPMMD_CACHE_ONLY = (1 << 4) + RPMOSTREE_SYSROOT_UPGRADER_FLAGS_PKGCACHE_ONLY = (1 << 4), + RPMOSTREE_SYSROOT_UPGRADER_FLAGS_SYNTHETIC_PULL = (1 << 5), } RpmOstreeSysrootUpgraderFlags; /* _NONE means we're doing pure ostree, no client-side computation. diff --git a/src/daemon/rpmostreed-os.c b/src/daemon/rpmostreed-os.c index 187e9193f7..95829f2543 100644 --- a/src/daemon/rpmostreed-os.c +++ b/src/daemon/rpmostreed-os.c @@ -546,8 +546,8 @@ deploy_flags_from_options (GVariant *options, ret |= RPMOSTREE_TRANSACTION_DEPLOY_FLAG_DRY_RUN; if (vardict_lookup_bool (&dict, "no-overrides", FALSE)) ret |= RPMOSTREE_TRANSACTION_DEPLOY_FLAG_NO_OVERRIDES; - if (vardict_lookup_bool (&dict, "rpmmd-cache-only", FALSE)) - ret |= RPMOSTREE_TRANSACTION_DEPLOY_FLAG_RPMMD_CACHE_ONLY; + if (vardict_lookup_bool (&dict, "cache-only", FALSE)) + ret |= RPMOSTREE_TRANSACTION_DEPLOY_FLAG_CACHE_ONLY; return ret; } diff --git a/src/daemon/rpmostreed-transaction-types.c b/src/daemon/rpmostreed-transaction-types.c index 18f1d8b0c6..d07d4cd19f 100644 --- a/src/daemon/rpmostreed-transaction-types.c +++ b/src/daemon/rpmostreed-transaction-types.c @@ -558,11 +558,20 @@ deploy_transaction_execute (RpmostreedTransaction *transaction, upgrader_flags |= RPMOSTREE_SYSROOT_UPGRADER_FLAGS_ALLOW_OLDER; if (self->flags & RPMOSTREE_TRANSACTION_DEPLOY_FLAG_DRY_RUN) upgrader_flags |= RPMOSTREE_SYSROOT_UPGRADER_FLAGS_DRY_RUN; - if (self->flags & RPMOSTREE_TRANSACTION_DEPLOY_FLAG_RPMMD_CACHE_ONLY) - upgrader_flags |= RPMOSTREE_SYSROOT_UPGRADER_FLAGS_RPMMD_CACHE_ONLY; const gboolean no_overrides = ((self->flags & RPMOSTREE_TRANSACTION_DEPLOY_FLAG_NO_OVERRIDES) > 0); + if (self->flags & RPMOSTREE_TRANSACTION_DEPLOY_FLAG_CACHE_ONLY) + { + /* practically, we could unite those two into a single flag, though it's nice to be + * able to keep them separate as well */ + + /* don't pull, just resolve ref locally and timestamp check */ + upgrader_flags |= RPMOSTREE_SYSROOT_UPGRADER_FLAGS_SYNTHETIC_PULL; + /* turn on rpmmd cache only in the upgrader */ + upgrader_flags |= RPMOSTREE_SYSROOT_UPGRADER_FLAGS_PKGCACHE_ONLY; + } + /* this should have been checked already */ if (no_overrides) { @@ -803,7 +812,10 @@ deploy_transaction_execute (RpmostreedTransaction *transaction, rpmostree_sysroot_upgrader_set_origin (upgrader, origin); /* Mainly for the `install` and `override` commands */ - if (!(self->flags & RPMOSTREE_TRANSACTION_DEPLOY_FLAG_NO_PULL_BASE)) + const gboolean no_pull_base = + ((self->flags & RPMOSTREE_TRANSACTION_DEPLOY_FLAG_NO_PULL_BASE) > 0); + + if (!no_pull_base) { gboolean base_changed; diff --git a/src/daemon/rpmostreed-transaction-types.h b/src/daemon/rpmostreed-transaction-types.h index 66be27aa86..8608217176 100644 --- a/src/daemon/rpmostreed-transaction-types.h +++ b/src/daemon/rpmostreed-transaction-types.h @@ -54,7 +54,7 @@ typedef enum { RPMOSTREE_TRANSACTION_DEPLOY_FLAG_NO_PULL_BASE = (1 << 4), RPMOSTREE_TRANSACTION_DEPLOY_FLAG_DRY_RUN = (1 << 5), RPMOSTREE_TRANSACTION_DEPLOY_FLAG_NO_OVERRIDES = (1 << 6), - RPMOSTREE_TRANSACTION_DEPLOY_FLAG_RPMMD_CACHE_ONLY = (1 << 7), + RPMOSTREE_TRANSACTION_DEPLOY_FLAG_CACHE_ONLY = (1 << 7), } RpmOstreeTransactionDeployFlags; diff --git a/src/libpriv/rpmostree-core.c b/src/libpriv/rpmostree-core.c index a1d3c20029..651f3282ba 100644 --- a/src/libpriv/rpmostree-core.c +++ b/src/libpriv/rpmostree-core.c @@ -243,6 +243,7 @@ struct _RpmOstreeContext { RpmOstreeTreespec *spec; gboolean empty; + gboolean pkgcache_only; DnfContext *hifctx; OstreeRepo *ostreerepo; OstreeRepo *pkgcache_repo; @@ -403,6 +404,15 @@ rpmostree_context_ensure_tmpdir (RpmOstreeContext *self, return TRUE; } +void +rpmostree_context_set_pkgcache_only (RpmOstreeContext *self, + gboolean pkgcache_only) +{ + /* if called, must always be before setup() */ + g_assert (!self->spec); + self->pkgcache_only = pkgcache_only; +} + /* Pick up repos dir and passwd from @cfg_deployment. */ void rpmostree_context_configure_from_deployment (RpmOstreeContext *self, @@ -600,13 +610,22 @@ rpmostree_context_setup (RpmOstreeContext *self, if (!dnf_context_setup (self->hifctx, cancellable, error)) return FALSE; - /* NB: missing "repos" --> let hif figure it out for itself */ - if (g_variant_dict_lookup (self->spec->dict, "repos", "^a&s", &enabled_repos)) - if (!context_repos_enable_only (self, (const char *const*)enabled_repos, error)) - return FALSE; + /* disable all repos in pkgcache-only mode, otherwise obey "repos" key */ + if (self->pkgcache_only) + { + if (!context_repos_enable_only (self, NULL, error)) + return FALSE; + } + else + { + /* NB: missing "repos" --> let hif figure it out for itself */ + if (g_variant_dict_lookup (self->spec->dict, "repos", "^a&s", &enabled_repos)) + if (!context_repos_enable_only (self, (const char *const*)enabled_repos, error)) + return FALSE; + } g_autoptr(GPtrArray) repos = get_enabled_rpmmd_repos (self->hifctx, DNF_REPO_ENABLED_PACKAGES); - if (repos->len == 0) + if (repos->len == 0 && !self->pkgcache_only) { /* To be nice, let's only make this fatal if "packages" is empty (e.g. if * we're only installing local RPMs. Missing deps will cause the regular @@ -616,6 +635,7 @@ rpmostree_context_setup (RpmOstreeContext *self, g_strv_length (pkgs) > 0) return glnx_throw (error, "No enabled repositories"); } + /* Ensure that each repo that's enabled is marked as required; this should be * the default, but we make sure. This is a bit of a messy topic, but for * rpm-ostree we're being more strict about requiring repos. @@ -807,8 +827,7 @@ rpmostree_find_cache_branch_by_nevra (OstreeRepo *pkgcache, g_autoptr(GHashTable) refs = NULL; if (!ostree_repo_list_refs_ext (pkgcache, "rpmostree/pkg", &refs, - OSTREE_REPO_LIST_REFS_EXT_NONE, cancellable, - error)) + OSTREE_REPO_LIST_REFS_EXT_NONE, cancellable, error)) return FALSE; GLNX_HASH_TABLE_FOREACH (refs, const char*, ref) @@ -872,8 +891,7 @@ checkout_pkg_metadata_by_nevra (RpmOstreeContext *self, &header, cancellable, error)) return FALSE; - return checkout_pkg_metadata (self, nevra, header, - cancellable, error); + return checkout_pkg_metadata (self, nevra, header, cancellable, error); } /* Fetches decomposed NEVRA information from pkgcache for a given nevra string. Requires the @@ -921,7 +939,21 @@ rpmostree_context_download_metadata (RpmOstreeContext *self, { g_assert (!self->empty); - g_autoptr(GPtrArray) rpmmd_repos = get_enabled_rpmmd_repos (self->hifctx, DNF_REPO_ENABLED_PACKAGES); + g_autoptr(GPtrArray) rpmmd_repos = + get_enabled_rpmmd_repos (self->hifctx, DNF_REPO_ENABLED_PACKAGES); + + if (self->pkgcache_only) + { + g_assert_cmpint (rpmmd_repos->len, ==, 0); + + /* this is essentially a no-op */ + g_autoptr(DnfState) hifstate = dnf_state_new (); + if (!dnf_context_setup_sack (self->hifctx, hifstate, error)) + return FALSE; + + /* Note early return; no repos to fetch. */ + return TRUE; + } g_autoptr(GString) enabled_repos = g_string_new ("Enabled rpm-md repositories:"); for (guint i = 0; i < rpmmd_repos->len; i++) @@ -1507,7 +1539,8 @@ install_pkg_from_cache (RpmOstreeContext *self, /* This is the great lie: we make libdnf et al. think that they're dealing with a full * RPM, all while crossing our fingers that they don't try to look past the header. * Ideally, it would be best if libdnf could learn to treat the pkgcache repo as another - * DnfRepo. */ + * DnfRepo. We could do this all in-memory though it doesn't seem like libsolv has an + * appropriate API for this. */ g_autofree char *rpm = g_strdup_printf ("%s/metarpm/%s.rpm", self->tmpdir.path, nevra); DnfPackage *pkg = dnf_sack_add_cmdline_package (dnf_context_get_sack (self->hifctx), rpm); if (!pkg) @@ -1517,6 +1550,49 @@ install_pkg_from_cache (RpmOstreeContext *self, return TRUE; } +/* This is a hacky way to bridge the gap between libdnf and our pkgcache. We extract the + * metarpm for every RPM in our cache and present that as the cmdline repo to libdnf. But we + * do still want all the niceties of the libdnf stack, e.g. HyGoal, libsolv depsolv, etc... + */ +static gboolean +add_remaining_pkgcache_pkgs (RpmOstreeContext *self, + GHashTable *already_added, + GCancellable *cancellable, + GError **error) +{ + g_assert (self->pkgcache_repo); + g_assert (self->pkgcache_only); + + DnfSack *sack = dnf_context_get_sack (self->hifctx); + g_assert (sack); + + g_autoptr(GHashTable) refs = NULL; + if (!ostree_repo_list_refs_ext (self->pkgcache_repo, "rpmostree/pkg", &refs, + OSTREE_REPO_LIST_REFS_EXT_NONE, cancellable, error)) + return FALSE; + + GLNX_HASH_TABLE_FOREACH (refs, const char*, ref) + { + g_autofree char *nevra = rpmostree_cache_branch_to_nevra (ref); + if (g_hash_table_contains (already_added, nevra)) + continue; + + g_autoptr(GVariant) header = NULL; + if (!get_header_variant (self->pkgcache_repo, ref, &header, cancellable, error)) + return FALSE; + + if (!checkout_pkg_metadata (self, nevra, header, cancellable, error)) + return FALSE; + + g_autofree char *rpm = g_strdup_printf ("%s/metarpm/%s.rpm", self->tmpdir.path, nevra); + DnfPackage *pkg = dnf_sack_add_cmdline_package (sack, rpm); + if (!pkg) + return glnx_throw (error, "Failed to add local pkg %s to sack", nevra); + } + + return TRUE; +} + /* Check for/download new rpm-md, then depsolve */ gboolean rpmostree_context_prepare (RpmOstreeContext *self, @@ -1562,6 +1638,9 @@ rpmostree_context_prepare (RpmOstreeContext *self, g_ptr_array_add (removed_pkgnames, (gpointer)pkgname); } + /* track cached pkgs already added to the sack so far */ + g_autoptr(GHashTable) already_added = g_hash_table_new (g_str_hash, g_str_equal); + /* Handle packages to replace */ g_autoptr(GPtrArray) replaced_nevras = g_ptr_array_new (); for (char **it = cached_replace_pkgs; it && *it; it++) @@ -1575,6 +1654,7 @@ rpmostree_context_prepare (RpmOstreeContext *self, return FALSE; g_ptr_array_add (replaced_nevras, (gpointer)nevra); + g_hash_table_add (already_added, (gpointer)nevra); } /* For each new local package, tell libdnf to add it to the goal */ @@ -1587,6 +1667,18 @@ rpmostree_context_prepare (RpmOstreeContext *self, if (!install_pkg_from_cache (self, nevra, sha256, cancellable, error)) return FALSE; + + g_hash_table_add (already_added, (gpointer)nevra); + } + + /* If we're in cache-only mode, add all the remaining pkgs now. We do this *after* the + * replace & local pkgs since they already handle a subset of the cached pkgs and have + * SHA256 checks. But we do it *before* dnf_context_install() since those subjects are not + * directly linked to a cached pkg, so we need to teach libdnf about them beforehand. */ + if (self->pkgcache_only) + { + if (!add_remaining_pkgcache_pkgs (self, already_added, cancellable, error)) + return FALSE; } /* Loop over each named package, and tell libdnf to add it to the goal */ diff --git a/src/libpriv/rpmostree-core.h b/src/libpriv/rpmostree-core.h index cfe3790271..e9e382b3bf 100644 --- a/src/libpriv/rpmostree-core.h +++ b/src/libpriv/rpmostree-core.h @@ -44,6 +44,9 @@ RpmOstreeContext *rpmostree_context_new_tree (int basedir_dfd, GCancellable *cancellable, GError **error); +void rpmostree_context_set_pkgcache_only (RpmOstreeContext *self, + gboolean pkgcache_only); + DnfContext * rpmostree_context_get_hif (RpmOstreeContext *self); RpmOstreeTreespec *rpmostree_treespec_new_from_keyfile (GKeyFile *keyfile, GError **error); diff --git a/tests/vmcheck/test-basic.sh b/tests/vmcheck/test-basic.sh index 638bc38f0f..4ae27d1a0f 100755 --- a/tests/vmcheck/test-basic.sh +++ b/tests/vmcheck/test-basic.sh @@ -173,15 +173,12 @@ vm_build_rpm_repo_mode skip refresh-md-old-pkg vm_rpmostree refresh-md vm_build_rpm_repo_mode skip refresh-md-new-pkg vm_rpmostree refresh-md # shouldn't do anything since it hasn't expired yet -if ! vm_rpmostree install -C refresh-md-old-pkg --dry-run; then - assert_not_reached "failed to dry-run install old pkg from cached rpmmd" -fi -if vm_rpmostree install -C refresh-md-new-pkg --dry-run; then +if vm_rpmostree install refresh-md-new-pkg --dry-run; then assert_not_reached "successfully dry-run installed new pkg from cached rpmmd?" fi vm_rpmostree refresh-md -f -if ! vm_rpmostree install -C refresh-md-new-pkg --dry-run; then +if ! vm_rpmostree install refresh-md-new-pkg --dry-run; then assert_not_reached "failed to dry-run install new pkg from cached rpmmd?" fi vm_stop_httpd vmcheck -echo "ok refresh-md and --cache-only" +echo "ok refresh-md"