diff --git a/usr/bin/spoc-app b/usr/bin/spoc-app index 6085771..7b23d97 100644 --- a/usr/bin/spoc-app +++ b/usr/bin/spoc-app @@ -7,16 +7,6 @@ import os from spoc import publisher from spoc.cli import readable_size -ACTION_LIST = 1 -ACTION_INSTALL = 2 -ACTION_UPGRADE = 3 -ACTION_UNINSTALL = 4 -ACTION_START = 5 -ACTION_STOP = 6 -ACTION_STATUS = 7 -ACTION_PUBLISH = 8 -ACTION_UNPUBLISH = 9 - def listing(): raise NotImplementedException() @@ -61,61 +51,61 @@ parser.set_defaults(action=None) subparsers = parser.add_subparsers() parser_list = subparsers.add_parser('list') -parser_list.set_defaults(action=ACTION_LIST) +parser_list.set_defaults(action=listing) parser_list.add_argument('type', choices=('installed', 'online', 'upgrades', 'published'), default='installed', const='installed', nargs='?') parser_install = subparsers.add_parser('install') -parser_install.set_defaults(action=ACTION_INSTALL) +parser_install.set_defaults(action=install) parser_install.add_argument('app') parser_upgrade = subparsers.add_parser('upgrade') -parser_upgrade.set_defaults(action=ACTION_UPGRADE) +parser_upgrade.set_defaults(action=upgrade) parser_upgrade.add_argument('app') parser_uninstall = subparsers.add_parser('uninstall') -parser_uninstall.set_defaults(action=ACTION_UNINSTALL) +parser_uninstall.set_defaults(action=uninstall) parser_uninstall.add_argument('app') parser_start = subparsers.add_parser('start') -parser_start.set_defaults(action=ACTION_START) +parser_start.set_defaults(action=start) parser_start.add_argument('app') parser_stop = subparsers.add_parser('stop') -parser_stop.set_defaults(action=ACTION_STOP) +parser_stop.set_defaults(action=stop) parser_stop.add_argument('app') parser_status = subparsers.add_parser('status') -parser_status.set_defaults(action=ACTION_STATUS) +parser_status.set_defaults(action=status) parser_status.add_argument('app') parser_publish = subparsers.add_parser('publish') -parser_publish.set_defaults(action=ACTION_PUBLISH) +parser_publish.set_defaults(action=publish) parser_publish.add_argument('-f', '--force', action='store_true', help='Force republish already published application') parser_publish.add_argument('file') parser_unpublish = subparsers.add_parser('unpublish') -parser_unpublish.set_defaults(action=ACTION_UNPUBLISH) +parser_unpublish.set_defaults(action=unpublish) parser_unpublish.add_argument('app') args = parser.parse_args() -if args.action == ACTION_LIST: +if args.action is listing: listing(args.type) -elif args.action == ACTION_INSTALL: +elif args.action is install: install(args.app) -elif args.action == ACTION_UPGRADE: +elif args.action is upgrade: upgrade(args.app) -elif args.action == ACTION_UNINSTALL: +elif args.action is uninstall: uninstall(args.app) -elif args.action == ACTION_START: +elif args.action is start: start(args.app) -elif args.action == ACTION_STOP: +elif args.action is stop: stop(args.app) -elif args.action == ACTION_STATUS: +elif args.action is status: status(args.app) -elif args.action == ACTION_PUBLISH: +elif args.action is publish: publish(args.file, args.force) -elif args.action == ACTION_UNPUBLISH: +elif args.action is unpublish: unpublish(args.app) else: parser.print_usage() diff --git a/usr/bin/spoc-container b/usr/bin/spoc-container index da58d7c..a4d2c8a 100644 --- a/usr/bin/spoc-container +++ b/usr/bin/spoc-container @@ -10,15 +10,6 @@ from spoc.container import Container, STATE_RUNNING, STATE_STOPPED from spoc.image import Image from spoc.config import VOLUME_DIR -ACTION_LIST = 1 -ACTION_CREATE = 2 -ACTION_MODIFY = 3 -ACTION_DESTROY = 4 -ACTION_START = 5 -ACTION_STOP = 6 -ACTION_STATUS = 7 -ACTION_EXEC = 8 - def listing(state): if state == 'all': containers = repo_local.get_containers().keys() @@ -115,11 +106,11 @@ parser.set_defaults(action=None) subparsers = parser.add_subparsers() parser_list = subparsers.add_parser('list') -parser_list.set_defaults(action=ACTION_LIST) +parser_list.set_defaults(action=listing) parser_list.add_argument('type', choices=('all', 'running', 'stopped'), default='all', const='all', nargs='?') parser_create = subparsers.add_parser('create') -parser_create.set_defaults(action=ACTION_CREATE) +parser_create.set_defaults(action=create) parser_create.add_argument('-d', '--depends', action='append', default=[], help='Add another container as a start dependency') parser_create.add_argument('-m', '--mount', action='append', default=[], help='Add mount to the container - format volume:mountpoint') parser_create.add_argument('-e', '--env', action='append', default=[], help='Add environment variable for the container - format KEY=value') @@ -134,7 +125,7 @@ parser_create.add_argument('container') parser_create.add_argument('image') parser_modify = subparsers.add_parser('modify') -parser_modify.set_defaults(action=ACTION_MODIFY) +parser_modify.set_defaults(action=modify) parser_modify.add_argument('-d', '--depends', action='append', default=[], help='Add another container as a start dependency - prepend the name with ! to remove the dependency') parser_modify.add_argument('-m', '--mount', action='append', default=[], help='Add mount to the container - format volume:mountpoint - specify empty mountpoint to remove the mount') parser_modify.add_argument('-e', '--env', action='append', default=[], help='Add environment variable for the container - format KEY=value - specify empty value to remove the env') @@ -148,23 +139,23 @@ parser_modify.add_argument('-a', '--autostart', choices=('on', 'off'), help='Set parser_modify.add_argument('container') parser_destroy = subparsers.add_parser('destroy') -parser_destroy.set_defaults(action=ACTION_DESTROY) +parser_destroy.set_defaults(action=destroy) parser_destroy.add_argument('container') parser_start = subparsers.add_parser('start') -parser_start.set_defaults(action=ACTION_START) +parser_start.set_defaults(action=start) parser_start.add_argument('container') parser_stop = subparsers.add_parser('stop') -parser_stop.set_defaults(action=ACTION_STOP) +parser_stop.set_defaults(action=stop) parser_stop.add_argument('container') parser_status = subparsers.add_parser('status') -parser_status.set_defaults(action=ACTION_STATUS) +parser_status.set_defaults(action=status) parser_status.add_argument('container') parser_exec = subparsers.add_parser('exec') -parser_exec.set_defaults(action=ACTION_EXEC) +parser_exec.set_defaults(action=execute) parser_exec.add_argument('-u', '--uid', help='Sets the command UID') parser_exec.add_argument('-g', '--gid', help='Sets the command GID') parser_exec.add_argument('container') @@ -172,21 +163,21 @@ parser_exec.add_argument('command', nargs=argparse.REMAINDER) args = parser.parse_args() -if args.action == ACTION_LIST: +if args.action is listing: listing(args.type) -elif args.action == ACTION_CREATE: +elif args.action is create: create(args.container, args.image, args.depends, args.mount, args.env, args.uid, args.gid, args.cmd, args.workdir, args.ready, args.stopsig, args.autostart) -elif args.action == ACTION_MODIFY: +elif args.action is modify: modify(args.container, args.depends, args.mount, args.env, args.uid, args.gid, args.cmd, args.workdir, args.ready, args.stopsig, args.autostart) -elif args.action == ACTION_DESTROY: +elif args.action is destroy: destroy(args.container) -elif args.action == ACTION_START: +elif args.action is start: start(args.container) -elif args.action == ACTION_STOP: +elif args.action is stop: stop(args.container) -elif args.action == ACTION_STATUS: +elif args.action is status: status(args.container) -elif args.action == ACTION_EXEC: +elif args.action is execute: execute(args.container, args.command, args.uid, args.gid) else: parser.print_usage() diff --git a/usr/bin/spoc-image b/usr/bin/spoc-image index b092e73..7013b27 100644 --- a/usr/bin/spoc-image +++ b/usr/bin/spoc-image @@ -13,14 +13,6 @@ from spoc.image import Image from spoc.imagebuilder import ImageBuilder from spoc.cli import ActionQueue, readable_size -ACTION_LIST = 1 -ACTION_DOWNLOAD = 2 -ACTION_DELETE = 3 -ACTION_CLEAN = 4 -ACTION_BUILD = 5 -ACTION_PUBLISH = 6 -ACTION_UNPUBLISH = 7 - def get_image_name(file_path): # Read and return image name from image file with open(file_path) as f: @@ -116,50 +108,50 @@ parser.set_defaults(action=None) subparsers = parser.add_subparsers() parser_list = subparsers.add_parser('list') -parser_list.set_defaults(action=ACTION_LIST) +parser_list.set_defaults(action=listing) parser_list.add_argument('type', choices=('installed', 'online', 'published'), default='installed', const='installed', nargs='?') parser_download = subparsers.add_parser('download') -parser_download.set_defaults(action=ACTION_DOWNLOAD) +parser_download.set_defaults(action=download) parser_download.add_argument('image') parser_delete = subparsers.add_parser('delete') -parser_delete.set_defaults(action=ACTION_DELETE) +parser_delete.set_defaults(action=delete) parser_delete.add_argument('image') parser_clean = subparsers.add_parser('clean') -parser_clean.set_defaults(action=ACTION_CLEAN) +parser_clean.set_defaults(action=clean) parser_build = subparsers.add_parser('build') -parser_build.set_defaults(action=ACTION_BUILD) +parser_build.set_defaults(action=build) parser_build.add_argument('-f', '--force', action='store_true', help='Force rebuild already existing image') parser_build.add_argument('-p', '--publish', action='store_true', help='Publish the image after successful build') parser_build.add_argument('file') parser_publish = subparsers.add_parser('publish') -parser_publish.set_defaults(action=ACTION_PUBLISH) +parser_publish.set_defaults(action=publish) parser_publish.add_argument('-f', '--force', action='store_true', help='Force republish already published image') parser_publish.add_argument('image') parser_unpublish = subparsers.add_parser('unpublish') -parser_unpublish.set_defaults(action=ACTION_UNPUBLISH) +parser_unpublish.set_defaults(action=unpublish) parser_unpublish.add_argument('image') args = parser.parse_args() -if args.action == ACTION_LIST: +if args.action is listing: listing(args.type) -elif args.action == ACTION_DOWNLOAD: +elif args.action is download: download(args.image) -elif args.action == ACTION_DELETE: +elif args.action is delete: delete(args.image) -elif args.action == ACTION_CLEAN: +elif args.action is clean: clean() -elif args.action == ACTION_BUILD: +elif args.action is build: build(args.file, args.force, args.publish) -elif args.action == ACTION_PUBLISH: +elif args.action is publish: publish(args.image, args.force) -elif args.action == ACTION_UNPUBLISH: +elif args.action is unpublish: unpublish(args.image) else: parser.print_usage() diff --git a/usr/lib/python3.8/spoc/container.py b/usr/lib/python3.8/spoc/container.py index e078b1a..f5581c2 100644 --- a/usr/lib/python3.8/spoc/container.py +++ b/usr/lib/python3.8/spoc/container.py @@ -1,5 +1,6 @@ # -*- coding: utf-8 -*- +import copy import os import shlex import shutil @@ -56,11 +57,12 @@ class Container: setattr(self, key, definition[key]) def get_definition(self): + # Return shallow copy of container definition as dictionary definition = {} for key in DEFINITION_MEMBERS: value = getattr(self, key) if value: - definition[key] = value + definition[key] = copy.copy(value) return definition def get_state(self): diff --git a/usr/lib/python3.8/spoc/image.py b/usr/lib/python3.8/spoc/image.py index 629b6db..74798c9 100644 --- a/usr/lib/python3.8/spoc/image.py +++ b/usr/lib/python3.8/spoc/image.py @@ -1,5 +1,6 @@ # -*- coding: utf-8 -*- +import copy import os import shutil import tarfile @@ -35,11 +36,16 @@ class Image: setattr(self, key, definition[key]) def get_definition(self, including_self_layer=False): + # Return shallow copy of image definition as dictionary definition = {} for key in DEFINITION_MEMBERS: value = getattr(self, key) if value: - definition[key] = value + definition[key] = copy.copy(value) + # Always add layers in definition even if there are none to ease processing elsewhere + if 'layers' not in definition: + definition['layers'] = [] + # Add the image's layer as tompost, if requested (useful for container creation) if including_self_layer: definition['layers'].append(self.name) return definition diff --git a/usr/lib/python3.8/spoc/imagebuilder.py b/usr/lib/python3.8/spoc/imagebuilder.py index 9316bfc..3c0c658 100644 --- a/usr/lib/python3.8/spoc/imagebuilder.py +++ b/usr/lib/python3.8/spoc/imagebuilder.py @@ -42,7 +42,6 @@ class ImageBuilder: elif 'FROM' == directive: # Set the values of image from which this one inherits self.image.set_definition(Image(args).get_definition(True)) - self.image.layers.append(self.image.name) elif 'COPY' == directive: srcdst = args.split() self.copy_files(srcdst[0], srcdst[1] if len(srcdst) > 1 else '') @@ -99,7 +98,7 @@ class ImageBuilder: if not uid.isdigit() or not gid.isdigit(): # Resolve the UID/GID from container if either of them is entered as string container = Container(self.image.name, False) - container.set_definition(self.image.get_definition()) + container.set_definition(self.image.get_definition(True)) container.create() uid,gid = container.get_uidgid(uid, gid) container.destroy()