Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use, when possible, an ENV var to get the installation type #8500

Merged
merged 6 commits into from
Nov 19, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
Empty file.
28 changes: 28 additions & 0 deletions kolibri/utils/constants/installation_types.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
"""
This module contains constants representing the type of "installers" used to install Kolibri.
"""
from __future__ import unicode_literals

APK = "apk"
DEB = "deb"
FLATPAK = "flatpak"
GNOME = "gnome"
KOLIBRI_SERVER = "kolibriserver"
MACOS = "mac"
PEX = "pex"
WHL = "whl"
WINDOWS = "windows"
WINDOWS_APP = "windowsapp"

install_type_map = {
APK: "apk - {}",
DEB: "deb kolibri - {}",
FLATPAK: "Flatpak - {}",
GNOME: "GNOME - {}",
KOLIBRI_SERVER: "deb kolibri-server - {}",
MACOS: "Mac - {}",
PEX: "pex",
WHL: "whl",
WINDOWS: "Windows - {}",
WINDOWS_APP: "Windows App - {}",
}
132 changes: 86 additions & 46 deletions kolibri/utils/server.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
from zeroconf import InterfaceChoice

import kolibri
from .constants import installation_types
from .system import become_daemon
from .system import pid_exists
from kolibri.utils import conf
Expand Down Expand Up @@ -913,77 +914,116 @@ def get_urls(listen_port=None):
return e.status_code, []


def get_installer_version(installer_type): # noqa: C901
def get_debian_pkg_version(package):
"""
In case we want to distinguish between dpkg and apt installations
we can use apt-cache show madison and compare versions with dpkg
if dpkg > madison, it's dpkg otherwise it's apt
"""
try:
output = check_output(["dpkg", "-s", package])
if hasattr(output, "decode"): # needed in python 2.x
output = output.decode("utf-8")
package_info = output.split("\n")
version_info = [output for output in package_info if "Version" in output]
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure why this line didn't raise an issue before, but it's now causing a python 2 syntax check flake8 failure:

kolibri/utils/server.py:929:40: F812 list comprehension redefines 'output' from line 927

https://github.com/indirectlylit/kolibri/runs/4301449205?check_suite_focus=true

Perhaps this should be:

             version_info = [info for info in package_info if "Version" in info]

Weirdly, I see the linting error in my fork but not in the LE org repo.

cc @jredrejo @rtibbles

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

interesting, the check is skipped in LE but not in my repo:

https://github.com/learningequality/kolibri/runs/4301448839?check_suite_focus=true

image

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh - probably because the path match check looks back through the history of runs (which are constrained to a specific fork) so because the learning equality repo had a test run that had previously passed for this set of files, it doesn't run again, whereas your fork didn't.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

filed a follow-up here: #8772

Copy link
Member Author

@jredrejo jredrejo Nov 23, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

weird, that's not new code, it has been there since kolibri 0.11

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it is more likely a floating linter version issue than an issue with the CI setup.

if version_info:
version = version_info[0].split(":")[1].strip()
return version
except CalledProcessError: # package not installed!
pass # will return None
return None

def get_deb_kolibriserver_version():
return get_debian_pkg_version("kolibri-server")

def get_deb_version():
return get_debian_pkg_version("kolibri")

def get_apk_version():
return os.environ.get("KOLIBRI_APK_VERSION_NAME")

installer_version = os.environ.get("KOLIBRI_INSTALLER_VERSION")
if installer_version:
return installer_version

version_funcs = {
installation_types.DEB: get_deb_version,
installation_types.KOLIBRI_SERVER: get_deb_kolibriserver_version,
installation_types.APK: get_apk_version,
}

if installer_type in version_funcs:
return version_funcs[installer_type]()
else:
return None


def installation_type(cmd_line=None): # noqa:C901
"""
Tries to guess how the running kolibri server was installed

:returns: install_type is the type of detected installation
"""

install_type = os.environ.get("KOLIBRI_INSTALLATION_TYPE", "Unknown")

if cmd_line is None:
cmd_line = sys.argv
install_type = "Unknown"

def is_debian_package():
# find out if this is from the debian package
install_type = "dpkg"
install_type = installation_types.DEB
try:
check_output(["apt-cache", "show", "kolibri"])
apt_repo = str(check_output(["apt-cache", "madison", "kolibri"]))
if len(apt_repo) > 4: # repo will have at least http
install_type = "apt"
check_output(["dpkg", "-s", "kolibri"])
except (
CalledProcessError,
FileNotFoundError,
): # kolibri package not installed!
if sys.path[-1] != "/usr/lib/python3/dist-packages":
install_type = "whl"
install_type = installation_types.WHL
return install_type

def is_kolibri_server():
# running under uwsgi, finding out if we are using kolibri-server
install_type = ""
install_type = "Unknown"
try:
package_info = (
check_output(["apt-cache", "show", "kolibri-server"])
.decode("utf-8")
.split("\n")
)
version = [output for output in package_info if "Version" in output]
install_type = "kolibri-server {}".format(version[0])
check_output(["dpkg", "-s", "kolibri-server"])
install_type = installation_types.KOLIBRI_SERVER
except CalledProcessError: # kolibri-server package not installed!
install_type = "uwsgi"
install_type = installation_types.WHL
return install_type

if len(cmd_line) > 1 or "uwsgi" in cmd_line:
launcher = cmd_line[0]
if launcher.endswith(".pex"):
install_type = "pex"
elif "runserver" in cmd_line:
install_type = "devserver"
elif launcher == "/usr/bin/kolibri":
install_type = is_debian_package()
elif launcher == "uwsgi":
package = is_debian_package()
if package != "whl":
kolibri_server = is_kolibri_server()
install_type = "kolibri({kolibri_type}) with {kolibri_server}".format(
kolibri_type=package, kolibri_server=kolibri_server
)
elif "\\Scripts\\kolibri" in launcher:
paths = sys.path
for path in paths:
if "kolibri.exe" in path:
install_type = "Windows"
break
elif "start" in cmd_line:
install_type = "whl"
if on_android():

version_name = os.environ.get("KOLIBRI_APK_VERSION_NAME")

if version_name:
install_type = "apk - {}".format(version_name)
# in case the KOLIBRI_INSTALLATION_TYPE is not set, let's use the old method:
if install_type == "Unknown":
if on_android():
install_type = installation_types.APK
elif len(cmd_line) > 1 or "uwsgi" in cmd_line:
launcher = cmd_line[0]
if launcher.endswith(".pex"):
install_type = installation_types.PEX
elif "runserver" in cmd_line:
install_type = "devserver"
elif launcher == "/usr/bin/kolibri":
install_type = is_debian_package()
elif launcher == "uwsgi":
package = is_debian_package()
if package != "whl":
install_type = is_kolibri_server()
elif "\\Scripts\\kolibri" in launcher:
paths = sys.path
for path in paths:
if "kolibri.exe" in path:
install_type = installation_types.WINDOWS
break
elif "start" in cmd_line:
install_type = installation_types.WHL

if install_type in installation_types.install_type_map:
version = get_installer_version(install_type)
if version:
return installation_types.install_type_map[install_type].format(version)
else:
install_type = "apk"
return installation_types.install_type_map[install_type].split(" - ")[0]

return install_type
22 changes: 17 additions & 5 deletions kolibri/utils/tests/test_server.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,14 @@
from kolibri.core.tasks.scheduler import Scheduler
from kolibri.core.tasks.test.base import connection
from kolibri.utils import server
from kolibri.utils.constants import installation_types


class TestServerInstallation(object):
@mock.patch("sys.argv", ["kolibri-0.9.3.pex", "start"])
def test_pex(self):
install_type = server.installation_type()
assert install_type == "pex"
assert install_type == installation_types.PEX

def test_dev(self):
sys_args = [
Expand All @@ -36,27 +37,38 @@ def test_dev(self):
assert install_type == "devserver"

@mock.patch("sys.argv", ["/usr/bin/kolibri", "start"])
@mock.patch("os.environ", {"KOLIBRI_INSTALLER_VERSION": "1.0"})
def test_dpkg(self):
with mock.patch("kolibri.utils.server.check_output", return_value=""):
install_type = server.installation_type()
assert install_type == "dpkg"
assert install_type == installation_types.install_type_map[
installation_types.DEB
].format("1.0")

@mock.patch("sys.argv", ["/usr/bin/kolibri", "start"])
@mock.patch("os.environ", {"KOLIBRI_INSTALLER_VERSION": "1.0"})
def test_apt(apt):
with mock.patch("kolibri.utils.server.check_output", return_value="any repo"):
install_type = server.installation_type()
assert install_type == "apt"
assert install_type == installation_types.install_type_map[
installation_types.DEB
].format("1.0")

@mock.patch("sys.argv", ["C:\\Python34\\Scripts\\kolibri", "start"])
@mock.patch("sys.path", ["", "C:\\Program Files\\Kolibri\\kolibri.exe"])
@mock.patch("os.environ", {"KOLIBRI_INSTALLER_VERSION": "1.0"})
def test_windows(self):
install_type = server.installation_type()
assert install_type == "Windows"
assert install_type == installation_types.install_type_map[
installation_types.WINDOWS
].format("1.0")

@mock.patch("sys.argv", ["/usr/local/bin/kolibri", "start"])
def test_whl(self):
install_type = server.installation_type()
assert install_type == "whl"
assert (
install_type == installation_types.install_type_map[installation_types.WHL]
)


@pytest.fixture
Expand Down