def __init__(self, easyconfigs): self.container_base = build_option('container_base') self.container_build_image = build_option('container_build_image') self.container_path = container_path() self.easyconfigs = easyconfigs self.image_format = build_option('container_image_format') self.img_name = build_option('container_image_name') self.log = fancylogger.getLogger(self.__class__.__name__, fname=False) self.mns = ActiveMNS() self.tmpdir = build_option('container_tmpdir')
def build_singularity_image(def_path): """Build Singularity container image by calling out to 'singularity' (requires admin privileges!).""" cont_path = container_path() def_file = os.path.basename(def_path) # use --imagename if specified, otherwise derive based on filename of recipe img_name = build_option('container_image_name') if img_name is None: # definition file Singularity.<app>-<version, container name <app>-<version>.<img|simg> img_name = def_file.split('.', 1)[1] cmd_opts = '' image_format = build_option('container_image_format') # squashfs image format (default for Singularity) if image_format in [None, CONT_IMAGE_FORMAT_SQUASHFS]: img_path = os.path.join(cont_path, img_name + '.simg') # ext3 image format, creating as writable container elif image_format == CONT_IMAGE_FORMAT_EXT3: img_path = os.path.join(cont_path, img_name + '.img') cmd_opts = '--writable' # sandbox image format, creates as a directory but acts like a container elif image_format == CONT_IMAGE_FORMAT_SANDBOX: img_path = os.path.join(cont_path, img_name) cmd_opts = '--sandbox' else: raise EasyBuildError("Unknown container image format specified for Singularity: %s" % image_format) if os.path.exists(img_path): if build_option('force'): print_msg("WARNING: overwriting existing container image at %s due to --force" % img_path) remove_file(img_path) else: raise EasyBuildError("Container image already exists at %s, not overwriting it without --force", img_path) # resolve full path to 'singularity' binary, since it may not be available via $PATH under sudo... singularity = which('singularity') cmd_env = '' singularity_tmpdir = build_option('container_tmpdir') if singularity_tmpdir: cmd_env += 'SINGULARITY_TMPDIR=%s' % singularity_tmpdir cmd = ' '.join(['sudo', cmd_env, singularity, 'build', cmd_opts, img_path, def_path]) print_msg("Running '%s', you may need to enter your 'sudo' password..." % cmd) run_cmd(cmd, stream_output=True) print_msg("Singularity image created at %s" % img_path, log=_log)
def generate_singularity_recipe(easyconfigs, container_base): """Main function to Singularity definition file and image.""" cont_path = container_path() # make sure location to write container recipes & images exists mkdir(cont_path, parents=True) base_specs = parse_container_base(container_base) # extracting application name,version, version suffix, toolchain name, toolchain version from # easyconfig class bootstrap_agent = base_specs['bootstrap_agent'] base_image, base_image_tag = None, None # with localimage it only takes 2 arguments. --container-base localimage:/path/to/image # checking if path to image is valid and verify image extension is '.img' or '.simg' if base_specs['bootstrap_agent'] == LOCALIMAGE: base_image = base_specs['arg1'] if os.path.exists(base_image): # get the extension of container image image_ext = os.path.splitext(base_image)[1] if image_ext == '.img' or image_ext == '.simg': _log.debug("Extension for base container image to use is OK: %s", image_ext) else: raise EasyBuildError("Invalid image extension '%s' must be .img or .simg", image_ext) else: raise EasyBuildError("Singularity base image at specified path does not exist: %s", base_image) # otherwise, bootstrap agent is 'docker' or 'shub' # format --container-base {docker|shub}:<image>:<tag> else: base_image = base_specs['arg1'] # image tag is optional base_image_tag = base_specs.get('arg2', None) bootstrap_from = base_image if base_image_tag: bootstrap_from += ':' + base_image_tag # if there is osdependencies in easyconfig then add them to Singularity recipe install_os_deps = '' for ec in easyconfigs: for osdep in ec['ec']['osdependencies']: if isinstance(osdep, basestring): install_os_deps += "yum install -y %s\n" % osdep # tuple entry indicates multiple options elif isinstance(osdep, tuple): install_os_deps += "yum --skip-broken -y install %s\n" % ' '.join(osdep) else: raise EasyBuildError("Unknown format of OS dependency specification encountered: %s", osdep) # module names to load in container environment mod_names = [e['ec'].full_mod_name for e in easyconfigs] # name of Singularity definition file img_name = build_option('container_image_name') if img_name: def_file_label = os.path.splitext(img_name)[0] else: def_file_label = mod_names[0].replace('/', '-') def_file = 'Singularity.%s' % def_file_label # adding all the regions for writing the Singularity definition file content = SINGULARITY_TEMPLATE % { 'bootstrap': bootstrap_agent, 'from': bootstrap_from, 'install_os_deps': install_os_deps, 'easyconfigs': ' '.join(os.path.basename(e['spec']) for e in easyconfigs), 'mod_names': ' '.join(mod_names), } def_path = os.path.join(cont_path, def_file) if os.path.exists(def_path): if build_option('force'): print_msg("WARNING: overwriting existing container recipe at %s due to --force" % def_path) else: raise EasyBuildError("Container recipe at %s already exists, not overwriting it without --force", def_path) write_file(def_path, content) print_msg("Singularity definition file created at %s" % def_path, log=_log) return def_path