19 Commits

Author SHA1 Message Date
acdoussan
357fef9782 better handling for default networks (#42)
* better handling for default networks

* fix for issues/43

* check for none explictly rather than allowing truthy/falsy conversion

* update to read volume data from mounts, rather than host config binds

Co-authored-by: Adam Doussan <acdoussan@Adams-MacBook-Pro.local>
2022-08-14 17:54:43 -04:00
acdoussan
0c4ff4fb25 Fix volumes missing in generated compose file (#41)
* fix volume export based off of https://github.com/Red5d/docker-autocompose/issues/17#issuecomment-943041549

* remove unneeded space

* export volumes in addition to networks

* fix syntax error

* actuall fix syntax errors

Co-authored-by: Adam Doussan <acdoussan@Adams-MacBook-Pro.local>
2022-08-13 16:31:05 -04:00
Red5d
e19c4654af Merge pull request #38 from ostafen/master
Add -a/--all flag to list all containers
2022-04-09 22:12:19 -04:00
Stefano
d6dddedb3d Add -a/--all flag to list all containers 2022-04-09 09:30:14 +02:00
Red5d
0dfdac353f Merge pull request #36 from ostafen/master
Format properly date/datetime labels
2022-03-16 09:24:12 -04:00
Red5d
a8f00e0deb Merge pull request #37 from alexanderpetrenz/master
Adding Name Attribute to each Network
2022-03-16 08:45:53 -04:00
alexanderpetrenz
adf98bb062 Merge branch 'Red5d:master' into master 2022-03-11 09:26:21 +01:00
Alexander Petrenz
1af6b49233 added name attribute to every retrieved network 2022-03-11 08:59:55 +01:00
Alexander Petrenz
40aaf8e82c fixed network retrieval 2022-03-11 08:59:47 +01:00
Stefano
63810906f9 Format properly date/datetime labels 2022-03-10 20:45:35 +01:00
Red5d
e32c9d4275 Merge pull request #35 from ostafen/master
Fix ERROR: network must be a mapping, not an array.
2022-03-10 14:04:38 -05:00
Stefano
caa747b605 Fix ERROR: network must be a mapping, not an array. 2022-03-10 15:32:50 +01:00
Red5d
d783902265 Merge pull request #34 from alexanderpetrenz/master
command property: replace string concatenation by taking over given list
2022-03-09 08:10:19 -05:00
Alexander Petrenz
e6badd31c3 now collecting networks not present in every container 2022-03-08 15:49:49 +01:00
Alexander Petrenz
3f756235b2 command property: replace string concatenation by taking over given list 2022-03-08 14:33:33 +01:00
Red5d
b9c096dd94 Merge pull request #33 from moschlar/patch-1
Update autocompose.py
2022-03-07 14:50:04 -05:00
Moritz Schlarb
e7dbe41f23 Update autocompose.py
Print error message to stderr so that I will be seen when redirecting stdout to `docker-compose.yml`
2022-03-07 11:56:06 +01:00
Red5d
d976d520b4 Merge pull request #32 from hgghyxo/patch-1
Update README.md
2022-02-24 11:14:25 -05:00
hgghyxo
ec211717ed Update README.md
adding a oneliner to print out all containers
2022-02-24 11:49:46 +01:00
2 changed files with 81 additions and 19 deletions

View File

@@ -43,3 +43,6 @@ Use the new image to generate a docker-compose file from a running container or
docker run --rm -v /var/run/docker.sock:/var/run/docker.sock ghcr.io/red5d/docker-autocompose <container-name-or-id> <additional-names-or-ids>... docker run --rm -v /var/run/docker.sock:/var/run/docker.sock ghcr.io/red5d/docker-autocompose <container-name-or-id> <additional-names-or-ids>...
To print out all containers in a docker-compose format:
docker run --rm -v /var/run/docker.sock:/var/run/docker.sock ghcr.io/red5d/docker-autocompose $(docker ps -aq)

View File

@@ -1,29 +1,74 @@
#! /usr/bin/env python #! /usr/bin/env python
import datetime
import sys, argparse, pyaml, docker import sys, argparse, pyaml, docker
from collections import OrderedDict from collections import OrderedDict
def list_container_names():
c = docker.from_env()
return [container.name for container in c.containers.list(all=True)]
def main(): def main():
parser = argparse.ArgumentParser(description='Generate docker-compose yaml definition from running container.') parser = argparse.ArgumentParser(description='Generate docker-compose yaml definition from running container.')
parser.add_argument('-a', '--all', action='store_true', help='Include all active containers')
parser.add_argument('-v', '--version', type=int, default=3, help='Compose file version (1 or 3)') parser.add_argument('-v', '--version', type=int, default=3, help='Compose file version (1 or 3)')
parser.add_argument('cnames', nargs='*', type=str, help='The name of the container to process.') parser.add_argument('cnames', nargs='*', type=str, help='The name of the container to process.')
args = parser.parse_args() args = parser.parse_args()
container_names = args.cnames
if args.all:
container_names.extend(list_container_names())
struct = {} struct = {}
networks = [] networks = {}
for cname in args.cnames: volumes = {}
cfile, networks = generate(cname) for cname in container_names:
cfile, c_networks, c_volumes = generate(cname)
struct.update(cfile) struct.update(cfile)
render(struct, args, networks) if c_networks is None:
networks = None
else:
networks.update(c_networks)
if c_volumes is None:
volumes = None
else:
volumes.update(c_volumes)
render(struct, args, networks, volumes)
def render(struct, args, networks): def render(struct, args, networks, volumes):
# Render yaml file # Render yaml file
if args.version == 1: if args.version == 1:
pyaml.p(OrderedDict(struct)) pyaml.p(OrderedDict(struct))
else: else:
pyaml.p(OrderedDict({'version': '"3"', 'services': struct, 'networks': networks})) ans = {'version': '"3"', 'services': struct}
if networks is not None:
ans['networks'] = networks
if volumes is not None:
ans['volumes'] = volumes
pyaml.p(OrderedDict(ans))
def is_date_or_time(s: str):
for parse_func in [datetime.date.fromisoformat, datetime.datetime.fromisoformat]:
try:
parse_func(s.rstrip('Z'))
return True
except ValueError:
pass
return False
def fix_label(label: str):
return f"'{label}'" if is_date_or_time(label) else label
def generate(cname): def generate(cname):
@@ -32,7 +77,7 @@ def generate(cname):
try: try:
cid = [x.short_id for x in c.containers.list(all=True) if cname == x.name or x.short_id in cname][0] cid = [x.short_id for x in c.containers.list(all=True) if cname == x.name or x.short_id in cname][0]
except IndexError: except IndexError:
print("That container is not available.") print("That container is not available.", file=sys.stderr)
sys.exit(1) sys.exit(1)
cattrs = c.containers.get(cid).attrs cattrs = c.containers.get(cid).attrs
@@ -44,6 +89,8 @@ def generate(cname):
cfile[cattrs['Name'][1:]] = {} cfile[cattrs['Name'][1:]] = {}
ct = cfile[cattrs['Name'][1:]] ct = cfile[cattrs['Name'][1:]]
default_networks = ['bridge', 'host', 'none']
values = { values = {
'cap_add': cattrs['HostConfig']['CapAdd'], 'cap_add': cattrs['HostConfig']['CapAdd'],
'cap_drop': cattrs['HostConfig']['CapDrop'], 'cap_drop': cattrs['HostConfig']['CapDrop'],
@@ -55,15 +102,15 @@ def generate(cname):
'environment': cattrs['Config']['Env'], 'environment': cattrs['Config']['Env'],
'extra_hosts': cattrs['HostConfig']['ExtraHosts'], 'extra_hosts': cattrs['HostConfig']['ExtraHosts'],
'image': cattrs['Config']['Image'], 'image': cattrs['Config']['Image'],
'labels': cattrs['Config']['Labels'], 'labels': {label: fix_label(value) for label, value in cattrs['Config']['Labels'].items()},
'links': cattrs['HostConfig']['Links'], 'links': cattrs['HostConfig']['Links'],
#'log_driver': cattrs['HostConfig']['LogConfig']['Type'], #'log_driver': cattrs['HostConfig']['LogConfig']['Type'],
#'log_opt': cattrs['HostConfig']['LogConfig']['Config'], #'log_opt': cattrs['HostConfig']['LogConfig']['Config'],
'logging': {'driver': cattrs['HostConfig']['LogConfig']['Type'], 'options': cattrs['HostConfig']['LogConfig']['Config']}, 'logging': {'driver': cattrs['HostConfig']['LogConfig']['Type'], 'options': cattrs['HostConfig']['LogConfig']['Config']},
'networks': {x for x in cattrs['NetworkSettings']['Networks'].keys() if x != 'bridge'}, 'networks': {x for x in cattrs['NetworkSettings']['Networks'].keys() if x not in default_networks},
'security_opt': cattrs['HostConfig']['SecurityOpt'], 'security_opt': cattrs['HostConfig']['SecurityOpt'],
'ulimits': cattrs['HostConfig']['Ulimits'], 'ulimits': cattrs['HostConfig']['Ulimits'],
'volumes': cattrs['HostConfig']['Binds'], 'volumes': [f'{m["Name"]}:{m["Destination"]}' for m in cattrs['Mounts'] if m['Type'] == 'volume'],
'volume_driver': cattrs['HostConfig']['VolumeDriver'], 'volume_driver': cattrs['HostConfig']['VolumeDriver'],
'volumes_from': cattrs['HostConfig']['VolumesFrom'], 'volumes_from': cattrs['HostConfig']['VolumesFrom'],
'entrypoint': cattrs['Config']['Entrypoint'], 'entrypoint': cattrs['Config']['Entrypoint'],
@@ -87,19 +134,31 @@ def generate(cname):
networks = {} networks = {}
if values['networks'] == set(): if values['networks'] == set():
del values['networks'] del values['networks']
assumed_default_network = list(cattrs['NetworkSettings']['Networks'].keys())[0]
values['network_mode'] = assumed_default_network
networks = None
else: else:
networklist = c.networks.list() networklist = c.networks.list()
for network in networklist: for network in networklist:
if network.attrs['Name'] in values['networks']: if network.attrs['Name'] in values['networks']:
networks[network.attrs['Name']] = {'external': (not network.attrs['Internal'])} networks[network.attrs['Name']] = {'external': (not network.attrs['Internal']),
'name': network.attrs['Name']}
volumes = {}
if values['volumes'] is not None:
for volume in values['volumes']:
volume_name = volume.split(':')[0]
volumes[volume_name] = {'external': True}
else:
volumes = None
# Check for command and add it if present. # Check for command and add it if present.
if cattrs['Config']['Cmd'] != None: if cattrs['Config']['Cmd'] is not None:
values['command'] = " ".join(cattrs['Config']['Cmd']), values['command'] = cattrs['Config']['Cmd']
# Check for exposed/bound ports and add them if needed. # Check for exposed/bound ports and add them if needed.
try: try:
expose_value = list(cattrs['Config']['ExposedPorts'].keys()) expose_value = list(cattrs['Config']['ExposedPorts'].keys())
ports_value = [cattrs['HostConfig']['PortBindings'][key][0]['HostIp']+':'+cattrs['HostConfig']['PortBindings'][key][0]['HostPort']+':'+key for key in cattrs['HostConfig']['PortBindings']] ports_value = [cattrs['HostConfig']['PortBindings'][key][0]['HostIp']+':'+cattrs['HostConfig']['PortBindings'][key][0]['HostPort']+':'+key for key in cattrs['HostConfig']['PortBindings']]
# If bound ports found, don't use the 'expose' value. # If bound ports found, don't use the 'expose' value.
@@ -122,7 +181,7 @@ def generate(cname):
if (value != None) and (value != "") and (value != []) and (value != 'null') and (value != {}) and (value != "default") and (value != 0) and (value != ",") and (value != "no"): if (value != None) and (value != "") and (value != []) and (value != 'null') and (value != {}) and (value != "default") and (value != 0) and (value != ",") and (value != "no"):
ct[key] = value ct[key] = value
return cfile, networks return cfile, networks, volumes
if __name__ == "__main__": if __name__ == "__main__":