Skip to content

Commit 7453753

Browse files
committed
feat: add --build-dir option to allow building on a scratch space
1 parent 9445ca5 commit 7453753

1 file changed

Lines changed: 52 additions & 13 deletions

File tree

cvmfs-singularity-sync

Lines changed: 52 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ import urllib.request, urllib.error, urllib.parse
3939
import hashlib
4040
import traceback
4141
import subprocess
42+
import shutil
4243
import dockerhub
4344
import cleanup
4445
import sqlitedict
@@ -109,6 +110,14 @@ def main():
109110
help="Indicate that this is a dry-run",
110111
default=False)
111112

113+
# Alternative build root for faster singularity build
114+
parser.add_argument("--build-dir",
115+
dest='build_dir',
116+
help="Alternative directory for singularity build (e.g. on a faster filesystem). "
117+
"If set, images are built here first, then moved to the final image directory.",
118+
type=str,
119+
default=None)
120+
112121
try:
113122
args = parser.parse_args()
114123
except:
@@ -141,7 +150,7 @@ def main():
141150
if args.docker:
142151
image = args.docker
143152
if not args.dryrun:
144-
return publish_image(image, singularity_rootfs, args.registry, doauth, manifest_cache)
153+
return publish_image(image, singularity_rootfs, args.registry, doauth, manifest_cache, build_dir=args.build_dir)
145154
else:
146155
return verify_image(image, args.registry, doauth, manifest_cache)
147156
else:
@@ -190,7 +199,7 @@ def main():
190199
for i in range(tries):
191200
if not args.dryrun:
192201
try:
193-
retval = publish_image(image, singularity_rootfs, registry, doauth, manifest_cache)
202+
retval = publish_image(image, singularity_rootfs, registry, doauth, manifest_cache, build_dir=args.build_dir)
194203
except Exception as ex:
195204
if i < tries -1:
196205
print("Failed to publish image: {}".format(image))
@@ -367,7 +376,7 @@ def get_manifest(hub, namespace, repo_name, repo_tag, manifest_cache):
367376
manifest_cache[digest] = manifest
368377
return manifest, digest
369378

370-
def publish_image(image, singularity_rootfs, registry, doauth, manifest_cache):
379+
def publish_image(image, singularity_rootfs, registry, doauth, manifest_cache, build_dir=None):
371380

372381
# Tell the user the namespace, repo name and tag
373382
registry, namespace, repo_name, repo_tag = parse_image(image)
@@ -442,24 +451,54 @@ def publish_image(image, singularity_rootfs, registry, doauth, manifest_cache):
442451
if 'password' in auth:
443452
singularity_env['SINGULARITY_DOCKER_PASSWORD'] = auth['password']
444453

454+
# Determine the actual build target directory.
455+
# If --build-dir is specified, build the sandbox on the (potentially faster)
456+
# alternative filesystem, then move the result into the final image_dir.
457+
if build_dir:
458+
build_dir = os.path.abspath(build_dir)
459+
# Mirror the image_parentdir/image_dir structure under build_dir so
460+
# concurrent builds for different images don't collide.
461+
build_parentdir = os.path.join(build_dir, image_hash[0:sep+3])
462+
build_target = os.path.join(build_parentdir, image_hash[sep+3:])
463+
try:
464+
os.makedirs(build_parentdir)
465+
except OSError as oe:
466+
if oe.errno != errno.EEXIST:
467+
raise
468+
else:
469+
build_target = image_dir
470+
445471
print("Calling singularity to build sandbox from image")
446-
subprocess.check_call(
447-
['singularity', '--silent', 'build',
448-
'--disable-cache=true', # Images are only downloaded once
449-
'--force', # Don't get stuck at a prompt if the target somehow exists
450-
'--fix-perms',
451-
'--sandbox',
452-
image_dir, 'docker://' + image],
453-
env=singularity_env)
472+
try:
473+
subprocess.check_call(
474+
['singularity', '--silent', 'build',
475+
'--disable-cache=true', # Images are only downloaded once
476+
'--force', # Don't get stuck at a prompt if the target somehow exists
477+
'--fix-perms',
478+
'--sandbox',
479+
build_target, 'docker://' + image],
480+
env=singularity_env)
481+
except BaseException:
482+
# Clean up partial build artifacts in the alternative build root
483+
if build_dir and os.path.exists(build_target):
484+
print("Cleaning up partial build at %s" % build_target)
485+
shutil.rmtree(build_target, ignore_errors=True)
486+
raise
454487

455488
# Various fixups to make the image compatible with CVMFS and singularity.
456-
srv = os.path.join(image_dir, "srv")
457-
cvmfs = os.path.join(image_dir, "cvmfs")
489+
srv = os.path.join(build_target, "srv")
490+
cvmfs = os.path.join(build_target, "cvmfs")
458491
if not os.path.exists(srv):
459492
os.makedirs(srv)
460493
if not os.path.exists(cvmfs):
461494
os.makedirs(cvmfs)
462495

496+
# If we built in an alternative build root, move the result to the final
497+
# image directory inside CVMFS.
498+
if build_dir:
499+
print("Moving image from build dir %s to %s" % (build_target, image_dir))
500+
shutil.move(build_target, image_dir)
501+
463502
make_final_symlink(image_dir, singularity_rootfs, namespace, repo_name, repo_tag)
464503
# Publish CVMFS as necessary.
465504
return publish_txn()

0 commit comments

Comments
 (0)