Add support for installation from definition file

This commit is contained in:
Disassembler 2022-01-02 10:15:27 +01:00
parent d2e17c8d49
commit c2cd5b12a0
No known key found for this signature in database
GPG Key ID: 524BD33A0EE29499
6 changed files with 81 additions and 39 deletions

View File

@ -40,20 +40,20 @@ def list_updates():
return dict(sorted(apps.items())) return dict(sorted(apps.items()))
@locked() @locked()
def install(app_name): def install(app_name, from_file=None):
if app_name in podman.get_apps(): if app_name in podman.get_apps():
raise AppAlreadyInstalledError(app_name) raise AppAlreadyInstalledError(app_name)
if app_name not in repo.get_apps(): if not from_file and app_name not in repo.get_apps():
raise AppNotInRepoError(app_name) raise AppNotInRepoError(app_name)
app.install(app_name) app.install(app_name, from_file=from_file)
@locked() @locked()
def update(app_name): def update(app_name, from_file=None):
if app_name not in podman.get_apps(): if app_name not in podman.get_apps():
raise AppNotInstalledError(app_name) raise AppNotInstalledError(app_name)
if app_name not in list_updates(): if not from_file and app_name not in list_updates():
raise AppNotUpdateableError(app_name) raise AppNotUpdateableError(app_name)
app.update(app_name) app.update(app_name, from_file=from_file)
@locked() @locked()
def uninstall(app_name): def uninstall(app_name):

View File

@ -1,4 +1,5 @@
import os import os
import json
from . import autostart from . import autostart
from . import config from . import config
@ -11,8 +12,14 @@ class App:
self.app_name = app_name self.app_name = app_name
self.env_file = os.path.join(config.DATA_DIR, f'{app_name}.env') self.env_file = os.path.join(config.DATA_DIR, f'{app_name}.env')
def install(self, is_update=False): def get_definition(self, from_file=None):
definition = repo.get_apps()[self.app_name] if from_file:
with open(from_file, encoding='utf-8') as f:
return json.load(f)
return repo.get_apps()[self.app_name]
def install(self, is_update=False, from_file=None):
definition = self.get_definition(from_file)
version = definition['version'] version = definition['version']
containers = definition['containers'] containers = definition['containers']
@ -44,8 +51,8 @@ class App:
self.create_pod(version) self.create_pod(version)
self.create_containers(containers) self.create_containers(containers)
def update(self): def update(self, from_file=None):
self.install(is_update=True) self.install(is_update=True, from_file=from_file)
def uninstall(self): def uninstall(self):
autostart.set_app(self.app_name, False) autostart.set_app(self.app_name, False)
@ -124,11 +131,11 @@ class App:
podman.create_container(self.app_name, name, image, env_file=self.env_file, podman.create_container(self.app_name, name, image, env_file=self.env_file,
volumes=volumes, requires=requires, hosts=hosts) volumes=volumes, requires=requires, hosts=hosts)
def install(app_name): def install(app_name, from_file=None):
App(app_name).install() App(app_name).install(from_file=from_file)
def update(app_name): def update(app_name, from_file=None):
App(app_name).update() App(app_name).update(from_file=from_file)
def uninstall(app_name): def uninstall(app_name):
App(app_name).uninstall() App(app_name).uninstall()

View File

@ -42,11 +42,11 @@ def listing(list_type):
for app_name, app_version in apps.items(): for app_name, app_version in apps.items():
print(app_name, app_version) print(app_name, app_version)
def install(app_name): def install(app_name, from_file):
spoc.install(app_name) spoc.install(app_name, from_file)
def update(app_name): def update(app_name, from_file):
spoc.update(app_name) spoc.update(app_name, from_file)
def uninstall(app_name): def uninstall(app_name):
spoc.uninstall(app_name) spoc.uninstall(app_name)
@ -93,10 +93,16 @@ def parse_args(args=None):
parser_install = subparsers.add_parser('install') parser_install = subparsers.add_parser('install')
parser_install.set_defaults(action=install) parser_install.set_defaults(action=install)
parser_install.add_argument('app', help='Name of the application to install') parser_install.add_argument('app', help='Name of the application to install')
parser_install.add_argument('--from-file',
help='Filename containing the application definition ' \
'to be used instead of online repository')
parser_update = subparsers.add_parser('update') parser_update = subparsers.add_parser('update')
parser_update.set_defaults(action=update) parser_update.set_defaults(action=update)
parser_update.add_argument('app', help='Name of the application to update') parser_update.add_argument('app', help='Name of the application to update')
parser_update.add_argument('--from-file',
help='Filename containing the application definition ' \
'to be used instead of online repository')
parser_uninstall = subparsers.add_parser('uninstall') parser_uninstall = subparsers.add_parser('uninstall')
parser_uninstall.set_defaults(action=uninstall) parser_uninstall.set_defaults(action=uninstall)
@ -143,9 +149,9 @@ def main(): # pylint: disable=too-many-branches
if args.action is listing: if args.action is listing:
listing(args.type) listing(args.type)
elif args.action is install: elif args.action is install:
install(args.app) install(args.app, args.from_file)
elif args.action is update: elif args.action is update:
update(args.app) update(args.app, args.from_file)
elif args.action is uninstall: elif args.action is uninstall:
uninstall(args.app) uninstall(args.app)
elif args.action is start: elif args.action is start:

View File

@ -28,6 +28,26 @@ def test_init():
assert instance.env_file == os.path.join(config.DATA_DIR, 'someapp.env') assert instance.env_file == os.path.join(config.DATA_DIR, 'someapp.env')
@patch('spoc.repo.get_apps', return_value=MOCK_REPODATA) @patch('spoc.repo.get_apps', return_value=MOCK_REPODATA)
def test_get_definition(repo_get_apps):
instance = app.App('someapp')
definition = instance.get_definition()
assert definition == MOCK_REPODATA['someapp']
repo_get_apps.assert_called_once()
@patch('spoc.repo.get_apps')
@patch('builtins.open', new_callable=mock_open, read_data=json.dumps(MOCK_REPODATA['someapp']))
def test_get_definition_from_file(file_open, repo_get_apps):
instance = app.App('someapp')
definition = instance.get_definition('somefile')
assert definition == MOCK_REPODATA['someapp']
file_open.assert_called_once_with('somefile', encoding='utf-8')
repo_get_apps.assert_not_called()
@patch('spoc.app.App.get_definition', return_value=MOCK_REPODATA['someapp'])
@patch('spoc.app.App.get_existing_volumes', return_value=set('somevol')) @patch('spoc.app.App.get_existing_volumes', return_value=set('somevol'))
@patch('spoc.app.App.remove_volumes') @patch('spoc.app.App.remove_volumes')
@patch('spoc.app.App.create_volumes') @patch('spoc.app.App.create_volumes')
@ -37,11 +57,11 @@ def test_init():
@patch('spoc.app.App.create_containers') @patch('spoc.app.App.create_containers')
def test_install(create_containers, create_pod, write_env_vars, #pylint: disable=too-many-arguments def test_install(create_containers, create_pod, write_env_vars, #pylint: disable=too-many-arguments
read_env_vars, create_volumes, remove_volumes, read_env_vars, create_volumes, remove_volumes,
get_existing_volumes, repo_get_apps): get_existing_volumes, get_definition):
instance = app.App('someapp') instance = app.App('someapp')
instance.install() instance.install()
repo_get_apps.assert_called_once() get_definition.assert_called_once()
get_existing_volumes.assert_called_once() get_existing_volumes.assert_called_once()
remove_volumes.assert_called_once_with(set('somevol')) remove_volumes.assert_called_once_with(set('somevol'))
create_volumes.assert_called_once_with(set(('migrate', 'storage', 'uploads', 'postgres-data'))) create_volumes.assert_called_once_with(set(('migrate', 'storage', 'uploads', 'postgres-data')))
@ -50,7 +70,7 @@ def test_install(create_containers, create_pod, write_env_vars, #pylint: disable
create_pod.assert_called_once_with('0.23.5-210416') create_pod.assert_called_once_with('0.23.5-210416')
create_containers.assert_called_once_with(MOCK_REPODATA['someapp']['containers']) create_containers.assert_called_once_with(MOCK_REPODATA['someapp']['containers'])
@patch('spoc.repo.get_apps', return_value=MOCK_REPODATA) @patch('spoc.app.App.get_definition', return_value=MOCK_REPODATA['someapp'])
@patch('spoc.app.App.get_existing_volumes', return_value=set(('somevol', 'migrate', 'storage'))) @patch('spoc.app.App.get_existing_volumes', return_value=set(('somevol', 'migrate', 'storage')))
@patch('spoc.app.App.remove_volumes') @patch('spoc.app.App.remove_volumes')
@patch('spoc.app.App.create_volumes') @patch('spoc.app.App.create_volumes')
@ -60,11 +80,11 @@ def test_install(create_containers, create_pod, write_env_vars, #pylint: disable
@patch('spoc.app.App.create_containers') @patch('spoc.app.App.create_containers')
def test_update(create_containers, create_pod, write_env_vars, #pylint: disable=too-many-arguments def test_update(create_containers, create_pod, write_env_vars, #pylint: disable=too-many-arguments
read_env_vars, create_volumes, remove_volumes, read_env_vars, create_volumes, remove_volumes,
get_existing_volumes, repo_get_apps): get_existing_volumes, get_definition):
instance = app.App('someapp') instance = app.App('someapp')
instance.update() instance.update(from_file='somefile')
repo_get_apps.assert_called_once() get_definition.assert_called_once()
get_existing_volumes.assert_called_once() get_existing_volumes.assert_called_once()
remove_volumes.assert_called_once_with(set(('somevol',))) remove_volumes.assert_called_once_with(set(('somevol',)))
create_volumes.assert_called_once_with(set(('uploads', 'postgres-data'))) create_volumes.assert_called_once_with(set(('uploads', 'postgres-data')))
@ -229,14 +249,14 @@ def test_module_install(instance):
app.install('someapp') app.install('someapp')
instance.assert_called_once_with('someapp') instance.assert_called_once_with('someapp')
instance.return_value.install.assert_called_once() instance.return_value.install.assert_called_once_with(from_file=None)
@patch('spoc.app.App') @patch('spoc.app.App')
def test_module_update(instance): def test_module_update(instance):
app.update('someapp') app.update('someapp', from_file='somefile')
instance.assert_called_once_with('someapp') instance.assert_called_once_with('someapp')
instance.return_value.update.assert_called_once_with() instance.return_value.update.assert_called_once_with(from_file='somefile')
@patch('spoc.app.App') @patch('spoc.app.App')
def test_module_uninstall(instance): def test_module_uninstall(instance):

View File

@ -97,15 +97,15 @@ def test_listing_invalid(list_updates, list_online, list_installed, capsys):
@patch('spoc.install') @patch('spoc.install')
def test_install(install): def test_install(install):
spoc_cli.install('someapp') spoc_cli.install('someapp', 'somefile')
install.assert_called_once_with('someapp') install.assert_called_once_with('someapp', 'somefile')
@patch('spoc.update') @patch('spoc.update')
def test_update(update): def test_update(update):
spoc_cli.update('someapp') spoc_cli.update('someapp', None)
update.assert_called_once_with('someapp') update.assert_called_once_with('someapp', None)
@patch('spoc.uninstall') @patch('spoc.uninstall')
def test_uninstall(uninstall): def test_uninstall(uninstall):
@ -212,14 +212,14 @@ def test_main_listing_online(listing):
def test_main_install(install): def test_main_install(install):
spoc_cli.main() spoc_cli.main()
install.assert_called_once_with('someapp') install.assert_called_once_with('someapp', None)
@patch('sys.argv', ['foo', 'update', 'someapp']) @patch('sys.argv', ['foo', 'update', '--from-file', 'somefile', 'someapp'])
@patch('spoc_cli.update') @patch('spoc_cli.update')
def test_main_update(update): def test_main_update(update):
spoc_cli.main() spoc_cli.main()
update.assert_called_once_with('someapp') update.assert_called_once_with('someapp', 'somefile')
@patch('sys.argv', ['foo', 'uninstall', 'someapp']) @patch('sys.argv', ['foo', 'uninstall', 'someapp'])
@patch('spoc_cli.uninstall') @patch('spoc_cli.uninstall')

View File

@ -66,11 +66,20 @@ def test_list_updates(podman_get_apps, repo_get_apps):
@patch('spoc.app.install') @patch('spoc.app.install')
def test_install(app_install, podman_get_apps, repo_get_apps): def test_install(app_install, podman_get_apps, repo_get_apps):
spoc.install.__wrapped__('someapp') spoc.install.__wrapped__('someapp')
#spoc.install('someapp')
podman_get_apps.assert_called_once() podman_get_apps.assert_called_once()
repo_get_apps.assert_called_once() repo_get_apps.assert_called_once()
app_install.assert_called_once_with('someapp') app_install.assert_called_once_with('someapp', from_file=None)
@patch('spoc.repo.get_apps')
@patch('spoc.podman.get_apps', return_value={})
@patch('spoc.app.install')
def test_install_from_file(app_install, podman_get_apps, repo_get_apps):
spoc.install.__wrapped__('someapp', 'somefile')
podman_get_apps.assert_called_once()
repo_get_apps.assert_not_called()
app_install.assert_called_once_with('someapp', from_file='somefile')
@patch('spoc.repo.get_apps', return_value={'someapp': {'version': '0.1'}}) @patch('spoc.repo.get_apps', return_value={'someapp': {'version': '0.1'}})
@patch('spoc.podman.get_apps', return_value={'someapp': '0.1'}) @patch('spoc.podman.get_apps', return_value={'someapp': '0.1'})
@ -102,7 +111,7 @@ def test_update(app_update, podman_get_apps, list_updates):
podman_get_apps.assert_called_once() podman_get_apps.assert_called_once()
list_updates.assert_called_once() list_updates.assert_called_once()
app_update.assert_called_once_with('someapp') app_update.assert_called_once_with('someapp', from_file=None)
@patch('spoc.list_updates', return_value={}) @patch('spoc.list_updates', return_value={})
@patch('spoc.podman.get_apps', return_value={}) @patch('spoc.podman.get_apps', return_value={})