import bisect from cnr.exception import PackageAlreadyExists from cnr.models.package_base import manifest_media_type from data import oci_model from data.database import (db_transaction, get_epoch_timestamp, Manifest, ManifestList, Tag, ManifestListManifest, Blob, ManifestBlob) LIST_MEDIA_TYPE = 'application/vnd.cnr.manifest.list.v0.json' SCHEMA_VERSION = 'v0' def get_app_release(repo, tag_name, media_type): """ Returns (tag, manifest, blob) given a repo object, tag_name, and media_type). """ tag = oci_model.tag.get_tag(repo, tag_name, tag_kind='release') media_type_id = ManifestListManifest.media_type.get_id(manifest_media_type(media_type)) manifestlistmanifest = (tag.manifest_list.manifestlistmanifest_set .join(Manifest) .where(ManifestListManifest.media_type == media_type_id).get()) manifest = manifestlistmanifest.manifest blob = Blob.select().join(ManifestBlob).where(ManifestBlob.manifest == manifest).get() return (tag, manifest, blob) def create_app_release(repo, tag_name, manifest, digest): """ Create a new application release, it includes creating a new Tag, ManifestList, ManifestListManifests, Manifest, ManifestBlob. To deduplicate the ManifestList, the manifestlist_json is kept ordered by the manifest.id. To find the insert point in the ManifestList it uses bisect on the manifest-ids list. """ with db_transaction(): # Create/get the package manifest manifest = oci_model.manifest.get_or_create_manifest(manifest, manifest['mediaType']) # get the tag tag = oci_model.tag.get_or_initialize_tag(repo, tag_name) if tag.manifest_list is None: tag.manifest_list = ManifestList(media_type=ManifestList.media_type.get_id(LIST_MEDIA_TYPE), schema_version=SCHEMA_VERSION, manifest_list_json=[]) elif oci_model.tag.tag_media_type_exists(tag, manifest.media_type): raise PackageAlreadyExists("package exists already") list_json = tag.manifest_list.manifest_list_json mlm_query = (ManifestListManifest .select() .where(ManifestListManifest.manifest_list == tag.manifest_list)) list_manifest_ids = sorted([mlm.manifest_id for mlm in mlm_query]) insert_point = bisect.bisect_left(list_manifest_ids, manifest.id) list_json.insert(insert_point, manifest.manifest_json) list_manifest_ids.insert(insert_point, manifest.id) manifestlist = oci_model.manifest_list.get_or_create_manifest_list(list_json, LIST_MEDIA_TYPE, SCHEMA_VERSION) oci_model.manifest_list.create_manifestlistmanifest(manifestlist, list_manifest_ids, list_json) tag = oci_model.tag.create_or_update_tag(repo, tag_name, manifest_list=manifestlist, tag_kind="release") blob_digest = digest try: (ManifestBlob .select() .join(Blob) .where(ManifestBlob.manifest == manifest, Blob.digest == blob_digest).get()) except ManifestBlob.DoesNotExist: blob = oci_model.blob.get_blob(blob_digest) ManifestBlob.create(manifest=manifest, blob=blob) return tag def delete_app_release(repo, tag_name, media_type): """ Delete a Tag/media-type couple """ media_type_id = ManifestListManifest.media_type.get_id(manifest_media_type(media_type)) with db_transaction(): tag = oci_model.tag.get_tag(repo, tag_name) manifest_list = tag.manifest_list list_json = manifest_list.manifest_list_json mlm_query = (ManifestListManifest .select() .where(ManifestListManifest.manifest_list == tag.manifest_list)) list_manifest_ids = sorted([mlm.manifest_id for mlm in mlm_query]) manifestlistmanifest = (tag .manifest_list .manifestlistmanifest_set .where(ManifestListManifest.media_type == media_type_id).get()) index = list_manifest_ids.index(manifestlistmanifest.manifest_id) list_manifest_ids.pop(index) list_json.pop(index) if not list_json: tag.lifetime_end = get_epoch_timestamp() tag.save() else: manifestlist = oci_model.manifest_list.get_or_create_manifest_list(list_json, LIST_MEDIA_TYPE, SCHEMA_VERSION) oci_model.manifest_list.create_manifestlistmanifest(manifestlist, list_manifest_ids, list_json) tag = oci_model.tag.create_or_update_tag(repo, tag_name, manifest_list=manifestlist, tag_kind="release") return tag def get_releases(repo, media_type=None): """ Returns an array of Tag.name for a repo, can filter by media_type. """ release_query = (Tag .select() .where(Tag.repository == repo, Tag.tag_kind == Tag.tag_kind.get_id("release"))) if media_type: release_query = oci_model.tag.filter_tags_by_media_type(release_query, media_type) return [t.name for t in oci_model.tag.tag_alive_oci(release_query)]