diff --git a/src/app/rpmostree-builtin-rebase.c b/src/app/rpmostree-builtin-rebase.c index 2115e27c..e8deec50 100644 --- a/src/app/rpmostree-builtin-rebase.c +++ b/src/app/rpmostree-builtin-rebase.c @@ -36,6 +36,8 @@ static gboolean opt_reboot; static gboolean opt_skip_purge; static char * opt_branch; static char * opt_remote; +static char * opt_custom_origin_url; +static char * opt_custom_origin_description; static gboolean opt_cache_only; static gboolean opt_download_only; static gboolean opt_experimental; @@ -48,6 +50,8 @@ static GOptionEntry option_entries[] = { { "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 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 }, + { "custom-origin-description", 0, 0, G_OPTION_ARG_STRING, &opt_custom_origin_description, "Human-readable description of custom origin", NULL }, + { "custom-origin-url", 0, 0, G_OPTION_ARG_STRING, &opt_custom_origin_url, "Machine-readable description of custom origin", NULL }, { "experimental", 0, 0, G_OPTION_ARG_NONE, &opt_experimental, "Enable experimental features", NULL }, { NULL } }; @@ -138,6 +142,14 @@ rpmostree_builtin_rebase (int argc, g_variant_dict_insert (&dict, "cache-only", "b", opt_cache_only); g_variant_dict_insert (&dict, "download-only", "b", opt_download_only); g_variant_dict_insert (&dict, "skip-purge", "b", opt_skip_purge); + if (opt_custom_origin_url) + { + if (!opt_custom_origin_description) + return glnx_throw (error, "--custom-origin-description must be supplied with --custom-origin-url"); + g_variant_dict_insert (&dict, "custom-origin", "(ss)", + opt_custom_origin_url, + opt_custom_origin_description); + } g_autoptr(GVariant) options = g_variant_ref_sink (g_variant_dict_end (&dict)); /* Use newer D-Bus API only if we have to. */ diff --git a/src/app/rpmostree-builtin-status.c b/src/app/rpmostree-builtin-status.c index 40bf5d9d..19f6d776 100644 --- a/src/app/rpmostree-builtin-status.c +++ b/src/app/rpmostree-builtin-status.c @@ -480,6 +480,8 @@ print_one_deployment (RPMOSTreeSysroot *sysroot_proxy, g_print ("%s ", is_booted ? libsd_special_glyph (BLACK_CIRCLE) : " "); RpmOstreeRefspecType refspectype = RPMOSTREE_REFSPEC_TYPE_OSTREE; + const char *custom_origin_url = NULL; + const char *custom_origin_description = NULL; if (origin_refspec) { const char *refspec_data; @@ -489,6 +491,22 @@ print_one_deployment (RPMOSTreeSysroot *sysroot_proxy, switch (refspectype) { case RPMOSTREE_REFSPEC_TYPE_CHECKSUM: + { + g_variant_dict_lookup (dict, "custom-origin", "(&s&s)", + &custom_origin_url, + &custom_origin_description); + /* Canonicalize the empty string to NULL */ + if (custom_origin_url && !*custom_origin_url) + custom_origin_url = NULL; + if (custom_origin_url) + { + g_assert (custom_origin_description && *custom_origin_description); + g_print ("%s", custom_origin_url); + } + else + g_print ("%s", canonrefspec); + } + break; case RPMOSTREE_REFSPEC_TYPE_OSTREE: { g_print ("%s", canonrefspec); @@ -526,6 +544,9 @@ print_one_deployment (RPMOSTreeSysroot *sysroot_proxy, g_print ("%s", checksum); g_print ("\n"); + if (custom_origin_description) + rpmostree_print_kv ("CustomOrigin", max_key_len, custom_origin_description); + const char *remote_not_found = NULL; g_variant_dict_lookup (dict, "remote-error", "s", &remote_not_found); if (remote_not_found) diff --git a/src/daemon/org.projectatomic.rpmostree1.xml b/src/daemon/org.projectatomic.rpmostree1.xml index 385ef73c..f0f2c7e6 100644 --- a/src/daemon/org.projectatomic.rpmostree1.xml +++ b/src/daemon/org.projectatomic.rpmostree1.xml @@ -291,6 +291,7 @@ "override-reset-packages" (type 'as') "override-replace-packages" (type 'as') "override-replace-local-packages" (type 'ah') + "custom-origin" (type '(ss)') Available options: "reboot" (type 'b') diff --git a/src/daemon/rpmostreed-deployment-utils.c b/src/daemon/rpmostreed-deployment-utils.c index 1648cbfb..71942192 100644 --- a/src/daemon/rpmostreed-deployment-utils.c +++ b/src/daemon/rpmostreed-deployment-utils.c @@ -304,7 +304,16 @@ rpmostreed_deployment_generate_variant (OstreeSysroot *sysroot, switch (refspec_type) { case RPMOSTREE_REFSPEC_TYPE_CHECKSUM: - /* Nothing to do here */ + { + g_autofree char *custom_origin_url = NULL; + g_autofree char *custom_origin_description = NULL; + rpmostree_origin_get_custom_description (origin, &custom_origin_url, + &custom_origin_description); + if (custom_origin_url) + g_variant_dict_insert (&dict, "custom-origin", "(ss)", + custom_origin_url, + custom_origin_description); + } break; case RPMOSTREE_REFSPEC_TYPE_OSTREE: { diff --git a/src/daemon/rpmostreed-transaction-types.c b/src/daemon/rpmostreed-transaction-types.c index 4442db8a..26566a46 100644 --- a/src/daemon/rpmostreed-transaction-types.c +++ b/src/daemon/rpmostreed-transaction-types.c @@ -42,7 +42,8 @@ vardict_lookup_bool (GVariantDict *dict, gboolean dfault); static gboolean -change_origin_refspec (OstreeSysroot *sysroot, +change_origin_refspec (GVariantDict *options, + OstreeSysroot *sysroot, RpmOstreeOrigin *origin, const gchar *src_refspec, GCancellable *cancellable, @@ -101,9 +102,35 @@ change_origin_refspec (OstreeSysroot *sysroot, if (strcmp (current_refspec, new_refspec) == 0) return glnx_throw (error, "Old and new refs are equal: %s", new_refspec); - if (!rpmostree_origin_set_rebase (origin, new_refspec, error)) + /* Re-classify after canonicalization to ensure we handle TYPE_CHECKSUM */ + if (!rpmostree_refspec_classify (new_refspec, &refspectype, &refspecdata, error)) return FALSE; + if (refspectype == RPMOSTREE_REFSPEC_TYPE_CHECKSUM) + { + const char *custom_origin_url = NULL; + const char *custom_origin_description = NULL; + g_variant_dict_lookup (options, "custom-origin", "(&s&s)", + &custom_origin_url, + &custom_origin_description); + if (custom_origin_url && *custom_origin_url) + { + g_assert (custom_origin_description); + if (!*custom_origin_description) + return glnx_throw (error, "Invalid custom-origin"); + } + if (!rpmostree_origin_set_rebase_custom (origin, new_refspec, + custom_origin_url, + custom_origin_description, + error)) + return FALSE; + } + else + { + if (!rpmostree_origin_set_rebase (origin, new_refspec, error)) + return FALSE; + } + g_autofree gchar *current_remote = NULL; g_autofree gchar *current_branch = NULL; g_assert (ostree_parse_refspec (current_refspec, ¤t_remote, ¤t_branch, NULL)); @@ -305,7 +332,7 @@ package_diff_transaction_execute (RpmostreedTransaction *transaction, if (self->refspec != NULL) { - if (!change_origin_refspec (sysroot, origin, self->refspec, + if (!change_origin_refspec (NULL, sysroot, origin, self->refspec, cancellable, NULL, NULL, error)) return FALSE; } @@ -828,7 +855,7 @@ deploy_transaction_execute (RpmostreedTransaction *transaction, g_autofree gchar *old_refspec = NULL; if (self->refspec) { - if (!change_origin_refspec (sysroot, origin, self->refspec, cancellable, + if (!change_origin_refspec (self->options, sysroot, origin, self->refspec, cancellable, &old_refspec, &new_refspec, error)) return FALSE; } @@ -1304,7 +1331,16 @@ deploy_transaction_execute (RpmostreedTransaction *transaction, { if (refspec_type == RPMOSTREE_REFSPEC_TYPE_CHECKSUM && layering_type < RPMOSTREE_SYSROOT_UPGRADER_LAYERING_RPMMD_REPOS) - rpmostree_output_message ("Pinned to commit; no upgrade available"); + { + g_autofree char *custom_origin_url = NULL; + g_autofree char *custom_origin_description = NULL; + rpmostree_origin_get_custom_description (origin, &custom_origin_url, + &custom_origin_description); + if (custom_origin_description) + rpmostree_output_message ("Pinned to commit by custom origin: %s", custom_origin_description); + else + rpmostree_output_message ("Pinned to commit; no upgrade available"); + } else if (is_upgrade) rpmostree_output_message ("No upgrade available."); else diff --git a/src/libpriv/rpmostree-origin.c b/src/libpriv/rpmostree-origin.c index 453632b5..d604bfaf 100644 --- a/src/libpriv/rpmostree-origin.c +++ b/src/libpriv/rpmostree-origin.c @@ -265,6 +265,25 @@ rpmostree_origin_get_rojig_description (RpmOstreeOrigin *origin) return g_variant_builder_end (builder); } +static char * +keyfile_get_nonempty_string (GKeyFile *kf, const char *section, const char *key) +{ + char *ret = g_key_file_get_string (kf, section, key, NULL); + if (ret && !*ret) + g_clear_pointer (&ret, g_free); + return ret; +} + +void +rpmostree_origin_get_custom_description (RpmOstreeOrigin *origin, + char **custom_type, + char **custom_description) +{ + *custom_type = keyfile_get_nonempty_string (origin->kf, "origin", "custom-url"); + if (*custom_type) + *custom_description = keyfile_get_nonempty_string (origin->kf, "origin", "custom-description"); +} + GHashTable * rpmostree_origin_get_packages (RpmOstreeOrigin *origin) { @@ -469,11 +488,20 @@ rpmostree_origin_set_rojig_description (RpmOstreeOrigin *origin, } gboolean -rpmostree_origin_set_rebase (RpmOstreeOrigin *origin, - const char *new_refspec, - GError **error) +rpmostree_origin_set_rebase_custom (RpmOstreeOrigin *origin, + const char *new_refspec, + const char *custom_origin_url, + const char *custom_origin_description, + GError **error) { - /* We don't want to carry any commit overrides or version pinning during a + /* Require non-empty strings */ + if (custom_origin_url) + { + g_return_val_if_fail (*custom_origin_url, FALSE); + g_return_val_if_fail (custom_origin_description && *custom_origin_description, FALSE); + } + + /* We don't want to carry any commit overrides or version pinning during a * rebase by default. */ rpmostree_origin_set_override_commit (origin, NULL, NULL); @@ -497,13 +525,29 @@ rpmostree_origin_set_rebase (RpmOstreeOrigin *origin, g_key_file_has_key (origin->kf, "origin", "baserefspec", NULL) ? "baserefspec" : "refspec"; g_key_file_set_string (origin->kf, "origin", refspec_key, origin->cached_refspec); + if (!custom_origin_url) + { + g_key_file_remove_key (origin->kf, "origin", "custom-url", NULL); + g_key_file_remove_key (origin->kf, "origin", "custom-description", NULL); + } + else + { + /* Custom origins have to be checksums */ + g_assert_cmpint (origin->refspec_type, ==, RPMOSTREE_REFSPEC_TYPE_CHECKSUM); + g_key_file_set_string (origin->kf, "origin", "custom-url", custom_origin_url); + if (custom_origin_description) + g_key_file_set_string (origin->kf, "origin", "custom-description", custom_origin_description); + } } break; case RPMOSTREE_REFSPEC_TYPE_ROJIG: { + g_assert (!custom_origin_url); origin->cached_refspec = g_strdup (refspecdata); g_key_file_remove_key (origin->kf, "origin", "refspec", NULL); g_key_file_remove_key (origin->kf, "origin", "baserefspec", NULL); + g_key_file_remove_key (origin->kf, "origin", "custom-url", NULL); + g_key_file_remove_key (origin->kf, "origin", "custom-description", NULL); /* Peeled */ g_key_file_set_string (origin->kf, "origin", "rojig", origin->cached_refspec); } @@ -513,6 +557,14 @@ rpmostree_origin_set_rebase (RpmOstreeOrigin *origin, return TRUE; } +gboolean +rpmostree_origin_set_rebase (RpmOstreeOrigin *origin, + const char *new_refspec, + GError **error) +{ + return rpmostree_origin_set_rebase_custom (origin, new_refspec, NULL, NULL, error); +} + /* Like g_key_file_set_string(), but remove the key if @value is NULL */ static void set_or_unset_str (GKeyFile *kf, diff --git a/src/libpriv/rpmostree-origin.h b/src/libpriv/rpmostree-origin.h index 90cebae3..bff31723 100644 --- a/src/libpriv/rpmostree-origin.h +++ b/src/libpriv/rpmostree-origin.h @@ -75,6 +75,11 @@ rpmostree_origin_get_rojig_version (RpmOstreeOrigin *origin); GVariant * rpmostree_origin_get_rojig_description (RpmOstreeOrigin *origin); +void +rpmostree_origin_get_custom_description (RpmOstreeOrigin *origin, + char **custom_type, + char **custom_description); + GHashTable * rpmostree_origin_get_packages (RpmOstreeOrigin *origin); @@ -136,6 +141,12 @@ gboolean rpmostree_origin_set_rebase (RpmOstreeOrigin *origin, const char *new_refspec, GError **error); +gboolean +rpmostree_origin_set_rebase_custom (RpmOstreeOrigin *origin, + const char *new_refspec, + const char *custom_origin_url, + const char *custom_origin_description, + GError **error); gboolean rpmostree_origin_add_packages (RpmOstreeOrigin *origin, diff --git a/tests/vmcheck/test-misc-2.sh b/tests/vmcheck/test-misc-2.sh index 62990002..7ceef759 100755 --- a/tests/vmcheck/test-misc-2.sh +++ b/tests/vmcheck/test-misc-2.sh @@ -26,6 +26,24 @@ set -x # More miscellaneous tests +# Custom origin https://github.com/projectatomic/rpm-ostree/pull/1406 +booted_csum=$(vm_get_booted_csum) +oscontainer_source="oscontainer://quay.io/exampleos@sha256:98ea6e4f216f2fb4b69fff9b3a44842c38686ca685f3f55dc48c5d3fb1107be4" +if vm_rpmostree rebase --skip-purge --custom-origin-url "${oscontainer_source}" \ + :${booted_csum} 2>err.txt; then + fatal "rebased without description" +fi +assert_file_has_content_literal err.txt '--custom-origin-description must be supplied' +vm_rpmostree rebase --skip-purge --custom-origin-description "'Updated via pivot'" \ + --custom-origin-url "${oscontainer_source}" \ + :${booted_csum} +vm_rpmostree status > status.txt +assert_file_has_content_literal status.txt 'CustomOrigin: Updated via pivot' +assert_file_has_content_literal status.txt "${oscontainer_source}" +vm_rpmostree upgrade >out.txt +assert_file_has_content_literal out.txt 'Pinned to commit by custom origin: Updated via pivot' +vm_rpmostree cleanup -p + # Add metadata string containing EnfOfLife attribtue META_ENDOFLIFE_MESSAGE="this is a test for metadata message" commit=$(vm_cmd ostree commit -b vmcheck \