66 lines
2.5 KiB
Python
66 lines
2.5 KiB
Python
"""Shared CLI invoker for any function in functions/.
|
|
|
|
Usage (inside the lambda pod, via `bash ctrl/invoke.sh [name [event_json]]`):
|
|
python invoke.py # invokes the only function, or the first found
|
|
python invoke.py lambda_function # specific function
|
|
python invoke.py lambda_function '{}' # with event payload
|
|
"""
|
|
|
|
import importlib.util
|
|
import json
|
|
import os
|
|
import sys
|
|
from pathlib import Path
|
|
|
|
# Defaults match the in-cluster configmap so behavior is identical whether
|
|
# this script is invoked directly or via the FastAPI runner. `setdefault`
|
|
# never overrides an existing env var — the configmap wins in the pod.
|
|
os.environ.setdefault("BUCKET_NAME", "my-company-reports-bucket")
|
|
os.environ.setdefault("PREFIX", "2026/04/")
|
|
os.environ.setdefault("S3_ENDPOINT_URL", "http://minio:9000")
|
|
os.environ.setdefault("AWS_ACCESS_KEY_ID", "minioadmin")
|
|
os.environ.setdefault("AWS_SECRET_ACCESS_KEY", "minioadmin")
|
|
os.environ.setdefault("AWS_REGION", "us-east-1")
|
|
|
|
REPO_ROOT = Path(__file__).parent
|
|
FUNCTIONS_DIR = Path(os.environ.get("FUNCTIONS_DIR", str(REPO_ROOT / "functions")))
|
|
SHARED_DIR = REPO_ROOT / "shared"
|
|
|
|
# Make shared/ importable from any handler ("from shared import ..."). Matches
|
|
# how a Lambda Layer would expose code on PYTHONPATH at runtime.
|
|
if SHARED_DIR.exists() and str(REPO_ROOT) not in sys.path:
|
|
sys.path.insert(0, str(REPO_ROOT))
|
|
|
|
|
|
def _pick_default_function() -> str:
|
|
candidates = sorted(
|
|
d.name for d in FUNCTIONS_DIR.iterdir()
|
|
if d.is_dir() and not d.name.startswith("_") and (d / "handler.py").exists()
|
|
)
|
|
if not candidates:
|
|
print(f"no function folders with handler.py in {FUNCTIONS_DIR}", file=sys.stderr)
|
|
sys.exit(2)
|
|
return candidates[0]
|
|
|
|
|
|
def _load(name: str):
|
|
path = FUNCTIONS_DIR / name / "handler.py"
|
|
if not path.exists():
|
|
print(f"function not found: {path}", file=sys.stderr)
|
|
sys.exit(2)
|
|
spec = importlib.util.spec_from_file_location(f"functions.{name}.handler", path)
|
|
module = importlib.util.module_from_spec(spec)
|
|
spec.loader.exec_module(module)
|
|
if not hasattr(module, "handler"):
|
|
print(f"{path} has no handler(event, context)", file=sys.stderr)
|
|
sys.exit(2)
|
|
return module
|
|
|
|
|
|
if __name__ == "__main__":
|
|
name = sys.argv[1] if len(sys.argv) > 1 else _pick_default_function()
|
|
event = json.loads(sys.argv[2]) if len(sys.argv) > 2 else {}
|
|
module = _load(name)
|
|
response = module.handler(event, None)
|
|
print(json.dumps(response, indent=2))
|