Explorar el Código

[jobbrowser] Add Hive kill query via Notebook call

Call is done as the logged-in user making the call and will
go throught the user existing HiveServer2 session or create
a new session.

$.post("/jobbrowser/api/job/action/queries-hive/kill", {
  "operation": JSON.stringify({'action': 'kill'}),
  "interface": JSON.stringify('queries-hive'),
  "app_ids": JSON.stringify(['d94d2fb4815a05c4:b1ccec1500000000'])
}, function(data) {
  console.log(JSON.stringify(data));
});

Using the notebook API is a good skeleton example for when we could
have spefici actions like kill for other SQL dialects.

Note: not compatible with connectors.
Romain Rigaux hace 5 años
padre
commit
d6c085cf6c

+ 3 - 3
apps/jobbrowser/src/jobbrowser/api2.py

@@ -118,7 +118,9 @@ def action(request, interface=None, action=None):
     return serve_403_error(request)
 
   response['operation'] = operation
-  response.update(get_api(request.user, interface, cluster=cluster).action(app_ids, operation))
+  response.update(
+      get_api(request.user, interface, cluster=cluster).action(app_ids, operation)
+  )
 
   return JsonResponse(response)
 
@@ -188,9 +190,7 @@ def query_store_api(request, path=None):
       response['code'] = ex_response.status_code
       response['message'] = ex_response.reason
       response['content'] = ex_response.text
-
   else:
-
     if path == 'api/query/search':
       filters = json.loads(request.body)
       resp = get_api(request.user, interface='queries-hive').apps(filters['search'])

+ 13 - 2
apps/jobbrowser/src/jobbrowser/apis/hive_query_api.py

@@ -24,10 +24,11 @@ from django.utils.translation import ugettext as _
 
 from desktop.lib.exceptions_renderable import PopupException
 from desktop.lib.python_util import current_ms_from_utc
-
 from desktop.lib.rest.http_client import HttpClient
 from desktop.lib.rest.resource import Resource
 
+from notebook.models import _get_notebook_api
+
 from jobbrowser.apis.base_api import Api
 from jobbrowser.models import HiveQuery
 from jobbrowser.conf import QUERY_STORE
@@ -116,7 +117,17 @@ class HiveQueryApi(Api):
   def action(self, appid, action):
     message = {'message': '', 'status': 0}
 
-    return message;
+    if action.get('action') == 'kill':
+      for queryid in appid:
+        notebook = {}
+        snippet = {'result': {'handle': {'secret': queryid, 'guid': queryid}}}
+        connector_id = 'hive'
+
+        response = _get_notebook_api(self.user, connector_id).cancel(notebook, snippet)
+        message['status'] = response['status'] if response['status'] != 0 else message['status']
+        message['message'] = _('kill action performed')
+
+    return message
 
   def logs(self, appid, app_type, log_name=None, is_embeddable=False):
     return {'logs': ''}

+ 37 - 6
apps/jobbrowser/src/jobbrowser/apis/hive_query_api_tests.py

@@ -43,6 +43,37 @@ else:
 LOG = logging.getLogger(__name__)
 
 
+
+class TestHiveQueryApiNotebook():
+
+  def setUp(self):
+    self.client = make_logged_in_client(username="test", groupname="default", recreate=True, is_superuser=False)
+    self.user = rewrite_user(User.objects.get(username="test"))
+
+
+  def test_kill_query(self):
+    with patch('jobbrowser.apis.hive_query_api._get_notebook_api') as _get_notebook_api:
+      cancel_call = Mock(return_value={'status': 0})
+      _get_notebook_api.return_value = Mock(cancel=cancel_call)
+
+      appid = 'd94d2fb4815a05c4:b1ccec1500000000'
+      data = {
+        'operation': json.dumps({'action': 'kill'}),
+        'interface': json.dumps('queries-hive'),
+        'app_ids': json.dumps([appid])
+      }
+      response = self.client.post("/jobbrowser/api/job/action/queries-hive/kill", data)
+      response_data = json.loads(response.content)
+
+      notebook = {}
+      snippet = {'result': {'handle': {'secret': appid, 'guid': appid}}}
+
+      _get_notebook_api.assert_called_once_with(self.user, 'hive')
+      cancel_call.assert_called_once_with(notebook, snippet)
+
+      assert_equal(0, response_data['status'])
+
+
 class TestHiveQueryApi():
 
   def setUp(self):
@@ -88,12 +119,12 @@ class TestHiveQueryApi():
       response = self.client.post("/jobbrowser/api/jobs/queries-hive", content_type='application/json', data=_data)
       data = json.loads(response.content)
 
-      assert_equal(2, len(data['queries'])) # pagination
-      assert_equal('3', data['queries'][0]['queryId']) # query id (with order_by)
-      assert_equal("SUCCESS", data['queries'][0]['status']) # facet selection
-      assert_true("select" in data['queries'][0]['query']) # search text
-      assert_equal(3, data['meta']['size']) # total filtered queries count
-      assert_equal(2, data['meta']['limit']) # limit value of filter
+      assert_equal(2, len(data['queries']))  # pagination
+      assert_equal('3', data['queries'][0]['queryId'])  # query id (with order_by)
+      assert_equal("SUCCESS", data['queries'][0]['status'])  # facet selection
+      assert_true("select" in data['queries'][0]['query'])  # search text
+      assert_equal(3, data['meta']['size'])  # total filtered queries count
+      assert_equal(2, data['meta']['limit'])  # limit value of filter
 
 
   # TODO

+ 0 - 1
apps/jobbrowser/src/jobbrowser/apis/query_api.py

@@ -148,7 +148,6 @@ class QueryApi(Api):
     message = {'message': '', 'status': 0}
 
     if action.get('action') == 'kill':
-
       for _id in appid:
         result = self.api.kill(_id)
         if result.get('error'):

+ 40 - 0
desktop/libs/notebook/src/notebook/models.py

@@ -239,6 +239,46 @@ def make_notebook2(name='Browse', description='', is_saved=False, snippets=None)
   return editor
 
 
+def _get_notebook_api(user, connector_id, interpreter=None):
+  '''
+  Helper utils until the API gets simplified.
+  '''
+  notebook_json = """
+    {
+      "selectedSnippet": "hive",
+      "showHistory": false,
+      "description": "Test Query",
+      "name": "Test Query",
+      "sessions": [
+          {
+              "type": "hive",
+              "properties": [],
+              "id": null
+          }
+      ],
+      "type": "hive",
+      "id": null,
+      "snippets": [{"id":"2b7d1f46-17a0-30af-efeb-33d4c29b1055","type":"%(connector_id)s","status":"running",\
+         "statement":"select * from web_logs","properties":{"settings":[],"variables":[],"files":[],"functions":[]},\
+          "result":{"id":"b424befa-f4f5-8799-a0b4-79753f2552b1","type":"table",\
+          "handle":{"log_context":null,"statements_count":1,\
+          "end":{"column":21,"row":0},"statement_id":0,"has_more_statements":false,\
+          "start":{"column":0,"row":0},"secret":"rVRWw7YPRGqPT7LZ/TeFaA==an","has_result_set":true,\
+          "statement":"select * from web_logs","operation_type":0,"modified_row_count":null,"guid":"7xm6+epkRx6dyvYvGNYePA==an"}},\
+          "lastExecuted": 1462554843817,"database":"default"}],
+      "uuid": "d9efdee1-ef25-4d43-b8f9-1a170f69a05a"
+  }
+  """ % {
+    'connector_id': connector_id,
+  }
+  snippet = json.loads(notebook_json)['snippets'][0]
+  snippet['interpreter'] = interpreter
+
+  request = MockRequest(user)
+
+  return get_api(request, snippet)
+
+
 class MockedDjangoRequest(object):
 
   def __init__(self, user, get=None, post=None, method='POST'):