Add script to re-tag plugin

pull/20/head
Jack O'Sullivan 3 years ago
parent ef789c0f4b
commit 4a4110649d

1
.gitignore vendored

@ -4,3 +4,4 @@
/multiarch/
.vscode/
__pycache__/

@ -0,0 +1,81 @@
import os
import dxf
MTYPE_PLUGIN_CONFIG = 'application/vnd.docker.plugin.v1+json'
MTYPE_LAYER = 'application/vnd.docker.image.rootfs.diff.tar.gzip'
MTYPE_MANIFEST = 'application/vnd.docker.distribution.manifest.v2+json'
MTYPE_MANIFEST_LIST = 'application/vnd.docker.distribution.manifest.list.v2+json'
class Platform:
def __init__(self, s):
self.buildx = s
split = s.split('/')
if len(split) < 2:
raise Exception('Invalid platform format')
self.dirname = '_'.join(split)
self.os = split[0]
self.architecture = split[1]
self.variant = None
if len(split) > 3:
raise Exception('Invalid platform format')
elif len(split) == 3:
self.variant = split[2]
elif self.architecture == 'arm64':
# Weird exception? (seen in alpine images)
self.variant = 'v8'
@property
def manifest(self):
d = {
'os': self.os,
'architecture': self.architecture,
}
if self.variant is not None:
d['variant'] = self.variant
return d
def tag(self, t):
if self.variant is not None:
return f'{t}-{self.os}-{self.architecture}-{self.variant}'
return f'{t}-{self.os}-{self.architecture}'
def __str__(self):
return f'Platform(os={self.os}, architecture={self.architecture}, variant={self.variant})'
def __repr__(self):
return str(self)
def dxf_auth(reg: dxf.DXF, res):
reg.authenticate(username=os.getenv('REGISTRY_USERNAME'), password=os.getenv('REGISTRY_PASSWORD'), response=res)
class DXF(dxf.DXF):
def set_manifest(self, alias, manifest_json, mime=MTYPE_MANIFEST):
"""
Give a name (alias) to a manifest.
:param alias: Alias name
:type alias: str
:param manifest_json: A V2 Schema 2 manifest JSON string
:type digests: list
"""
self._request('put',
'manifests/' + alias,
data=manifest_json,
headers={'Content-Type': mime})
def push_manifest(self, manifest_dict, ref=None, mime=MTYPE_MANIFEST):
mf = json.dumps(manifest_dict, sort_keys=True).encode('utf-8')
size = len(mf)
digest = dxf.hash_bytes(mf)
if ref is None:
ref = digest
self.set_manifest(ref, mf, mime=mime)
return size, digest

@ -8,87 +8,9 @@ import gzip
import tarfile
import concurrent.futures
import requests
from docker_image import reference
import dxf
MTYPE_PLUGIN_CONFIG = 'application/vnd.docker.plugin.v1+json'
MTYPE_LAYER = 'application/vnd.docker.image.rootfs.diff.tar.gzip'
MTYPE_MANIFEST = 'application/vnd.docker.distribution.manifest.v2+json'
MTYPE_MANIFEST_LIST = 'application/vnd.docker.distribution.manifest.list.v2+json'
class Platform:
def __init__(self, s):
self.buildx = s
split = s.split('/')
if len(split) < 2:
raise Exception('Invalid platform format')
self.dirname = '_'.join(split)
self.os = split[0]
self.architecture = split[1]
self.variant = None
if len(split) > 3:
raise Exception('Invalid platform format')
elif len(split) == 3:
self.variant = split[2]
elif self.architecture == 'arm64':
# Weird exception? (seen in alpine images)
self.variant = 'v8'
@property
def manifest(self):
d = {
'os': self.os,
'architecture': self.architecture,
}
if self.variant is not None:
d['variant'] = self.variant
return d
@property
def tag(self):
if self.variant is not None:
return f'{self.os}-{self.architecture}-{self.variant}'
return f'{self.os}-{self.architecture}'
def __str__(self):
return f'Platform(os={self.os}, architecture={self.architecture}, variant={self.variant})'
def __repr__(self):
return str(self)
def dxf_auth(reg: dxf.DXF, res):
reg.authenticate(username=os.getenv('REGISTRY_USERNAME'), password=os.getenv('REGISTRY_PASSWORD'), response=res)
class DXF(dxf.DXF):
def set_manifest(self, alias, manifest_json, mime=MTYPE_MANIFEST):
"""
Give a name (alias) to a manifest.
:param alias: Alias name
:type alias: str
:param manifest_json: A V2 Schema 2 manifest JSON string
:type digests: list
"""
self._request('put',
'manifests/' + alias,
data=manifest_json,
headers={'Content-Type': mime})
def push_manifest(self, manifest_dict, ref=None, mime=MTYPE_MANIFEST):
mf = json.dumps(manifest_dict, sort_keys=True).encode('utf-8')
size = len(mf)
digest = dxf.hash_bytes(mf)
if ref is None:
ref = digest
self.set_manifest(ref, mf, mime=mime)
return size, digest
from common import *
def tar_filter(p: Platform):
def f(info: tarfile.TarInfo):
@ -120,7 +42,6 @@ def main():
tag = ref['tag']
reg = DXF(hostname, repo, auth=dxf_auth)
#print(reg.list_aliases())
print(f'Pushing config file `{args.config}`')
config_size = os.path.getsize(args.config)
@ -151,7 +72,7 @@ def main():
layer_digest = reg.push_blob(data=f, digest=h, check_exists=True)
print(f'Pushed {p.buildx} layer as {layer_digest}')
platform_tag = f'{tag}-{p.tag}'
platform_tag = p.tag(tag)
print(f'Pushing {p.buildx} manifest with tag {platform_tag}')
size, digest = reg.push_manifest({
'schemaVersion': 2,

@ -0,0 +1,38 @@
#!/usr/bin/env python3
import argparse
from docker_image import reference
from common import *
def main():
parser = argparse.ArgumentParser(description='Re-tag an existing multiarch plugin')
parser.add_argument('image', help='existing image (registry/image:tag)')
parser.add_argument('tag', help='new tag')
parser.add_argument('-p', '--platforms', default='linux/amd64', help='buildx platforms')
args = parser.parse_args()
platforms = [Platform(p) for p in args.platforms.split(',')]
ref = reference.Reference.parse(args.image)
hostname, repo = ref.split_hostname()
without_tag = ref['name']
old_tag = ref['tag']
new_tag = args.tag
reg = DXF(hostname, repo, auth=dxf_auth)
for p in platforms:
mf = reg.get_manifest(p.tag(old_tag))
print(f'Re-tagging {without_tag}:{p.tag(old_tag)} as {without_tag}:{p.tag(new_tag)}')
reg.set_manifest(p.tag(new_tag), mf, mime=MTYPE_MANIFEST)
print(f'Re-tagging {args.image} as {without_tag}:{new_tag}')
mf = reg.get_manifest(old_tag)
reg.set_manifest(new_tag, mf, mime=MTYPE_MANIFEST_LIST)
if __name__ == '__main__':
main()
Loading…
Cancel
Save