ostree/summary: Generate an ostree-metadata ref when updating summary

This is the new way of publishing repository metadata, rather than as
additional-metadata in the summary file. The use of an ostree-metadata
ref means that the metadata from multiple upstream collections is not
conflated when doing P2P mirroring of many repositories.

The new ref is only generated if the repository has a collection ID set.
The old summary file continues to be generated for backwards
compatibility (and because it continues to be the canonical ref →
checksum map for the repository).

The new code is only used if configured with --enable-experimental-api.

Includes unit tests.

Signed-off-by: Philip Withnall <withnall@endlessm.com>

Closes: #1158
Approved by: cgwalters
This commit is contained in:
Philip Withnall 2017-09-11 14:42:52 +01:00 committed by Atomic Bot
parent 16aa4d728a
commit feeb3548f4
3 changed files with 120 additions and 1 deletions

View File

@ -83,7 +83,16 @@ Boston, MA 02111-1307, USA.
your organisation or repository using a dot prefix. The values
must be in GVariant text format. For example,
<command>exampleos.end-of-life "@t 1445385600"</command>.
</para></listitem>
</para>
<!-- FIXME: Uncomment this when collection ID support becomes non-experimental.
<para>If the repository has a collection ID configured, the
<filename>ostree-metadata</filename> branch for that collection ID
will also be updated with a new commit containing the given metadata,
which will be signed if the summary file is signed.</para>
-->
</listitem>
</varlistentry>
<varlistentry>

View File

@ -106,6 +106,97 @@ ostree_builtin_summary (int argc, char **argv, GCancellable *cancellable, GError
return FALSE;
}
#ifdef OSTREE_ENABLE_EXPERIMENTAL_API
const char *collection_id = ostree_repo_get_collection_id (repo);
#else /* if !OSTREE_ENABLE_EXPERIMENTAL_API */
const char *collection_id = NULL;
#endif /* OSTREE_ENABLE_EXPERIMENTAL_API */
/* Write out a new metadata commit for the repository. */
if (collection_id != NULL)
{
#ifdef OSTREE_ENABLE_EXPERIMENTAL_API
OstreeCollectionRef collection_ref = { (gchar *) collection_id, (gchar *) OSTREE_REPO_METADATA_REF };
g_autofree char *old_ostree_metadata_checksum = NULL;
g_autofree gchar *new_ostree_metadata_checksum = NULL;
g_autoptr(OstreeMutableTree) mtree = NULL;
g_autoptr(OstreeRepoFile) repo_file = NULL;
g_autoptr(GVariantDict) new_summary_commit_dict = NULL;
g_autoptr(GVariant) new_summary_commit = NULL;
if (!ostree_repo_resolve_rev (repo, OSTREE_REPO_METADATA_REF,
TRUE, &old_ostree_metadata_checksum, error))
return FALSE;
/* Add bindings to the metadata. */
new_summary_commit_dict = g_variant_dict_new (additional_metadata);
g_variant_dict_insert (new_summary_commit_dict, OSTREE_COMMIT_META_KEY_COLLECTION_BINDING,
"s", collection_ref.collection_id);
g_variant_dict_insert_value (new_summary_commit_dict, OSTREE_COMMIT_META_KEY_REF_BINDING,
g_variant_new_strv ((const gchar * const *) &collection_ref.ref_name, 1));
new_summary_commit = g_variant_dict_end (new_summary_commit_dict);
if (!ostree_repo_prepare_transaction (repo, NULL, cancellable, error))
return FALSE;
/* Set up an empty mtree. */
mtree = ostree_mutable_tree_new ();
glnx_unref_object GFileInfo *fi = g_file_info_new ();
g_file_info_set_attribute_uint32 (fi, "unix::uid", 0);
g_file_info_set_attribute_uint32 (fi, "unix::gid", 0);
g_file_info_set_attribute_uint32 (fi, "unix::mode", (0755 | S_IFDIR));
g_autofree guchar *csum_raw = NULL;
g_autofree char *csum = NULL;
g_autoptr(GVariant) dirmeta = ostree_create_directory_metadata (fi, NULL /* xattrs */);
if (!ostree_repo_write_metadata (repo, OSTREE_OBJECT_TYPE_DIR_META, NULL,
dirmeta, &csum_raw, cancellable, error))
return FALSE;
csum = ostree_checksum_from_bytes (csum_raw);
ostree_mutable_tree_set_metadata_checksum (mtree, csum);
if (!ostree_repo_write_mtree (repo, mtree, (GFile **) &repo_file, NULL, error))
return FALSE;
if (!ostree_repo_write_commit (repo, old_ostree_metadata_checksum,
NULL /* subject */, NULL /* body */,
new_summary_commit, repo_file, &new_ostree_metadata_checksum,
NULL, error))
return FALSE;
if (opt_key_ids != NULL)
{
for (const char * const *iter = (const char * const *) opt_key_ids;
iter != NULL && *iter != NULL; iter++)
{
const char *key_id = *iter;
if (!ostree_repo_sign_commit (repo,
new_ostree_metadata_checksum,
key_id,
opt_gpg_homedir,
cancellable,
error))
return FALSE;
}
}
ostree_repo_transaction_set_collection_ref (repo, &collection_ref,
new_ostree_metadata_checksum);
if (!ostree_repo_commit_transaction (repo, NULL, cancellable, error))
return FALSE;
#else /* if !OSTREE_ENABLE_EXPERIMENTAL_API */
g_assert_not_reached ();
return FALSE;
#endif /* OSTREE_ENABLE_EXPERIMENTAL_API */
}
/* Regenerate and sign the conventional summary file. */
if (!ostree_repo_regenerate_summary (repo, additional_metadata, cancellable, error))
return FALSE;

View File

@ -91,4 +91,23 @@ ${CMD_PREFIX} ostree --repo=repo summary --update --add-metadata=map='@a{sv} {}'
${CMD_PREFIX} ostree --repo=repo summary --view > summary
assert_file_has_content summary "^map: {}$"
# Check the ostree-metadata ref has also been created with the same content and appropriate bindings.
${CMD_PREFIX} ostree --repo=repo refs --collections > refs
assert_file_has_content refs "^(org.example.Collection1, ostree-metadata)$"
${CMD_PREFIX} ostree --repo=repo show ostree-metadata --raw > metadata
assert_file_has_content metadata "'map': <@a{sv} {}>"
assert_file_has_content metadata "'ostree.ref-binding': <\['ostree-metadata'\]>"
assert_file_has_content metadata "'ostree.collection-binding': <'org.example.Collection1'>"
# There should be 5 commits in the ostree-metadata branch, since weve updated the summary 5 times.
${CMD_PREFIX} ostree --repo=repo log ostree-metadata | grep 'commit ' | wc -l > commit-count
assert_file_has_content commit-count "^5$"
# The ostree-metadata commits should not contain any files
${CMD_PREFIX} ostree --repo=repo ls ostree-metadata > files
assert_file_has_content files " /$"
cat files | wc -l > files-count
assert_file_has_content files-count "^1$"
echo "ok 2 update summary with collections"