use packaging.version to compare version and list possible updates for images
parent
5cef4df383
commit
d1872892fd
|
|
@ -6,4 +6,4 @@ services:
|
||||||
volumes:
|
volumes:
|
||||||
- ./docker-compose.py:/docker-compose.py
|
- ./docker-compose.py:/docker-compose.py
|
||||||
- ./sample:/services
|
- ./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):
|
def start(files, ignores):
|
||||||
collector = Collector()
|
collector = Collector()
|
||||||
for f in files:
|
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")
|
log.warn(f"skip {f} due to ignore rule")
|
||||||
continue
|
continue
|
||||||
if not f.endswith(COMPOSE_FILE):
|
if not f.endswith(COMPOSE_FILE):
|
||||||
|
|
@ -124,14 +124,15 @@ def start(files, ignores):
|
||||||
#print(json.dumps(collector.get_images_sources(), indent=1))
|
#print(json.dumps(collector.get_images_sources(), indent=1))
|
||||||
return collector.get_images_sources()
|
return collector.get_images_sources()
|
||||||
|
|
||||||
|
def args_setup(description):
|
||||||
|
parser = argparse.ArgumentParser(description=description)
|
||||||
if __name__ == "__main__":
|
|
||||||
parser = argparse.ArgumentParser(description="Docker-compose parser")
|
|
||||||
parser.add_argument("compose_files", nargs="+")
|
parser.add_argument("compose_files", nargs="+")
|
||||||
parser.add_argument("--output", "-o")
|
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()
|
args = parser.parse_args()
|
||||||
|
|
||||||
|
|
@ -1,43 +1,80 @@
|
||||||
import requests
|
|
||||||
from datetime import datetime
|
|
||||||
import argparse
|
import argparse
|
||||||
import json
|
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/"
|
TAGS = "https://registry.hub.docker.com/v2/repositories/{image}/tags/"
|
||||||
LIB_PREFIX = "library/"
|
LIB_PREFIX = "library/"
|
||||||
|
|
||||||
TAG_STORE = {}
|
TAG_STORE = {}
|
||||||
|
|
||||||
def get_tags(image):
|
def api_call(url):
|
||||||
if "/" not in image:
|
|
||||||
image = LIB_PREFIX + image
|
|
||||||
url = TAGS.format(image=image)
|
|
||||||
result = requests.get(url)
|
result = requests.get(url)
|
||||||
|
if not result.ok:
|
||||||
|
log.error(result, result.url)
|
||||||
|
return {}
|
||||||
data = result.json()
|
data = result.json()
|
||||||
tags = {}
|
tags = {}
|
||||||
for entry in data["results"]:
|
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
|
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):
|
def get_new_tags(image):
|
||||||
if not ":" in image:
|
if not ":" in image:
|
||||||
print("using implicit latest, skip")
|
log.warn("using implicit latest, skip")
|
||||||
return
|
return
|
||||||
image_name, current_tag = image.split(":")
|
image_name, current_tag = image.split(":")
|
||||||
if not image_name in TAG_STORE:
|
if not image_name in TAG_STORE:
|
||||||
TAG_STORE[image_name] = get_tags(image_name)
|
TAG_STORE[image_name] = get_tags(image_name)
|
||||||
if current_tag in TAG_STORE[image_name]:
|
#if current_tag in TAG_STORE[image_name]:
|
||||||
first_update = TAG_STORE[image_name][current_tag]
|
# first_update = TAG_STORE[image_name][current_tag]
|
||||||
else:
|
#else:
|
||||||
print("!!! FALLBACK!")
|
# print("!!! FALLBACK!")
|
||||||
first_update = TAG_STORE[image_name].entry_set()[0]
|
# first_update = list(TAG_STORE[image_name].values())[0]
|
||||||
print(first_update)
|
#print(first_update)
|
||||||
new_tags = {}
|
new_tags = {}
|
||||||
for tag in TAG_STORE[image_name]:
|
for tag in TAG_STORE[image_name]:
|
||||||
print("("+str(tag)+")")
|
log.debug("check("+str(tag)+")")
|
||||||
|
if compare(current_tag, tag):
|
||||||
|
log.debug("NEWER!!!")
|
||||||
update = TAG_STORE[image_name][tag]
|
update = TAG_STORE[image_name][tag]
|
||||||
if update > first_update:
|
|
||||||
new_tags[tag] = str(update)
|
new_tags[tag] = str(update)
|
||||||
|
log.debug(tag)
|
||||||
|
|
||||||
return new_tags
|
return new_tags
|
||||||
|
|
||||||
if __name__=="__main__":
|
if __name__=="__main__":
|
||||||
|
|
@ -47,7 +84,7 @@ if __name__=="__main__":
|
||||||
|
|
||||||
args= parser.parse_args()
|
args= parser.parse_args()
|
||||||
for i in args.image:
|
for i in args.image:
|
||||||
print(i)
|
log.debug(i)
|
||||||
if args.list:
|
if args.list:
|
||||||
print(json.dumps({k:str(v) for k,v in get_tags(i).items()}, indent=1))
|
print(json.dumps({k:str(v) for k,v in get_tags(i).items()}, indent=1))
|
||||||
else:
|
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