Skip to content

Commit dd6e503

Browse files
SNOW-3009259: Collect /etc/os-release information (#2754)
1 parent 3dba407 commit dd6e503

4 files changed

Lines changed: 594 additions & 0 deletions

File tree

src/snowflake/connector/auth/_auth.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@
5656
PYTHON_CONNECTOR_USER_AGENT,
5757
ReauthenticationRequest,
5858
)
59+
from ..os_details import get_os_details
5960
from ..platform_detection import detect_platforms
6061
from ..session_manager import BaseHttpConfig, HttpConfig
6162
from ..session_manager import SessionManager as SyncSessionManager
@@ -159,6 +160,7 @@ def base_auth_data(
159160
platform_detection_timeout_seconds=platform_detection_timeout_seconds,
160161
session_manager=session_manager.clone(max_retries=0),
161162
),
163+
"OS_DETAILS": get_os_details(),
162164
**build_minicore_usage_for_session(),
163165
},
164166
},
Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
#!/usr/bin/env python
2+
"""Module for extracting OS details from /etc/os-release on Linux systems."""
3+
4+
from __future__ import annotations
5+
6+
import logging
7+
import platform
8+
import re
9+
10+
logger = logging.getLogger(__name__)
11+
12+
# Allowed keys to extract from /etc/os-release
13+
ALLOWED_KEYS = [
14+
"NAME",
15+
"PRETTY_NAME",
16+
"ID",
17+
"BUILD_ID",
18+
"IMAGE_ID",
19+
"IMAGE_VERSION",
20+
"VERSION",
21+
"VERSION_ID",
22+
]
23+
24+
# Regex to parse: KEY=value or KEY="value"
25+
OS_RELEASE_KEY_VALUE_REGEX = re.compile(r'^([A-Z0-9_]+)=(?:"([^"]*)"|(.*))$')
26+
27+
# Cache the OS details so we only read the file once
28+
_cached_os_details: dict[str, str] | None = None
29+
_cache_initialized = False
30+
31+
32+
def extract_linux_os_release() -> dict[str, str]:
33+
"""
34+
Extract OS details from /etc/os-release file.
35+
36+
Returns:
37+
Dictionary containing OS details with keys from ALLOWED_KEYS.
38+
39+
Raises:
40+
FileNotFoundError: If /etc/os-release does not exist.
41+
IOError: If there's an error reading the file.
42+
"""
43+
result: dict[str, str] = {}
44+
45+
with open("/etc/os-release", encoding="utf-8") as f:
46+
contents = f.read()
47+
48+
for line in contents.split("\n"):
49+
match = OS_RELEASE_KEY_VALUE_REGEX.match(line)
50+
if match:
51+
key, quoted_value, unquoted_value = match.groups()
52+
if key in ALLOWED_KEYS:
53+
result[key] = (
54+
quoted_value if quoted_value is not None else unquoted_value
55+
)
56+
57+
return result
58+
59+
60+
def get_os_details() -> dict[str, str] | None:
61+
"""
62+
Get OS details from /etc/os-release (Linux only).
63+
64+
This function caches the result on first call. Returns None on non-Linux
65+
platforms or if there's an error reading the file.
66+
67+
Returns:
68+
Dictionary containing OS details, or None if unavailable or on error.
69+
"""
70+
global _cached_os_details, _cache_initialized
71+
72+
if _cache_initialized:
73+
return _cached_os_details
74+
75+
_cache_initialized = True
76+
77+
# Only attempt to read os-release on Linux
78+
if platform.system() != "Linux":
79+
_cached_os_details = None
80+
return None
81+
82+
try:
83+
_cached_os_details = extract_linux_os_release()
84+
return _cached_os_details
85+
except Exception as e:
86+
logger.debug("Error extracting OS details: %s", e)
87+
_cached_os_details = None
88+
return None

0 commit comments

Comments
 (0)