Hello community,

here is the log from the commit of package azure-cli-core for openSUSE:Factory 
checked in at 2020-12-04 21:28:23
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/azure-cli-core (Old)
 and      /work/SRC/openSUSE:Factory/.azure-cli-core.new.5913 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "azure-cli-core"

Fri Dec  4 21:28:23 2020 rev:21 rq:851831 version:2.15.0

Changes:
--------
--- /work/SRC/openSUSE:Factory/azure-cli-core/azure-cli-core.changes    
2020-11-17 21:25:46.157406606 +0100
+++ /work/SRC/openSUSE:Factory/.azure-cli-core.new.5913/azure-cli-core.changes  
2020-12-04 21:28:24.526118011 +0100
@@ -1,0 +2,9 @@
+Wed Nov 18 11:55:02 UTC 2020 - John Paul Adrian Glaubitz 
<adrian.glaub...@suse.com>
+
+- New upstream release
+  + Version 2.15.0
+  + For detailed information about changes see the
+    HISTORY.rst file provided with this package
+- Update Requires from setup.py
+
+-------------------------------------------------------------------

Old:
----
  azure-cli-core-2.14.2.tar.gz

New:
----
  azure-cli-core-2.15.0.tar.gz

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Other differences:
------------------
++++++ azure-cli-core.spec ++++++
--- /var/tmp/diff_new_pack.IHpf33/_old  2020-12-04 21:28:24.974118654 +0100
+++ /var/tmp/diff_new_pack.IHpf33/_new  2020-12-04 21:28:24.974118654 +0100
@@ -17,7 +17,7 @@
 
 
 Name:           azure-cli-core
-Version:        2.14.2
+Version:        2.15.0
 Release:        0
 Summary:        Microsoft Azure CLI Core Module
 License:        MIT
@@ -42,7 +42,7 @@
 Requires:       python3-argcomplete < 2.0
 Requires:       python3-argcomplete >= 1.8
 Requires:       python3-azure-mgmt-core < 2.0.0
-Requires:       python3-azure-mgmt-core >= 1.2.0
+Requires:       python3-azure-mgmt-core >= 1.2.1
 Requires:       python3-azure-mgmt-resource < 16.0.0
 Requires:       python3-azure-mgmt-resource >= 15.0.0
 Requires:       python3-azure-nspkg >= 3.0.0

++++++ azure-cli-core-2.14.2.tar.gz -> azure-cli-core-2.15.0.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/azure-cli-core-2.14.2/HISTORY.rst 
new/azure-cli-core-2.15.0/HISTORY.rst
--- old/azure-cli-core-2.14.2/HISTORY.rst       2020-11-09 06:56:54.000000000 
+0100
+++ new/azure-cli-core-2.15.0/HISTORY.rst       2020-11-13 09:42:05.000000000 
+0100
@@ -3,6 +3,10 @@
 Release History
 ===============
 
+2.15.0
+++++++
+* Upgrade azure-mgmt-core to 1.2.1 (#15780)
+
 2.14.2
 ++++++
 * No changes
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/azure-cli-core-2.14.2/PKG-INFO 
new/azure-cli-core-2.15.0/PKG-INFO
--- old/azure-cli-core-2.14.2/PKG-INFO  2020-11-09 06:57:09.000000000 +0100
+++ new/azure-cli-core-2.15.0/PKG-INFO  2020-11-13 09:42:18.000000000 +0100
@@ -1,6 +1,6 @@
 Metadata-Version: 2.1
 Name: azure-cli-core
-Version: 2.14.2
+Version: 2.15.0
 Summary: Microsoft Azure Command-Line Tools Core Module
 Home-page: https://github.com/Azure/azure-cli
 Author: Microsoft Corporation
@@ -15,6 +15,10 @@
         Release History
         ===============
         
+        2.15.0
+        ++++++
+        * Upgrade azure-mgmt-core to 1.2.1 (#15780)
+        
         2.14.2
         ++++++
         * No changes
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/azure-cli-core-2.14.2/azure/cli/core/__init__.py 
new/azure-cli-core-2.15.0/azure/cli/core/__init__.py
--- old/azure-cli-core-2.14.2/azure/cli/core/__init__.py        2020-11-09 
06:56:54.000000000 +0100
+++ new/azure-cli-core-2.15.0/azure/cli/core/__init__.py        2020-11-13 
09:42:05.000000000 +0100
@@ -6,7 +6,7 @@
 
 from __future__ import print_function
 
-__version__ = "2.14.2"
+__version__ = "2.15.0"
 
 import os
 import sys
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/azure-cli-core-2.14.2/azure/cli/core/_profile.py 
new/azure-cli-core-2.15.0/azure/cli/core/_profile.py
--- old/azure-cli-core-2.14.2/azure/cli/core/_profile.py        2020-11-09 
06:56:54.000000000 +0100
+++ new/azure-cli-core-2.15.0/azure/cli/core/_profile.py        2020-11-13 
09:42:05.000000000 +0100
@@ -24,7 +24,6 @@
     is_windows, is_wsl
 from azure.cli.core.cloud import get_active_cloud, set_cloud_subscription
 
-
 logger = get_logger(__name__)
 
 # Names below are used by azure-xplat-cli to persist account information into
@@ -273,6 +272,15 @@
                 if hasattr(s, 'home_tenant_id'):
                     subscription_dict[_HOME_TENANT_ID] = s.home_tenant_id
                 if hasattr(s, 'managed_by_tenants'):
+                    if s.managed_by_tenants is None:
+                        # managedByTenants is missing from the response. This 
is a known service issue:
+                        # 
https://github.com/Azure/azure-rest-api-specs/issues/9567
+                        # pylint: disable=line-too-long
+                        raise CLIError("Invalid profile is used for cloud 
'{cloud_name}'. "
+                                       "To configure the cloud profile, run 
`az cloud set --name {cloud_name} --profile <profile>(e.g. 2019-03-01-hybrid)`. 
"
+                                       "For more information about using Azure 
CLI with Azure Stack, see "
+                                       
"https://docs.microsoft.com/azure-stack/user/azure-stack-version-profiles-azurecli2";
+                                       
.format(cloud_name=self.cli_ctx.cloud.name))
                     subscription_dict[_MANAGED_BY_TENANTS] = [{_TENANT_ID: 
t.tenant_id} for t in s.managed_by_tenants]
 
             consolidated.append(subscription_dict)
@@ -559,26 +567,35 @@
                     external_tenants_info.append(sub[_TENANT_ID])
 
         if identity_type is None:
-            def _retrieve_token():
+            def _retrieve_token(sdk_resource=None):
+                # When called by
+                #   - Track 1 SDK, use `resource` specified by CLI
+                #   - Track 2 SDK, use `sdk_resource` specified by SDK and 
ignore `resource` specified by CLI
+                token_resource = sdk_resource or resource
+                logger.debug("Retrieving token from ADAL for resource %r", 
token_resource)
+
                 if in_cloud_console() and 
account[_USER_ENTITY].get(_CLOUD_SHELL_ID):
-                    return self._get_token_from_cloud_shell(resource)
+                    return self._get_token_from_cloud_shell(token_resource)
                 if user_type == _USER:
                     return 
self._creds_cache.retrieve_token_for_user(username_or_sp_id,
-                                                                     
account[_TENANT_ID], resource)
+                                                                     
account[_TENANT_ID], token_resource)
                 use_cert_sn_issuer = 
account[_USER_ENTITY].get(_SERVICE_PRINCIPAL_CERT_SN_ISSUER_AUTH)
-                return 
self._creds_cache.retrieve_token_for_service_principal(username_or_sp_id, 
resource,
+                return 
self._creds_cache.retrieve_token_for_service_principal(username_or_sp_id, 
token_resource,
                                                                               
account[_TENANT_ID],
                                                                               
use_cert_sn_issuer)
 
-            def _retrieve_tokens_from_external_tenants():
+            def _retrieve_tokens_from_external_tenants(sdk_resource=None):
+                token_resource = sdk_resource or resource
+                logger.debug("Retrieving token from ADAL for external tenants 
and resource %r", token_resource)
+
                 external_tokens = []
                 for sub_tenant_id in external_tenants_info:
                     if user_type == _USER:
                         
external_tokens.append(self._creds_cache.retrieve_token_for_user(
-                            username_or_sp_id, sub_tenant_id, resource))
+                            username_or_sp_id, sub_tenant_id, token_resource))
                     else:
                         
external_tokens.append(self._creds_cache.retrieve_token_for_service_principal(
-                            username_or_sp_id, resource, sub_tenant_id, 
resource))
+                            username_or_sp_id, token_resource, sub_tenant_id, 
token_resource))
                 return external_tokens
 
             from azure.cli.core.adal_authentication import AdalAuthentication
@@ -621,6 +638,8 @@
         return username_or_sp_id, sp_secret, None, str(account[_TENANT_ID])
 
     def get_raw_token(self, resource=None, subscription=None, tenant=None):
+        logger.debug("Profile.get_raw_token invoked with resource=%r, 
subscription=%r, tenant=%r",
+                     resource, subscription, tenant)
         if subscription and tenant:
             raise CLIError("Please specify only one of subscription and 
tenant, not both")
         account = self.get_subscription(subscription)
@@ -805,7 +824,11 @@
             from azure.cli.core.profiles._shared import get_client_class
             from azure.cli.core.profiles import ResourceType, get_api_version
             from azure.cli.core.commands.client_factory import 
configure_common_settings
+            from azure.cli.core.azclierror import CLIInternalError
             client_type = 
get_client_class(ResourceType.MGMT_RESOURCE_SUBSCRIPTIONS)
+            if client_type is None:
+                raise CLIInternalError("Unable to get '{}' in profile '{}'"
+                                       
.format(ResourceType.MGMT_RESOURCE_SUBSCRIPTIONS, cli_ctx.cloud.profile))
             api_version = get_api_version(cli_ctx, 
ResourceType.MGMT_RESOURCE_SUBSCRIPTIONS)
             client = client_type(credentials, api_version=api_version,
                                  
base_url=self.cli_ctx.cloud.endpoints.resource_manager)
@@ -817,10 +840,13 @@
 
     def find_from_user_account(self, username, password, tenant, resource):
         context = self._create_auth_context(tenant)
-        if password:
-            token_entry = 
context.acquire_token_with_username_password(resource, username, password, 
_CLIENT_ID)
-        else:  # when refresh account, we will leverage local cached tokens
-            token_entry = context.acquire_token(resource, username, _CLIENT_ID)
+        try:
+            if password:
+                token_entry = 
context.acquire_token_with_username_password(resource, username, password, 
_CLIENT_ID)
+            else:  # when refresh account, we will leverage local cached tokens
+                token_entry = context.acquire_token(resource, username, 
_CLIENT_ID)
+        except Exception as err:  # pylint: disable=broad-except
+            _login_exception_handler(err)
 
         if not token_entry:
             return []
@@ -841,8 +867,11 @@
 
         # exchange the code for the token
         context = self._create_auth_context(tenant)
-        token_entry = 
context.acquire_token_with_authorization_code(results['code'], 
results['reply_url'],
-                                                                    resource, 
_CLIENT_ID, None)
+        try:
+            token_entry = 
context.acquire_token_with_authorization_code(results['code'], 
results['reply_url'],
+                                                                        
resource, _CLIENT_ID, None)
+        except Exception as err:  # pylint: disable=broad-except
+            _login_exception_handler(err)
         self.user_id = token_entry[_TOKEN_ENTRY_USER_ID]
         logger.warning("You have logged in. Now let us find all the 
subscriptions to which you have access...")
         if tenant is None:
@@ -853,7 +882,10 @@
 
     def find_through_interactive_flow(self, tenant, resource):
         context = self._create_auth_context(tenant)
-        code = context.acquire_user_code(resource, _CLIENT_ID)
+        try:
+            code = context.acquire_user_code(resource, _CLIENT_ID)
+        except Exception as err:  # pylint: disable=broad-except
+            _login_exception_handler(err)
         logger.warning(code['message'])
         token_entry = context.acquire_token_with_device_code(resource, code, 
_CLIENT_ID)
         self.user_id = token_entry[_TOKEN_ENTRY_USER_ID]
@@ -1261,6 +1293,12 @@
         results['no_browser'] = True
         return
 
+    # Emit a warning to inform that a browser is opened.
+    # Only show the path part of the URL and hide the query string.
+    logger.warning("The default web browser has been opened at %s. Please 
continue the login in the web browser. "
+                   "If no web browser is available or if the web browser fails 
to open, use device code flow "
+                   "with `az login --use-device-code`.", url.split('?')[0])
+
     # wait for callback from browser.
     while True:
         web_server.handle_request()
@@ -1305,3 +1343,13 @@
     if results.get('no_browser'):
         raise RuntimeError()
     return results
+
+
+def _login_exception_handler(ex):
+    from requests.exceptions import InvalidURL
+    if isinstance(ex, InvalidURL):
+        import traceback
+        from azure.cli.core.azclierror import UnclassifiedUserFault
+        logger.debug('Invalid url when acquiring token\n%s', 
traceback.format_exc())
+        raise UnclassifiedUserFault(error_msg='Invalid url when acquiring 
token',
+                                    recommendation='Please make sure the cloud 
is registered with valid url')
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/azure-cli-core-2.14.2/azure/cli/core/adal_authentication.py 
new/azure-cli-core-2.15.0/azure/cli/core/adal_authentication.py
--- old/azure-cli-core-2.14.2/azure/cli/core/adal_authentication.py     
2020-11-09 06:56:54.000000000 +0100
+++ new/azure-cli-core-2.15.0/azure/cli/core/adal_authentication.py     
2020-11-13 09:42:05.000000000 +0100
@@ -3,6 +3,7 @@
 # Licensed under the MIT License. See License.txt in the project root for 
license information.
 # 
--------------------------------------------------------------------------------------------
 
+import datetime
 import time
 import requests
 import adal
@@ -10,23 +11,35 @@
 from msrest.authentication import Authentication
 from msrestazure.azure_active_directory import MSIAuthentication
 from azure.core.credentials import AccessToken
-from azure.cli.core.util import in_cloud_console
+from azure.cli.core.util import in_cloud_console, scopes_to_resource
 
 from knack.util import CLIError
+from knack.log import get_logger
+
+logger = get_logger(__name__)
 
 
 class AdalAuthentication(Authentication):  # pylint: 
disable=too-few-public-methods
 
     def __init__(self, token_retriever, external_tenant_token_retriever=None):
+        # DO NOT call _token_retriever from outside azure-cli-core. It is only 
available for user or
+        # Service Principal credential (AdalAuthentication), but not for 
Managed Identity credential
+        # (MSIAuthenticationWrapper).
+        # To retrieve a raw token, either call
+        #   - Profile.get_raw_token, which is more direct
+        #   - AdalAuthentication.get_token, which is designed for Track 2 SDKs
         self._token_retriever = token_retriever
         self._external_tenant_token_retriever = external_tenant_token_retriever
 
-    def _get_token(self):
+    def _get_token(self, sdk_resource=None):
+        """
+        :param sdk_resource: `resource` converted from Track 2 SDK's `scopes`
+        """
         external_tenant_tokens = None
         try:
-            scheme, token, full_token = self._token_retriever()
+            scheme, token, full_token = self._token_retriever(sdk_resource)
             if self._external_tenant_token_retriever:
-                external_tenant_tokens = 
self._external_tenant_token_retriever()
+                external_tenant_tokens = 
self._external_tenant_token_retriever(sdk_resource)
         except CLIError as err:
             if in_cloud_console():
                 AdalAuthentication._log_hostname()
@@ -60,7 +73,16 @@
 
     # This method is exposed for Azure Core.
     def get_token(self, *scopes, **kwargs):  # pylint:disable=unused-argument
-        _, token, full_token, _ = self._get_token()
+        logger.debug("AdalAuthentication.get_token invoked by Track 2 SDK with 
scopes=%s", scopes)
+
+        _, token, full_token, _ = 
self._get_token(_try_scopes_to_resource(scopes))
+
+        try:
+            expires_on = full_token['expiresOn']
+            return AccessToken(token, 
int(datetime.datetime.strptime(expires_on, '%Y-%m-%d %H:%M:%S.%f').timestamp()))
+        except:  # pylint: disable=bare-except
+            pass  # To avoid crashes due to some unexpected token formats
+
         try:
             return AccessToken(token, int(full_token['expiresIn'] + 
time.time()))
         except KeyError:  # needed to deal with differing unserialized MSI 
token payload
@@ -68,6 +90,7 @@
 
     # This method is exposed for msrest.
     def signed_session(self, session=None):  # pylint: disable=arguments-differ
+        logger.debug("AdalAuthentication.signed_session invoked by Track 1 
SDK")
         session = session or super(AdalAuthentication, self).signed_session()
 
         scheme, token, _, external_tenant_tokens = self._get_token()
@@ -82,8 +105,6 @@
     @staticmethod
     def _log_hostname():
         import socket
-        from knack.log import get_logger
-        logger = get_logger(__name__)
         logger.warning("A Cloud Shell credential problem occurred. When you 
report the issue with the error "
                        "below, please mention the hostname '%s'", 
socket.gethostname())
 
@@ -91,13 +112,16 @@
 class MSIAuthenticationWrapper(MSIAuthentication):
     # This method is exposed for Azure Core. Add *scopes, **kwargs to fit 
azure.core requirement
     def get_token(self, *scopes, **kwargs):  # pylint:disable=unused-argument
+        logger.debug("MSIAuthenticationWrapper.get_token invoked by Track 2 
SDK with scopes=%s", scopes)
+        resource = _try_scopes_to_resource(scopes)
+        if resource:
+            # If available, use resource provided by SDK
+            self.resource = resource
         self.set_token()
         return AccessToken(self.token['access_token'], 
int(self.token['expires_on']))
 
     def set_token(self):
         import traceback
-        from knack.log import get_logger
-        logger = get_logger(__name__)
         from azure.cli.core.azclierror import AzureConnectionError, 
AzureResponseError
         try:
             super(MSIAuthenticationWrapper, self).set_token()
@@ -109,11 +133,41 @@
         except requests.exceptions.HTTPError as err:
             logger.debug('throw requests.exceptions.HTTPError when doing 
MSIAuthentication: \n%s',
                          traceback.format_exc())
-            raise AzureResponseError('Failed to connect to MSI. Please make 
sure MSI is configured correctly.\n'
-                                     'Get Token request returned http error: 
{}, reason: {}'
-                                     .format(err.response.status, 
err.response.reason))
+            try:
+                raise AzureResponseError('Failed to connect to MSI. Please 
make sure MSI is configured correctly.\n'
+                                         'Get Token request returned http 
error: {}, reason: {}'
+                                         .format(err.response.status, 
err.response.reason))
+            except AttributeError:
+                raise AzureResponseError('Failed to connect to MSI. Please 
make sure MSI is configured correctly.\n'
+                                         'Get Token request returned: 
{}'.format(err.response))
         except TimeoutError as err:
             logger.debug('throw TimeoutError when doing MSIAuthentication: 
\n%s',
                          traceback.format_exc())
             raise AzureConnectionError('MSI endpoint is not responding. Please 
make sure MSI is configured correctly.\n'
                                        'Error detail: {}'.format(str(err)))
+
+    def signed_session(self, session=None):
+        logger.debug("MSIAuthenticationWrapper.signed_session invoked by Track 
1 SDK")
+        super().signed_session(session)
+
+
+def _try_scopes_to_resource(scopes):
+    """Wrap scopes_to_resource to workaround some SDK issues."""
+
+    # Track 2 SDKs generated before 
https://github.com/Azure/autorest.python/pull/239 don't maintain
+    # credential_scopes and call `get_token` with empty scopes.
+    # As a workaround, return None so that the CLI-managed resource is used.
+    if not scopes:
+        logger.debug("No scope is provided by the SDK, use the CLI-managed 
resource.")
+        return None
+
+    # Track 2 SDKs generated before 
https://github.com/Azure/autorest.python/pull/745 extend default
+    # credential_scopes with custom credential_scopes. Instead, 
credential_scopes should be replaced by
+    # custom credential_scopes. 
https://github.com/Azure/azure-sdk-for-python/issues/12947
+    # As a workaround, remove the first one if there are multiple scopes 
provided.
+    if len(scopes) > 1:
+        logger.debug("Multiple scopes are provided by the SDK, discarding the 
first one: %s", scopes[0])
+        return scopes_to_resource(scopes[1:])
+
+    # Exactly only one scope is provided
+    return scopes_to_resource(scopes)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/azure-cli-core-2.14.2/azure/cli/core/azclierror.py 
new/azure-cli-core-2.15.0/azure/cli/core/azclierror.py
--- old/azure-cli-core-2.14.2/azure/cli/core/azclierror.py      2020-11-09 
06:56:54.000000000 +0100
+++ new/azure-cli-core-2.15.0/azure/cli/core/azclierror.py      2020-11-13 
09:42:05.000000000 +0100
@@ -15,15 +15,15 @@
 
 # Error types in AzureCLI are from different sources, and there are many 
general error types like CLIError, AzureError.
 # Besides, many error types with different names are actually showing the same 
kind of error.
-# For example, CloudError, CLIError and ValidtionError all could be a 
resource-not-found error.
+# For example, CloudError, CLIError and ValidationError all could be a 
resource-not-found error.
 # Therefore, here we define the new error classes to map and categorize all of 
the error types from different sources.
 
 
 # region: Base Layer
 # Base class for all the AzureCLI defined error classes.
-# DO NOT raise the error class here directly in your codes.
 class AzCLIError(CLIError):
-    """ Base class for all the AzureCLI defined error classes. """
+    """ Base class for all the AzureCLI defined error classes.
+    DO NOT raise this error class in your codes. """
 
     def __init__(self, error_msg, recommendation=None):
         # error message
@@ -32,17 +32,17 @@
         # set recommendations to fix the error if the message is not 
actionable,
         # they will be printed to users after the error message, one 
recommendation per line
         self.recommendations = []
-        if isinstance(recommendation, str):
-            self.recommendations = [recommendation]
-        elif isinstance(recommendation, list):
-            self.recommendations = recommendation
+        self.set_recommendation(recommendation)
 
         # exception trace for the error
         self.exception_trace = None
         super().__init__(error_msg)
 
     def set_recommendation(self, recommendation):
-        self.recommendations.append(recommendation)
+        if isinstance(recommendation, str):
+            self.recommendations.append(recommendation)
+        elif isinstance(recommendation, list):
+            self.recommendations.extend(recommendation)
 
     def set_exception_trace(self, exception_trace):
         self.exception_trace = exception_trace
@@ -68,35 +68,44 @@
 
 # region: Second Layer
 # Main categories of the AzureCLI error types, used for Telemetry analysis
-# DO NOT raise the error classes here directly in your codes.
 class UserFault(AzCLIError):
-    """ Users should be responsible for the errors. """
+    """ Users should be responsible for the errors.
+    DO NOT raise this error class in your codes. """
     def send_telemetry(self):
         super().send_telemetry()
         telemetry.set_user_fault(self.error_msg)
 
 
 class ServiceError(AzCLIError):
-    """ Azure Services should be responsible for the errors.  """
+    """ Azure Services should be responsible for the errors.
+    DO NOT raise this error class in your codes. """
     def send_telemetry(self):
         super().send_telemetry()
         telemetry.set_failure(self.error_msg)
 
 
 class ClientError(AzCLIError):
-    """ AzureCLI should be responsible for the errors. """
+    """ AzureCLI should be responsible for the errors.
+    DO NOT raise this error class in your codes. """
     def send_telemetry(self):
         super().send_telemetry()
         telemetry.set_failure(self.error_msg)
         if self.exception_trace:
             telemetry.set_exception(self.exception_trace, '')
+
+
+class UnknownError(AzCLIError):
+    """ Unclear errors, could not know who should be responsible for the 
errors.
+    DO NOT raise this error class in your codes. """
+    def send_telemetry(self):
+        super().send_telemetry()
+        telemetry.set_failure(self.error_msg)
 # endregion
 
 
 # region: Third Layer
-# Sub-categories of the AzureCLI error types, shown to users
-# Raise the error classes here in your codes
-# Avoid using fallback error classes unless you can not find a proper one
+# Specific categories of the AzureCLI error types
+# Raise the error classes here in your codes. Avoid using fallback error 
classes unless you can not find a proper one.
 # Command related error types
 class CommandNotFoundError(UserFault):
     """ Command is misspelled or not recognized by AzureCLI. """
@@ -115,7 +124,7 @@
 
 
 class MutuallyExclusiveArgumentError(UserFault):
-    """ Arguments can not be specfied together. """
+    """ Arguments can not be specified together. """
     pass
 
 
@@ -124,10 +133,10 @@
     pass
 
 
-class ArgumentParseError(UserFault):
-    """ Fallback of the argument parsing related errors.
+class ArgumentUsageError(UserFault):
+    """ Fallback of the argument usage related errors.
     Avoid using this class unless the error can not be classified
-    into the above Argument related error types. """
+    into the Argument related specific error types. """
     pass
 
 
@@ -139,10 +148,12 @@
 
 class UnauthorizedError(UserFault):
     """ Unauthorized request: 401 error """
+    pass
 
 
 class ForbiddenError(UserFault):
     """ Service refuse to response: 403 error """
+    pass
 
 
 class ResourceNotFoundError(UserFault):
@@ -158,7 +169,8 @@
 class AzureResponseError(UserFault):
     """ Fallback of the response related errors.
     Avoid using this class unless the error can not be classified
-    into the above Response related error types. """
+    into the Response related specific error types. """
+    pass
 
 
 # Request related error types
@@ -171,20 +183,13 @@
     """ Fallback of the request related errors. Error occurs while attempting
     to make a request to the service. No request is sent.
     Avoid using this class unless the error can not be classified
-    into the above Request related errors types. """
-
-
-# Validation related error types
-class ValidationError(UserFault):
-    """ Fallback of the errors in validation functions.
-    Avoid using this class unless the error can not be classified into
-    the Argument, Request and Response related error types above. """
+    into the Request related specific errors types. """
     pass
 
 
-# CLI internal error type
-class CLIInternalError(ClientError):
-    """ AzureCLI internal error """
+# File operation related error types
+class FileOperationError(UserFault):
+    """ For file or directory operation related errors. """
     pass
 
 
@@ -194,11 +199,30 @@
     pass
 
 
-# Unknow error type
-class UnknownError(UserFault):
-    """ Reserved for the errors which can not be categorized into the error 
types above.
-    Usually for the very general error type like CLIError, AzureError.
-    Error type info will not printed to users for this class. """
+# ARM template related error types
+class InvalidTemplateError(UserFault):
+    """ ARM template validation fails. It could be caused by incorrect 
template files or parameters """
+    pass
+
+
+class DeploymentError(UserFault):
+    """ ARM template deployment fails. Template file is valid, and error 
occurs in deployment. """
+    pass
+
+
+# Validation related error types
+class ValidationError(UserFault):
+    """ Fallback of the errors in validation functions.
+    Avoid using this class unless the error can not be classified into
+    the Argument, Request and Response related specific error types. """
+    pass
+
+
+class UnclassifiedUserFault(UserFault):
+    """ Fallback of the UserFault related error types.
+    Avoid using this class unless the error can not be classified into
+    the UserFault related specific error types.
+    """
     def print_error(self):
         from azure.cli.core.azlogging import CommandLoggerContext
         with CommandLoggerContext(logger):
@@ -209,4 +233,10 @@
                 for recommendation in self.recommendations:
                     print(recommendation, file=sys.stderr)
 
+
+# CLI internal error type
+class CLIInternalError(ClientError):
+    """ AzureCLI internal error """
+    pass
+
 # endregion
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/azure-cli-core-2.14.2/azure/cli/core/cloud.py 
new/azure-cli-core-2.15.0/azure/cli/core/cloud.py
--- old/azure-cli-core-2.14.2/azure/cli/core/cloud.py   2020-11-09 
06:56:54.000000000 +0100
+++ new/azure-cli-core-2.15.0/azure/cli/core/cloud.py   2020-11-13 
09:42:05.000000000 +0100
@@ -346,7 +346,7 @@
         app_insights_resource_id='https://api.applicationinsights.azure.cn',
         log_analytics_resource_id='https://api.loganalytics.azure.cn',
         
app_insights_telemetry_channel_resource_id='https://dc.applicationinsights.azure.cn/v2/track',
-        synapse_analytics_resource_id='https://dev.azuresynapse.net',
+        synapse_analytics_resource_id='https://dev.azuresynapse.azure.cn',
         portal='https://portal.azure.cn'),
     suffixes=CloudSuffixes(
         storage_endpoint='core.chinacloudapi.cn',
@@ -377,6 +377,7 @@
         app_insights_resource_id='https://api.applicationinsights.us',
         log_analytics_resource_id='https://api.loganalytics.us',
         
app_insights_telemetry_channel_resource_id='https://dc.applicationinsights.us/v2/track',
+        
synapse_analytics_resource_id='https://dev.azuresynapse.usgovcloudapi.net',
         portal='https://portal.azure.us'),
     suffixes=CloudSuffixes(
         storage_endpoint='core.usgovcloudapi.net',
@@ -387,7 +388,8 @@
         mysql_server_endpoint='.mysql.database.usgovcloudapi.net',
         postgresql_server_endpoint='.postgres.database.usgovcloudapi.net',
         mariadb_server_endpoint='.mariadb.database.usgovcloudapi.net',
-        acr_login_server_endpoint='.azurecr.us'))
+        acr_login_server_endpoint='.azurecr.us',
+        synapse_analytics_endpoint='.dev.azuresynapse.usgovcloudapi.net'))
 
 AZURE_GERMAN_CLOUD = Cloud(
     'AzureGermanCloud',
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/azure-cli-core-2.14.2/azure/cli/core/commands/__init__.py 
new/azure-cli-core-2.15.0/azure/cli/core/commands/__init__.py
--- old/azure-cli-core-2.14.2/azure/cli/core/commands/__init__.py       
2020-11-09 06:56:54.000000000 +0100
+++ new/azure-cli-core-2.15.0/azure/cli/core/commands/__init__.py       
2020-11-13 09:42:05.000000000 +0100
@@ -706,8 +706,7 @@
             return event_data['result']
         except Exception as ex:  # pylint: disable=broad-except
             if cmd_copy.exception_handler:
-                cmd_copy.exception_handler(ex)
-                return CommandResultItem(None, exit_code=1, error=ex)
+                return cmd_copy.exception_handler(ex)
             six.reraise(*sys.exc_info())
 
     def _run_jobs_serially(self, jobs, ids):
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/azure-cli-core-2.14.2/azure/cli/core/commands/client_factory.py 
new/azure-cli-core-2.15.0/azure/cli/core/commands/client_factory.py
--- old/azure-cli-core-2.14.2/azure/cli/core/commands/client_factory.py 
2020-11-09 06:56:54.000000000 +0100
+++ new/azure-cli-core-2.15.0/azure/cli/core/commands/client_factory.py 
2020-11-13 09:42:05.000000000 +0100
@@ -110,12 +110,21 @@
     client.config.generate_client_request_id = 'x-ms-client-request-id' not in 
cli_ctx.data['headers']
 
 
-def configure_common_settings_track2(cli_ctx):
+def _prepare_client_kwargs_track2(cli_ctx):
+    """Prepare kwargs for Track 2 SDK client."""
     client_kwargs = {}
 
+    # Prepare connection_verify to change SSL verification behavior, used by 
ConnectionConfiguration
     client_kwargs.update(_debug.change_ssl_cert_verification_track2())
 
+    # Enable NetworkTraceLoggingPolicy which logs all headers (except 
Authorization) without being redacted
     client_kwargs['logging_enable'] = True
+
+    # Disable ARMHttpLoggingPolicy which logs only allowed headers
+    from azure.core.pipeline.policies import SansIOHTTPPolicy
+    client_kwargs['http_logging_policy'] = SansIOHTTPPolicy()
+
+    # Prepare User-Agent header, used by UserAgentPolicy
     client_kwargs['user_agent'] = get_az_user_agent()
 
     try:
@@ -125,13 +134,20 @@
     except KeyError:
         pass
 
+    # Prepare custom headers, used by HeadersPolicy
     headers = dict(cli_ctx.data['headers'])
+
+    # - Prepare CommandName header
     command_name_suffix = ';completer-request' if 
cli_ctx.data['completer_active'] else ''
     headers['CommandName'] = "{}{}".format(cli_ctx.data['command'], 
command_name_suffix)
+
+    # - Prepare ParameterSetName header
     if cli_ctx.data.get('safe_params'):
         headers['ParameterSetName'] = ' '.join(cli_ctx.data['safe_params'])
+
     client_kwargs['headers'] = headers
 
+    # Prepare x-ms-client-request-id header, used by RequestIdPolicy
     if 'x-ms-client-request-id' in cli_ctx.data['headers']:
         client_kwargs['request_id'] = 
cli_ctx.data['headers']['x-ms-client-request-id']
 
@@ -150,6 +166,7 @@
                              aux_tenants=None,
                              **kwargs):
     from azure.cli.core._profile import Profile
+    from azure.cli.core.util import resource_to_scopes
     logger.debug('Getting management service client client_type=%s', 
client_type.__name__)
     resource = resource or cli_ctx.cloud.endpoints.active_directory_resource_id
     profile = Profile(cli_ctx=cli_ctx)
@@ -168,7 +185,8 @@
         client_kwargs.update(kwargs)
 
     if is_track2(client_type):
-        client_kwargs.update(configure_common_settings_track2(cli_ctx))
+        client_kwargs.update(_prepare_client_kwargs_track2(cli_ctx))
+        client_kwargs['credential_scopes'] = resource_to_scopes(resource)
 
     if subscription_bound:
         client = client_type(cred, subscription_id, **client_kwargs)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/azure-cli-core-2.14.2/azure/cli/core/parser.py 
new/azure-cli-core-2.15.0/azure/cli/core/parser.py
--- old/azure-cli-core-2.14.2/azure/cli/core/parser.py  2020-11-09 
06:56:54.000000000 +0100
+++ new/azure-cli-core-2.15.0/azure/cli/core/parser.py  2020-11-13 
09:42:05.000000000 +0100
@@ -5,7 +5,6 @@
 
 from __future__ import print_function
 
-import sys
 import difflib
 
 import argparse
@@ -21,7 +20,7 @@
 from azure.cli.core.azclierror import UnrecognizedArgumentError
 from azure.cli.core.azclierror import RequiredArgumentMissingError
 from azure.cli.core.azclierror import InvalidArgumentValueError
-from azure.cli.core.azclierror import ArgumentParseError
+from azure.cli.core.azclierror import ArgumentUsageError
 from azure.cli.core.azclierror import CommandNotFoundError
 from azure.cli.core.azclierror import ValidationError
 
@@ -69,12 +68,6 @@
 class AzCliCommandParser(CLICommandParser):
     """ArgumentParser implementation specialized for the Azure CLI utility."""
 
-    @staticmethod
-    def recommendation_provider(version, command, parameters, extension):  # 
pylint: disable=unused-argument
-        logger.debug("recommendation_provider: version: %s, command: %s, 
parameters: %s, extension: %s",
-                     version, command, parameters, extension)
-        return []
-
     def __init__(self, cli_ctx=None, cli_help=None, **kwargs):
         self.command_source = kwargs.pop('_command_source', None)
         self._raw_arguments = None
@@ -172,7 +165,7 @@
         recommender.set_help_examples(self.get_examples(self.prog))
         recommendation = recommender.recommend_a_command()
 
-        az_error = ArgumentParseError(message)
+        az_error = ArgumentUsageError(message)
         if 'unrecognized arguments' in message:
             az_error = UnrecognizedArgumentError(message)
         elif 'arguments are required' in message:
@@ -188,11 +181,6 @@
             
az_error.set_recommendation(OVERVIEW_REFERENCE.format(command=self.prog))
         az_error.print_error()
         az_error.send_telemetry()
-
-        # For ai-did-you-mean-this
-        failure_recovery_recommendations = 
self._get_failure_recovery_recommendations()
-        self._suggestion_msg.extend(failure_recovery_recommendations)
-        self._print_suggestion_msg(sys.stderr)
         self.exit(2)
 
     def format_help(self):
@@ -289,25 +277,12 @@
 
         return command, parameters, extension
 
-    def _get_failure_recovery_recommendations(self, action=None, **kwargs):
-        # Gets failure recovery recommendations
-        from azure.cli.core import __version__ as core_version
-        failure_recovery_arguments = 
self._get_failure_recovery_arguments(action)
-        recommendations = 
AzCliCommandParser.recommendation_provider(core_version,
-                                                                     
*failure_recovery_arguments,
-                                                                     **kwargs)
-        return recommendations
-
     def _get_values(self, action, arg_strings):
         value = super(AzCliCommandParser, self)._get_values(action, 
arg_strings)
         if action.dest and isinstance(action.dest, str) and not 
action.dest.startswith('_'):
             self.specified_arguments.append(action.dest)
         return value
 
-    def _print_suggestion_msg(self, file=None):
-        if self._suggestion_msg:
-            print('\n'.join(self._suggestion_msg), file=file)
-
     def parse_known_args(self, args=None, namespace=None):
         # retrieve the raw argument list in case parsing known arguments fails.
         self._raw_arguments = args
@@ -482,7 +457,7 @@
             if candidates:
                 az_error.set_recommendation("Did you mean '{}' 
?".format(candidates[0]))
 
-            # recommand a command for user
+            # recommend a command for user
             recommender = CommandRecommender(*command_arguments, error_msg, 
cli_ctx)
             
recommender.set_help_examples(self.get_examples(command_name_inferred))
             recommended_command = recommender.recommend_a_command()
@@ -497,11 +472,8 @@
 
             
az_error.set_recommendation(OVERVIEW_REFERENCE.format(command=self.prog))
 
-            az_error.print_error()
-            az_error.send_telemetry()
-
             if not caused_by_extension_not_installed:
-                failure_recovery_recommendations = 
self._get_failure_recovery_recommendations(action)
-                self._suggestion_msg.extend(failure_recovery_recommendations)
-                self._print_suggestion_msg(sys.stderr)
+                az_error.print_error()
+                az_error.send_telemetry()
+
             self.exit(2)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/azure-cli-core-2.14.2/azure/cli/core/util.py 
new/azure-cli-core-2.15.0/azure/cli/core/util.py
--- old/azure-cli-core-2.14.2/azure/cli/core/util.py    2020-11-09 
06:56:54.000000000 +0100
+++ new/azure-cli-core-2.15.0/azure/cli/core/util.py    2020-11-13 
09:42:05.000000000 +0100
@@ -55,7 +55,7 @@
 
 def handle_exception(ex):  # pylint: disable=too-many-locals, 
too-many-statements, too-many-branches
     # For error code, follow guidelines at 
https://docs.python.org/2/library/sys.html#sys.exit,
-    from jmespath.exceptions import JMESPathTypeError
+    from jmespath.exceptions import JMESPathError
     from msrestazure.azure_exceptions import CloudError
     from msrest.exceptions import HttpOperationError, ValidationError, 
ClientRequestError
     from azure.cli.core.azlogging import CommandLoggerContext
@@ -76,7 +76,7 @@
         if isinstance(ex, azclierror.AzCLIError):
             az_error = ex
 
-        elif isinstance(ex, JMESPathTypeError):
+        elif isinstance(ex, JMESPathError):
             error_msg = "Invalid jmespath query supplied for `--query`: 
{}".format(error_msg)
             az_error = azclierror.InvalidArgumentValueError(error_msg)
             az_error.set_recommendation(QUERY_REFERENCE)
@@ -96,8 +96,8 @@
             az_error = azclierror.ValidationError(error_msg)
 
         elif isinstance(ex, CLIError):
-            # TODO: Fine-grained analysis here for Unknown error
-            az_error = azclierror.UnknownError(error_msg)
+            # TODO: Fine-grained analysis here
+            az_error = azclierror.UnclassifiedUserFault(error_msg)
 
         elif isinstance(ex, AzureError):
             if extract_common_error_message(ex):
@@ -1165,3 +1165,36 @@
             refresh_known_clouds()
     except Exception as ex:  # pylint: disable=broad-except
         logger.warning(ex)
+
+
+def resource_to_scopes(resource):
+    """Convert the ADAL resource ID to MSAL scopes by appending the /.default 
suffix and return a list.
+    For example:
+       'https://management.core.windows.net/' -> 
['https://management.core.windows.net//.default']
+       'https://managedhsm.azure.com' -> 
['https://managedhsm.azure.com/.default']
+
+    :param resource: The ADAL resource ID
+    :return: A list of scopes
+    """
+    # 
https://docs.microsoft.com/en-us/azure/active-directory/develop/v2-permissions-and-consent#trailing-slash-and-default
+    # We should not trim the trailing slash, like in 
https://management.azure.com/
+    # In other word, the trailing slash should be preserved and scope should 
be https://management.azure.com//.default
+    scope = resource + '/.default'
+    return [scope]
+
+
+def scopes_to_resource(scopes):
+    """Convert MSAL scopes to ADAL resource by stripping the /.default suffix 
and return a str.
+    For example:
+       ['https://management.core.windows.net//.default'] -> 
'https://management.core.windows.net/'
+       ['https://managedhsm.azure.com/.default'] -> 
'https://managedhsm.azure.com'
+
+    :param scopes: The MSAL scopes. It can be a list or tuple of string
+    :return: The ADAL resource
+    :rtype: str
+    """
+    scope = scopes[0]
+    if scope.endswith("/.default"):
+        scope = scope[:-len("/.default")]
+
+    return scope
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/azure-cli-core-2.14.2/azure_cli_core.egg-info/PKG-INFO 
new/azure-cli-core-2.15.0/azure_cli_core.egg-info/PKG-INFO
--- old/azure-cli-core-2.14.2/azure_cli_core.egg-info/PKG-INFO  2020-11-09 
06:57:09.000000000 +0100
+++ new/azure-cli-core-2.15.0/azure_cli_core.egg-info/PKG-INFO  2020-11-13 
09:42:18.000000000 +0100
@@ -1,6 +1,6 @@
 Metadata-Version: 2.1
 Name: azure-cli-core
-Version: 2.14.2
+Version: 2.15.0
 Summary: Microsoft Azure Command-Line Tools Core Module
 Home-page: https://github.com/Azure/azure-cli
 Author: Microsoft Corporation
@@ -15,6 +15,10 @@
         Release History
         ===============
         
+        2.15.0
+        ++++++
+        * Upgrade azure-mgmt-core to 1.2.1 (#15780)
+        
         2.14.2
         ++++++
         * No changes
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/azure-cli-core-2.14.2/azure_cli_core.egg-info/requires.txt 
new/azure-cli-core-2.15.0/azure_cli_core.egg-info/requires.txt
--- old/azure-cli-core-2.14.2/azure_cli_core.egg-info/requires.txt      
2020-11-09 06:57:09.000000000 +0100
+++ new/azure-cli-core-2.15.0/azure_cli_core.egg-info/requires.txt      
2020-11-13 09:42:18.000000000 +0100
@@ -16,7 +16,7 @@
 six~=1.12
 pkginfo>=1.5.0.1
 azure-mgmt-resource==10.2.0
-azure-mgmt-core==1.2.0
+azure-mgmt-core==1.2.1
 
 [:python_version<"3.0"]
 futures
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/azure-cli-core-2.14.2/setup.py 
new/azure-cli-core-2.15.0/setup.py
--- old/azure-cli-core-2.14.2/setup.py  2020-11-09 06:56:54.000000000 +0100
+++ new/azure-cli-core-2.15.0/setup.py  2020-11-13 09:42:05.000000000 +0100
@@ -9,7 +9,7 @@
 from codecs import open
 from setuptools import setup
 
-VERSION = "2.14.2"
+VERSION = "2.15.0"
 
 # If we have source, validate that our version numbers match
 # This should prevent uploading releases with mismatched versions.
@@ -61,7 +61,7 @@
     'six~=1.12',
     'pkginfo>=1.5.0.1',
     'azure-mgmt-resource==10.2.0',
-    'azure-mgmt-core==1.2.0'
+    'azure-mgmt-core==1.2.1'
 ]
 
 TESTS_REQUIRE = [
_______________________________________________
openSUSE Commits mailing list -- commit@lists.opensuse.org
To unsubscribe, email commit-le...@lists.opensuse.org
List Netiquette: https://en.opensuse.org/openSUSE:Mailing_list_netiquette
List Archives: 
https://lists.opensuse.org/archives/list/commit@lists.opensuse.org

Reply via email to