chore(): Finishing up POC features
* Adds the ability to filter * Adds the ability to sort by time * Adds the ability to return latest by date
This commit is contained in:
parent
502750732c
commit
48b811795a
6 changed files with 130 additions and 28 deletions
|
@ -1,15 +1,17 @@
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
|
|
||||||
|
|
||||||
class Serialize:
|
class Serialize:
|
||||||
def boolean(string: str) -> bool:
|
def boolean(string: str) -> bool:
|
||||||
if string == "true" or string == "True":
|
if string == "true" or string == "True":
|
||||||
return True
|
return True
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
||||||
def integer(string: str) -> int:
|
def integer(string: str) -> int:
|
||||||
return int(string)
|
return int(string)
|
||||||
|
|
||||||
|
|
||||||
def time(string: str) -> datetime:
|
def time(string: str) -> datetime:
|
||||||
return datetime.strptime(string, '%Y-%m-%dT%H:%M:%S.%f%z')
|
try:
|
||||||
|
return datetime.strptime(string, "%Y-%m-%dT%H:%M:%S.%f%z")
|
||||||
|
except TypeError:
|
||||||
|
return string
|
||||||
|
|
|
@ -1,10 +1,13 @@
|
||||||
|
import re
|
||||||
from typing import Self
|
from typing import Self
|
||||||
from src.common import Serialize
|
from src.common import Serialize
|
||||||
|
|
||||||
|
|
||||||
class Images:
|
class Images:
|
||||||
"""
|
"""
|
||||||
Images class object.
|
Images class object.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, images: list) -> None:
|
def __init__(self, images: list) -> None:
|
||||||
"""
|
"""
|
||||||
Initialize an images object.
|
Initialize an images object.
|
||||||
|
@ -12,6 +15,7 @@ class Images:
|
||||||
self.images = []
|
self.images = []
|
||||||
|
|
||||||
self.deserialize(images=images)
|
self.deserialize(images=images)
|
||||||
|
self.sort_by_time()
|
||||||
|
|
||||||
def deserialize(self, images: list) -> Self:
|
def deserialize(self, images: list) -> Self:
|
||||||
"""
|
"""
|
||||||
|
@ -30,11 +34,41 @@ class Images:
|
||||||
images.append(image.serialize())
|
images.append(image.serialize())
|
||||||
return images
|
return images
|
||||||
|
|
||||||
|
def filter_by_os(self, os="") -> Self:
|
||||||
|
images = []
|
||||||
|
for image in self.images:
|
||||||
|
if image.os == os:
|
||||||
|
images.append(image.serialize())
|
||||||
|
return Images(images)
|
||||||
|
|
||||||
|
def filter_by_os_version(self, os_version="") -> Self:
|
||||||
|
images = []
|
||||||
|
for image in self.images:
|
||||||
|
if image.os_version == os_version:
|
||||||
|
images.append(image.serialize())
|
||||||
|
return Images(images)
|
||||||
|
|
||||||
|
def filter_by_architecture(self, architecture="") -> Self:
|
||||||
|
images = []
|
||||||
|
for image in self.images:
|
||||||
|
if image.architecture == architecture:
|
||||||
|
images.append(image.serialize())
|
||||||
|
return Images(images)
|
||||||
|
|
||||||
|
def sort_by_time(self) -> Self:
|
||||||
|
self.images.sort(key=lambda image: image.last_pushed, reverse=True)
|
||||||
|
return self
|
||||||
|
|
||||||
|
def get_latest(self) -> Self:
|
||||||
|
self.sort_by_time()
|
||||||
|
return Images([self.serialize()[0]])
|
||||||
|
|
||||||
|
|
||||||
class Image:
|
class Image:
|
||||||
"""
|
"""
|
||||||
Image class object.
|
Image class object.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, image: dict) -> None:
|
def __init__(self, image: dict) -> None:
|
||||||
"""
|
"""
|
||||||
Initialize an image object.
|
Initialize an image object.
|
||||||
|
@ -42,7 +76,9 @@ class Image:
|
||||||
self.architecture = "arm"
|
self.architecture = "arm"
|
||||||
self.features = ""
|
self.features = ""
|
||||||
self.variant = "v7"
|
self.variant = "v7"
|
||||||
self.digest = "sha256:2627e55acb9ac183f1c94e6a44f869620d164bbb10d7c42b29df08513eb20c29"
|
self.digest = (
|
||||||
|
"sha256:2627e55acb9ac183f1c94e6a44f869620d164bbb10d7c42b29df08513eb20c29"
|
||||||
|
)
|
||||||
self.os = "linux"
|
self.os = "linux"
|
||||||
self.os_features = ""
|
self.os_features = ""
|
||||||
self.os_version = None
|
self.os_version = None
|
||||||
|
@ -57,17 +93,17 @@ class Image:
|
||||||
"""
|
"""
|
||||||
Method to deserialize image dictionary.
|
Method to deserialize image dictionary.
|
||||||
"""
|
"""
|
||||||
self.architecture = image.get('architecture', None)
|
self.architecture = image.get("architecture", None)
|
||||||
self.features = image.get('features', None)
|
self.features = image.get("features", None)
|
||||||
self.variant = image.get('variant', None)
|
self.variant = image.get("variant", None)
|
||||||
self.digest = image.get('digest', None)
|
self.digest = image.get("digest", None)
|
||||||
self.os = image.get('os', None)
|
self.os = image.get("os", None)
|
||||||
self.os_features = image.get('os_features', None)
|
self.os_features = image.get("os_features", None)
|
||||||
self.os_version = image.get('os_version', None)
|
self.os_version = image.get("os_version", None)
|
||||||
self.size = Serialize.integer(image.get('size', -1))
|
self.size = Serialize.integer(image.get("size", -1))
|
||||||
self.status = image.get('status', None)
|
self.status = image.get("status", None)
|
||||||
self.last_pulled = Serialize.time(image.get('last_pulled', None))
|
self.last_pulled = Serialize.time(image.get("last_pulled", None))
|
||||||
self.last_pushed = Serialize.time(image.get('last_pushed', None))
|
self.last_pushed = Serialize.time(image.get("last_pushed", None))
|
||||||
|
|
||||||
return self
|
return self
|
||||||
|
|
||||||
|
|
|
@ -8,8 +8,12 @@ password = None
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
reg = Registry(username=username, password=password)
|
reg = Registry(username=username, password=password)
|
||||||
registry = reg.populate_tags("traefik", namespace="rapidfort", url_params="page_size=25")
|
registry = reg.populate_tags(image="alpine", url_params="page_size=25")
|
||||||
print(registry.tags.serialize())
|
|
||||||
|
latest = registry.tags.filter_by_os(os="linux").filter_by_architecture(architecture="arm").get_latest().serialize()
|
||||||
|
digest = latest[0]['images'][0]['digest']
|
||||||
|
digest_algorithm, digest_hash = digest.split(":")
|
||||||
|
print(digest_hash)
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
main()
|
main()
|
||||||
|
|
|
@ -2,25 +2,29 @@ from typing import Self
|
||||||
from src.upstream import Upstream
|
from src.upstream import Upstream
|
||||||
from src.tags import Tags
|
from src.tags import Tags
|
||||||
|
|
||||||
|
|
||||||
class Registry(Upstream):
|
class Registry(Upstream):
|
||||||
"""
|
"""
|
||||||
Registry class
|
Registry class
|
||||||
|
|
||||||
Keeps track of the image tags pulled from Upstream
|
Keeps track of the image tags pulled from Upstream
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
url: str = "https://hub.docker.com",
|
url: str = "https://hub.docker.com",
|
||||||
username: str = None,
|
username: str = None,
|
||||||
password: str = None,
|
password: str = None,
|
||||||
) -> None:
|
) -> None:
|
||||||
"""
|
"""
|
||||||
Initialization method.
|
Initialization method.
|
||||||
"""
|
"""
|
||||||
self.tags = None
|
self.tags = None
|
||||||
Upstream.__init__(self, url=url, username=username, password=password)
|
Upstream.__init__(self, url=url, username=username, password=password)
|
||||||
|
|
||||||
def populate_tags(self, image: str, namespace: str = None, url_params: str = None) -> Self:
|
def populate_tags(
|
||||||
|
self, image: str, namespace: str = "library", url_params: str = None
|
||||||
|
) -> Self:
|
||||||
tags = self.get_tags(image=image, namespace=namespace, url_params=url_params)
|
tags = self.get_tags(image=image, namespace=namespace, url_params=url_params)
|
||||||
self.tags = Tags(tags)
|
self.tags = Tags(tags)
|
||||||
|
|
||||||
|
|
57
src/tags.py
57
src/tags.py
|
@ -1,11 +1,14 @@
|
||||||
from typing import Self
|
from typing import Self
|
||||||
|
from copy import deepcopy
|
||||||
from src.images import Images
|
from src.images import Images
|
||||||
from src.common import Serialize
|
from src.common import Serialize
|
||||||
|
|
||||||
|
|
||||||
class Tags:
|
class Tags:
|
||||||
"""
|
"""
|
||||||
Tags class object.
|
Tags class object.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, tags: list) -> None:
|
def __init__(self, tags: list) -> None:
|
||||||
"""
|
"""
|
||||||
Initialize a tag object.
|
Initialize a tag object.
|
||||||
|
@ -31,10 +34,43 @@ class Tags:
|
||||||
tags.append(tag.serialize())
|
tags.append(tag.serialize())
|
||||||
return tags
|
return tags
|
||||||
|
|
||||||
|
def filter_by_os(self, os="") -> Self:
|
||||||
|
# TODO: Abstract filter functions
|
||||||
|
tags = []
|
||||||
|
for tag in self.tags:
|
||||||
|
tags.append(tag.filter_by_os(os=os).serialize())
|
||||||
|
return Tags(tags)
|
||||||
|
|
||||||
|
def filter_by_os_version(self, os_version="") -> Self:
|
||||||
|
tags = []
|
||||||
|
for tag in self.tags:
|
||||||
|
tags.append(tag.filter_by_os_version(os_version=os_version).serialize())
|
||||||
|
return Tags(tags)
|
||||||
|
|
||||||
|
def filter_by_architecture(self, architecture="") -> Self:
|
||||||
|
tags = []
|
||||||
|
for tag in self.tags:
|
||||||
|
tags.append(tag.filter_by_architecture(architecture=architecture).serialize())
|
||||||
|
return Tags(tags)
|
||||||
|
|
||||||
|
def sort_by_image_time(self) -> Self:
|
||||||
|
self.tags.sort(key=lambda tag: tag.images.serialize()[0]['last_pushed'], reverse=True)
|
||||||
|
return self
|
||||||
|
|
||||||
|
def get_latest(self) -> Self:
|
||||||
|
tags = deepcopy(self)
|
||||||
|
for tag in tags.tags:
|
||||||
|
tag.images = tag.images.get_latest()
|
||||||
|
tags.sort_by_image_time()
|
||||||
|
tags.tags = [tags.tags[0]]
|
||||||
|
return tags
|
||||||
|
|
||||||
|
|
||||||
class Tag:
|
class Tag:
|
||||||
"""
|
"""
|
||||||
Tag class object.
|
Tag class object.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, tag: dict) -> None:
|
def __init__(self, tag: dict) -> None:
|
||||||
"""
|
"""
|
||||||
Initialize a tag object.
|
Initialize a tag object.
|
||||||
|
@ -62,8 +98,8 @@ class Tag:
|
||||||
"""
|
"""
|
||||||
Method to parse tag dictionary.
|
Method to parse tag dictionary.
|
||||||
"""
|
"""
|
||||||
self.creator = Serialize.integer(tag.get('creator', -1))
|
self.creator = Serialize.integer(tag.get("creator", -1))
|
||||||
self.id = Serialize.integer(tag.get('id', -1))
|
self.id = Serialize.integer(tag.get("id", -1))
|
||||||
self.images = Images(tag.get("images", None))
|
self.images = Images(tag.get("images", None))
|
||||||
self.last_updated = Serialize.time(tag.get("last_updated", None))
|
self.last_updated = Serialize.time(tag.get("last_updated", None))
|
||||||
self.last_updater = Serialize.integer(tag.get("last_updater", None))
|
self.last_updater = Serialize.integer(tag.get("last_updater", None))
|
||||||
|
@ -86,5 +122,20 @@ class Tag:
|
||||||
Method to serialize tag object.
|
Method to serialize tag object.
|
||||||
"""
|
"""
|
||||||
tags = self.__dict__
|
tags = self.__dict__
|
||||||
tags['images'] = self.images.serialize()
|
tags["images"] = self.images.serialize()
|
||||||
return tags
|
return tags
|
||||||
|
|
||||||
|
def filter_by_os(self, os="") -> Self:
|
||||||
|
tag = deepcopy(self)
|
||||||
|
tag.images = self.images.filter_by_os(os=os)
|
||||||
|
return tag
|
||||||
|
|
||||||
|
def filter_by_os_version(self, os_version="") -> Self:
|
||||||
|
tag = deepcopy(self)
|
||||||
|
tag.images = self.images.filter_by_os_version(os_version=os_version)
|
||||||
|
return tag
|
||||||
|
|
||||||
|
def filter_by_architecture(self, architecture="") -> Self:
|
||||||
|
tag = deepcopy(self)
|
||||||
|
tag.images = self.images.filter_by_architecture(architecture=architecture)
|
||||||
|
return tag
|
||||||
|
|
|
@ -21,13 +21,18 @@ class Upstream:
|
||||||
if username and password:
|
if username and password:
|
||||||
self.req.get_token(url + "/v2/users/login", username, password)
|
self.req.get_token(url + "/v2/users/login", username, password)
|
||||||
|
|
||||||
def get_tags(self, image, namespace: str = "library", url_params: str = None) -> dict:
|
def get_tags(
|
||||||
|
self, image, namespace: str = "library", url_params: str = None
|
||||||
|
) -> dict:
|
||||||
"""
|
"""
|
||||||
Method which returns the tags page in a dictionary.
|
Method which returns the tags page in a dictionary.
|
||||||
"""
|
"""
|
||||||
if url_params:
|
if url_params:
|
||||||
url = "{registry_url}/v2/namespaces/{namespace}/repositories/{repository}/tags?{url_params}".format(
|
url = "{registry_url}/v2/namespaces/{namespace}/repositories/{repository}/tags?{url_params}".format(
|
||||||
registry_url=self.url, namespace=namespace, repository=image, url_params=url_params
|
registry_url=self.url,
|
||||||
|
namespace=namespace,
|
||||||
|
repository=image,
|
||||||
|
url_params=url_params,
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
url = "{registry_url}/v2/namespaces/{namespace}/repositories/{repository}/tags".format(
|
url = "{registry_url}/v2/namespaces/{namespace}/repositories/{repository}/tags".format(
|
||||||
|
|
Loading…
Reference in a new issue