openmedialibrary/oml/update.py

170 lines
6.6 KiB
Python

# -*- coding: utf-8 -*-
# vi:si:et:sw=4:sts=4:ts=4
from contextlib import closing
import json
import os
import tarfile
import urllib.request, urllib.error, urllib.parse
import shutil
import subprocess
import ed25519
import ox
from oxtornado import actions
import settings
ENCODING='base64'
def verify(release):
vk = ed25519.VerifyingKey(settings.OML_UPDATE_KEY, encoding=ENCODING)
value = []
for module in sorted(release['modules']):
value += [str('%s/%s' % (release['modules'][module]['version'], release['modules'][module]['sha1']))]
value = '\n'.join(value)
value = value.encode()
sig = release['signature'].encode()
try:
vk.verify(sig, value, encoding=ENCODING)
except ed25519.BadSignatureError:
return False
return True
def get(url, filename=None):
request = urllib.request.Request(url, headers={
'User-Agent': settings.USER_AGENT
})
with closing(urllib.request.urlopen(request)) as u:
if not filename:
data = u.read()
return data
else:
dirname = os.path.dirname(filename)
if dirname and not os.path.exists(dirname):
os.makedirs(dirname)
with open(filename, 'wb') as fd:
data = u.read(4096)
while data:
fd.write(data)
data = u.read(4096)
def check():
if settings.release:
release_data = get(settings.server.get('release_url'))
release = json.loads(release_data.decode('utf-8'))
old = settings.release['modules']['openmedialibrary']['version']
new = release['modules']['openmedialibrary']['version']
return verify(release) and old < new
return False
def download():
if not os.path.exists(os.path.join(settings.config_path, 'release.json')):
return True
release_data = get(settings.server.get('release_url'))
release = json.loads(release_data.decode('utf-8'))
if verify(release):
ox.makedirs(settings.updates_path)
os.chdir(os.path.dirname(settings.base_dir))
current_files = {'release.json'}
for module in release['modules']:
if release['modules'][module]['version'] > settings.release['modules'][module]['version']:
module_tar = os.path.join(settings.updates_path, release['modules'][module]['name'])
base_url = settings.server.get('release_url').rsplit('/', 1)[0]
url = '/'.join([base_url, release['modules'][module]['name']])
if not os.path.exists(module_tar):
print('downloading', os.path.basename(module_tar))
get(url, module_tar)
if ox.sha1sum(module_tar) != release['modules'][module]['sha1']:
os.unlink(module_tar)
return False
current_files.add(os.path.basename(module_tar))
with open(os.path.join(settings.updates_path, 'release.json'), 'wb') as fd:
fd.write(release_data)
for f in set(next(os.walk(settings.updates_path))[2])-current_files:
os.unlink(os.path.join(settings.updates_path, f))
return True
return True
def install():
if not os.path.exists(os.path.join(settings.updates_path, 'release.json')):
return True
if not os.path.exists(os.path.join(settings.config_path, 'release.json')):
return True
with open(os.path.join(settings.updates_path, 'release.json')) as fd:
release = json.load(fd)
old = settings.release['modules']['openmedialibrary']['version']
new = release['modules']['openmedialibrary']['version']
if verify(release) and old < new:
os.chdir(os.path.dirname(settings.base_dir))
for module in release['modules']:
if release['modules'][module]['version'] > settings.release['modules'][module]['version']:
module_tar = os.path.join(settings.updates_path, release['modules'][module]['name'])
if os.path.exists(module_tar) and ox.sha1sum(module_tar) == release['modules'][module]['sha1']:
#tar fails if old platform is moved before extract
new = '%s_new' % module
ox.makedirs(new)
os.chdir(new)
tar = tarfile.open(module_tar)
tar.extractall()
tar.close()
os.chdir(os.path.dirname(settings.base_dir))
shutil.move(module, '%s_old' % module)
shutil.move(os.path.join(new, module), module)
shutil.rmtree('%s_old' % module)
shutil.rmtree(new)
else:
os.unlink(module_tar)
return False
shutil.copy(os.path.join(settings.updates_path, 'release.json'), os.path.join(settings.config_path, 'release.json'))
for cmd in [
['./ctl', 'stop'],
['./ctl', 'setup'],
['./ctl', 'postupdate', '-o', old, '-n', new]
]:
subprocess.call(cmd)
return True
return True
def getVersion(data):
'''
check if new version is available
'''
response = {
'current': settings.MINOR_VERSION,
'upgrade': False,
}
if settings.MINOR_VERSION == 'git':
cmd = ['git', 'rev-parse', '@']
p = subprocess.Popen(cmd, stdout=subprocess.PIPE, close_fds=True)
stdout, stderr = p.communicate()
current = stdout.strip()
cmd = ['git', 'ls-remote', 'origin', '-h', 'refs/heads/master']
p = subprocess.Popen(cmd, stdout=subprocess.PIPE, close_fds=True)
stdout, stderr = p.communicate()
new = stdout.strip()[:40]
response['update'] = current != new
else:
if not os.path.exists(os.path.join(settings.updates_path, 'release.json')):
return response
if not os.path.exists(os.path.join(settings.config_path, 'release.json')):
return response
with open(os.path.join(settings.updates_path, 'release.json')) as fd:
release = json.load(fd)
current = settings.release['modules']['openmedialibrary']['version']
response['current'] = current
new = release['modules']['openmedialibrary']['version']
response['new'] = new
response['update'] = current < new
return response
actions.register(getVersion, cache=False)
def restart(data):
'''
restart (and upgrade if upgrades are available)
'''
subprocess.Popen([os.path.join(settings.base_dir, 'ctl'), 'restart'], close_fds=True)
return {}
actions.register(restart, cache=False)