]> git.apps.os.sepia.ceph.com Git - ceph.git/commitdiff
containers/make-manifest.py: Add --promote
authorDan Mick <dan.mick@redhat.com>
Sat, 16 Nov 2024 00:21:26 +0000 (16:21 -0800)
committerDan Mick <dan.mick@redhat.com>
Thu, 5 Dec 2024 08:10:54 +0000 (00:10 -0800)
The promote function finds the latest image on the prerelease repo,
finds all its tags, and copies them all to the release repo.

Signed-off-by: Dan Mick <dan.mick@redhat.com>
(cherry picked from commit 4f050d7811fa6ec347c3414d5749e0f86480d7f4)

container/make-manifest-list.py

index 371e65a13298dae65febbdca2e6db9e5135b9bb6..b12598e0b6a06a868c98e5f4e443a1fd9fd78ea1 100755 (executable)
@@ -1,11 +1,32 @@
 #!/usr/bin/python3
 #
+# in default mode:
 # make a combined "manifest-list" container out of two arch-specific containers
 # searches for latest tags on HOST/{AMD,ARM}64_REPO, makes sure they refer
 # to the same Ceph SHA1, and creates a manifest-list ("fat") image on
-# MANIFEST_HOST/MANIFEST_REPO with the 'standard' set of tags.
+# MANIFEST_HOST/MANIFEST_REPO with the 'standard' set of tags:
+# v<major>
+# v<major>.<minor>
+# v<major>.<minor>.<micro>
+# v<major>.<minor>.<micro>-<YYYYMMDD>
 #
-# uses scratch local manifest LOCALMANIFEST, will be destroyed if present
+# uses scratch local manifest LOCALMANIFEST, defined here; will be destroyed if present
+#
+# in promote mode (by adding the --promote argument):
+# instead of building the manifest-list container, copy it
+# (and all of its tags) from the prerelease repo to the release repo
+#
+# Assumes valid logins to the necessary hosts/repos with permission to write images
+#
+# Environment variables to set:
+# ARCH_SPECIFIC_HOST (default 'quay.ceph.io'): host of prerelease repos
+# AMD64_REPO (default 'ceph/prerelease-amd64') prerelease amd64 repo
+# ARM64_REPO (default 'ceph/prerelease-arm64') prerelease arm64 repo
+# MANIFEST_HOST (default 'quay.ceph.io') prerelease manifest-list host
+# MANIFEST_REPO (default 'ceph/prerelease') prerelease manifest-list repo
+# RELEASE_MANIFEST_HOST (default 'quay.io') release host
+# RELEASE_MANIFEST_REPO (default 'ceph/ceph') release repo
+
 
 import argparse
 from datetime import datetime
@@ -16,16 +37,6 @@ import re
 import subprocess
 import sys
 
-# optional env vars (will default if not set)
-
-OPTIONAL_VARS = (
-    'ARCH_SPECIFIC_HOST',
-    'AMD64_REPO',
-    'ARM64_REPO',
-    'MANIFEST_HOST',
-    'MANIFEST_REPO',
-)
-
 # Manifest image.  Will be destroyed if already present.
 LOCALMANIFEST = 'localhost/m'
 
@@ -66,10 +77,14 @@ def run_command_show_failure(args):
 
 
 @functools.lru_cache
+def get_tags(path):
+    cmdout = get_command_output(f'skopeo list-tags docker://{path}')
+    return json.loads(cmdout)['Tags']
+
+
 def get_latest_tag(path):
     try:
-        cmdout = get_command_output(f'skopeo list-tags docker://{path}')
-        latest_tag = json.loads(cmdout)['Tags'][-1]
+        latest_tag = get_tags(path)[-1]
     except IndexError:
         return None
     return latest_tag
@@ -90,19 +105,36 @@ def get_sha1(info):
     return labels.get('CEPH_SHA1', None)
 
 
+@functools.lru_cache
+def get_all_matching_digest_tags(path, tag):
+
+    matching_tags = list()
+    digest = get_image_inspect(f'{path}:{tag}')['Digest']
+
+    for t in get_tags(path):
+        this_digest = get_image_inspect(f'{path}:{t}')['Digest']
+        if this_digest == digest:
+            matching_tags.append(t)
+
+    return matching_tags
+
+
 def parse_args():
     ap = argparse.ArgumentParser()
-    ap.add_argument('-n', '--dry-run', action='store_true', help='do all local manipulations but do not push final containers to MANIFEST_HOST')
+    ap.add_argument('-n', '--dry-run', action='store_true', help='do all local manipulations but do not push final containers to MANIFEST_HOST, or in --promote, calculate but do not copy images to release host')
+    ap.add_argument('-P', '--promote', action='store_true', help='promote newest prerelease manifest container to released (move from MANIFEST_HOST to RELEASE_MANIFEST_HOST')
     args = ap.parse_args()
     return args
 
-def main():
-    args = parse_args()
+def build_prerelease(sysargs):
+    global args
+
     arch_specific_host = os.environ.get('ARCH_SPECIFIC_HOST', 'quay.ceph.io')
     amd64_repo = os.environ.get('AMD64_REPO', 'ceph/prerelease-amd64')
     arm64_repo = os.environ.get('ARM64_REPO', 'ceph/prerelease-arm64')
-    manifest_host = os.environ.get('MANIFEST_HOST', arch_specific_host)
+    manifest_host = os.environ.get('MANIFEST_HOST', 'quay.ceph.io')
     manifest_repo = os.environ.get('MANIFEST_REPO', 'ceph/prerelease')
+
     dump_vars(
         ('arch_specific_host',
          'amd64_repo',
@@ -111,7 +143,6 @@ def main():
          'manifest_repo',
          ),
         locals())
-
     repopaths = (
         f'{arch_specific_host}/{amd64_repo}',
         f'{arch_specific_host}/{arm64_repo}',
@@ -166,12 +197,56 @@ def main():
             f'v{major}.{minor}.{micro}',
             f'v{major}.{minor}.{micro}-{datetime.today().strftime("%Y%m%d")}',
         ):
-        if args.dry_run:
-            print(f'skipping podman manifest push localhost/m {base}:{t}')
+        if sysargs.dry_run:
+            print(f'skipping podman manifest push {LOCALMANIFEST} {base}:{t}')
         else:
             run_command_show_failure(
               f'podman manifest push localhost/m {base}:{t}')
 
+def promote(sysargs):
+    manifest_host = os.environ.get('MANIFEST_HOST', 'quay.ceph.io')
+    manifest_repo = os.environ.get('MANIFEST_REPO', 'ceph/prerelease')
+    release_manifest_host = os.environ.get('RELEASE_MANIFEST_HOST', 'quay.io')
+    release_manifest_repo = os.environ.get('RELEASE_MANIFEST_REPO', 'ceph/ceph')
+    dump_vars(
+        ('manifest_host',
+         'manifest_repo',
+         'release_manifest_host',
+         'release_manifest_repo',
+         ),
+        locals())
+
+    manifest_path = f'{manifest_host}/{manifest_repo}'
+    release_path = f'{release_manifest_host}/{release_manifest_repo}'
+    latest_tag = get_latest_tag(manifest_path)
+    all_tags = get_all_matching_digest_tags(manifest_path, latest_tag)
+
+    copypaths = list()
+    for t in all_tags:
+        from_path = f'{manifest_path}:{t}'
+        to_path = f'{release_path}:{t}'
+        copypaths.append((from_path, to_path))
+
+    if sysargs.dry_run:
+        for f, t in copypaths:
+            print(f'dry-run: Would copy: {f} -> {t}')
+        return(0)
+
+    for f, t in copypaths:
+        print(f'Will copy: {f} -> {t}')
+
+    for f, t in copypaths:
+        run_command_show_failure(f'skopeo copy --multi-arch=all docker://{f} docker://{t}')
+
+
+def main():
+    args = parse_args()
+
+    if args.promote:
+        promote(args)
+    else:
+        build_prerelease(args)
+
 
 if (__name__ == '__main__'):
     sys.exit(main())