use packaging.version to compare version and list possible updates for images
parent
5cef4df383
commit
d1872892fd
|
|
@ -6,4 +6,4 @@ services:
|
|||
volumes:
|
||||
- ./docker-compose.py:/docker-compose.py
|
||||
- ./sample:/services
|
||||
command: bash -c 'python3 /docker-compose.py /services/*'
|
||||
command: bash -c 'python3 /docker_compose.py /services/*'
|
||||
|
|
@ -109,7 +109,7 @@ class Collector:
|
|||
def start(files, ignores):
|
||||
collector = Collector()
|
||||
for f in files:
|
||||
if any([i in f for i in ignores]):
|
||||
if ignores and any([i in f for i in ignores]):
|
||||
log.warn(f"skip {f} due to ignore rule")
|
||||
continue
|
||||
if not f.endswith(COMPOSE_FILE):
|
||||
|
|
@ -124,14 +124,15 @@ def start(files, ignores):
|
|||
#print(json.dumps(collector.get_images_sources(), indent=1))
|
||||
return collector.get_images_sources()
|
||||
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
parser = argparse.ArgumentParser(description="Docker-compose parser")
|
||||
def args_setup(description):
|
||||
parser = argparse.ArgumentParser(description=description)
|
||||
parser.add_argument("compose_files", nargs="+")
|
||||
parser.add_argument("--output", "-o")
|
||||
parser.add_argument("--ignore", "-i", nargs="+")
|
||||
|
||||
parser.add_argument("--ignore", "-i", nargs="+", default=False)
|
||||
return parser
|
||||
|
||||
if __name__ == "__main__":
|
||||
parser = args_setup("Docker-compose parser")
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
|
|
@ -1,43 +1,80 @@
|
|||
import requests
|
||||
from datetime import datetime
|
||||
import argparse
|
||||
import json
|
||||
import logging
|
||||
from datetime import datetime
|
||||
|
||||
from packaging import version
|
||||
import requests
|
||||
|
||||
|
||||
log = logging
|
||||
|
||||
TAGS = "https://registry.hub.docker.com/v2/repositories/{image}/tags/"
|
||||
LIB_PREFIX = "library/"
|
||||
|
||||
TAG_STORE = {}
|
||||
|
||||
def get_tags(image):
|
||||
if "/" not in image:
|
||||
image = LIB_PREFIX + image
|
||||
url = TAGS.format(image=image)
|
||||
def api_call(url):
|
||||
result = requests.get(url)
|
||||
if not result.ok:
|
||||
log.error(result, result.url)
|
||||
return {}
|
||||
data = result.json()
|
||||
tags = {}
|
||||
for entry in data["results"]:
|
||||
tags[entry["name"]] = datetime.strptime(entry["last_updated"], "%Y-%m-%dT%H:%M:%S.%fZ")
|
||||
tags[entry["name"]] = datetime.strptime(entry["last_updated"], "%Y-%m-%dT%H:%M:%S.%fZ") if entry["last_updated"] else "------"
|
||||
if data['next']:
|
||||
tags.update(api_call(data['next']))
|
||||
return tags
|
||||
|
||||
def get_tags(image):
|
||||
if "/" not in image:
|
||||
image = LIB_PREFIX + image
|
||||
if ":" in image:
|
||||
image = image.split(":")[0]
|
||||
url = TAGS.format(image=image)
|
||||
tags = api_call(url)
|
||||
if not len(tags):
|
||||
raise ValueError(f"Unknown image '{image}'")
|
||||
return tags
|
||||
|
||||
def replace(string, replacements):
|
||||
for k,v in replacements:
|
||||
string = string.replace(k,v)
|
||||
return string
|
||||
|
||||
|
||||
def compare(base, other, replacements=[("-","+"),]):
|
||||
base = replace(base, replacements)
|
||||
other = replace(other, replacements)
|
||||
v1 = version.parse(base)
|
||||
v2 = version.parse(other)
|
||||
result = v1 < v2
|
||||
log.debug(f"{v1} < {v2}: {result}")
|
||||
return result
|
||||
|
||||
def get_new_tags(image):
|
||||
if not ":" in image:
|
||||
print("using implicit latest, skip")
|
||||
log.warn("using implicit latest, skip")
|
||||
return
|
||||
image_name, current_tag = image.split(":")
|
||||
if not image_name in TAG_STORE:
|
||||
TAG_STORE[image_name] = get_tags(image_name)
|
||||
if current_tag in TAG_STORE[image_name]:
|
||||
first_update = TAG_STORE[image_name][current_tag]
|
||||
else:
|
||||
print("!!! FALLBACK!")
|
||||
first_update = TAG_STORE[image_name].entry_set()[0]
|
||||
print(first_update)
|
||||
#if current_tag in TAG_STORE[image_name]:
|
||||
# first_update = TAG_STORE[image_name][current_tag]
|
||||
#else:
|
||||
# print("!!! FALLBACK!")
|
||||
# first_update = list(TAG_STORE[image_name].values())[0]
|
||||
#print(first_update)
|
||||
new_tags = {}
|
||||
for tag in TAG_STORE[image_name]:
|
||||
print("("+str(tag)+")")
|
||||
update = TAG_STORE[image_name][tag]
|
||||
if update > first_update:
|
||||
log.debug("check("+str(tag)+")")
|
||||
if compare(current_tag, tag):
|
||||
log.debug("NEWER!!!")
|
||||
update = TAG_STORE[image_name][tag]
|
||||
new_tags[tag] = str(update)
|
||||
log.debug(tag)
|
||||
|
||||
return new_tags
|
||||
|
||||
if __name__=="__main__":
|
||||
|
|
@ -47,7 +84,7 @@ if __name__=="__main__":
|
|||
|
||||
args= parser.parse_args()
|
||||
for i in args.image:
|
||||
print(i)
|
||||
log.debug(i)
|
||||
if args.list:
|
||||
print(json.dumps({k:str(v) for k,v in get_tags(i).items()}, indent=1))
|
||||
else:
|
||||
|
|
|
|||
|
|
@ -0,0 +1,2 @@
|
|||
requests
|
||||
packaging
|
||||
|
|
@ -0,0 +1,35 @@
|
|||
import argparse
|
||||
import json
|
||||
|
||||
import docker_compose
|
||||
import image_tags
|
||||
|
||||
|
||||
|
||||
|
||||
def main(args):
|
||||
updates = {}
|
||||
images = docker_compose.start(args.compose_files, args.ignore)
|
||||
for image in images:
|
||||
for tag in images[image]:
|
||||
image_ref = f"{image}:{tag}"
|
||||
try:
|
||||
newer_tags = image_tags.get_new_tags(image_ref)
|
||||
except ValueError as e:
|
||||
newer_tags = e.args
|
||||
updates[image_ref] = {
|
||||
"updates": newer_tags,
|
||||
"usages": images[image][tag]
|
||||
}
|
||||
if args.output:
|
||||
with open(args.output, "w") as out:
|
||||
json.dump(updates, out, indent=1, sort_keys=True)
|
||||
else:
|
||||
print(json.dumps(updates, indent=1))
|
||||
|
||||
|
||||
if __name__=="__main__":
|
||||
parser = docker_compose.args_setup("Show updates for docker-compose style services")
|
||||
args = parser.parse_args()
|
||||
main(args)
|
||||
|
||||
Loading…
Reference in New Issue