Browse Source

HUE-9205 [hive] Avoid stack trace when querying a table with missing permission

Romain 5 years ago
parent
commit
c415aef3e3

+ 29 - 8
apps/beeswax/src/beeswax/server/hive_server2_lib.py

@@ -740,10 +740,15 @@ class HiveServerClient(object):
         LOG.info('Retrying with a new session because for %s of %s' % (self.user, str(e)))
         LOG.info('Retrying with a new session because for %s of %s' % (self.user, str(e)))
 
 
     if self.has_close_sessions and self.max_number_of_sessions > 1 and \
     if self.has_close_sessions and self.max_number_of_sessions > 1 and \
-        Session.objects.get_n_sessions(self.user, n=self.max_number_of_sessions, application=self.query_server['server_name']).count() >= self.max_number_of_sessions:
+        Session.objects.get_n_sessions(
+            self.user,
+            n=self.max_number_of_sessions,
+            application=self.query_server['server_name']
+        ).count() >= self.max_number_of_sessions:
       raise Exception('Too many open sessions. Stop a running query before starting a new one')
       raise Exception('Too many open sessions. Stop a running query before starting a new one')
 
 
     session = self.open_session(self.user)
     session = self.open_session(self.user)
+
     return self._call_return_result_and_session(fn, req, status=status, session=session)
     return self._call_return_result_and_session(fn, req, status=status, session=session)
 
 
 
 
@@ -766,8 +771,8 @@ class HiveServerClient(object):
       if hasattr(res.status, 'errorMessage') and res.status.errorMessage:
       if hasattr(res.status, 'errorMessage') and res.status.errorMessage:
         message = res.status.errorMessage
         message = res.status.errorMessage
       else:
       else:
-        message = ''
-      raise QueryServerException(Exception('Bad status for request %s:\n%s' % (req, res)), message=message)
+        message = 'Bad status for request %s:\n%s' % (req, res)
+      raise QueryServerException(Exception(message))
     else:
     else:
       return (res, session)
       return (res, session)
 
 
@@ -860,7 +865,12 @@ class HiveServerClient(object):
       query = 'DESCRIBE FORMATTED `%s`.`%s`' % (database, table_name)
       query = 'DESCRIBE FORMATTED `%s`.`%s`' % (database, table_name)
 
 
     try:
     try:
-      desc_results, desc_schema, operation_handle, session = self.execute_statement(query, max_rows=10000, orientation=TFetchOrientation.FETCH_NEXT, session=session)
+      desc_results, desc_schema, operation_handle, session = self.execute_statement(
+          query,
+          max_rows=10000,
+          orientation=TFetchOrientation.FETCH_NEXT,
+          session=session
+      )
       self.close_operation(operation_handle)
       self.close_operation(operation_handle)
     except Exception as e:
     except Exception as e:
       ex_string = str(e)
       ex_string = str(e)
@@ -871,11 +881,22 @@ class HiveServerClient(object):
           query = 'DESCRIBE FORMATTED `%s` PARTITION(%s)' % (table_name, partition_spec)
           query = 'DESCRIBE FORMATTED `%s` PARTITION(%s)' % (table_name, partition_spec)
         else:
         else:
           query = 'DESCRIBE FORMATTED `%s`' % table_name
           query = 'DESCRIBE FORMATTED `%s`' % table_name
-        desc_results, desc_schema, operation_handle, session = self.execute_statement(query, max_rows=10000, orientation=TFetchOrientation.FETCH_NEXT, session=session)
+        desc_results, desc_schema, operation_handle, session = self.execute_statement(
+            query,
+            max_rows=10000,
+            orientation=TFetchOrientation.FETCH_NEXT,
+            session=session
+        )
         self.close_operation(operation_handle)
         self.close_operation(operation_handle)
-      elif 'not have privileges for DESCTABLE' in ex_string or 'AuthorizationException' in ex_string: # HUE-5608: No table permission but some column permissions
+      elif 'not have privileges for DESCTABLE' in ex_string \
+          or 'AuthorizationException' in ex_string:  # HUE-5608: No table permission but some column permissions
         query = 'DESCRIBE `%s`.`%s`' % (database, table_name)
         query = 'DESCRIBE `%s`.`%s`' % (database, table_name)
-        desc_results, desc_schema, operation_handle, session = self.execute_statement(query, max_rows=10000, orientation=TFetchOrientation.FETCH_NEXT, session=session)
+        desc_results, desc_schema, operation_handle, session = self.execute_statement(
+            query,
+            max_rows=10000,
+            orientation=TFetchOrientation.FETCH_NEXT,
+            session=session
+        )
         self.close_operation(operation_handle)
         self.close_operation(operation_handle)
 
 
         desc_results.results.columns[0].stringVal.values.insert(0, '# col_name')
         desc_results.results.columns[0].stringVal.values.insert(0, '# col_name')
@@ -1005,7 +1026,7 @@ class HiveServerClient(object):
     else:
     else:
       res = TFetchResultsResp(results=TRowSet(startRowOffset=0, rows=[], columns=[]))
       res = TFetchResultsResp(results=TRowSet(startRowOffset=0, rows=[], columns=[]))
 
 
-    if operation_handle.hasResultSet and TFetchOrientation.FETCH_FIRST: # Only fetch for the first call that should be with start_over
+    if operation_handle.hasResultSet and TFetchOrientation.FETCH_FIRST:  # Only fetch for the first call that should be with start_over
       meta_req = TGetResultSetMetadataReq(operationHandle=operation_handle)
       meta_req = TGetResultSetMetadataReq(operationHandle=operation_handle)
       (schema, session) = self.call(self._client.GetResultSetMetadata, meta_req)
       (schema, session) = self.call(self._client.GetResultSetMetadata, meta_req)
     else:
     else:

+ 80 - 4
apps/beeswax/src/beeswax/server/hive_server2_lib_tests.py

@@ -23,13 +23,13 @@ from nose.tools import assert_equal, assert_true, assert_raises, assert_not_equa
 from nose.plugins.skip import SkipTest
 from nose.plugins.skip import SkipTest
 from TCLIService.ttypes import TStatusCode
 from TCLIService.ttypes import TStatusCode
 
 
+from desktop.lib.django_test_util import make_logged_in_client
+from useradmin.models import User
+
 from beeswax.conf import MAX_NUMBER_OF_SESSIONS, CLOSE_SESSIONS
 from beeswax.conf import MAX_NUMBER_OF_SESSIONS, CLOSE_SESSIONS
 from beeswax.models import Session
 from beeswax.models import Session
-from beeswax.server.dbms import get_query_server_config
+from beeswax.server.dbms import get_query_server_config, QueryServerException
 from beeswax.server.hive_server2_lib import HiveServerTable, HiveServerClient
 from beeswax.server.hive_server2_lib import HiveServerTable, HiveServerClient
-from useradmin.models import User
-
-from desktop.lib.django_test_util import make_logged_in_client
 
 
 if sys.version_info[0] > 2:
 if sys.version_info[0] > 2:
   from unittest.mock import patch, Mock, MagicMock
   from unittest.mock import patch, Mock, MagicMock
@@ -209,6 +209,82 @@ class TestHiveServerClient():
         )
         )
 
 
 
 
+  def test_get_table_with_error(self):
+    query = Mock(
+      get_query_statement=Mock(return_value=['SELECT 1']),
+      settings=[]
+    )
+
+    with patch('beeswax.server.hive_server2_lib.thrift_util.get_client') as get_client:
+      get_client.return_value = Mock(
+        OpenSession=Mock(
+          return_value=Mock(
+            status=Mock(
+              statusCode=TStatusCode.SUCCESS_STATUS
+            ),
+            configuration={},
+            sessionHandle=Mock(
+              sessionId=Mock(
+                secret=b'1',
+                guid=b'1'
+              )
+            ),
+            serverProtocolVersion=11
+          )
+        ),
+        ExecuteStatement=Mock(
+          return_value=Mock(
+            status=Mock(
+              statusCode=TStatusCode.SUCCESS_STATUS
+            ),
+          )
+        ),
+        get_coordinator_host=Mock(return_value='hive-host')
+      )
+
+      client = HiveServerClient(self.query_server, self.user)
+
+      # Non empty error message from HS2
+      client._client.GetTables = Mock(
+        return_value=Mock(
+          status=Mock(
+            errorMessage='Error while compiling statement: FAILED: HiveAccessControlException Permission denied'
+          )
+        )
+      )
+
+      assert_raises(QueryServerException, client.get_table, database='database', table_name='table_name')
+
+      try:
+        client.get_table(database='database', table_name='table_name')
+      except QueryServerException as e:
+        assert_equal(
+          'Error while compiling statement: FAILED: HiveAccessControlException Permission denied',
+          str(e)
+        )
+
+      # Empty error message from HS2
+      get_tables_res = Mock(
+          status=Mock(
+            errorMessage=None
+          )
+      )
+      client._client.GetTables = Mock(
+        return_value=get_tables_res
+      )
+
+      try:
+        client.get_table(database='database', table_name='table_name')
+      except QueryServerException as e:
+        if sys.version_info[0] > 2:
+          req_string = "TGetTablesReq(sessionHandle=TSessionHandle(sessionId=THandleIdentifier(guid=b'1', secret=b'1')), catalogName=None, schemaName='database', tableName='table_name', tableTypes=None)"
+        else:
+          req_string = "TGetTablesReq(schemaName='database', sessionHandle=TSessionHandle(sessionId=THandleIdentifier(secret='1', guid='1')), tableName='table_name', tableTypes=None, catalogName=None)"
+        assert_equal(
+          "Bad status for request %s:\n%s" % (req_string, get_tables_res),
+          str(e)
+        )
+
 class TestHiveServerTable():
 class TestHiveServerTable():
 
 
   def test_cols_impala(self):
   def test_cols_impala(self):