From 4733c7ef94b98317edc875e6c0815adceea65a38 Mon Sep 17 00:00:00 2001 From: alsadi Date: Sat, 23 Mar 2019 21:42:04 +0200 Subject: [PATCH] apply formating and add pylintrc --- .pylintrc | 13 ++ ...docker-compose.yml => docker-compose.yaml} | 0 podman-compose.py | 191 +++++++++++------- 3 files changed, 129 insertions(+), 75 deletions(-) create mode 100644 .pylintrc rename examples/busybox/{docker-compose.yml => docker-compose.yaml} (100%) diff --git a/.pylintrc b/.pylintrc new file mode 100644 index 0000000..629d529 --- /dev/null +++ b/.pylintrc @@ -0,0 +1,13 @@ +[MESSAGES CONTROL] +disable=W0614,C0410,C0321,C0111,I0011,C0103 +# allow _ for ignored variables +# allow generic names like a,b,c and i,j,k,l,m,n and x,y,z +# allow k,v for key/value +# allow e for exceptions, it for iterator +# allow w,h for width, height +# allow op for operation/operator/opcode +# allow t, t0, t1, t2, and t3 for time +# allow dt for delta time +# allow db for database +# allow ls for list +good-names=_,a,b,c,dt,db,e,f,fn,fd,i,j,k,v,kv,kw,l,m,n,ls,t,t0,t1,t2,t3,w,h,x,y,z,it,op diff --git a/examples/busybox/docker-compose.yml b/examples/busybox/docker-compose.yaml similarity index 100% rename from examples/busybox/docker-compose.yml rename to examples/busybox/docker-compose.yaml diff --git a/podman-compose.py b/podman-compose.py index d39fb0a..bf26ab4 100755 --- a/podman-compose.py +++ b/podman-compose.py @@ -21,12 +21,17 @@ import yaml # helpers -def try_int(i, fallback = None): - try: return int(i) - except ValueError: pass - except TypeError: pass + +def try_int(i, fallback=None): + try: + return int(i) + except ValueError: + pass + except TypeError: + pass return fallback + def norm_as_list(src): """ given a dictionary {key1:value1, key2: None} or list @@ -42,6 +47,7 @@ def norm_as_list(src): dst = [src] return dst + def norm_as_dict(src): """ given a list ["key1=value1", "key2"] @@ -52,7 +58,7 @@ def norm_as_dict(src): elif isinstance(src, dict): dst = dict(src) elif hasattr(src, '__iter__'): - dst = [ i.split("=", 1) for i in src if i ] + dst = [i.split("=", 1) for i in src if i] dst = dict([(a if len(a) == 2 else (a[0], None)) for a in dst]) else: raise ValueError("dictionary or iterable is expected") @@ -61,7 +67,7 @@ def norm_as_dict(src): # transformation helpers -def adj_hosts(services, cnt, dst = "127.0.0.1"): +def adj_hosts(services, cnt, dst="127.0.0.1"): """ adjust container cnt in-place to add hosts pointing to dst for services """ @@ -80,6 +86,7 @@ def adj_hosts(services, cnt, dst = "127.0.0.1"): extra_hosts.append("{}:{}".format(alias, dst)) cnt["extra_hosts"] = extra_hosts + def move_list(dst, containers, key): """ move key (like port forwarding) from containers to dst (a pod or a infra container) @@ -90,7 +97,9 @@ def move_list(dst, containers, key): if a0: a.update(a0) del cnt[key] - if a: dst[key] = list(a) + if a: + dst[key] = list(a) + def move_port_fw(dst, containers): """ @@ -98,6 +107,7 @@ def move_port_fw(dst, containers): """ move_list(dst, containers, "ports") + def move_extra_hosts(dst, containers): """ move port forwarding from containers to dst (a pod or a infra container) @@ -108,10 +118,13 @@ def move_extra_hosts(dst, containers): # transformations transformations = {} + + def trans(func): transformations[func.__name__.replace("tr_", "")] = func return func + @trans def tr_identity(project_name, services, given_containers): containers = [] @@ -119,48 +132,53 @@ def tr_identity(project_name, services, given_containers): containers.append(dict(cnt)) return [], containers + @trans def tr_publishall(project_name, services, given_containers): containers = [] for cnt0 in given_containers: - cnt = dict(cnt0, publishall = True) + cnt = dict(cnt0, publishall=True) # adjust hosts to point to the gateway, TODO: adjust host env adj_hosts(services, cnt, '10.0.2.2') containers.append(cnt) return [], containers + @trans def tr_hostnet(project_name, services, given_containers): containers = [] for cnt0 in given_containers: - cnt = dict(cnt0, network_mode = "host") + cnt = dict(cnt0, network_mode="host") # adjust hosts to point to localhost, TODO: adjust host env adj_hosts(services, cnt, '127.0.0.1') containers.append(cnt) return [], containers + @trans def tr_cntnet(project_name, services, given_containers): containers = [] infra_name = project_name + "_infra" infra = dict( - name = infra_name, - image = "k8s.gcr.io/pause:3.1", + name=infra_name, + image="k8s.gcr.io/pause:3.1", ) for cnt0 in given_containers: - cnt = dict(cnt0, network_mode = "container:"+infra_name) + cnt = dict(cnt0, network_mode="container:"+infra_name) deps = cnt.get("depends") or [] deps.append(infra_name) cnt["depends"] = deps # adjust hosts to point to localhost, TODO: adjust host env adj_hosts(services, cnt, '127.0.0.1') - if "hostname" in cnt: del cnt["hostname"] + if "hostname" in cnt: + del cnt["hostname"] containers.append(cnt) move_port_fw(infra, containers) move_extra_hosts(infra, containers) containers.insert(0, infra) return [], containers + @trans def tr_1pod(project_name, services, given_containers): """ @@ -168,16 +186,17 @@ def tr_1pod(project_name, services, given_containers): services: {service_name: ["container_name1", "..."]}, currently only one is supported given_containers: [{}, ...] """ - pod = dict(name = project_name) + pod = dict(name=project_name) containers = [] for cnt0 in given_containers: - cnt = dict(cnt0, pod = project_name) + cnt = dict(cnt0, pod=project_name) # services can be accessed as localhost because they are on one pod # adjust hosts to point to localhost, TODO: adjust host env adj_hosts(services, cnt, '127.0.0.1') containers.append(cnt) return [pod], containers + @trans def tr_1podfw(project_name, services, given_containers): pods, containers = tr_1pod(project_name, services, given_containers) @@ -185,19 +204,24 @@ def tr_1podfw(project_name, services, given_containers): move_port_fw(pod, containers) return pods, containers + def down(project_name, dirname, pods, containers, dry_run, podman_path): for cnt in containers: cmd = """{} stop -t=1 '{name}'""".format(podman_path, **cnt) print(cmd) - if dry_run == False: subprocess.Popen(cmd, shell = True).wait() + if dry_run == False: + subprocess.Popen(cmd, shell=True).wait() for cnt in containers: cmd = """{} rm '{name}'""".format(podman_path, **cnt) print(cmd) - if dry_run == False: subprocess.Popen(cmd, shell = True).wait() + if dry_run == False: + subprocess.Popen(cmd, shell=True).wait() for pod in pods: cmd = """{} pod rm '{name}'""".format(podman_path, **pod) print(cmd) - if dry_run == False: subprocess.Popen(cmd, shell = True).wait() + if dry_run == False: + subprocess.Popen(cmd, shell=True).wait() + def container_to_args(cnt, dirname, podman_path): pod = cnt.get('pod') or '' @@ -206,14 +230,14 @@ def container_to_args(cnt, dirname, podman_path): '--name={}'.format(cnt.get('name')), '-d' ] - + if pod: args.append('--pod={}'.format(pod)) if cnt.get('read_only'): args.append('--read-only') for i in cnt.get('labels', []): args.extend(['--label', i]) - net=cnt.get("network_mode") + net = cnt.get("network_mode") if net: args.extend(['--network', net]) env = norm_as_list(cnt.get('environment', {})) @@ -249,7 +273,7 @@ def container_to_args(cnt, dirname, podman_path): if cnt.get('tty'): args.append('--tty') # currently podman shipped by fedora does not package this - #if cnt.get('init'): + # if cnt.get('init'): # args.append('--init') entrypoint = cnt.get('entrypoint') if entrypoint is not None: @@ -257,40 +281,49 @@ def container_to_args(cnt, dirname, podman_path): args.extend(['--entrypoint', json.dumps(entrypoint)]) else: args.extend(['--entrypoint', entrypoint]) - args.append(cnt.get('image')) # command, ..etc. + args.append(cnt.get('image')) # command, ..etc. command = cnt.get('command') if command is not None: # TODO: handle if command is string args.extend(command) return args + def rec_deps(services, container_by_name, cnt, init_service): deps = cnt["_deps"] for dep in deps: dep_cnts = services.get(dep) - if not dep_cnts: continue + if not dep_cnts: + continue dep_cnt = container_by_name.get(dep_cnts[0]) if dep_cnt: # TODO: avoid creating loops, A->B->A - if init_service and init_service in dep_cnt["_deps"]: continue - new_deps = rec_deps(services, container_by_name, dep_cnt, init_service) + if init_service and init_service in dep_cnt["_deps"]: + continue + new_deps = rec_deps(services, container_by_name, + dep_cnt, init_service) deps.update(new_deps) return deps + def flat_deps(services, container_by_name): for name, cnt in container_by_name.items(): - deps = set([(c.split(":")[0] if ":" in c else c) for c in cnt.get("links", []) ]) + deps = set([(c.split(":")[0] if ":" in c else c) + for c in cnt.get("links", [])]) deps.update(cnt.get("depends", [])) cnt["_deps"] = deps for name, cnt in container_by_name.items(): rec_deps(services, container_by_name, cnt, cnt.get('_service')) + def up(project_name, dirname, pods, containers, no_cleanup, dry_run, podman_path): - if dry_run == False: os.chdir(dirname) - + if dry_run == False: + os.chdir(dirname) + # no need remove them if they have same hash label - if no_cleanup == False: down(project_name, dirname, pods, containers, dry_run, podman_path) - + if no_cleanup == False: + down(project_name, dirname, pods, containers, dry_run, podman_path) + for pod in pods: args = [ podman_path, "pod", "create", @@ -301,11 +334,11 @@ def up(project_name, dirname, pods, containers, no_cleanup, dry_run, podman_path for i in ports: args.extend(['-p', i]) print(" ".join(args)) - + if dry_run == False: p = subprocess.Popen(args) print(p.wait()) - + for cnt in containers: # TODO: -e , --add-host, -v, --read-only args = container_to_args(cnt, dirname, podman_path) @@ -314,29 +347,33 @@ def up(project_name, dirname, pods, containers, no_cleanup, dry_run, podman_path if dry_run == False: 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) - if dry_run == False: time.sleep(1) + if dry_run == False: + time.sleep(1) + + +def main(command, filename, project_name, no_ansi, no_cleanup, dry_run, transform_policy, podman_path, host_env=None): + filename = os.path.realpath(filename) + dirname = os.path.dirname(filename) + dir_basename = os.path.basename(dirname) -def main(command, filename, project_name, no_ansi, no_cleanup, dry_run, transform_policy, podman_path, host_env = None): - filename = os.path.realpath(filename) - dirname = os.path.dirname(filename) - dir_basename = os.path.basename(dirname) - 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 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: compose = yaml.safe_load(f) - - # debug mode + + # debug mode #print(json.dumps(compose, indent = 2)) - - ver = compose.get('version') + + ver = compose.get('version') services = compose.get('services') podman_compose_labels = [ "io.podman.compose.config-hash=123", @@ -355,13 +392,14 @@ def main(command, filename, project_name, no_ansi, no_cleanup, dry_run, transfor container_names_by_service[service_name] = [] for num in range(1, replicas+1): name = "{project_name}_{service_name}_{num}".format( - project_name = project_name, - service_name = service_name, - num = num, + project_name=project_name, + service_name=service_name, + num=num, ) container_names_by_service[service_name].append(name) # print(service_name,service_desc) - cnt = dict(name = name, num = num, service_name = service_name, **service_desc) + cnt = dict(name=name, num=num, + service_name=service_name, **service_desc) labels = norm_as_list(cnt.get('labels')) labels.extend(podman_compose_labels) labels.extend([ @@ -375,50 +413,53 @@ def main(command, filename, project_name, no_ansi, no_cleanup, dry_run, transfor flat_deps(container_names_by_service, container_by_name) #print("deps:", [(c["name"], c["_deps"]) for c in given_containers]) given_containers = container_by_name.values() - given_containers.sort(key = lambda c: len(c.get('_deps') or [])) + given_containers.sort(key=lambda c: len(c.get('_deps') or [])) #print("sorted:", [c["name"] for c in given_containers]) tr = transformations[transform_policy] - 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] if cmd == "up": - up(project_name, dirname, pods, containers, no_cleanup, dry_run, podman_path) + up(project_name, dirname, pods, containers, + no_cleanup, dry_run, podman_path) elif cmd == "down": down(project_name, dirname, pods, containers, dry_run, podman_path) else: raise NotImplementedError("command {} is not implemented".format(cmd)) + if __name__ == "__main__": parser = argparse.ArgumentParser() - parser.add_argument('command', metavar = 'command', - help = 'command to run', - choices = ['up', 'down'], nargs = 1, default = "up") + parser.add_argument('command', metavar='command', + help='command to run', + choices=['up', 'down'], nargs=1, default="up") parser.add_argument("-f", "--file", - help = "Specify an alternate compose file (default: docker-compose.yml)", - type = str, default = "docker-compose.yml") + help="Specify an alternate compose file (default: docker-compose.yml)", + type=str, default="docker-compose.yml") parser.add_argument("-p", "--project-name", - help = "Specify an alternate project name (default: directory name)", - type = str, default = None) + help="Specify an alternate project name (default: directory name)", + 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") + help="Specify an alternate path to podman (default: use location in $PATH variable)", + type=str, default="podman") 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') + 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') + help="No action; perform a simulation of commands", action='store_true') parser.add_argument("-t", "--transform_policy", - help = "how to translate docker compose to podman [1pod|hostnet|accurate]", - choices = ['1pod', '1podfw', 'hostnet', 'cntnet', 'publishall', 'identity'], default = '1podfw') - + help="how to translate docker compose to podman [1pod|hostnet|accurate]", + choices=['1pod', '1podfw', 'hostnet', 'cntnet', 'publishall', 'identity'], default='1podfw') + args = parser.parse_args() - main ( - command = args.command, - filename = args.file, - project_name = args.project_name, - no_ansi = args.no_ansi, - no_cleanup = args.no_cleanup, - dry_run = args.dry_run, - transform_policy = args.transform_policy, - podman_path = args.podman_path + main( + command=args.command, + filename=args.file, + project_name=args.project_name, + no_ansi=args.no_ansi, + no_cleanup=args.no_cleanup, + dry_run=args.dry_run, + transform_policy=args.transform_policy, + podman_path=args.podman_path )