diff --git a/usr/lib/python3.6/vmmgr/actionqueue.py b/usr/lib/python3.6/vmmgr/actionqueue.py
index de3376f..c8366ee 100644
--- a/usr/lib/python3.6/vmmgr/actionqueue.py
+++ b/usr/lib/python3.6/vmmgr/actionqueue.py
@@ -57,7 +57,7 @@ class ActionQueue:
# If the action finished without errors, restore nominal state by deleting the item from action list
self.clear_action(item.key)
except BaseException as e:
- # If the action failed, store the exception and leave it in the list form manual clearance
+ # If the action failed, store the exception and leave it in the list for manual clearance
with self.lock:
item.data = e
diff --git a/usr/lib/python3.6/vmmgr/appmgr.py b/usr/lib/python3.6/vmmgr/appmgr.py
index 1c79d2c..147fcee 100644
--- a/usr/lib/python3.6/vmmgr/appmgr.py
+++ b/usr/lib/python3.6/vmmgr/appmgr.py
@@ -1,20 +1,12 @@
# -*- coding: utf-8 -*-
import json
+import math
import os
import requests
import subprocess
import time
-class InstallItem:
- def __init__(self):
- self.bytes_total = 1
- self.bytes_downloaded = 0
-
- @property
- def percent_downloaded(self):
- return round(self.bytes_downloaded / self.bytes_total * 100)
-
class AppMgr:
def __init__(self, conf):
self.conf = conf
@@ -64,26 +56,22 @@ class AppMgr:
def install_app(self, item):
# Main installation function. Wrapper for installation via native package manager
- item.data = InstallItem()
- # Alpine apk provides machine-readable progress in bytes_downloaded/bytes_total format output to file descriptor of choice
+ item.data = 0
+ # Alpine apk provides machine-readable progress in bytes_downloaded/bytes_total format output to file descriptor of choice, so create a pipe for it
pipe_rfd, pipe_wfd = os.pipe()
- with subprocess.Popen(['apk', '--progress-fd', str(pipe_wfd), '--no-cache', 'add', 'vm-{}@vm'.format(item.key)], pass_fds=[pipe_wfd]) as p:
- while p.poll() == None:
- time.sleep(0.1)
- # Read pipe
- data = b''
- while True:
- chunk = os.read(pipe_rfd, 8192)
- data += chunk
- if len(chunk) < 8192:
- break
- # Parse last apk progress line
- progress = data.decode().splitlines()[-1].split('/')
- item.data.bytes_downloaded = progress[0]
- item.data.bytes_total = progress[1]
- # Close pipe
- os.close(pipe_rfd)
- os.close(pipe_wfd)
+ with os.fdopen(pipe_rfd) as pipe_rf:
+ with subprocess.Popen(['apk', '--progress-fd', str(pipe_wfd), '--no-cache', 'add', 'vm-{}@vm'.format(item.key)], pass_fds=[pipe_wfd]) as p:
+ # Close write pipe for vmmgr to not block the pipe once apk finishes
+ os.close(pipe_wfd)
+ while p.poll() == None:
+ # Wait for line end or EOF in read pipe and process it
+ data = pipe_rf.readline()
+ if data:
+ progress = data.rstrip().split('/')
+ item.data = math.floor(int(progress[0]) / int(progress[1]) * 100)
+ # If the apk command didn't finish with returncode 0, raise an exception
+ if p.returncode:
+ raise CalledProcessError(p.returncode, cmd)
def uninstall_app(self, item):
# Main uninstallation function. Wrapper for uninstallation via native package manager
@@ -93,16 +81,18 @@ class AppMgr:
self.update_app_autostart(app, False)
if name in self.conf['apps']:
del self.conf['apps'][name]
- subprocess.run(['apk', '--no-cache', 'del', 'vm-{}@vm'.format(app)])
+ subprocess.run(['apk', '--no-cache', 'del', 'vm-{}@vm'.format(app)], check=True)
def fetch_online_packages(self, repo_conf):
# Fetches list of online packages
auth = (repo_conf['user'], repo_conf['pwd']) if repo_conf['user'] else None
- packages = requests.get('{}/packages.json'.format(repo_conf['url']), auth=auth, timeout=5)
- if packages.status_code != 200:
- return packages.status_code
- self.online_packages = json.loads(packages.content)
- return 200
+ try:
+ packages = requests.get('{}/packages.json'.format(repo_conf['url']), auth=auth, timeout=5)
+ except:
+ return 0
+ if packages.status_code == 200:
+ self.online_packages = json.loads(packages.content)
+ return packages.status_code
def get_services_deps(self):
# Fisrt, build a dictionary of {app: [needs]}
diff --git a/usr/lib/python3.6/vmmgr/wsgiapp.py b/usr/lib/python3.6/vmmgr/wsgiapp.py
index 2c44dcf..acd53b6 100644
--- a/usr/lib/python3.6/vmmgr/wsgiapp.py
+++ b/usr/lib/python3.6/vmmgr/wsgiapp.py
@@ -164,11 +164,8 @@ class WSGIApp:
# Application manager view.
repo_error = None
repo_conf = self.vmmgr.get_repo_conf()
- try:
- status = self.appmgr.fetch_online_packages(repo_conf)
- except InvalidSignature:
- repo_error = request.session.lang.invalid_packages_signature()
- if status in (401, 403):
+ status = self.appmgr.fetch_online_packages(repo_conf)
+ if status == 401:
repo_error = request.session.lang.repo_invalid_credentials()
elif status != 200:
repo_error = request.session.lang.repo_unavailable()
@@ -212,10 +209,8 @@ class WSGIApp:
status = '{} OK'.format(lang.package_manager_error())
actions = None
else:
- if item.data.stage == 0:
+ if item.data < 100:
status = '{} ({} %)'.format(lang.status_downloading(), item.data)
- elif item.data.stage == 1:
- status = lang.status_installing_deps()
else:
status = lang.status_installing()
elif item.action == self.appmgr.uninstall_app:
diff --git a/usr/lib/python3.6/vmmgr/wsgilang.py b/usr/lib/python3.6/vmmgr/wsgilang.py
index b023bd6..37bb726 100644
--- a/usr/lib/python3.6/vmmgr/wsgilang.py
+++ b/usr/lib/python3.6/vmmgr/wsgilang.py
@@ -23,7 +23,6 @@ class WSGILang:
'stop_start_error': 'Došlo k chybě při spouštění/zastavování. Zkuste akci opakovat nebo restartuje virtuální stroj.',
'installation_in_progress': 'Probíhá instalace jiného balíku. Vyčkejte na její dokončení.',
'package_manager_error': 'Došlo k chybě při instalaci aplikace. Zkuste akci opakovat nebo restartuje virtuální stroj.',
- 'invalid_packages_signature': 'Digitální podpis seznamu balíků není platný. Kontaktujte správce distribučního serveru.',
'repo_invalid_credentials': 'Přístupové údaje k distribučnímu serveru nejsou správné.',
'repo_unavailable': 'Distribuční server není dostupný. Zkontroluje připojení k síti',
'bad_password': 'Nesprávné heslo',
@@ -38,7 +37,6 @@ class WSGILang:
'status_stopping': 'Zastavuje se',
'status_stopped': 'Zastavena',
'status_downloading': 'Stahuje se',
- 'status_installing_deps': 'Instalují se závislosti',
'status_installing': 'Instaluje se',
'status_uninstalling': 'Odinstalovává se',
'status_not_installed': 'Není nainstalována',