Repository: voxel51/fiftyone

Issue:
Linux mongodb binary doesn't work on some older/non-Ubuntu distros
For tracking purposes.
Downloads from: https://www.mongodb.com/try/download/community

Works out of the box (with ubuntu1804 package):
* Ubuntu 18.04
* Ubuntu 20.04
* Debian 10
* Fedora 26
* Fedora 32
* Arch (latest)
* CentOS 8

Doesn't work:
* Ubuntu 16.04: Needs ubuntu1604 package from mongodb.org
* Debian 9: Needs debian92 package from mongodb.org

Untested:
* CentOS 6
* SUSE
* Amazon Linux (listed on mongodb.org)
* Gentoo (not listed on mongodb.org)
* Others not listed on mongodb.org


Discussion:

Resolving patch:
diff --git a/fiftyone/constants.py b/fiftyone/constants.py
--- a/fiftyone/constants.py
+++ b/fiftyone/constants.py
@@ -56,8 +56,6 @@
     # development installation
     FIFTYONE_DB_BIN_DIR = os.path.join(FIFTYONE_CONFIG_DIR, "bin")
 DB_PATH = os.path.join(FIFTYONE_CONFIG_DIR, "var/lib/mongo")
-DB_BIN_PATH = os.path.join(FIFTYONE_DB_BIN_DIR, "mongod")
-DB_CLIENT_BIN_PATH = os.path.join(FIFTYONE_DB_BIN_DIR, "mongo")
 DB_LOG_PATH = os.path.join(FIFTYONE_CONFIG_DIR, "var/log/mongodb/mongo.log")
 
 # Server setup
diff --git a/fiftyone/core/service.py b/fiftyone/core/service.py
--- a/fiftyone/core/service.py
+++ b/fiftyone/core/service.py
@@ -20,9 +20,11 @@
 
 import logging
 import os
+import re
 import subprocess
 import sys
 
+from packaging.version import Version
 import requests
 
 import eta.core.utils as etau
@@ -96,13 +98,17 @@ def wait(self):
 class DatabaseService(Service):
     """Service that controls the underlying MongoDB database."""
 
-    command = [
-        foc.DB_BIN_PATH,
-        "--dbpath",
-        foc.DB_PATH,
-        "--logpath",
-        foc.DB_LOG_PATH,
-    ]
+    MIN_MONGO_VERSION = "3.6"
+
+    @property
+    def command(self):
+        return [
+            DatabaseService.find_mongod(),
+            "--dbpath",
+            foc.DB_PATH,
+            "--logpath",
+            foc.DB_LOG_PATH,
+        ]
 
     def start(self):
         """Starts the DatabaseService."""
@@ -117,6 +123,42 @@ def start(self):
 
         fod.delete_non_persistent_datasets()
 
+    @staticmethod
+    def find_mongod():
+        search_paths = [
+            foc.FIFTYONE_DB_BIN_DIR,
+            os.path.join(foc.FIFTYONE_CONFIG_DIR, "bin"),
+        ] + os.environ["PATH"].split(os.pathsep)
+        searched = set()
+        attempts = []
+        for folder in search_paths:
+            if folder in searched:
+                continue
+            searched.add(folder)
+            mongod_path = os.path.join(folder, "mongod")
+            if os.path.isfile(mongod_path):
+                ok, out, err = etau.communicate([mongod_path, "--version"])
+                out = out.decode(errors="ignore").strip()
+                err = err.decode(errors="ignore").strip()
+                mongod_version = None
+                if ok:
+                    match = re.search(r"db version.+?([\d\.]+)", out, re.I)
+                    if match:
+                        mongod_version = match.group(1)
+                        if Version(mongod_version) >= Version(
+                            DatabaseService.MIN_MONGO_VERSION
+                        ):
+                            return mongod_path
+                attempts.append((mongod_path, mongod_version, err))
+        for path, version, err in attempts:
+            if version is not None:
+                logger.warn("%s: incompatible version %s" % (path, version))
+            else:
+                logger.error("%s: failed to launch: %s" % (path, err))
+        raise RuntimeError(
+            "Could not find mongod >= %s" % DatabaseService.MIN_MONGO_VERSION
+        )
+
 
 class ServerService(Service):
     """Service that controls the FiftyOne web server."""
diff --git a/package/db/setup.py b/package/db/setup.py
--- a/package/db/setup.py
+++ b/package/db/setup.py
@@ -25,9 +25,12 @@
 MONGODB_DOWNLOAD_URLS = {
     "linux": "https://fastdl.mongodb.org/linux/mongodb-linux-x86_64-ubuntu1804-4.2.6.tgz",
     "mac": "https://fastdl.mongodb.org/osx/mongodb-macos-x86_64-4.2.6.tgz",
+    "ubuntu1604": "https://fastdl.mongodb.org/linux/mongodb-linux-x86_64-ubuntu1604-4.2.6.tgz",
+    "debian9": "https://fastdl.mongodb.org/linux/mongodb-linux-x86_64-debian92-4.2.6.tgz",
 }
 # mongodb binaries to distribute
 MONGODB_BINARIES = ["mongod"]
+LINUX_DISTRO = os.environ.get("FIFTYONE_DB_BUILD_LINUX_DISTRO")
 
 
 class CustomBdistWheel(bdist_wheel):
@@ -68,6 +71,15 @@ def write_wheelfile(self, *args, **kwargs):
             for k, v in MONGODB_DOWNLOAD_URLS.items()
             if self.plat_name.startswith(k)
         )
+        if LINUX_DISTRO is not None:
+            if not self.plat_name.startswith("linux"):
+                raise ValueError(
+                    "Cannot build for distro %r on platform %r"
+                    % (LINUX_DISTRO, self.plat_name)
+                )
+            if LINUX_DISTRO not in MONGODB_DOWNLOAD_URLS:
+                raise ValueError("Unrecognized distro: %r" % LINUX_DISTRO)
+            mongo_zip_url = MONGODB_DOWNLOAD_URLS[LINUX_DISTRO]
         mongo_zip_filename = os.path.basename(mongo_zip_url)
         mongo_zip_dest = os.path.join(
             os.path.dirname(os.path.abspath(__file__)),
@@ -107,8 +119,12 @@ def write_wheelfile(self, *args, **kwargs):
     "bdist_wheel": CustomBdistWheel,
 }
 
+name_suffix = ""
+if LINUX_DISTRO:
+    name_suffix = "_" + LINUX_DISTRO
+
 setup(
-    name="fiftyone_db",
+    name="fiftyone_db" + name_suffix,
     version="0.1.0",
     description="Project FiftyOne database",
     author="Voxel51, Inc.",


Test patch:
diff --git a/package/db/tests/test_db.py b/package/db/tests/test_db.py
--- a/package/db/tests/test_db.py
+++ b/package/db/tests/test_db.py
@@ -1,4 +1,5 @@
 import os
+import subprocess
 
 
 def test_db_import():
@@ -7,3 +8,11 @@ def test_db_import():
     assert os.path.isfile(
         os.path.join(fiftyone.db.FIFTYONE_DB_BIN_DIR, "mongod")
     )
+
+
+def test_db_exec():
+    import fiftyone.db
+
+    subprocess.check_call(
+        [os.path.join(fiftyone.db.FIFTYONE_DB_BIN_DIR, "mongod"), "--version"]
+    )