Skip to content

Commit 627bb93

Browse files
committed
Feature: [Ubuntu, no-CVM] Added additional UT coverage and addressed some comments
1 parent 25e0af9 commit 627bb93

4 files changed

Lines changed: 122 additions & 73 deletions

File tree

src/core/src/bootstrap/EnvLayer.py

Lines changed: 7 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -297,17 +297,16 @@ def detect_confidential_vm_by_fde(self):
297297
if script_dir and not os.path.isdir(script_dir):
298298
try:
299299
os.makedirs(script_dir)
300-
except OSError:
301-
if not os.path.isdir(script_dir):
302-
raise
300+
except Exception:
301+
raise
303302

304303
self.file_system.write_with_retry(script_path, detection_script, 'w')
305304

306305
code, out = self.run_command_output('bash "{0}"'.format(script_path), False, False)
307306
command_output = str(out).strip() if out is not None else str()
308307
return code == 0 and re.search(r'\bFDE\s*=\s*true\b', command_output, re.IGNORECASE) is not None, command_output
309308
except Exception:
310-
return False, command_output
309+
raise Exception("FDE_DETECTION_ERROR:{0}; OUTPUT:{1}".format(str(e), command_output))
311310
finally:
312311
if script_path is not None and os.path.exists(script_path):
313312
try:
@@ -320,13 +319,10 @@ def detect_confidential_vm_by_imds(self):
320319
"""Queries Azure IMDS and returns whether the VM reports ConfidentialVM security type."""
321320
command = 'curl -s --connect-timeout 2 --max-time 2 -H Metadata:true --noproxy "*" "http://169.254.169.254/metadata/instance?api-version=2025-04-07"'
322321

323-
try:
324-
code, out = self.run_command_output(command, False, False)
325-
command_output = str(out).strip() if out is not None else str()
326-
if code == 0 and re.search(r'"securityType"\s*:\s*"ConfidentialVM"', command_output, re.IGNORECASE) is not None:
327-
return True, 'IMDS:ConfidentialVM'
328-
except Exception:
329-
pass
322+
code, out = self.run_command_output(command, False, False)
323+
command_output = str(out).strip() if out is not None else str()
324+
if code == 0 and re.search(r'"securityType"\s*:\s*"ConfidentialVM"', command_output, re.IGNORECASE) is not None:
325+
return True, 'IMDS:ConfidentialVM'
330326

331327
return False, str()
332328

src/core/src/core_logic/PatchInstaller.py

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -811,8 +811,6 @@ def try_update_certificates_for_default_patching(self):
811811
try:
812812
is_confidential_vm, detection_details = self.env_layer.detect_confidential_vm()
813813
except Exception as e:
814-
is_confidential_vm = False
815-
detection_details = str()
816814
self.composite_logger.log_warning("Unable to determine whether the VM is a Confidential VM before attempting the UEFI certificate update. Continuing with patch installation... [Error: {0}]".format(str(e)))
817815
return
818816

src/core/tests/Test_EnvLayer.py

Lines changed: 94 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
# limitations under the License.
1414
#
1515
# Requires Python 2.7+
16+
import os
1617
import platform
1718
import sys
1819
import unittest
@@ -84,6 +85,42 @@ def mock_linux_distribution_to_return_rhel_10(self):
8485

8586
def mock_distro_os_release_attr_return_rhel_10(self, attribute):
8687
return '10.0'
88+
89+
def mock_run_command_output_fde_true(self, cmd, no_output=False, chk_err=False):
90+
return 0, 'test-vm,/dev/sda1,FDE=true,LUKS:/dev/sda1'
91+
92+
def mock_run_command_output_fde_false(self, cmd, no_output=False, chk_err=False):
93+
return 0, 'test-vm,/dev/sda1,FDE=false,LUKS:/dev/sda1'
94+
95+
def mock_run_command_output_imds_true(self, cmd, no_output=False, chk_err=False):
96+
return 0, '"securityProfile": { "encryptionAtHost": "false", "secureBootEnabled": "false", "securityType": "ConfidentialVM", "virtualTpmEnabled": "false"}'
97+
98+
def mock_run_command_output_imds_false(self, cmd, no_output=False, chk_err=False):
99+
return 0, '{"compute":{"securityProfile":{"securityType":""}}}'
100+
101+
def mock_run_command_raises_exception(self, cmd, no_output=False, chk_err=False):
102+
raise Exception('Test Exception')
103+
104+
def mock_detect_confidential_vm_by_fde_returns_true(self):
105+
return True, 'test-vm,/dev/sda1,FDE=true,LUKS:/dev/sda1'
106+
107+
def mock_detect_confidential_vm_by_fde_returns_false(self):
108+
return False, str()
109+
110+
def mock_detect_confidential_vm_by_imds_returns_true(self):
111+
return True, 'IMDS:ConfidentialVM'
112+
113+
def mock_detect_confidential_vm_by_imds_returns_false(self):
114+
return False, str()
115+
116+
def mock_os_remove_raises_exeception(self, path):
117+
raise Exception('Test Exception')
118+
119+
def mock_os_makedirs_raises_exeception(self, path):
120+
raise Exception('Test Exception')
121+
122+
def mock_os_path_isdir_returns_false(self, path):
123+
return False
87124
# endregion
88125

89126
def test_get_package_manager(self):
@@ -138,84 +175,82 @@ def test_is_distro_azure_linux_3(self):
138175
distro.os_release_attr = self.backup_envlayer_distro_os_release_attr
139176

140177
def test_detect_confidential_vm_by_fde(self):
178+
backup_detect_cvm_bash_file_path = Constants.AzGPSPaths.DETECT_CVM
141179
backup_run_command_output = self.envlayer.run_command_output
180+
backup_os_remove = os.remove
181+
backup_os_path_isdir = os.path.isdir
182+
backup_os_makedirs = os.makedirs
142183

143-
self.envlayer.run_command_output = lambda cmd, no_output=False, chk_err=False: (0, 'test-vm,/dev/sda1,FDE=true,LUKS:/dev/sda1')
144-
is_confidential_vm, detection_details = self.envlayer.detect_confidential_vm_by_fde()
184+
test_input_output_table = [
185+
[self.mock_run_command_output_fde_true, backup_os_remove, backup_os_path_isdir, backup_os_makedirs, False, True, 'FDE=true'],
186+
[self.mock_run_command_output_fde_false, backup_os_remove, backup_os_path_isdir, backup_os_makedirs, False, False, str()],
187+
[self.mock_run_command_output_fde_true, self.mock_os_remove_raises_exeception, backup_os_path_isdir, backup_os_makedirs, False, True, 'FDE=true'],
188+
[self.mock_run_command_output_fde_true, backup_os_remove, self.mock_os_path_isdir_returns_false, self.mock_os_makedirs_raises_exeception, True, False, str()],
189+
[self.mock_run_command_output_fde_true, self.mock_os_remove_raises_exeception, self.mock_os_path_isdir_returns_false, self.mock_os_makedirs_raises_exeception, True, False, str()],
190+
]
145191

146-
self.assertTrue(is_confidential_vm)
147-
self.assertIn('FDE=true', detection_details)
192+
Constants.AzGPSPaths.DETECT_CVM = os.path.join(os.getcwd(), 'patch.detectcvm.sh')
193+
for row in test_input_output_table:
194+
self.envlayer.run_command_output = row[0]
195+
os.remove = row[1]
196+
os.path.isdir = row[2]
197+
os.makedirs = row[3]
198+
expected_raises_exception = row[4]
199+
expected_is_confidential_vm = row[5]
200+
expected_detection_details = row[6]
201+
202+
if expected_raises_exception:
203+
self.assertRaises(Exception, self.envlayer.detect_confidential_vm_by_fde)
204+
else:
205+
is_confidential_vm, detection_details = self.envlayer.detect_confidential_vm_by_fde()
206+
self.assertEqual(is_confidential_vm, expected_is_confidential_vm)
207+
self.assertIn(expected_detection_details, detection_details)
148208

149209
self.envlayer.run_command_output = backup_run_command_output
210+
os.remove = backup_os_remove
211+
os.path.isdir = backup_os_path_isdir
212+
os.makedirs = backup_os_makedirs
213+
Constants.AzGPSPaths.DETECT_CVM = backup_detect_cvm_bash_file_path
150214

151215
def test_detect_confidential_vm_by_imds(self):
152216
backup_run_command_output = self.envlayer.run_command_output
153217

154-
self.envlayer.run_command_output = lambda cmd, no_output=False, chk_err=False: (0, '{"compute":{"securityProfile":{"securityType":"ConfidentialVM"}}}')
155-
is_confidential_vm, detection_details = self.envlayer.detect_confidential_vm_by_imds()
218+
test_input_output_table = [
219+
[self.mock_run_command_output_imds_true, True, 'IMDS:ConfidentialVM'],
220+
[self.mock_run_command_output_imds_false, False, str()],
221+
]
156222

157-
self.assertTrue(is_confidential_vm)
158-
self.assertEqual('IMDS:ConfidentialVM', detection_details)
223+
for row in test_input_output_table:
224+
self.envlayer.run_command_output = row[0]
225+
is_confidential_vm, detection_details = self.envlayer.detect_confidential_vm_by_imds()
226+
self.assertEqual(is_confidential_vm, row[1])
227+
self.assertIn(row[2], detection_details)
159228

160229
self.envlayer.run_command_output = backup_run_command_output
161230

162-
def test_detect_confidential_vm_checks_imds_before_fde(self):
163-
backup_platform = self.envlayer.platform
164-
backup_detect_confidential_vm_by_fde = self.envlayer.detect_confidential_vm_by_fde
165-
backup_detect_confidential_vm_by_imds = self.envlayer.detect_confidential_vm_by_imds
166-
167-
calls = []
168-
self.envlayer.platform = self.envlayer.Platform()
169-
self.envlayer.platform.os_type = lambda: 'Linux'
170-
171-
def detect_confidential_vm_by_fde():
172-
calls.append('fde')
173-
return True, 'test-vm,/dev/sda1,FDE=true,LUKS:/dev/sda1'
174-
175-
def detect_confidential_vm_by_imds():
176-
calls.append('imds')
177-
return True, 'IMDS:ConfidentialVM'
178-
179-
self.envlayer.detect_confidential_vm_by_fde = detect_confidential_vm_by_fde
180-
self.envlayer.detect_confidential_vm_by_imds = detect_confidential_vm_by_imds
181-
182-
is_confidential_vm, detection_details = self.envlayer.detect_confidential_vm()
183-
184-
self.assertTrue(is_confidential_vm)
185-
self.assertIn('IMDS:ConfidentialVM', detection_details)
186-
self.assertEqual(['imds'], calls)
187-
188-
self.envlayer.platform = backup_platform
189-
self.envlayer.detect_confidential_vm_by_fde = backup_detect_confidential_vm_by_fde
190-
self.envlayer.detect_confidential_vm_by_imds = backup_detect_confidential_vm_by_imds
231+
def test_detect_confidential_vm(self):
232+
self.backup_platform_system = platform.system
191233

192-
def test_detect_confidential_vm_checks_fde_when_imds_not_detected(self):
193-
backup_platform = self.envlayer.platform
194234
backup_detect_confidential_vm_by_fde = self.envlayer.detect_confidential_vm_by_fde
195235
backup_detect_confidential_vm_by_imds = self.envlayer.detect_confidential_vm_by_imds
196236

197-
calls = []
198-
self.envlayer.platform = self.envlayer.Platform()
199-
self.envlayer.platform.os_type = lambda: 'Linux'
200-
201-
def detect_confidential_vm_by_fde():
202-
calls.append('fde')
203-
return True, 'test-vm,/dev/sda1,FDE=true,LUKS:/dev/sda1'
204-
205-
def detect_confidential_vm_by_imds():
206-
calls.append('imds')
207-
return False, str()
208-
209-
self.envlayer.detect_confidential_vm_by_fde = detect_confidential_vm_by_fde
210-
self.envlayer.detect_confidential_vm_by_imds = detect_confidential_vm_by_imds
211-
212-
is_confidential_vm, detection_details = self.envlayer.detect_confidential_vm()
237+
test_input_output_table = [
238+
["Linux", self.mock_detect_confidential_vm_by_fde_returns_true, self.mock_detect_confidential_vm_by_imds_returns_true, True, 'IMDS:ConfidentialVM'],
239+
["Linux", self.mock_detect_confidential_vm_by_fde_returns_true, self.mock_detect_confidential_vm_by_imds_returns_false, True, 'FDE=true'],
240+
["Windows", self.mock_run_command_output_fde_true, self.mock_run_command_output_imds_true, False, str()],
241+
["Linux", self.mock_detect_confidential_vm_by_fde_returns_false, self.mock_detect_confidential_vm_by_imds_returns_false, False, str()],
242+
]
213243

214-
self.assertTrue(is_confidential_vm)
215-
self.assertIn('FDE=true', detection_details)
216-
self.assertEqual(['imds', 'fde'], calls)
244+
for row in test_input_output_table:
245+
platform.system = self.mock_platform_system if row[0] == 'Linux' else self.mock_platform_system_windows
246+
self.envlayer.detect_confidential_vm_by_fde = row[1]
247+
self.envlayer.detect_confidential_vm_by_imds = row[2]
248+
is_confidential_vm, detection_details = self.envlayer.detect_confidential_vm()
249+
self.assertEqual(is_confidential_vm, row[3])
250+
self.assertIn(row[4], detection_details)
217251

218-
self.envlayer.platform = backup_platform
252+
# restore original methods
253+
platform.system = self.backup_platform_system
219254
self.envlayer.detect_confidential_vm_by_fde = backup_detect_confidential_vm_by_fde
220255
self.envlayer.detect_confidential_vm_by_imds = backup_detect_confidential_vm_by_imds
221256

src/core/tests/Test_PatchInstaller.py

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,12 @@ def tearDown(self):
3535
# region Mocks
3636
def mock_update_certs_raise_exception(self):
3737
raise Exception("Simulated cert update failure")
38+
39+
def mock_detect_confidential_vm_raises_exception(self):
40+
raise Exception("Simulated VM detection failure")
41+
42+
def mock_detect_confidential_vm_by_imds_returns_true(self):
43+
return True, 'IMDS:ConfidentialVM'
3844
# endregion
3945

4046
# region Utility functions (update cert tests)
@@ -819,12 +825,26 @@ def test_try_update_certs_swallows_exception_from_update_certs(self):
819825

820826
def test_try_update_certificates_skips_confidential_vm(self):
821827
runtime = self._create_update_certs_runtime(enable_uefi_cert_update=True, health_store_id="pub_off_sku_2025.01.01")
822-
runtime.patch_installer.env_layer.detect_confidential_vm = lambda: (True, 'IMDS:ConfidentialVM')
828+
backup_detect_confidential_vm_by_imds = runtime.env_layer.detect_confidential_vm_by_imds
823829

830+
runtime.env_layer.detect_confidential_vm_by_imds = self.mock_detect_confidential_vm_by_imds_returns_true
824831
method_called = self._track_method_call(runtime.patch_installer.package_manager, 'update_certs')
825832
runtime.patch_installer.start_installation(simulate=True)
833+
self.assertEqual(len(method_called), 0)
834+
835+
runtime.env_layer.detect_confidential_vm_by_imds = backup_detect_confidential_vm_by_imds
836+
runtime.stop()
826837

838+
def test_try_update_certificates_skips_when_detect_confidential_vm_raises_exception(self):
839+
runtime = self._create_update_certs_runtime(enable_uefi_cert_update=True, health_store_id="pub_off_sku_2025.01.01")
840+
backup_detect_confidential_vm = runtime.env_layer.detect_confidential_vm
841+
842+
runtime.env_layer.detect_confidential_vm = self.mock_detect_confidential_vm_raises_exception
843+
method_called = self._track_method_call(runtime.patch_installer.package_manager, 'update_certs')
844+
runtime.patch_installer.start_installation(simulate=True)
827845
self.assertEqual(len(method_called), 0)
846+
847+
runtime.env_layer.detect_confidential_vm = backup_detect_confidential_vm
828848
runtime.stop()
829849
# endregion
830850

0 commit comments

Comments
 (0)