Skip to content

Commit

Permalink
cmd-push-container-manifest: attempt to make more idempotent
Browse files Browse the repository at this point in the history
Check if the digests are already present in the remote
manifest and if you want to force or not a new manifest upload.

Fixes coreos#3150

Signed-off-by: Renata Ravanelli <[email protected]>
(cherry picked from commit bfbbe41)

jlebon: Had to modify new code to use `runcmd` from `cosalib.utils.`.
  • Loading branch information
ravanelli authored and dustymabe committed Dec 1, 2022
1 parent 03ba9e5 commit adea942
Showing 1 changed file with 39 additions and 4 deletions.
43 changes: 39 additions & 4 deletions src/cmd-push-container-manifest
Original file line number Diff line number Diff line change
Expand Up @@ -4,18 +4,24 @@
# arguments provided by the user.

import argparse
import json
import os
import sys
from cosalib.container_manifest import create_and_push_container_manifest
from cosalib.builds import Builds
from cosalib.meta import GenericBuildMeta
from cosalib.utils import runcmd
from cosalib.cmdlib import sha256sum_file

sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))


def main():
args = parse_args()
map_arch = {}
map_arch['arm64'] = 'aarch64'
map_arch['amd64'] = 'x86_64'

if args.authfile:
os.environ["REGISTRY_AUTH_FILE"] = args.authfile
if args.images:
Expand All @@ -37,6 +43,18 @@ def main():
# - Store the path to the container image in the container_images list
images = []
buildmetas = dict()
registry_digests = {}
upload = False
# Collect registry_digests of current manifest list in remote registry
inspect = skopeo_inspect(f'{args.repo}:{args.tags[0]}', args.authfile)
if inspect.returncode == 0:
manifests = json.loads(inspect.stdout)
for manifest in manifests['manifests']:
arch = manifest['platform']['architecture']
if arch in map_arch:
arch = map_arch[arch]
registry_digests[arch] = manifest['digest']

for arch in args.arches:
if arch not in build_arches:
print(f"Requested architecture {arch} is not in {args.build}")
Expand All @@ -48,6 +66,13 @@ def main():
if not buildmeta['images'][args.artifact]:
print(f"No artifact {args.artifact} in {args.build}/{arch}")
raise Exception

# Checks if the meta digest matches each arch digest in the remote.
# If it doesn't match (or doesn't exist), we need to upload.
if 'digest' in buildmetas[arch][args.metajsonname]:
meta_digest = buildmetas[arch][args.metajsonname]['digest']
if meta_digest != registry_digests.get(arch):
upload = True
ociarchive = os.path.join(builddir, buildmeta['images'][args.artifact]['path'])
ocisha256sum = buildmeta['images'][args.artifact]['sha256']
if not os.path.exists(ociarchive):
Expand All @@ -58,6 +83,10 @@ def main():
raise Exception
images.append(f"oci-archive:{ociarchive}")

if not upload and not args.force:
print("Remote already matches desired state; skipping push. Use --force to override.")
return

# Create/Upload the manifest list to the container registry
manifest_info = create_and_push_container_manifest(
args.repo, args.tags, images, args.v2s2)
Expand All @@ -68,10 +97,8 @@ def main():
assert len(manifest_info['manifests']) == len(buildmetas)
for manifest in manifest_info['manifests']:
arch = manifest['platform']['architecture']
if arch == 'arm64':
arch = 'aarch64'
elif arch == 'amd64':
arch = 'x86_64'
if arch in map_arch:
arch = map_arch[arch]
buildmetas[arch][args.metajsonname] = {
'image': args.repo,
'digest': manifest['digest']
Expand Down Expand Up @@ -107,6 +134,7 @@ Examples:
parser.add_argument("--authfile", help="A file to use for registry auth")
parser.add_argument('--v2s2', action='store_true',
help='Use old image manifest version 2 schema 2 format')
parser.add_argument("--force", help="Force manifest overwriting", action='store_true')

group = parser.add_mutually_exclusive_group(required=True)
group.add_argument("--image", dest='images', action='append', default=[],
Expand All @@ -126,5 +154,12 @@ Examples:
return parser.parse_args()


def skopeo_inspect(fqin, authfile):
args = ['skopeo', 'inspect', '--raw']
if authfile:
args += ['--authfile', authfile]
return runcmd((args + [f'docker://{fqin}']), capture_output=True, check=False)


if __name__ == '__main__':
sys.exit(main())

0 comments on commit adea942

Please sign in to comment.