From 731431929a43427f18b13dc80bfe68b7493ea4da Mon Sep 17 00:00:00 2001 From: Christoph Butz Date: Wed, 1 Jul 2026 13:43:03 +0100 Subject: [PATCH 01/14] [aks-preview] Rename OpenTelemetry metrics/logs port and enable/disable flags with deprecated aliases Renames the OTLP CLI parameters on 'az aks create/update' to distinguish the HTTP/protobuf port and to align logs with logs-and-traces, keeping the old names working as deprecated aliases: - --opentelemetry-metrics-port -> --opentelemetry-metrics-port-http - --opentelemetry-logs-port -> --opentelemetry-logs-traces-port-http - --enable-opentelemetry-logs -> --enable-opentelemetry-logs-traces - --disable-opentelemetry-logs -> --disable-opentelemetry-logs-traces Each renamed option is registered as two arguments sharing one destination: the new name carries is_preview=True and the old name is a separate deprecated argument (deprecate_info). This avoids the knack crash from combining is_preview with an option-level c.deprecate (Deprecated has no __len__), and lets the new names show [Preview] while the old names show [Deprecated] and redirect. A validator merges any value supplied via a deprecated alias onto the current destination so downstream logic reads a single dest. Updates help text, validator/decorator error messages, and tests to the new names; migrates the live integration scenarios and fixes stale API field assertions (openTelemetryLogs.port -> openTelemetryLogsAndTraces.httpPort). --- src/aks-preview/azext_aks_preview/_help.py | 56 ++++---- src/aks-preview/azext_aks_preview/_params.py | 101 ++++++++++++-- .../azext_aks_preview/_validators.py | 34 ++++- src/aks-preview/azext_aks_preview/custom.py | 10 ++ .../managed_cluster_decorator.py | 29 ++-- .../tests/latest/test_aks_commands.py | 129 ++++++++++++++---- .../tests/latest/test_validators.py | 65 ++++++++- 7 files changed, 334 insertions(+), 90 deletions(-) diff --git a/src/aks-preview/azext_aks_preview/_help.py b/src/aks-preview/azext_aks_preview/_help.py index 45cd68ef61b..dd8d04994d8 100644 --- a/src/aks-preview/azext_aks_preview/_help.py +++ b/src/aks-preview/azext_aks_preview/_help.py @@ -630,21 +630,21 @@ - name: --enable-opentelemetry-metrics type: bool short-summary: Enable OpenTelemetry metrics collection. Requires Azure Monitor metrics to be enabled. - - name: --opentelemetry-metrics-port + - name: --opentelemetry-metrics-port-http type: int - short-summary: Port for OpenTelemetry metrics collection (default port will be used if not specified) + short-summary: HTTP/protobuf port for OpenTelemetry metrics collection (default port will be used if not specified) - name: --disable-opentelemetry-metrics type: bool short-summary: Disable OpenTelemetry metrics collection - - name: --enable-opentelemetry-logs + - name: --enable-opentelemetry-logs-traces type: bool - short-summary: Enable OpenTelemetry logs collection. Requires Azure Monitor logs to be enabled. - - name: --opentelemetry-logs-port + short-summary: Enable OpenTelemetry logs and traces collection. Requires Azure Monitor logs to be enabled. + - name: --opentelemetry-logs-traces-port-http type: int - short-summary: Port for OpenTelemetry logs collection (default port will be used if not specified) - - name: --disable-opentelemetry-logs + short-summary: HTTP/protobuf port for OpenTelemetry logs and traces collection (default port will be used if not specified) + - name: --disable-opentelemetry-logs-traces type: bool - short-summary: Disable OpenTelemetry logs collection + short-summary: Disable OpenTelemetry logs and traces collection - name: --nodepool-labels type: string short-summary: The node labels for all node pools in this cluster. See https://aka.ms/node-labels for syntax of labels. @@ -844,14 +844,14 @@ text: az aks create -g MyResourceGroup -n MyManagedCluster --enable-azure-monitor-app-monitoring - name: Create a kubernetes cluster with OpenTelemetry metrics collection enabled text: az aks create -g MyResourceGroup -n MyManagedCluster --enable-opentelemetry-metrics --enable-azuremonitormetrics - - name: Create a kubernetes cluster with OpenTelemetry logs collection enabled - text: az aks create -g MyResourceGroup -n MyManagedCluster --enable-opentelemetry-logs --enable-addons monitoring + - name: Create a kubernetes cluster with OpenTelemetry logs and traces collection enabled + text: az aks create -g MyResourceGroup -n MyManagedCluster --enable-opentelemetry-logs-traces --enable-addons monitoring - name: Create a kubernetes cluster with Azure Monitor logs enabled (shorthand) text: az aks create -g MyResourceGroup -n MyManagedCluster --enable-azure-monitor-logs - name: Create a kubernetes cluster with OpenTelemetry metrics on custom port - text: az aks create -g MyResourceGroup -n MyManagedCluster --enable-opentelemetry-metrics --opentelemetry-metrics-port 8888 --enable-azuremonitormetrics - - name: Create a kubernetes cluster with OpenTelemetry logs on custom port - text: az aks create -g MyResourceGroup -n MyManagedCluster --enable-opentelemetry-logs --opentelemetry-logs-port 4317 --enable-azure-monitor-logs + text: az aks create -g MyResourceGroup -n MyManagedCluster --enable-opentelemetry-metrics --opentelemetry-metrics-port-http 8888 --enable-azuremonitormetrics + - name: Create a kubernetes cluster with OpenTelemetry logs and traces on custom port + text: az aks create -g MyResourceGroup -n MyManagedCluster --enable-opentelemetry-logs-traces --opentelemetry-logs-traces-port-http 4317 --enable-azure-monitor-logs - name: Create a kubernetes cluster with a nodepool having ip allocation mode set to "StaticBlock" text: az aks create -g MyResourceGroup -n MyManagedCluster --os-sku Ubuntu --max-pods MaxPodsPerNode --network-plugin azure --vnet-subnet-id /subscriptions/00000/resourceGroups/AnotherResourceGroup/providers/Microsoft.Network/virtualNetworks/MyVnet/subnets/NodeSubnet --pod-subnet-id /subscriptions/00000/resourceGroups/AnotherResourceGroup/providers/Microsoft.Network/virtualNetworks/MyVnet/subnets/PodSubnet --pod-ip-allocation-mode StaticBlock - name: Create a kubernetes cluster with a VirtualMachines nodepool @@ -1398,21 +1398,21 @@ - name: --enable-opentelemetry-metrics type: bool short-summary: Enable OpenTelemetry metrics collection. Requires Azure Monitor metrics to be enabled. - - name: --opentelemetry-metrics-port + - name: --opentelemetry-metrics-port-http type: int - short-summary: Port for OpenTelemetry metrics collection (default port will be used if not specified) + short-summary: HTTP/protobuf port for OpenTelemetry metrics collection (default port will be used if not specified) - name: --disable-opentelemetry-metrics type: bool short-summary: Disable OpenTelemetry metrics collection - - name: --enable-opentelemetry-logs + - name: --enable-opentelemetry-logs-traces type: bool - short-summary: Enable OpenTelemetry logs collection. Requires Azure Monitor logs to be enabled. - - name: --opentelemetry-logs-port + short-summary: Enable OpenTelemetry logs and traces collection. Requires Azure Monitor logs to be enabled. + - name: --opentelemetry-logs-traces-port-http type: int - short-summary: Port for OpenTelemetry logs collection (default port will be used if not specified) - - name: --disable-opentelemetry-logs + short-summary: HTTP/protobuf port for OpenTelemetry logs and traces collection (default port will be used if not specified) + - name: --disable-opentelemetry-logs-traces type: bool - short-summary: Disable OpenTelemetry logs collection + short-summary: Disable OpenTelemetry logs and traces collection - name: --enable-private-cluster type: bool short-summary: Enable private cluster for apiserver vnet integration cluster. @@ -1654,16 +1654,16 @@ text: az aks update -g MyResourceGroup -n MyManagedCluster --disable-gateway-api - name: Enable OpenTelemetry metrics collection on an existing cluster text: az aks update -g MyResourceGroup -n MyManagedCluster --enable-opentelemetry-metrics - - name: Enable OpenTelemetry logs collection on an existing cluster - text: az aks update -g MyResourceGroup -n MyManagedCluster --enable-opentelemetry-logs + - name: Enable OpenTelemetry logs and traces collection on an existing cluster + text: az aks update -g MyResourceGroup -n MyManagedCluster --enable-opentelemetry-logs-traces - name: Configure OpenTelemetry metrics with custom port - text: az aks update -g MyResourceGroup -n MyManagedCluster --enable-opentelemetry-metrics --opentelemetry-metrics-port 8888 - - name: Configure OpenTelemetry logs with custom port - text: az aks update -g MyResourceGroup -n MyManagedCluster --enable-opentelemetry-logs --opentelemetry-logs-port 4317 + text: az aks update -g MyResourceGroup -n MyManagedCluster --enable-opentelemetry-metrics --opentelemetry-metrics-port-http 8888 + - name: Configure OpenTelemetry logs and traces with custom port + text: az aks update -g MyResourceGroup -n MyManagedCluster --enable-opentelemetry-logs-traces --opentelemetry-logs-traces-port-http 4317 - name: Disable OpenTelemetry metrics collection on an existing cluster text: az aks update -g MyResourceGroup -n MyManagedCluster --disable-opentelemetry-metrics - - name: Disable OpenTelemetry logs collection on an existing cluster - text: az aks update -g MyResourceGroup -n MyManagedCluster --disable-opentelemetry-logs + - name: Disable OpenTelemetry logs and traces collection on an existing cluster + text: az aks update -g MyResourceGroup -n MyManagedCluster --disable-opentelemetry-logs-traces """ helps['aks kollect'] = """ diff --git a/src/aks-preview/azext_aks_preview/_params.py b/src/aks-preview/azext_aks_preview/_params.py index 5db313112ca..8ff293acd9a 100644 --- a/src/aks-preview/azext_aks_preview/_params.py +++ b/src/aks-preview/azext_aks_preview/_params.py @@ -1161,30 +1161,71 @@ def load_arguments(self, _): validator=validate_azure_monitor_and_opentelemetry_for_create ) c.argument("opentelemetry_metrics_port", + options_list=["--opentelemetry-metrics-port-http"], is_preview=True, type=int, - help="Port for OpenTelemetry metrics collection" + help="HTTP/protobuf port for OpenTelemetry metrics collection" ) + c.argument("opentelemetry_metrics_port_deprecated", + options_list=["--opentelemetry-metrics-port"], + deprecate_info=c.deprecate( + target="--opentelemetry-metrics-port", + redirect="--opentelemetry-metrics-port-http", + ), + type=int, + help="HTTP/protobuf port for OpenTelemetry metrics collection" + ) + c.argument("disable_opentelemetry_metrics", is_preview=True, action="store_true", help="Disable OpenTelemetry metrics collection" ) c.argument("enable_opentelemetry_logs", - options_list=["--enable-opentelemetry-logs"], + options_list=["--enable-opentelemetry-logs-traces"], is_preview=True, action="store_true", - help="Enable OpenTelemetry logs collection" + help="Enable OpenTelemetry logs and traces collection" + ) + c.argument("enable_opentelemetry_logs_deprecated", + options_list=["--enable-opentelemetry-logs"], + deprecate_info=c.deprecate( + target="--enable-opentelemetry-logs", + redirect="--enable-opentelemetry-logs-traces", + ), + action="store_true", + help="Enable OpenTelemetry logs and traces collection" ) c.argument("opentelemetry_logs_port", + options_list=["--opentelemetry-logs-traces-port-http"], is_preview=True, type=int, - help="Port for OpenTelemetry logs collection" + help="HTTP/protobuf port for OpenTelemetry logs and traces collection" + ) + c.argument("opentelemetry_logs_port_deprecated", + options_list=["--opentelemetry-logs-port"], + deprecate_info=c.deprecate( + target="--opentelemetry-logs-port", + redirect="--opentelemetry-logs-traces-port-http", + ), + type=int, + help="HTTP/protobuf port for OpenTelemetry logs and traces collection" ) + c.argument("disable_opentelemetry_logs", + options_list=["--disable-opentelemetry-logs-traces"], is_preview=True, action="store_true", - help="Disable OpenTelemetry logs collection" + help="Disable OpenTelemetry logs and traces collection" + ) + c.argument("disable_opentelemetry_logs_deprecated", + options_list=["--disable-opentelemetry-logs"], + deprecate_info=c.deprecate( + target="--disable-opentelemetry-logs", + redirect="--disable-opentelemetry-logs-traces", + ), + action="store_true", + help="Disable OpenTelemetry logs and traces collection" ) c.argument("enable_cost_analysis", action="store_true" @@ -1765,29 +1806,71 @@ def load_arguments(self, _): validator=validate_azure_monitor_and_opentelemetry_for_update ) c.argument("opentelemetry_metrics_port", + options_list=["--opentelemetry-metrics-port-http"], is_preview=True, type=int, - help="Port for OpenTelemetry metrics collection" + help="HTTP/protobuf port for OpenTelemetry metrics collection" ) + c.argument("opentelemetry_metrics_port_deprecated", + options_list=["--opentelemetry-metrics-port"], + deprecate_info=c.deprecate( + target="--opentelemetry-metrics-port", + redirect="--opentelemetry-metrics-port-http", + ), + type=int, + help="HTTP/protobuf port for OpenTelemetry metrics collection" + ) + c.argument("disable_opentelemetry_metrics", is_preview=True, action="store_true", help="Disable OpenTelemetry metrics collection" ) c.argument("enable_opentelemetry_logs", + options_list=["--enable-opentelemetry-logs-traces"], is_preview=True, action="store_true", - help="Enable OpenTelemetry logs collection" + help="Enable OpenTelemetry logs and traces collection" + ) + c.argument("enable_opentelemetry_logs_deprecated", + options_list=["--enable-opentelemetry-logs"], + deprecate_info=c.deprecate( + target="--enable-opentelemetry-logs", + redirect="--enable-opentelemetry-logs-traces", + ), + action="store_true", + help="Enable OpenTelemetry logs and traces collection" ) c.argument("opentelemetry_logs_port", + options_list=["--opentelemetry-logs-traces-port-http"], is_preview=True, type=int, - help="Port for OpenTelemetry logs collection" + help="HTTP/protobuf port for OpenTelemetry logs and traces collection" ) + c.argument("opentelemetry_logs_port_deprecated", + options_list=["--opentelemetry-logs-port"], + deprecate_info=c.deprecate( + target="--opentelemetry-logs-port", + redirect="--opentelemetry-logs-traces-port-http", + ), + type=int, + help="HTTP/protobuf port for OpenTelemetry logs and traces collection" + ) + c.argument("disable_opentelemetry_logs", + options_list=["--disable-opentelemetry-logs-traces"], is_preview=True, action="store_true", - help="Disable OpenTelemetry logs collection" + help="Disable OpenTelemetry logs and traces collection" + ) + c.argument("disable_opentelemetry_logs_deprecated", + options_list=["--disable-opentelemetry-logs"], + deprecate_info=c.deprecate( + target="--disable-opentelemetry-logs", + redirect="--disable-opentelemetry-logs-traces", + ), + action="store_true", + help="Disable OpenTelemetry logs and traces collection" ) c.argument( "enable_vpa", diff --git a/src/aks-preview/azext_aks_preview/_validators.py b/src/aks-preview/azext_aks_preview/_validators.py index b84add3f8bb..73d713fd93e 100644 --- a/src/aks-preview/azext_aks_preview/_validators.py +++ b/src/aks-preview/azext_aks_preview/_validators.py @@ -1093,7 +1093,7 @@ def validate_opentelemetry_ports(namespace): if metrics_port is not None and logs_port is not None and metrics_port == logs_port: raise ArgumentUsageError( "OpenTelemetry metrics port and logs port cannot be the same. " - "Please specify different ports for --opentelemetry-metrics-port and --opentelemetry-logs-port." + "Please specify different ports for --opentelemetry-metrics-port-http and --opentelemetry-logs-traces-port-http." ) # Validate port ranges @@ -1154,7 +1154,7 @@ def validate_opentelemetry_logs_dependencies(namespace): # Check mutual exclusion if enable_otlp_logs and disable_otlp_logs: raise MutuallyExclusiveArgumentError( - "Cannot specify both --enable-opentelemetry-logs and --disable-opentelemetry-logs at the same time." + "Cannot specify both --enable-opentelemetry-logs-traces and --disable-opentelemetry-logs-traces at the same time." ) # Check if trying to enable OTLP logs without Azure Monitor @@ -1179,14 +1179,41 @@ def validate_opentelemetry_logs_dependencies_for_update(namespace): # Check mutual exclusion if enable_otlp_logs and disable_otlp_logs: raise MutuallyExclusiveArgumentError( - "Cannot specify both --enable-opentelemetry-logs and --disable-opentelemetry-logs at the same time." + "Cannot specify both --enable-opentelemetry-logs-traces and --disable-opentelemetry-logs-traces at the same time." ) # For update operations, validation is deferred to the decorator where we have access # to the cluster's Azure Monitor profile +def _merge_opentelemetry_deprecated_aliases(namespace): + """Merge deprecated OpenTelemetry alias dests into their current dests. + + The old option names are registered as separate arguments (so the new names can carry the + preview tag while the old names carry the deprecation tag). This copies any value supplied via + a deprecated alias onto the current dest, so all downstream validation and decorator logic only + needs to read the current dests. + """ + # (current_dest, deprecated_dest, is_flag) + pairs = [ + ("opentelemetry_metrics_port", "opentelemetry_metrics_port_deprecated", False), + ("opentelemetry_logs_port", "opentelemetry_logs_port_deprecated", False), + ("enable_opentelemetry_logs", "enable_opentelemetry_logs_deprecated", True), + ("disable_opentelemetry_logs", "disable_opentelemetry_logs_deprecated", True), + ] + for current, deprecated, is_flag in pairs: + if not hasattr(namespace, deprecated): + continue + deprecated_value = getattr(namespace, deprecated) + if is_flag: + if deprecated_value: + setattr(namespace, current, True) + elif deprecated_value is not None and getattr(namespace, current, None) is None: + setattr(namespace, current, deprecated_value) + + def validate_azure_monitor_and_opentelemetry_for_create(namespace): """Main validator for Azure Monitor and OpenTelemetry configurations for create operations.""" + _merge_opentelemetry_deprecated_aliases(namespace) # Run all OpenTelemetry-related validations validate_opentelemetry_ports(namespace) validate_opentelemetry_metrics_dependencies(namespace) @@ -1195,6 +1222,7 @@ def validate_azure_monitor_and_opentelemetry_for_create(namespace): def validate_azure_monitor_and_opentelemetry_for_update(namespace): """Main validator for Azure Monitor and OpenTelemetry configurations for update operations.""" + _merge_opentelemetry_deprecated_aliases(namespace) # Run all OpenTelemetry-related validations validate_opentelemetry_ports(namespace) validate_opentelemetry_metrics_dependencies_for_update(namespace) diff --git a/src/aks-preview/azext_aks_preview/custom.py b/src/aks-preview/azext_aks_preview/custom.py index 89db7e706d4..b48a2d5073b 100644 --- a/src/aks-preview/azext_aks_preview/custom.py +++ b/src/aks-preview/azext_aks_preview/custom.py @@ -1399,6 +1399,11 @@ def aks_create( enable_opentelemetry_logs=False, opentelemetry_logs_port=None, disable_opentelemetry_logs=False, + # opentelemetry deprecated aliases (merged into the parameters above by the validator) + opentelemetry_metrics_port_deprecated=None, + enable_opentelemetry_logs_deprecated=False, + opentelemetry_logs_port_deprecated=None, + disable_opentelemetry_logs_deprecated=False, # metrics profile enable_cost_analysis=False, # AI toolchain operator @@ -1642,6 +1647,11 @@ def aks_update( enable_opentelemetry_logs=False, opentelemetry_logs_port=None, disable_opentelemetry_logs=False, + # opentelemetry deprecated aliases (merged into the parameters above by the validator) + opentelemetry_metrics_port_deprecated=None, + enable_opentelemetry_logs_deprecated=False, + opentelemetry_logs_port_deprecated=None, + disable_opentelemetry_logs_deprecated=False, enable_vpa=False, disable_vpa=False, enable_optimized_addon_scaling=False, diff --git a/src/aks-preview/azext_aks_preview/managed_cluster_decorator.py b/src/aks-preview/azext_aks_preview/managed_cluster_decorator.py index cbf374a13e2..9796c7070f3 100644 --- a/src/aks-preview/azext_aks_preview/managed_cluster_decorator.py +++ b/src/aks-preview/azext_aks_preview/managed_cluster_decorator.py @@ -3012,19 +3012,19 @@ def get_opentelemetry_metrics_port(self) -> Union[int, None]: # Validate that port is not negative if opentelemetry_metrics_port < 0: raise InvalidArgumentValueError( - "--opentelemetry-metrics-port must be a non-negative integer." + "--opentelemetry-metrics-port-http must be a non-negative integer." ) # Check if disabling Azure Monitor metrics - port specification is invalid if self.get_disable_azure_monitor_metrics(): raise InvalidArgumentValueError( - "--opentelemetry-metrics-port cannot be specified when --disable-azure-monitor-metrics is used." + "--opentelemetry-metrics-port-http cannot be specified when --disable-azure-monitor-metrics is used." ) # For CREATE: --enable-opentelemetry-metrics must be explicitly specified if self.decorator_mode == DecoratorMode.CREATE: if not self.get_enable_opentelemetry_metrics(): raise InvalidArgumentValueError( - "--opentelemetry-metrics-port can only be specified when " + "--opentelemetry-metrics-port-http can only be specified when " "--enable-opentelemetry-metrics is also specified." ) # For UPDATE: allow if either explicitly enabling OR already enabled in cluster @@ -3039,7 +3039,7 @@ def get_opentelemetry_metrics_port(self) -> Union[int, None]: ) if not explicitly_enabling and not already_enabled: raise InvalidArgumentValueError( - "--opentelemetry-metrics-port can only be specified when " + "--opentelemetry-metrics-port-http can only be specified when " "--enable-opentelemetry-metrics is also specified or " "OpenTelemetry metrics are already enabled." ) @@ -3061,8 +3061,8 @@ def _get_enable_opentelemetry_logs(self, enable_validation: bool = False) -> boo if enable_opentelemetry_logs and self._get_disable_opentelemetry_logs( enable_validation=False): raise MutuallyExclusiveArgumentError( - "Cannot specify --enable-opentelemetry-logs and " - "--disable-opentelemetry-logs at the same time." + "Cannot specify --enable-opentelemetry-logs-traces and " + "--disable-opentelemetry-logs-traces at the same time." ) # For update operations, validate that Azure Monitor logs is enabled @@ -3126,7 +3126,8 @@ def _get_disable_opentelemetry_logs(self, enable_validation: bool = False) -> bo if enable_validation: if disable_opentelemetry_logs and self._get_enable_opentelemetry_logs(enable_validation=False): raise MutuallyExclusiveArgumentError( - "Cannot specify --enable-opentelemetry-logs and --disable-opentelemetry-logs at the same time." + "Cannot specify --enable-opentelemetry-logs-traces and " + "--disable-opentelemetry-logs-traces at the same time." ) return disable_opentelemetry_logs if disable_opentelemetry_logs is not None else False @@ -3149,20 +3150,20 @@ def get_opentelemetry_logs_port(self) -> Union[int, None]: # Validate that port is not negative if opentelemetry_logs_port < 0: raise InvalidArgumentValueError( - "--opentelemetry-logs-port must be a non-negative integer." + "--opentelemetry-logs-traces-port-http must be a non-negative integer." ) # Check if disabling Azure Monitor logs - port specification is invalid if self.get_disable_azure_monitor_logs(): raise InvalidArgumentValueError( - "--opentelemetry-logs-port cannot be specified when --disable-azure-monitor-logs is used." + "--opentelemetry-logs-traces-port-http cannot be specified when --disable-azure-monitor-logs is used." ) - # For CREATE: --enable-opentelemetry-logs must be explicitly specified + # For CREATE: --enable-opentelemetry-logs-traces must be explicitly specified if self.decorator_mode == DecoratorMode.CREATE: if not self.get_enable_opentelemetry_logs(): raise InvalidArgumentValueError( - "--opentelemetry-logs-port can only be specified when " - "--enable-opentelemetry-logs is also specified." + "--opentelemetry-logs-traces-port-http can only be specified when " + "--enable-opentelemetry-logs-traces is also specified." ) # For UPDATE: allow if either explicitly enabling OR already enabled in cluster elif self.decorator_mode == DecoratorMode.UPDATE: @@ -3176,8 +3177,8 @@ def get_opentelemetry_logs_port(self) -> Union[int, None]: ) if not explicitly_enabling and not already_enabled: raise InvalidArgumentValueError( - "--opentelemetry-logs-port can only be specified when " - "--enable-opentelemetry-logs is also specified or " + "--opentelemetry-logs-traces-port-http can only be specified when " + "--enable-opentelemetry-logs-traces is also specified or " "OpenTelemetry logs are already enabled." ) diff --git a/src/aks-preview/azext_aks_preview/tests/latest/test_aks_commands.py b/src/aks-preview/azext_aks_preview/tests/latest/test_aks_commands.py index ab701e80d9a..a0d4c78f94a 100644 --- a/src/aks-preview/azext_aks_preview/tests/latest/test_aks_commands.py +++ b/src/aks-preview/azext_aks_preview/tests/latest/test_aks_commands.py @@ -16565,7 +16565,7 @@ def test_aks_create_with_azuremonitorlogs_and_opentelemetry(self, resource_group create_cmd = ( 'aks create --resource-group={resource_group} --name={name} --location={location} --ssh-key-value={ssh_key_value} --node-vm-size={node_vm_size} ' - '--enable-managed-identity --enable-azure-monitor-logs --enable-opentelemetry-logs --opentelemetry-logs-port=8080 ' + '--enable-managed-identity --enable-azure-monitor-logs --enable-opentelemetry-logs-traces --opentelemetry-logs-traces-port-http=8080 ' '--aks-custom-headers AKSHTTPCustomFeatures=Microsoft.ContainerService/AzureMonitorAppMonitoringPreview --output=json' ) self.cmd(create_cmd, checks=[ @@ -16573,8 +16573,8 @@ def test_aks_create_with_azuremonitorlogs_and_opentelemetry(self, resource_group self.check('addonProfiles.omsagent.enabled', True), self.exists('addonProfiles.omsagent.config.logAnalyticsWorkspaceResourceID'), self.check('addonProfiles.omsagent.config.useAADAuth', 'true'), - self.check('azureMonitorProfile.appMonitoring.openTelemetryLogs.enabled', True), - self.check('azureMonitorProfile.appMonitoring.openTelemetryLogs.port', 8080), + self.check('azureMonitorProfile.appMonitoring.openTelemetryLogsAndTraces.enabled', True), + self.check('azureMonitorProfile.appMonitoring.openTelemetryLogsAndTraces.httpPort', 8080), ]) # delete @@ -16607,7 +16607,7 @@ def test_aks_update_with_azuremonitorlogs_and_opentelemetry(self, resource_group # update: enable-azure-monitor-logs with OpenTelemetry logs update_cmd = ( 'aks update --resource-group={resource_group} --name={name} --yes --output=json ' - '--enable-azure-monitor-logs --enable-opentelemetry-logs --opentelemetry-logs-port=9090 ' + '--enable-azure-monitor-logs --enable-opentelemetry-logs-traces --opentelemetry-logs-traces-port-http=9090 ' '--aks-custom-headers AKSHTTPCustomFeatures=Microsoft.ContainerService/AzureMonitorAppMonitoringPreview' ) self.cmd(update_cmd, checks=[ @@ -16615,20 +16615,20 @@ def test_aks_update_with_azuremonitorlogs_and_opentelemetry(self, resource_group self.check('addonProfiles.omsagent.enabled', True), self.exists('addonProfiles.omsagent.config.logAnalyticsWorkspaceResourceID'), self.check('addonProfiles.omsagent.config.useAADAuth', 'true'), - self.check('azureMonitorProfile.appMonitoring.openTelemetryLogs.enabled', True), - self.check('azureMonitorProfile.appMonitoring.openTelemetryLogs.port', 9090), + self.check('azureMonitorProfile.appMonitoring.openTelemetryLogsAndTraces.enabled', True), + self.check('azureMonitorProfile.appMonitoring.openTelemetryLogsAndTraces.httpPort', 9090), ]) # update: disable OpenTelemetry logs but keep Azure Monitor logs update_cmd = ( 'aks update --resource-group={resource_group} --name={name} --yes --output=json ' - '--disable-opentelemetry-logs' + '--disable-opentelemetry-logs-traces' ) self.cmd(update_cmd, checks=[ self.check('provisioningState', 'Succeeded'), self.check('addonProfiles.omsagent.enabled', True), # Still enabled self.check('addonProfiles.omsagent.config.useAADAuth', 'true'), - self.check('azureMonitorProfile.appMonitoring.openTelemetryLogs.enabled', False), + self.check('azureMonitorProfile.appMonitoring.openTelemetryLogsAndTraces.enabled', False), ]) # update: disable-azure-monitor-logs (should also disable OpenTelemetry logs) @@ -16802,7 +16802,7 @@ def test_aks_create_with_azuremonitormetrics_and_opentelemetry(self, resource_gr create_cmd = ( 'aks create --resource-group={resource_group} --name={name} --location={location} --ssh-key-value={ssh_key_value} --node-vm-size={node_vm_size} ' - '--enable-managed-identity --enable-azure-monitor-metrics --enable-opentelemetry-metrics --opentelemetry-metrics-port=8080 ' + '--enable-managed-identity --enable-azure-monitor-metrics --enable-opentelemetry-metrics --opentelemetry-metrics-port-http=8080 ' '--aks-custom-headers AKSHTTPCustomFeatures=Microsoft.ContainerService/AzureMonitorAppMonitoringPreview --output=json' ) self.cmd(create_cmd, checks=[ @@ -16825,7 +16825,7 @@ def test_aks_create_with_azuremonitormetrics_and_opentelemetry(self, resource_gr self.check('provisioningState', 'Succeeded'), self.check('azureMonitorProfile.metrics.enabled', True), self.check('azureMonitorProfile.appMonitoring.openTelemetryMetrics.enabled', True), - self.check('azureMonitorProfile.appMonitoring.openTelemetryMetrics.port', 8080), + self.check('azureMonitorProfile.appMonitoring.openTelemetryMetrics.httpPort', 8080), ]) # delete @@ -16858,7 +16858,7 @@ def test_aks_update_with_azuremonitormetrics_and_opentelemetry(self, resource_gr # update: enable-azure-monitor-metrics with OpenTelemetry metrics update_cmd = ( 'aks update --resource-group={resource_group} --name={name} --yes ' - '--enable-azure-monitor-metrics --enable-opentelemetry-metrics --opentelemetry-metrics-port=9090 ' + '--enable-azure-monitor-metrics --enable-opentelemetry-metrics --opentelemetry-metrics-port-http=9090 ' '--aks-custom-headers AKSHTTPCustomFeatures=Microsoft.ContainerService/AzureMonitorAppMonitoringPreview ' '--output=json' ) @@ -16957,6 +16957,75 @@ def test_aks_update_with_azuremonitormetrics_and_opentelemetry(self, resource_gr self.is_empty(), ]) + @live_only() + @AllowLargeResponse() + @AKSCustomResourceGroupPreparer(random_name_length=17, name_prefix='clitest', location='westus2') + def test_aks_create_with_opentelemetry_deprecated_flag_aliases(self, resource_group, resource_group_location): + # Verifies the deprecated OTLP flag aliases still work and redirect to the + # renamed parameters (i.e. --enable-opentelemetry-logs -> --enable-opentelemetry-logs-traces, + # --opentelemetry-logs-port -> --opentelemetry-logs-traces-port-http, + # --opentelemetry-metrics-port -> --opentelemetry-metrics-port-http). + # reset the count so in replay mode the random names will start with 0 + self.test_resources_count = 0 + aks_name = self.create_random_name('cliakstest', 16) + node_vm_size = 'standard_d2s_v3' + self.kwargs.update({ + 'resource_group': resource_group, + 'name': aks_name, + 'location': resource_group_location, + 'resource_type': 'Microsoft.ContainerService/ManagedClusters', + 'ssh_key_value': self.generate_ssh_keys(), + 'node_vm_size': node_vm_size, + }) + + # Intentionally use the DEPRECATED flag names; they must still bind the same + # destinations and configure OpenTelemetry on the new API surface. + create_cmd = ( + 'aks create --resource-group={resource_group} --name={name} --location={location} --ssh-key-value={ssh_key_value} --node-vm-size={node_vm_size} ' + '--enable-managed-identity --enable-azure-monitor-logs --enable-azure-monitor-metrics ' + '--enable-opentelemetry-logs --opentelemetry-logs-port=8080 ' + '--enable-opentelemetry-metrics --opentelemetry-metrics-port=8081 ' + '--aks-custom-headers AKSHTTPCustomFeatures=Microsoft.ContainerService/AzureMonitorAppMonitoringPreview --output=json' + ) + self.cmd(create_cmd, checks=[ + self.check('provisioningState', 'Succeeded'), + ]) + + # azuremonitor metrics is enabled in a postprocessing update; wait for it to settle. + wait_cmd = ' '.join([ + 'aks', 'wait', '--resource-group={resource_group}', '--name={name}', '--updated', + '--interval 60', '--timeout 1800', + ]) + self.cmd(wait_cmd, checks=[ + self.is_empty(), + ]) + + self.cmd('aks show -g {resource_group} -n {name} --output=json', checks=[ + self.check('provisioningState', 'Succeeded'), + self.check('addonProfiles.omsagent.enabled', True), + self.check('azureMonitorProfile.metrics.enabled', True), + self.check('azureMonitorProfile.appMonitoring.openTelemetryLogsAndTraces.enabled', True), + self.check('azureMonitorProfile.appMonitoring.openTelemetryLogsAndTraces.httpPort', 8080), + self.check('azureMonitorProfile.appMonitoring.openTelemetryMetrics.enabled', True), + self.check('azureMonitorProfile.appMonitoring.openTelemetryMetrics.httpPort', 8081), + ]) + + # Disable using the deprecated disable alias. + update_cmd = ( + 'aks update --resource-group={resource_group} --name={name} --yes --output=json ' + '--disable-opentelemetry-logs' + ) + self.cmd(update_cmd, checks=[ + self.check('provisioningState', 'Succeeded'), + self.check('azureMonitorProfile.appMonitoring.openTelemetryLogsAndTraces.enabled', False), + ]) + + # delete + cmd = 'aks delete --resource-group={resource_group} --name={name} --yes --no-wait' + self.cmd(cmd, checks=[ + self.is_empty(), + ]) + @live_only() @AllowLargeResponse(999999) @AKSCustomResourceGroupPreparer(random_name_length=17, name_prefix='clitest', location='swedencentral') @@ -17092,8 +17161,8 @@ def test_aks_comprehensive_monitoring_integration(self, resource_group, resource create_cmd = ( 'aks create --resource-group={resource_group} --name={name} --location={location} --ssh-key-value={ssh_key_value} --node-vm-size={node_vm_size} ' '--enable-managed-identity --enable-azure-monitor-logs --enable-azure-monitor-metrics --enable-azure-monitor-app-monitoring ' - '--enable-opentelemetry-logs --opentelemetry-logs-port=8080 ' - '--enable-opentelemetry-metrics --opentelemetry-metrics-port=8081 ' + '--enable-opentelemetry-logs-traces --opentelemetry-logs-traces-port-http=8080 ' + '--enable-opentelemetry-metrics --opentelemetry-metrics-port-http=8081 ' '--enable-windows-recording-rules ' '--aks-custom-headers AKSHTTPCustomFeatures=Microsoft.ContainerService/AzureMonitorAppMonitoringPreview --output=json' ) @@ -17124,17 +17193,17 @@ def test_aks_comprehensive_monitoring_integration(self, resource_group, resource # Azure Monitor app monitoring checks self.check('azureMonitorProfile.appMonitoring.autoInstrumentation.enabled', True), # OpenTelemetry logs checks - self.check('azureMonitorProfile.appMonitoring.openTelemetryLogs.enabled', True), - self.check('azureMonitorProfile.appMonitoring.openTelemetryLogs.port', 8080), + self.check('azureMonitorProfile.appMonitoring.openTelemetryLogsAndTraces.enabled', True), + self.check('azureMonitorProfile.appMonitoring.openTelemetryLogsAndTraces.httpPort', 8080), # OpenTelemetry metrics checks self.check('azureMonitorProfile.appMonitoring.openTelemetryMetrics.enabled', True), - self.check('azureMonitorProfile.appMonitoring.openTelemetryMetrics.port', 8081), + self.check('azureMonitorProfile.appMonitoring.openTelemetryMetrics.httpPort', 8081), ]) # Phase 2: Update - disable only OpenTelemetry logs (keep everything else) update_cmd = ( 'aks update --resource-group={resource_group} --name={name} --yes --output=json ' - '--disable-opentelemetry-logs' + '--disable-opentelemetry-logs-traces' ) self.cmd(update_cmd, checks=[ self.check('provisioningState', 'Succeeded'), @@ -17158,7 +17227,7 @@ def test_aks_comprehensive_monitoring_integration(self, resource_group, resource # Azure Monitor app monitoring should still be enabled self.check('azureMonitorProfile.appMonitoring.autoInstrumentation.enabled', True), # OpenTelemetry logs should be disabled - self.check('azureMonitorProfile.appMonitoring.openTelemetryLogs.enabled', False), + self.check('azureMonitorProfile.appMonitoring.openTelemetryLogsAndTraces.enabled', False), # OpenTelemetry metrics should still be enabled self.check('azureMonitorProfile.appMonitoring.openTelemetryMetrics.enabled', True), ]) @@ -17196,8 +17265,8 @@ def test_aks_comprehensive_monitoring_integration(self, resource_group, resource # Phase 4: Update - re-enable all OpenTelemetry features with different ports update_cmd = ( 'aks update --resource-group={resource_group} --name={name} --yes --output=json ' - '--enable-opentelemetry-logs --opentelemetry-logs-port=9090 ' - '--enable-opentelemetry-metrics --opentelemetry-metrics-port=9091 ' + '--enable-opentelemetry-logs-traces --opentelemetry-logs-traces-port-http=9090 ' + '--enable-opentelemetry-metrics --opentelemetry-metrics-port-http=9091 ' '--aks-custom-headers AKSHTTPCustomFeatures=Microsoft.ContainerService/AzureMonitorAppMonitoringPreview' ) self.cmd(update_cmd, checks=[ @@ -17220,10 +17289,10 @@ def test_aks_comprehensive_monitoring_integration(self, resource_group, resource self.check('azureMonitorProfile.metrics.enabled', True), self.check('azureMonitorProfile.appMonitoring.autoInstrumentation.enabled', True), # OpenTelemetry features should be re-enabled with new ports - self.check('azureMonitorProfile.appMonitoring.openTelemetryLogs.enabled', True), - self.check('azureMonitorProfile.appMonitoring.openTelemetryLogs.port', 9090), + self.check('azureMonitorProfile.appMonitoring.openTelemetryLogsAndTraces.enabled', True), + self.check('azureMonitorProfile.appMonitoring.openTelemetryLogsAndTraces.httpPort', 9090), self.check('azureMonitorProfile.appMonitoring.openTelemetryMetrics.enabled', True), - self.check('azureMonitorProfile.appMonitoring.openTelemetryMetrics.port', 9091), + self.check('azureMonitorProfile.appMonitoring.openTelemetryMetrics.httpPort', 9091), ]) # Phase 5: Update - disable Azure Monitor metrics (should also disable OpenTelemetry metrics) @@ -17253,7 +17322,7 @@ def test_aks_comprehensive_monitoring_integration(self, resource_group, resource # Azure Monitor app monitoring should still be enabled self.check('azureMonitorProfile.appMonitoring.autoInstrumentation.enabled', True), # OpenTelemetry logs should still be enabled (independent of metrics) - self.check('azureMonitorProfile.appMonitoring.openTelemetryLogs.enabled', True), + self.check('azureMonitorProfile.appMonitoring.openTelemetryLogsAndTraces.enabled', True), ]) # Phase 6: Update - disable Azure Monitor logs (should also disable OpenTelemetry logs) @@ -17285,8 +17354,8 @@ def test_aks_comprehensive_monitoring_integration(self, resource_group, resource update_cmd = ( 'aks update --resource-group={resource_group} --name={name} --yes --output=json ' '--enable-azure-monitor-logs --enable-azure-monitor-metrics --enable-azure-monitor-app-monitoring ' - '--enable-opentelemetry-logs --opentelemetry-logs-port=7070 ' - '--enable-opentelemetry-metrics --opentelemetry-metrics-port=7071 ' + '--enable-opentelemetry-logs-traces --opentelemetry-logs-traces-port-http=7070 ' + '--enable-opentelemetry-metrics --opentelemetry-metrics-port-http=7071 ' '--enable-windows-recording-rules ' '--aks-custom-headers AKSHTTPCustomFeatures=Microsoft.ContainerService/AzureMonitorAppMonitoringPreview' ) @@ -17311,17 +17380,17 @@ def test_aks_comprehensive_monitoring_integration(self, resource_group, resource self.check('azureMonitorProfile.metrics.enabled', True), self.check('azureMonitorProfile.appMonitoring.autoInstrumentation.enabled', True), # All OpenTelemetry features should be enabled with new ports - self.check('azureMonitorProfile.appMonitoring.openTelemetryLogs.enabled', True), - self.check('azureMonitorProfile.appMonitoring.openTelemetryLogs.port', 7070), + self.check('azureMonitorProfile.appMonitoring.openTelemetryLogsAndTraces.enabled', True), + self.check('azureMonitorProfile.appMonitoring.openTelemetryLogsAndTraces.httpPort', 7070), self.check('azureMonitorProfile.appMonitoring.openTelemetryMetrics.enabled', True), - self.check('azureMonitorProfile.appMonitoring.openTelemetryMetrics.port', 7071), + self.check('azureMonitorProfile.appMonitoring.openTelemetryMetrics.httpPort', 7071), ]) # Phase 8: Final cleanup - disable all monitoring features update_cmd = ( 'aks update --resource-group={resource_group} --name={name} --yes --output=json ' '--disable-azure-monitor-logs --disable-azure-monitor-metrics --disable-azure-monitor-app-monitoring ' - '--disable-opentelemetry-logs --disable-opentelemetry-metrics' + '--disable-opentelemetry-logs-traces --disable-opentelemetry-metrics' ) self.cmd(update_cmd, checks=[ self.check('provisioningState', 'Succeeded'), diff --git a/src/aks-preview/azext_aks_preview/tests/latest/test_validators.py b/src/aks-preview/azext_aks_preview/tests/latest/test_validators.py index 5ebffe2db4c..82ce6895277 100644 --- a/src/aks-preview/azext_aks_preview/tests/latest/test_validators.py +++ b/src/aks-preview/azext_aks_preview/tests/latest/test_validators.py @@ -2042,6 +2042,59 @@ def __init__(self, opentelemetry_metrics_port=None, opentelemetry_logs_port=None self.opentelemetry_logs_port = opentelemetry_logs_port +class OpenTelemetryDeprecatedAliasNamespace: + def __init__(self, opentelemetry_metrics_port=None, opentelemetry_metrics_port_deprecated=None, + opentelemetry_logs_port=None, opentelemetry_logs_port_deprecated=None, + enable_opentelemetry_logs=False, enable_opentelemetry_logs_deprecated=False, + disable_opentelemetry_logs=False, disable_opentelemetry_logs_deprecated=False): + self.opentelemetry_metrics_port = opentelemetry_metrics_port + self.opentelemetry_metrics_port_deprecated = opentelemetry_metrics_port_deprecated + self.opentelemetry_logs_port = opentelemetry_logs_port + self.opentelemetry_logs_port_deprecated = opentelemetry_logs_port_deprecated + self.enable_opentelemetry_logs = enable_opentelemetry_logs + self.enable_opentelemetry_logs_deprecated = enable_opentelemetry_logs_deprecated + self.disable_opentelemetry_logs = disable_opentelemetry_logs + self.disable_opentelemetry_logs_deprecated = disable_opentelemetry_logs_deprecated + + +class TestMergeOpenTelemetryDeprecatedAliases(unittest.TestCase): + def test_deprecated_port_merged_when_current_unset(self): + ns = OpenTelemetryDeprecatedAliasNamespace(opentelemetry_metrics_port_deprecated=8080) + validators._merge_opentelemetry_deprecated_aliases(ns) + self.assertEqual(ns.opentelemetry_metrics_port, 8080) + + def test_current_port_takes_precedence_over_deprecated(self): + ns = OpenTelemetryDeprecatedAliasNamespace( + opentelemetry_metrics_port=9000, + opentelemetry_metrics_port_deprecated=8080, + ) + validators._merge_opentelemetry_deprecated_aliases(ns) + self.assertEqual(ns.opentelemetry_metrics_port, 9000) + + def test_deprecated_logs_port_merged(self): + ns = OpenTelemetryDeprecatedAliasNamespace(opentelemetry_logs_port_deprecated=4317) + validators._merge_opentelemetry_deprecated_aliases(ns) + self.assertEqual(ns.opentelemetry_logs_port, 4317) + + def test_deprecated_enable_flag_merged(self): + ns = OpenTelemetryDeprecatedAliasNamespace(enable_opentelemetry_logs_deprecated=True) + validators._merge_opentelemetry_deprecated_aliases(ns) + self.assertTrue(ns.enable_opentelemetry_logs) + + def test_deprecated_disable_flag_merged(self): + ns = OpenTelemetryDeprecatedAliasNamespace(disable_opentelemetry_logs_deprecated=True) + validators._merge_opentelemetry_deprecated_aliases(ns) + self.assertTrue(ns.disable_opentelemetry_logs) + + def test_no_deprecated_values_leaves_current_unchanged(self): + ns = OpenTelemetryDeprecatedAliasNamespace() + validators._merge_opentelemetry_deprecated_aliases(ns) + self.assertIsNone(ns.opentelemetry_metrics_port) + self.assertIsNone(ns.opentelemetry_logs_port) + self.assertFalse(ns.enable_opentelemetry_logs) + self.assertFalse(ns.disable_opentelemetry_logs) + + class TestValidateOpenTelemetryPorts(unittest.TestCase): def test_no_ports_specified(self): namespace = OpenTelemetryPortsNamespace() @@ -2070,7 +2123,7 @@ def test_same_ports_throws_error(self): ) err = ( "OpenTelemetry metrics port and logs port cannot be the same. " - "Please specify different ports for --opentelemetry-metrics-port and --opentelemetry-logs-port." + "Please specify different ports for --opentelemetry-metrics-port-http and --opentelemetry-logs-traces-port-http." ) with self.assertRaises(ArgumentUsageError) as cm: validators.validate_opentelemetry_ports(namespace) @@ -2220,7 +2273,7 @@ def test_mutually_exclusive_flags_throws_error(self): enable_opentelemetry_logs=True, disable_opentelemetry_logs=True ) - err = "Cannot specify both --enable-opentelemetry-logs and --disable-opentelemetry-logs at the same time." + err = "Cannot specify both --enable-opentelemetry-logs-traces and --disable-opentelemetry-logs-traces at the same time." with self.assertRaises(MutuallyExclusiveArgumentError) as cm: validators.validate_opentelemetry_logs_dependencies(namespace) self.assertEqual(str(cm.exception), err) @@ -2258,7 +2311,7 @@ def test_mutually_exclusive_flags_throws_error_for_update(self): enable_opentelemetry_logs=True, disable_opentelemetry_logs=True ) - err = "Cannot specify both --enable-opentelemetry-logs and --disable-opentelemetry-logs at the same time." + err = "Cannot specify both --enable-opentelemetry-logs-traces and --disable-opentelemetry-logs-traces at the same time." with self.assertRaises(MutuallyExclusiveArgumentError) as cm: validators.validate_opentelemetry_logs_dependencies_for_update(namespace) self.assertEqual(str(cm.exception), err) @@ -2303,7 +2356,7 @@ def test_port_conflict_throws_error(self): ) err = ( "OpenTelemetry metrics port and logs port cannot be the same. " - "Please specify different ports for --opentelemetry-metrics-port and --opentelemetry-logs-port." + "Please specify different ports for --opentelemetry-metrics-port-http and --opentelemetry-logs-traces-port-http." ) with self.assertRaises(ArgumentUsageError) as cm: validators.validate_azure_monitor_and_opentelemetry_for_create(namespace) @@ -2353,7 +2406,7 @@ def test_port_conflict_throws_error_for_update(self): ) err = ( "OpenTelemetry metrics port and logs port cannot be the same. " - "Please specify different ports for --opentelemetry-metrics-port and --opentelemetry-logs-port." + "Please specify different ports for --opentelemetry-metrics-port-http and --opentelemetry-logs-traces-port-http." ) with self.assertRaises(ArgumentUsageError) as cm: validators.validate_azure_monitor_and_opentelemetry_for_update(namespace) @@ -2374,7 +2427,7 @@ def test_mutually_exclusive_logs_flags_throws_error(self): enable_opentelemetry_logs=True, disable_opentelemetry_logs=True ) - err = "Cannot specify both --enable-opentelemetry-logs and --disable-opentelemetry-logs at the same time." + err = "Cannot specify both --enable-opentelemetry-logs-traces and --disable-opentelemetry-logs-traces at the same time." with self.assertRaises(MutuallyExclusiveArgumentError) as cm: validators.validate_azure_monitor_and_opentelemetry_for_update(namespace) self.assertEqual(str(cm.exception), err) From 3eaef5210953c93dd065dad90c77018e8c7d7f20 Mon Sep 17 00:00:00 2001 From: Christoph Butz Date: Wed, 1 Jul 2026 13:43:37 +0100 Subject: [PATCH 02/14] [aks-preview] Add OpenTelemetry gRPC ports for metrics and logs/traces Adds --opentelemetry-metrics-port-grpc and --opentelemetry-logs-traces-port-grpc (preview) to 'az aks create/update', wiring the new grpc_port field on the appMonitoring OpenTelemetry profiles (API 2026-04-02-preview). - Validation: all four OTLP ports (metrics/logs x http/grpc) must be in 1..65535 and distinct; grpc ports follow the same enable/disable gating as the http ports. - Disable paths now clear both http_port and grpc_port, including when the parent Azure Monitor metrics/logs are disabled. - Adds unit, validator, and live integration coverage; bumps version to 21.0.0b8 and updates HISTORY. --- src/aks-preview/HISTORY.rst | 7 +- src/aks-preview/azext_aks_preview/_help.py | 16 ++ src/aks-preview/azext_aks_preview/_params.py | 28 +++- .../azext_aks_preview/_validators.py | 31 ++-- src/aks-preview/azext_aks_preview/custom.py | 4 + .../managed_cluster_decorator.py | 154 ++++++++++++++++-- .../tests/latest/test_aks_commands.py | 3 +- .../latest/test_managed_cluster_decorator.py | 98 +++++++++++ .../tests/latest/test_validators.py | 87 ++++++++-- src/aks-preview/setup.py | 2 +- 10 files changed, 386 insertions(+), 44 deletions(-) diff --git a/src/aks-preview/HISTORY.rst b/src/aks-preview/HISTORY.rst index 1622ab34e2d..6c7f132c666 100644 --- a/src/aks-preview/HISTORY.rst +++ b/src/aks-preview/HISTORY.rst @@ -12,8 +12,13 @@ To release a new version, please select a new version number (usually plus 1 to Pending +++++++ +21.0.0b11 ++++++++++ +* `az aks create` and `az aks update`: Add `--opentelemetry-metrics-port-grpc` and `--opentelemetry-logs-traces-port-grpc` (preview) to configure the gRPC ports for OpenTelemetry metrics and logs/traces collection, in addition to the existing HTTP/protobuf ports. +* `az aks create` and `az aks update`: Rename the OpenTelemetry parameters, keeping the previous names as deprecated aliases: `--opentelemetry-metrics-port` -> `--opentelemetry-metrics-port-http`, `--opentelemetry-logs-port` -> `--opentelemetry-logs-traces-port-http`, `--enable-opentelemetry-logs` -> `--enable-opentelemetry-logs-traces`, `--disable-opentelemetry-logs` -> `--disable-opentelemetry-logs-traces`. + 21.0.0b10 -++++++++ ++++++++++ * `az aks create`: Add parameters `--system-node-subnet-id`, `--node-subnet-id` and `--enable-hosted-system` to support BYO VNet for Automatic Managed System Pool clusters. 21.0.0b9 diff --git a/src/aks-preview/azext_aks_preview/_help.py b/src/aks-preview/azext_aks_preview/_help.py index dd8d04994d8..0ba2af0ddf1 100644 --- a/src/aks-preview/azext_aks_preview/_help.py +++ b/src/aks-preview/azext_aks_preview/_help.py @@ -633,6 +633,9 @@ - name: --opentelemetry-metrics-port-http type: int short-summary: HTTP/protobuf port for OpenTelemetry metrics collection (default port will be used if not specified) + - name: --opentelemetry-metrics-port-grpc + type: int + short-summary: gRPC port for OpenTelemetry metrics collection (default port will be used if not specified) - name: --disable-opentelemetry-metrics type: bool short-summary: Disable OpenTelemetry metrics collection @@ -642,6 +645,9 @@ - name: --opentelemetry-logs-traces-port-http type: int short-summary: HTTP/protobuf port for OpenTelemetry logs and traces collection (default port will be used if not specified) + - name: --opentelemetry-logs-traces-port-grpc + type: int + short-summary: gRPC port for OpenTelemetry logs and traces collection (default port will be used if not specified) - name: --disable-opentelemetry-logs-traces type: bool short-summary: Disable OpenTelemetry logs and traces collection @@ -852,6 +858,8 @@ text: az aks create -g MyResourceGroup -n MyManagedCluster --enable-opentelemetry-metrics --opentelemetry-metrics-port-http 8888 --enable-azuremonitormetrics - name: Create a kubernetes cluster with OpenTelemetry logs and traces on custom port text: az aks create -g MyResourceGroup -n MyManagedCluster --enable-opentelemetry-logs-traces --opentelemetry-logs-traces-port-http 4317 --enable-azure-monitor-logs + - name: Create a kubernetes cluster with OpenTelemetry metrics on custom HTTP and gRPC ports + text: az aks create -g MyResourceGroup -n MyManagedCluster --enable-opentelemetry-metrics --opentelemetry-metrics-port-http 8888 --opentelemetry-metrics-port-grpc 8889 --enable-azuremonitormetrics - name: Create a kubernetes cluster with a nodepool having ip allocation mode set to "StaticBlock" text: az aks create -g MyResourceGroup -n MyManagedCluster --os-sku Ubuntu --max-pods MaxPodsPerNode --network-plugin azure --vnet-subnet-id /subscriptions/00000/resourceGroups/AnotherResourceGroup/providers/Microsoft.Network/virtualNetworks/MyVnet/subnets/NodeSubnet --pod-subnet-id /subscriptions/00000/resourceGroups/AnotherResourceGroup/providers/Microsoft.Network/virtualNetworks/MyVnet/subnets/PodSubnet --pod-ip-allocation-mode StaticBlock - name: Create a kubernetes cluster with a VirtualMachines nodepool @@ -1401,6 +1409,9 @@ - name: --opentelemetry-metrics-port-http type: int short-summary: HTTP/protobuf port for OpenTelemetry metrics collection (default port will be used if not specified) + - name: --opentelemetry-metrics-port-grpc + type: int + short-summary: gRPC port for OpenTelemetry metrics collection (default port will be used if not specified) - name: --disable-opentelemetry-metrics type: bool short-summary: Disable OpenTelemetry metrics collection @@ -1410,6 +1421,9 @@ - name: --opentelemetry-logs-traces-port-http type: int short-summary: HTTP/protobuf port for OpenTelemetry logs and traces collection (default port will be used if not specified) + - name: --opentelemetry-logs-traces-port-grpc + type: int + short-summary: gRPC port for OpenTelemetry logs and traces collection (default port will be used if not specified) - name: --disable-opentelemetry-logs-traces type: bool short-summary: Disable OpenTelemetry logs and traces collection @@ -1660,6 +1674,8 @@ text: az aks update -g MyResourceGroup -n MyManagedCluster --enable-opentelemetry-metrics --opentelemetry-metrics-port-http 8888 - name: Configure OpenTelemetry logs and traces with custom port text: az aks update -g MyResourceGroup -n MyManagedCluster --enable-opentelemetry-logs-traces --opentelemetry-logs-traces-port-http 4317 + - name: Configure OpenTelemetry metrics with custom HTTP and gRPC ports + text: az aks update -g MyResourceGroup -n MyManagedCluster --enable-opentelemetry-metrics --opentelemetry-metrics-port-http 8888 --opentelemetry-metrics-port-grpc 8889 - name: Disable OpenTelemetry metrics collection on an existing cluster text: az aks update -g MyResourceGroup -n MyManagedCluster --disable-opentelemetry-metrics - name: Disable OpenTelemetry logs and traces collection on an existing cluster diff --git a/src/aks-preview/azext_aks_preview/_params.py b/src/aks-preview/azext_aks_preview/_params.py index 8ff293acd9a..4c3b14c05cb 100644 --- a/src/aks-preview/azext_aks_preview/_params.py +++ b/src/aks-preview/azext_aks_preview/_params.py @@ -1175,7 +1175,12 @@ def load_arguments(self, _): type=int, help="HTTP/protobuf port for OpenTelemetry metrics collection" ) - + c.argument("opentelemetry_metrics_port_grpc", + options_list=["--opentelemetry-metrics-port-grpc"], + is_preview=True, + type=int, + help="gRPC port for OpenTelemetry metrics collection" + ) c.argument("disable_opentelemetry_metrics", is_preview=True, action="store_true", @@ -1211,7 +1216,12 @@ def load_arguments(self, _): type=int, help="HTTP/protobuf port for OpenTelemetry logs and traces collection" ) - + c.argument("opentelemetry_logs_traces_port_grpc", + options_list=["--opentelemetry-logs-traces-port-grpc"], + is_preview=True, + type=int, + help="gRPC port for OpenTelemetry logs and traces collection" + ) c.argument("disable_opentelemetry_logs", options_list=["--disable-opentelemetry-logs-traces"], is_preview=True, @@ -1820,7 +1830,12 @@ def load_arguments(self, _): type=int, help="HTTP/protobuf port for OpenTelemetry metrics collection" ) - + c.argument("opentelemetry_metrics_port_grpc", + options_list=["--opentelemetry-metrics-port-grpc"], + is_preview=True, + type=int, + help="gRPC port for OpenTelemetry metrics collection" + ) c.argument("disable_opentelemetry_metrics", is_preview=True, action="store_true", @@ -1856,7 +1871,12 @@ def load_arguments(self, _): type=int, help="HTTP/protobuf port for OpenTelemetry logs and traces collection" ) - + c.argument("opentelemetry_logs_traces_port_grpc", + options_list=["--opentelemetry-logs-traces-port-grpc"], + is_preview=True, + type=int, + help="gRPC port for OpenTelemetry logs and traces collection" + ) c.argument("disable_opentelemetry_logs", options_list=["--disable-opentelemetry-logs-traces"], is_preview=True, diff --git a/src/aks-preview/azext_aks_preview/_validators.py b/src/aks-preview/azext_aks_preview/_validators.py index 73d713fd93e..9c6f1b67184 100644 --- a/src/aks-preview/azext_aks_preview/_validators.py +++ b/src/aks-preview/azext_aks_preview/_validators.py @@ -1085,24 +1085,31 @@ def validate_location_resource_group_cluster_parameters(namespace): def validate_opentelemetry_ports(namespace): - """Validate that OpenTelemetry metrics and logs ports don't conflict.""" - metrics_port = getattr(namespace, 'opentelemetry_metrics_port', None) - logs_port = getattr(namespace, 'opentelemetry_logs_port', None) - - # Check if both ports are specified and are the same - if metrics_port is not None and logs_port is not None and metrics_port == logs_port: - raise ArgumentUsageError( - "OpenTelemetry metrics port and logs port cannot be the same. " - "Please specify different ports for --opentelemetry-metrics-port-http and --opentelemetry-logs-traces-port-http." - ) + """Validate that the OpenTelemetry HTTP and gRPC ports are in range and all distinct.""" + ports = [ + ("--opentelemetry-metrics-port-http", getattr(namespace, 'opentelemetry_metrics_port', None)), + ("--opentelemetry-metrics-port-grpc", getattr(namespace, 'opentelemetry_metrics_port_grpc', None)), + ("--opentelemetry-logs-traces-port-http", getattr(namespace, 'opentelemetry_logs_port', None)), + ("--opentelemetry-logs-traces-port-grpc", getattr(namespace, 'opentelemetry_logs_traces_port_grpc', None)), + ] # Validate port ranges - for port, port_name in [(metrics_port, 'metrics'), (logs_port, 'logs')]: + for flag, port in ports: if port is not None and not (1 <= port <= 65535): raise ArgumentUsageError( - f"OpenTelemetry {port_name} port must be between 1 and 65535, got {port}." + f"OpenTelemetry port {flag} must be between 1 and 65535, got {port}." ) + # All specified OpenTelemetry ports (HTTP and gRPC, metrics and logs/traces) must be distinct + specified = [(flag, port) for flag, port in ports if port is not None] + for i in range(len(specified)): + for j in range(i + 1, len(specified)): + if specified[i][1] == specified[j][1]: + raise ArgumentUsageError( + "OpenTelemetry ports must all be different. " + f"{specified[i][0]} and {specified[j][0]} cannot both be set to {specified[i][1]}." + ) + def validate_opentelemetry_metrics_dependencies(namespace): """Validate OpenTelemetry metrics dependencies for create operations.""" diff --git a/src/aks-preview/azext_aks_preview/custom.py b/src/aks-preview/azext_aks_preview/custom.py index b48a2d5073b..c05a406e71c 100644 --- a/src/aks-preview/azext_aks_preview/custom.py +++ b/src/aks-preview/azext_aks_preview/custom.py @@ -1395,9 +1395,11 @@ def aks_create( # opentelemetry parameters enable_opentelemetry_metrics=False, opentelemetry_metrics_port=None, + opentelemetry_metrics_port_grpc=None, disable_opentelemetry_metrics=False, enable_opentelemetry_logs=False, opentelemetry_logs_port=None, + opentelemetry_logs_traces_port_grpc=None, disable_opentelemetry_logs=False, # opentelemetry deprecated aliases (merged into the parameters above by the validator) opentelemetry_metrics_port_deprecated=None, @@ -1643,9 +1645,11 @@ def aks_update( # opentelemetry parameters enable_opentelemetry_metrics=False, opentelemetry_metrics_port=None, + opentelemetry_metrics_port_grpc=None, disable_opentelemetry_metrics=False, enable_opentelemetry_logs=False, opentelemetry_logs_port=None, + opentelemetry_logs_traces_port_grpc=None, disable_opentelemetry_logs=False, # opentelemetry deprecated aliases (merged into the parameters above by the validator) opentelemetry_metrics_port_deprecated=None, diff --git a/src/aks-preview/azext_aks_preview/managed_cluster_decorator.py b/src/aks-preview/azext_aks_preview/managed_cluster_decorator.py index 9796c7070f3..3b75fb840aa 100644 --- a/src/aks-preview/azext_aks_preview/managed_cluster_decorator.py +++ b/src/aks-preview/azext_aks_preview/managed_cluster_decorator.py @@ -3046,6 +3046,51 @@ def get_opentelemetry_metrics_port(self) -> Union[int, None]: return opentelemetry_metrics_port + def get_opentelemetry_metrics_port_grpc(self) -> Union[int, None]: + """Obtain the value of opentelemetry_metrics_port_grpc. + :return: int or None + """ + opentelemetry_metrics_port_grpc = self.raw_param.get("opentelemetry_metrics_port_grpc") + + # Validate that port is only specified when OpenTelemetry metrics are enabled + if opentelemetry_metrics_port_grpc is not None: + # Validate that port is not negative + if opentelemetry_metrics_port_grpc < 0: + raise InvalidArgumentValueError( + "--opentelemetry-metrics-port-grpc must be a non-negative integer." + ) + # Check if disabling Azure Monitor metrics - port specification is invalid + if self.get_disable_azure_monitor_metrics(): + raise InvalidArgumentValueError( + "--opentelemetry-metrics-port-grpc cannot be specified when --disable-azure-monitor-metrics is used." + ) + + # For CREATE: --enable-opentelemetry-metrics must be explicitly specified + if self.decorator_mode == DecoratorMode.CREATE: + if not self.get_enable_opentelemetry_metrics(): + raise InvalidArgumentValueError( + "--opentelemetry-metrics-port-grpc can only be specified when " + "--enable-opentelemetry-metrics is also specified." + ) + # For UPDATE: allow if either explicitly enabling OR already enabled in cluster + elif self.decorator_mode == DecoratorMode.UPDATE: + explicitly_enabling = self.get_enable_opentelemetry_metrics() + already_enabled = ( + self.mc and + self.mc.azure_monitor_profile and + self.mc.azure_monitor_profile.app_monitoring and + self.mc.azure_monitor_profile.app_monitoring.open_telemetry_metrics and + self.mc.azure_monitor_profile.app_monitoring.open_telemetry_metrics.enabled + ) + if not explicitly_enabling and not already_enabled: + raise InvalidArgumentValueError( + "--opentelemetry-metrics-port-grpc can only be specified when " + "--enable-opentelemetry-metrics is also specified or " + "OpenTelemetry metrics are already enabled." + ) + + return opentelemetry_metrics_port_grpc + def _get_enable_opentelemetry_logs(self, enable_validation: bool = False) -> bool: """Internal function to obtain the value of enable_opentelemetry_logs. This function supports the option of enable_validation. When enabled, if both enable_opentelemetry_logs and @@ -3184,6 +3229,51 @@ def get_opentelemetry_logs_port(self) -> Union[int, None]: return opentelemetry_logs_port + def get_opentelemetry_logs_traces_port_grpc(self) -> Union[int, None]: + """Obtain the value of opentelemetry_logs_traces_port_grpc. + :return: int or None + """ + opentelemetry_logs_traces_port_grpc = self.raw_param.get("opentelemetry_logs_traces_port_grpc") + + # Validate that port is only specified when OpenTelemetry logs are enabled + if opentelemetry_logs_traces_port_grpc is not None: + # Validate that port is not negative + if opentelemetry_logs_traces_port_grpc < 0: + raise InvalidArgumentValueError( + "--opentelemetry-logs-traces-port-grpc must be a non-negative integer." + ) + # Check if disabling Azure Monitor logs - port specification is invalid + if self.get_disable_azure_monitor_logs(): + raise InvalidArgumentValueError( + "--opentelemetry-logs-traces-port-grpc cannot be specified when --disable-azure-monitor-logs is used." + ) + + # For CREATE: --enable-opentelemetry-logs-traces must be explicitly specified + if self.decorator_mode == DecoratorMode.CREATE: + if not self.get_enable_opentelemetry_logs(): + raise InvalidArgumentValueError( + "--opentelemetry-logs-traces-port-grpc can only be specified when " + "--enable-opentelemetry-logs-traces is also specified." + ) + # For UPDATE: allow if either explicitly enabling OR already enabled in cluster + elif self.decorator_mode == DecoratorMode.UPDATE: + explicitly_enabling = self.get_enable_opentelemetry_logs() + already_enabled = ( + self.mc and + self.mc.azure_monitor_profile and + self.mc.azure_monitor_profile.app_monitoring and + self.mc.azure_monitor_profile.app_monitoring.open_telemetry_logs_and_traces and + self.mc.azure_monitor_profile.app_monitoring.open_telemetry_logs_and_traces.enabled + ) + if not explicitly_enabling and not already_enabled: + raise InvalidArgumentValueError( + "--opentelemetry-logs-traces-port-grpc can only be specified when " + "--enable-opentelemetry-logs-traces is also specified or " + "OpenTelemetry logs are already enabled." + ) + + return opentelemetry_logs_traces_port_grpc + def get_enable_high_log_scale_mode(self) -> Union[bool, None]: """Obtain the value of enable_high_log_scale_mode. @@ -5083,8 +5173,11 @@ def _setup_opentelemetry_metrics(self, mc: ManagedCluster) -> None: otlp_metrics_config = ( self.models.ManagedClusterAzureMonitorProfileAppMonitoringOpenTelemetryMetrics(enabled=True)) metrics_port = self.context.get_opentelemetry_metrics_port() - if metrics_port: + if metrics_port is not None: otlp_metrics_config.http_port = metrics_port + metrics_port_grpc = self.context.get_opentelemetry_metrics_port_grpc() + if metrics_port_grpc is not None: + otlp_metrics_config.grpc_port = metrics_port_grpc mc.azure_monitor_profile.app_monitoring.open_telemetry_metrics = otlp_metrics_config @@ -5096,8 +5189,9 @@ def _disable_opentelemetry_metrics(self, mc: ManagedCluster) -> None: self.models.ManagedClusterAzureMonitorProfileAppMonitoringOpenTelemetryMetrics(enabled=False)) else: mc.azure_monitor_profile.app_monitoring.open_telemetry_metrics.enabled = False - # Clear the port when disabling OpenTelemetry metrics + # Clear the ports when disabling OpenTelemetry metrics mc.azure_monitor_profile.app_monitoring.open_telemetry_metrics.http_port = None + mc.azure_monitor_profile.app_monitoring.open_telemetry_metrics.grpc_port = None def _setup_opentelemetry_logs(self, mc: ManagedCluster) -> None: """Set up OpenTelemetry logs configuration.""" @@ -5106,8 +5200,11 @@ def _setup_opentelemetry_logs(self, mc: ManagedCluster) -> None: otel_logs_cls = self.models.ManagedClusterAzureMonitorProfileAppMonitoringOpenTelemetryLogsAndTraces otlp_logs_config = otel_logs_cls(enabled=True) logs_port = self.context.get_opentelemetry_logs_port() - if logs_port: + if logs_port is not None: otlp_logs_config.http_port = logs_port + logs_port_grpc = self.context.get_opentelemetry_logs_traces_port_grpc() + if logs_port_grpc is not None: + otlp_logs_config.grpc_port = logs_port_grpc mc.azure_monitor_profile.app_monitoring.open_telemetry_logs_and_traces = otlp_logs_config @@ -5121,8 +5218,9 @@ def _disable_opentelemetry_logs(self, mc: ManagedCluster) -> None: ) else: mc.azure_monitor_profile.app_monitoring.open_telemetry_logs_and_traces.enabled = False - # Clear the port when disabling OpenTelemetry logs + # Clear the ports when disabling OpenTelemetry logs mc.azure_monitor_profile.app_monitoring.open_telemetry_logs_and_traces.http_port = None + mc.azure_monitor_profile.app_monitoring.open_telemetry_logs_and_traces.grpc_port = None def set_up_azure_monitor_profile(self, mc: ManagedCluster) -> ManagedCluster: """Set up azure monitor profile for the ManagedCluster object. @@ -7553,8 +7651,10 @@ def update_azure_monitor_profile(self, mc: ManagedCluster) -> ManagedCluster: otlp_metrics_config = ( self.models.ManagedClusterAzureMonitorProfileAppMonitoringOpenTelemetryMetrics(enabled=True) ) - if self.context.get_opentelemetry_metrics_port(): + if self.context.get_opentelemetry_metrics_port() is not None: otlp_metrics_config.http_port = self.context.get_opentelemetry_metrics_port() + if self.context.get_opentelemetry_metrics_port_grpc() is not None: + otlp_metrics_config.grpc_port = self.context.get_opentelemetry_metrics_port_grpc() mc.azure_monitor_profile.app_monitoring.open_telemetry_metrics = otlp_metrics_config @@ -7570,8 +7670,10 @@ def update_azure_monitor_profile(self, mc: ManagedCluster) -> ManagedCluster: # Configure OpenTelemetry logs with custom port if provided otel_logs_cls = self.models.ManagedClusterAzureMonitorProfileAppMonitoringOpenTelemetryLogsAndTraces otlp_logs_config = otel_logs_cls(enabled=True) - if self.context.get_opentelemetry_logs_port(): + if self.context.get_opentelemetry_logs_port() is not None: otlp_logs_config.http_port = self.context.get_opentelemetry_logs_port() + if self.context.get_opentelemetry_logs_traces_port_grpc() is not None: + otlp_logs_config.grpc_port = self.context.get_opentelemetry_logs_traces_port_grpc() mc.azure_monitor_profile.app_monitoring.open_telemetry_logs_and_traces = otlp_logs_config @@ -7606,6 +7708,7 @@ def update_azure_monitor_profile(self, mc: ManagedCluster) -> ManagedCluster: else: mc.azure_monitor_profile.app_monitoring.open_telemetry_metrics.enabled = False mc.azure_monitor_profile.app_monitoring.open_telemetry_metrics.http_port = None + mc.azure_monitor_profile.app_monitoring.open_telemetry_metrics.grpc_port = None # Handle disable OpenTelemetry logs updates if self.context.get_disable_opentelemetry_logs(): @@ -7624,9 +7727,10 @@ def update_azure_monitor_profile(self, mc: ManagedCluster) -> ManagedCluster: else: mc.azure_monitor_profile.app_monitoring.open_telemetry_logs_and_traces.enabled = False mc.azure_monitor_profile.app_monitoring.open_telemetry_logs_and_traces.http_port = None + mc.azure_monitor_profile.app_monitoring.open_telemetry_logs_and_traces.grpc_port = None # Handle standalone port updates for OpenTelemetry metrics - if (self.context.get_opentelemetry_metrics_port() and + if (self.context.get_opentelemetry_metrics_port() is not None and not self.context.get_enable_opentelemetry_metrics() and not self.context.get_disable_opentelemetry_metrics()): # Only update port if OpenTelemetry metrics is already enabled and we're not changing the enabled state @@ -7637,8 +7741,19 @@ def update_azure_monitor_profile(self, mc: ManagedCluster) -> ManagedCluster: metrics_port = self.context.get_opentelemetry_metrics_port() mc.azure_monitor_profile.app_monitoring.open_telemetry_metrics.http_port = metrics_port + # Handle standalone gRPC port updates for OpenTelemetry metrics + if (self.context.get_opentelemetry_metrics_port_grpc() is not None and + not self.context.get_enable_opentelemetry_metrics() and + not self.context.get_disable_opentelemetry_metrics()): + if (mc.azure_monitor_profile and + mc.azure_monitor_profile.app_monitoring and + mc.azure_monitor_profile.app_monitoring.open_telemetry_metrics and + mc.azure_monitor_profile.app_monitoring.open_telemetry_metrics.enabled): + metrics_port_grpc = self.context.get_opentelemetry_metrics_port_grpc() + mc.azure_monitor_profile.app_monitoring.open_telemetry_metrics.grpc_port = metrics_port_grpc + # Handle standalone port updates for OpenTelemetry logs - if (self.context.get_opentelemetry_logs_port() and + if (self.context.get_opentelemetry_logs_port() is not None and not self.context.get_enable_opentelemetry_logs() and not self.context.get_disable_opentelemetry_logs()): # Only update port if OpenTelemetry logs is already enabled and we're not changing the enabled state @@ -7649,6 +7764,17 @@ def update_azure_monitor_profile(self, mc: ManagedCluster) -> ManagedCluster: logs_port = self.context.get_opentelemetry_logs_port() mc.azure_monitor_profile.app_monitoring.open_telemetry_logs_and_traces.http_port = logs_port + # Handle standalone gRPC port updates for OpenTelemetry logs + if (self.context.get_opentelemetry_logs_traces_port_grpc() is not None and + not self.context.get_enable_opentelemetry_logs() and + not self.context.get_disable_opentelemetry_logs()): + if (mc.azure_monitor_profile and + mc.azure_monitor_profile.app_monitoring and + mc.azure_monitor_profile.app_monitoring.open_telemetry_logs_and_traces and + mc.azure_monitor_profile.app_monitoring.open_telemetry_logs_and_traces.enabled): + logs_port_grpc = self.context.get_opentelemetry_logs_traces_port_grpc() + mc.azure_monitor_profile.app_monitoring.open_telemetry_logs_and_traces.grpc_port = logs_port_grpc + # TODO: should remove get value from enable_azuremonitormetrics once the option is removed # TODO: should remove get value from disable_azuremonitormetrics once the option is removed azure_monitor_metrics = (self.context.raw_param.get("enable_azuremonitormetrics") or @@ -7659,8 +7785,10 @@ def update_azure_monitor_profile(self, mc: ManagedCluster) -> ManagedCluster: self.context.raw_param.get("enable_opentelemetry_logs") or self.context.raw_param.get("disable_opentelemetry_metrics") or self.context.raw_param.get("disable_opentelemetry_logs") or - self.context.get_opentelemetry_metrics_port() or - self.context.get_opentelemetry_logs_port()) + self.context.get_opentelemetry_metrics_port() is not None or + self.context.get_opentelemetry_metrics_port_grpc() is not None or + self.context.get_opentelemetry_logs_port() is not None or + self.context.get_opentelemetry_logs_traces_port_grpc() is not None) if azure_monitor_metrics or opentelemetry: ensure_azure_monitor_profile_prerequisites( self.cmd, @@ -8653,8 +8781,9 @@ def _disable_azure_monitor_logs(self, mc: ManagedCluster) -> None: # Also disable OpenTelemetry logs when disabling Azure Monitor logs if opentelemetry_logs_enabled: mc.azure_monitor_profile.app_monitoring.open_telemetry_logs_and_traces.enabled = False - # Clear the port when disabling OpenTelemetry logs + # Clear the ports when disabling OpenTelemetry logs mc.azure_monitor_profile.app_monitoring.open_telemetry_logs_and_traces.http_port = None + mc.azure_monitor_profile.app_monitoring.open_telemetry_logs_and_traces.grpc_port = None def _disable_azure_monitor_metrics(self, mc: ManagedCluster) -> None: """Disable Azure Monitor metrics configuration.""" @@ -8695,8 +8824,9 @@ def _disable_azure_monitor_metrics(self, mc: ManagedCluster) -> None: # Also disable OpenTelemetry metrics when disabling Azure Monitor metrics if opentelemetry_metrics_enabled: mc.azure_monitor_profile.app_monitoring.open_telemetry_metrics.enabled = False - # Clear the port when disabling OpenTelemetry metrics + # Clear the ports when disabling OpenTelemetry metrics mc.azure_monitor_profile.app_monitoring.open_telemetry_metrics.http_port = None + mc.azure_monitor_profile.app_monitoring.open_telemetry_metrics.grpc_port = None def update_addon_profiles(self, mc: ManagedCluster) -> ManagedCluster: """Update addon profiles for the ManagedCluster object. diff --git a/src/aks-preview/azext_aks_preview/tests/latest/test_aks_commands.py b/src/aks-preview/azext_aks_preview/tests/latest/test_aks_commands.py index a0d4c78f94a..8df1c4f88db 100644 --- a/src/aks-preview/azext_aks_preview/tests/latest/test_aks_commands.py +++ b/src/aks-preview/azext_aks_preview/tests/latest/test_aks_commands.py @@ -16802,7 +16802,7 @@ def test_aks_create_with_azuremonitormetrics_and_opentelemetry(self, resource_gr create_cmd = ( 'aks create --resource-group={resource_group} --name={name} --location={location} --ssh-key-value={ssh_key_value} --node-vm-size={node_vm_size} ' - '--enable-managed-identity --enable-azure-monitor-metrics --enable-opentelemetry-metrics --opentelemetry-metrics-port-http=8080 ' + '--enable-managed-identity --enable-azure-monitor-metrics --enable-opentelemetry-metrics --opentelemetry-metrics-port-http=8080 --opentelemetry-metrics-port-grpc=8082 ' '--aks-custom-headers AKSHTTPCustomFeatures=Microsoft.ContainerService/AzureMonitorAppMonitoringPreview --output=json' ) self.cmd(create_cmd, checks=[ @@ -16826,6 +16826,7 @@ def test_aks_create_with_azuremonitormetrics_and_opentelemetry(self, resource_gr self.check('azureMonitorProfile.metrics.enabled', True), self.check('azureMonitorProfile.appMonitoring.openTelemetryMetrics.enabled', True), self.check('azureMonitorProfile.appMonitoring.openTelemetryMetrics.httpPort', 8080), + self.check('azureMonitorProfile.appMonitoring.openTelemetryMetrics.grpcPort', 8082), ]) # delete diff --git a/src/aks-preview/azext_aks_preview/tests/latest/test_managed_cluster_decorator.py b/src/aks-preview/azext_aks_preview/tests/latest/test_managed_cluster_decorator.py index 203e266488e..08b7ba86556 100644 --- a/src/aks-preview/azext_aks_preview/tests/latest/test_managed_cluster_decorator.py +++ b/src/aks-preview/azext_aks_preview/tests/latest/test_managed_cluster_decorator.py @@ -8645,6 +8645,30 @@ def test_get_opentelemetry_metrics_port(self): ) self.assertEqual(ctx_2.get_opentelemetry_metrics_port(), 8080) + def test_get_opentelemetry_metrics_port_grpc(self): + # default + ctx_1 = AKSPreviewManagedClusterContext( + self.cmd, + AKSManagedClusterParamDict({"opentelemetry_metrics_port_grpc": None}), + self.models, + decorator_mode=DecoratorMode.CREATE, + ) + self.assertEqual(ctx_1.get_opentelemetry_metrics_port_grpc(), None) + + # custom value + ctx_2 = AKSPreviewManagedClusterContext( + self.cmd, + AKSManagedClusterParamDict( + { + "opentelemetry_metrics_port_grpc": 8082, + "enable_opentelemetry_metrics": True, + } + ), + self.models, + decorator_mode=DecoratorMode.CREATE, + ) + self.assertEqual(ctx_2.get_opentelemetry_metrics_port_grpc(), 8082) + def test_get_enable_opentelemetry_logs(self): # default ctx_1 = AKSPreviewManagedClusterContext( @@ -8704,6 +8728,80 @@ def test_get_opentelemetry_logs_port(self): ) self.assertEqual(ctx_2.get_opentelemetry_logs_port(), 8081) + def test_get_opentelemetry_logs_traces_port_grpc(self): + # default + ctx_1 = AKSPreviewManagedClusterContext( + self.cmd, + AKSManagedClusterParamDict({"opentelemetry_logs_traces_port_grpc": None}), + self.models, + decorator_mode=DecoratorMode.CREATE, + ) + self.assertEqual(ctx_1.get_opentelemetry_logs_traces_port_grpc(), None) + + # custom value + ctx_2 = AKSPreviewManagedClusterContext( + self.cmd, + AKSManagedClusterParamDict( + {"opentelemetry_logs_traces_port_grpc": 8083, "enable_opentelemetry_logs": True} + ), + self.models, + decorator_mode=DecoratorMode.CREATE, + ) + self.assertEqual(ctx_2.get_opentelemetry_logs_traces_port_grpc(), 8083) + + def test_set_up_azure_monitor_profile_with_opentelemetry_grpc_ports(self): + # Test enabling OpenTelemetry metrics with both HTTP and gRPC ports + dec_1 = AKSPreviewManagedClusterCreateDecorator( + self.cmd, + self.client, + { + "enable_azure_monitor_metrics": True, + "enable_opentelemetry_metrics": True, + "opentelemetry_metrics_port": 8080, + "opentelemetry_metrics_port_grpc": 8082, + }, + CUSTOM_MGMT_AKS_PREVIEW, + ) + + mc_1 = self.models.ManagedCluster( + location="test_location", + identity=self.models.ManagedClusterIdentity(type="SystemAssigned"), + ) + dec_1.context.attach_mc(mc_1) + dec_mc_1 = dec_1.set_up_azure_monitor_profile(mc_1) + + otlp_metrics = dec_mc_1.azure_monitor_profile.app_monitoring.open_telemetry_metrics + self.assertTrue(otlp_metrics.enabled) + self.assertEqual(otlp_metrics.http_port, 8080) + self.assertEqual(otlp_metrics.grpc_port, 8082) + + def test_disable_opentelemetry_metrics_clears_grpc_port(self): + # Disabling OpenTelemetry metrics must clear BOTH http and grpc ports + dec_1 = AKSPreviewManagedClusterCreateDecorator( + self.cmd, + self.client, + {"disable_opentelemetry_metrics": True}, + CUSTOM_MGMT_AKS_PREVIEW, + ) + existing_metrics = self.models.ManagedClusterAzureMonitorProfileAppMonitoringOpenTelemetryMetrics( + enabled=True, http_port=8080, grpc_port=8082 + ) + app_monitoring = self.models.ManagedClusterAzureMonitorProfileAppMonitoring( + open_telemetry_metrics=existing_metrics + ) + mc_1 = self.models.ManagedCluster( + location="test_location", + azure_monitor_profile=self.models.ManagedClusterAzureMonitorProfile( + app_monitoring=app_monitoring + ), + ) + dec_1.context.attach_mc(mc_1) + dec_1._disable_opentelemetry_metrics(mc_1) + otlp_metrics = mc_1.azure_monitor_profile.app_monitoring.open_telemetry_metrics + self.assertFalse(otlp_metrics.enabled) + self.assertIsNone(otlp_metrics.http_port) + self.assertIsNone(otlp_metrics.grpc_port) + def test_set_up_azure_monitor_profile_with_opentelemetry(self): # Test enabling Azure Monitor metrics with OpenTelemetry metrics dec_1 = AKSPreviewManagedClusterCreateDecorator( diff --git a/src/aks-preview/azext_aks_preview/tests/latest/test_validators.py b/src/aks-preview/azext_aks_preview/tests/latest/test_validators.py index 82ce6895277..1432e65a41e 100644 --- a/src/aks-preview/azext_aks_preview/tests/latest/test_validators.py +++ b/src/aks-preview/azext_aks_preview/tests/latest/test_validators.py @@ -2037,9 +2037,12 @@ def test_valid_custom_endpoints(self): class OpenTelemetryPortsNamespace: - def __init__(self, opentelemetry_metrics_port=None, opentelemetry_logs_port=None): + def __init__(self, opentelemetry_metrics_port=None, opentelemetry_logs_port=None, + opentelemetry_metrics_port_grpc=None, opentelemetry_logs_traces_port_grpc=None): self.opentelemetry_metrics_port = opentelemetry_metrics_port self.opentelemetry_logs_port = opentelemetry_logs_port + self.opentelemetry_metrics_port_grpc = opentelemetry_metrics_port_grpc + self.opentelemetry_logs_traces_port_grpc = opentelemetry_logs_traces_port_grpc class OpenTelemetryDeprecatedAliasNamespace: @@ -2116,14 +2119,52 @@ def test_different_ports_specified(self): ) validators.validate_opentelemetry_ports(namespace) - def test_same_ports_throws_error(self): + def test_all_four_distinct_ports_specified(self): + namespace = OpenTelemetryPortsNamespace( + opentelemetry_metrics_port=8080, + opentelemetry_metrics_port_grpc=8081, + opentelemetry_logs_port=8082, + opentelemetry_logs_traces_port_grpc=8083, + ) + validators.validate_opentelemetry_ports(namespace) + + def test_same_http_ports_throws_error(self): namespace = OpenTelemetryPortsNamespace( opentelemetry_metrics_port=8080, opentelemetry_logs_port=8080 ) err = ( - "OpenTelemetry metrics port and logs port cannot be the same. " - "Please specify different ports for --opentelemetry-metrics-port-http and --opentelemetry-logs-traces-port-http." + "OpenTelemetry ports must all be different. " + "--opentelemetry-metrics-port-http and --opentelemetry-logs-traces-port-http " + "cannot both be set to 8080." + ) + with self.assertRaises(ArgumentUsageError) as cm: + validators.validate_opentelemetry_ports(namespace) + self.assertEqual(str(cm.exception), err) + + def test_same_metrics_http_and_grpc_throws_error(self): + namespace = OpenTelemetryPortsNamespace( + opentelemetry_metrics_port=8080, + opentelemetry_metrics_port_grpc=8080, + ) + err = ( + "OpenTelemetry ports must all be different. " + "--opentelemetry-metrics-port-http and --opentelemetry-metrics-port-grpc " + "cannot both be set to 8080." + ) + with self.assertRaises(ArgumentUsageError) as cm: + validators.validate_opentelemetry_ports(namespace) + self.assertEqual(str(cm.exception), err) + + def test_same_grpc_ports_throws_error(self): + namespace = OpenTelemetryPortsNamespace( + opentelemetry_metrics_port_grpc=9090, + opentelemetry_logs_traces_port_grpc=9090, + ) + err = ( + "OpenTelemetry ports must all be different. " + "--opentelemetry-metrics-port-grpc and --opentelemetry-logs-traces-port-grpc " + "cannot both be set to 9090." ) with self.assertRaises(ArgumentUsageError) as cm: validators.validate_opentelemetry_ports(namespace) @@ -2131,28 +2172,42 @@ def test_same_ports_throws_error(self): def test_metrics_port_below_range(self): namespace = OpenTelemetryPortsNamespace(opentelemetry_metrics_port=0) - err = "OpenTelemetry metrics port must be between 1 and 65535, got 0." + err = "OpenTelemetry port --opentelemetry-metrics-port-http must be between 1 and 65535, got 0." with self.assertRaises(ArgumentUsageError) as cm: validators.validate_opentelemetry_ports(namespace) self.assertEqual(str(cm.exception), err) def test_metrics_port_above_range(self): namespace = OpenTelemetryPortsNamespace(opentelemetry_metrics_port=65536) - err = "OpenTelemetry metrics port must be between 1 and 65535, got 65536." + err = "OpenTelemetry port --opentelemetry-metrics-port-http must be between 1 and 65535, got 65536." + with self.assertRaises(ArgumentUsageError) as cm: + validators.validate_opentelemetry_ports(namespace) + self.assertEqual(str(cm.exception), err) + + def test_metrics_grpc_port_above_range(self): + namespace = OpenTelemetryPortsNamespace(opentelemetry_metrics_port_grpc=70000) + err = "OpenTelemetry port --opentelemetry-metrics-port-grpc must be between 1 and 65535, got 70000." with self.assertRaises(ArgumentUsageError) as cm: validators.validate_opentelemetry_ports(namespace) self.assertEqual(str(cm.exception), err) def test_logs_port_below_range(self): namespace = OpenTelemetryPortsNamespace(opentelemetry_logs_port=-1) - err = "OpenTelemetry logs port must be between 1 and 65535, got -1." + err = "OpenTelemetry port --opentelemetry-logs-traces-port-http must be between 1 and 65535, got -1." with self.assertRaises(ArgumentUsageError) as cm: validators.validate_opentelemetry_ports(namespace) self.assertEqual(str(cm.exception), err) def test_logs_port_above_range(self): namespace = OpenTelemetryPortsNamespace(opentelemetry_logs_port=100000) - err = "OpenTelemetry logs port must be between 1 and 65535, got 100000." + err = "OpenTelemetry port --opentelemetry-logs-traces-port-http must be between 1 and 65535, got 100000." + with self.assertRaises(ArgumentUsageError) as cm: + validators.validate_opentelemetry_ports(namespace) + self.assertEqual(str(cm.exception), err) + + def test_logs_grpc_port_below_range(self): + namespace = OpenTelemetryPortsNamespace(opentelemetry_logs_traces_port_grpc=0) + err = "OpenTelemetry port --opentelemetry-logs-traces-port-grpc must be between 1 and 65535, got 0." with self.assertRaises(ArgumentUsageError) as cm: validators.validate_opentelemetry_ports(namespace) self.assertEqual(str(cm.exception), err) @@ -2166,6 +2221,7 @@ def test_valid_edge_case_ports(self): validators.validate_opentelemetry_ports(namespace) + class OpenTelemetryMetricsDependenciesNamespace: def __init__(self, enable_opentelemetry_metrics=False, disable_opentelemetry_metrics=False, enable_azure_monitor_metrics=False, enable_azuremonitormetrics=False): @@ -2322,7 +2378,8 @@ def __init__(self, enable_opentelemetry_metrics=False, disable_opentelemetry_met enable_opentelemetry_logs=False, disable_opentelemetry_logs=False, enable_azure_monitor_metrics=False, enable_azuremonitormetrics=False, enable_azure_monitor_logs=False, - opentelemetry_metrics_port=None, opentelemetry_logs_port=None): + opentelemetry_metrics_port=None, opentelemetry_logs_port=None, + opentelemetry_metrics_port_grpc=None, opentelemetry_logs_traces_port_grpc=None): self.enable_opentelemetry_metrics = enable_opentelemetry_metrics self.disable_opentelemetry_metrics = disable_opentelemetry_metrics self.enable_opentelemetry_logs = enable_opentelemetry_logs @@ -2332,6 +2389,8 @@ def __init__(self, enable_opentelemetry_metrics=False, disable_opentelemetry_met self.enable_azure_monitor_logs = enable_azure_monitor_logs self.opentelemetry_metrics_port = opentelemetry_metrics_port self.opentelemetry_logs_port = opentelemetry_logs_port + self.opentelemetry_metrics_port_grpc = opentelemetry_metrics_port_grpc + self.opentelemetry_logs_traces_port_grpc = opentelemetry_logs_traces_port_grpc class TestValidateAzureMonitorAndOpenTelemetryForCreate(unittest.TestCase): @@ -2355,8 +2414,9 @@ def test_port_conflict_throws_error(self): opentelemetry_logs_port=8080 ) err = ( - "OpenTelemetry metrics port and logs port cannot be the same. " - "Please specify different ports for --opentelemetry-metrics-port-http and --opentelemetry-logs-traces-port-http." + "OpenTelemetry ports must all be different. " + "--opentelemetry-metrics-port-http and --opentelemetry-logs-traces-port-http " + "cannot both be set to 8080." ) with self.assertRaises(ArgumentUsageError) as cm: validators.validate_azure_monitor_and_opentelemetry_for_create(namespace) @@ -2405,8 +2465,9 @@ def test_port_conflict_throws_error_for_update(self): opentelemetry_logs_port=8080 ) err = ( - "OpenTelemetry metrics port and logs port cannot be the same. " - "Please specify different ports for --opentelemetry-metrics-port-http and --opentelemetry-logs-traces-port-http." + "OpenTelemetry ports must all be different. " + "--opentelemetry-metrics-port-http and --opentelemetry-logs-traces-port-http " + "cannot both be set to 8080." ) with self.assertRaises(ArgumentUsageError) as cm: validators.validate_azure_monitor_and_opentelemetry_for_update(namespace) diff --git a/src/aks-preview/setup.py b/src/aks-preview/setup.py index 9fccc8c9ea6..18cd0c71ffc 100644 --- a/src/aks-preview/setup.py +++ b/src/aks-preview/setup.py @@ -9,7 +9,7 @@ from setuptools import find_packages, setup -VERSION = "21.0.0b10" +VERSION = "21.0.0b11" CLASSIFIERS = [ "Development Status :: 4 - Beta", From c4b189ccc6e37e494efb1054fa2a5534f7e6f7c5 Mon Sep 17 00:00:00 2001 From: Christoph Butz Date: Thu, 2 Jul 2026 12:05:58 +0100 Subject: [PATCH 03/14] [aks-preview] Extend OTLP live tests with gRPC port coverage for logs and metrics Adds --opentelemetry-*-port-grpc flags and grpcPort assertions to the live OpenTelemetry integration scenarios so gRPC is exercised symmetrically for both the metrics and logs/traces signals (previously only metrics-create asserted grpcPort). The comprehensive monitoring test also now asserts that disabling OpenTelemetry metrics/logs clears both httpPort and grpcPort. --- .../tests/latest/test_aks_commands.py | 30 ++++++++++++++----- 1 file changed, 22 insertions(+), 8 deletions(-) diff --git a/src/aks-preview/azext_aks_preview/tests/latest/test_aks_commands.py b/src/aks-preview/azext_aks_preview/tests/latest/test_aks_commands.py index 8df1c4f88db..e617089a6b5 100644 --- a/src/aks-preview/azext_aks_preview/tests/latest/test_aks_commands.py +++ b/src/aks-preview/azext_aks_preview/tests/latest/test_aks_commands.py @@ -16565,7 +16565,7 @@ def test_aks_create_with_azuremonitorlogs_and_opentelemetry(self, resource_group create_cmd = ( 'aks create --resource-group={resource_group} --name={name} --location={location} --ssh-key-value={ssh_key_value} --node-vm-size={node_vm_size} ' - '--enable-managed-identity --enable-azure-monitor-logs --enable-opentelemetry-logs-traces --opentelemetry-logs-traces-port-http=8080 ' + '--enable-managed-identity --enable-azure-monitor-logs --enable-opentelemetry-logs-traces --opentelemetry-logs-traces-port-http=8080 --opentelemetry-logs-traces-port-grpc=8082 ' '--aks-custom-headers AKSHTTPCustomFeatures=Microsoft.ContainerService/AzureMonitorAppMonitoringPreview --output=json' ) self.cmd(create_cmd, checks=[ @@ -16575,6 +16575,7 @@ def test_aks_create_with_azuremonitorlogs_and_opentelemetry(self, resource_group self.check('addonProfiles.omsagent.config.useAADAuth', 'true'), self.check('azureMonitorProfile.appMonitoring.openTelemetryLogsAndTraces.enabled', True), self.check('azureMonitorProfile.appMonitoring.openTelemetryLogsAndTraces.httpPort', 8080), + self.check('azureMonitorProfile.appMonitoring.openTelemetryLogsAndTraces.grpcPort', 8082), ]) # delete @@ -16607,7 +16608,7 @@ def test_aks_update_with_azuremonitorlogs_and_opentelemetry(self, resource_group # update: enable-azure-monitor-logs with OpenTelemetry logs update_cmd = ( 'aks update --resource-group={resource_group} --name={name} --yes --output=json ' - '--enable-azure-monitor-logs --enable-opentelemetry-logs-traces --opentelemetry-logs-traces-port-http=9090 ' + '--enable-azure-monitor-logs --enable-opentelemetry-logs-traces --opentelemetry-logs-traces-port-http=9090 --opentelemetry-logs-traces-port-grpc=9092 ' '--aks-custom-headers AKSHTTPCustomFeatures=Microsoft.ContainerService/AzureMonitorAppMonitoringPreview' ) self.cmd(update_cmd, checks=[ @@ -16617,6 +16618,7 @@ def test_aks_update_with_azuremonitorlogs_and_opentelemetry(self, resource_group self.check('addonProfiles.omsagent.config.useAADAuth', 'true'), self.check('azureMonitorProfile.appMonitoring.openTelemetryLogsAndTraces.enabled', True), self.check('azureMonitorProfile.appMonitoring.openTelemetryLogsAndTraces.httpPort', 9090), + self.check('azureMonitorProfile.appMonitoring.openTelemetryLogsAndTraces.grpcPort', 9092), ]) # update: disable OpenTelemetry logs but keep Azure Monitor logs @@ -17162,8 +17164,8 @@ def test_aks_comprehensive_monitoring_integration(self, resource_group, resource create_cmd = ( 'aks create --resource-group={resource_group} --name={name} --location={location} --ssh-key-value={ssh_key_value} --node-vm-size={node_vm_size} ' '--enable-managed-identity --enable-azure-monitor-logs --enable-azure-monitor-metrics --enable-azure-monitor-app-monitoring ' - '--enable-opentelemetry-logs-traces --opentelemetry-logs-traces-port-http=8080 ' - '--enable-opentelemetry-metrics --opentelemetry-metrics-port-http=8081 ' + '--enable-opentelemetry-logs-traces --opentelemetry-logs-traces-port-http=8080 --opentelemetry-logs-traces-port-grpc=8082 ' + '--enable-opentelemetry-metrics --opentelemetry-metrics-port-http=8081 --opentelemetry-metrics-port-grpc=8083 ' '--enable-windows-recording-rules ' '--aks-custom-headers AKSHTTPCustomFeatures=Microsoft.ContainerService/AzureMonitorAppMonitoringPreview --output=json' ) @@ -17196,9 +17198,11 @@ def test_aks_comprehensive_monitoring_integration(self, resource_group, resource # OpenTelemetry logs checks self.check('azureMonitorProfile.appMonitoring.openTelemetryLogsAndTraces.enabled', True), self.check('azureMonitorProfile.appMonitoring.openTelemetryLogsAndTraces.httpPort', 8080), + self.check('azureMonitorProfile.appMonitoring.openTelemetryLogsAndTraces.grpcPort', 8082), # OpenTelemetry metrics checks self.check('azureMonitorProfile.appMonitoring.openTelemetryMetrics.enabled', True), self.check('azureMonitorProfile.appMonitoring.openTelemetryMetrics.httpPort', 8081), + self.check('azureMonitorProfile.appMonitoring.openTelemetryMetrics.grpcPort', 8083), ]) # Phase 2: Update - disable only OpenTelemetry logs (keep everything else) @@ -17229,6 +17233,9 @@ def test_aks_comprehensive_monitoring_integration(self, resource_group, resource self.check('azureMonitorProfile.appMonitoring.autoInstrumentation.enabled', True), # OpenTelemetry logs should be disabled self.check('azureMonitorProfile.appMonitoring.openTelemetryLogsAndTraces.enabled', False), + # Disabling OpenTelemetry logs should clear both ports + self.check('azureMonitorProfile.appMonitoring.openTelemetryLogsAndTraces.httpPort', None), + self.check('azureMonitorProfile.appMonitoring.openTelemetryLogsAndTraces.grpcPort', None), # OpenTelemetry metrics should still be enabled self.check('azureMonitorProfile.appMonitoring.openTelemetryMetrics.enabled', True), ]) @@ -17261,13 +17268,16 @@ def test_aks_comprehensive_monitoring_integration(self, resource_group, resource self.check('azureMonitorProfile.appMonitoring.autoInstrumentation.enabled', True), # OpenTelemetry metrics should be disabled self.check('azureMonitorProfile.appMonitoring.openTelemetryMetrics.enabled', False), + # Disabling OpenTelemetry metrics should clear both ports + self.check('azureMonitorProfile.appMonitoring.openTelemetryMetrics.httpPort', None), + self.check('azureMonitorProfile.appMonitoring.openTelemetryMetrics.grpcPort', None), ]) # Phase 4: Update - re-enable all OpenTelemetry features with different ports update_cmd = ( 'aks update --resource-group={resource_group} --name={name} --yes --output=json ' - '--enable-opentelemetry-logs-traces --opentelemetry-logs-traces-port-http=9090 ' - '--enable-opentelemetry-metrics --opentelemetry-metrics-port-http=9091 ' + '--enable-opentelemetry-logs-traces --opentelemetry-logs-traces-port-http=9090 --opentelemetry-logs-traces-port-grpc=9092 ' + '--enable-opentelemetry-metrics --opentelemetry-metrics-port-http=9091 --opentelemetry-metrics-port-grpc=9093 ' '--aks-custom-headers AKSHTTPCustomFeatures=Microsoft.ContainerService/AzureMonitorAppMonitoringPreview' ) self.cmd(update_cmd, checks=[ @@ -17292,8 +17302,10 @@ def test_aks_comprehensive_monitoring_integration(self, resource_group, resource # OpenTelemetry features should be re-enabled with new ports self.check('azureMonitorProfile.appMonitoring.openTelemetryLogsAndTraces.enabled', True), self.check('azureMonitorProfile.appMonitoring.openTelemetryLogsAndTraces.httpPort', 9090), + self.check('azureMonitorProfile.appMonitoring.openTelemetryLogsAndTraces.grpcPort', 9092), self.check('azureMonitorProfile.appMonitoring.openTelemetryMetrics.enabled', True), self.check('azureMonitorProfile.appMonitoring.openTelemetryMetrics.httpPort', 9091), + self.check('azureMonitorProfile.appMonitoring.openTelemetryMetrics.grpcPort', 9093), ]) # Phase 5: Update - disable Azure Monitor metrics (should also disable OpenTelemetry metrics) @@ -17355,8 +17367,8 @@ def test_aks_comprehensive_monitoring_integration(self, resource_group, resource update_cmd = ( 'aks update --resource-group={resource_group} --name={name} --yes --output=json ' '--enable-azure-monitor-logs --enable-azure-monitor-metrics --enable-azure-monitor-app-monitoring ' - '--enable-opentelemetry-logs-traces --opentelemetry-logs-traces-port-http=7070 ' - '--enable-opentelemetry-metrics --opentelemetry-metrics-port-http=7071 ' + '--enable-opentelemetry-logs-traces --opentelemetry-logs-traces-port-http=7070 --opentelemetry-logs-traces-port-grpc=7072 ' + '--enable-opentelemetry-metrics --opentelemetry-metrics-port-http=7071 --opentelemetry-metrics-port-grpc=7073 ' '--enable-windows-recording-rules ' '--aks-custom-headers AKSHTTPCustomFeatures=Microsoft.ContainerService/AzureMonitorAppMonitoringPreview' ) @@ -17383,8 +17395,10 @@ def test_aks_comprehensive_monitoring_integration(self, resource_group, resource # All OpenTelemetry features should be enabled with new ports self.check('azureMonitorProfile.appMonitoring.openTelemetryLogsAndTraces.enabled', True), self.check('azureMonitorProfile.appMonitoring.openTelemetryLogsAndTraces.httpPort', 7070), + self.check('azureMonitorProfile.appMonitoring.openTelemetryLogsAndTraces.grpcPort', 7072), self.check('azureMonitorProfile.appMonitoring.openTelemetryMetrics.enabled', True), self.check('azureMonitorProfile.appMonitoring.openTelemetryMetrics.httpPort', 7071), + self.check('azureMonitorProfile.appMonitoring.openTelemetryMetrics.grpcPort', 7073), ]) # Phase 8: Final cleanup - disable all monitoring features From 13c8f97b03ad94a4154c582144c8726452b973f9 Mon Sep 17 00:00:00 2001 From: Christoph Butz Date: Thu, 2 Jul 2026 15:06:03 +0100 Subject: [PATCH 04/14] [aks-preview] Fix OTLP live tests: remove rejected aksHTTPCustomFeatures header The AzureMonitorAppMonitoringPreview capability is gated by subscription feature registration, not by a per-request AKSHTTPCustomFeatures header. The resource provider now rejects that header with '(BadRequest) Parameter aksHTTPCustomFeatures is not allowed', causing every OpenTelemetry @live_only scenario to fail before reaching its assertions. Removes '--aks-custom-headers AKSHTTPCustomFeatures=Microsoft.ContainerService/AzureMonitorAppMonitoringPreview' from the six OpenTelemetry live tests. Verified live that create/update with the OTLP flags succeeds once the header is dropped (feature registered on the subscription). The recorded app-monitoring tests are left unchanged, as they replay a cassette that still contains the header. --- .../tests/latest/test_aks_commands.py | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/src/aks-preview/azext_aks_preview/tests/latest/test_aks_commands.py b/src/aks-preview/azext_aks_preview/tests/latest/test_aks_commands.py index e617089a6b5..8d7440896f3 100644 --- a/src/aks-preview/azext_aks_preview/tests/latest/test_aks_commands.py +++ b/src/aks-preview/azext_aks_preview/tests/latest/test_aks_commands.py @@ -16566,7 +16566,7 @@ def test_aks_create_with_azuremonitorlogs_and_opentelemetry(self, resource_group create_cmd = ( 'aks create --resource-group={resource_group} --name={name} --location={location} --ssh-key-value={ssh_key_value} --node-vm-size={node_vm_size} ' '--enable-managed-identity --enable-azure-monitor-logs --enable-opentelemetry-logs-traces --opentelemetry-logs-traces-port-http=8080 --opentelemetry-logs-traces-port-grpc=8082 ' - '--aks-custom-headers AKSHTTPCustomFeatures=Microsoft.ContainerService/AzureMonitorAppMonitoringPreview --output=json' + '--output=json' ) self.cmd(create_cmd, checks=[ self.check('provisioningState', 'Succeeded'), @@ -16609,7 +16609,6 @@ def test_aks_update_with_azuremonitorlogs_and_opentelemetry(self, resource_group update_cmd = ( 'aks update --resource-group={resource_group} --name={name} --yes --output=json ' '--enable-azure-monitor-logs --enable-opentelemetry-logs-traces --opentelemetry-logs-traces-port-http=9090 --opentelemetry-logs-traces-port-grpc=9092 ' - '--aks-custom-headers AKSHTTPCustomFeatures=Microsoft.ContainerService/AzureMonitorAppMonitoringPreview' ) self.cmd(update_cmd, checks=[ self.check('provisioningState', 'Succeeded'), @@ -16805,7 +16804,7 @@ def test_aks_create_with_azuremonitormetrics_and_opentelemetry(self, resource_gr create_cmd = ( 'aks create --resource-group={resource_group} --name={name} --location={location} --ssh-key-value={ssh_key_value} --node-vm-size={node_vm_size} ' '--enable-managed-identity --enable-azure-monitor-metrics --enable-opentelemetry-metrics --opentelemetry-metrics-port-http=8080 --opentelemetry-metrics-port-grpc=8082 ' - '--aks-custom-headers AKSHTTPCustomFeatures=Microsoft.ContainerService/AzureMonitorAppMonitoringPreview --output=json' + '--output=json' ) self.cmd(create_cmd, checks=[ self.check('provisioningState', 'Succeeded'), @@ -16862,7 +16861,6 @@ def test_aks_update_with_azuremonitormetrics_and_opentelemetry(self, resource_gr update_cmd = ( 'aks update --resource-group={resource_group} --name={name} --yes ' '--enable-azure-monitor-metrics --enable-opentelemetry-metrics --opentelemetry-metrics-port-http=9090 ' - '--aks-custom-headers AKSHTTPCustomFeatures=Microsoft.ContainerService/AzureMonitorAppMonitoringPreview ' '--output=json' ) @@ -16988,7 +16986,7 @@ def test_aks_create_with_opentelemetry_deprecated_flag_aliases(self, resource_gr '--enable-managed-identity --enable-azure-monitor-logs --enable-azure-monitor-metrics ' '--enable-opentelemetry-logs --opentelemetry-logs-port=8080 ' '--enable-opentelemetry-metrics --opentelemetry-metrics-port=8081 ' - '--aks-custom-headers AKSHTTPCustomFeatures=Microsoft.ContainerService/AzureMonitorAppMonitoringPreview --output=json' + '--output=json' ) self.cmd(create_cmd, checks=[ self.check('provisioningState', 'Succeeded'), @@ -17167,7 +17165,7 @@ def test_aks_comprehensive_monitoring_integration(self, resource_group, resource '--enable-opentelemetry-logs-traces --opentelemetry-logs-traces-port-http=8080 --opentelemetry-logs-traces-port-grpc=8082 ' '--enable-opentelemetry-metrics --opentelemetry-metrics-port-http=8081 --opentelemetry-metrics-port-grpc=8083 ' '--enable-windows-recording-rules ' - '--aks-custom-headers AKSHTTPCustomFeatures=Microsoft.ContainerService/AzureMonitorAppMonitoringPreview --output=json' + '--output=json' ) self.cmd(create_cmd, checks=[ self.check('provisioningState', 'Succeeded'), @@ -17278,7 +17276,6 @@ def test_aks_comprehensive_monitoring_integration(self, resource_group, resource 'aks update --resource-group={resource_group} --name={name} --yes --output=json ' '--enable-opentelemetry-logs-traces --opentelemetry-logs-traces-port-http=9090 --opentelemetry-logs-traces-port-grpc=9092 ' '--enable-opentelemetry-metrics --opentelemetry-metrics-port-http=9091 --opentelemetry-metrics-port-grpc=9093 ' - '--aks-custom-headers AKSHTTPCustomFeatures=Microsoft.ContainerService/AzureMonitorAppMonitoringPreview' ) self.cmd(update_cmd, checks=[ self.check('provisioningState', 'Succeeded'), @@ -17370,7 +17367,6 @@ def test_aks_comprehensive_monitoring_integration(self, resource_group, resource '--enable-opentelemetry-logs-traces --opentelemetry-logs-traces-port-http=7070 --opentelemetry-logs-traces-port-grpc=7072 ' '--enable-opentelemetry-metrics --opentelemetry-metrics-port-http=7071 --opentelemetry-metrics-port-grpc=7073 ' '--enable-windows-recording-rules ' - '--aks-custom-headers AKSHTTPCustomFeatures=Microsoft.ContainerService/AzureMonitorAppMonitoringPreview' ) self.cmd(update_cmd, checks=[ self.check('provisioningState', 'Succeeded'), From 5e5c6b10e96121014fc7c7a87211a7759ec06d50 Mon Sep 17 00:00:00 2001 From: Christoph Butz Date: Mon, 13 Jul 2026 17:51:20 +0100 Subject: [PATCH 05/14] [aks-preview] Exclude required OpenTelemetry option names from lint --- src/aks-preview/linter_exclusions.yml | 36 +++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/src/aks-preview/linter_exclusions.yml b/src/aks-preview/linter_exclusions.yml index 685a4f9efc2..480b2dddbeb 100644 --- a/src/aks-preview/linter_exclusions.yml +++ b/src/aks-preview/linter_exclusions.yml @@ -138,15 +138,33 @@ aks create: opentelemetry_metrics_port: rule_exclusions: - option_length_too_long + opentelemetry_metrics_port_deprecated: + rule_exclusions: + - option_length_too_long + opentelemetry_metrics_port_grpc: + rule_exclusions: + - option_length_too_long enable_opentelemetry_logs: rule_exclusions: - option_length_too_long + enable_opentelemetry_logs_deprecated: + rule_exclusions: + - option_length_too_long disable_opentelemetry_logs: rule_exclusions: - option_length_too_long + disable_opentelemetry_logs_deprecated: + rule_exclusions: + - option_length_too_long opentelemetry_logs_port: rule_exclusions: - option_length_too_long + opentelemetry_logs_port_deprecated: + rule_exclusions: + - option_length_too_long + opentelemetry_logs_traces_port_grpc: + rule_exclusions: + - option_length_too_long enable_static_egress_gateway: rule_exclusions: - option_length_too_long @@ -317,15 +335,33 @@ aks update: opentelemetry_metrics_port: rule_exclusions: - option_length_too_long + opentelemetry_metrics_port_deprecated: + rule_exclusions: + - option_length_too_long + opentelemetry_metrics_port_grpc: + rule_exclusions: + - option_length_too_long enable_opentelemetry_logs: rule_exclusions: - option_length_too_long + enable_opentelemetry_logs_deprecated: + rule_exclusions: + - option_length_too_long disable_opentelemetry_logs: rule_exclusions: - option_length_too_long + disable_opentelemetry_logs_deprecated: + rule_exclusions: + - option_length_too_long opentelemetry_logs_port: rule_exclusions: - option_length_too_long + opentelemetry_logs_port_deprecated: + rule_exclusions: + - option_length_too_long + opentelemetry_logs_traces_port_grpc: + rule_exclusions: + - option_length_too_long bootstrap_artifact_source: rule_exclusions: - option_length_too_long From 07260986a063eeb813532329cb5471b1356b3e5e Mon Sep 17 00:00:00 2001 From: Christoph Butz Date: Tue, 28 Jul 2026 15:43:08 +0100 Subject: [PATCH 06/14] [aks-preview] Don't trigger Prometheus onboarding for OTLP logs/traces The gate guarding ensure_azure_monitor_profile_prerequisites in update_azure_monitor_profile treated every OpenTelemetry parameter as metrics-related. Setting only an OTLP logs/traces port therefore ran the full Prometheus onboarding path, creating an Azure Monitor Workspace, DCE, DCR, DCRA, a Grafana link and recording rules. OpenTelemetry logs and traces ride the Container Insights pipeline and have no dependency on those artifacts, so restrict the gate to Azure Monitor metrics and OpenTelemetry metrics parameters only. Add five regression tests that assert on the prerequisites mock. The existing OpenTelemetry tests patch it purely to suppress network calls and never assert on it, which is why this went unnoticed. Three cover the logs/traces cases and two guard against over-correcting by verifying that metrics-only and mixed-signal commands still onboard exactly once. --- src/aks-preview/HISTORY.rst | 1 + .../managed_cluster_decorator.py | 19 +-- .../latest/test_managed_cluster_decorator.py | 131 ++++++++++++++++++ 3 files changed, 142 insertions(+), 9 deletions(-) diff --git a/src/aks-preview/HISTORY.rst b/src/aks-preview/HISTORY.rst index 6c7f132c666..d2a8a4e5c2c 100644 --- a/src/aks-preview/HISTORY.rst +++ b/src/aks-preview/HISTORY.rst @@ -16,6 +16,7 @@ Pending +++++++++ * `az aks create` and `az aks update`: Add `--opentelemetry-metrics-port-grpc` and `--opentelemetry-logs-traces-port-grpc` (preview) to configure the gRPC ports for OpenTelemetry metrics and logs/traces collection, in addition to the existing HTTP/protobuf ports. * `az aks create` and `az aks update`: Rename the OpenTelemetry parameters, keeping the previous names as deprecated aliases: `--opentelemetry-metrics-port` -> `--opentelemetry-metrics-port-http`, `--opentelemetry-logs-port` -> `--opentelemetry-logs-traces-port-http`, `--enable-opentelemetry-logs` -> `--enable-opentelemetry-logs-traces`, `--disable-opentelemetry-logs` -> `--disable-opentelemetry-logs-traces`. +* `az aks update`: Fix the OpenTelemetry logs and traces parameters (`--enable-opentelemetry-logs-traces`, `--disable-opentelemetry-logs-traces`, `--opentelemetry-logs-traces-port-http`, `--opentelemetry-logs-traces-port-grpc`) incorrectly triggering Azure Monitor Workspace (Prometheus) onboarding. Only Azure Monitor metrics and OpenTelemetry metrics parameters do so now. 21.0.0b10 +++++++++ diff --git a/src/aks-preview/azext_aks_preview/managed_cluster_decorator.py b/src/aks-preview/azext_aks_preview/managed_cluster_decorator.py index 3b75fb840aa..be84f5dc6d6 100644 --- a/src/aks-preview/azext_aks_preview/managed_cluster_decorator.py +++ b/src/aks-preview/azext_aks_preview/managed_cluster_decorator.py @@ -7781,15 +7781,16 @@ def update_azure_monitor_profile(self, mc: ManagedCluster) -> ManagedCluster: self.context.get_enable_azure_monitor_metrics() or self.context.raw_param.get("disable_azuremonitormetrics") or self.context.get_disable_azure_monitor_metrics()) - opentelemetry = (self.context.raw_param.get("enable_opentelemetry_metrics") or - self.context.raw_param.get("enable_opentelemetry_logs") or - self.context.raw_param.get("disable_opentelemetry_metrics") or - self.context.raw_param.get("disable_opentelemetry_logs") or - self.context.get_opentelemetry_metrics_port() is not None or - self.context.get_opentelemetry_metrics_port_grpc() is not None or - self.context.get_opentelemetry_logs_port() is not None or - self.context.get_opentelemetry_logs_traces_port_grpc() is not None) - if azure_monitor_metrics or opentelemetry: + opentelemetry_metrics = (self.context.raw_param.get("enable_opentelemetry_metrics") or + self.context.raw_param.get("disable_opentelemetry_metrics") or + self.context.get_opentelemetry_metrics_port() is not None or + self.context.get_opentelemetry_metrics_port_grpc() is not None) + # NOTE: only Azure Monitor metrics and OpenTelemetry *metrics* may require the Prometheus + # artifacts (Azure Monitor Workspace, DCE, DCR, DCRA, Grafana link, recording rules) that + # ensure_azure_monitor_profile_prerequisites creates. OpenTelemetry *logs and traces* ride + # on the Container Insights pipeline and must NOT trigger Prometheus onboarding, otherwise + # a command that only sets an OTLP logs/traces port silently provisions an AMW. + if azure_monitor_metrics or opentelemetry_metrics: ensure_azure_monitor_profile_prerequisites( self.cmd, self.context.get_subscription_id(), diff --git a/src/aks-preview/azext_aks_preview/tests/latest/test_managed_cluster_decorator.py b/src/aks-preview/azext_aks_preview/tests/latest/test_managed_cluster_decorator.py index 08b7ba86556..3480c4c3bee 100644 --- a/src/aks-preview/azext_aks_preview/tests/latest/test_managed_cluster_decorator.py +++ b/src/aks-preview/azext_aks_preview/tests/latest/test_managed_cluster_decorator.py @@ -17684,6 +17684,137 @@ def test_update_azure_monitor_profile_with_opentelemetry_logs(self): 9091, ) + def _otlp_gate_addon_profiles(self): + """Monitoring addon profile used by the OpenTelemetry prerequisites-gate tests.""" + return { + "omsagent": self.models.ManagedClusterAddonProfile( + enabled=True, + config={ + "logAnalyticsWorkspaceResourceID": "/subscriptions/test/resourceGroups/test/providers/Microsoft.OperationalInsights/workspaces/test-workspace" + }, + ) + } + + def _otlp_gate_mc_with_container_insights(self, otlp_logs_enabled=True): + """Cluster with Azure Monitor logs (Container Insights) on and Prometheus metrics off.""" + otel_logs_cls = self.models.ManagedClusterAzureMonitorProfileAppMonitoringOpenTelemetryLogsAndTraces + app_monitoring = self.models.ManagedClusterAzureMonitorProfileAppMonitoring( + open_telemetry_logs_and_traces=otel_logs_cls( + enabled=otlp_logs_enabled, http_port=4318, grpc_port=4317 + ), + ) + return self.models.ManagedCluster( + location="test_location", + addon_profiles=self._otlp_gate_addon_profiles(), + azure_monitor_profile=self.models.ManagedClusterAzureMonitorProfile( + container_insights=self.models.ManagedClusterAzureMonitorProfileContainerInsights( + enabled=True + ), + app_monitoring=app_monitoring, + ), + ) + + def _run_otlp_gate(self, raw_parameters, mc): + """Run update_azure_monitor_profile and return the patched prerequisites mock.""" + dec = AKSPreviewManagedClusterUpdateDecorator( + self.cmd, + self.client, + raw_parameters, + CUSTOM_MGMT_AKS_PREVIEW, + ) + dec.context.attach_mc(mc) + with patch( + "azext_aks_preview.managed_cluster_decorator.ensure_azure_monitor_profile_prerequisites" + ) as mock_prereq, patch.object( + dec.context, "get_subscription_id", return_value="test-subscription" + ), patch.object( + dec.context, "get_resource_group_name", return_value="test-rg" + ), patch.object( + dec.context, "get_name", return_value="test-cluster" + ), patch.object( + dec.context, "get_location", return_value="test-location" + ): + dec.update_azure_monitor_profile(mc) + return mock_prereq + + def test_opentelemetry_logs_traces_ports_do_not_trigger_prometheus_prerequisites(self): + # OpenTelemetry logs and traces ride the Container Insights pipeline. Setting their ports + # must NOT provision the Prometheus artifacts (Azure Monitor Workspace, DCE, DCR, DCRA, + # Grafana link, recording rules) created by ensure_azure_monitor_profile_prerequisites. + mock_prereq = self._run_otlp_gate( + { + "opentelemetry_logs_port": 2331, + "opentelemetry_logs_traces_port_grpc": 2332, + }, + self._otlp_gate_mc_with_container_insights(), + ) + mock_prereq.assert_not_called() + + def test_enable_opentelemetry_logs_traces_does_not_trigger_prometheus_prerequisites(self): + mock_prereq = self._run_otlp_gate( + {"enable_opentelemetry_logs": True}, + self._otlp_gate_mc_with_container_insights(otlp_logs_enabled=False), + ) + mock_prereq.assert_not_called() + + def test_disable_opentelemetry_logs_traces_does_not_trigger_prometheus_prerequisites(self): + mock_prereq = self._run_otlp_gate( + {"disable_opentelemetry_logs": True}, + self._otlp_gate_mc_with_container_insights(), + ) + mock_prereq.assert_not_called() + + def test_opentelemetry_metrics_still_triggers_prometheus_prerequisites(self): + # Guard against over-correcting: OpenTelemetry metrics DOES need the Prometheus artifacts. + mc = self.models.ManagedCluster( + location="test_location", + identity=self.models.ManagedClusterIdentity(type="SystemAssigned"), + azure_monitor_profile=self.models.ManagedClusterAzureMonitorProfile( + metrics=self.models.ManagedClusterAzureMonitorProfileMetrics(enabled=True), + ), + ) + mock_prereq = self._run_otlp_gate( + { + "enable_azure_monitor_metrics": True, + "enable_opentelemetry_metrics": True, + "opentelemetry_metrics_port_grpc": 8082, + }, + mc, + ) + mock_prereq.assert_called_once() + + def test_opentelemetry_mixed_signals_trigger_prometheus_prerequisites_once(self): + # Metrics and logs/traces arguments in the same command: the metrics side still onboards, + # and the logs/traces side neither suppresses nor duplicates that call. + otel_logs_cls = self.models.ManagedClusterAzureMonitorProfileAppMonitoringOpenTelemetryLogsAndTraces + mc = self.models.ManagedCluster( + location="test_location", + identity=self.models.ManagedClusterIdentity(type="SystemAssigned"), + addon_profiles=self._otlp_gate_addon_profiles(), + azure_monitor_profile=self.models.ManagedClusterAzureMonitorProfile( + metrics=self.models.ManagedClusterAzureMonitorProfileMetrics(enabled=True), + container_insights=self.models.ManagedClusterAzureMonitorProfileContainerInsights( + enabled=True + ), + app_monitoring=self.models.ManagedClusterAzureMonitorProfileAppMonitoring( + open_telemetry_logs_and_traces=otel_logs_cls( + enabled=True, http_port=4318, grpc_port=4317 + ), + ), + ), + ) + mock_prereq = self._run_otlp_gate( + { + "enable_azure_monitor_metrics": True, + "enable_opentelemetry_metrics": True, + "opentelemetry_metrics_port_grpc": 8082, + "opentelemetry_logs_port": 2331, + "opentelemetry_logs_traces_port_grpc": 2332, + }, + mc, + ) + mock_prereq.assert_called_once() + def test_disable_azure_monitor_app_monitoring_preserves_opentelemetry(self): # Test that disabling Azure Monitor app monitoring preserves existing OpenTelemetry configuration dec = AKSPreviewManagedClusterUpdateDecorator( From 3085e1841470c214415a0551825ad26e418b075c Mon Sep 17 00:00:00 2001 From: Christoph Butz Date: Fri, 31 Jul 2026 18:08:32 +0100 Subject: [PATCH 07/14] [aks-preview] Isolate monitoring workspaces in OTLP live tests The OpenTelemetry live tests enabled Azure Monitor logs and metrics without naming a workspace, so both fell back to the subscription-wide defaults (DefaultWorkspace-- and DefaultAzureMonitorWorkspace-). Under PARALLELISM=8 concurrent tests race to create/update those shared singletons, producing '(Conflict) Workspace cannot be updated while current provisioning state is not Succeeded' - observed on two tests in ADO build 174587325. Each affected test now provisions its own Log Analytics workspace and Azure Monitor workspace in its own resource group, and passes --workspace-resource-id / --azure-monitor-workspace-resource-id at every enablement site. Adds a _create_azure_monitor_workspace helper mirroring the existing _create_log_analytics_workspace. Workspace assertions now compare the returned logAnalyticsWorkspaceResourceID against the dedicated workspace id (case-insensitive) instead of merely asserting it exists, so a silent fallback to the shared default workspace can no longer pass. --- .../tests/latest/test_aks_commands.py | 99 ++++++++++++++++--- 1 file changed, 88 insertions(+), 11 deletions(-) diff --git a/src/aks-preview/azext_aks_preview/tests/latest/test_aks_commands.py b/src/aks-preview/azext_aks_preview/tests/latest/test_aks_commands.py index 46c8879e7ad..617d89c56c3 100644 --- a/src/aks-preview/azext_aks_preview/tests/latest/test_aks_commands.py +++ b/src/aks-preview/azext_aks_preview/tests/latest/test_aks_commands.py @@ -59,6 +59,28 @@ def _create_log_analytics_workspace(self, resource_group_location): "--location {workspace_location} --query id -o tsv" ).output.strip() + def _create_azure_monitor_workspace(self, resource_group_location): + """Create a dedicated Azure Monitor workspace (Prometheus) for a test. + + Without --azure-monitor-workspace-resource-id the metrics onboarding falls back to the + subscription's shared DefaultAzureMonitorWorkspace-, which races when live tests + run in parallel. + """ + amw_name = self.create_random_name("cliamw", 16) + amw_location = ( + "eastus2" if resource_group_location.lower().endswith("euap") else resource_group_location + ) + self.kwargs.update( + { + "amw_name": amw_name, + "amw_location": amw_location, + } + ) + return self.cmd( + "monitor account create -g {resource_group} -n {amw_name} " + "--location {amw_location} --query id -o tsv" + ).output.strip() + def _get_versions(self, location): """Return the previous and current Kubernetes minor release versions, such as ("1.11.6", "1.12.4").""" supported_versions = self.cmd( @@ -16562,16 +16584,26 @@ def test_aks_create_with_azuremonitorlogs_and_opentelemetry(self, resource_group 'ssh_key_value': self.generate_ssh_keys(), 'node_vm_size': node_vm_size, }) + # Use a dedicated workspace: without --workspace-resource-id the monitoring addon falls back + # to the subscription's shared default workspace, which conflicts when live tests run in parallel. + self.kwargs.update({ + 'workspace_resource_id': self._create_log_analytics_workspace(resource_group_location), + }) create_cmd = ( 'aks create --resource-group={resource_group} --name={name} --location={location} --ssh-key-value={ssh_key_value} --node-vm-size={node_vm_size} ' - '--enable-managed-identity --enable-azure-monitor-logs --enable-opentelemetry-logs-traces --opentelemetry-logs-traces-port-http=8080 --opentelemetry-logs-traces-port-grpc=8082 ' + '--enable-managed-identity --enable-azure-monitor-logs --workspace-resource-id={workspace_resource_id} ' + '--enable-opentelemetry-logs-traces --opentelemetry-logs-traces-port-http=8080 --opentelemetry-logs-traces-port-grpc=8082 ' '--output=json' ) self.cmd(create_cmd, checks=[ self.check('provisioningState', 'Succeeded'), self.check('addonProfiles.omsagent.enabled', True), - self.exists('addonProfiles.omsagent.config.logAnalyticsWorkspaceResourceID'), + self.check( + 'addonProfiles.omsagent.config.logAnalyticsWorkspaceResourceID', + self.kwargs['workspace_resource_id'], + case_sensitive=False, + ), self.check('addonProfiles.omsagent.config.useAADAuth', 'true'), self.check('azureMonitorProfile.appMonitoring.openTelemetryLogsAndTraces.enabled', True), self.check('azureMonitorProfile.appMonitoring.openTelemetryLogsAndTraces.httpPort', 8080), @@ -16597,6 +16629,11 @@ def test_aks_update_with_azuremonitorlogs_and_opentelemetry(self, resource_group 'ssh_key_value': self.generate_ssh_keys(), 'node_vm_size': node_vm_size, }) + # Use a dedicated workspace: without --workspace-resource-id the monitoring addon falls back + # to the subscription's shared default workspace, which conflicts when live tests run in parallel. + self.kwargs.update({ + 'workspace_resource_id': self._create_log_analytics_workspace(resource_group_location), + }) # create: without enable-azure-monitor-logs create_cmd = 'aks create --resource-group={resource_group} --name={name} --location={location} --ssh-key-value={ssh_key_value} --node-vm-size={node_vm_size} --enable-managed-identity --output=json' @@ -16608,12 +16645,17 @@ def test_aks_update_with_azuremonitorlogs_and_opentelemetry(self, resource_group # update: enable-azure-monitor-logs with OpenTelemetry logs update_cmd = ( 'aks update --resource-group={resource_group} --name={name} --yes --output=json ' - '--enable-azure-monitor-logs --enable-opentelemetry-logs-traces --opentelemetry-logs-traces-port-http=9090 --opentelemetry-logs-traces-port-grpc=9092 ' + '--enable-azure-monitor-logs --workspace-resource-id={workspace_resource_id} ' + '--enable-opentelemetry-logs-traces --opentelemetry-logs-traces-port-http=9090 --opentelemetry-logs-traces-port-grpc=9092 ' ) self.cmd(update_cmd, checks=[ self.check('provisioningState', 'Succeeded'), self.check('addonProfiles.omsagent.enabled', True), - self.exists('addonProfiles.omsagent.config.logAnalyticsWorkspaceResourceID'), + self.check( + 'addonProfiles.omsagent.config.logAnalyticsWorkspaceResourceID', + self.kwargs['workspace_resource_id'], + case_sensitive=False, + ), self.check('addonProfiles.omsagent.config.useAADAuth', 'true'), self.check('azureMonitorProfile.appMonitoring.openTelemetryLogsAndTraces.enabled', True), self.check('azureMonitorProfile.appMonitoring.openTelemetryLogsAndTraces.httpPort', 9090), @@ -16800,10 +16842,16 @@ def test_aks_create_with_azuremonitormetrics_and_opentelemetry(self, resource_gr 'ssh_key_value': self.generate_ssh_keys(), 'node_vm_size': node_vm_size, }) + # Use a dedicated Azure Monitor workspace: without --azure-monitor-workspace-resource-id + # metrics onboarding falls back to the shared default AMW, which races under parallelism. + self.kwargs.update({ + 'amw_resource_id': self._create_azure_monitor_workspace(resource_group_location), + }) create_cmd = ( 'aks create --resource-group={resource_group} --name={name} --location={location} --ssh-key-value={ssh_key_value} --node-vm-size={node_vm_size} ' - '--enable-managed-identity --enable-azure-monitor-metrics --enable-opentelemetry-metrics --opentelemetry-metrics-port-http=8080 --opentelemetry-metrics-port-grpc=8082 ' + '--enable-managed-identity --enable-azure-monitor-metrics --azure-monitor-workspace-resource-id={amw_resource_id} ' + '--enable-opentelemetry-metrics --opentelemetry-metrics-port-http=8080 --opentelemetry-metrics-port-grpc=8082 ' '--output=json' ) self.cmd(create_cmd, checks=[ @@ -16849,6 +16897,11 @@ def test_aks_update_with_azuremonitormetrics_and_opentelemetry(self, resource_gr 'ssh_key_value': self.generate_ssh_keys(), 'node_vm_size': node_vm_size, }) + # Use a dedicated Azure Monitor workspace: without --azure-monitor-workspace-resource-id + # metrics onboarding falls back to the shared default AMW, which races under parallelism. + self.kwargs.update({ + 'amw_resource_id': self._create_azure_monitor_workspace(resource_group_location), + }) # create: without enable-azure-monitor-metrics create_cmd = 'aks create --resource-group={resource_group} --name={name} --location={location} --ssh-key-value={ssh_key_value} --node-vm-size={node_vm_size} --enable-managed-identity --output=json' @@ -16860,7 +16913,8 @@ def test_aks_update_with_azuremonitormetrics_and_opentelemetry(self, resource_gr # update: enable-azure-monitor-metrics with OpenTelemetry metrics update_cmd = ( 'aks update --resource-group={resource_group} --name={name} --yes ' - '--enable-azure-monitor-metrics --enable-opentelemetry-metrics --opentelemetry-metrics-port-http=9090 ' + '--enable-azure-monitor-metrics --azure-monitor-workspace-resource-id={amw_resource_id} ' + '--enable-opentelemetry-metrics --opentelemetry-metrics-port-http=9090 ' '--output=json' ) @@ -16978,12 +17032,19 @@ def test_aks_create_with_opentelemetry_deprecated_flag_aliases(self, resource_gr 'ssh_key_value': self.generate_ssh_keys(), 'node_vm_size': node_vm_size, }) + # Use dedicated workspaces: without explicit ids the monitoring addons fall back to the + # subscription's shared default workspaces, which conflict when live tests run in parallel. + self.kwargs.update({ + 'workspace_resource_id': self._create_log_analytics_workspace(resource_group_location), + 'amw_resource_id': self._create_azure_monitor_workspace(resource_group_location), + }) # Intentionally use the DEPRECATED flag names; they must still bind the same # destinations and configure OpenTelemetry on the new API surface. create_cmd = ( 'aks create --resource-group={resource_group} --name={name} --location={location} --ssh-key-value={ssh_key_value} --node-vm-size={node_vm_size} ' - '--enable-managed-identity --enable-azure-monitor-logs --enable-azure-monitor-metrics ' + '--enable-managed-identity --enable-azure-monitor-logs --workspace-resource-id={workspace_resource_id} ' + '--enable-azure-monitor-metrics --azure-monitor-workspace-resource-id={amw_resource_id} ' '--enable-opentelemetry-logs --opentelemetry-logs-port=8080 ' '--enable-opentelemetry-metrics --opentelemetry-metrics-port=8081 ' '--output=json' @@ -17157,11 +17218,18 @@ def test_aks_comprehensive_monitoring_integration(self, resource_group, resource 'ssh_key_value': self.generate_ssh_keys(), 'node_vm_size': node_vm_size, }) + # Use dedicated workspaces: without explicit ids the monitoring addons fall back to the + # subscription's shared default workspaces, which conflict when live tests run in parallel. + self.kwargs.update({ + 'workspace_resource_id': self._create_log_analytics_workspace(resource_group_location), + 'amw_resource_id': self._create_azure_monitor_workspace(resource_group_location), + }) # Phase 1: Create cluster with all monitoring features enabled create_cmd = ( 'aks create --resource-group={resource_group} --name={name} --location={location} --ssh-key-value={ssh_key_value} --node-vm-size={node_vm_size} ' - '--enable-managed-identity --enable-azure-monitor-logs --enable-azure-monitor-metrics --enable-azure-monitor-app-monitoring ' + '--enable-managed-identity --enable-azure-monitor-logs --workspace-resource-id={workspace_resource_id} ' + '--enable-azure-monitor-metrics --azure-monitor-workspace-resource-id={amw_resource_id} --enable-azure-monitor-app-monitoring ' '--enable-opentelemetry-logs-traces --opentelemetry-logs-traces-port-http=8080 --opentelemetry-logs-traces-port-grpc=8082 ' '--enable-opentelemetry-metrics --opentelemetry-metrics-port-http=8081 --opentelemetry-metrics-port-grpc=8083 ' '--enable-windows-recording-rules ' @@ -17187,7 +17255,11 @@ def test_aks_comprehensive_monitoring_integration(self, resource_group, resource self.check('provisioningState', 'Succeeded'), # Azure Monitor logs checks self.check('addonProfiles.omsagent.enabled', True), - self.exists('addonProfiles.omsagent.config.logAnalyticsWorkspaceResourceID'), + self.check( + 'addonProfiles.omsagent.config.logAnalyticsWorkspaceResourceID', + self.kwargs['workspace_resource_id'], + case_sensitive=False, + ), self.check('addonProfiles.omsagent.config.useAADAuth', 'true'), # Azure Monitor metrics checks self.check('azureMonitorProfile.metrics.enabled', True), @@ -17363,7 +17435,8 @@ def test_aks_comprehensive_monitoring_integration(self, resource_group, resource # Phase 7: Update - re-enable all monitoring features at once update_cmd = ( 'aks update --resource-group={resource_group} --name={name} --yes --output=json ' - '--enable-azure-monitor-logs --enable-azure-monitor-metrics --enable-azure-monitor-app-monitoring ' + '--enable-azure-monitor-logs --workspace-resource-id={workspace_resource_id} ' + '--enable-azure-monitor-metrics --azure-monitor-workspace-resource-id={amw_resource_id} --enable-azure-monitor-app-monitoring ' '--enable-opentelemetry-logs-traces --opentelemetry-logs-traces-port-http=7070 --opentelemetry-logs-traces-port-grpc=7072 ' '--enable-opentelemetry-metrics --opentelemetry-metrics-port-http=7071 --opentelemetry-metrics-port-grpc=7073 ' '--enable-windows-recording-rules ' @@ -17384,7 +17457,11 @@ def test_aks_comprehensive_monitoring_integration(self, resource_group, resource self.check('provisioningState', 'Succeeded'), # All Azure Monitor features should be enabled self.check('addonProfiles.omsagent.enabled', True), - self.exists('addonProfiles.omsagent.config.logAnalyticsWorkspaceResourceID'), + self.check( + 'addonProfiles.omsagent.config.logAnalyticsWorkspaceResourceID', + self.kwargs['workspace_resource_id'], + case_sensitive=False, + ), self.check('addonProfiles.omsagent.config.useAADAuth', 'true'), self.check('azureMonitorProfile.metrics.enabled', True), self.check('azureMonitorProfile.appMonitoring.autoInstrumentation.enabled', True), From d4f5c599a512537f1c88a380159be7e4858c0a27 Mon Sep 17 00:00:00 2001 From: Christoph Butz Date: Fri, 31 Jul 2026 18:09:24 +0100 Subject: [PATCH 08/14] [aks-preview] Fix misleading OTLP port examples in help The logs/traces examples passed 4317 to --opentelemetry-logs-traces-port-http, but 4317 is the OTLP gRPC port by convention (4318 is HTTP). Mapping the canonical gRPC port to the HTTP flag works against the goal of making the HTTP-vs-gRPC distinction unambiguous. Uses distinct, convention-neutral ports and shows the paired gRPC flag, matching the style of the metrics examples. --- src/aks-preview/azext_aks_preview/_help.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/aks-preview/azext_aks_preview/_help.py b/src/aks-preview/azext_aks_preview/_help.py index 1d99e025c58..52a94956a54 100644 --- a/src/aks-preview/azext_aks_preview/_help.py +++ b/src/aks-preview/azext_aks_preview/_help.py @@ -856,8 +856,8 @@ text: az aks create -g MyResourceGroup -n MyManagedCluster --enable-azure-monitor-logs - name: Create a kubernetes cluster with OpenTelemetry metrics on custom port text: az aks create -g MyResourceGroup -n MyManagedCluster --enable-opentelemetry-metrics --opentelemetry-metrics-port-http 8888 --enable-azuremonitormetrics - - name: Create a kubernetes cluster with OpenTelemetry logs and traces on custom port - text: az aks create -g MyResourceGroup -n MyManagedCluster --enable-opentelemetry-logs-traces --opentelemetry-logs-traces-port-http 4317 --enable-azure-monitor-logs + - name: Create a kubernetes cluster with OpenTelemetry logs and traces on custom ports + text: az aks create -g MyResourceGroup -n MyManagedCluster --enable-opentelemetry-logs-traces --opentelemetry-logs-traces-port-http 8080 --opentelemetry-logs-traces-port-grpc 8082 --enable-azure-monitor-logs - name: Create a kubernetes cluster with OpenTelemetry metrics on custom HTTP and gRPC ports text: az aks create -g MyResourceGroup -n MyManagedCluster --enable-opentelemetry-metrics --opentelemetry-metrics-port-http 8888 --opentelemetry-metrics-port-grpc 8889 --enable-azuremonitormetrics - name: Create a kubernetes cluster with a nodepool having ip allocation mode set to "StaticBlock" @@ -1672,8 +1672,8 @@ text: az aks update -g MyResourceGroup -n MyManagedCluster --enable-opentelemetry-logs-traces - name: Configure OpenTelemetry metrics with custom port text: az aks update -g MyResourceGroup -n MyManagedCluster --enable-opentelemetry-metrics --opentelemetry-metrics-port-http 8888 - - name: Configure OpenTelemetry logs and traces with custom port - text: az aks update -g MyResourceGroup -n MyManagedCluster --enable-opentelemetry-logs-traces --opentelemetry-logs-traces-port-http 4317 + - name: Configure OpenTelemetry logs and traces with custom ports + text: az aks update -g MyResourceGroup -n MyManagedCluster --enable-opentelemetry-logs-traces --opentelemetry-logs-traces-port-http 8080 --opentelemetry-logs-traces-port-grpc 8082 - name: Configure OpenTelemetry metrics with custom HTTP and gRPC ports text: az aks update -g MyResourceGroup -n MyManagedCluster --enable-opentelemetry-metrics --opentelemetry-metrics-port-http 8888 --opentelemetry-metrics-port-grpc 8889 - name: Disable OpenTelemetry metrics collection on an existing cluster From a468de3387929234e921383287d7f83fa127e73a Mon Sep 17 00:00:00 2001 From: Christoph Butz Date: Fri, 31 Jul 2026 19:07:39 +0100 Subject: [PATCH 09/14] [aks-preview] Restore AKSHTTPCustomFeatures header on OTLP live tests Reverts the test-side change in 13c8f97b0, which dropped --aks-custom-headers AKSHTTPCustomFeatures=Microsoft.ContainerService/AzureMonitorAppMonitoringPreview from the six OpenTelemetry live tests after the RP answered '(BadRequest) Parameter aksHTTPCustomFeatures is not allowed'. That rejection turns out to be scoped to the subscription used for the manual verification, not to the feature or the API. Probing a non-CI subscription reproduces the same BadRequest for an unrelated feature (Microsoft.ContainerService/LocalDNSPreview), i.e. the parameter is refused wholesale there, so the observation says nothing about AzureMonitorAppMonitoringPreview specifically. The live-test subscription behaves differently: ADO build 172295593 (2026-07-15) ran test_aks_automatic_sku_with_hosted_system_enabled, which passes AKSHTTPCustomFeatures for AutomaticSKUPreview and AKS-AutomaticHostedSystemProfilePreview, and reported all tests passed. Removing the header is therefore a plausible cause of the PreviewFeatureNotRegistered failures in build 174587325. Restoring it is cheap to evaluate: the six tests already fail, so a re-run can only add information. If they still report PreviewFeatureNotRegistered, the feature needs registering on the test subscription and this commit can be reverted on its own. --- .../azext_aks_preview/tests/latest/test_aks_commands.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/aks-preview/azext_aks_preview/tests/latest/test_aks_commands.py b/src/aks-preview/azext_aks_preview/tests/latest/test_aks_commands.py index 617d89c56c3..8659723a1e5 100644 --- a/src/aks-preview/azext_aks_preview/tests/latest/test_aks_commands.py +++ b/src/aks-preview/azext_aks_preview/tests/latest/test_aks_commands.py @@ -16594,6 +16594,7 @@ def test_aks_create_with_azuremonitorlogs_and_opentelemetry(self, resource_group 'aks create --resource-group={resource_group} --name={name} --location={location} --ssh-key-value={ssh_key_value} --node-vm-size={node_vm_size} ' '--enable-managed-identity --enable-azure-monitor-logs --workspace-resource-id={workspace_resource_id} ' '--enable-opentelemetry-logs-traces --opentelemetry-logs-traces-port-http=8080 --opentelemetry-logs-traces-port-grpc=8082 ' + '--aks-custom-headers AKSHTTPCustomFeatures=Microsoft.ContainerService/AzureMonitorAppMonitoringPreview ' '--output=json' ) self.cmd(create_cmd, checks=[ @@ -16647,6 +16648,7 @@ def test_aks_update_with_azuremonitorlogs_and_opentelemetry(self, resource_group 'aks update --resource-group={resource_group} --name={name} --yes --output=json ' '--enable-azure-monitor-logs --workspace-resource-id={workspace_resource_id} ' '--enable-opentelemetry-logs-traces --opentelemetry-logs-traces-port-http=9090 --opentelemetry-logs-traces-port-grpc=9092 ' + '--aks-custom-headers AKSHTTPCustomFeatures=Microsoft.ContainerService/AzureMonitorAppMonitoringPreview ' ) self.cmd(update_cmd, checks=[ self.check('provisioningState', 'Succeeded'), @@ -16852,6 +16854,7 @@ def test_aks_create_with_azuremonitormetrics_and_opentelemetry(self, resource_gr 'aks create --resource-group={resource_group} --name={name} --location={location} --ssh-key-value={ssh_key_value} --node-vm-size={node_vm_size} ' '--enable-managed-identity --enable-azure-monitor-metrics --azure-monitor-workspace-resource-id={amw_resource_id} ' '--enable-opentelemetry-metrics --opentelemetry-metrics-port-http=8080 --opentelemetry-metrics-port-grpc=8082 ' + '--aks-custom-headers AKSHTTPCustomFeatures=Microsoft.ContainerService/AzureMonitorAppMonitoringPreview ' '--output=json' ) self.cmd(create_cmd, checks=[ @@ -16915,6 +16918,7 @@ def test_aks_update_with_azuremonitormetrics_and_opentelemetry(self, resource_gr 'aks update --resource-group={resource_group} --name={name} --yes ' '--enable-azure-monitor-metrics --azure-monitor-workspace-resource-id={amw_resource_id} ' '--enable-opentelemetry-metrics --opentelemetry-metrics-port-http=9090 ' + '--aks-custom-headers AKSHTTPCustomFeatures=Microsoft.ContainerService/AzureMonitorAppMonitoringPreview ' '--output=json' ) @@ -17047,6 +17051,7 @@ def test_aks_create_with_opentelemetry_deprecated_flag_aliases(self, resource_gr '--enable-azure-monitor-metrics --azure-monitor-workspace-resource-id={amw_resource_id} ' '--enable-opentelemetry-logs --opentelemetry-logs-port=8080 ' '--enable-opentelemetry-metrics --opentelemetry-metrics-port=8081 ' + '--aks-custom-headers AKSHTTPCustomFeatures=Microsoft.ContainerService/AzureMonitorAppMonitoringPreview ' '--output=json' ) self.cmd(create_cmd, checks=[ @@ -17233,6 +17238,7 @@ def test_aks_comprehensive_monitoring_integration(self, resource_group, resource '--enable-opentelemetry-logs-traces --opentelemetry-logs-traces-port-http=8080 --opentelemetry-logs-traces-port-grpc=8082 ' '--enable-opentelemetry-metrics --opentelemetry-metrics-port-http=8081 --opentelemetry-metrics-port-grpc=8083 ' '--enable-windows-recording-rules ' + '--aks-custom-headers AKSHTTPCustomFeatures=Microsoft.ContainerService/AzureMonitorAppMonitoringPreview ' '--output=json' ) self.cmd(create_cmd, checks=[ @@ -17348,6 +17354,7 @@ def test_aks_comprehensive_monitoring_integration(self, resource_group, resource 'aks update --resource-group={resource_group} --name={name} --yes --output=json ' '--enable-opentelemetry-logs-traces --opentelemetry-logs-traces-port-http=9090 --opentelemetry-logs-traces-port-grpc=9092 ' '--enable-opentelemetry-metrics --opentelemetry-metrics-port-http=9091 --opentelemetry-metrics-port-grpc=9093 ' + '--aks-custom-headers AKSHTTPCustomFeatures=Microsoft.ContainerService/AzureMonitorAppMonitoringPreview ' ) self.cmd(update_cmd, checks=[ self.check('provisioningState', 'Succeeded'), @@ -17440,6 +17447,7 @@ def test_aks_comprehensive_monitoring_integration(self, resource_group, resource '--enable-opentelemetry-logs-traces --opentelemetry-logs-traces-port-http=7070 --opentelemetry-logs-traces-port-grpc=7072 ' '--enable-opentelemetry-metrics --opentelemetry-metrics-port-http=7071 --opentelemetry-metrics-port-grpc=7073 ' '--enable-windows-recording-rules ' + '--aks-custom-headers AKSHTTPCustomFeatures=Microsoft.ContainerService/AzureMonitorAppMonitoringPreview ' ) self.cmd(update_cmd, checks=[ self.check('provisioningState', 'Succeeded'), From 20fb22d8d280994dffdf253dc7cd972427d7b025 Mon Sep 17 00:00:00 2001 From: Christoph Butz Date: Tue, 4 Aug 2026 10:52:01 +0100 Subject: [PATCH 10/14] [aks-preview] Use per-option deprecation for renamed OpenTelemetry flags The Azure CLI Extensions Breaking Change Test reported 8 x rule 1010 (ParaPropUpdate) failures, one per renamed OpenTelemetry flag on aks create and aks update. azure-cli-diff-tool tracks a parameter by its dest and treats the option list as breaking unless the old options remain a subset of the new ones (meta_change_detect.py: set(base_val).issubset(set(cmp_val))). Registering the previous option names as separate *_deprecated dests meant each original dest lost its original option, so every rename was flagged as a break even though the old flags still worked. Keep a single dest per flag and pass the previous name as a Deprecated entry inside options_list, the idiomatic pattern already used for --agent-pool-name in this file. The exported metadata now lists both option names on the original dest plus options_deprecate_info, which the tool reports as non-breaking. Note: is_preview cannot be combined with a Deprecated entry in options_list, because knack _handle_previews computes sorted(options_list, key=len) and Deprecated has no __len__. The four renamed flags therefore drop is_preview; the two genuinely new gRPC flags keep it. Preview status is not part of the metadata the breaking change tool compares. Because argparse now writes both option names to the same dest, the _merge_opentelemetry_deprecated_aliases validator helper, the eight *_deprecated command parameters and the alias merge tests are all redundant and removed. --- src/aks-preview/azext_aks_preview/_params.py | 144 +++++++----------- .../azext_aks_preview/_validators.py | 28 ---- src/aks-preview/azext_aks_preview/custom.py | 10 -- .../tests/latest/test_validators.py | 53 ------- 4 files changed, 56 insertions(+), 179 deletions(-) diff --git a/src/aks-preview/azext_aks_preview/_params.py b/src/aks-preview/azext_aks_preview/_params.py index b85fd26bad6..d7fdf491521 100644 --- a/src/aks-preview/azext_aks_preview/_params.py +++ b/src/aks-preview/azext_aks_preview/_params.py @@ -1161,17 +1161,13 @@ def load_arguments(self, _): validator=validate_azure_monitor_and_opentelemetry_for_create ) c.argument("opentelemetry_metrics_port", - options_list=["--opentelemetry-metrics-port-http"], - is_preview=True, - type=int, - help="HTTP/protobuf port for OpenTelemetry metrics collection" - ) - c.argument("opentelemetry_metrics_port_deprecated", - options_list=["--opentelemetry-metrics-port"], - deprecate_info=c.deprecate( - target="--opentelemetry-metrics-port", - redirect="--opentelemetry-metrics-port-http", - ), + options_list=[ + "--opentelemetry-metrics-port-http", + c.deprecate( + target="--opentelemetry-metrics-port", + redirect="--opentelemetry-metrics-port-http", + ), + ], type=int, help="HTTP/protobuf port for OpenTelemetry metrics collection" ) @@ -1187,32 +1183,24 @@ def load_arguments(self, _): help="Disable OpenTelemetry metrics collection" ) c.argument("enable_opentelemetry_logs", - options_list=["--enable-opentelemetry-logs-traces"], - is_preview=True, - action="store_true", - help="Enable OpenTelemetry logs and traces collection" - ) - c.argument("enable_opentelemetry_logs_deprecated", - options_list=["--enable-opentelemetry-logs"], - deprecate_info=c.deprecate( - target="--enable-opentelemetry-logs", - redirect="--enable-opentelemetry-logs-traces", - ), + options_list=[ + "--enable-opentelemetry-logs-traces", + c.deprecate( + target="--enable-opentelemetry-logs", + redirect="--enable-opentelemetry-logs-traces", + ), + ], action="store_true", help="Enable OpenTelemetry logs and traces collection" ) c.argument("opentelemetry_logs_port", - options_list=["--opentelemetry-logs-traces-port-http"], - is_preview=True, - type=int, - help="HTTP/protobuf port for OpenTelemetry logs and traces collection" - ) - c.argument("opentelemetry_logs_port_deprecated", - options_list=["--opentelemetry-logs-port"], - deprecate_info=c.deprecate( - target="--opentelemetry-logs-port", - redirect="--opentelemetry-logs-traces-port-http", - ), + options_list=[ + "--opentelemetry-logs-traces-port-http", + c.deprecate( + target="--opentelemetry-logs-port", + redirect="--opentelemetry-logs-traces-port-http", + ), + ], type=int, help="HTTP/protobuf port for OpenTelemetry logs and traces collection" ) @@ -1223,17 +1211,13 @@ def load_arguments(self, _): help="gRPC port for OpenTelemetry logs and traces collection" ) c.argument("disable_opentelemetry_logs", - options_list=["--disable-opentelemetry-logs-traces"], - is_preview=True, - action="store_true", - help="Disable OpenTelemetry logs and traces collection" - ) - c.argument("disable_opentelemetry_logs_deprecated", - options_list=["--disable-opentelemetry-logs"], - deprecate_info=c.deprecate( - target="--disable-opentelemetry-logs", - redirect="--disable-opentelemetry-logs-traces", - ), + options_list=[ + "--disable-opentelemetry-logs-traces", + c.deprecate( + target="--disable-opentelemetry-logs", + redirect="--disable-opentelemetry-logs-traces", + ), + ], action="store_true", help="Disable OpenTelemetry logs and traces collection" ) @@ -1816,17 +1800,13 @@ def load_arguments(self, _): validator=validate_azure_monitor_and_opentelemetry_for_update ) c.argument("opentelemetry_metrics_port", - options_list=["--opentelemetry-metrics-port-http"], - is_preview=True, - type=int, - help="HTTP/protobuf port for OpenTelemetry metrics collection" - ) - c.argument("opentelemetry_metrics_port_deprecated", - options_list=["--opentelemetry-metrics-port"], - deprecate_info=c.deprecate( - target="--opentelemetry-metrics-port", - redirect="--opentelemetry-metrics-port-http", - ), + options_list=[ + "--opentelemetry-metrics-port-http", + c.deprecate( + target="--opentelemetry-metrics-port", + redirect="--opentelemetry-metrics-port-http", + ), + ], type=int, help="HTTP/protobuf port for OpenTelemetry metrics collection" ) @@ -1842,32 +1822,24 @@ def load_arguments(self, _): help="Disable OpenTelemetry metrics collection" ) c.argument("enable_opentelemetry_logs", - options_list=["--enable-opentelemetry-logs-traces"], - is_preview=True, - action="store_true", - help="Enable OpenTelemetry logs and traces collection" - ) - c.argument("enable_opentelemetry_logs_deprecated", - options_list=["--enable-opentelemetry-logs"], - deprecate_info=c.deprecate( - target="--enable-opentelemetry-logs", - redirect="--enable-opentelemetry-logs-traces", - ), + options_list=[ + "--enable-opentelemetry-logs-traces", + c.deprecate( + target="--enable-opentelemetry-logs", + redirect="--enable-opentelemetry-logs-traces", + ), + ], action="store_true", help="Enable OpenTelemetry logs and traces collection" ) c.argument("opentelemetry_logs_port", - options_list=["--opentelemetry-logs-traces-port-http"], - is_preview=True, - type=int, - help="HTTP/protobuf port for OpenTelemetry logs and traces collection" - ) - c.argument("opentelemetry_logs_port_deprecated", - options_list=["--opentelemetry-logs-port"], - deprecate_info=c.deprecate( - target="--opentelemetry-logs-port", - redirect="--opentelemetry-logs-traces-port-http", - ), + options_list=[ + "--opentelemetry-logs-traces-port-http", + c.deprecate( + target="--opentelemetry-logs-port", + redirect="--opentelemetry-logs-traces-port-http", + ), + ], type=int, help="HTTP/protobuf port for OpenTelemetry logs and traces collection" ) @@ -1878,17 +1850,13 @@ def load_arguments(self, _): help="gRPC port for OpenTelemetry logs and traces collection" ) c.argument("disable_opentelemetry_logs", - options_list=["--disable-opentelemetry-logs-traces"], - is_preview=True, - action="store_true", - help="Disable OpenTelemetry logs and traces collection" - ) - c.argument("disable_opentelemetry_logs_deprecated", - options_list=["--disable-opentelemetry-logs"], - deprecate_info=c.deprecate( - target="--disable-opentelemetry-logs", - redirect="--disable-opentelemetry-logs-traces", - ), + options_list=[ + "--disable-opentelemetry-logs-traces", + c.deprecate( + target="--disable-opentelemetry-logs", + redirect="--disable-opentelemetry-logs-traces", + ), + ], action="store_true", help="Disable OpenTelemetry logs and traces collection" ) diff --git a/src/aks-preview/azext_aks_preview/_validators.py b/src/aks-preview/azext_aks_preview/_validators.py index 9c6f1b67184..1a59efefc9d 100644 --- a/src/aks-preview/azext_aks_preview/_validators.py +++ b/src/aks-preview/azext_aks_preview/_validators.py @@ -1192,35 +1192,8 @@ def validate_opentelemetry_logs_dependencies_for_update(namespace): # to the cluster's Azure Monitor profile -def _merge_opentelemetry_deprecated_aliases(namespace): - """Merge deprecated OpenTelemetry alias dests into their current dests. - - The old option names are registered as separate arguments (so the new names can carry the - preview tag while the old names carry the deprecation tag). This copies any value supplied via - a deprecated alias onto the current dest, so all downstream validation and decorator logic only - needs to read the current dests. - """ - # (current_dest, deprecated_dest, is_flag) - pairs = [ - ("opentelemetry_metrics_port", "opentelemetry_metrics_port_deprecated", False), - ("opentelemetry_logs_port", "opentelemetry_logs_port_deprecated", False), - ("enable_opentelemetry_logs", "enable_opentelemetry_logs_deprecated", True), - ("disable_opentelemetry_logs", "disable_opentelemetry_logs_deprecated", True), - ] - for current, deprecated, is_flag in pairs: - if not hasattr(namespace, deprecated): - continue - deprecated_value = getattr(namespace, deprecated) - if is_flag: - if deprecated_value: - setattr(namespace, current, True) - elif deprecated_value is not None and getattr(namespace, current, None) is None: - setattr(namespace, current, deprecated_value) - - def validate_azure_monitor_and_opentelemetry_for_create(namespace): """Main validator for Azure Monitor and OpenTelemetry configurations for create operations.""" - _merge_opentelemetry_deprecated_aliases(namespace) # Run all OpenTelemetry-related validations validate_opentelemetry_ports(namespace) validate_opentelemetry_metrics_dependencies(namespace) @@ -1229,7 +1202,6 @@ def validate_azure_monitor_and_opentelemetry_for_create(namespace): def validate_azure_monitor_and_opentelemetry_for_update(namespace): """Main validator for Azure Monitor and OpenTelemetry configurations for update operations.""" - _merge_opentelemetry_deprecated_aliases(namespace) # Run all OpenTelemetry-related validations validate_opentelemetry_ports(namespace) validate_opentelemetry_metrics_dependencies_for_update(namespace) diff --git a/src/aks-preview/azext_aks_preview/custom.py b/src/aks-preview/azext_aks_preview/custom.py index 3dcb9c3a490..098799c125d 100644 --- a/src/aks-preview/azext_aks_preview/custom.py +++ b/src/aks-preview/azext_aks_preview/custom.py @@ -1402,11 +1402,6 @@ def aks_create( opentelemetry_logs_port=None, opentelemetry_logs_traces_port_grpc=None, disable_opentelemetry_logs=False, - # opentelemetry deprecated aliases (merged into the parameters above by the validator) - opentelemetry_metrics_port_deprecated=None, - enable_opentelemetry_logs_deprecated=False, - opentelemetry_logs_port_deprecated=None, - disable_opentelemetry_logs_deprecated=False, # metrics profile enable_cost_analysis=False, # AI toolchain operator @@ -1652,11 +1647,6 @@ def aks_update( opentelemetry_logs_port=None, opentelemetry_logs_traces_port_grpc=None, disable_opentelemetry_logs=False, - # opentelemetry deprecated aliases (merged into the parameters above by the validator) - opentelemetry_metrics_port_deprecated=None, - enable_opentelemetry_logs_deprecated=False, - opentelemetry_logs_port_deprecated=None, - disable_opentelemetry_logs_deprecated=False, enable_vpa=False, disable_vpa=False, enable_optimized_addon_scaling=False, diff --git a/src/aks-preview/azext_aks_preview/tests/latest/test_validators.py b/src/aks-preview/azext_aks_preview/tests/latest/test_validators.py index 1432e65a41e..aefca08f0d6 100644 --- a/src/aks-preview/azext_aks_preview/tests/latest/test_validators.py +++ b/src/aks-preview/azext_aks_preview/tests/latest/test_validators.py @@ -2045,59 +2045,6 @@ def __init__(self, opentelemetry_metrics_port=None, opentelemetry_logs_port=None self.opentelemetry_logs_traces_port_grpc = opentelemetry_logs_traces_port_grpc -class OpenTelemetryDeprecatedAliasNamespace: - def __init__(self, opentelemetry_metrics_port=None, opentelemetry_metrics_port_deprecated=None, - opentelemetry_logs_port=None, opentelemetry_logs_port_deprecated=None, - enable_opentelemetry_logs=False, enable_opentelemetry_logs_deprecated=False, - disable_opentelemetry_logs=False, disable_opentelemetry_logs_deprecated=False): - self.opentelemetry_metrics_port = opentelemetry_metrics_port - self.opentelemetry_metrics_port_deprecated = opentelemetry_metrics_port_deprecated - self.opentelemetry_logs_port = opentelemetry_logs_port - self.opentelemetry_logs_port_deprecated = opentelemetry_logs_port_deprecated - self.enable_opentelemetry_logs = enable_opentelemetry_logs - self.enable_opentelemetry_logs_deprecated = enable_opentelemetry_logs_deprecated - self.disable_opentelemetry_logs = disable_opentelemetry_logs - self.disable_opentelemetry_logs_deprecated = disable_opentelemetry_logs_deprecated - - -class TestMergeOpenTelemetryDeprecatedAliases(unittest.TestCase): - def test_deprecated_port_merged_when_current_unset(self): - ns = OpenTelemetryDeprecatedAliasNamespace(opentelemetry_metrics_port_deprecated=8080) - validators._merge_opentelemetry_deprecated_aliases(ns) - self.assertEqual(ns.opentelemetry_metrics_port, 8080) - - def test_current_port_takes_precedence_over_deprecated(self): - ns = OpenTelemetryDeprecatedAliasNamespace( - opentelemetry_metrics_port=9000, - opentelemetry_metrics_port_deprecated=8080, - ) - validators._merge_opentelemetry_deprecated_aliases(ns) - self.assertEqual(ns.opentelemetry_metrics_port, 9000) - - def test_deprecated_logs_port_merged(self): - ns = OpenTelemetryDeprecatedAliasNamespace(opentelemetry_logs_port_deprecated=4317) - validators._merge_opentelemetry_deprecated_aliases(ns) - self.assertEqual(ns.opentelemetry_logs_port, 4317) - - def test_deprecated_enable_flag_merged(self): - ns = OpenTelemetryDeprecatedAliasNamespace(enable_opentelemetry_logs_deprecated=True) - validators._merge_opentelemetry_deprecated_aliases(ns) - self.assertTrue(ns.enable_opentelemetry_logs) - - def test_deprecated_disable_flag_merged(self): - ns = OpenTelemetryDeprecatedAliasNamespace(disable_opentelemetry_logs_deprecated=True) - validators._merge_opentelemetry_deprecated_aliases(ns) - self.assertTrue(ns.disable_opentelemetry_logs) - - def test_no_deprecated_values_leaves_current_unchanged(self): - ns = OpenTelemetryDeprecatedAliasNamespace() - validators._merge_opentelemetry_deprecated_aliases(ns) - self.assertIsNone(ns.opentelemetry_metrics_port) - self.assertIsNone(ns.opentelemetry_logs_port) - self.assertFalse(ns.enable_opentelemetry_logs) - self.assertFalse(ns.disable_opentelemetry_logs) - - class TestValidateOpenTelemetryPorts(unittest.TestCase): def test_no_ports_specified(self): namespace = OpenTelemetryPortsNamespace() From 73fa98668f32f2d6401d85baa0cb954ed784aea6 Mon Sep 17 00:00:00 2001 From: Christoph Butz Date: Thu, 6 Aug 2026 12:13:56 +0100 Subject: [PATCH 11/14] [aks-preview] Wait for the monitoring extension between OTLP update steps test_aks_update_with_azuremonitorlogs_and_opentelemetry failed in live run 175416696 with: (CreateOrUpdateExtensionFailed) Create or update of core cluster extension aks-managed-azure-monitor-logs of type microsoft.azuremonitor.containers failed. Update failed for this resource, as there is a conflicting operation in progress. The monitoring cluster extension keeps provisioning after 'az aks update' returns, so the following '--disable-opentelemetry-logs-traces' update collided with the still-running enable operation. Adds 'az aks wait --updated' after the enable update and after the disable update, which is the pattern test_aks_comprehensive_monitoring_integration already uses. That test runs the same disable command and passed in the same run. --- .../tests/latest/test_aks_commands.py | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/src/aks-preview/azext_aks_preview/tests/latest/test_aks_commands.py b/src/aks-preview/azext_aks_preview/tests/latest/test_aks_commands.py index 8659723a1e5..488bbf82fa5 100644 --- a/src/aks-preview/azext_aks_preview/tests/latest/test_aks_commands.py +++ b/src/aks-preview/azext_aks_preview/tests/latest/test_aks_commands.py @@ -16664,6 +16664,17 @@ def test_aks_update_with_azuremonitorlogs_and_opentelemetry(self, resource_group self.check('azureMonitorProfile.appMonitoring.openTelemetryLogsAndTraces.grpcPort', 9092), ]) + # The monitoring cluster extension keeps provisioning after the update call returns. + # Without this wait the next update collides with it and fails with + # CreateOrUpdateExtensionFailed ("conflicting operation in progress"). + wait_cmd = ' '.join([ + 'aks', 'wait', '--resource-group={resource_group}', '--name={name}', '--updated', + '--interval 60', '--timeout 1800', + ]) + self.cmd(wait_cmd, checks=[ + self.is_empty(), + ]) + # update: disable OpenTelemetry logs but keep Azure Monitor logs update_cmd = ( 'aks update --resource-group={resource_group} --name={name} --yes --output=json ' @@ -16676,6 +16687,10 @@ def test_aks_update_with_azuremonitorlogs_and_opentelemetry(self, resource_group self.check('azureMonitorProfile.appMonitoring.openTelemetryLogsAndTraces.enabled', False), ]) + self.cmd(wait_cmd, checks=[ + self.is_empty(), + ]) + # update: disable-azure-monitor-logs (should also disable OpenTelemetry logs) update_cmd = ( 'aks update --resource-group={resource_group} --name={name} --yes --output=json ' From bd5f1608b256827f492dd196935cd8db21372edf Mon Sep 17 00:00:00 2001 From: Christoph Butz Date: Thu, 6 Aug 2026 12:14:19 +0100 Subject: [PATCH 12/14] [aks-preview] Use the current flag name in aks create examples The 'aks create' examples used '--enable-azuremonitormetrics', which is deprecated with hide=True and redirects to '--enable-azure-monitor-metrics'. Examples must show the current name. Addresses the review comment on the OpenTelemetry metrics examples. The parameter documentation entries for the deprecated names are unchanged, because those still document the deprecated flags themselves. --- src/aks-preview/azext_aks_preview/_help.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/aks-preview/azext_aks_preview/_help.py b/src/aks-preview/azext_aks_preview/_help.py index 52a94956a54..bfdc000bf06 100644 --- a/src/aks-preview/azext_aks_preview/_help.py +++ b/src/aks-preview/azext_aks_preview/_help.py @@ -845,21 +845,21 @@ - name: Create a kubernetes cluster with Azure Service Mesh enabled. text: az aks create -g MyResourceGroup -n MyManagedCluster --enable-azure-service-mesh - name: Create a kubernetes cluster with Azure Monitor Metrics enabled. - text: az aks create -g MyResourceGroup -n MyManagedCluster --enable-azuremonitormetrics + text: az aks create -g MyResourceGroup -n MyManagedCluster --enable-azure-monitor-metrics - name: Create a kubernetes cluster with Azure Monitor App Monitoring enabled text: az aks create -g MyResourceGroup -n MyManagedCluster --enable-azure-monitor-app-monitoring - name: Create a kubernetes cluster with OpenTelemetry metrics collection enabled - text: az aks create -g MyResourceGroup -n MyManagedCluster --enable-opentelemetry-metrics --enable-azuremonitormetrics + text: az aks create -g MyResourceGroup -n MyManagedCluster --enable-opentelemetry-metrics --enable-azure-monitor-metrics - name: Create a kubernetes cluster with OpenTelemetry logs and traces collection enabled text: az aks create -g MyResourceGroup -n MyManagedCluster --enable-opentelemetry-logs-traces --enable-addons monitoring - name: Create a kubernetes cluster with Azure Monitor logs enabled (shorthand) text: az aks create -g MyResourceGroup -n MyManagedCluster --enable-azure-monitor-logs - name: Create a kubernetes cluster with OpenTelemetry metrics on custom port - text: az aks create -g MyResourceGroup -n MyManagedCluster --enable-opentelemetry-metrics --opentelemetry-metrics-port-http 8888 --enable-azuremonitormetrics + text: az aks create -g MyResourceGroup -n MyManagedCluster --enable-opentelemetry-metrics --opentelemetry-metrics-port-http 8888 --enable-azure-monitor-metrics - name: Create a kubernetes cluster with OpenTelemetry logs and traces on custom ports text: az aks create -g MyResourceGroup -n MyManagedCluster --enable-opentelemetry-logs-traces --opentelemetry-logs-traces-port-http 8080 --opentelemetry-logs-traces-port-grpc 8082 --enable-azure-monitor-logs - name: Create a kubernetes cluster with OpenTelemetry metrics on custom HTTP and gRPC ports - text: az aks create -g MyResourceGroup -n MyManagedCluster --enable-opentelemetry-metrics --opentelemetry-metrics-port-http 8888 --opentelemetry-metrics-port-grpc 8889 --enable-azuremonitormetrics + text: az aks create -g MyResourceGroup -n MyManagedCluster --enable-opentelemetry-metrics --opentelemetry-metrics-port-http 8888 --opentelemetry-metrics-port-grpc 8889 --enable-azure-monitor-metrics - name: Create a kubernetes cluster with a nodepool having ip allocation mode set to "StaticBlock" text: az aks create -g MyResourceGroup -n MyManagedCluster --os-sku Ubuntu --max-pods MaxPodsPerNode --network-plugin azure --vnet-subnet-id /subscriptions/00000/resourceGroups/AnotherResourceGroup/providers/Microsoft.Network/virtualNetworks/MyVnet/subnets/NodeSubnet --pod-subnet-id /subscriptions/00000/resourceGroups/AnotherResourceGroup/providers/Microsoft.Network/virtualNetworks/MyVnet/subnets/PodSubnet --pod-ip-allocation-mode StaticBlock - name: Create a kubernetes cluster with a VirtualMachines nodepool From f0528fc5c4fb42b76569ec13159aea4de66f3b6e Mon Sep 17 00:00:00 2001 From: Christoph Butz Date: Thu, 6 Aug 2026 12:28:27 +0100 Subject: [PATCH 13/14] [aks-preview] Restore the preview tag on the renamed OpenTelemetry flags The renamed flags lost their [Preview] tag when they moved to per-option deprecation. knack computes the preview target with sorted(options_list, key=len), and a plain Deprecated has no __len__, so is_preview=True raised TypeError when combined with a deprecated option name. Adds a small Deprecated subclass that reports the length of its option name. isinstance checks in _handle_deprecations still match, and sorted() now picks the longest option, which is the current name. Each renamed flag shows [Preview] on the current name and [Deprecated] on the old name. Verified in az aks update --help. The exported metadata is unchanged, so the breaking change test still reports 0 breaking findings. --- src/aks-preview/azext_aks_preview/_params.py | 77 ++++++++++++-------- 1 file changed, 45 insertions(+), 32 deletions(-) diff --git a/src/aks-preview/azext_aks_preview/_params.py b/src/aks-preview/azext_aks_preview/_params.py index d7fdf491521..474528b10f5 100644 --- a/src/aks-preview/azext_aks_preview/_params.py +++ b/src/aks-preview/azext_aks_preview/_params.py @@ -284,6 +284,35 @@ ) from knack.arguments import CLIArgumentType +from knack.deprecation import Deprecated + + +class _SizedDeprecated(Deprecated): + """A Deprecated option that reports a length. + + knack computes the preview target with sorted(options_list, key=len). A plain Deprecated + has no __len__, so combining is_preview=True with a deprecated option name raises + TypeError. Reporting the length of the option name keeps both status tags usable and + makes the longest (current) option name the preview target. + """ + + def __len__(self): + return len(self.target) + + +def _deprecate_option(c, target, redirect): + """Deprecate a single option name and keep it compatible with is_preview.""" + return _SizedDeprecated( + c.command_loader.cli_ctx, + target=target, + redirect=redirect, + object_type="option", + message_func=lambda self: ( + "Option '{}' has been deprecated and will be removed in a future release. " + "Use '{}' instead.".format(self.target, self.redirect) + ), + ) + # candidates for enumeration # consts for AgentPool @@ -1163,11 +1192,9 @@ def load_arguments(self, _): c.argument("opentelemetry_metrics_port", options_list=[ "--opentelemetry-metrics-port-http", - c.deprecate( - target="--opentelemetry-metrics-port", - redirect="--opentelemetry-metrics-port-http", - ), + _deprecate_option(c, "--opentelemetry-metrics-port", "--opentelemetry-metrics-port-http"), ], + is_preview=True, type=int, help="HTTP/protobuf port for OpenTelemetry metrics collection" ) @@ -1185,22 +1212,18 @@ def load_arguments(self, _): c.argument("enable_opentelemetry_logs", options_list=[ "--enable-opentelemetry-logs-traces", - c.deprecate( - target="--enable-opentelemetry-logs", - redirect="--enable-opentelemetry-logs-traces", - ), + _deprecate_option(c, "--enable-opentelemetry-logs", "--enable-opentelemetry-logs-traces"), ], + is_preview=True, action="store_true", help="Enable OpenTelemetry logs and traces collection" ) c.argument("opentelemetry_logs_port", options_list=[ "--opentelemetry-logs-traces-port-http", - c.deprecate( - target="--opentelemetry-logs-port", - redirect="--opentelemetry-logs-traces-port-http", - ), + _deprecate_option(c, "--opentelemetry-logs-port", "--opentelemetry-logs-traces-port-http"), ], + is_preview=True, type=int, help="HTTP/protobuf port for OpenTelemetry logs and traces collection" ) @@ -1213,11 +1236,9 @@ def load_arguments(self, _): c.argument("disable_opentelemetry_logs", options_list=[ "--disable-opentelemetry-logs-traces", - c.deprecate( - target="--disable-opentelemetry-logs", - redirect="--disable-opentelemetry-logs-traces", - ), + _deprecate_option(c, "--disable-opentelemetry-logs", "--disable-opentelemetry-logs-traces"), ], + is_preview=True, action="store_true", help="Disable OpenTelemetry logs and traces collection" ) @@ -1802,11 +1823,9 @@ def load_arguments(self, _): c.argument("opentelemetry_metrics_port", options_list=[ "--opentelemetry-metrics-port-http", - c.deprecate( - target="--opentelemetry-metrics-port", - redirect="--opentelemetry-metrics-port-http", - ), + _deprecate_option(c, "--opentelemetry-metrics-port", "--opentelemetry-metrics-port-http"), ], + is_preview=True, type=int, help="HTTP/protobuf port for OpenTelemetry metrics collection" ) @@ -1824,22 +1843,18 @@ def load_arguments(self, _): c.argument("enable_opentelemetry_logs", options_list=[ "--enable-opentelemetry-logs-traces", - c.deprecate( - target="--enable-opentelemetry-logs", - redirect="--enable-opentelemetry-logs-traces", - ), + _deprecate_option(c, "--enable-opentelemetry-logs", "--enable-opentelemetry-logs-traces"), ], + is_preview=True, action="store_true", help="Enable OpenTelemetry logs and traces collection" ) c.argument("opentelemetry_logs_port", options_list=[ "--opentelemetry-logs-traces-port-http", - c.deprecate( - target="--opentelemetry-logs-port", - redirect="--opentelemetry-logs-traces-port-http", - ), + _deprecate_option(c, "--opentelemetry-logs-port", "--opentelemetry-logs-traces-port-http"), ], + is_preview=True, type=int, help="HTTP/protobuf port for OpenTelemetry logs and traces collection" ) @@ -1852,11 +1867,9 @@ def load_arguments(self, _): c.argument("disable_opentelemetry_logs", options_list=[ "--disable-opentelemetry-logs-traces", - c.deprecate( - target="--disable-opentelemetry-logs", - redirect="--disable-opentelemetry-logs-traces", - ), + _deprecate_option(c, "--disable-opentelemetry-logs", "--disable-opentelemetry-logs-traces"), ], + is_preview=True, action="store_true", help="Disable OpenTelemetry logs and traces collection" ) From f33099b39eda851188511eb9531132334c26a72b Mon Sep 17 00:00:00 2001 From: Christoph Butz Date: Thu, 6 Aug 2026 13:21:23 +0100 Subject: [PATCH 14/14] [aks-preview] Build the sized deprecation through c.deprecate() The helper constructed the Deprecated object directly, which duplicated the message text and skipped the applicability and stale-context checks that c.deprecate() performs. It also ignored the expiration field when building the message. Now the helper calls c.deprecate() and only replaces the class, so the message, the object type and both checks stay identical to every other deprecated option in this file. --- src/aks-preview/azext_aks_preview/_params.py | 21 ++++++++++---------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/src/aks-preview/azext_aks_preview/_params.py b/src/aks-preview/azext_aks_preview/_params.py index 474528b10f5..af20566a676 100644 --- a/src/aks-preview/azext_aks_preview/_params.py +++ b/src/aks-preview/azext_aks_preview/_params.py @@ -301,17 +301,16 @@ def __len__(self): def _deprecate_option(c, target, redirect): - """Deprecate a single option name and keep it compatible with is_preview.""" - return _SizedDeprecated( - c.command_loader.cli_ctx, - target=target, - redirect=redirect, - object_type="option", - message_func=lambda self: ( - "Option '{}' has been deprecated and will be removed in a future release. " - "Use '{}' instead.".format(self.target, self.redirect) - ), - ) + """Deprecate a single option name and keep it compatible with is_preview. + + Builds the Deprecated object through c.deprecate() so that the message, the object type + and the applicability checks stay identical to every other deprecated option, then only + adds the __len__ behaviour that knack needs for the preview tag. + """ + deprecated = c.deprecate(target=target, redirect=redirect) + if deprecated is not None: + deprecated.__class__ = _SizedDeprecated + return deprecated # candidates for enumeration