Prechádzať zdrojové kódy

HUE-9064 [ws] Parameterize channels config to be setup

For py3 only. No change in py2 and off by default.
Skeleton only so non documented currently.
Next step is TaskServer / Editor integration. These commits will enable
making progress on it.
Romain 6 rokov pred
rodič
commit
1383cc848f

+ 29 - 0
desktop/core/src/desktop/conf.py

@@ -1766,6 +1766,35 @@ TASK_SERVER = ConfigSection(
 ))
 
 
+def has_channels():
+  return sys.version_info[0] > 2 and WEBSOCKETS.ENABLED.get()
+
+
+WEBSOCKETS = ConfigSection(
+  key="websockets",
+  help=_("Django channels Websockets configuration. Requires Python 3."),
+  members=dict(
+    ENABLED= Config(
+      key='enabled',
+      default=False,
+      type=coerce_bool,
+      help=_('If websockets channels are to be used for communicating with clients.')
+    ),
+    LAYER_HOST = Config(
+      key='layer_host',
+      default='127.0.0.1',
+      help=_('Layer backend host.')
+    ),
+    LAYER_PORT = Config(
+      key='layer_port',
+      type=int,
+      default=6379,
+      help=_('Layer backend port.')
+    ),
+  )
+)
+
+
 def get_clusters(user):
   clusters = []
   cluster_config = CLUSTERS.get()

+ 34 - 12
desktop/core/src/desktop/routing.py

@@ -1,12 +1,34 @@
-from channels.auth import AuthMiddlewareStack
-from channels.routing import ProtocolTypeRouter, URLRouter
-import notebook.routing
-
-application = ProtocolTypeRouter({
-    # (http->django views is added by default)
-    'websocket': AuthMiddlewareStack(
-        URLRouter(
-            notebook.routing.websocket_urlpatterns
-        )
-    ),
-})
+#!/usr/bin/env python
+# Licensed to Cloudera, Inc. under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  Cloudera, Inc. licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+from desktop.conf import has_channels
+
+
+if has_channels():
+  from channels.auth import AuthMiddlewareStack
+  from channels.routing import ProtocolTypeRouter, URLRouter
+
+  import notebook.routing
+
+  application = ProtocolTypeRouter({
+      # (http->django views is added by default)
+      'websocket': AuthMiddlewareStack(
+          URLRouter(
+              notebook.routing.websocket_urlpatterns
+          )
+      ),
+  })

+ 10 - 10
desktop/core/src/desktop/settings.py

@@ -36,6 +36,7 @@ from django.utils.translation import ugettext_lazy as _
 import desktop.redaction
 from desktop.lib.paths import get_desktop_root
 from desktop.lib.python_util import force_dict_to_strings
+from desktop.conf import has_channels
 
 from aws.conf import is_enabled as is_s3_enabled
 from azure.conf import is_abfs_enabled
@@ -204,8 +205,6 @@ INSTALLED_APPS = [
     # Desktop injects all the other installed apps into here magically.
     'desktop',
 
-    'channels',
-
     # App that keeps track of failed logins.
     'axes',
     'webpack_loader',
@@ -482,16 +481,17 @@ if EMAIL_BACKEND == 'sendgrid_backend.SendgridBackend':
   SENDGRID_SANDBOX_MODE_IN_DEBUG = DEBUG
 
 
-ASGI_APPLICATION = 'desktop.routing.application'
-
-CHANNEL_LAYERS = {
+if has_channels():
+  INSTALLED_APPS.append('channels')
+  ASGI_APPLICATION = 'desktop.routing.application'
+  CHANNEL_LAYERS = {
     'default': {
-        'BACKEND': 'channels_redis.core.RedisChannelLayer',
-        'CONFIG': {
-            "hosts": [('127.0.0.1', 6379)],
-        },
+      'BACKEND': 'channels_redis.core.RedisChannelLayer',
+      'CONFIG': {
+        'hosts': [(desktop.conf.WEBSOCKETS.LAYER_HOST.get(), desktop.conf.WEBSOCKETS.LAYER_PORT.get())],
+      },
     },
-}
+  }
 
 # Used for securely creating sessions. Should be unique and not shared with anybody. Changing auth backends will invalidate all open sessions.
 SECRET_KEY = desktop.conf.get_secret_key()

+ 2 - 1
desktop/core/src/desktop/templates/common_header.mako

@@ -217,7 +217,7 @@ if USE_NEW_EDITOR.get():
 ${ hueIcons.symbols() }
 
 
-% if request.environ.get("PATH_INFO").find("/hue/") < 0:
+% if hasattr(request, 'environ') and request.environ.get("PATH_INFO").find("/hue/") < 0:
   <script>
     window.location.replace("/");
   </script>
@@ -239,6 +239,7 @@ ${ hueIcons.symbols() }
         count += 1
     return found_app, count
 %>
+
 % if not skip_topbar:
 <div class="navigator">
   <div class="pull-right">

+ 4 - 1
desktop/core/src/desktop/templates/common_notebook_ko_components.mako

@@ -21,6 +21,7 @@ from django.utils.translation import ugettext as _
 from desktop import conf
 from desktop.lib.i18n import smart_unicode
 from desktop.views import _ko
+
 from beeswax.conf import DOWNLOAD_ROW_LIMIT, DOWNLOAD_BYTES_LIMIT
 from notebook.conf import ENABLE_SQL_INDEXER
 %>
@@ -418,7 +419,8 @@ from notebook.conf import ENABLE_SQL_INDEXER
           $('.clipboard-content').empty();
         });
 
-        var chatSocket = new WebSocket('ws://' + window.location.host + '/ws/chat/' + self.snippet.id() + '/');
+        % if conf.WEBSOCKETS.ENABLED.get():
+        var chatSocket = new WebSocket('ws://' + window.location.host + '/ws/editor/results/' + self.snippet.id() + '/');
 
         chatSocket.onmessage = function(e) {
           var data = JSON.parse(e.data);
@@ -429,6 +431,7 @@ from notebook.conf import ENABLE_SQL_INDEXER
         chatSocket.onclose = function(e) {
           console.error('Chat socket closed unexpectedly');
         };
+        % endif
 
         self.trySaveResults = function () {
           if (self.isValidDestination()) {

+ 42 - 20
desktop/libs/notebook/src/notebook/consumer.py

@@ -1,28 +1,50 @@
-from channels.generic.websocket import AsyncWebsocketConsumer
+#!/usr/bin/env python
+# Licensed to Cloudera, Inc. under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  Cloudera, Inc. licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
 import json
 
-class ChatConsumer(AsyncWebsocketConsumer):
+from desktop.conf import has_channels
+
+if has_channels():
+  from channels.generic.websocket import AsyncWebsocketConsumer
+
+
+  class EditorConsumer(AsyncWebsocketConsumer):
 
-  async def connect(self):
-    await self.accept()
+    async def connect(self):
+      await self.accept()
 
-    await self.send(text_data=json.dumps({
-      'type': 'channel_name',
-      'data': self.channel_name,
-      'accept': True
-    }))
+      await self.send(text_data=json.dumps({
+        'type': 'channel_name',
+        'data': self.channel_name,
+        'accept': True
+      }))
 
 
-  async def task_progress(self, event):
-    await self.send(text_data=json.dumps({
-      'type': 'task_progress',
-      'data': event["data"]
-    }))
+    async def task_progress(self, event):
+      await self.send(text_data=json.dumps({
+        'type': 'task_progress',
+        'data': event["data"]
+      }))
 
 
-def _send_to_channel(channel_name, message_type, message_data):
-  channel_layer = get_channel_layer()
-  async_to_sync(channel_layer.send)(channel_name, {
-      "type": message_type,
-      "data": message_data,
-  })
+  def _send_to_channel(channel_name, message_type, message_data):
+    channel_layer = get_channel_layer()
+    async_to_sync(channel_layer.send)(channel_name, {
+        "type": message_type,
+        "data": message_data,
+    })

+ 24 - 4
desktop/libs/notebook/src/notebook/routing.py

@@ -1,9 +1,29 @@
+#!/usr/bin/env python
+# Licensed to Cloudera, Inc. under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  Cloudera, Inc. licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
 # from django.urls import re_path # Django 2
 from django.conf.urls import url
 
+from desktop.conf import has_channels
+
 
-from notebook import consumer
+if has_channels():
+  from notebook import consumer
 
-websocket_urlpatterns = [
-    url(r'ws/chat/(?P<room_name>[\w\-]+)/$', consumer.ChatConsumer),
-]
+  websocket_urlpatterns = [
+      url(r'ws/editor/results/(?P<query_uuid>[\w\-]+)/$', consumer.EditorConsumer),
+  ]