#!/usr/bin/python3 # -*- coding: utf-8 -*- import argparse import shlex import os from spoc.container import Container from spoc.image import Image from spoc.paths import VOLUME_DIR ACTION_CREATE = 1 ACTION_MODIFY = 2 ACTION_DESTROY = 3 ACTION_START = 4 ACTION_STOP = 5 ACTION_STATUS = 6 ACTION_EXEC = 7 def modify_depend(container, depend): if depend.startswith('!'): try: container.depends.remove(depend[1:]) except KeyError: pass else: # Add the dependency and remove duplicates container.depends.append(depend) container.depends = list(set(container.depends)) def modify_mount(container, mount): volume,mountpoint = mount.split(':', 1) mountpoint = mountpoint.lstrip('/') if mountpoint: # If the volume doesn't exist yet, assume it will be a directory is_dir = not os.path.isfile(os.path.join(VOLUME_DIR, volume)) container.mounts[volume] = (mountpoint, is_dir) else: try: del container.mounts[volume] except KeyError: pass def modify_env(container, env): key,value = env.split('=', 1) if value: container.env[key] = value else: try: del container.env[key] except KeyError: pass def modify_container(container, depends, mounts, envs, uid, gid, cmd, cwd, ready, halt, autostart): for depend in depends: modify_depend(container, depend) for mount in mounts: modify_mount(container, mount) for env in envs: modify_env(container, env) autostart = autostart == 'on' args = locals() for member in ('uid', 'gid', 'cmd', 'cwd', 'ready', 'halt', 'autostart'): value = args[member] if value: setattr(container, member, value) def create(container_name, image_name, depends, mounts, env, uid, gid, cmd, cwd, ready, halt, autostart): # Create container based on image definition and extrea fields container = Container(container_name, False) container.set_definition(Image(image_name).get_definition()) modify_container(container, depends, mounts, env, uid, gid, cmd, cwd, ready, halt, autostart) container.create() def modify(container_name, depends, mounts, env, uid, gid, cmd, cwd, ready, halt, autostart): # Change configuration of an existing container container = Container(container_name) modify_container(container, depends, mounts, env, uid, gid, cmd, cwd, ready, halt, autostart) container.create() def destroy(container_name): # Remove container and its directory Container(container_name, False).destroy() def start(container_name): # Start the container using init values from its definition Container(container_name).start() def stop(container_name): # Stop the container using halt signal from its definition Container(container_name).stop() def status(container_name): # Prints current running status of the container print(Container(container_name).get_state()) def execute(container_name, command, uid, gid): # Execute a command in container's namespace Container(container_name).execute(command, uid, gid) parser = argparse.ArgumentParser(description='SPOC container manager') parser.set_defaults(action=None) subparsers = parser.add_subparsers() parser_create = subparsers.add_parser('create') parser_create.set_defaults(action=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') parser_create.add_argument('-u', '--uid', help='Sets the container init UID') parser_create.add_argument('-g', '--gid', help='Sets the container init GID') parser_create.add_argument('-c', '--cmd', help='Sets the container init command') parser_create.add_argument('-w', '--workdir', help='Sets the container init working directory') parser_create.add_argument('-r', '--ready', help='Sets the container ready command') parser_create.add_argument('-s', '--stopsig', help='Sets the signal to be sent to init on container shutdown') parser_create.add_argument('-a', '--autostart', choices=('on', 'off'), help='Sets the container to be automatically started after the host boots up') 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.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') parser_modify.add_argument('-u', '--uid', help='Sets the container init UID') parser_modify.add_argument('-g', '--gid', help='Sets the container init GID') parser_modify.add_argument('-c', '--cmd', help='Sets the container init command') parser_modify.add_argument('-w', '--workdir', help='Sets the container init working directory') parser_modify.add_argument('-r', '--ready', help='Sets the container ready command') parser_modify.add_argument('-s', '--stopsig', help='Sets the signal to be sent to init on container shutdown') parser_modify.add_argument('-a', '--autostart', choices=('on', 'off'), help='Sets the container to be automatically started after the host boots up') parser_modify.add_argument('container') parser_destroy = subparsers.add_parser('destroy') parser_destroy.set_defaults(action=ACTION_DESTROY) parser_destroy.add_argument('container') parser_start = subparsers.add_parser('start') parser_start.set_defaults(action=ACTION_START) parser_start.add_argument('container') parser_stop = subparsers.add_parser('stop') parser_stop.set_defaults(action=ACTION_STOP) parser_stop.add_argument('container') parser_status = subparsers.add_parser('status') parser_status.set_defaults(action=ACTION_STATUS) parser_status.add_argument('container') parser_exec = subparsers.add_parser('exec') parser_exec.set_defaults(action=ACTION_EXEC) 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') parser_exec.add_argument('command', nargs=argparse.REMAINDER) args = parser.parse_args() if args.action == ACTION_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: 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: destroy(args.container) elif args.action == ACTION_START: start(args.container) elif args.action == ACTION_STOP: stop(args.container) elif args.action == ACTION_STATUS: status(args.container) elif args.action == ACTION_EXEC: execute(args.container, args.command, args.uid, args.gid) else: parser.print_usage()