rebase: Add support for "custom origin" descriptions

We're looking to embed an ostree commit inside a container image,
to make it easier to transport around with other images.

Conceptually here the host system is tracking a container (just
like for rojig we're tracking an RPM).  This is the first step
towards making that support nicer; tooling can do
`rebase --custom-origin-url oscontainer://quay.io/exampleos@sha256:...`
and have that show up in `rpm-ostree status`.

There are two values, one intended to be machine readable (like
the `ostree://` and `rojig://` and one for humans which we
display when an admin types `rpm-ostree upgrade`.

This builds on prior work in
27bd7b97bb from #1396 .

Closes: #1406
Approved by: jlebon
This commit is contained in:
Colin Walters 2018-06-08 15:39:56 -04:00 committed by Atomic Bot
parent 95de58c29d
commit 096004426c
8 changed files with 170 additions and 10 deletions

View File

@ -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. */

View File

@ -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)

View File

@ -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')

View File

@ -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:
{

View File

@ -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, &current_remote, &current_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

View File

@ -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,

View File

@ -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,

View File

@ -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 \