Skip to content

Commit 9361e5d

Browse files
authored
Merge pull request #26 from NeroBurner/fix_cleanup
fix cleanup using graph.dot
2 parents 169d3e9 + 30265a3 commit 9361e5d

1 file changed

Lines changed: 47 additions & 29 deletions

File tree

aptly/publisher/__init__.py

Lines changed: 47 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -166,39 +166,57 @@ def remove_unused_packages(self, repo_dict):
166166
self.client.do_delete('/repos/%s/packages' % repo_name, data={'PackageRefs': packages})
167167

168168
def cleanup_snapshots(self):
169-
snapshots = self.client.do_get('/snapshots', {'sort': 'time'})
170-
exclude = []
169+
# requesting graph.dot always works, even if graphviz (dot) is not installed
170+
dot_data = self.client.do_get('/graph.dot')
171171

172-
# Add currently published snapshots into exclude list
173-
publishes = self.client.do_get('/publish')
174-
for publish in publishes:
175-
exclude.extend(
176-
[x['Name'] for x in publish['Sources']]
177-
)
172+
# extract edges from dot-data
173+
# edges = list of pairs with (node_id, node_id)
174+
edges = re.findall('"([^"]+)"->"([^"]+)";', dot_data)
178175

179-
# Add last snapshots into exclude list
180-
# TODO: ignore snapshots that are source for merged snapshots
181-
snapshot_latest = []
182-
for snapshot in snapshots:
183-
base_name = snapshot['Name'].split('-')[0]
184-
if base_name not in snapshot_latest:
185-
snapshot_latest.append(base_name)
186-
if snapshot['Name'] not in exclude:
187-
lg.debug("Not deleting latest snapshot %s" % snapshot['Name'])
188-
exclude.append(snapshot['Name'])
176+
# extract nodes from dot-data
177+
# list of tuples with (node_id, node_type, node_name)
178+
# node_type is one of 'Repo'|'Snapshot'|'Publish'
179+
nodes_raw = re.findall('[ \t]+"([^"]+)".*label="{(Repo|Snapshot|Published) ([^|]+)[^\}"]+}"', dot_data)
189180

190-
exclude = self.list_uniq(exclude)
181+
# convert nodes_raw into nodes
182+
# nodes = dict of node_id to node
183+
# node = (node_type, node_name)
184+
nodes = {node_id: (node_type, node_name) for node_id, node_type, node_name in nodes_raw}
191185

192-
for snapshot in snapshots:
193-
if snapshot['Name'] not in exclude:
194-
lg.info("Deleting snapshot %s" % snapshot['Name'])
195-
try:
196-
self.client.do_delete('/snapshots/%s' % snapshot['Name'])
197-
except AptlyException as e:
198-
if e.res.status_code == 409:
199-
lg.warning("Snapshot %s is being used, can't delete" % snapshot['Name'])
200-
else:
201-
raise
186+
# start with published and flood fill other nodes
187+
published_node_ids = [key for key in nodes if nodes[key][0] == 'Published']
188+
189+
# do flood fill
190+
while True:
191+
# get a list of source node_ids of incoming edges
192+
incoming_node_ids = [edge_from for edge_from, edge_to in edges if edge_to in published_node_ids and edge_from not in published_node_ids]
193+
194+
# add incoming node_ids
195+
published_node_ids.extend(incoming_node_ids)
196+
197+
# no new nodes, we are finished
198+
if len(incoming_node_ids) == 0:
199+
break
200+
201+
# we got published nodes, invert the set
202+
non_published_node_ids = [node_id for node_id in nodes if node_id not in published_node_ids]
203+
204+
# list of nodes with (node_type, node_name)
205+
unreleased_nodes = [nodes[node_id] for node_id in non_published_node_ids]
206+
207+
# use only nodes of type 'Snapshot'
208+
unreleased_snapshots = [node[1] for node in unreleased_nodes if node[0] == 'Snapshot']
209+
210+
# actually delete snapshots that are not published
211+
for snapshot in unreleased_snapshots:
212+
lg.info("Deleting snapshot %s" % snapshot)
213+
try:
214+
self.client.do_delete('/snapshots/%s' % snapshot)
215+
except AptlyException as e:
216+
if e.res.status_code == 409:
217+
lg.warning("Snapshot %s is being used, can't delete" % snapshot)
218+
else:
219+
raise
202220

203221

204222
class Publish(object):

0 commit comments

Comments
 (0)