From b8a69920d4c3c281eea5cf7f73e244f41ed1ec66 Mon Sep 17 00:00:00 2001 From: Mike Sul Date: Wed, 28 May 2025 12:46:35 +0200 Subject: [PATCH 1/2] apps: Preserve app image digest Make `skopeo` preserve the original container image manifest so its hash matches the hash specified in its digest reference in an app compose project or in an image index. Signed-off-by: Mike Sul --- apps/target_apps_fetcher.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/apps/target_apps_fetcher.py b/apps/target_apps_fetcher.py index 9f633c37..d154ea70 100644 --- a/apps/target_apps_fetcher.py +++ b/apps/target_apps_fetcher.py @@ -183,8 +183,9 @@ def fetch_image(self, target_name: str, arch: str, image: str, dst_root_dir: str image_dir = os.path.join(dst_root_dir, uri.host, uri.name, uri.hash) os.makedirs(image_dir, exist_ok=True) subprocess.check_call(['skopeo', '--insecure-policy', '--override-arch', arch, 'copy', - '--retry-times', '3', '--format', 'v2s2', '--dest-shared-blob-dir', - self.blobs_dir(target_name), 'docker://' + image, 'oci:' + image_dir]) + '--preserve-digests', '--retry-times', '3', '--format', 'v2s2', + '--dest-shared-blob-dir', self.blobs_dir(target_name), + 'docker://' + image, 'oci:' + image_dir]) # Store the image manifest in the blob directory, as result it contains all blobs/nodes of # the app's merkle tree. It allows to check app integrity on devices with preloaded apps and From 26b74d15b3c8b581cf33074060e7626d5bdee8a5 Mon Sep 17 00:00:00 2001 From: Mike Sul Date: Wed, 28 May 2025 13:34:26 +0200 Subject: [PATCH 2/2] apps: Fetch app bundle index Fetch app bundle index if it is reference is found in an app manifest. It allows checking app bundle integrity during offline update. Signed-off-by: Mike Sul --- apps/target_apps_fetcher.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/apps/target_apps_fetcher.py b/apps/target_apps_fetcher.py index d154ea70..8b51af37 100644 --- a/apps/target_apps_fetcher.py +++ b/apps/target_apps_fetcher.py @@ -143,6 +143,14 @@ def _fetch_apps(self, target, apps_shortlist=None, force=False): with tarfile.open(fileobj=BIO(app_blob)) as t: t.extract('docker-compose.yml', app_dir) + if 'annotations' in manifest['layers'][0] and \ + 'org.foundries.app.bundle.index.digest' in manifest['layers'][0]['annotations']: + app_index_digest = manifest['layers'][0]['annotations']['org.foundries.app.bundle.index.digest'] + app_index_hash = app_index_digest[len('sha256:'):] + app_index = self._registry_client.pull_layer(uri, app_index_digest) + with open(os.path.join(blobs_dir, app_index_hash), 'wb') as f: + f.write(app_index) + # Download and store the layers' manifest that contains a list of all layers that app's images are based on. # It's needed for aklite to calculate an update size in an offline update case. for lm in manifest.get('manifests', []):