Implement App download and installation
This commit is contained in:
parent
31a973ee03
commit
cdfd0de2b6
@ -9,7 +9,8 @@ from spoc import repo_local
|
||||
from spoc import repo_online
|
||||
from spoc import repo_publish
|
||||
from spoc.app import App
|
||||
from spoc.cli import readable_size
|
||||
from spoc.cli import ActionQueue, readable_size
|
||||
from spoc.image import Image
|
||||
|
||||
def listing(list_type):
|
||||
if list_type == 'installed':
|
||||
@ -24,23 +25,33 @@ def listing(list_type):
|
||||
for app in apps:
|
||||
print(app)
|
||||
|
||||
def install():
|
||||
raise NotImplementedException()
|
||||
def install(app_name):
|
||||
queue = ActionQueue()
|
||||
required_images = []
|
||||
for container in repo_online.get_app(app_name)['containers'].values():
|
||||
required_images.extend(repo_online.get_image(container['image'])['layers'])
|
||||
local_images = repo_local.get_images()
|
||||
for layer in set(required_images):
|
||||
if layer not in local_images:
|
||||
queue.download_image(Image(layer, False))
|
||||
queue.download_app(App(app_name, False))
|
||||
queue.process()
|
||||
|
||||
def upgrade():
|
||||
raise NotImplementedException()
|
||||
def update(app_name):
|
||||
App(app_name).update()
|
||||
|
||||
def uninstall():
|
||||
raise NotImplementedException()
|
||||
def uninstall(app_name):
|
||||
App(app_name).uninstall()
|
||||
|
||||
def start():
|
||||
raise NotImplementedException()
|
||||
def start(app_name):
|
||||
App(app_name).start()
|
||||
|
||||
def stop():
|
||||
raise NotImplementedException()
|
||||
def stop(app_name):
|
||||
App(app_name).stop()
|
||||
|
||||
def status():
|
||||
raise NotImplementedException()
|
||||
def status(app_name):
|
||||
for container,status in App(app_name).status():
|
||||
print(f'{container}: {status}')
|
||||
|
||||
def publish(filename, force):
|
||||
app_name = os.path.basename(os.path.dirname(os.path.abspath(filename)))
|
||||
@ -69,9 +80,9 @@ parser_install = subparsers.add_parser('install')
|
||||
parser_install.set_defaults(action=install)
|
||||
parser_install.add_argument('app')
|
||||
|
||||
parser_upgrade = subparsers.add_parser('upgrade')
|
||||
parser_upgrade.set_defaults(action=upgrade)
|
||||
parser_upgrade.add_argument('app')
|
||||
parser_update = subparsers.add_parser('update')
|
||||
parser_update.set_defaults(action=update)
|
||||
parser_update.add_argument('app')
|
||||
|
||||
parser_uninstall = subparsers.add_parser('uninstall')
|
||||
parser_uninstall.set_defaults(action=uninstall)
|
||||
@ -104,8 +115,8 @@ if args.action is listing:
|
||||
listing(args.type)
|
||||
elif args.action is install:
|
||||
install(args.app)
|
||||
elif args.action is upgrade:
|
||||
upgrade(args.app)
|
||||
elif args.action is update:
|
||||
update(args.app)
|
||||
elif args.action is uninstall:
|
||||
uninstall(args.app)
|
||||
elif args.action is start:
|
||||
|
@ -2,12 +2,16 @@
|
||||
|
||||
import json
|
||||
import os
|
||||
import subprocess
|
||||
import tarfile
|
||||
import urllib.parse
|
||||
|
||||
from . import repo_local
|
||||
from . import repo_online
|
||||
from . import repo_publish
|
||||
from .config import PUB_APPS_DIR
|
||||
from .config import APPS_DIR, ONLINE_APPS_URL, PUB_APPS_DIR, TMP_APPS_DIR, LAYERS_DIR, VOLUMES_DIR
|
||||
from .container import Container
|
||||
from .image import Image
|
||||
|
||||
class App:
|
||||
def __init__(self, name, load_from_repo=True):
|
||||
@ -30,6 +34,74 @@ class App:
|
||||
'containers': [container.name for container in self.containers]
|
||||
}
|
||||
|
||||
def download(self, observer):
|
||||
os.makedirs(TMP_APPS_DIR, 0o700, True)
|
||||
archive_url = urllib.parse.urljoin(ONLINE_APPS_URL, f'{self.name}.tar.xz')
|
||||
archive_path = os.path.join(TMP_APPS_DIR, f'{self.name}.tar.xz')
|
||||
definition = repo_online.get_app(self.name)
|
||||
observer.units_total = definition['dlsize']
|
||||
repo_online.download_archive(archive_url, archive_path, definition['hash'], observer)
|
||||
|
||||
def unpack_downloaded(self, observer):
|
||||
archive_path = os.path.join(TMP_APPS_DIR, f'{self.name}.tar.xz')
|
||||
definition = repo_online.get_app(self.name)
|
||||
observer.units_total = definition['size']
|
||||
repo_online.unpack_archive(archive_path, APPS_DIR, definition['hash'], observer)
|
||||
|
||||
def run_script(self, action):
|
||||
# Runs script for an app, if the script is present
|
||||
app_dir = os.path.join(APPS_DIR, self.name)
|
||||
script_dir = os.path.join(app_dir, action)
|
||||
script_path = os.path.join(app_dir, f'{script_dir}.sh')
|
||||
if os.path.exists(script_path):
|
||||
# Run the script in its working directory, if there is one, so it doesn't have to figure out paths to packaged files
|
||||
env = os.environ.copy()
|
||||
env['LAYERS_DIR'] = LAYERS_DIR
|
||||
env['VOLUMES_DIR'] = VOLUMES_DIR
|
||||
cwd = script_dir if os.path.exists(script_dir) else app_dir
|
||||
subprocess.run(script_path, cwd=cwd, env=env, check=True)
|
||||
|
||||
def create_container(self, name, definition):
|
||||
container = Container(name, False)
|
||||
container.set_definition(Image(definition['image']).get_definition())
|
||||
if 'depends' in definition:
|
||||
container.depends = definition['depends']
|
||||
if 'env' in definition:
|
||||
container.env.update(definition['env'])
|
||||
if 'mounts' in definition:
|
||||
container.mounts.update(definition['mounts'])
|
||||
container.create()
|
||||
self.containers.append(container)
|
||||
|
||||
def install(self):
|
||||
definition = repo_online.get_app(self.name)
|
||||
self.version = definition['version']
|
||||
self.meta = definition['meta']
|
||||
self.run_script('uninstall')
|
||||
# Build containers
|
||||
for container,container_defintion in definition['containers'].items():
|
||||
self.create_container(container, container_defintion)
|
||||
# Run install script and register the app
|
||||
self.run_script('install')
|
||||
repo_local.register_app(self.name, self.get_definition())
|
||||
|
||||
def update(self):
|
||||
raise NotImplementedError()
|
||||
|
||||
def uninstall(self):
|
||||
raise NotImplementedError()
|
||||
|
||||
def start(self):
|
||||
for container in self.containers:
|
||||
container.start()
|
||||
|
||||
def stop(self):
|
||||
for container in self.containers:
|
||||
container.stop()
|
||||
|
||||
def status(self):
|
||||
return {container.name:container.get_state() for container in sorted(self.containers)}
|
||||
|
||||
def publish(self, filename):
|
||||
builddir = os.path.dirname(filename)
|
||||
os.makedirs(PUB_APPS_DIR, 0o700, True)
|
||||
|
@ -17,6 +17,11 @@ class ActionQueue:
|
||||
def delete_image(self, image):
|
||||
self.queue.append(ActionItem(f'Deleting image {image.name}', image.delete, False))
|
||||
|
||||
def download_app(self, app):
|
||||
self.queue.append(ActionItem(f'Downloading application {app.name}', app.download))
|
||||
self.queue.append(ActionItem(f'Unpacking application {app.name}', app.unpack_downloaded))
|
||||
self.queue.append(ActionItem(f'Installing application {app.name}', app.install, False))
|
||||
|
||||
def process(self):
|
||||
index = 0
|
||||
queue_length = len(self.queue)
|
||||
|
@ -9,7 +9,7 @@ import urllib.parse
|
||||
from . import repo_local
|
||||
from . import repo_online
|
||||
from . import repo_publish
|
||||
from .config import LAYERS_DIR, PUB_LAYERS_DIR, ONLINE_LAYERS_URL, TMP_LAYERS_DIR
|
||||
from .config import LAYERS_DIR, ONLINE_LAYERS_URL, PUB_LAYERS_DIR, TMP_LAYERS_DIR
|
||||
|
||||
DEFINITION_MEMBERS = {'layers', 'env', 'uid', 'gid', 'cmd', 'cwd', 'ready', 'halt'}
|
||||
|
||||
@ -56,6 +56,22 @@ class Image:
|
||||
except FileNotFoundError:
|
||||
pass
|
||||
|
||||
def download(self, observer):
|
||||
os.makedirs(TMP_LAYERS_DIR, 0o700, True)
|
||||
archive_url = urllib.parse.urljoin(ONLINE_LAYERS_URL, f'{self.name}.tar.xz')
|
||||
archive_path = os.path.join(TMP_LAYERS_DIR, f'{self.name}.tar.xz')
|
||||
definition = repo_online.get_image(self.name)
|
||||
observer.units_total = definition['dlsize']
|
||||
repo_online.download_archive(archive_url, archive_path, definition['hash'], observer)
|
||||
|
||||
def unpack_downloaded(self, observer):
|
||||
archive_path = os.path.join(TMP_LAYERS_DIR, f'{self.name}.tar.xz')
|
||||
definition = repo_online.get_image(self.name)
|
||||
observer.units_total = definition['size']
|
||||
repo_online.unpack_archive(archive_path, LAYERS_DIR, definition['hash'], observer)
|
||||
self.set_definition(definition)
|
||||
repo_local.register_image(self.name, definition)
|
||||
|
||||
def publish(self):
|
||||
os.makedirs(PUB_LAYERS_DIR, 0o700, True)
|
||||
files = repo_publish.TarSizeCounter()
|
||||
@ -76,19 +92,3 @@ class Image:
|
||||
os.unlink(archive_path)
|
||||
except FileNotFoundError:
|
||||
pass
|
||||
|
||||
def download(self, observer):
|
||||
os.makedirs(TMP_LAYERS_DIR, 0o700, True)
|
||||
definition = repo_online.get_image(self.name)
|
||||
observer.units_total = definition['dlsize']
|
||||
archive_url = urllib.parse.urljoin(ONLINE_LAYERS_URL, f'{self.name}.tar.xz')
|
||||
archive_path = os.path.join(TMP_LAYERS_DIR, f'{self.name}.tar.xz')
|
||||
repo_online.download_archive(archive_url, archive_path, definition['hash'], observer)
|
||||
|
||||
def unpack_downloaded(self, observer):
|
||||
definition = repo_online.get_image(self.name)
|
||||
observer.units_total = definition['size']
|
||||
archive_path = os.path.join(TMP_LAYERS_DIR, f'{self.name}.tar.xz')
|
||||
repo_online.unpack_archive(archive_path, LAYERS_DIR, definition['hash'], observer)
|
||||
self.set_definition(definition)
|
||||
repo_local.register_image(self.name, definition)
|
||||
|
Loading…
Reference in New Issue
Block a user