Skip to content

Commit 8b166f3

Browse files
committed
refactor(kc-compat): read proc version once
1 parent 7840024 commit 8b166f3

File tree

2 files changed

+40
-54
lines changed

2 files changed

+40
-54
lines changed

kc-compat.py

Lines changed: 15 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -34,17 +34,15 @@
3434
}
3535

3636

37-
def get_kernel_hash():
37+
def get_kernel_hash_from_data(version_data):
3838
try:
3939
# noinspection PyCompatibility
4040
from hashlib import sha1
4141
except ImportError:
4242
from sha import sha as sha1
43-
f = open('/proc/version', 'rb')
44-
try:
45-
return sha1(f.read()).hexdigest()
46-
finally:
47-
f.close()
43+
return sha1(version_data).hexdigest()
44+
45+
4846

4947

5048
def inside_vz_container():
@@ -89,8 +87,8 @@ def is_distro_supported(distro_name):
8987
return distro_name in SUPPORTED_DISTROS
9088

9189

92-
def is_compat():
93-
url = 'http://patches.kernelcare.com/' + get_kernel_hash() + '/version'
90+
def is_compat(kernel_hash):
91+
url = 'http://patches.kernelcare.com/' + kernel_hash + '/version'
9492
try:
9593
urlopen(url)
9694
return True
@@ -122,29 +120,25 @@ def main():
122120
myprint(silent, "UNSUPPORTED; INSIDE CONTAINER")
123121
return 2
124122

125-
# Get system information once for both report and compatibility checking
126-
kernel_hash = get_kernel_hash()
123+
try:
124+
with open('/proc/version', 'rb') as f:
125+
version_data = f.read()
126+
except (IOError, OSError):
127+
version_data = b''
128+
129+
kernel_hash = get_kernel_hash_from_data(version_data)
127130
distro_name = get_distro_info()
128131

129-
# Show system information for support if --report flag is used
130132
if report:
131133
print("=== KernelCare Compatibility Report ===")
132134
print(f"Kernel Hash: {kernel_hash}")
133135
print(f"Distribution: {distro_name or 'Unknown'}")
134136
print(f"Version: Not available")
135-
136-
# Get kernel version from /proc/version
137-
try:
138-
with open('/proc/version', 'r') as f:
139-
kernel_version = f.read().strip()
140-
print(f"Kernel: {kernel_version}")
141-
except (IOError, OSError):
142-
print("Kernel: Unable to read /proc/version")
143-
137+
print(f"Kernel: {version_data.decode('utf-8', errors='replace').strip()}")
144138
print("=====================================")
145139

146140
try:
147-
if is_compat():
141+
if is_compat(kernel_hash):
148142
myprint(silent, "COMPATIBLE")
149143
return 0
150144
else:

test_kc_compat.py

Lines changed: 25 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -11,17 +11,12 @@
1111

1212

1313
class TestGetKernelHash:
14-
@patch('builtins.open', new_callable=mock_open, read_data=b'Linux version 5.4.0-test')
15-
def test_get_kernel_hash_success(self, mock_file):
16-
result = kc_compat.get_kernel_hash()
14+
def test_get_kernel_hash_from_data(self):
15+
version_data = b'Linux version 5.4.0-test'
16+
result = kc_compat.get_kernel_hash_from_data(version_data)
1717
assert isinstance(result, str)
1818
assert len(result) == 40 # SHA1 hex digest length
19-
mock_file.assert_called_once_with('/proc/version', 'rb')
2019

21-
@patch('builtins.open', side_effect=IOError("File not found"))
22-
def test_get_kernel_hash_file_error(self, mock_file):
23-
with pytest.raises(IOError):
24-
kc_compat.get_kernel_hash()
2520

2621

2722
class TestContainerDetection:
@@ -86,32 +81,28 @@ def test_is_distro_supported(self):
8681

8782

8883
class TestIsCompat:
89-
@patch.object(kc_compat, 'get_kernel_hash', return_value='abcdef123456')
9084
@patch.object(kc_compat, 'urlopen')
91-
def test_is_compat_success(self, mock_urlopen, mock_hash):
85+
def test_is_compat_success(self, mock_urlopen):
9286
mock_urlopen.return_value = MagicMock()
93-
assert kc_compat.is_compat() == True
87+
assert kc_compat.is_compat('abcdef123456') == True
9488
mock_urlopen.assert_called_once_with('http://patches.kernelcare.com/abcdef123456/version')
9589

96-
@patch.object(kc_compat, 'get_kernel_hash', return_value='abcdef123456')
9790
@patch.object(kc_compat, 'urlopen')
98-
def test_is_compat_404_error_returns_false(self, mock_urlopen, mock_hash):
91+
def test_is_compat_404_error_returns_false(self, mock_urlopen):
9992
mock_urlopen.side_effect = HTTPError(None, 404, 'Not Found', None, None)
100-
assert kc_compat.is_compat() == False
93+
assert kc_compat.is_compat('abcdef123456') == False
10194

102-
@patch.object(kc_compat, 'get_kernel_hash', return_value='abcdef123456')
10395
@patch.object(kc_compat, 'urlopen')
104-
def test_is_compat_500_error_raises(self, mock_urlopen, mock_hash):
96+
def test_is_compat_500_error_raises(self, mock_urlopen):
10597
mock_urlopen.side_effect = HTTPError(None, 500, 'Server Error', None, None)
10698
with pytest.raises(HTTPError):
107-
kc_compat.is_compat()
99+
kc_compat.is_compat('abcdef123456')
108100

109-
@patch.object(kc_compat, 'get_kernel_hash', return_value='abcdef123456')
110101
@patch.object(kc_compat, 'urlopen')
111-
def test_is_compat_url_error_raises(self, mock_urlopen, mock_hash):
102+
def test_is_compat_url_error_raises(self, mock_urlopen):
112103
mock_urlopen.side_effect = URLError('Connection refused')
113104
with pytest.raises(URLError):
114-
kc_compat.is_compat()
105+
kc_compat.is_compat('abcdef123456')
115106

116107

117108
class TestMyprint:
@@ -201,27 +192,28 @@ def test_main_silent_mode(self, mock_print, mock_compat, mock_lxc, mock_vz):
201192
mock_print.assert_not_called()
202193

203194
@patch('sys.argv', ['kc-compat.py', '--report'])
204-
@patch.object(kc_compat, 'get_kernel_hash', return_value='abcdef123456')
205195
@patch.object(kc_compat, 'get_distro_info', return_value='centos')
206196
@patch.object(kc_compat, 'inside_vz_container', return_value=False)
207197
@patch.object(kc_compat, 'inside_lxc_container', return_value=False)
208198
@patch.object(kc_compat, 'is_compat', return_value=True)
209-
@patch('builtins.open', new_callable=mock_open, read_data='Linux version 5.4.0-test')
199+
@patch('builtins.open', new_callable=mock_open, read_data=b'Linux version 5.4.0-test')
210200
@patch('builtins.print')
211-
def test_main_report_mode(self, mock_print, mock_file, mock_compat, mock_lxc, mock_vz, mock_distro, mock_hash):
201+
def test_main_report_mode(self, mock_print, mock_file, mock_compat, mock_lxc, mock_vz, mock_distro):
212202
result = kc_compat.main()
213203
assert result == 0
214204
# Check that report header and information are printed, followed by COMPATIBLE
215-
expected_calls = [
216-
(("=== KernelCare Compatibility Report ===",),),
217-
(("Kernel Hash: abcdef123456",),),
218-
(("Distribution: centos",),),
219-
(("Version: Not available",),),
220-
(("Kernel: Linux version 5.4.0-test",),),
221-
(("=====================================",),),
222-
(("COMPATIBLE",),)
223-
]
224-
mock_print.assert_has_calls(expected_calls)
205+
# We need to check the actual calls made, not exact matches
206+
calls = mock_print.call_args_list
207+
assert len(calls) >= 7 # At least 7 print calls
208+
209+
# Check specific calls
210+
assert calls[0].args[0] == "=== KernelCare Compatibility Report ==="
211+
assert calls[1].args[0].startswith("Kernel Hash: ")
212+
assert calls[2].args[0] == "Distribution: centos"
213+
assert calls[3].args[0] == "Version: Not available"
214+
assert calls[4].args[0] == "Kernel: Linux version 5.4.0-test"
215+
assert calls[5].args[0] == "====================================="
216+
assert calls[6].args[0] == "COMPATIBLE"
225217

226218

227219
@patch('sys.argv', ['kc-compat.py'])

0 commit comments

Comments
 (0)