titan-iac/services/logging/opensearch-observability-setup-job.yaml

194 lines
5.9 KiB
YAML

# services/logging/opensearch-observability-setup-job.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: opensearch-observability-script
namespace: logging
data:
seed.py: |
import json
import os
import time
import urllib.error
import urllib.request
OSD_URL = os.environ.get(
"OSD_URL",
"http://opensearch-dashboards.logging.svc.cluster.local:5601",
).rstrip("/")
OBJECT_DIR = "/config"
def request_json(method, path, payload=None):
url = f"{OSD_URL}{path}"
data = None
headers = {"osd-xsrf": "true"}
if payload is not None:
data = json.dumps(payload).encode("utf-8")
headers["Content-Type"] = "application/json"
req = urllib.request.Request(url, data=data, method=method)
for key, value in headers.items():
req.add_header(key, value)
try:
with urllib.request.urlopen(req, timeout=30) as response:
body = response.read().decode("utf-8")
except urllib.error.HTTPError as exc:
detail = exc.read().decode("utf-8")
raise SystemExit(f"{method} {path} failed: {exc.code} {detail}")
if not body:
return {}
return json.loads(body)
def wait_ready():
for _ in range(60):
try:
request_json("GET", "/api/status")
return
except Exception:
time.sleep(5)
raise SystemExit("OpenSearch Dashboards did not become ready in time")
def load_payload(name):
path = os.path.join(OBJECT_DIR, name)
with open(path, "r", encoding="utf-8") as handle:
return json.load(handle)
def index_by_name(items, key):
lookup = {}
for item in items:
obj = item.get(key, {})
name = obj.get("name")
if not name:
continue
lookup.setdefault(name, item)
return lookup
def ensure_applications(apps):
existing = request_json("GET", "/api/observability/application/").get("data", [])
existing_by_name = {app.get("name"): app for app in existing if app.get("name")}
for app in apps:
name = app.get("name")
if not name:
continue
current = existing_by_name.get(name)
if not current:
request_json("POST", "/api/observability/application/", app)
print(f"created application: {name}")
continue
if app.get("baseQuery") != current.get("baseQuery"):
print(f"baseQuery differs for {name}; skipping update")
update_body = {}
for key in ("description", "servicesEntities", "traceGroups"):
if app.get(key, "") != current.get(key, ""):
update_body[key] = app.get(key, "")
if update_body:
request_json(
"PUT",
"/api/observability/application/",
{"appId": current["id"], "updateBody": update_body},
)
print(f"updated application: {name}")
def ensure_saved_objects(objects, object_type, endpoint):
existing = request_json(
"GET",
f"/api/observability/event_analytics/saved_objects?objectType={object_type}",
).get("observabilityObjectList", [])
key = "savedQuery" if object_type == "savedQuery" else "savedVisualization"
existing_by_name = index_by_name(existing, key)
for obj in objects:
name = obj.get("name")
if not name:
continue
current = existing_by_name.get(name)
if not current:
request_json("POST", endpoint, {"object": obj})
print(f"created {object_type}: {name}")
continue
current_body = current.get(key, {})
if current_body != obj:
request_json(
"PUT",
endpoint,
{"object_id": current["objectId"], "object": obj},
)
print(f"updated {object_type}: {name}")
def main():
wait_ready()
applications = load_payload("applications.json")
queries = load_payload("saved_queries.json")
visualizations = load_payload("saved_visualizations.json")
ensure_applications(applications)
ensure_saved_objects(queries, "savedQuery", "/api/observability/event_analytics/saved_objects/query")
ensure_saved_objects(
visualizations,
"savedVisualization",
"/api/observability/event_analytics/saved_objects/vis",
)
if __name__ == "__main__":
main()
---
apiVersion: batch/v1
kind: Job
metadata:
name: opensearch-observability-setup-2
namespace: logging
spec:
backoffLimit: 3
ttlSecondsAfterFinished: 3600
template:
spec:
restartPolicy: OnFailure
nodeSelector:
node-role.kubernetes.io/worker: "true"
hardware: rpi5
affinity:
nodeAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
- matchExpressions:
- key: hardware
operator: In
values:
- rpi5
containers:
- name: setup
image: python:3.11-alpine
command: ["python", "/scripts/seed.py"]
env:
- name: OSD_URL
value: http://opensearch-dashboards.logging.svc.cluster.local:5601
volumeMounts:
- name: scripts
mountPath: /scripts
readOnly: true
- name: objects
mountPath: /config
readOnly: true
volumes:
- name: scripts
configMap:
name: opensearch-observability-script
- name: objects
configMap:
name: opensearch-observability-objects