Source code for operators.tile.remove_in_s3

from typing import List, Union
from airflow.utils.decorators import apply_defaults
from airflow.utils.context import Context
from airflow.exceptions import AirflowException
from operators.tile.base_tile import BaseTileOperator


[docs] class RemoveInS3Operator(BaseTileOperator): """ Operator to delete tile(s) from S3 without affecting the tileserver. :param dest_s3: path(s) to the S3 files to delete :type dest_s3: Union[str, List[str]] :param is_dry_run: whether to perform a dry run, defaults to True :type is_dry_run: bool, optional :param verify_tile_exists: whether to verify if the tile exists in S3 before deleting, defaults to True :type verify_tile_exists: bool, optional :raises AirflowException: if the deletion fails """ ui_color = "#FFA500" # Orange shade ui_fgcolor = "#000000" custom_operator_name = "RemoveInS3Operator" template_fields = ["dest_s3", "is_dry_run"] @apply_defaults def __init__( self, dest_s3: Union[str, List[str]], is_dry_run: bool = True, verify_tile_exists: bool = True, *args, **kwargs, ): super().__init__( src_env=None, # Not needed for S3-only operations src_tiles=None, # Not needed for S3-only operations dest_s3=dest_s3 if isinstance(dest_s3, str) else None, is_dry_run=is_dry_run, verify_tile_exists=verify_tile_exists, *args, **kwargs, ) # Handle list of S3 paths directly if isinstance(dest_s3, list): self.dest_s3_list = [self._ensure_s3_path(path) for path in dest_s3]
[docs] def execute(self, context: Context) -> None: """ Delete tile(s) from S3. """ try: # Add clear indication of dry run mode at the beginning if self.is_dry_run: self.log.info( f"╔═════════════════════════════════════════════════════════════════╗" ) self.log.info( f"║ 🔍 DRY RUN MODE: No actual changes will be made to files or systems ║" ) self.log.info( f"╚═════════════════════════════════════════════════════════════════╝" ) if not self.dest_s3_list: raise AirflowException("dest_s3 is required for remove_in_s3 operation") self.log.info( f"Starting RemoveInS3Operator with {len(self.dest_s3_list)} S3 files" ) # Verify S3 files exist before proceeding if not self.is_dry_run and self.verify_tile_exists: for s3_path in self.dest_s3_list: self.verify_s3_file(s3_path) # List of tileset names for logging tileset_names = [self.get_tileset_name(path) for path in self.dest_s3_list] self.log.info(f"Removing tiles from S3: {tileset_names}") if self.is_dry_run: # Dry run path - just log what would happen for s3_path in self.dest_s3_list: tileset_name = self.get_tileset_name(s3_path) self.log.info( f"[DRY RUN] Would delete tile {tileset_name} from S3: {s3_path}" ) self.log.info( f"[DRY RUN] Would delete {len(self.dest_s3_list)} tiles from S3" ) else: # Actual execution path self.log.info(f"Deleting {len(self.dest_s3_list)} tiles from S3") errors = [] for s3_path in self.dest_s3_list: tileset_name = self.get_tileset_name(s3_path) try: self.log.info( f"Deleting tile {tileset_name} from S3: {s3_path}" ) self.s3_delete(s3_path) self.log.info( f"Successfully deleted tile {tileset_name} from S3" ) except Exception as e: errors.append(f"Failed to delete {s3_path}: {str(e)}") self.log.error(f"Error deleting {s3_path}: {e}") if errors: raise AirflowException( f"Failed to delete some files from S3: {'; '.join(errors)}" ) self.log.info( f"Successfully deleted {len(self.dest_s3_list)} tiles from S3" ) # Add clear indication of completion if self.is_dry_run: self.log.info( f"╔════════════════════════════════════════════════════════════════╗" ) self.log.info( f"║ ✓ DRY RUN COMPLETED: No actual changes were made ║" ) self.log.info( f"╚════════════════════════════════════════════════════════════════╝" ) else: self.log.info( f"✓ S3 deletion successfully completed for {len(self.dest_s3_list)} files" ) except Exception as ex: self.log.error(f"Error in RemoveInS3Operator: {ex}") # Send notification to Slack for failures error_message = f""" ⚠️ Error in RemoveInS3Operator: Files Count: {len(self.dest_s3_list)} {'[DRY RUN] - No actual changes were made' if self.is_dry_run else ''} Error: {str(ex)} """ self.notify_slack(error_message) raise ex