Source code for airflow_pydantic.extras.common.airflow_functions

import os
from datetime import datetime, timedelta
from logging import getLogger

from pytz import UTC

from ...airflow import AirflowFailException, AirflowSkipException

__all__ = (
    "clean_dag_runs",
    "clean_dags",
    "fail",
    "pass_",
    "skip",
)

_log = getLogger(__name__)






[docs] def fail(): _log.info("Failing task execution") raise AirflowFailException
[docs] def pass_(): _log.info("Passing task execution")
[docs] def clean_dag_runs(session, delete_successful, delete_failed, mark_failed_as_successful, max_dagruns, days_to_keep): from airflow.models import DagModel, DagRun from airflow.utils.state import State # Make cutoff_date timezone-aware (UTC) utc_now = datetime.now(tz=UTC) cutoff_date = utc_now - timedelta(days=days_to_keep) _log.info(f"Cutoff date for clean: {cutoff_date}") # Fetch all DAGs from the DagBag dag_ids = [d.dag_id for d in session.query(DagModel.dag_id).distinct(DagModel.dag_id).all()] _log.info(f"Found DAGs to clean up: {dag_ids}") deleted = 0 for dag_id in dag_ids: _log.info(f"Cleaning up DAG: {dag_id}") # Query for DAG runs of each DAG query = session.query(DagRun).filter(DagRun.dag_id == dag_id) if delete_successful is False: _log.info(f"Not deleting successful DAG runs for DAG: {dag_id}") query = query.filter(DagRun.state != State.SUCCESS) if delete_failed is False: _log.info(f"Not deleting failed DAG runs for DAG: {dag_id}") query = query.filter(DagRun.state != State.FAILED) dagruns = query.order_by(DagRun.execution_date.asc()).all() total_runs = len(dagruns) _log.info(f"Found {total_runs} DAG runs to clean up for DAG: {dag_id}") for dr in dagruns: # Compare execution_date (offset-aware) with cutoff_date (now offset-aware) if dr.execution_date < cutoff_date or total_runs > max_dagruns: _log.info(f"Deleting DAG run: {dr}") session.delete(dr) deleted += 1 total_runs -= 1 # Adjust count since we deleted one elif mark_failed_as_successful: # Need to iterate through all remaining if dr.state == State.FAILED: # Mark failed runs as successful _log.info(f"Marking failed DAG run as successful: {dr}") dr.state = State.SUCCESS session.merge(dr) elif not mark_failed_as_successful: break # Since they are ordered, no more to delete _log.info("Committing DAG run deletions") session.commit() _log.info(f"Total DAG runs deleted: {deleted}")
[docs] def clean_dags(session, **context): from airflow.models import DagModel _log.info("Starting to run Clear Process") dags = session.query(DagModel).all() entries_to_delete = [] _log.info(f"Found DAGs: {len(dags)}") for dag in dags: # Check if it is a zip-file if dag.fileloc is not None and ".zip/" in dag.fileloc: index = dag.fileloc.rfind(".zip/") + len(".zip") fileloc = dag.fileloc[0:index] else: fileloc = dag.fileloc if fileloc is None: _log.info(f"Adding to delete - `fileloc` None for DAG: {dag}") entries_to_delete.append(dag) elif not os.path.exists(fileloc): _log.info(f"Adding to delete - file does not exist for DAG: {dag}") entries_to_delete.append(dag) else: _log.info(f"Found valid file for DAG: {dag}") _log.info(f"Deleting dags:\n{len(entries_to_delete)}") for entry in entries_to_delete: session.delete(entry) _log.info("Committing DAG deletions") session.commit() _log.info(f"Total DAGs deleted: {len(entries_to_delete)}")