From f8b707e042e2cdd4685f7be4e9c6efdce277daa5 Mon Sep 17 00:00:00 2001 From: Dongjiang You Date: Fri, 17 Aug 2018 13:10:53 -0700 Subject: [PATCH] Provide a workaround for runtime operations without ARM requests --- src/command_modules/azure-cli-acr/HISTORY.rst | 4 ++ .../cli/command_modules/acr/_docker_utils.py | 47 +++++++++++++++++-- .../azure/cli/command_modules/acr/custom.py | 6 ++- .../cli/command_modules/acr/repository.py | 27 ++--------- src/command_modules/azure-cli-acr/setup.py | 2 +- 5 files changed, 55 insertions(+), 31 deletions(-) diff --git a/src/command_modules/azure-cli-acr/HISTORY.rst b/src/command_modules/azure-cli-acr/HISTORY.rst index 40616d69b7a..cef7a291226 100644 --- a/src/command_modules/azure-cli-acr/HISTORY.rst +++ b/src/command_modules/azure-cli-acr/HISTORY.rst @@ -3,6 +3,10 @@ Release History =============== +2.1.4 ++++++ +* Provide a workaround for runtime operations without ARM requests. + 2.1.3 +++++ * Add content-trust policy commands. diff --git a/src/command_modules/azure-cli-acr/azure/cli/command_modules/acr/_docker_utils.py b/src/command_modules/azure-cli-acr/azure/cli/command_modules/acr/_docker_utils.py index 0ca93e115a4..85f1b711375 100644 --- a/src/command_modules/azure-cli-acr/azure/cli/command_modules/acr/_docker_utils.py +++ b/src/command_modules/azure-cli-acr/azure/cli/command_modules/acr/_docker_utils.py @@ -10,7 +10,9 @@ from urlparse import urlparse, urlunparse from json import loads +from base64 import b64encode import requests +from requests.utils import to_native_string from msrest.http_logger import log_request, log_response from knack.util import CLIError @@ -18,6 +20,7 @@ from knack.log import get_logger from azure.cli.core.util import should_disable_connection_verify +from azure.cli.core.cloud import CloudSuffixNotSetException from ._client_factory import cf_acr_registries from ._constants import MANAGED_REGISTRY_SKU @@ -118,11 +121,18 @@ def _get_credentials(cli_ctx, :param str repository: Repository for which the access token is requested :param str permission: The requested permission on the repository, '*' or 'pull' """ - registry, resource_group_name = get_registry_by_name(cli_ctx, registry_name, resource_group_name) - login_server = registry.login_server - # 1. if username was specified, verify that password was also specified if username: + # Try to use the pre-defined login server suffix to construct login server from registry name. + # This is to avoid a management server request if username/password are already provided. + # In all other cases, including the suffix not defined, login server will be obtained from server. + login_server_suffix = get_login_server_suffix(cli_ctx) + if login_server_suffix: + login_server = '{}{}'.format(registry_name, login_server_suffix) + else: + registry, _ = get_registry_by_name(cli_ctx, registry_name, resource_group_name) + login_server = registry.login_server + if not password: try: password = prompt_pass(msg='Password: ') @@ -131,6 +141,9 @@ def _get_credentials(cli_ctx, return login_server, username, password + registry, resource_group_name = get_registry_by_name(cli_ctx, registry_name, resource_group_name) + login_server = registry.login_server + # 2. if we don't yet have credentials, attempt to get a refresh token if not password and registry.sku.name in MANAGED_REGISTRY_SKU: try: @@ -222,4 +235,30 @@ def log_registry_response(response): def get_login_server_suffix(cli_ctx): """Get the Azure Container Registry login server suffix in the current cloud.""" - return cli_ctx.cloud.suffixes.acr_login_server_endpoint + try: + return cli_ctx.cloud.suffixes.acr_login_server_endpoint + except CloudSuffixNotSetException: + # Ignore the error if the suffix is not set, the caller should then try to get login server from server. + return None + + +def _get_basic_auth_str(username, password): + return 'Basic ' + to_native_string( + b64encode(('%s:%s' % (username, password)).encode('latin1')).strip() + ) + + +def _get_bearer_auth_str(token): + return 'Bearer ' + token + + +def get_authorization_header(username, password): + """Get the authorization header as Basic auth if username is provided, or Bearer auth otherwise + :param str username: The username used to log into the container registry + :param str password: The password used to log into the container registry + """ + if username: + auth = _get_basic_auth_str(username, password) + else: + auth = _get_bearer_auth_str(password) + return {'Authorization': auth} diff --git a/src/command_modules/azure-cli-acr/azure/cli/command_modules/acr/custom.py b/src/command_modules/azure-cli-acr/azure/cli/command_modules/acr/custom.py index 1999bd8e123..ab7f6705256 100644 --- a/src/command_modules/azure-cli-acr/azure/cli/command_modules/acr/custom.py +++ b/src/command_modules/azure-cli-acr/azure/cli/command_modules/acr/custom.py @@ -183,9 +183,11 @@ def acr_login(cmd, registry_name, resource_group_name=None, username=None, passw "--password", password, login_server]) p.wait() - elif b'--password-stdin' in stderr: - pass else: + if b'--password-stdin' in stderr: + errors = [err for err in stderr.decode().split('\n') if '--password-stdin' not in err] + stderr = '\n'.join(errors).encode() + import sys output = getattr(sys.stderr, 'buffer', sys.stderr) output.write(stderr) diff --git a/src/command_modules/azure-cli-acr/azure/cli/command_modules/acr/repository.py b/src/command_modules/azure-cli-acr/azure/cli/command_modules/acr/repository.py index 02f22f440f9..0e1d4482311 100644 --- a/src/command_modules/azure-cli-acr/azure/cli/command_modules/acr/repository.py +++ b/src/command_modules/azure-cli-acr/azure/cli/command_modules/acr/repository.py @@ -4,9 +4,7 @@ # -------------------------------------------------------------------------------------------- import time -from base64 import b64encode import requests -from requests.utils import to_native_string try: from urllib.parse import unquote except ImportError: @@ -19,7 +17,7 @@ from azure.cli.core.util import should_disable_connection_verify from ._utils import validate_managed_registry -from ._docker_utils import get_access_credentials, log_registry_response +from ._docker_utils import get_access_credentials, get_authorization_header, log_registry_response logger = get_logger(__name__) @@ -43,25 +41,6 @@ DEFAULT_PAGINATION = 20 -def _get_basic_auth_str(username, password): - return 'Basic ' + to_native_string( - b64encode(('%s:%s' % (username, password)).encode('latin1')).strip() - ) - - -def _get_bearer_auth_str(token): - return 'Bearer ' + token - - -def _get_authorization_header(username, password): - if username is None: - auth = _get_bearer_auth_str(password) - else: - auth = _get_basic_auth_str(username, password) - - return {'Authorization': auth} - - def _parse_error_message(error_message, response): import json try: @@ -100,7 +79,7 @@ def _request_data_from_registry(http_method, raise ValueError("Non-empty json payload is required for http method: {}".format(http_method)) url = 'https://{}{}'.format(login_server, path) - headers = _get_authorization_header(username, password) + headers = get_authorization_header(username, password) for i in range(0, retry_times): errorMessage = None @@ -146,7 +125,7 @@ def _request_data_from_registry(http_method, def _get_manifest_digest(login_server, repository, tag, username, password, retry_times=3, retry_interval=5): url = 'https://{}/v2/{}/manifests/{}'.format(login_server, repository, tag) - headers = _get_authorization_header(username, password) + headers = get_authorization_header(username, password) headers.update(MANIFEST_V2_HEADER) for i in range(0, retry_times): diff --git a/src/command_modules/azure-cli-acr/setup.py b/src/command_modules/azure-cli-acr/setup.py index f849105d5d5..8675abfd5ab 100644 --- a/src/command_modules/azure-cli-acr/setup.py +++ b/src/command_modules/azure-cli-acr/setup.py @@ -14,7 +14,7 @@ logger.warn("Wheel is not available, disabling bdist_wheel hook") cmdclass = {} -VERSION = "2.1.3" +VERSION = "2.1.4" CLASSIFIERS = [ 'Development Status :: 4 - Beta', 'Intended Audience :: Developers',