Skip to content

Commit bf12a24

Browse files
Merge branch 'Azure:dev' into feature/container-network-logs
2 parents bd8bba3 + c190617 commit bf12a24

139 files changed

Lines changed: 4064 additions & 454 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/policies/resourceManagement.yml

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -231,7 +231,7 @@ configuration:
231231
then:
232232
- mentionUsers:
233233
mentionees:
234-
- mbifeld
234+
- danielsollondon
235235
- FumingZhang
236236
- andyliuliming
237237
replyTemplate: Thanks for the feedback! We are routing this to the appropriate team for follow-up. cc ${mentionees}.
@@ -1217,6 +1217,9 @@ configuration:
12171217
- jwilder
12181218
- thomas1206
12191219
- seanmck
1220+
- danielsollondon
1221+
- FumingZhang
1222+
- andyliuliming
12201223
replyTemplate: Thanks for the feedback! We are routing this to the appropriate team for follow-up. cc ${mentionees}.
12211224
assignMentionees: False
12221225
- if:

build_scripts/windows/Product.wxs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,8 @@
4949
<UpgradeVersion Property="WIX_DOWNGRADE_DETECTED" Minimum="$(var.ProductVersion)" IncludeMinimum="no" OnlyDetect="yes" />
5050
</Upgrade>
5151
<InstallExecuteSequence>
52-
<RemoveExistingProducts After="InstallExecute" />
52+
<!-- RemoveExistingProducts runs BEFORE InstallInitialize to completely remove old version first. -->
53+
<RemoveExistingProducts Before="InstallInitialize" />
5354
</InstallExecuteSequence>
5455

5556
<!-- New product architectures should upgrade the original x86 product - even of the same version. -->
@@ -74,6 +75,9 @@
7475
<Property Id="MSIFASTINSTALL" Value="7" />
7576
<Property Id="ApplicationFolderName" Value="Microsoft SDKs\Azure" />
7677
<Property Id="WixAppFolder" Value="WixPerMachineFolder" />
78+
79+
<!-- Force reinstall of all files during upgrade -->
80+
<SetProperty Id="REINSTALLMODE" Value="amus" Before="CostFinalize" Sequence="both">WIX_UPGRADE_DETECTED</SetProperty>
7781

7882
<Feature Id="ProductFeature"
7983
Title="Microsoft Azure CLI"

src/azure-cli-core/HISTORY.rst

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,14 @@
33
Release History
44
===============
55

6+
2.83.0
7+
++++++
8+
* Resolve CVE-2025-69277 (#32610)
9+
* Resolve CVE-2026-21441 (#32617)
10+
* Resolve CVE-2025-12084 (#32662)
11+
* Resolve CVE-2026-21226 (#32635)
12+
* Replace GitHub URLs with AME Storage for network isolated requirements (#32677)
13+
614
2.82.0
715
++++++
816
* Resolve CVE-2025-66418 (#32506)

src/azure-cli-core/azure/cli/core/__init__.py

Lines changed: 35 additions & 117 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,11 @@
44
# --------------------------------------------------------------------------------------------
55
# pylint: disable=line-too-long
66

7-
__version__ = "2.82.0"
7+
__version__ = "2.83.0"
88

99
import os
1010
import sys
1111
import timeit
12-
import concurrent.futures
13-
from concurrent.futures import ThreadPoolExecutor
1412

1513
from knack.cli import CLI
1614
from knack.commands import CLICommandsLoader
@@ -36,10 +34,6 @@
3634
ALWAYS_LOADED_MODULES = []
3735
# Extensions that will always be loaded if installed. They don't expose commands but hook into CLI core.
3836
ALWAYS_LOADED_EXTENSIONS = ['azext_ai_examples', 'azext_next']
39-
# Timeout (in seconds) for loading a single module. Acts as a safety valve to prevent indefinite hangs
40-
MODULE_LOAD_TIMEOUT_SECONDS = 30
41-
# Maximum number of worker threads for parallel module loading.
42-
MAX_WORKER_THREAD_COUNT = 4
4337

4438

4539
def _configure_knack():
@@ -203,17 +197,6 @@ def _configure_style(self):
203197
format_styled_text.theme = theme
204198

205199

206-
class ModuleLoadResult: # pylint: disable=too-few-public-methods
207-
def __init__(self, module_name, command_table, group_table, elapsed_time, error=None, traceback_str=None, command_loader=None):
208-
self.module_name = module_name
209-
self.command_table = command_table
210-
self.group_table = group_table
211-
self.elapsed_time = elapsed_time
212-
self.error = error
213-
self.traceback_str = traceback_str
214-
self.command_loader = command_loader
215-
216-
217200
class MainCommandsLoader(CLICommandsLoader):
218201

219202
# Format string for pretty-print the command module table
@@ -258,11 +241,11 @@ def load_command_table(self, args):
258241
import pkgutil
259242
import traceback
260243
from azure.cli.core.commands import (
261-
_load_extension_command_loader, ExtensionCommandSource)
244+
_load_module_command_loader, _load_extension_command_loader, BLOCKED_MODS, ExtensionCommandSource)
262245
from azure.cli.core.extension import (
263246
get_extensions, get_extension_path, get_extension_modname)
264247
from azure.cli.core.breaking_change import (
265-
import_core_breaking_changes, import_extension_breaking_changes)
248+
import_core_breaking_changes, import_module_breaking_changes, import_extension_breaking_changes)
266249

267250
def _update_command_table_from_modules(args, command_modules=None):
268251
"""Loads command tables from modules and merge into the main command table.
@@ -290,10 +273,38 @@ def _update_command_table_from_modules(args, command_modules=None):
290273
except ImportError as e:
291274
logger.warning(e)
292275

293-
results = self._load_modules(args, command_modules)
276+
count = 0
277+
cumulative_elapsed_time = 0
278+
cumulative_group_count = 0
279+
cumulative_command_count = 0
280+
logger.debug("Loading command modules:")
281+
logger.debug(self.header_mod)
294282

295-
count, cumulative_elapsed_time, cumulative_group_count, cumulative_command_count = \
296-
self._process_results_with_timing(results)
283+
for mod in [m for m in command_modules if m not in BLOCKED_MODS]:
284+
try:
285+
start_time = timeit.default_timer()
286+
module_command_table, module_group_table = _load_module_command_loader(self, args, mod)
287+
import_module_breaking_changes(mod)
288+
for cmd in module_command_table.values():
289+
cmd.command_source = mod
290+
self.command_table.update(module_command_table)
291+
self.command_group_table.update(module_group_table)
292+
293+
elapsed_time = timeit.default_timer() - start_time
294+
logger.debug(self.item_format_string, mod, elapsed_time,
295+
len(module_group_table), len(module_command_table))
296+
count += 1
297+
cumulative_elapsed_time += elapsed_time
298+
cumulative_group_count += len(module_group_table)
299+
cumulative_command_count += len(module_command_table)
300+
except Exception as ex: # pylint: disable=broad-except
301+
# Changing this error message requires updating CI script that checks for failed
302+
# module loading.
303+
from azure.cli.core import telemetry
304+
logger.error("Error loading command module '%s': %s", mod, ex)
305+
telemetry.set_exception(exception=ex, fault_type='module-load-error-' + mod,
306+
summary='Error loading module: {}'.format(mod))
307+
logger.debug(traceback.format_exc())
297308
# Summary line
298309
logger.debug(self.item_format_string,
299310
"Total ({})".format(count), cumulative_elapsed_time,
@@ -367,7 +378,7 @@ def _filter_modname(extensions):
367378
# from an extension requires this map to be up-to-date.
368379
# self._mod_to_ext_map[ext_mod] = ext_name
369380
start_time = timeit.default_timer()
370-
extension_command_table, extension_group_table, _ = \
381+
extension_command_table, extension_group_table = \
371382
_load_extension_command_loader(self, args, ext_mod)
372383
import_extension_breaking_changes(ext_mod)
373384

@@ -576,99 +587,6 @@ def load_arguments(self, command=None):
576587
self.extra_argument_registry.update(loader.extra_argument_registry)
577588
loader._update_command_definitions() # pylint: disable=protected-access
578589

579-
def _load_modules(self, args, command_modules):
580-
"""Load command modules using ThreadPoolExecutor with timeout protection."""
581-
from azure.cli.core.commands import BLOCKED_MODS
582-
583-
results = []
584-
with ThreadPoolExecutor(max_workers=MAX_WORKER_THREAD_COUNT) as executor:
585-
future_to_module = {executor.submit(self._load_single_module, mod, args): mod
586-
for mod in command_modules if mod not in BLOCKED_MODS}
587-
588-
for future in concurrent.futures.as_completed(future_to_module):
589-
try:
590-
result = future.result(timeout=MODULE_LOAD_TIMEOUT_SECONDS)
591-
results.append(result)
592-
except concurrent.futures.TimeoutError:
593-
mod = future_to_module[future]
594-
logger.warning("Module '%s' load timeout after %s seconds", mod, MODULE_LOAD_TIMEOUT_SECONDS)
595-
results.append(ModuleLoadResult(mod, {}, {}, 0,
596-
Exception(f"Module '{mod}' load timeout")))
597-
except (ImportError, AttributeError, TypeError, ValueError) as ex:
598-
mod = future_to_module[future]
599-
logger.warning("Module '%s' load failed: %s", mod, ex)
600-
results.append(ModuleLoadResult(mod, {}, {}, 0, ex))
601-
except Exception as ex: # pylint: disable=broad-exception-caught
602-
mod = future_to_module[future]
603-
logger.warning("Module '%s' load failed with unexpected exception: %s", mod, ex)
604-
results.append(ModuleLoadResult(mod, {}, {}, 0, ex))
605-
606-
return results
607-
608-
def _load_single_module(self, mod, args):
609-
from azure.cli.core.breaking_change import import_module_breaking_changes
610-
from azure.cli.core.commands import _load_module_command_loader
611-
import traceback
612-
try:
613-
start_time = timeit.default_timer()
614-
module_command_table, module_group_table, command_loader = _load_module_command_loader(self, args, mod)
615-
import_module_breaking_changes(mod)
616-
elapsed_time = timeit.default_timer() - start_time
617-
return ModuleLoadResult(mod, module_command_table, module_group_table, elapsed_time, command_loader=command_loader)
618-
except Exception as ex: # pylint: disable=broad-except
619-
tb_str = traceback.format_exc()
620-
return ModuleLoadResult(mod, {}, {}, 0, ex, tb_str)
621-
622-
def _handle_module_load_error(self, result):
623-
"""Handle errors that occurred during module loading."""
624-
from azure.cli.core import telemetry
625-
626-
logger.error("Error loading command module '%s': %s", result.module_name, result.error)
627-
telemetry.set_exception(exception=result.error,
628-
fault_type='module-load-error-' + result.module_name,
629-
summary='Error loading module: {}'.format(result.module_name))
630-
if result.traceback_str:
631-
logger.debug(result.traceback_str)
632-
633-
def _process_successful_load(self, result):
634-
"""Process successfully loaded module results."""
635-
if result.command_loader:
636-
self.loaders.append(result.command_loader)
637-
638-
for cmd in result.command_table:
639-
self.cmd_to_loader_map[cmd] = [result.command_loader]
640-
641-
for cmd in result.command_table.values():
642-
cmd.command_source = result.module_name
643-
644-
self.command_table.update(result.command_table)
645-
self.command_group_table.update(result.group_table)
646-
647-
logger.debug(self.item_format_string, result.module_name, result.elapsed_time,
648-
len(result.group_table), len(result.command_table))
649-
650-
def _process_results_with_timing(self, results):
651-
"""Process pre-loaded module results with timing and progress reporting."""
652-
logger.debug("Loading command modules:")
653-
logger.debug(self.header_mod)
654-
655-
count = 0
656-
cumulative_elapsed_time = 0
657-
cumulative_group_count = 0
658-
cumulative_command_count = 0
659-
660-
for result in results:
661-
if result.error:
662-
self._handle_module_load_error(result)
663-
else:
664-
self._process_successful_load(result)
665-
count += 1
666-
cumulative_elapsed_time += result.elapsed_time
667-
cumulative_group_count += len(result.group_table)
668-
cumulative_command_count += len(result.command_table)
669-
670-
return count, cumulative_elapsed_time, cumulative_group_count, cumulative_command_count
671-
672590

673591
class CommandIndex:
674592

src/azure-cli-core/azure/cli/core/cloud.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -373,7 +373,7 @@ class CloudNameEnum: # pylint: disable=too-few-public-methods
373373
active_directory_graph_resource_id='https://graph.windows.net/',
374374
microsoft_graph_resource_id='https://graph.microsoft.com/',
375375
active_directory_data_lake_resource_id='https://datalake.azure.net/',
376-
vm_image_alias_doc='https://raw.githubusercontent.com/Azure/azure-rest-api-specs/main/arm-compute/quickstart-templates/aliases.json',
376+
vm_image_alias_doc='https://azcliprod.blob.core.windows.net/cli/vm/aliases.json',
377377
media_resource_id='https://rest.media.azure.net',
378378
ossrdbms_resource_id='https://ossrdbms-aad.database.windows.net',
379379
app_insights_resource_id='https://api.applicationinsights.io',
@@ -410,7 +410,7 @@ class CloudNameEnum: # pylint: disable=too-few-public-methods
410410
active_directory_resource_id='https://management.core.chinacloudapi.cn/',
411411
active_directory_graph_resource_id='https://graph.chinacloudapi.cn/',
412412
microsoft_graph_resource_id='https://microsoftgraph.chinacloudapi.cn',
413-
vm_image_alias_doc='https://raw.githubusercontent.com/Azure/azure-rest-api-specs/main/arm-compute/quickstart-templates/aliases.json',
413+
vm_image_alias_doc='https://azcliprod.blob.core.windows.net/cli/vm/aliases.json',
414414
media_resource_id='https://rest.media.chinacloudapi.cn',
415415
ossrdbms_resource_id='https://ossrdbms-aad.database.chinacloudapi.cn',
416416
app_insights_resource_id='https://api.applicationinsights.azure.cn',
@@ -444,7 +444,7 @@ class CloudNameEnum: # pylint: disable=too-few-public-methods
444444
active_directory_resource_id='https://management.core.usgovcloudapi.net/',
445445
active_directory_graph_resource_id='https://graph.microsoftazure.us/',
446446
microsoft_graph_resource_id='https://graph.microsoft.us/',
447-
vm_image_alias_doc='https://raw.githubusercontent.com/Azure/azure-rest-api-specs/main/arm-compute/quickstart-templates/aliases.json',
447+
vm_image_alias_doc='https://azcliprod.blob.core.windows.net/cli/vm/aliases.json',
448448
media_resource_id='https://rest.media.usgovcloudapi.net',
449449
ossrdbms_resource_id='https://ossrdbms-aad.database.usgovcloudapi.net',
450450
app_insights_resource_id='https://api.applicationinsights.us',
@@ -479,7 +479,7 @@ class CloudNameEnum: # pylint: disable=too-few-public-methods
479479
active_directory_resource_id='https://management.core.cloudapi.de/',
480480
active_directory_graph_resource_id='https://graph.cloudapi.de/',
481481
microsoft_graph_resource_id='https://graph.microsoft.de',
482-
vm_image_alias_doc='https://raw.githubusercontent.com/Azure/azure-rest-api-specs/main/arm-compute/quickstart-templates/aliases.json',
482+
vm_image_alias_doc='https://azcliprod.blob.core.windows.net/cli/vm/aliases.json',
483483
media_resource_id='https://rest.media.cloudapi.de',
484484
ossrdbms_resource_id='https://ossrdbms-aad.database.cloudapi.de',
485485
portal='https://portal.microsoftazure.de'),

src/azure-cli-core/azure/cli/core/commands/__init__.py

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1134,17 +1134,22 @@ def _load_command_loader(loader, args, name, prefix):
11341134
logger.debug("Module '%s' is missing `get_command_loader` entry.", name)
11351135

11361136
command_table = {}
1137-
command_loader = None
11381137

11391138
if loader_cls:
11401139
command_loader = loader_cls(cli_ctx=loader.cli_ctx)
1140+
loader.loaders.append(command_loader) # This will be used by interactive
11411141
if command_loader.supported_resource_type():
11421142
command_table = command_loader.load_command_table(args)
1143+
if command_table:
1144+
for cmd in list(command_table.keys()):
1145+
# TODO: If desired to for extension to patch module, this can be uncommented
1146+
# if loader.cmd_to_loader_map.get(cmd):
1147+
# loader.cmd_to_loader_map[cmd].append(command_loader)
1148+
# else:
1149+
loader.cmd_to_loader_map[cmd] = [command_loader]
11431150
else:
11441151
logger.debug("Module '%s' is missing `COMMAND_LOADER_CLS` entry.", name)
1145-
1146-
group_table = command_loader.command_group_table if command_loader else {}
1147-
return command_table, group_table, command_loader
1152+
return command_table, command_loader.command_group_table
11481153

11491154

11501155
def _load_extension_command_loader(loader, args, ext):

src/azure-cli-core/azure/cli/core/tests/test_command_registration.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -230,7 +230,7 @@ def load_command_table(self, args):
230230
if command_table:
231231
module_command_table.update(command_table)
232232
loader.loaders.append(command_loader) # this will be used later by the load_arguments method
233-
return module_command_table, command_loader.command_group_table, command_loader
233+
return module_command_table, command_loader.command_group_table
234234

235235
expected_command_index = {'hello': ['azure.cli.command_modules.hello', 'azext_hello2', 'azext_hello1'],
236236
'extra': ['azure.cli.command_modules.extra']}
@@ -432,12 +432,12 @@ def test_command_index_positional_argument(self):
432432
# Test command index is built for command with positional argument
433433
cmd_tbl = loader.load_command_table(["extra", "extra", "positional_argument"])
434434
self.assertDictEqual(INDEX[CommandIndex._COMMAND_INDEX], self.expected_command_index)
435-
self.assertSetEqual(set(cmd_tbl), {'hello mod-only', 'hello overridden', 'extra final', 'hello ext-only'})
435+
self.assertEqual(list(cmd_tbl), ['hello mod-only', 'hello overridden', 'extra final', 'hello ext-only'])
436436

437437
# Test command index is used by command with positional argument
438438
cmd_tbl = loader.load_command_table(["hello", "mod-only", "positional_argument"])
439439
self.assertDictEqual(INDEX[CommandIndex._COMMAND_INDEX], self.expected_command_index)
440-
self.assertSetEqual(set(cmd_tbl), {'hello mod-only', 'hello overridden', 'hello ext-only'})
440+
self.assertEqual(list(cmd_tbl), ['hello mod-only', 'hello overridden', 'hello ext-only'])
441441

442442
# Test command index is used by command with positional argument
443443
cmd_tbl = loader.load_command_table(["extra", "final", "positional_argument2"])

0 commit comments

Comments
 (0)