Skip to content

Commit 1768a65

Browse files
Red5dd-EScape
andauthored
Fixes the --all option, volumes of type 'bind' and read only option (#46)
(from @d-EScape) One PR that includes my suggestions for #17 and some new ones: The -all option would not work because every iteration of container_names could set the 'networks' and 'volumes' to None. Even if a previous container had a network. Later iterations could not add a network, because it was no longer a dict, resulting in an exception. The code might need some cleaning up. I left some comments and old pieces (commented out) to explain to @Red5d what I did and why. Since I am new to this script and the docker-compose format i might have overlooked something. Please check. Co-authored-by: d-EScape <[email protected]>
1 parent 357fef9 commit 1768a65

File tree

1 file changed

+42
-20
lines changed

1 file changed

+42
-20
lines changed

autocompose.py

Lines changed: 42 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
#! /usr/bin/env python
1+
#! /usr/bin/env python3
22
import datetime
33
import sys, argparse, pyaml, docker
44
from collections import OrderedDict
@@ -14,6 +14,7 @@ def main():
1414
parser.add_argument('-a', '--all', action='store_true', help='Include all active containers')
1515
parser.add_argument('-v', '--version', type=int, default=3, help='Compose file version (1 or 3)')
1616
parser.add_argument('cnames', nargs='*', type=str, help='The name of the container to process.')
17+
parser.add_argument('-c', '--createvolumes', action='store_true', help='Create new volumes instead of reusing existing ones')
1718
args = parser.parse_args()
1819

1920
container_names = args.cnames
@@ -23,24 +24,24 @@ def main():
2324
struct = {}
2425
networks = {}
2526
volumes = {}
27+
containers = {}
2628
for cname in container_names:
27-
cfile, c_networks, c_volumes = generate(cname)
29+
cfile, c_networks, c_volumes = generate(cname, createvolumes=args.createvolumes)
2830

2931
struct.update(cfile)
3032

31-
if c_networks is None:
32-
networks = None
33-
else:
33+
if not c_networks == None:
3434
networks.update(c_networks)
35-
36-
if c_volumes is None:
37-
volumes = None
38-
else:
35+
if not c_volumes == None:
3936
volumes.update(c_volumes)
40-
37+
38+
# moving the networks = None statemens outside of the for loop. Otherwise any container could reset it.
39+
if len(networks) == 0:
40+
networks = None
41+
if len(volumes) == 0:
42+
volumes = None
4143
render(struct, args, networks, volumes)
4244

43-
4445
def render(struct, args, networks, volumes):
4546
# Render yaml file
4647
if args.version == 1:
@@ -71,7 +72,7 @@ def fix_label(label: str):
7172
return f"'{label}'" if is_date_or_time(label) else label
7273

7374

74-
def generate(cname):
75+
def generate(cname, createvolumes=False):
7576
c = docker.from_env()
7677

7778
try:
@@ -110,7 +111,9 @@ def generate(cname):
110111
'networks': {x for x in cattrs['NetworkSettings']['Networks'].keys() if x not in default_networks},
111112
'security_opt': cattrs['HostConfig']['SecurityOpt'],
112113
'ulimits': cattrs['HostConfig']['Ulimits'],
113-
'volumes': [f'{m["Name"]}:{m["Destination"]}' for m in cattrs['Mounts'] if m['Type'] == 'volume'],
114+
# the line below would not handle type bind
115+
# 'volumes': [f'{m["Name"]}:{m["Destination"]}' for m in cattrs['Mounts'] if m['Type'] == 'volume'],
116+
'mounts': cattrs['Mounts'], #this could be moved outside of the dict. will only use it for generate
114117
'volume_driver': cattrs['HostConfig']['VolumeDriver'],
115118
'volumes_from': cattrs['HostConfig']['VolumesFrom'],
116119
'entrypoint': cattrs['Config']['Entrypoint'],
@@ -143,14 +146,34 @@ def generate(cname):
143146
if network.attrs['Name'] in values['networks']:
144147
networks[network.attrs['Name']] = {'external': (not network.attrs['Internal']),
145148
'name': network.attrs['Name']}
146-
149+
# volumes = {}
150+
# if values['volumes'] is not None:
151+
# for volume in values['volumes']:
152+
# volume_name = volume.split(':')[0]
153+
# volumes[volume_name] = {'external': True}
154+
# else:
155+
# volumes = None
156+
157+
# handles both the returned values['volumes'] (in c_file) and volumes for both, the bind and volume types
158+
# also includes the read only option
147159
volumes = {}
148-
if values['volumes'] is not None:
149-
for volume in values['volumes']:
150-
volume_name = volume.split(':')[0]
151-
volumes[volume_name] = {'external': True}
152-
else:
160+
mountpoints = []
161+
if values['mounts'] is not None:
162+
for mount in values['mounts']:
163+
destination = mount['Destination']
164+
if not mount['RW']:
165+
destination = destination + ':ro'
166+
if mount['Type'] == 'volume':
167+
mountpoints.append(mount['Name'] + ':' + destination)
168+
if not createvolumes:
169+
volumes[mount['Name']] = {'external': True} #to reuse an existing volume ... better to make that a choice? (cli argument)
170+
elif mount['Type'] == 'bind':
171+
mountpoints.append(mount['Source'] + ':' + destination)
172+
values['volumes'] = mountpoints
173+
if len(volumes) == 0:
153174
volumes = None
175+
values['mounts'] = None #remove this temporary data from the returned data
176+
154177

155178
# Check for command and add it if present.
156179
if cattrs['Config']['Cmd'] is not None:
@@ -186,4 +209,3 @@ def generate(cname):
186209

187210
if __name__ == "__main__":
188211
main()
189-

0 commit comments

Comments
 (0)