From e88d39accfc97ba3ba12bd21ce050a125deb407f Mon Sep 17 00:00:00 2001 From: bgriddaluru Date: Thu, 5 Mar 2026 20:26:44 +0000 Subject: [PATCH 1/2] Use latest diagnostics image --- .../azext_connectedk8s/_constants.py | 8 +- .../azext_connectedk8s/_precheckutils.py | 8 +- src/connectedk8s/azext_connectedk8s/_utils.py | 96 +++++++++++++++++++ src/connectedk8s/azext_connectedk8s/custom.py | 1 + 4 files changed, 110 insertions(+), 3 deletions(-) diff --git a/src/connectedk8s/azext_connectedk8s/_constants.py b/src/connectedk8s/azext_connectedk8s/_constants.py index 4ec7bbb96ee..9e0a0f3e20f 100644 --- a/src/connectedk8s/azext_connectedk8s/_constants.py +++ b/src/connectedk8s/azext_connectedk8s/_constants.py @@ -417,9 +417,13 @@ SigningKey_CR_Snapshot = "signingkey_cr_snapshot.txt" # Connect Precheck Diagnoser constants -Cluster_Diagnostic_Checks_Job_Registry_Path = ( - "azurearck8s/helmchart/stable/clusterdiagnosticchecks:1.31.2" + +# Repository path (without tag) for the diagnostic checks helm chart on MCR +Cluster_Diagnostic_Checks_Job_Repo_Path = ( + "azurearck8s/helmchart/stable/clusterdiagnosticchecks" ) +# Environment variable that, when set, overrides the full registry path (supports non-MCR paths) +Diagnostic_Checks_Registry_Path_Env_Var = "DIAGNOSTIC_CHECKS_REGISTRY_PATH" Cluster_Diagnostic_Checks_Helm_Install_Failed_Fault_Type = ( "Error while installing cluster diagnostic checks helm release" ) diff --git a/src/connectedk8s/azext_connectedk8s/_precheckutils.py b/src/connectedk8s/azext_connectedk8s/_precheckutils.py index 231f2b4c659..bf0669b0628 100644 --- a/src/connectedk8s/azext_connectedk8s/_precheckutils.py +++ b/src/connectedk8s/azext_connectedk8s/_precheckutils.py @@ -46,6 +46,7 @@ def fetch_diagnostic_checks_results( azure_cloud: str, filepath_with_timestamp: str, storage_space_available: bool, + release_train: str | None = None, ) -> tuple[str, bool]: try: # Setting DNS and Outbound Check as working @@ -69,6 +70,7 @@ def fetch_diagnostic_checks_results( azure_cloud, filepath_with_timestamp, storage_space_available, + release_train, ) ) # If cluster_diagnostic_checks_container_log is not empty there were errors. Try to read the logs. @@ -153,6 +155,7 @@ def executing_cluster_diagnostic_checks_job( azure_cloud: str, filepath_with_timestamp: str, storage_space_available: bool, + release_train: str | None = None, ) -> str | None: job_name = "cluster-diagnostic-checks-job" # Setting the log output as Empty @@ -213,9 +216,12 @@ def executing_cluster_diagnostic_checks_job( return None mcr_url = azext_utils.get_mcr_path(cmd.cli_ctx.cloud.endpoints.active_directory) + registry_path = azext_utils.get_diagnostic_checks_registry_path( + mcr_url, release_train + ) chart_path = azext_utils.get_chart_path( - f"{mcr_url}/{consts.Cluster_Diagnostic_Checks_Job_Registry_Path}", + registry_path, kube_config, kube_context, helm_client_location, diff --git a/src/connectedk8s/azext_connectedk8s/_utils.py b/src/connectedk8s/azext_connectedk8s/_utils.py index 15547848469..78d163977b4 100644 --- a/src/connectedk8s/azext_connectedk8s/_utils.py +++ b/src/connectedk8s/azext_connectedk8s/_utils.py @@ -15,6 +15,8 @@ from subprocess import PIPE, Popen from typing import TYPE_CHECKING, Any +import requests + from azure.cli.core import get_default_cli, telemetry from azure.cli.core.azclierror import ( ArgumentUsageError, @@ -84,6 +86,100 @@ def get_mcr_path(active_directory_endpoint: str) -> str: return mcr_url +def fetch_diagnostic_checks_tags(mcr_url: str, repo_path: str) -> list[str]: + """Fetches all available tags for the diagnostic checks image from the OCI registry. + + Queries the standard OCI ``/v2//tags/list`` endpoint and returns the + raw list of tag strings. Returns an empty list on any failure so callers + can fall back gracefully. + """ + url = f"https://{mcr_url}/v2/{repo_path}/tags/list" + try: + response = requests.get(url, timeout=30) + response.raise_for_status() + return response.json().get("tags", []) + except Exception as e: + logger.debug("Failed to fetch diagnostic checks tags from %s: %s", url, e) + return [] + + +def select_latest_diagnostic_checks_tag(tags: list[str], is_preview: bool) -> str | None: + """Selects the latest tag from *tags*, filtered by whether preview is desired. + + Preview tags are expected to contain the substring ``preview`` (e.g. + ``1.32.0-preview``). Non-preview tags are those without it. The latest + tag is determined using :mod:`packaging.version` so that ``1.10.0`` sorts + higher than ``1.9.0``. Returns ``None`` when no matching tag is found. + """ + if is_preview: + candidates = [t for t in tags if "preview" in t.lower()] + else: + candidates = [t for t in tags if "preview" not in t.lower()] + + if not candidates: + return None + + def _parse_version(tag: str) -> version.Version: + # Strip the -preview suffix (and any other pre-release text) so that + # packaging.version can parse the numeric part reliably. + clean = re.sub(r"[-.]?preview.*$", "", tag, flags=re.IGNORECASE).strip("-") + try: + return version.parse(clean) + except Exception: + return version.parse("0.0.0") + + candidates.sort(key=_parse_version, reverse=True) + return candidates[0] + + +def get_diagnostic_checks_registry_path(mcr_url: str, release_train: str | None) -> str: + """Returns the full ``registry/repo:tag`` path for the diagnostic checks helm chart. + + Resolution order: + + 1. :envvar:`DIAGNOSTIC_CHECKS_REGISTRY_PATH` – when set, this value is + returned as-is, allowing non-MCR paths and custom tags for testing. + 2. Latest matching tag fetched live from the MCR OCI registry, filtered by + *release_train* (``"preview"`` → preview tags; anything else → stable). + + Raises :class:`~azure.cli.core.azclierror.CLIInternalError` if tag + discovery fails and no env override is set, rather than silently using a + potentially stale hardcoded tag. + """ + # 1. Environment variable override (supports non-MCR paths) + env_override = os.getenv(consts.Diagnostic_Checks_Registry_Path_Env_Var) + if env_override: + logger.debug( + "Using env override for diagnostic checks registry path: %s", env_override + ) + return env_override + + # 2. Live tag discovery from MCR + is_preview = (release_train or "").lower() == "preview" + repo_path = consts.Cluster_Diagnostic_Checks_Job_Repo_Path + tags = fetch_diagnostic_checks_tags(mcr_url, repo_path) + tag_kind = "preview" if is_preview else "stable" + if tags: + latest_tag = select_latest_diagnostic_checks_tag(tags, is_preview) + if latest_tag: + registry_path = f"{mcr_url}/{repo_path}:{latest_tag}" + logger.debug( + "Using latest diagnostic checks tag from MCR: %s", registry_path + ) + return registry_path + raise CLIInternalError( + f"No {tag_kind} tags found for diagnostic checks chart at '{mcr_url}/{repo_path}'. " + f"Set the '{consts.Diagnostic_Checks_Registry_Path_Env_Var}' environment variable " + "to specify a custom registry path." + ) + + raise CLIInternalError( + f"Failed to fetch diagnostic checks tags from '{mcr_url}/{repo_path}'. " + f"Set the '{consts.Diagnostic_Checks_Registry_Path_Env_Var}' environment variable " + "to specify a custom registry path and bypass MCR tag discovery." + ) + + def validate_connect_rp_location(cmd: CLICommand, location: str) -> None: subscription_id = ( os.getenv("AZURE_SUBSCRIPTION_ID") diff --git a/src/connectedk8s/azext_connectedk8s/custom.py b/src/connectedk8s/azext_connectedk8s/custom.py index d0e399bcbb2..07d8ddcd384 100644 --- a/src/connectedk8s/azext_connectedk8s/custom.py +++ b/src/connectedk8s/azext_connectedk8s/custom.py @@ -361,6 +361,7 @@ def create_connectedk8s( azure_cloud, filepath_with_timestamp, storage_space_available, + release_train, ) ) precheckutils.fetching_cli_output_logs( From 66e0c3ce687410d4b9dadf9a89a67fc31c90211d Mon Sep 17 00:00:00 2001 From: bgriddaluru Date: Thu, 5 Mar 2026 21:22:26 +0000 Subject: [PATCH 2/2] use release train from env --- src/connectedk8s/azext_connectedk8s/_precheckutils.py | 7 +------ src/connectedk8s/azext_connectedk8s/_utils.py | 8 +++++--- src/connectedk8s/azext_connectedk8s/custom.py | 1 - 3 files changed, 6 insertions(+), 10 deletions(-) diff --git a/src/connectedk8s/azext_connectedk8s/_precheckutils.py b/src/connectedk8s/azext_connectedk8s/_precheckutils.py index bf0669b0628..a7fd90783f5 100644 --- a/src/connectedk8s/azext_connectedk8s/_precheckutils.py +++ b/src/connectedk8s/azext_connectedk8s/_precheckutils.py @@ -46,7 +46,6 @@ def fetch_diagnostic_checks_results( azure_cloud: str, filepath_with_timestamp: str, storage_space_available: bool, - release_train: str | None = None, ) -> tuple[str, bool]: try: # Setting DNS and Outbound Check as working @@ -70,7 +69,6 @@ def fetch_diagnostic_checks_results( azure_cloud, filepath_with_timestamp, storage_space_available, - release_train, ) ) # If cluster_diagnostic_checks_container_log is not empty there were errors. Try to read the logs. @@ -155,7 +153,6 @@ def executing_cluster_diagnostic_checks_job( azure_cloud: str, filepath_with_timestamp: str, storage_space_available: bool, - release_train: str | None = None, ) -> str | None: job_name = "cluster-diagnostic-checks-job" # Setting the log output as Empty @@ -216,9 +213,7 @@ def executing_cluster_diagnostic_checks_job( return None mcr_url = azext_utils.get_mcr_path(cmd.cli_ctx.cloud.endpoints.active_directory) - registry_path = azext_utils.get_diagnostic_checks_registry_path( - mcr_url, release_train - ) + registry_path = azext_utils.get_diagnostic_checks_registry_path(mcr_url) chart_path = azext_utils.get_chart_path( registry_path, diff --git a/src/connectedk8s/azext_connectedk8s/_utils.py b/src/connectedk8s/azext_connectedk8s/_utils.py index 78d163977b4..a4f07c18405 100644 --- a/src/connectedk8s/azext_connectedk8s/_utils.py +++ b/src/connectedk8s/azext_connectedk8s/_utils.py @@ -132,7 +132,7 @@ def _parse_version(tag: str) -> version.Version: return candidates[0] -def get_diagnostic_checks_registry_path(mcr_url: str, release_train: str | None) -> str: +def get_diagnostic_checks_registry_path(mcr_url: str) -> str: """Returns the full ``registry/repo:tag`` path for the diagnostic checks helm chart. Resolution order: @@ -140,7 +140,8 @@ def get_diagnostic_checks_registry_path(mcr_url: str, release_train: str | None) 1. :envvar:`DIAGNOSTIC_CHECKS_REGISTRY_PATH` – when set, this value is returned as-is, allowing non-MCR paths and custom tags for testing. 2. Latest matching tag fetched live from the MCR OCI registry, filtered by - *release_train* (``"preview"`` → preview tags; anything else → stable). + the :envvar:`RELEASETRAIN` environment variable + (``"preview"`` → preview tags; anything else → stable). Raises :class:`~azure.cli.core.azclierror.CLIInternalError` if tag discovery fails and no env override is set, rather than silently using a @@ -155,7 +156,8 @@ def get_diagnostic_checks_registry_path(mcr_url: str, release_train: str | None) return env_override # 2. Live tag discovery from MCR - is_preview = (release_train or "").lower() == "preview" + release_train = os.getenv("RELEASETRAIN") if os.getenv("RELEASETRAIN") else "stable" + is_preview = release_train.lower() == "preview" repo_path = consts.Cluster_Diagnostic_Checks_Job_Repo_Path tags = fetch_diagnostic_checks_tags(mcr_url, repo_path) tag_kind = "preview" if is_preview else "stable" diff --git a/src/connectedk8s/azext_connectedk8s/custom.py b/src/connectedk8s/azext_connectedk8s/custom.py index 07d8ddcd384..d0e399bcbb2 100644 --- a/src/connectedk8s/azext_connectedk8s/custom.py +++ b/src/connectedk8s/azext_connectedk8s/custom.py @@ -361,7 +361,6 @@ def create_connectedk8s( azure_cloud, filepath_with_timestamp, storage_space_available, - release_train, ) ) precheckutils.fetching_cli_output_logs(