diff --git a/src/azure-cli/azure/cli/command_modules/acr/import.py b/src/azure-cli/azure/cli/command_modules/acr/import.py index 17bd01ca7a5..0250578f697 100644 --- a/src/azure-cli/azure/cli/command_modules/acr/import.py +++ b/src/azure-cli/azure/cli/command_modules/acr/import.py @@ -124,10 +124,17 @@ def _regional_endpoint_uri_to_login_server(uri, login_server_suffix): uri_lower = uri.strip().lower() suffix_lower = login_server_suffix.lower() - parts = uri_lower.split('.') - if len(parts) == 5 and parts[2] == 'geo' and uri_lower.endswith(suffix_lower): - return f"{parts[0]}{login_server_suffix}" + # A regional endpoint looks like "..geo". The suffix may contain + # more than two labels (e.g. ".azurecr.io" in public clouds vs the longer + # ".azurecr.sovcloud-azure.de" in sovereign clouds), so we strip the ".geo" tail + # rather than assuming a fixed number of dot-separated parts. + geo_suffix = f".geo{suffix_lower}" + if uri_lower.endswith(geo_suffix): + prefix = uri_lower[:-len(geo_suffix)] # "." + prefix_parts = prefix.split('.') + if len(prefix_parts) == 2 and prefix_parts[0] and prefix_parts[1]: + return f"{prefix_parts[0]}{login_server_suffix}" # If not a regional endpoint format, return as-is return uri diff --git a/src/azure-cli/azure/cli/command_modules/acr/tests/latest/test_regional_endpoint_utils.py b/src/azure-cli/azure/cli/command_modules/acr/tests/latest/test_regional_endpoint_utils.py index 28cb215bb9e..a971da8145e 100644 --- a/src/azure-cli/azure/cli/command_modules/acr/tests/latest/test_regional_endpoint_utils.py +++ b/src/azure-cli/azure/cli/command_modules/acr/tests/latest/test_regional_endpoint_utils.py @@ -30,6 +30,22 @@ def test_valid_regional_endpoint_conversion(self): result = acr_import._regional_endpoint_uri_to_login_server(regional_uri, login_server_suffix) self.assertEqual(result, expected) + def test_valid_regional_endpoint_conversion_multi_label_suffix(self): + """Regional endpoints in sovereign clouds whose login-server suffix has more than two + labels (e.g. '.azurecr.sovcloud-azure.de') must still be converted.""" + test_cases = [ + ('registry123.deloscloudgermanycentral.geo.azurecr.sovcloud-azure.de', + '.azurecr.sovcloud-azure.de', 'registry123.azurecr.sovcloud-azure.de'), + ('myregistry.francecentral.geo.azurecr.sovcloud-azure.fr', + '.azurecr.sovcloud-azure.fr', 'myregistry.azurecr.sovcloud-azure.fr'), + # DNL registry (hash suffix) in a multi-label sovereign cloud + ('myregistry-d7ezgzevdwfvc8ht.deloscloudgermanycentral.geo.azurecr.sovcloud-azure.de', + '.azurecr.sovcloud-azure.de', 'myregistry-d7ezgzevdwfvc8ht.azurecr.sovcloud-azure.de'), + ] + + for regional_uri, suffix, expected in test_cases: + result = acr_import._regional_endpoint_uri_to_login_server(regional_uri, suffix) + self.assertEqual(result, expected) def test_non_regional_endpoint_uris_unchanged(self): """Test that non-regional endpoint URIs are returned unchanged.""" @@ -40,6 +56,10 @@ def test_non_regional_endpoint_uris_unchanged(self): 'testregistry.azurecr.io', 'external-registry.com', 'testregistry.eastus.notgeo.azurecr.io', + # Malformed: empty region label must NOT be converted + 'testregistry..geo.azurecr.io', + 'testregistry.azurecr.sovcloud-azure.de', + 'testregistry.deloscloudgermanycentral.notgeo.azurecr.sovcloud-azure.de' ] for uri in test_cases: