1
0
mirror of https://github.com/quay/quay.git synced 2025-04-18 10:44:06 +03:00
quay/util/notification.py
Sunandadadi 6688bcca09
backend: implement basic functionality (PROJQUAY-7076) (#2984)
* database: adding subject_backfilled index to manifest table (PROJQUAY-7360) (#2963)

adding subject_backfilled index to manifest table

* Rebasing with main

* updating cypress data

* fixing conflicts and rebasing with latest code

* adding tests

* Forcing an empty commit.

* Forcing an empty commit.

* skip_locked test fix

* adding tests

* minor fixes

---------

Co-authored-by: Brandon Caton <bcaton@redhat.com>
2024-06-27 16:48:39 -04:00

122 lines
4.1 KiB
Python

import json
import logging
from data.database import (
ExternalNotificationEvent,
RepositoryNotification,
TagNotificationSuccess,
db_for_update,
get_epoch_timestamp_ms,
)
from data.model import db_transaction, oci
from data.model.user import get_namespace_by_user_id
from data.registry_model import registry_model
from notifications import spawn_notification
logger = logging.getLogger(__name__)
BATCH_SIZE = 10
# Define a constant for the SKIP_LOCKED flag for testing purposes,
# since we test with mysql 5.7 which does not support this flag.
SKIP_LOCKED = True
def fetch_active_notification(event, task_run_interval_ms=5 * 60 * 60 * 1000):
"""
task_run_interval_ms specifies how long a task must wait before being ran again.
"""
with db_transaction():
try:
# Fetch active notifications that match the event_name
query = (
RepositoryNotification.select(
RepositoryNotification.id,
RepositoryNotification.method,
RepositoryNotification.repository,
RepositoryNotification.event_config_json,
)
.where(
RepositoryNotification.event == event.id,
RepositoryNotification.number_of_failures < 3,
(
RepositoryNotification.last_ran_ms
< get_epoch_timestamp_ms() - task_run_interval_ms
)
| (RepositoryNotification.last_ran_ms.is_null(True)),
)
.order_by(RepositoryNotification.last_ran_ms.asc(nulls="first"))
)
notification = db_for_update(query, skip_locked=SKIP_LOCKED).get()
RepositoryNotification.update(last_ran_ms=get_epoch_timestamp_ms()).where(
RepositoryNotification.id == notification.id
).execute()
return notification
except RepositoryNotification.DoesNotExist:
return None
def track_tags_to_notify(tags, notification):
for tag in tags:
TagNotificationSuccess.create(
notification=notification.id, tag=tag.id, method=notification.method
)
def fetch_notified_tag_ids_for_event(notification):
response = (
TagNotificationSuccess.select(TagNotificationSuccess.tag)
.where(
TagNotificationSuccess.notification == notification.id,
TagNotificationSuccess.method == notification.method,
)
.distinct()
)
return [r.tag.id for r in response]
def scan_for_image_expiry_notifications(event_name, batch_size=BATCH_SIZE):
"""
Get the repository notification prioritized by last_ran_ms = None followed by asc order of last_ran_ms.
"""
event = ExternalNotificationEvent.get(ExternalNotificationEvent.name == event_name)
for _ in range(batch_size):
notification = fetch_active_notification(event)
if not notification:
return
repository = notification.repository
repo_id = repository.id
config = json.loads(notification.event_config_json)
if not config.get("days", None):
logger.error(
f"Missing key days in config for notification_id:{notification.id} created for repository_id:{repo_id}"
)
continue
# Fetch tags that were already notified
notified_tags = fetch_notified_tag_ids_for_event(notification)
# Fetch tags matching notification's config
tags = oci.tag.fetch_repo_tags_for_image_expiry_expiry_event(
repo_id, config["days"], notified_tags
)
if not len(tags):
continue
track_tags_to_notify(tags, notification)
namespace_name = get_namespace_by_user_id(repository.namespace_user)
repository_ref = registry_model.lookup_repository(namespace_name, repository.name)
# Push tags into queue notification worker queue
spawn_notification(
repository_ref,
event_name,
{"tags": [tag.name for tag in tags], "expiring_in": f"{config['days']} days"},
)
return