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:
Elia el Lazkani 2023-06-04 01:17:30 +02:00
parent 502750732c
commit 48b811795a
6 changed files with 130 additions and 28 deletions

View file

@ -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

View file

@ -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

View file

@ -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()

View file

@ -2,12 +2,14 @@ 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",
@ -20,7 +22,9 @@ class Registry(Upstream):
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)

View file

@ -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

View file

@ -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(