added tzdata and net-tools
This commit is contained in:
@@ -46,6 +46,8 @@ spec:
|
|||||||
value: /data
|
value: /data
|
||||||
- name: GLANCE_HELPER_KEY
|
- name: GLANCE_HELPER_KEY
|
||||||
value: oplQqnLnJK2vErRVYJpvVUcSDBOSbCHZSbsYY2bwSifgTMfT
|
value: oplQqnLnJK2vErRVYJpvVUcSDBOSbCHZSbsYY2bwSifgTMfT
|
||||||
|
- name: TZ
|
||||||
|
value: Europe/Budapest
|
||||||
ports:
|
ports:
|
||||||
- containerPort: 8000
|
- containerPort: 8000
|
||||||
command:
|
command:
|
||||||
@@ -54,18 +56,18 @@ spec:
|
|||||||
args:
|
args:
|
||||||
- 'set -eux;
|
- 'set -eux;
|
||||||
|
|
||||||
|
export DEBIAN_FRONTEND=noninteractive;
|
||||||
|
|
||||||
apt-get update;
|
apt-get update;
|
||||||
|
|
||||||
apt-get install -y --no-install-recommends curl ca-certificates iputils-ping
|
apt-get install -y --no-install-recommends curl ca-certificates iputils-ping
|
||||||
dnsutils;
|
dnsutils tzdata net-tools;
|
||||||
|
|
||||||
rm -rf /var/lib/apt/lists/*;
|
rm -rf /var/lib/apt/lists/*;
|
||||||
|
|
||||||
pip install --no-cache-dir fastapi uvicorn requests beautifulsoup4 prometheus-client;
|
pip install --no-cache-dir fastapi uvicorn requests beautifulsoup4 prometheus-client;
|
||||||
|
|
||||||
python -c "import uvicorn; uvicorn.run(''app:APP'', host=''0.0.0.0'', port=8000)"
|
python -c "import uvicorn; uvicorn.run(''app:APP'', host=''0.0.0.0'', port=8000)"'
|
||||||
|
|
||||||
'
|
|
||||||
volumeMounts:
|
volumeMounts:
|
||||||
- name: app
|
- name: app
|
||||||
mountPath: /app
|
mountPath: /app
|
||||||
@@ -183,60 +185,62 @@ data:
|
|||||||
\ media_type=\"application/json; charset=utf-8\")\n\n\n@APP.get(\"/metrics\")\n\
|
\ media_type=\"application/json; charset=utf-8\")\n\n\n@APP.get(\"/metrics\")\n\
|
||||||
def metrics():\n return Response(generate_latest(), media_type=CONTENT_TYPE_LATEST)\n\
|
def metrics():\n return Response(generate_latest(), media_type=CONTENT_TYPE_LATEST)\n\
|
||||||
\n\n# -------------------------------\n# Tandoor helpers\n# -------------------------------\n\
|
\n\n# -------------------------------\n# Tandoor helpers\n# -------------------------------\n\
|
||||||
def _today_str() -> str:\n # Use Europe/Budapest for \"day\" boundaries\n \
|
def _today_str() -> str:\n # Use Europe/Budapest for \"day\" boundaries (fallback\
|
||||||
\ return datetime.now(tz=ZoneInfo(\"Europe/Budapest\")).date().isoformat()\n\
|
\ to UTC if tzdata missing)\n try:\n return datetime.now(tz=ZoneInfo(\"\
|
||||||
\ndef _load_json(path: Path, default):\n try:\n with path.open(\"r\"\
|
Europe/Budapest\")).date().isoformat()\n except Exception:\n return\
|
||||||
, encoding=\"utf-8\") as f:\n return json.load(f)\n except Exception:\n\
|
\ datetime.utcnow().date().isoformat()\n\ndef _load_json(path: Path, default):\n\
|
||||||
\ return default\n\ndef _save_json(path: Path, data) -> None:\n tmp\
|
\ try:\n with path.open(\"r\", encoding=\"utf-8\") as f:\n \
|
||||||
\ = path.with_suffix(path.suffix + \".tmp\")\n with tmp.open(\"w\", encoding=\"\
|
\ return json.load(f)\n except Exception:\n return default\n\ndef\
|
||||||
utf-8\") as f:\n json.dump(data, f, ensure_ascii=False, indent=2)\n \
|
\ _save_json(path: Path, data) -> None:\n tmp = path.with_suffix(path.suffix\
|
||||||
\ tmp.replace(path)\n\ndef _tandoor_headers() -> Dict[str, str]:\n token =\
|
\ + \".tmp\")\n with tmp.open(\"w\", encoding=\"utf-8\") as f:\n json.dump(data,\
|
||||||
\ os.getenv(\"TANDOOR_TOKEN\", \"\")\n if not token:\n return {\"Accept\"\
|
\ f, ensure_ascii=False, indent=2)\n tmp.replace(path)\n\ndef _tandoor_headers()\
|
||||||
: \"application/json\"}\n return {\"Accept\": \"application/json\", \"Authorization\"\
|
\ -> Dict[str, str]:\n token = os.getenv(\"TANDOOR_TOKEN\", \"\")\n if not\
|
||||||
: f\"Bearer {token}\"}\n\ndef _rewrite_to_public(maybe_url: Optional[str]) ->\
|
\ token:\n return {\"Accept\": \"application/json\"}\n return {\"Accept\"\
|
||||||
\ Optional[str]:\n if not maybe_url:\n return None\n\n # Relative\
|
: \"application/json\", \"Authorization\": f\"Bearer {token}\"}\n\ndef _rewrite_to_public(maybe_url:\
|
||||||
\ path -> public\n if maybe_url.startswith(\"/\"):\n return TANDOOR_PUBLIC_URL\
|
\ Optional[str]) -> Optional[str]:\n if not maybe_url:\n return None\n\
|
||||||
\ + maybe_url\n\n # If the API returns internal host URLs, rewrite scheme+host\
|
\n # Relative path -> public\n if maybe_url.startswith(\"/\"):\n \
|
||||||
\ to public\n try:\n u = urlparse(maybe_url)\n pub = urlparse(TANDOOR_PUBLIC_URL)\n\
|
\ return TANDOOR_PUBLIC_URL + maybe_url\n\n # If the API returns internal host\
|
||||||
\ internal = urlparse(TANDOOR_INTERNAL_URL)\n if u.netloc and internal.netloc\
|
\ URLs, rewrite scheme+host to public\n try:\n u = urlparse(maybe_url)\n\
|
||||||
\ and u.netloc == internal.netloc:\n u = u._replace(scheme=pub.scheme,\
|
\ pub = urlparse(TANDOOR_PUBLIC_URL)\n internal = urlparse(TANDOOR_INTERNAL_URL)\n\
|
||||||
\ netloc=pub.netloc)\n return urlunparse(u)\n except Exception:\n\
|
\ if u.netloc and internal.netloc and u.netloc == internal.netloc:\n \
|
||||||
\ pass\n\n return maybe_url\n\ndef _fetch_recipes_flat() -> List[Dict[str,\
|
\ u = u._replace(scheme=pub.scheme, netloc=pub.netloc)\n return\
|
||||||
\ Any]]:\n # Prefer /api/recipe/flat/ because it's already {id,name,image}\
|
\ urlunparse(u)\n except Exception:\n pass\n\n return maybe_url\n\
|
||||||
\ list\n flat_url = f\"{TANDOOR_INTERNAL_URL}/api/recipe/flat/\"\n r = requests.get(flat_url,\
|
\ndef _fetch_recipes_flat() -> List[Dict[str, Any]]:\n # Prefer /api/recipe/flat/\
|
||||||
\ headers=_tandoor_headers(), timeout=15)\n if r.status_code == 200:\n \
|
\ because it's already {id,name,image} list\n flat_url = f\"{TANDOOR_INTERNAL_URL}/api/recipe/flat/\"\
|
||||||
\ data = r.json()\n # Expected: list\n if isinstance(data, list):\n\
|
\n r = requests.get(flat_url, headers=_tandoor_headers(), timeout=15)\n \
|
||||||
\ out = []\n for x in data:\n out.append({\n\
|
\ if r.status_code == 200:\n data = r.json()\n # Expected: list\n\
|
||||||
\ \"id\": int(x.get(\"id\", 0)),\n \"name\"\
|
\ if isinstance(data, list):\n out = []\n for x in\
|
||||||
: str(x.get(\"name\", \"\")),\n \"image\": _rewrite_to_public(x.get(\"\
|
\ data:\n out.append({\n \"id\": int(x.get(\"\
|
||||||
image\")),\n })\n return [x for x in out if x[\"id\"\
|
id\", 0)),\n \"name\": str(x.get(\"name\", \"\")),\n \
|
||||||
] and x[\"name\"]]\n\n # Fallback: paginated /api/recipe/\n list_url = f\"\
|
\ \"image\": _rewrite_to_public(x.get(\"image\")),\n \
|
||||||
{TANDOOR_INTERNAL_URL}/api/recipe/?page_size=250\"\n r = requests.get(list_url,\
|
\ })\n return [x for x in out if x[\"id\"] and x[\"name\"]]\n\n\
|
||||||
\ headers=_tandoor_headers(), timeout=15)\n r.raise_for_status()\n data\
|
\ # Fallback: paginated /api/recipe/\n list_url = f\"{TANDOOR_INTERNAL_URL}/api/recipe/?page_size=250\"\
|
||||||
\ = r.json()\n items = data.get(\"results\", []) if isinstance(data, dict)\
|
\n r = requests.get(list_url, headers=_tandoor_headers(), timeout=15)\n \
|
||||||
\ else []\n out = []\n for x in items:\n out.append({\n \
|
\ r.raise_for_status()\n data = r.json()\n items = data.get(\"results\"\
|
||||||
\ \"id\": int(x.get(\"id\", 0)),\n \"name\": str(x.get(\"name\",\
|
, []) if isinstance(data, dict) else []\n out = []\n for x in items:\n \
|
||||||
\ \"\")),\n \"image\": _rewrite_to_public(x.get(\"image\")),\n \
|
\ out.append({\n \"id\": int(x.get(\"id\", 0)),\n \
|
||||||
\ })\n return [x for x in out if x[\"id\"] and x[\"name\"]]\n\ndef _get_cooked_for_today()\
|
\ \"name\": str(x.get(\"name\", \"\")),\n \"image\": _rewrite_to_public(x.get(\"\
|
||||||
\ -> List[int]:\n today = _today_str()\n cooked = _load_json(COOKED_PATH,\
|
image\")),\n })\n return [x for x in out if x[\"id\"] and x[\"name\"\
|
||||||
\ {})\n ids = cooked.get(today, [])\n # normalize\n try:\n return\
|
]]\n\ndef _get_cooked_for_today() -> List[int]:\n today = _today_str()\n \
|
||||||
\ [int(i) for i in ids]\n except Exception:\n return []\n\ndef _set_cooked_today(ids:\
|
\ cooked = _load_json(COOKED_PATH, {})\n ids = cooked.get(today, [])\n \
|
||||||
\ List[int]) -> None:\n today = _today_str()\n cooked = _load_json(COOKED_PATH,\
|
\ # normalize\n try:\n return [int(i) for i in ids]\n except Exception:\n\
|
||||||
\ {})\n cooked[today] = sorted(list({int(i) for i in ids}))\n # Optional\
|
\ return []\n\ndef _set_cooked_today(ids: List[int]) -> None:\n today\
|
||||||
\ cleanup: keep only last 14 days\n try:\n keys = sorted(cooked.keys())\n\
|
\ = _today_str()\n cooked = _load_json(COOKED_PATH, {})\n cooked[today]\
|
||||||
\ if len(keys) > 14:\n for k in keys[:-14]:\n \
|
\ = sorted(list({int(i) for i in ids}))\n # Optional cleanup: keep only last\
|
||||||
\ cooked.pop(k, None)\n except Exception:\n pass\n _save_json(COOKED_PATH,\
|
\ 14 days\n try:\n keys = sorted(cooked.keys())\n if len(keys)\
|
||||||
\ cooked)\n\ndef _get_picks_today() -> List[int]:\n today = _today_str()\n\
|
\ > 14:\n for k in keys[:-14]:\n cooked.pop(k, None)\n\
|
||||||
\ picks = _load_json(PICKS_PATH, {})\n ids = picks.get(today, [])\n try:\n\
|
\ except Exception:\n pass\n _save_json(COOKED_PATH, cooked)\n\n\
|
||||||
\ return [int(i) for i in ids]\n except Exception:\n return []\n\
|
def _get_picks_today() -> List[int]:\n today = _today_str()\n picks = _load_json(PICKS_PATH,\
|
||||||
\ndef _set_picks_today(ids: List[int]) -> None:\n today = _today_str()\n \
|
\ {})\n ids = picks.get(today, [])\n try:\n return [int(i) for i\
|
||||||
\ picks = _load_json(PICKS_PATH, {})\n picks[today] = [int(i) for i in ids\
|
\ in ids]\n except Exception:\n return []\n\ndef _set_picks_today(ids:\
|
||||||
\ if int(i) > 0]\n # cleanup old days\n try:\n keys = sorted(picks.keys())\n\
|
\ List[int]) -> None:\n today = _today_str()\n picks = _load_json(PICKS_PATH,\
|
||||||
\ if len(keys) > 14:\n for k in keys[:-14]:\n \
|
\ {})\n picks[today] = [int(i) for i in ids if int(i) > 0]\n # cleanup old\
|
||||||
\ picks.pop(k, None)\n except Exception:\n pass\n _save_json(PICKS_PATH,\
|
\ days\n try:\n keys = sorted(picks.keys())\n if len(keys) >\
|
||||||
\ picks)\n\ndef _ensure_daily_picks(recipes: List[Dict[str, Any]], count: int)\
|
\ 14:\n for k in keys[:-14]:\n picks.pop(k, None)\n\
|
||||||
\ -> List[int]:\n cooked = set(_get_cooked_for_today())\n picks = _get_picks_today()\n\
|
\ except Exception:\n pass\n _save_json(PICKS_PATH, picks)\n\ndef\
|
||||||
\n # Remove picks that are cooked today\n picks = [i for i in picks if i\
|
\ _ensure_daily_picks(recipes: List[Dict[str, Any]], count: int) -> List[int]:\n\
|
||||||
|
\ cooked = set(_get_cooked_for_today())\n picks = _get_picks_today()\n\n\
|
||||||
|
\ # Remove picks that are cooked today\n picks = [i for i in picks if i\
|
||||||
\ not in cooked]\n\n # Top up to requested count if needed\n if len(picks)\
|
\ not in cooked]\n\n # Top up to requested count if needed\n if len(picks)\
|
||||||
\ < count:\n available = [r[\"id\"] for r in recipes if r[\"id\"] not in\
|
\ < count:\n available = [r[\"id\"] for r in recipes if r[\"id\"] not in\
|
||||||
\ cooked and r[\"id\"] not in picks]\n # If everything is cooked (or too\
|
\ cooked and r[\"id\"] not in picks]\n # If everything is cooked (or too\
|
||||||
@@ -305,25 +309,3 @@ kind: Ingress
|
|||||||
metadata:
|
metadata:
|
||||||
name: glance-helper
|
name: glance-helper
|
||||||
namespace: glance-system
|
namespace: glance-system
|
||||||
annotations:
|
|
||||||
cert-manager.io/cluster-issuer: letsencrypt-prod
|
|
||||||
external-dns.alpha.kubernetes.io/hostname: glance-helper.dooplex.hu,glance-helper.home
|
|
||||||
nginx.ingress.kubernetes.io/ssl-redirect: '"true"'
|
|
||||||
nginx.ingress.kubernetes.io/proxy-body-size: 10m
|
|
||||||
spec:
|
|
||||||
ingressClassName: nginx-internal
|
|
||||||
rules:
|
|
||||||
- host: glance-helper.dooplex.hu
|
|
||||||
http:
|
|
||||||
paths:
|
|
||||||
- path: /
|
|
||||||
pathType: Prefix
|
|
||||||
backend:
|
|
||||||
service:
|
|
||||||
name: glance-helper
|
|
||||||
port:
|
|
||||||
number: 8000
|
|
||||||
tls:
|
|
||||||
- hosts:
|
|
||||||
- glance-helper.dooplex.hu
|
|
||||||
secretName: glance-helper-tls
|
|
||||||
Reference in New Issue
Block a user