Repository: learningequality/kolibri

Issue:
zeroconf: Crashes when starting devserver
### Observed behavior
Tried to run devserver.
```
Traceback (most recent call last):
  File "/home/richard/.virtualenvs/kolibri/bin/kolibri", line 11, in <module>
    load_entry_point('kolibri', 'console_scripts', 'kolibri')()
  File "/home/richard/.virtualenvs/kolibri/local/lib/python2.7/site-packages/click/core.py", line 764, in __call__
    return self.main(*args, **kwargs)
  File "/home/richard/.virtualenvs/kolibri/local/lib/python2.7/site-packages/click/core.py", line 717, in main
    rv = self.invoke(ctx)
  File "/home/richard/.virtualenvs/kolibri/local/lib/python2.7/site-packages/click/core.py", line 1137, in invoke
    return _process_result(sub_ctx.command.invoke(sub_ctx))
  File "/home/richard/github/kolibri/kolibri/utils/cli.py", line 237, in invoke
    return super(KolibriDjangoCommand, self).invoke(ctx)
  File "/home/richard/.virtualenvs/kolibri/local/lib/python2.7/site-packages/click/core.py", line 956, in invoke
    return ctx.invoke(self.callback, **ctx.params)
  File "/home/richard/.virtualenvs/kolibri/local/lib/python2.7/site-packages/click/core.py", line 555, in invoke
    return callback(*args, **kwargs)
  File "/home/richard/github/kolibri/kolibri/utils/cli.py", line 610, in services
    server.services()
  File "/home/richard/github/kolibri/kolibri/utils/server.py", line 167, in services
    run_services(port=port)
  File "/home/richard/github/kolibri/kolibri/utils/server.py", line 121, in run_services
    register_zeroconf_service(port=port, id=instance.id[:4])
  File "/home/richard/github/kolibri/kolibri/core/discovery/utils/network/search.py", line 160, in register_zeroconf_service
    ZEROCONF_STATE["service"].register()
  File "/home/richard/github/kolibri/kolibri/core/discovery/utils/network/search.py", line 68, in register
    properties=self.data,
  File "/home/richard/.virtualenvs/kolibri/local/lib/python2.7/site-packages/zeroconf.py", line 1485, in __init__
    self._set_properties(properties)
  File "/home/richard/.virtualenvs/kolibri/local/lib/python2.7/site-packages/zeroconf.py", line 1516, in _set_properties
    result = b"".join((result, int2byte(len(item)), item))
ValueError: chr() arg not in range(256)
```

### Expected behavior
Devserver should run :)


Discussion:
Sorry, it seems that things still run as expected, it's just the zero conf service has crashed in the background - have updated the title.
The above error was on Python2, the error below is from Python 3:
```
Traceback (most recent call last):
  File "/home/richard/.virtualenvs/kolibripy3/bin/kolibri", line 11, in <module>
    load_entry_point('kolibri', 'console_scripts', 'kolibri')()
  File "/home/richard/.virtualenvs/kolibripy3/lib/python3.6/site-packages/click/core.py", line 764, in __call__
    return self.main(*args, **kwargs)
  File "/home/richard/.virtualenvs/kolibripy3/lib/python3.6/site-packages/click/core.py", line 717, in main
    rv = self.invoke(ctx)
  File "/home/richard/.virtualenvs/kolibripy3/lib/python3.6/site-packages/click/core.py", line 1137, in invoke
    return _process_result(sub_ctx.command.invoke(sub_ctx))
  File "/home/richard/github/kolibri/kolibri/utils/cli.py", line 237, in invoke
    return super(KolibriDjangoCommand, self).invoke(ctx)
  File "/home/richard/.virtualenvs/kolibripy3/lib/python3.6/site-packages/click/core.py", line 956, in invoke
    return ctx.invoke(self.callback, **ctx.params)
  File "/home/richard/.virtualenvs/kolibripy3/lib/python3.6/site-packages/click/core.py", line 555, in invoke
    return callback(*args, **kwargs)
  File "/home/richard/github/kolibri/kolibri/utils/cli.py", line 624, in services
    server.services()
  File "/home/richard/github/kolibri/kolibri/utils/server.py", line 151, in services
    run_services(port=port)
  File "/home/richard/github/kolibri/kolibri/utils/server.py", line 112, in run_services
    register_zeroconf_service(port=port, id=instance.id[:4])
  File "/home/richard/github/kolibri/kolibri/core/discovery/utils/network/search.py", line 160, in register_zeroconf_service
    ZEROCONF_STATE["service"].register()
  File "/home/richard/github/kolibri/kolibri/core/discovery/utils/network/search.py", line 68, in register
    properties=self.data,
  File "/home/richard/.virtualenvs/kolibripy3/lib/python3.6/site-packages/zeroconf.py", line 1485, in __init__
    self._set_properties(properties)
  File "/home/richard/.virtualenvs/kolibripy3/lib/python3.6/site-packages/zeroconf.py", line 1516, in _set_properties
    result = b"".join((result, int2byte(len(item)), item))
struct.error: ubyte format requires 0 <= number <= 255
```

Resolving patch:
diff --git a/kolibri/core/discovery/utils/network/search.py b/kolibri/core/discovery/utils/network/search.py
--- a/kolibri/core/discovery/utils/network/search.py
+++ b/kolibri/core/discovery/utils/network/search.py
@@ -11,8 +11,7 @@
 from zeroconf import USE_IP_OF_OUTGOING_INTERFACE
 from zeroconf import Zeroconf
 
-from kolibri.core.auth.models import Facility
-from kolibri.core.content.models import ChannelMetadata
+import kolibri
 
 logger = logging.getLogger(__name__)
 
@@ -106,13 +105,17 @@ def add_service(self, zeroconf, type, name):
         info = zeroconf.get_service_info(type, name)
         id = _id_from_name(name)
         ip = socket.inet_ntoa(info.address)
+
         self.instances[id] = {
             "id": id,
             "ip": ip,
             "local": ip in get_all_addresses(),
             "port": info.port,
             "host": info.server.strip("."),
-            "data": {key: json.loads(val) for (key, val) in info.properties.items()},
+            "data": {
+                bytes.decode(key): json.loads(val)
+                for (key, val) in info.properties.items()
+            },
             "base_url": "http://{ip}:{port}/".format(ip=ip, port=info.port),
         }
         logger.info(
@@ -150,12 +153,7 @@ def register_zeroconf_service(port, id):
     if ZEROCONF_STATE["service"] is not None:
         unregister_zeroconf_service()
     logger.info("Registering ourselves to zeroconf network with id '%s'..." % id)
-    data = {
-        "facilities": list(Facility.objects.values("id", "dataset_id", "name")),
-        "channels": list(
-            ChannelMetadata.objects.filter(root__available=True).values("id", "name")
-        ),
-    }
+    data = {"version": kolibri.VERSION}
     ZEROCONF_STATE["service"] = KolibriZeroconfService(id=id, port=port, data=data)
     ZEROCONF_STATE["service"].register()
 


Test patch:
diff --git a/kolibri/core/discovery/test/test_network_search.py b/kolibri/core/discovery/test/test_network_search.py
--- a/kolibri/core/discovery/test/test_network_search.py
+++ b/kolibri/core/discovery/test/test_network_search.py
@@ -21,6 +21,7 @@
 MOCK_INTERFACE_IP = "111.222.111.222"
 MOCK_PORT = 555
 MOCK_ID = "abba"
+MOCK_PROPERTIES = {b"version": '[0, 13, 0, "alpha", 0]'}
 
 
 class MockServiceBrowser(object):
@@ -48,8 +49,9 @@ def get_service_info(self, type_, name, timeout=3000):
             server=".".join([id, LOCAL_DOMAIN, ""]),
             address=socket.inet_aton(MOCK_INTERFACE_IP),
             port=MOCK_PORT,
-            properties={"facilities": "[]", "channels": "[]"},
+            properties=MOCK_PROPERTIES,
         )
+
         return info
 
     def add_service_listener(self, type_, listener):
@@ -114,7 +116,7 @@ def test_register_zeroconf_service(self):
                 "self": True,
                 "port": MOCK_PORT,
                 "host": ".".join([MOCK_ID, LOCAL_DOMAIN]),
-                "data": {"facilities": [], "channels": []},
+                "data": {"version": [0, 13, 0, "alpha", 0]},
                 "base_url": "http://{ip}:{port}/".format(
                     ip=MOCK_INTERFACE_IP, port=MOCK_PORT
                 ),