This commit is contained in:
Muayyad alsadi 2021-05-17 15:03:47 +03:00
parent 045cef299b
commit 9317f987fc

View File

@ -61,6 +61,11 @@ dir_re = re.compile("^[~/\.]")
propagation_re = re.compile("^(?:z|Z|r?shared|r?slave|r?private)$") propagation_re = re.compile("^(?:z|Z|r?shared|r?slave|r?private)$")
norm_re = re.compile('[^-_a-z0-9]') norm_re = re.compile('[^-_a-z0-9]')
PODMAN_CMDS = (
"pull", "push", "build", "inspect",
"run", "start", "stop", "rm", "volume",
)
def parse_short_mount(mount_str, basedir): def parse_short_mount(mount_str, basedir):
mount_a = mount_str.split(':') mount_a = mount_str.split(':')
mount_opt_dict = {} mount_opt_dict = {}
@ -384,10 +389,10 @@ def assert_volume(compose, mount_dict):
print("podman volume inspect {vol_name} || podman volume create {vol_name}".format(vol_name=vol_name)) print("podman volume inspect {vol_name} || podman volume create {vol_name}".format(vol_name=vol_name))
# TODO: might move to using "volume list" # TODO: might move to using "volume list"
# podman volume list --format '{{.Name}}\t{{.MountPoint}}' -f 'label=io.podman.compose.project=HERE' # podman volume list --format '{{.Name}}\t{{.MountPoint}}' -f 'label=io.podman.compose.project=HERE'
try: out = compose.podman.output(["volume", "inspect", vol_name]).decode('utf-8') try: out = compose.podman.output([], "volume", ["inspect", vol_name]).decode('utf-8')
except subprocess.CalledProcessError: except subprocess.CalledProcessError:
compose.podman.output(["volume", "create", "--label", "io.podman.compose.project={}".format(proj_name), "--label", "com.docker.compose.project={}".format(proj_name), vol_name]) compose.podman.output([], "volume", ["create", "--label", "io.podman.compose.project={}".format(proj_name), "--label", "com.docker.compose.project={}".format(proj_name), vol_name])
out = compose.podman.output(["volume", "inspect", vol_name]).decode('utf-8') out = compose.podman.output([], "volume", ["inspect", vol_name]).decode('utf-8')
def mount_desc_to_mount_args(compose, mount_desc, srv_name, cnt_name): def mount_desc_to_mount_args(compose, mount_desc, srv_name, cnt_name):
basedir = compose.dirname basedir = compose.dirname
@ -499,13 +504,12 @@ def get_mount_args(compose, cnt, volume):
args = mount_desc_to_mount_args(compose, volume, srv_name, cnt['name']) args = mount_desc_to_mount_args(compose, volume, srv_name, cnt['name'])
return ['--mount', args] return ['--mount', args]
def container_to_args(compose, cnt, detached=True, podman_command='run'): def container_to_args(compose, cnt, detached=True):
# TODO: double check -e , --add-host, -v, --read-only # TODO: double check -e , --add-host, -v, --read-only
dirname = compose.dirname dirname = compose.dirname
shared_vols = compose.shared_vols shared_vols = compose.shared_vols
pod = cnt.get('pod', None) or '' pod = cnt.get('pod', None) or ''
podman_args = [ podman_args = [
podman_command,
'--name={}'.format(cnt.get('name', None)), '--name={}'.format(cnt.get('name', None)),
] ]
@ -691,18 +695,22 @@ class Podman:
self.podman_path = podman_path self.podman_path = podman_path
self.dry_run = dry_run self.dry_run = dry_run
def output(self, podman_args): def output(self, podman_args, cmd='', cmd_args=None):
cmd = [self.podman_path]+podman_args cmd_args = cmd_args or []
return subprocess.check_output(cmd) xargs = self.compose.get_podman_args(cmd) if cmd else []
cmd_ls = [self.podman_path, *podman_args, cmd] + xargs + cmd_args
print(cmd_ls)
return subprocess.check_output(cmd_ls)
def run(self, podman_args, wait=True, sleep=1, obj=None): def run(self, podman_args, cmd='', cmd_args=None, wait=True, sleep=1, obj=None):
podman_args_str = [str(arg) for arg in podman_args] cmd_args = cmd_args or []
print("podman " + " ".join(podman_args_str)) xargs = self.compose.get_podman_args(cmd) if cmd else []
cmd_ls = [self.podman_path, *podman_args, cmd] + xargs + cmd_args
print(" ".join(cmd_ls))
if self.dry_run: if self.dry_run:
return None return None
cmd = [self.podman_path]+podman_args_str
# 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)
p = subprocess.Popen(cmd) p = subprocess.Popen(cmd_ls)
if wait: if wait:
exit_code = p.wait() exit_code = p.wait()
print(exit_code) print(exit_code)
@ -808,6 +816,16 @@ class PodmanCompose:
self.container_by_name = None self.container_by_name = None
self._prefer_volume_over_mount = True self._prefer_volume_over_mount = True
def get_podman_args(self, cmd):
xargs = []
for args in self.global_args.podman_args:
xargs.extend(shlex.split(args))
cmd_norm = cmd if cmd != 'create' else 'run'
cmd_args = self.global_args.__dict__.get(f"podman_{cmd_norm}_args", None) or []
for args in cmd_args:
xargs.extend(shlex.split(args))
return xargs
def run(self): def run(self):
args = self._parse_args() args = self._parse_args()
podman_path = args.podman_path podman_path = args.podman_path
@ -823,7 +841,7 @@ class PodmanCompose:
if not args.dry_run: if not args.dry_run:
# just to make sure podman is running # just to make sure podman is running
try: try:
self.podman_version = self.podman.output(["--version"]).decode('utf-8').strip() self.podman_version = self.podman.output(["--version"], '', []).decode('utf-8').strip()
except subprocess.CalledProcessError: except subprocess.CalledProcessError:
self.podman_version = None self.podman_version = None
if not self.podman_version: if not self.podman_version:
@ -1022,6 +1040,13 @@ class PodmanCompose:
parser.add_argument("--podman-path", parser.add_argument("--podman-path",
help="Specify an alternate path to podman (default: use location in $PATH variable)", help="Specify an alternate path to podman (default: use location in $PATH variable)",
type=str, default="podman") type=str, default="podman")
parser.add_argument("--podman-args",
help="custom global arguments to be passed to `podman`",
metavar='args', action='append', default=[])
for podman_cmd in PODMAN_CMDS:
parser.add_argument(f"--podman-{podman_cmd}-args",
help=f"custom arguments to be passed to `podman {podman_cmd}`",
metavar='args', action='append', default=[])
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", parser.add_argument("--no-cleanup",
@ -1072,7 +1097,7 @@ class cmd_parse:
@cmd_run(podman_compose, 'version', 'show version') @cmd_run(podman_compose, 'version', 'show version')
def compose_version(compose, args): def compose_version(compose, args):
print("podman-composer version ", __version__) print("podman-composer version ", __version__)
compose.podman.run(["--version"], sleep=0) compose.podman.run(["--version"], "", [], sleep=0)
@cmd_run(podman_compose, 'pull', 'pull stack images') @cmd_run(podman_compose, 'pull', 'pull stack images')
def compose_pull(compose, args): def compose_pull(compose, args):
@ -1081,7 +1106,7 @@ def compose_pull(compose, args):
if cnt.get('build', None): continue if cnt.get('build', None): continue
images.add(cnt["image"]) images.add(cnt["image"])
for image in images: for image in images:
compose.podman.run(["pull", image], sleep=0) compose.podman.run([], "pull", [image], sleep=0)
@cmd_run(podman_compose, 'push', 'push stack images') @cmd_run(podman_compose, 'push', 'push stack images')
def compose_push(compose, args): def compose_push(compose, args):
@ -1089,12 +1114,12 @@ def compose_push(compose, args):
for cnt in compose.containers: for cnt in compose.containers:
if 'build' not in cnt: continue if 'build' not in cnt: continue
if services and cnt['_service'] not in services: continue if services and cnt['_service'] not in services: continue
compose.podman.run(["push", cnt["image"]], sleep=0) compose.podman.run([], "push", [cnt["image"]], sleep=0)
def build_one(compose, args, cnt): def build_one(compose, args, cnt):
if 'build' not in cnt: return if 'build' not in cnt: return
if getattr(args, 'if_not_exists', None): if getattr(args, 'if_not_exists', None):
try: img_id = compose.podman.output(['inspect', '-t', 'image', '-f', '{{.Id}}', cnt["image"]]) try: img_id = compose.podman.output([], 'inspect', ['-t', 'image', '-f', '{{.Id}}', cnt["image"]])
except subprocess.CalledProcessError: img_id = None except subprocess.CalledProcessError: img_id = None
if img_id: return if img_id: return
build_desc = cnt['build'] build_desc = cnt['build']
@ -1106,10 +1131,7 @@ def build_one(compose, args, cnt):
dockerfile = os.path.join(ctx, build_desc.get("dockerfile", "dockerfile")) dockerfile = os.path.join(ctx, build_desc.get("dockerfile", "dockerfile"))
if not os.path.exists(dockerfile): if not os.path.exists(dockerfile):
raise OSError("Dockerfile not found in "+ctx) raise OSError("Dockerfile not found in "+ctx)
build_args = [ build_args = ["-t", cnt["image"], "-f", dockerfile]
"build", "-t", cnt["image"],
"-f", dockerfile
]
if "target" in build_desc: if "target" in build_desc:
build_args.extend(["--target", build_desc["target"]]) build_args.extend(["--target", build_desc["target"]])
container_to_ulimit_args(cnt, build_args) container_to_ulimit_args(cnt, build_args)
@ -1121,7 +1143,7 @@ def build_one(compose, args, cnt):
for build_arg in args_list + args.build_arg: for build_arg in args_list + args.build_arg:
build_args.extend(("--build-arg", build_arg,)) build_args.extend(("--build-arg", build_arg,))
build_args.append(ctx) build_args.append(ctx)
compose.podman.run(build_args, sleep=0) compose.podman.run([], "build", build_args, sleep=0)
@cmd_run(podman_compose, 'build', 'build stack images') @cmd_run(podman_compose, 'build', 'build stack images')
def compose_build(compose, args): def compose_build(compose, args):
@ -1140,14 +1162,14 @@ def compose_build(compose, args):
def create_pods(compose, args): def create_pods(compose, args):
for pod in compose.pods: for pod in compose.pods:
podman_args = [ podman_args = [
"pod", "create", "create",
"--name={}".format(pod["name"]), "--name={}".format(pod["name"]),
"--share", "net", "--share", "net",
] ]
ports = pod.get("ports", None) or [] ports = pod.get("ports", None) or []
for i in ports: for i in ports:
podman_args.extend(['-p', i]) podman_args.extend(['-p', i])
compose.podman.run(podman_args) compose.podman.run([], "pod", podman_args)
def up_specific(compose, args): def up_specific(compose, args):
deps = [] deps = []
@ -1181,12 +1203,12 @@ def compose_up(compose, args):
create_pods(compose, args) create_pods(compose, args)
for cnt in compose.containers: for cnt in compose.containers:
podman_args = container_to_args(compose, cnt, podman_args = container_to_args(compose, cnt, detached=args.detach)
detached=args.detach, podman_command=podman_command) subproc = compose.podman.run([], podman_command, podman_args)
subproc = compose.podman.run(podman_args)
if podman_command == 'run' and subproc.returncode: if podman_command == 'run' and subproc.returncode:
compose.podman.run(['start', cnt['name']]) compose.podman.run([], 'start', [cnt['name']])
if args.no_start or args.detach or args.dry_run: return if args.no_start or args.detach or args.dry_run:
return
# TODO: handle already existing # TODO: handle already existing
# TODO: if error creating do not enter loop # TODO: if error creating do not enter loop
# TODO: colors if sys.stdout.isatty() # TODO: colors if sys.stdout.isatty()
@ -1194,8 +1216,8 @@ def compose_up(compose, args):
threads = [] threads = []
for cnt in compose.containers: for cnt in compose.containers:
# TODO: remove sleep from podman.run # TODO: remove sleep from podman.run
obj = compose if args.get(exit_code_from, None) == cnt['name'] else None obj = compose if args.__dict__.get('exit_code_from', None) == cnt['name'] else None
thread = Thread(target=compose.podman.run, args=[['start', '-a', cnt['name']], obj], daemon=True, name=cnt['name']) thread = Thread(target=compose.podman.run, args=[[], 'start', ['-a', cnt['name']]], kwargs={"obj":obj}, daemon=True, name=cnt['name'])
thread.start() thread.start()
threads.append(thread) threads.append(thread)
time.sleep(1) time.sleep(1)
@ -1217,19 +1239,19 @@ def compose_down(compose, args):
podman_args.extend(['-t', "{}".format(timeout)]) podman_args.extend(['-t', "{}".format(timeout)])
for cnt in compose.containers: for cnt in compose.containers:
compose.podman.run(["stop", *podman_args, cnt["name"]], sleep=0) compose.podman.run([], "stop", [*podman_args, cnt["name"]], sleep=0)
for cnt in compose.containers: for cnt in compose.containers:
compose.podman.run(["rm", cnt["name"]], sleep=0) compose.podman.run([], "rm", [cnt["name"]], sleep=0)
for pod in compose.pods: for pod in compose.pods:
compose.podman.run(["pod", "rm", pod["name"]], sleep=0) compose.podman.run([], "pod", ["rm", pod["name"]], sleep=0)
@cmd_run(podman_compose, 'ps', 'show status of containers') @cmd_run(podman_compose, 'ps', 'show status of containers')
def compose_ps(compose, args): def compose_ps(compose, args):
proj_name = compose.project_name proj_name = compose.project_name
if args.quiet == True: if args.quiet == True:
compose.podman.run(["ps", "-a", "--format", "{{.ID}}", "--filter", f"label=io.podman.compose.project={proj_name}"]) compose.podman.run([], "ps", ["-a", "--format", "{{.ID}}", "--filter", f"label=io.podman.compose.project={proj_name}"])
else: else:
compose.podman.run(["ps", "-a", "--filter", f"label=io.podman.compose.project={proj_name}"]) compose.podman.run([], "ps", ["-a", "--filter", f"label=io.podman.compose.project={proj_name}"])
@cmd_run(podman_compose, 'run', 'create a container similar to a service to run a one-off command') @cmd_run(podman_compose, 'run', 'create a container similar to a service to run a one-off command')
def compose_run(compose, args): def compose_run(compose, args):
@ -1268,14 +1290,14 @@ def compose_run(compose, args):
podman_args.insert(1, '-i') podman_args.insert(1, '-i')
if args.rm: if args.rm:
podman_args.insert(1, '--rm') podman_args.insert(1, '--rm')
compose.podman.run(podman_args, sleep=0) compose.podman.run([], 'run', podman_args, sleep=0)
@cmd_run(podman_compose, 'exec', 'execute a command in a running container') @cmd_run(podman_compose, 'exec', 'execute a command in a running container')
def compose_exec(compose, args): def compose_exec(compose, args):
container_names=compose.container_names_by_service[args.service] container_names=compose.container_names_by_service[args.service]
container_name=container_names[args.index - 1] container_name=container_names[args.index - 1]
cnt = compose.container_by_name[container_name] cnt = compose.container_by_name[container_name]
podman_args = ['exec', '--interactive'] podman_args = ['--interactive']
if args.privileged: podman_args += ['--privileged'] if args.privileged: podman_args += ['--privileged']
if args.user: podman_args += ['--user', args.user] if args.user: podman_args += ['--user', args.user]
if args.workdir: podman_args += ['--workdir', args.workdir] if args.workdir: podman_args += ['--workdir', args.workdir]
@ -1289,7 +1311,7 @@ def compose_exec(compose, args):
podman_args += [container_name] podman_args += [container_name]
if args.cnt_command is not None and len(args.cnt_command) > 0: if args.cnt_command is not None and len(args.cnt_command) > 0:
podman_args += args.cnt_command podman_args += args.cnt_command
compose.podman.run(podman_args, sleep=0) compose.podman.run([], 'exec', podman_args, sleep=0)
def transfer_service_status(compose, args, action): def transfer_service_status(compose, args, action):
@ -1300,12 +1322,12 @@ def transfer_service_status(compose, args, action):
if service not in container_names_by_service: if service not in container_names_by_service:
raise ValueError("unknown service: " + service) raise ValueError("unknown service: " + service)
targets.extend(container_names_by_service[service]) targets.extend(container_names_by_service[service])
podman_args=[action] podman_args=[]
timeout=getattr(args, 'timeout', None) timeout=getattr(args, 'timeout', None)
if timeout is not None: if timeout is not None:
podman_args.extend(['-t', "{}".format(timeout)]) podman_args.extend(['-t', "{}".format(timeout)])
for target in targets: for target in targets:
compose.podman.run(podman_args+[target], sleep=0) compose.podman.run([], action, podman_args+[target], sleep=0)
@cmd_run(podman_compose, 'start', 'start specific services') @cmd_run(podman_compose, 'start', 'start specific services')
def compose_start(compose, args): def compose_start(compose, args):
@ -1326,7 +1348,7 @@ def compose_logs(compose, args):
if args.service not in container_names_by_service: if args.service not in container_names_by_service:
raise ValueError("unknown service: " + args.service) raise ValueError("unknown service: " + args.service)
target = container_names_by_service[args.service] target = container_names_by_service[args.service]
podman_args = ['logs'] podman_args = []
if args.follow: if args.follow:
podman_args.append('-f') podman_args.append('-f')
# the default value is to print all logs which is in podman = 0 and not # the default value is to print all logs which is in podman = 0 and not
@ -1335,7 +1357,7 @@ def compose_logs(compose, args):
podman_args.extend(['--tail', args.tail]) podman_args.extend(['--tail', args.tail])
if args.timestamps: if args.timestamps:
podman_args.append('-t') podman_args.append('-t')
compose.podman.run(podman_args+target) compose.podman.run([], 'logs', podman_args+target)
################### ###################
# command arguments parsing # command arguments parsing