summaryrefslogtreecommitdiffstats
path: root/container/make-manifest-list.py
diff options
context:
space:
mode:
Diffstat (limited to 'container/make-manifest-list.py')
-rwxr-xr-xcontainer/make-manifest-list.py164
1 files changed, 164 insertions, 0 deletions
diff --git a/container/make-manifest-list.py b/container/make-manifest-list.py
new file mode 100755
index 00000000000..010dcaed2b7
--- /dev/null
+++ b/container/make-manifest-list.py
@@ -0,0 +1,164 @@
+#!/usr/bin/python3
+#
+# 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.
+#
+# uses scratch local manifest LOCALMANIFEST, will be destroyed if present
+
+from datetime import datetime
+import functools
+import json
+import os
+import re
+import subprocess
+import sys
+
+# optional env vars (will default if not set)
+
+OPTIONAL_VARS = (
+ 'HOST',
+ 'AMD64_REPO',
+ 'ARM64_REPO',
+ 'MANIFEST_HOST',
+ 'MANIFEST_REPO',
+)
+
+# Manifest image. Will be destroyed if already present.
+LOCALMANIFEST = 'localhost/m'
+
+
+def dump_vars(names, vardict):
+ for name in names:
+ print(f'{name}: {vardict[name]}', file=sys.stderr)
+
+
+def run_command(args):
+ print(f'running {args}', file=sys.stderr)
+ if not isinstance(args, list):
+ args = args.split()
+ try:
+ result = subprocess.run(
+ args,
+ capture_output=True,
+ text=True,
+ check=True)
+ return True, result.stdout, result.stderr
+
+ except subprocess.CalledProcessError as e:
+ print(f"Command '{e.cmd}' returned {e.returncode}")
+ print("Error output:")
+ print(e.stderr)
+ return False, result.stdout, result.stderr
+
+
+def get_command_output(args):
+ success, stdout, stderr = run_command(args)
+ return (stdout if success else None)
+
+
+def run_command_show_failure(args):
+ success, stdout, stderr = run_command(args)
+ if not success:
+ print(f'{args} failed:', file=sys.stderr)
+ print(f'stdout:\n{stdout}')
+ print(f'stderr:\n{stderr}')
+ return success
+
+
+@functools.lru_cache
+def get_latest_tag(path):
+ latest_tag = json.loads(
+ get_command_output(f'skopeo list-tags docker://{path}')
+ )['Tags'][-1]
+ return latest_tag
+
+
+@functools.lru_cache
+def get_image_inspect(path):
+ info = json.loads(
+ get_command_output(f'skopeo inspect docker://{path}')
+ )
+ return info
+
+
+def get_sha1(info):
+ return info['Labels']['GIT_COMMIT']
+
+
+def main():
+ host = os.environ.get('HOST', 'quay.io')
+ amd64_repo = os.environ.get('AMD64_REPO', 'ceph/ceph-amd64')
+ arm64_repo = os.environ.get('ARM64_REPO', 'ceph/ceph-arm64')
+ manifest_host = os.environ.get('MANIFEST_HOST', host)
+ manifest_repo = os.environ.get('MANIFEST_REPO', 'ceph/ceph')
+ dump_vars(
+ ('host',
+ 'amd64_repo',
+ 'arm64_repo',
+ 'manifest_host',
+ 'manifest_repo',
+ ),
+ locals())
+
+ repopaths = (
+ f'{host}/{amd64_repo}',
+ f'{host}/{arm64_repo}',
+ )
+ tags = [get_latest_tag(p) for p in repopaths]
+ print(f'latest tags: amd64:{tags[0]} arm64:{tags[1]}')
+
+ # check that version of latest tag matches
+ version_re = \
+ r'v(?P<major>\d+)\.(?P<minor>\d+)\.(?P<micro>\d+)-(?P<date>\d+)'
+ versions = list()
+ for tag in tags:
+ mo = re.match(version_re, tag)
+ ver = f'{mo.group("major")}.{mo.group("minor")}.{mo.group("micro")}'
+ versions.append(ver)
+ if versions[0] != versions[1]:
+ print(
+ f'version mismatch: amd64:{versions[0]} arm64:{versions[1]}',
+ file=sys.stderr,
+ )
+ return(1)
+
+ major, minor, micro = mo.group(1), mo.group(2), mo.group(3)
+ print(f'Ceph version: {major}.{minor}.{micro}', file=sys.stderr)
+
+ # check that ceph sha1 of two arch images matches
+ paths_with_tags = [f'{p}:{t}' for (p, t) in zip(repopaths, tags)]
+ info = [get_image_inspect(p) for p in paths_with_tags]
+ sha1s = [get_sha1(i) for i in info]
+ if sha1s[0] != sha1s[1]:
+ print(
+ f'sha1 mismatch: amd64: {sha1s[0]} arm64: {sha1s[1]}',
+ file=sys.stderr,
+ )
+ builddate = [i['Created'] for i in info]
+ print(
+ f'Build dates: amd64: {builddate[0]} arm64: {builddate[1]}',
+ file=sys.stderr,
+ )
+ return(1)
+
+ # create manifest list image with the standard list of tags
+ # ignore failure on manifest rm
+ run_command(f'podman manifest rm localhost/m')
+ run_command_show_failure(f'podman manifest create localhost/m')
+ for p in paths_with_tags:
+ run_command_show_failure(f'podman manifest add m {p}')
+ base = f'{manifest_host}/{manifest_repo}'
+ for t in (
+ f'v{major}',
+ f'v{major}.{minor}',
+ f'v{major}.{minor}.{micro}',
+ f'v{major}.{minor}.{micro}-{datetime.today().strftime("%Y%m%d")}',
+ ):
+ run_command_show_failure(
+ f'podman manifest push localhost/m {base}:{t}')
+
+
+if (__name__ == '__main__'):
+ sys.exit(main())