Skip to content

Commit f286e0f

Browse files
Add az timeouts to local setup verification
1 parent 1647c34 commit f286e0f

2 files changed

Lines changed: 62 additions & 3 deletions

File tree

setup/verify_local_setup.py

Lines changed: 33 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@
2525
import sys
2626
from pathlib import Path
2727

28+
AZURE_CLI_TIMEOUT_SECONDS = 15
29+
2830
# Configure UTF-8 encoding for console output
2931
if sys.stdout.encoding != 'utf-8': # pragma: no cover
3032
sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding='utf-8')
@@ -210,10 +212,19 @@ def check_azure_cli():
210212
return False, 'Install Azure CLI: https://learn.microsoft.com/cli/azure/install-azure-cli'
211213

212214
try:
213-
result = subprocess.run([az_path, '--version'], capture_output=True, text=True, check=True)
215+
result = subprocess.run(
216+
[az_path, '--version'],
217+
capture_output=True,
218+
text=True,
219+
check=True,
220+
timeout=AZURE_CLI_TIMEOUT_SECONDS,
221+
stdin=subprocess.DEVNULL,
222+
)
214223
version_line = (result.stdout.splitlines() or ['unknown version'])[0].strip()
215224
version = version_line.split()[-1] if version_line else 'unknown'
216225
return True, f'Azure CLI {version} detected'
226+
except subprocess.TimeoutExpired:
227+
return False, f'Azure CLI version check timed out after {AZURE_CLI_TIMEOUT_SECONDS} seconds. Retry verification or run: az --version'
217228
except subprocess.CalledProcessError:
218229
return False, 'Reinstall Azure CLI: https://learn.microsoft.com/cli/azure/install-azure-cli'
219230

@@ -225,7 +236,14 @@ def check_bicep_cli():
225236
return False, 'Install Azure CLI first: https://learn.microsoft.com/cli/azure/install-azure-cli'
226237

227238
try:
228-
result = subprocess.run([az_path, 'bicep', 'version'], capture_output=True, text=True, check=True)
239+
result = subprocess.run(
240+
[az_path, 'bicep', 'version'],
241+
capture_output=True,
242+
text=True,
243+
check=True,
244+
timeout=AZURE_CLI_TIMEOUT_SECONDS,
245+
stdin=subprocess.DEVNULL,
246+
)
229247
version_line = (result.stdout.splitlines() or ['unknown version'])[0].strip()
230248
version = 'unknown'
231249
if 'version' in version_line.lower():
@@ -235,6 +253,8 @@ def check_bicep_cli():
235253
version = parts[index + 1]
236254
break
237255
return True, f'Bicep {version} detected'
256+
except subprocess.TimeoutExpired:
257+
return False, f'Bicep version check timed out after {AZURE_CLI_TIMEOUT_SECONDS} seconds. Retry verification or run: az bicep version'
238258
except subprocess.CalledProcessError:
239259
return False, 'Install Bicep: az bicep install'
240260

@@ -251,12 +271,16 @@ def check_azure_login():
251271
capture_output=True,
252272
text=True,
253273
check=True,
274+
timeout=AZURE_CLI_TIMEOUT_SECONDS,
275+
stdin=subprocess.DEVNULL,
254276
)
255277
account = json.loads(result.stdout)
256278
name = account.get('name', 'unknown')
257279
tenant = account.get('tenantId', 'unknown')
258280
subscription = account.get('id', 'unknown')
259281
return True, f'Logged in (sub: {name}, id: {subscription}, tenant: {tenant})'
282+
except subprocess.TimeoutExpired:
283+
return False, f'Azure login check timed out after {AZURE_CLI_TIMEOUT_SECONDS} seconds. Retry verification or run: az account show'
260284
except (subprocess.CalledProcessError, json.JSONDecodeError):
261285
return False, (
262286
'Log in via the APIM Developer CLI or `az login --tenant <tenant-id>`,'
@@ -292,6 +316,8 @@ def check_azure_providers():
292316
capture_output=True,
293317
text=True,
294318
check=True,
319+
timeout=AZURE_CLI_TIMEOUT_SECONDS,
320+
stdin=subprocess.DEVNULL,
295321
)
296322
registered_providers = sorted(set(json.loads(result.stdout)))
297323

@@ -302,6 +328,11 @@ def check_azure_providers():
302328

303329
fix_cmds = ', '.join([f'az provider register -n {provider}' for provider in missing_providers])
304330
return False, f'Register missing providers: {fix_cmds}'
331+
except subprocess.TimeoutExpired:
332+
return False, (
333+
f'Azure provider check timed out after {AZURE_CLI_TIMEOUT_SECONDS} seconds. '
334+
'Retry verification after Azure CLI responds normally.'
335+
)
305336
except (subprocess.CalledProcessError, json.JSONDecodeError, FileNotFoundError):
306337
return False, 'Log in then retry: az login --tenant <tenant> && az account set --subscription <subscription>'
307338

tests/python/test_verify_local_setup.py

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -512,6 +512,15 @@ def test_check_azure_cli_subprocess_error() -> None:
512512
assert 'Reinstall' in fix
513513

514514

515+
def test_check_azure_cli_timeout() -> None:
516+
"""Azure CLI check should fail instead of hanging when az does not respond."""
517+
with patch('shutil.which', return_value='/usr/bin/az'):
518+
with patch('subprocess.run', side_effect=subprocess.TimeoutExpired('az --version', vls.AZURE_CLI_TIMEOUT_SECONDS)):
519+
ok, fix = vls.check_azure_cli()
520+
assert ok is False
521+
assert 'timed out' in fix
522+
523+
515524
def test_check_azure_cli_empty_version() -> None:
516525
"""Azure CLI check should handle empty version output."""
517526
with patch('shutil.which', return_value='/usr/bin/az'):
@@ -552,6 +561,15 @@ def test_check_bicep_cli_subprocess_error() -> None:
552561
assert 'Install Bicep' in fix
553562

554563

564+
def test_check_bicep_cli_timeout() -> None:
565+
"""Bicep CLI check should fail instead of hanging when az does not respond."""
566+
with patch('shutil.which', return_value='/usr/bin/az'):
567+
with patch('subprocess.run', side_effect=subprocess.TimeoutExpired('az bicep version', vls.AZURE_CLI_TIMEOUT_SECONDS)):
568+
ok, fix = vls.check_bicep_cli()
569+
assert ok is False
570+
assert 'timed out' in fix
571+
572+
555573
@pytest.mark.parametrize(
556574
'stdout,expected_version,should_have_unknown',
557575
[
@@ -596,6 +614,15 @@ def test_check_azure_login_not_logged_in() -> None:
596614
assert 'az login' in fix
597615

598616

617+
def test_check_azure_login_timeout() -> None:
618+
"""Azure login check should fail instead of hanging when az account show stalls."""
619+
with patch('shutil.which', return_value='/usr/bin/az'):
620+
with patch('subprocess.run', side_effect=subprocess.TimeoutExpired('az account show', vls.AZURE_CLI_TIMEOUT_SECONDS)):
621+
ok, fix = vls.check_azure_login()
622+
assert ok is False
623+
assert 'timed out' in fix
624+
625+
599626
def test_check_azure_login_no_cli() -> None:
600627
"""Azure login check should fail when CLI missing."""
601628
with patch('shutil.which', return_value=None):
@@ -648,6 +675,7 @@ def test_check_azure_providers_no_az() -> None:
648675
'exception',
649676
[
650677
subprocess.CalledProcessError(1, 'az'),
678+
subprocess.TimeoutExpired('az provider list', vls.AZURE_CLI_TIMEOUT_SECONDS),
651679
],
652680
)
653681
def test_check_azure_providers_subprocess_error(exception: Exception) -> None:
@@ -656,7 +684,7 @@ def test_check_azure_providers_subprocess_error(exception: Exception) -> None:
656684
with patch('subprocess.run', side_effect=exception):
657685
ok, fix = vls.check_azure_providers()
658686
assert ok is False
659-
assert 'Log in' in fix or 'login' in fix.lower()
687+
assert 'Log in' in fix or 'login' in fix.lower() or 'timed out' in fix
660688

661689

662690
def test_check_azure_providers_json_error() -> None:

0 commit comments

Comments
 (0)