Files
dev_blog/functions/backup/main.py
T
2026-05-13 20:20:31 -05:00

81 lines
3.1 KiB
Python

import os
import subprocess
import tempfile
import logging
import functions_framework
from opentelemetry import _logs
from opentelemetry.sdk._logs import LoggerProvider, LoggingHandler
from opentelemetry.sdk._logs.export import BatchLogRecordProcessor
from opentelemetry.exporter.otlp.proto.http._log_exporter import OTLPLogExporter
from opentelemetry.sdk.resources import Resource
# ---------------------------------------------------------------------------
# OTEL Logging Setup
# ---------------------------------------------------------------------------
resource = Resource.create({
"service.name": "dev-blog-backup",
"deployment.environment": os.environ.get("ENVIRONMENT", "production")
})
logger_provider = LoggerProvider(resource=resource)
_logs.set_logger_provider(logger_provider)
# Export logs via OTLP (async/non-blocking via BatchLogRecordProcessor)
exporter = OTLPLogExporter()
logger_provider.add_log_record_processor(BatchLogRecordProcessor(exporter))
# Attach OTEL handler to the root logger
otel_handler = LoggingHandler(level=logging.INFO, logger_provider=logger_provider)
root_logger = logging.getLogger()
root_logger.addHandler(otel_handler)
# Create a logger for this module
logger = logging.getLogger(__name__)
logger.setLevel(logging.INFO)
# Disable the default stream handler for GCP to minimize billable logs
# By setting the root logger to WARNING, we ensure that standard output
# (captured by GCP) only contains high-priority logs.
# We also want to make sure we don't duplicate logs.
for handler in root_logger.handlers:
if not isinstance(handler, LoggingHandler):
handler.setLevel(logging.WARNING)
root_logger.setLevel(logging.WARNING)
@functions_framework.cloud_event
def run_backup(cloud_event):
logger.info(f"Triggered by event: {cloud_event['id']}")
# Restic environments are expected to be set via Secret Manager / Env vars
source_bucket = os.environ.get('SOURCE_BUCKET')
r2_account_id = os.environ.get('R2_ACCOUNT_ID')
r2_bucket = os.environ.get('R2_BUCKET')
# Construct the Restic repository URL for Cloudflare R2
os.environ['RESTIC_REPOSITORY'] = f"s3:https://{r2_account_id}.r2.cloudflarestorage.com/{r2_bucket}"
with tempfile.TemporaryDirectory() as tmpdir:
logger.info(f"Syncing {source_bucket} to {tmpdir}...")
try:
subprocess.run(['gsutil', '-m', 'rsync', '-r', source_bucket, tmpdir], check=True, capture_output=True, text=True)
except subprocess.CalledProcessError as e:
logger.error(f"Sync failed: {e.stderr}")
raise e
logger.info("Starting restic backup to R2...")
try:
result = subprocess.run(
['restic', 'backup', tmpdir, '--tag', 'gcs-trigger'],
capture_output=True,
text=True,
check=True
)
logger.info(result.stdout)
except subprocess.CalledProcessError as e:
logger.error(f"Restic failed: {e.stderr}")
raise e
logger.info("Backup completed successfully.")
# Ensure logs are flushed before the function exits
logger_provider.force_flush()