new switches: --dry-run, --no-cleanup, --podman-path

Small refactoring and three new switches: `--dry-run` for debugging (doesn't execute podman), `--no-cleanup` doesn't stop & remove containers on a fresh run, `--podman-path` allows the user to point to podman binary if not in $PATH
This commit is contained in:
tobwen 2019-03-20 23:49:17 +01:00
parent f6f711a82a
commit bbc62e34b0

View File

@ -1,6 +1,5 @@
#! /usr/bin/env python #! /usr/bin/env python
# https://docs.docker.com/compose/compose-file/#service-configuration-reference # https://docs.docker.com/compose/compose-file/#service-configuration-reference
# https://docs.docker.com/samples/ # https://docs.docker.com/samples/
# https://docs.docker.com/compose/gettingstarted/ # https://docs.docker.com/compose/gettingstarted/
@ -9,7 +8,6 @@
from __future__ import print_function from __future__ import print_function
import os import os
import argparse import argparse
import subprocess import subprocess
@ -60,6 +58,7 @@ def norm_as_dict(src):
raise ValueError("dictionary or iterable is expected") raise ValueError("dictionary or iterable is expected")
return dst return dst
# transformation helpers # transformation helpers
def adj_hosts(services, cnt, dst = "127.0.0.1"): def adj_hosts(services, cnt, dst = "127.0.0.1"):
@ -81,8 +80,6 @@ def adj_hosts(services, cnt, dst="127.0.0.1"):
extra_hosts.append("{}:{}".format(alias, dst)) extra_hosts.append("{}:{}".format(alias, dst))
cnt["extra_hosts"] = extra_hosts cnt["extra_hosts"] = extra_hosts
def move_list(dst, containers, key): def move_list(dst, containers, key):
""" """
move key (like port forwarding) from containers to dst (a pod or a infra container) move key (like port forwarding) from containers to dst (a pod or a infra container)
@ -188,27 +185,28 @@ def tr_1podfw(project_name, services, given_containers):
move_port_fw(pod, containers) move_port_fw(pod, containers)
return pods, containers return pods, containers
def down(project_name, dirname, pods, containers): def down(project_name, dirname, pods, containers, dry_run, podman_path):
for cnt in containers: for cnt in containers:
cmd="""podman stop -t=1 '{name}'""".format(**cnt) cmd = """{} stop -t=1 '{name}'""".format(podman_path, **cnt)
print(cmd) print(cmd)
subprocess.Popen(cmd, shell=True).wait() if dry_run == False: subprocess.Popen(cmd, shell = True).wait()
for cnt in containers: for cnt in containers:
cmd="""podman rm '{name}'""".format(**cnt) cmd = """{} rm '{name}'""".format(podman_path, **cnt)
print(cmd) print(cmd)
subprocess.Popen(cmd, shell=True).wait() if dry_run == False: subprocess.Popen(cmd, shell = True).wait()
for pod in pods: for pod in pods:
cmd="""podman pod rm '{name}'""".format(**pod) cmd = """{} pod rm '{name}'""".format(podman_path, **pod)
print(cmd) print(cmd)
subprocess.Popen(cmd, shell=True).wait() if dry_run == False: subprocess.Popen(cmd, shell = True).wait()
def container_to_args(cnt, dirname): def container_to_args(cnt, dirname, podman_path):
pod = cnt.get('pod') or '' pod = cnt.get('pod') or ''
args = [ args = [
'podman', 'run', podman_path, 'run',
'--name={}'.format(cnt.get('name')), '--name={}'.format(cnt.get('name')),
'-d' '-d'
] ]
if pod: if pod:
args.append('--pod={}'.format(pod)) args.append('--pod={}'.format(pod))
if cnt.get('read_only'): if cnt.get('read_only'):
@ -287,13 +285,15 @@ def flat_deps(services, container_by_name):
for name, cnt in container_by_name.items(): for name, cnt in container_by_name.items():
rec_deps(services, container_by_name, cnt, cnt.get('_service')) rec_deps(services, container_by_name, cnt, cnt.get('_service'))
def up(project_name, dirname, pods, containers): def up(project_name, dirname, pods, containers, no_cleanup, dry_run, podman_path):
os.chdir(dirname) if dry_run == False: os.chdir(dirname)
# no need remove them if they have same hash label # no need remove them if they have same hash label
down(project_name, dirname, pods, containers) if no_cleanup == False: down(project_name, dirname, pods, containers, dry_run, podman_path)
for pod in pods: for pod in pods:
args = [ args = [
"podman", "pod", "create", podman_path, "pod", "create",
"--name={}".format(pod["name"]), "--name={}".format(pod["name"]),
"--share", "net", "--share", "net",
] ]
@ -301,27 +301,41 @@ def up(project_name, dirname, pods, containers):
for i in ports: for i in ports:
args.extend(['-p', i]) args.extend(['-p', i])
print(" ".join(args)) print(" ".join(args))
if dry_run == False:
p = subprocess.Popen(args) p = subprocess.Popen(args)
print(p.wait()) print(p.wait())
#print(opts)
for cnt in containers: for cnt in containers:
# TODO: -e , --add-host, -v, --read-only # TODO: -e , --add-host, -v, --read-only
args = container_to_args(cnt, dirname) args = container_to_args(cnt, dirname, podman_path)
print(" ".join(args)) print(" ".join(args))
p=subprocess.Popen(args) ## print("""podman run -d --pod='{pod}' --name='{name}' '{image}'""".format(**cnt))
print(p.wait()) if dry_run == False:
#print("""podman run -d --pod='{pod}' --name='{name}' '{image}'""".format(**cnt)) subprocess.Popen(args).wait()
# subprocess.Popen(args, bufsize = 0, executable = None, stdin = None, stdout = None, stderr = None, preexec_fn = None, close_fds = False, shell = False, cwd = None, env = None, universal_newlines = False, startupinfo = None, creationflags = 0) # subprocess.Popen(args, bufsize = 0, executable = None, stdin = None, stdout = None, stderr = None, preexec_fn = None, close_fds = False, shell = False, cwd = None, env = None, universal_newlines = False, startupinfo = None, creationflags = 0)
time.sleep(1) if dry_run == False: time.sleep(1)
def main(command, filename, project_name, no_ansi, transform_policy, host_env=None): def main(command, filename, project_name, no_ansi, no_cleanup, dry_run, transform_policy, podman_path, host_env = None):
filename = os.path.realpath(filename) filename = os.path.realpath(filename)
dirname = os.path.dirname(filename) dirname = os.path.dirname(filename)
dir_basename = os.path.basename(dirname) dir_basename = os.path.basename(dirname)
if not project_name: project_name = dir_basename
if podman_path != 'podman':
if os.path.isfile(podman_path) and os.access(podman_path, os.X_OK):
podman_path = os.path.realpath(podman_path)
else:
# this also works if podman hasn't been installed now
if dry_run == False: raise IOError("Binary {} has not been found.".format(podman_path))
if not project_name:
project_name = dir_basename
with open(filename, 'r') as f: with open(filename, 'r') as f:
compose = yaml.safe_load(f) compose = yaml.safe_load(f)
print(json.dumps(compose, indent=2))
# debug mode
#print(json.dumps(compose, indent = 2))
ver = compose.get('version') ver = compose.get('version')
services = compose.get('services') services = compose.get('services')
podman_compose_labels = [ podman_compose_labels = [
@ -367,37 +381,44 @@ def main(command, filename, project_name, no_ansi, transform_policy, host_env=No
pods, containers = tr(project_name, container_names_by_service, given_containers) pods, containers = tr(project_name, container_names_by_service, given_containers)
cmd = command[0] cmd = command[0]
if cmd == "up": if cmd == "up":
up(project_name, dirname, pods, containers) up(project_name, dirname, pods, containers, no_cleanup, dry_run, podman_path)
elif cmd == "down": elif cmd == "down":
down(project_name, dirname, pods, containers) down(project_name, dirname, pods, containers, dry_run, podman_path)
else: else:
raise NotImplementedError("command {} is not implemented".format(cmd)) raise NotImplementedError("command {} is not implemented".format(cmd))
if __name__ == "__main__": if __name__ == "__main__":
parser = argparse.ArgumentParser() parser = argparse.ArgumentParser()
parser.add_argument('command', metavar = 'command', parser.add_argument('command', metavar = 'command',
help = 'command to run', help = 'command to run',
choices = ['up', 'down'], nargs = 1, default = "up") choices = ['up', 'down'], nargs = 1, default = "up")
parser.add_argument("-f", "--file", parser.add_argument("-f", "--file",
help = "Specify an alternate compose file (default: docker-compose.yml)", help = "Specify an alternate compose file (default: docker-compose.yml)",
type=str, default='docker-compose.yml') type = str, default = "docker-compose.yml")
parser.add_argument("-p", "--project-name", parser.add_argument("-p", "--project-name",
help = "Specify an alternate project name (default: directory name)", help = "Specify an alternate project name (default: directory name)",
type = str, default = None) type = str, default = None)
parser.add_argument("--podman-path",
help = "Specify an alternate path to podman (default: use location in $PATH variable)",
type = str, default = "podman")
parser.add_argument("--no-ansi", parser.add_argument("--no-ansi",
help = "Do not print ANSI control characters", action = 'store_true') help = "Do not print ANSI control characters", action = 'store_true')
parser.add_argument("--no-cleanup",
help = "Do not stop and remove existing pod & containers", action = 'store_true')
parser.add_argument("--dry-run",
help = "No action; perform a simulation of commands", action = 'store_true')
parser.add_argument("-t", "--transform_policy", parser.add_argument("-t", "--transform_policy",
help = "how to translate docker compose to podman [1pod|hostnet|accurate]", help = "how to translate docker compose to podman [1pod|hostnet|accurate]",
choices = ['1pod', '1podfw', 'hostnet', 'cntnet', 'publishall', 'identity'], default = '1podfw') choices = ['1pod', '1podfw', 'hostnet', 'cntnet', 'publishall', 'identity'], default = '1podfw')
args = parser.parse_args() args = parser.parse_args()
main ( main (
command = args.command, command = args.command,
filename = args.file, filename = args.file,
project_name = args.project_name, project_name = args.project_name,
no_ansi = args.no_ansi, no_ansi = args.no_ansi,
transform_policy=args.transform_policy) no_cleanup = args.no_cleanup,
dry_run = args.dry_run,
transform_policy = args.transform_policy,
podman_path = args.podman_path
)