From 754a0ade88cfe810e85600308a9832be96c6eb2f Mon Sep 17 00:00:00 2001
From: AvivGuiser <avivguiser@gmail.com>
Date: Tue, 17 Jun 2025 23:24:28 +0300
Subject: [PATCH 1/4] migrate validate_endpoint to python

Signed-off-by: AvivGuiser <avivguiser@gmail.com>
---
 Video/validate_endpoint.py | 136 +++++++++++++++++++++++++++++++++++++
 Video/video.sh             |   2 +-
 2 files changed, 137 insertions(+), 1 deletion(-)
 create mode 100644 Video/validate_endpoint.py

diff --git a/Video/validate_endpoint.py b/Video/validate_endpoint.py
new file mode 100644
index 0000000000..a822c17670
--- /dev/null
+++ b/Video/validate_endpoint.py
@@ -0,0 +1,136 @@
+#!/usr/bin/env python3
+
+import sys
+import os
+import base64
+import json
+from datetime import datetime
+from datetime import timezone
+from urllib.parse import urlparse
+import requests
+from requests.adapters import HTTPAdapter
+
+
+def get_timestamp():
+    """Get formatted timestamp based on SE_LOG_TIMESTAMP_FORMAT."""
+    ts_format = os.environ.get('SE_LOG_TIMESTAMP_FORMAT', '%Y-%m-%d %H:%M:%S,%f')
+    # Convert bash format to Python format
+    if '%3N' in ts_format:
+        # Replace %3N (bash milliseconds) with %f (Python microseconds) and trim later
+        ts_format_python = ts_format.replace('%3N', '%f')
+        timestamp = datetime.now(timezone.utc).strftime(ts_format_python)
+        # Convert microseconds to milliseconds (trim last 3 digits)
+        if ',%f' in ts_format:
+            # Find the microseconds part and trim to milliseconds
+            parts = timestamp.rsplit(',', 1)
+            if len(parts) == 2 and len(parts[1]) == 6:
+                timestamp = parts[0] + ',' + parts[1][:3]
+    else:
+        timestamp = datetime.now(timezone.utc).strftime(ts_format)
+    return timestamp
+
+
+def create_session(max_time=1):
+    """Create requests session with timeout configuration."""
+    session = requests.Session()
+    return session
+
+
+def get_basic_auth():
+    """Get basic authentication header if credentials are provided."""
+    username = os.environ.get('SE_ROUTER_USERNAME')
+    password = os.environ.get('SE_ROUTER_PASSWORD')
+
+    if username and password:
+        credentials = f"{username}:{password}"
+        encoded_credentials = base64.b64encode(credentials.encode()).decode()
+        return {"Authorization": f"Basic {encoded_credentials}"}
+
+    return {}
+
+
+def validate_endpoint(endpoint, graphql_endpoint=False, max_time=1):
+    """
+    Validate an endpoint by making HTTP request and checking status code.
+
+    Args:
+        endpoint (str): The endpoint URL to validate
+        graphql_endpoint (bool): Whether this is a GraphQL endpoint
+        max_time (int): Maximum time for request in seconds
+    """
+    process_name = "endpoint.checks"
+    session = create_session(max_time)
+
+    # Set up headers
+    headers = {}
+    headers.update(get_basic_auth())
+
+    try:
+        if graphql_endpoint:
+            # GraphQL endpoint check
+            headers['Content-Type'] = 'application/json'
+            data = {"query": "{ grid { sessionCount } }"}
+
+            response = session.post(
+                endpoint,
+                headers=headers,
+                json=data,
+                timeout=max_time,
+                verify=False  # Equivalent to curl's -k flag
+            )
+        else:
+            # Regular endpoint check
+            response = session.get(
+                endpoint,
+                headers=headers,
+                timeout=max_time,
+                verify=False  # Equivalent to curl's -k flag
+            )
+
+        status_code = response.status_code
+
+    except requests.exceptions.Timeout:
+        print(f"{get_timestamp()} [{process_name}] - Endpoint {endpoint} timed out after {max_time} seconds")
+        return False
+    except requests.exceptions.ConnectionError:
+        print(f"{get_timestamp()} [{process_name}] - Failed to connect to endpoint {endpoint}")
+        return False
+    except requests.exceptions.RequestException as e:
+        print(f"{get_timestamp()} [{process_name}] - Error connecting to endpoint {endpoint}: {str(e)}")
+        return False
+
+    # Handle different status codes
+    if status_code == 404:
+        print(f"{get_timestamp()} [{process_name}] - Endpoint {endpoint} is not found - status code: {status_code}")
+        return False
+    elif status_code == 401:
+        print(f"{get_timestamp()} [{process_name}] - Endpoint {endpoint} requires authentication - status code: {status_code}. Please provide valid credentials via SE_ROUTER_USERNAME and SE_ROUTER_PASSWORD environment variables.")
+        return False
+    elif status_code != 200:
+        print(f"{get_timestamp()} [{process_name}] - Endpoint {endpoint} is not available - status code: {status_code}")
+        return False
+
+    return True
+
+
+def main():
+    """Main function to handle command line arguments and execute validation."""
+    if len(sys.argv) < 2:
+        print("Usage: python3 validate_endpoint.py <endpoint> [graphql_endpoint]")
+        print("  endpoint: The URL endpoint to validate")
+        print("  graphql_endpoint: 'true' if this is a GraphQL endpoint (default: false)")
+        sys.exit(1)
+
+    endpoint = sys.argv[1]
+    graphql_endpoint = len(sys.argv) > 2 and sys.argv[2].lower() == 'true'
+    max_time = int(os.environ.get('SE_ENDPOINT_CHECK_TIMEOUT', 1))
+
+    # Validate the endpoint
+    success = validate_endpoint(endpoint, graphql_endpoint, max_time)
+
+    # Exit with appropriate code
+    sys.exit(0 if success else 1)
+
+
+if __name__ == "__main__":
+    main()
diff --git a/Video/video.sh b/Video/video.sh
index 47ce8a8c6f..22c683ccd7 100755
--- a/Video/video.sh
+++ b/Video/video.sh
@@ -91,7 +91,7 @@ function wait_for_display() {
 function check_if_api_respond() {
   endpoint_checks=$(curl --noproxy "*" -H "${BASIC_AUTH}" -sk -o /dev/null -w "%{http_code}" "${NODE_STATUS_ENDPOINT}")
   if [[ "${endpoint_checks}" != "200" ]]; then
-    /opt/bin/validate_endpoint.sh "${NODE_STATUS_ENDPOINT}"
+    python3 /opt/bin/validate_endpoint.py "${NODE_STATUS_ENDPOINT}"
     return 1
   fi
   return 0

From 6ee5112ef047834f638278916d31d330e12eb314 Mon Sep 17 00:00:00 2001
From: AvivGuiser <avivguiser@gmail.com>
Date: Wed, 18 Jun 2025 00:07:22 +0300
Subject: [PATCH 2/4] remove validate_endpoint.sh script

Signed-off-by: AvivGuiser <avivguiser@gmail.com>
---
 Video/validate_endpoint.sh | 30 ------------------------------
 1 file changed, 30 deletions(-)
 delete mode 100755 Video/validate_endpoint.sh

diff --git a/Video/validate_endpoint.sh b/Video/validate_endpoint.sh
deleted file mode 100755
index 0220458175..0000000000
--- a/Video/validate_endpoint.sh
+++ /dev/null
@@ -1,30 +0,0 @@
-#!/usr/bin/env bash
-
-endpoint=$1
-graphql_endpoint=${2:-false}
-max_time=1
-ts_format=${SE_LOG_TIMESTAMP_FORMAT:-"%Y-%m-%d %H:%M:%S,%3N"}
-process_name="endpoint.checks"
-
-if [ -n "${SE_ROUTER_USERNAME}" ] && [ -n "${SE_ROUTER_PASSWORD}" ]; then
-  BASIC_AUTH="$(echo -en "${SE_ROUTER_USERNAME}:${SE_ROUTER_PASSWORD}" | base64 -w0)"
-  BASIC_AUTH="Authorization: Basic ${BASIC_AUTH}"
-fi
-
-if [ "${graphql_endpoint}" = "true" ]; then
-  endpoint_checks=$(curl --noproxy "*" -m ${max_time} -k -X POST \
-    -H "Content-Type: application/json" \
-    -H "${BASIC_AUTH}" \
-    --data '{"query":"{ grid { sessionCount } }"}' \
-    -s "${endpoint}" -o /dev/null -w "%{http_code}")
-else
-  endpoint_checks=$(curl --noproxy "*" -H "${BASIC_AUTH}" -m ${max_time} -s -k -o /dev/null -w "%{http_code}" "${endpoint}")
-fi
-
-if [[ "$endpoint_checks" = "404" ]]; then
-  echo "$(date -u +"${ts_format}") [${process_name}] - Endpoint ${endpoint} is not found - status code: ${endpoint_checks}"
-elif [[ "$endpoint_checks" = "401" ]]; then
-  echo "$(date -u +"${ts_format}") [${process_name}] - Endpoint ${endpoint} requires authentication - status code: ${endpoint_checks}. Please provide valid credentials via SE_ROUTER_USERNAME and SE_ROUTER_PASSWORD environment variables."
-elif [[ "$endpoint_checks" != "200" ]]; then
-  echo "$(date -u +"${ts_format}") [${process_name}] - Endpoint ${endpoint} is not available - status code: ${endpoint_checks}"
-fi

From 970759796d5918cce74530ba85e6f55f0fcf58de Mon Sep 17 00:00:00 2001
From: AvivGuiser <avivguiser@gmail.com>
Date: Wed, 18 Jun 2025 10:15:55 +0300
Subject: [PATCH 3/4] fix timestmp issue

Signed-off-by: AvivGuiser <avivguiser@gmail.com>
---
 Video/validate_endpoint.py | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/Video/validate_endpoint.py b/Video/validate_endpoint.py
index a822c17670..6ca5f7e7f9 100644
--- a/Video/validate_endpoint.py
+++ b/Video/validate_endpoint.py
@@ -20,7 +20,7 @@ def get_timestamp():
         ts_format_python = ts_format.replace('%3N', '%f')
         timestamp = datetime.now(timezone.utc).strftime(ts_format_python)
         # Convert microseconds to milliseconds (trim last 3 digits)
-        if ',%f' in ts_format:
+        if '%f' in ts_format_python:
             # Find the microseconds part and trim to milliseconds
             parts = timestamp.rsplit(',', 1)
             if len(parts) == 2 and len(parts[1]) == 6:

From e3459b81b1046b27827b861e23ef8a9d6ad95bb6 Mon Sep 17 00:00:00 2001
From: AvivGuiser <avivguiser@gmail.com>
Date: Wed, 18 Jun 2025 20:15:04 +0300
Subject: [PATCH 4/4] remove max_time

Signed-off-by: AvivGuiser <avivguiser@gmail.com>
---
 Video/validate_endpoint.py | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/Video/validate_endpoint.py b/Video/validate_endpoint.py
index 6ca5f7e7f9..739792074f 100644
--- a/Video/validate_endpoint.py
+++ b/Video/validate_endpoint.py
@@ -30,7 +30,7 @@ def get_timestamp():
     return timestamp
 
 
-def create_session(max_time=1):
+def create_session():
     """Create requests session with timeout configuration."""
     session = requests.Session()
     return session
@@ -59,7 +59,7 @@ def validate_endpoint(endpoint, graphql_endpoint=False, max_time=1):
         max_time (int): Maximum time for request in seconds
     """
     process_name = "endpoint.checks"
-    session = create_session(max_time)
+    session = create_session()
 
     # Set up headers
     headers = {}