Skip to content

Commit 0a2042c

Browse files
authoredOct 16, 2018
Merge pull request #94 from jthiltges/add-cleanup
Add cleanup based on Derek's PR
2 parents dcbe240 + a2721ec commit 0a2042c

File tree

2 files changed

+100
-0
lines changed

2 files changed

+100
-0
lines changed
 

‎cleanup.py

+98
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
#!/usr/bin/env python
2+
"""
3+
Cleanup for Singularity container
4+
5+
Scan the images in the singularity CVMFS. If an image directory has not been "linked" to for 2 days,
6+
remove the image directory.
7+
8+
Maintains state in a file in the root singularity directory named .missing_links.json
9+
10+
"""
11+
import glob
12+
import os
13+
import json
14+
import shutil
15+
import argparse
16+
import time
17+
from datetime import datetime, timedelta
18+
19+
SINGULARITY_BASE = '/cvmfs/singularity.opensciencegrid.org'
20+
21+
# /cvmfs/singularity.opensciencegrid.org/.missing_links.json
22+
JSON_LOCATION = os.path.join(SINGULARITY_BASE, '.missing_links.json')
23+
24+
# JSON structure:
25+
# {
26+
# "missing_links": {
27+
# "/cvmfs/singularity.opensciencegrid.org/.images/7d/ba009871baa50e01d655a80f79728800401bbd0f5e7e18b5055839e713c09f": "<timestamp_last_linked>"
28+
# ...
29+
# }
30+
# }
31+
32+
def cleanup(delay=2, test=False):
33+
'''Clean up unlinked singularity images'''
34+
# Read in the old json, if it exists
35+
json_missing_links = {}
36+
try:
37+
with open(JSON_LOCATION) as json_file:
38+
json_missing_links = json.load(json_file)['missing_links']
39+
except (IOError, ValueError):
40+
# File is missing, unreadable, or damaged
41+
pass
42+
43+
# Get all the images in the repo
44+
45+
# Walk the directory /cvmfs/singularity.opensciencegrid.org/.images/*
46+
image_dirs = glob.glob(os.path.join(SINGULARITY_BASE, '.images/*/*'))
47+
48+
# Walk the named image dirs
49+
named_image_dir = glob.glob(os.path.join(SINGULARITY_BASE, '*/*'))
50+
51+
# For named image dir, look at the what the symlink points at
52+
for named_image in named_image_dir:
53+
link_target = os.readlink(named_image)
54+
while link_target in image_dirs:
55+
image_dirs.remove(link_target)
56+
# Remove linked image from json (in case link is restored)
57+
json_missing_links.pop(link_target, None)
58+
59+
# Now, for each image, see if it's in the json
60+
for image_dir in image_dirs:
61+
if image_dir not in json_missing_links:
62+
# Add it to the json
63+
print("Newly found missing link: %s" % (image_dir))
64+
json_missing_links[image_dir] = int(time.time())
65+
66+
# Loop through the json missing links, removing directories if over the `delay` days
67+
expiry = datetime.now() - timedelta(days=delay)
68+
for image_dir, last_linked in json_missing_links.items():
69+
date_last_linked = datetime.fromtimestamp(last_linked)
70+
if date_last_linked < expiry:
71+
# Confirm that we're inside the managed directory
72+
if not image_dir.startswith(SINGULARITY_BASE):
73+
continue
74+
# Remove the directory
75+
print("Removing missing link: %s" % image_dir)
76+
if not test:
77+
shutil.rmtree(image_dir)
78+
del json_missing_links[image_dir]
79+
80+
# Write out the end json
81+
with open(JSON_LOCATION, 'w') as json_file:
82+
json.dump({"missing_links": json_missing_links}, json_file)
83+
84+
def main():
85+
'''Main function'''
86+
args = parse_args()
87+
cleanup(test=args.test)
88+
89+
def parse_args():
90+
'''Parse CLI options'''
91+
parser = argparse.ArgumentParser()
92+
93+
parser.add_argument('--test', action='store_true',
94+
help="Don't remove files, but go through the motions of removing them.")
95+
return parser.parse_args()
96+
97+
if __name__ == "__main__":
98+
main()

‎cvmfs-singularity-sync

+2
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ import urllib2
4343
import hashlib
4444
import tempfile
4545
import tarfile
46+
import cleanup
4647

4748
def main():
4849
parser = argparse.ArgumentParser(description="Bootstrap Docker images for Singularity containers deployed to CVMFS")
@@ -129,6 +130,7 @@ def main():
129130
if retval:
130131
final_retval = retval
131132
print "All requested images have been attempted; final return code: %d" % final_retval
133+
cleanup.cleanup()
132134
return final_retval
133135

134136

0 commit comments

Comments
 (0)
Please sign in to comment.