137 lines
		
	
	
	
		
			6 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
			
		
		
	
	
			137 lines
		
	
	
	
		
			6 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
| import bisect
 | |
| 
 | |
| from cnr.exception import PackageAlreadyExists
 | |
| from cnr.models.package_base import manifest_media_type
 | |
| 
 | |
| from data.database import (db_transaction, get_epoch_timestamp, Manifest, ManifestList, Tag,
 | |
|                            ManifestListManifest, Blob, ManifestBlob)
 | |
| from data.oci_model import (blob as blob_model, manifest as manifest_model,
 | |
|                             manifest_list as manifest_list_model,
 | |
|                             tag as tag_model)
 | |
| 
 | |
| 
 | |
| LIST_MEDIA_TYPE = 'application/vnd.cnr.manifest.list.v0.json'
 | |
| SCHEMA_VERSION = 'v0'
 | |
| 
 | |
| 
 | |
| def _ensure_sha256_header(digest):
 | |
|   if digest.startswith('sha256:'):
 | |
|     return digest
 | |
|   return 'sha256:' + digest
 | |
| 
 | |
| 
 | |
| def get_app_release(repo, tag_name, media_type):
 | |
|   """ Returns (tag, manifest, blob) given a repo object, tag_name, and media_type). """
 | |
|   tag = tag_model.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 delete_app_release(repo, tag_name, media_type):
 | |
|   """ Terminate a Tag/media-type couple
 | |
|   It find the corresponding tag/manifest and remove from the manifestlistmanifest the manifest
 | |
|   1. it terminates the current tag (in all-cases)
 | |
|   2. if the new manifestlist is not empty, it creates a new tag for it
 | |
|   """
 | |
|   media_type_id = ManifestListManifest.media_type.get_id(manifest_media_type(media_type))
 | |
| 
 | |
|   with db_transaction():
 | |
|     tag = tag_model.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 = manifest_list_model.get_or_create_manifest_list(list_json, LIST_MEDIA_TYPE,
 | |
|                                                                      SCHEMA_VERSION)
 | |
|       manifest_list_model.create_manifestlistmanifest(manifestlist, list_manifest_ids,
 | |
|                                                       list_json)
 | |
|       tag = tag_model.create_or_update_tag(repo, tag_name, manifest_list=manifestlist,
 | |
|                                            tag_kind="release")
 | |
|     return tag
 | |
| 
 | |
| 
 | |
| def create_app_release(repo, tag_name, manifest_data, digest, force=False):
 | |
|   """ 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 = manifest_model.get_or_create_manifest(manifest_data, manifest_data['mediaType'])
 | |
|     # get the tag
 | |
|     tag = tag_model.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 tag_model.tag_media_type_exists(tag, manifest.media_type):
 | |
|       if force:
 | |
|         delete_app_release(repo, tag_name, manifest.media_type.name)
 | |
|         return create_app_release(repo, tag_name, manifest_data, digest, force=False)
 | |
|       else:
 | |
|         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 = manifest_list_model.get_or_create_manifest_list(list_json, LIST_MEDIA_TYPE,
 | |
|                                                                        SCHEMA_VERSION)
 | |
|     manifest_list_model.create_manifestlistmanifest(manifestlist, list_manifest_ids, list_json)
 | |
| 
 | |
|     tag = tag_model.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 == _ensure_sha256_header(blob_digest)).get())
 | |
|     except ManifestBlob.DoesNotExist:
 | |
|       blob = blob_model.get_blob(blob_digest)
 | |
|       ManifestBlob.create(manifest=manifest, blob=blob)
 | |
|     return tag
 | |
| 
 | |
| def get_release_objs(repo, media_type=None):
 | |
|   """ Returns an array of Tag for a repo, with optional filtering 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 = tag_model.filter_tags_by_media_type(release_query, media_type)
 | |
| 
 | |
|   return tag_model.tag_alive_oci(release_query)
 | |
| 
 | |
| def get_releases(repo, media_type=None):
 | |
|   """ Returns an array of Tag.name for a repo, can filter by media_type. """
 | |
|   return [t.name for t in get_release_objs(repo, media_type)]
 |