Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 70c1e98

Browse files
committed
lint and changelog
1 parent 3f02d26 commit 70c1e98

File tree

3 files changed

+74
-46
lines changed

3 files changed

+74
-46
lines changed

changelog/68204.fixed.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
Fix kernelpkg.latest_available() regex for Debian Bullseye/Ubuntu Noble compatibility.
2+
3+
This fix addresses a critical bug where kernelpkg.latest_available() would crash with
4+
AttributeError on modern Debian and Ubuntu systems due to a regex pattern that couldn't
5+
parse current package version formats like '6.1.147-1' (Debian 12) and '6.8.0-45-generic'
6+
(Ubuntu 24.04). The function now handles both dash and dot separators in version strings
7+
and includes graceful fallback behavior.

salt/modules/kernelpkg_linux_apt.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -90,12 +90,12 @@ def latest_available():
9090
if not version:
9191
# Fallback: if regex fails, return the latest installed version
9292
return latest_installed()
93-
93+
9494
# Extract version components: major.minor, patch, build/suffix
95-
major_minor = version.group(1) # e.g., "6.1" or "6.8"
96-
patch = version.group(2) # e.g., "147" or "0"
97-
suffix = version.group(3) # e.g., "1" or "45-generic"
98-
95+
major_minor = version.group(1) # e.g., "6.1" or "6.8"
96+
patch = version.group(2) # e.g., "147" or "0"
97+
suffix = version.group(3) # e.g., "1" or "45-generic"
98+
9999
# For simple Debian format like "6.1.147-1", suffix is just build number
100100
# For Ubuntu format like "6.8.0-45-generic", extract build number from suffix
101101
if re.match(r"^\d+$", suffix):

tests/pytests/unit/modules/test_kernelpkg_linux_apt_issue_68204_fixed.py

Lines changed: 62 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -42,11 +42,11 @@ def test_issue_68204_debian_bookworm_format():
4242
# Mock pkg.latest_version to return Debian Bookworm format
4343
mock_latest_version = MagicMock(return_value="6.1.147-1")
4444
mock_latest_installed = MagicMock(return_value="6.1.0-38-cloud-amd64")
45-
45+
4646
with patch.dict(kernelpkg.__salt__, {"pkg.latest_version": mock_latest_version}):
4747
with patch.object(kernelpkg, "latest_installed", mock_latest_installed):
4848
result = kernelpkg.latest_available()
49-
49+
5050
# Should not crash and should return a properly formatted version
5151
assert isinstance(result, str)
5252
assert "6.1.147" in result
@@ -62,13 +62,15 @@ def test_issue_68204_ubuntu_noble_format():
6262
# Mock pkg.latest_version to return Ubuntu Noble format
6363
mock_latest_version = MagicMock(return_value="6.8.0-45-generic")
6464
mock_latest_installed = MagicMock(return_value="6.1.0-38-generic")
65-
65+
6666
# Mock kernel type to return "generic" for this test
6767
with patch.object(kernelpkg, "_kernel_type", return_value="generic"):
68-
with patch.dict(kernelpkg.__salt__, {"pkg.latest_version": mock_latest_version}):
68+
with patch.dict(
69+
kernelpkg.__salt__, {"pkg.latest_version": mock_latest_version}
70+
):
6971
with patch.object(kernelpkg, "latest_installed", mock_latest_installed):
7072
result = kernelpkg.latest_available()
71-
73+
7274
# Should not crash and should return a properly formatted version
7375
assert isinstance(result, str)
7476
assert "6.8.0" in result
@@ -83,12 +85,14 @@ def test_issue_68204_debian_bullseye_format():
8385
"""
8486
mock_latest_version = MagicMock(return_value="5.10.0-18-amd64")
8587
mock_latest_installed = MagicMock(return_value="5.10.0-17-amd64")
86-
88+
8789
with patch.object(kernelpkg, "_kernel_type", return_value="amd64"):
88-
with patch.dict(kernelpkg.__salt__, {"pkg.latest_version": mock_latest_version}):
90+
with patch.dict(
91+
kernelpkg.__salt__, {"pkg.latest_version": mock_latest_version}
92+
):
8993
with patch.object(kernelpkg, "latest_installed", mock_latest_installed):
9094
result = kernelpkg.latest_available()
91-
95+
9296
assert isinstance(result, str)
9397
assert "5.10.0" in result
9498
assert "18" in result
@@ -102,12 +106,14 @@ def test_issue_68204_ubuntu_jammy_format():
102106
"""
103107
mock_latest_version = MagicMock(return_value="5.15.0-91-generic")
104108
mock_latest_installed = MagicMock(return_value="5.15.0-90-generic")
105-
109+
106110
with patch.object(kernelpkg, "_kernel_type", return_value="generic"):
107-
with patch.dict(kernelpkg.__salt__, {"pkg.latest_version": mock_latest_version}):
111+
with patch.dict(
112+
kernelpkg.__salt__, {"pkg.latest_version": mock_latest_version}
113+
):
108114
with patch.object(kernelpkg, "latest_installed", mock_latest_installed):
109115
result = kernelpkg.latest_available()
110-
116+
111117
assert isinstance(result, str)
112118
assert "5.15.0" in result
113119
assert "91" in result
@@ -121,11 +127,11 @@ def test_issue_68204_debian_security_update_format():
121127
"""
122128
mock_latest_version = MagicMock(return_value="6.1.147-1+deb12u1")
123129
mock_latest_installed = MagicMock(return_value="6.1.0-38-cloud-amd64")
124-
130+
125131
with patch.dict(kernelpkg.__salt__, {"pkg.latest_version": mock_latest_version}):
126132
with patch.object(kernelpkg, "latest_installed", mock_latest_installed):
127133
result = kernelpkg.latest_available()
128-
134+
129135
assert isinstance(result, str)
130136
assert "6.1.147" in result
131137
# The security update suffix should be handled gracefully
@@ -138,12 +144,14 @@ def test_issue_68204_ubuntu_complex_format():
138144
"""
139145
mock_latest_version = MagicMock(return_value="5.15.0-91.101-generic")
140146
mock_latest_installed = MagicMock(return_value="5.15.0-90-generic")
141-
147+
142148
with patch.object(kernelpkg, "_kernel_type", return_value="generic"):
143-
with patch.dict(kernelpkg.__salt__, {"pkg.latest_version": mock_latest_version}):
149+
with patch.dict(
150+
kernelpkg.__salt__, {"pkg.latest_version": mock_latest_version}
151+
):
144152
with patch.object(kernelpkg, "latest_installed", mock_latest_installed):
145153
result = kernelpkg.latest_available()
146-
154+
147155
assert isinstance(result, str)
148156
assert "5.15.0" in result
149157
assert "91" in result
@@ -157,12 +165,14 @@ def test_issue_68204_backport_format():
157165
"""
158166
mock_latest_version = MagicMock(return_value="6.1.147-1~bpo11+1")
159167
mock_latest_installed = MagicMock(return_value="5.10.0-18-amd64")
160-
168+
161169
with patch.object(kernelpkg, "_kernel_type", return_value="amd64"):
162-
with patch.dict(kernelpkg.__salt__, {"pkg.latest_version": mock_latest_version}):
170+
with patch.dict(
171+
kernelpkg.__salt__, {"pkg.latest_version": mock_latest_version}
172+
):
163173
with patch.object(kernelpkg, "latest_installed", mock_latest_installed):
164174
result = kernelpkg.latest_available()
165-
175+
166176
assert isinstance(result, str)
167177
assert "6.1.147" in result
168178
# Backport suffix should be handled gracefully
@@ -175,11 +185,11 @@ def test_issue_68204_empty_result_fallback():
175185
"""
176186
mock_latest_version = MagicMock(return_value="")
177187
mock_latest_installed = MagicMock(return_value="6.1.0-38-cloud-amd64")
178-
188+
179189
with patch.dict(kernelpkg.__salt__, {"pkg.latest_version": mock_latest_version}):
180190
with patch.object(kernelpkg, "latest_installed", mock_latest_installed):
181191
result = kernelpkg.latest_available()
182-
192+
183193
assert result == "6.1.0-38-cloud-amd64"
184194

185195

@@ -190,11 +200,11 @@ def test_issue_68204_malformed_version_fallback():
190200
"""
191201
mock_latest_version = MagicMock(return_value="not-a-version-at-all")
192202
mock_latest_installed = MagicMock(return_value="6.1.0-38-cloud-amd64")
193-
203+
194204
with patch.dict(kernelpkg.__salt__, {"pkg.latest_version": mock_latest_version}):
195205
with patch.object(kernelpkg, "latest_installed", mock_latest_installed):
196206
result = kernelpkg.latest_available()
197-
207+
198208
assert result == "6.1.0-38-cloud-amd64"
199209

200210

@@ -206,15 +216,17 @@ def test_issue_68204_original_error_reproduction():
206216
"""
207217
# These are the exact version formats that caused the original issue
208218
failing_versions = [
209-
"6.1.147-1", # Debian Bullseye/Bookworm
219+
"6.1.147-1", # Debian Bullseye/Bookworm
210220
"6.8.0-45-generic", # Ubuntu Noble
211221
]
212-
222+
213223
for version in failing_versions:
214224
mock_latest_version = MagicMock(return_value=version)
215225
mock_latest_installed = MagicMock(return_value="6.1.0-38-cloud-amd64")
216-
217-
with patch.dict(kernelpkg.__salt__, {"pkg.latest_version": mock_latest_version}):
226+
227+
with patch.dict(
228+
kernelpkg.__salt__, {"pkg.latest_version": mock_latest_version}
229+
):
218230
with patch.object(kernelpkg, "latest_installed", mock_latest_installed):
219231
# This line would have caused AttributeError with the old regex
220232
# Now it should work without any exception
@@ -224,36 +236,45 @@ def test_issue_68204_original_error_reproduction():
224236
assert isinstance(result, str)
225237
except AttributeError as e:
226238
if "'NoneType' object has no attribute 'group'" in str(e):
227-
pytest.fail(f"Original bug still present for version {version}: {e}")
239+
pytest.fail(
240+
f"Original bug still present for version {version}: {e}"
241+
)
228242
else:
229243
# Some other AttributeError, re-raise
230244
raise
231245

232246

233247
@pytest.mark.skipif(not HAS_MODULES, reason="Salt modules could not be loaded")
234-
@pytest.mark.parametrize("version_string,description", [
235-
("6.1.147-1", "Debian 12 Bookworm"),
236-
("6.8.0-45-generic", "Ubuntu 24.04 Noble"),
237-
("6.1.0-18-amd64", "Debian 11 Bullseye"),
238-
("6.2.0-37-generic", "Ubuntu 23.04 Lunar"),
239-
("5.15.0-91-generic", "Ubuntu 22.04 Jammy"),
240-
("6.1.147-1+deb12u1", "Debian security update"),
241-
])
248+
@pytest.mark.parametrize(
249+
"version_string,description",
250+
[
251+
("6.1.147-1", "Debian 12 Bookworm"),
252+
("6.8.0-45-generic", "Ubuntu 24.04 Noble"),
253+
("6.1.0-18-amd64", "Debian 11 Bullseye"),
254+
("6.2.0-37-generic", "Ubuntu 23.04 Lunar"),
255+
("5.15.0-91-generic", "Ubuntu 22.04 Jammy"),
256+
("6.1.147-1+deb12u1", "Debian security update"),
257+
],
258+
)
242259
def test_issue_68204_comprehensive_format_validation(version_string, description):
243260
"""
244261
Test - Comprehensive validation that all reported problematic formats now work
245262
This test validates the complete fix using the actual problematic version strings
246263
"""
247264
mock_latest_version = MagicMock(return_value=version_string)
248265
mock_latest_installed = MagicMock(return_value="6.1.0-38-cloud-amd64")
249-
266+
250267
with patch.dict(kernelpkg.__salt__, {"pkg.latest_version": mock_latest_version}):
251268
with patch.object(kernelpkg, "latest_installed", mock_latest_installed):
252269
# This should NOT raise an AttributeError anymore
253270
result = kernelpkg.latest_available()
254-
271+
255272
# Validate that we get a string result (no crash)
256-
assert isinstance(result, str), f"Failed for {description} format: {version_string}"
257-
273+
assert isinstance(
274+
result, str
275+
), f"Failed for {description} format: {version_string}"
276+
258277
# Validate that result is not empty
259-
assert len(result) > 0, f"Empty result for {description} format: {version_string}"
278+
assert (
279+
len(result) > 0
280+
), f"Empty result for {description} format: {version_string}"

0 commit comments

Comments
 (0)