mirror of
https://github.com/Red5d/docker-autocompose
synced 2026-03-04 23:18:45 +00:00
Compare commits
3 Commits
357fef9782
...
85e398193f
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
85e398193f | ||
|
|
dfc73d0bbd | ||
|
|
1768a65fce |
@@ -46,3 +46,7 @@ Use the new image to generate a docker-compose file from a running container or
|
||||
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)
|
||||
|
||||
## Contributing
|
||||
|
||||
When making changes, please validate the output from the script by writing it to a file (docker-compose.yml or docker-compose.yaml) and running "docker-compose config" in the same folder with it to ensure that the resulting compose file will be accepted by docker-compose.
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#! /usr/bin/env python
|
||||
#! /usr/bin/env python3
|
||||
import datetime
|
||||
import sys, argparse, pyaml, docker
|
||||
from collections import OrderedDict
|
||||
@@ -14,6 +14,7 @@ def main():
|
||||
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('cnames', nargs='*', type=str, help='The name of the container to process.')
|
||||
parser.add_argument('-c', '--createvolumes', action='store_true', help='Create new volumes instead of reusing existing ones')
|
||||
args = parser.parse_args()
|
||||
|
||||
container_names = args.cnames
|
||||
@@ -23,30 +24,30 @@ def main():
|
||||
struct = {}
|
||||
networks = {}
|
||||
volumes = {}
|
||||
containers = {}
|
||||
for cname in container_names:
|
||||
cfile, c_networks, c_volumes = generate(cname)
|
||||
cfile, c_networks, c_volumes = generate(cname, createvolumes=args.createvolumes)
|
||||
|
||||
struct.update(cfile)
|
||||
|
||||
if c_networks is None:
|
||||
networks = None
|
||||
else:
|
||||
if not c_networks == None:
|
||||
networks.update(c_networks)
|
||||
|
||||
if c_volumes is None:
|
||||
volumes = None
|
||||
else:
|
||||
if not c_volumes == None:
|
||||
volumes.update(c_volumes)
|
||||
|
||||
|
||||
# moving the networks = None statemens outside of the for loop. Otherwise any container could reset it.
|
||||
if len(networks) == 0:
|
||||
networks = None
|
||||
if len(volumes) == 0:
|
||||
volumes = None
|
||||
render(struct, args, networks, volumes)
|
||||
|
||||
|
||||
def render(struct, args, networks, volumes):
|
||||
# Render yaml file
|
||||
if args.version == 1:
|
||||
pyaml.p(OrderedDict(struct))
|
||||
else:
|
||||
ans = {'version': '"3"', 'services': struct}
|
||||
ans = {'version': '"3.6"', 'services': struct}
|
||||
|
||||
if networks is not None:
|
||||
ans['networks'] = networks
|
||||
@@ -71,7 +72,7 @@ def fix_label(label: str):
|
||||
return f"'{label}'" if is_date_or_time(label) else label
|
||||
|
||||
|
||||
def generate(cname):
|
||||
def generate(cname, createvolumes=False):
|
||||
c = docker.from_env()
|
||||
|
||||
try:
|
||||
@@ -110,7 +111,9 @@ def generate(cname):
|
||||
'networks': {x for x in cattrs['NetworkSettings']['Networks'].keys() if x not in default_networks},
|
||||
'security_opt': cattrs['HostConfig']['SecurityOpt'],
|
||||
'ulimits': cattrs['HostConfig']['Ulimits'],
|
||||
'volumes': [f'{m["Name"]}:{m["Destination"]}' for m in cattrs['Mounts'] if m['Type'] == 'volume'],
|
||||
# the line below would not handle type bind
|
||||
# 'volumes': [f'{m["Name"]}:{m["Destination"]}' for m in cattrs['Mounts'] if m['Type'] == 'volume'],
|
||||
'mounts': cattrs['Mounts'], #this could be moved outside of the dict. will only use it for generate
|
||||
'volume_driver': cattrs['HostConfig']['VolumeDriver'],
|
||||
'volumes_from': cattrs['HostConfig']['VolumesFrom'],
|
||||
'entrypoint': cattrs['Config']['Entrypoint'],
|
||||
@@ -143,14 +146,34 @@ def generate(cname):
|
||||
if network.attrs['Name'] in values['networks']:
|
||||
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
|
||||
|
||||
# handles both the returned values['volumes'] (in c_file) and volumes for both, the bind and volume types
|
||||
# also includes the read only option
|
||||
volumes = {}
|
||||
if values['volumes'] is not None:
|
||||
for volume in values['volumes']:
|
||||
volume_name = volume.split(':')[0]
|
||||
volumes[volume_name] = {'external': True}
|
||||
else:
|
||||
mountpoints = []
|
||||
if values['mounts'] is not None:
|
||||
for mount in values['mounts']:
|
||||
destination = mount['Destination']
|
||||
if not mount['RW']:
|
||||
destination = destination + ':ro'
|
||||
if mount['Type'] == 'volume':
|
||||
mountpoints.append(mount['Name'] + ':' + destination)
|
||||
if not createvolumes:
|
||||
volumes[mount['Name']] = {'external': True} #to reuse an existing volume ... better to make that a choice? (cli argument)
|
||||
elif mount['Type'] == 'bind':
|
||||
mountpoints.append(mount['Source'] + ':' + destination)
|
||||
values['volumes'] = mountpoints
|
||||
if len(volumes) == 0:
|
||||
volumes = None
|
||||
values['mounts'] = None #remove this temporary data from the returned data
|
||||
|
||||
|
||||
# Check for command and add it if present.
|
||||
if cattrs['Config']['Cmd'] is not None:
|
||||
@@ -186,4 +209,3 @@ def generate(cname):
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
|
||||
|
||||
Reference in New Issue
Block a user