From fe4d63ffdb5bc4e09a802116ff60a4ae02675898 Mon Sep 17 00:00:00 2001 From: MontrealSergiy Date: Fri, 15 Dec 2023 14:43:58 -0500 Subject: [PATCH 01/12] rename singularity to apptainer in variables --- .../app/controllers/bourreaux_controller.rb | 2 +- .../controllers/tool_configs_controller.rb | 8 +- BrainPortal/app/models/bourreau.rb | 6 +- BrainPortal/app/models/cluster_task.rb | 138 +++++++++--------- .../models/sing_bindmount_data_provider.rb | 38 ++--- .../app/models/sing_squashfs_data_provider.rb | 28 ++-- BrainPortal/app/models/tool_config.rb | 53 +++---- BrainPortal/app/views/bourreaux/show.html.erb | 6 +- .../_dp_types_explained.html.erb | 6 +- .../app/views/tool_configs/show.html.erb | 6 +- .../config/console_rc/lib/pretty_view.rb | 4 +- ...singularity_to_apptainer_overlays_specs.rb | 5 + ...gularity_to_apptainer_use_short_workdir.rb | 5 + ...ingularity_to_apptainer_executable_name.rb | 5 + .../schema_task_generator.rb | 6 +- BrainPortal/spec/models/tool_config_spec.rb | 4 +- 16 files changed, 168 insertions(+), 152 deletions(-) create mode 100644 BrainPortal/db/migrate/20231214214407_rename_singularity_to_apptainer_overlays_specs.rb create mode 100644 BrainPortal/db/migrate/20231214214526_rename_singularity_to_apptainer_use_short_workdir.rb create mode 100644 BrainPortal/db/migrate/20231214214549_rename_singularity_to_apptainer_executable_name.rb diff --git a/BrainPortal/app/controllers/bourreaux_controller.rb b/BrainPortal/app/controllers/bourreaux_controller.rb index 988e68fb3..d56714008 100644 --- a/BrainPortal/app/controllers/bourreaux_controller.rb +++ b/BrainPortal/app/controllers/bourreaux_controller.rb @@ -623,7 +623,7 @@ def bourreau_params #:nodoc: :cms_default_queue, :cms_extra_qsub_args, :cms_shared_dir, :workers_instances, :workers_chk_time, :workers_log_to, :workers_verbose, :help_url, :rr_timeout, :proxied_host, :spaced_dp_ignore_patterns, :support_email, :system_from_email, :external_status_page_url, - :docker_executable_name, :docker_present, :singularity_executable_name, :singularity_present, + :docker_executable_name, :docker_present, :apptainer_executable_name, :apptainer_present, :small_logo, :large_logo, :license_agreements ) end diff --git a/BrainPortal/app/controllers/tool_configs_controller.rb b/BrainPortal/app/controllers/tool_configs_controller.rb index d71b2f6da..7ab32f29f 100644 --- a/BrainPortal/app/controllers/tool_configs_controller.rb +++ b/BrainPortal/app/controllers/tool_configs_controller.rb @@ -206,7 +206,7 @@ def update #:nodoc: @tool_config.container_image_userfile_id = other_tc.container_image_userfile_id @tool_config.container_exec_args = other_tc.container_exec_args.presence @tool_config.container_index_location = other_tc.container_index_location - @tool_config.singularity_overlays_specs = other_tc.singularity_overlays_specs + @tool_config.apptainer_overlays_specs = other_tc.apptainer_overlays_specs @tool_config.extra_qsub_args = other_tc.extra_qsub_args @tool_config.cloud_disk_image = other_tc.cloud_disk_image @tool_config.cloud_vm_user = other_tc.cloud_vm_user @@ -243,7 +243,7 @@ def update #:nodoc: container_image_userfile_id containerhub_image_name container_engine container_index_location container_exec_args inputs_readonly - singularity_overlays_specs singularity_use_short_workdir + apptainer_overlays_specs apptainer_use_short_workdir boutiques_descriptor_path ) ) @@ -304,8 +304,8 @@ def tool_config_params #:nodoc: :version_name, :description, :tool_id, :bourreau_id, :env_array, :script_prologue, :script_epilogue, :group_id, :ncpus, :container_image_userfile_id, :containerhub_image_name, :container_index_location, :inputs_readonly, - :container_engine, :extra_qsub_args, :singularity_overlays_specs, :container_exec_args, - :singularity_use_short_workdir, + :container_engine, :extra_qsub_args, :apptainer_overlays_specs, :container_exec_args, + :apptainer_use_short_workdir, :boutiques_descriptor_path, # The configuration of a tool in a VM managed by a # ScirCloud Bourreau is defined by the following diff --git a/BrainPortal/app/models/bourreau.rb b/BrainPortal/app/models/bourreau.rb index 2124e9e65..98d4aaf78 100644 --- a/BrainPortal/app/models/bourreau.rb +++ b/BrainPortal/app/models/bourreau.rb @@ -80,11 +80,11 @@ def docker_present? #:nodoc: alias docker_present docker_present? #:nodoc: - def singularity_present? #:nodoc: - singularity_executable_name.present? + def apptainer_present? #:nodoc: + apptainer_executable_name.present? end - alias singularity_present singularity_present? #:nodoc: + alias apptainer_present apptainer_present? #:nodoc: diff --git a/BrainPortal/app/models/cluster_task.rb b/BrainPortal/app/models/cluster_task.rb index e2623f0c2..5d3ab1806 100644 --- a/BrainPortal/app/models/cluster_task.rb +++ b/BrainPortal/app/models/cluster_task.rb @@ -424,7 +424,7 @@ def make_available(userfile, file_path, userfile_sub_path = nil, start_dir = nil userfile = Userfile.find(userfile) unless userfile.is_a?(Userfile) # Detect if we're trying to access a userfile content that is - # containerized in a singularity overlay. + # containerized in a apptainer overlay. is_local = nil if self.tool_config .data_providers_with_overlays @@ -432,7 +432,7 @@ def make_available(userfile, file_path, userfile_sub_path = nil, start_dir = nil .include? userfile.data_provider_id userfile_path = Pathname.new(userfile.provider_full_path) # path inside container is_local = true - self.addlog("Input file '#{userfile.name}' is on a local Singularity overlay") + self.addlog("Input file '#{userfile.name}' is on a local apptainer overlay") else userfile.sync_to_cache userfile_path = Pathname.new(userfile.cache_full_path) @@ -618,10 +618,10 @@ def tool_config_system(command) # Build script script = "" - # flag to guaranty propagation of env variables to the singularity/apptainer + # flag to guaranty propagation of env variables to the singularity/Apptainer # as far I know only needed to reverse effect of --cleanenv option, and otherwise all vars are copied to the container # yet potentially more cases may be identified - propagate = self.use_singularity? + propagate = self.use_apptainer? # Add prologues in specialization order script += bourreau_glob_config.to_bash_prologue propagate if bourreau_glob_config script += tool_glob_config.to_bash_prologue propagate if tool_glob_config @@ -1778,14 +1778,14 @@ def submit_cluster_job # Joined version of all the lines in the scientific script command_script = commands.join("\n") - # In case of Docker or Singularity, we rewrite the scientific script inside + # In case of Docker or Apptainer, we rewrite the scientific script inside # yet another wrapper script. if self.use_docker? command_script = wrap_new_HOME(command_script, self.full_cluster_workdir) command_script = self.docker_commands(command_script) - elsif self.use_singularity? - load_singularity_image - command_script = self.singularity_commands(command_script) # note: invokes wrap_new_HOME itself + elsif self.use_apptainer? + load_apptainer_image + command_script = self.apptainer_commands(command_script) # note: invokes wrap_new_HOME itself else command_script = wrap_new_HOME(command_script, self.full_cluster_workdir) end @@ -1800,9 +1800,9 @@ def submit_cluster_job # by ClusterTask # #{ClusterTask.revision_info.to_s} -#{bourreau_glob_config ? bourreau_glob_config.to_bash_prologue(self.use_singularity?) : ""} -#{tool_glob_config ? tool_glob_config.to_bash_prologue(self.use_singularity?) : ""} -#{tool_config ? tool_config.to_bash_prologue(self.use_singularity?) : ""} +#{bourreau_glob_config ? bourreau_glob_config.to_bash_prologue(self.use_apptainer?) : ""} +#{tool_glob_config ? tool_glob_config.to_bash_prologue(self.use_apptainer?) : ""} +#{tool_config ? tool_config.to_bash_prologue(self.use_apptainer?) : ""} #{self.supplemental_cbrain_tool_config_init} # CbrainTask '#{self.name}' commands section @@ -1849,9 +1849,9 @@ def submit_cluster_job # Record runtime environment bash #{Rails.root.to_s.bash_escape}/vendor/cbrain/bin/runtime_info.sh > #{runtime_info_basename} -# With apptainer/singularity jobs, we sometimes get an error booting the container, +# With Apptainer jobs, we sometimes get an error booting the container, # so we try up to five times. -for singularity_attempts in 1 2 3 4 5 ; do # note: the number 5 is used a bit below in an 'if' +for apptainer_attempts in 1 2 3 4 5 ; do # note: the number 5 is used a bit below in an 'if' SECONDS=0 # this is a special bash variable, see the doc # stdout and stderr captured below will be re-substituted in @@ -1861,20 +1861,20 @@ def submit_cluster_job test $status -eq 0 && break # all is good - # Detect failed boot of singularity container + # Detect failed boot of apptainer container if ! grep -i 'FATAL.*container.*creation.*failed' #{science_stderr_basename} >/dev/null ; then break # move on, for any other error or even non zero successes fi # Detect that final attempt to boot failed - if test $singularity_attempts -eq 5 ; then + if test $apptainer_attempts -eq 5 ; then echo "Apptainer container boot attempts all failed, giving up." status=99 # why not break fi # Cleanup and try again - echo "Apptainer boot attempt number $singularity_attempts failed, trying again." + echo "Apptainer boot attempt number $apptainer_attempts failed, trying again." grep -v -i 'FATAL.*container.*creation.*failed' < #{science_stderr_basename} > #{science_stderr_basename}.clean mv -f #{science_stderr_basename}.clean #{science_stderr_basename} done @@ -2311,34 +2311,34 @@ def load_docker_image_cmd #:nodoc: ################################################################## - # Singularity support methods + # Apptainer support methods ################################################################## - # Returns true if the task's ToolConfig is configured to point to a singularity image + # Returns true if the task's ToolConfig is configured to point to a Apptainer image # for the task's processing. - def use_singularity? - return self.tool_config.use_singularity? + def use_apptainer? + return self.tool_config.use_apptainer? end - # Return the 'singularity' command to be used for the task; this is fetched - # from the Bourreau's own attribute. Default: "singularity". - def singularity_executable_name - return self.bourreau.singularity_executable_name.presence || "singularity" + # Return the 'apptainer' command to be used for the task; this is fetched + # from the Bourreau's own attribute. Default: "apptainer". + def apptainer_executable_name + return self.bourreau.apptainer_executable_name.presence || "apptainer" end # Returns true if the admin has configured this option in the # task's ToolConfig attributes. - def use_singularity_short_workdir? - self.tool_config.singularity_use_short_workdir + def use_apptainer_short_workdir? + self.tool_config.apptainer_use_short_workdir end # Returns the command line(s) associated with the task, wrapped in - # a Singularity call if a Singularity image has to be used. +command_script+ + # a Apptainer call if a Apptainer image has to be used. +command_script+ # is the raw scientific bash script. - def singularity_commands(command_script) + def apptainer_commands(command_script) - # Basename of the singularity wrapper script - singularity_wrapper_basename = ".singularity.#{self.run_id}.sh" + # Basename of the apptainer wrapper script + apptainer_wrapper_basename = ".apptainer.#{self.run_id}.sh" # Values we substitute in our script: # Numbers in (paren) correspond to the comment @@ -2347,9 +2347,9 @@ def singularity_commands(command_script) # (7) The path to the task's work directory task_workdir = self.full_cluster_workdir # a string short_workdir = "/T#{self.id}" # only used in short workdir mode - effect_workdir = use_singularity_short_workdir? ? short_workdir : task_workdir + effect_workdir = use_apptainer_short_workdir? ? short_workdir : task_workdir - # (1) additional singularity execution command options defined in ToolConfig + # (1) additional Apptainer execution command options defined in ToolConfig container_exec_args = self.tool_config.container_exec_args.presence # (2) The root of the DataProvider cache @@ -2383,7 +2383,7 @@ def singularity_commands(command_script) # Some of them might be patterns (e.g. /a/b/data*.squashfs) that need to # be resolved locally. # This will be a string "--overlay=path:ro --overlay=path:ro" etc. - overlay_paths = self.tool_config.singularity_overlays_full_paths.map do |path| + overlay_paths = self.tool_config.apptainer_overlays_full_paths.map do |path| paths = Dir.glob(path) # checks on the local file system cb_error "Can't find any overlays matching '#{path}'" if paths.blank? paths @@ -2395,8 +2395,8 @@ def singularity_commands(command_script) # Wrap new HOME environment command_script = wrap_new_HOME(command_script, effect_workdir) - # Set singularity command - singularity_commands = <<-SINGULARITY_COMMANDS + # Set apptainer command + apptainer_commands = <<-APPTAINER_COMMANDS # Note to developers: # During a standard CBRAIN task, this script is invoked with no arguments @@ -2406,7 +2406,7 @@ def singularity_commands(command_script) # These two variables control the mode switching at the end of the script. mode="exec" -sing_basename=./#{singularity_wrapper_basename.bash_escape} # note: the ./ is necessary +sing_basename=./#{apptainer_wrapper_basename.bash_escape} # note: the ./ is necessary # In 'shell' mode we replace them with other things. if test $# -eq 1 -a "X$1" = "Xshell" ; then @@ -2414,18 +2414,18 @@ def singularity_commands(command_script) sing_basename="" fi -# Build a local wrapper script to run in a singularity container -cat << \"SINGULARITYJOB\" > #{singularity_wrapper_basename.bash_escape} +# Build a local wrapper script to run in a apptainer container +cat << \"APPTAINERJOB\" > #{apptainer_wrapper_basename.bash_escape} #!/bin/bash -# Singularity wrapper script created automatically for #{self.class.to_s} +# Apptainer wrapper script created automatically for #{self.class.to_s} # #{self.class.revision_info.to_s} # by ClusterTask # #{ClusterTask.revision_info.to_s} # CBRAIN internal consistency test 1: must run under proper UID if test "$UID" -ne "#{Process.uid}" ; then - echo "Singularity internal script running with wrong UID (expected UID=#{Process.uid})" + echo "Apptainer internal script running with wrong UID (expected UID=#{Process.uid})" echo "Runtime IDs: `id`" exit 2 fi @@ -2461,7 +2461,7 @@ def singularity_commands(command_script) # CBRAIN internal consistency test 6: all mounted ext3 filesystems should be # on a device different from the task's workdir. Otherwise something went -# wrong with the mounts. Singularity or Apptainer can sometimes do that +# wrong with the mounts. Apptainer can sometimes do that # if the command is improperly built (order of mounts args etc). workdir_devid=$(stat -c %d .) # dev number of task workdir for mount in #{capture_basenames.map(&:bash_escape).join(" ")} ; do @@ -2479,12 +2479,12 @@ def singularity_commands(command_script) # Scientific commands start here #{command_script} -SINGULARITYJOB +APPTAINERJOB # Make sure it is executable -chmod 755 #{singularity_wrapper_basename.bash_escape} +chmod 755 #{apptainer_wrapper_basename.bash_escape} -# Invoke Singularity with our wrapper script above. +# Invoke Apptainer with our wrapper script above. # Tricks used here: # 1) we supply (if any) additional options for the exec command # 2) we mount the local data provider cache root directory @@ -2494,8 +2494,8 @@ def singularity_commands(command_script) # 4) we mount each (if any) of the root directories for local data providers # 5) we mount (if any) other fixed file system overlays # 6) we mount (if any) capture ext3 filesystems -# 7) with -H we set the task's work directory as the singularity $HOME directory -#{singularity_executable_name} \\ +# 7) with -H we set the task's work directory as the apptainer $HOME directory +#{apptainer_executable_name} \\ $mode \\ #{container_exec_args} \\ -B #{cache_dir.bash_escape} \\ @@ -2509,9 +2509,9 @@ def singularity_commands(command_script) #{container_image_name.bash_escape} \\ $sing_basename - SINGULARITY_COMMANDS + APPTAINER_COMMANDS - return singularity_commands + return apptainer_commands end @@ -2742,7 +2742,7 @@ def validate_json_string json_string end ################################################################## - # Singularity load + # Apptainer load ################################################################## private @@ -2754,56 +2754,56 @@ def container_image_name #:nodoc: ".container-#{self.id}.img" end - # Load the singularity image either from a repository or from a CBRAIN file - def load_singularity_image #:nodoc: + # Load the Apptainer image either from a repository or from a CBRAIN file + def load_apptainer_image #:nodoc: if self.tool_config.container_image - load_singularity_image_from_userfile + load_apptainer_image_from_userfile elsif self.tool_config.containerhub_image_name - load_singularity_image_from_repo + load_apptainer_image_from_repo else - cb_error "Cannot find source for singularity image..." + cb_error "Cannot find source for Apptainer image..." end end # Create a link to our image; the image is a registered CBRAIN file - def load_singularity_image_from_userfile #:nodoc: - singularity_image = self.tool_config.container_image - self.addlog("Syncing the singularity image '#{singularity_image.name}'") + def load_apptainer_image_from_userfile #:nodoc: + apptainer_image = self.tool_config.container_image + self.addlog("Syncing the Apptainer image '#{apptainer_image.name}'") # Sync the userfile content - singularity_image.sync_to_cache + apptainer_image.sync_to_cache # Create the symlink to the cached image. - cachename = singularity_image.cache_full_path + cachename = apptainer_image.cache_full_path image_name = container_image_name safe_symlink(cachename,image_name) end - # Perform the singularity build; the image will be cached + # Perform the Apptainer build; the image will be cached # as a special, hidden userfile on the ScratchDataProvider. - def load_singularity_image_from_repo #:nodoc: - singularity_image_name = self.tool_config.containerhub_image_name - singularity_index_location = self.tool_config.container_index_location.presence || "shub://" + def load_apptainer_image_from_repo #:nodoc: + apptainer_image_name = self.tool_config.containerhub_image_name + apptainer_index_location = self.tool_config.container_index_location.presence || "shub://" - self.addlog("Building singularity image '#{singularity_image_name}'") + self.addlog("Building Apptainer image '#{apptainer_image_name}'") # Find or create the userfile holding the image content. - scratch_name = "Singularity-build-" + singularity_image_name.gsub(/[^a-z0-9_\.\-]+/i,"_") # must respect userfile convention. + scratch_name = "Apptainer-build-" + apptainer_image_name.gsub(/[^a-z0-9_\.\-]+/i,"_") # must respect userfile convention. scratch_name.sub!(/(\.img)?$/i, ".img") scratch_userfile = SingularityImage.find_or_create_as_scratch(:name => scratch_name) do |cache_path| # Optimization: if another find_or_create_as_scratch has already beaten us to the punch # and dowloaded the file, just skip this block altogether. The way SyncStatus works, several # of these blocks can be scheduled to run, but only one will execute at at any given time. next if File.exists?(cache_path.to_s) && File.size(cache_path.to_s) > 0 - # Run singularity build command - out, err = tool_config_system("umask 000; #{singularity_executable_name} build #{cache_path.to_s.bash_escape} #{singularity_index_location.bash_escape}#{singularity_image_name.bash_escape}") + # Run apptainer build command + out, err = tool_config_system("umask 000; #{apptainer_executable_name} build #{cache_path.to_s.bash_escape} #{apptainer_index_location.bash_escape}#{apptainer_image_name.bash_escape}") # Check that all is OK; if not we store the captured outputs for investigation if ! File.exists?(cache_path.to_s) || File.size(cache_path.to_s) < 500.kilobytes - capfile = "singularity_build_traces-#{self.run_id}.txt" + capfile = "apptainer_build_traces-#{self.run_id}.txt" File.open(capfile,"w") do |fh| fh.write("=== Stdout ===\n#{out}\n=== Stderr ===\n#{err}\n=== ====== ===\n") end - cb_error "Cannot build singularity image. Captured outputs are in #{capfile}" + cb_error "Cannot build apptainer image. Captured outputs are in #{capfile}" end end diff --git a/BrainPortal/app/models/sing_bindmount_data_provider.rb b/BrainPortal/app/models/sing_bindmount_data_provider.rb index 516c70df4..b0c3e0ada 100644 --- a/BrainPortal/app/models/sing_bindmount_data_provider.rb +++ b/BrainPortal/app/models/sing_bindmount_data_provider.rb @@ -22,22 +22,22 @@ # This class implements a Data Provider which fetches # files out of a single filesystem file. The -# implementation requires Singularity 3.2 or better to -# be installed on the host, as well as a singularity +# implementation requires Apptainer 1.1 or Singularity 3.7 and better +# be installed on the host, as well as an Apptainer # container image that contains the basic Linux # commands and the 'rsync' command too. # # For the moment the DP is read-only. Allowing # write access would require a mechanism in CBRAIN -# to prevent more than one singularity process to +# to prevent more than one Apptainer process to # be accessing the mounted file at any one time. # # Bind mounts are performed using a command such as: # -# singularity shell -B filesystem.img:/my/mount/point:image-src=/inside/dir,ro sing_squashfs.simg +# apptainer shell -B filesystem.img:/my/mount/point:image-src=/inside/dir,ro sing_squashfs.simg # # where: -# 1- sing_squashfs.simg is a singularity image file, +# 1- sing_squashfs.simg is an Apptainer image file, # 2- filesystem.img is either a ext3 or squashfs filesystem, # 3- /inside/dir is a path inside that filesystem, # 4- /my/mount/point is a path where the data will be mounted under @@ -51,7 +51,7 @@ # # The attribute 'remote_dir' must be a full path to the location of # the filesystem image 'filesystem.img' (even though it is not a 'dir'). -# The singularity image file will be expected to be called 'sing_squashfs.simg' +# The apptainer image file will be expected to be called 'sing_squashfs.simg' # and be located in the same subdirectory as 'filesystem.img'. class SingBindmountDataProvider < SshDataProvider @@ -64,14 +64,14 @@ class SingBindmountDataProvider < SshDataProvider # TODO: Change this is the DP is ever made writable! BROWSE_CACHE_EXPIRATION = 2.months #:nodoc: - # This is the basename of the singularity image + # This is the basename of the Apptainer image # we use to access the remote filesystem; we # expect this image to be installed in the same # directory that contains it. Its minimum # requirements are that 1) basic UNIX commands # exist on it 2) the rsync command is installed # in it too. - SINGULARITY_IMAGE_BASENAME = 'sing_squashfs.simg' + APPTAINER_IMAGE_BASENAME = 'sing_squashfs.simg' # We use this to point to the directory INSIDE the container # were the root of the data is stored. @@ -91,7 +91,7 @@ def read_only=(val) #:nodoc: # This returns the category of the data provider def self.pretty_category_name #:nodoc: - "Singularity Bind Mount" + "Apptainer Bind Mount" end # This uses the new CBRAIN capability of registering @@ -109,20 +109,20 @@ def impl_is_alive? #:nodoc: # Raise an exception with a message indicating what is wrong with the config. # This method is not part of the official method API def check_remote_config! #:nodoc: - # Check we have one singularity image file - remote_cmd = "cd #{self.real_remote_dir.to_s.bash_escape} && test -f #{SINGULARITY_IMAGE_BASENAME} && echo OK-Exists" + # Check we have one apptainer image file + remote_cmd = "cd #{self.real_remote_dir.to_s.bash_escape} && test -f #{APPTAINER_IMAGE_BASENAME} && echo OK-Exists" text = self.remote_bash_this(remote_cmd) # The following check will also make sure the remote shell is clean! - cb_error "No installed singularity image #{SINGULARITY_IMAGE_BASENAME}, or remote shell is unclean" unless text =~ /\AOK-Exists\s*\z/ + cb_error "No installed apptainer image #{APPTAINER_IMAGE_BASENAME}, or remote shell is unclean" unless text =~ /\AOK-Exists\s*\z/ # Check we have the remote filesystem file remote_cmd = "test -f #{self.remote_dir.to_s.bash_escape} && echo OK-Exists" cb_error "Filesystem file '#{self.remote_dir}' not found" unless text =~ /\AOK-Exists\s*\z/ - # Check we have singularity version 3.7 or better + # Check we have apptainer 1.1 or better or singularity version 3.7 or better remote_cmd = "(singularity --version 2>/dev/null || apptainer --version 2>/dev/null)" text = self.remote_bash_this(remote_cmd) - cb_error "Can't find singularity version number on remote host" unless text =~ /^((singularity|apptainer) version )?(\d+)\.(\d+)/ + cb_error "Can't find apptainer version number on remote host" unless text =~ /^((singularity|apptainer) version )?(\d+)\.(\d+)/ _, _, tool, major, minor = Regexp.last_match.to_a major = major.to_i minor = minor.to_i @@ -268,7 +268,7 @@ def impl_provider_rename(userfile,newname) #:nodoc: # Returns true if we have to use 'ssh' to # connect to the remote server. Returns false # when we can optimize requests by running - # singularity locally. The local situation is + # apptainer locally. The local situation is # detected pretty much like in the Smart DP # module: if the hostname is the same as *remote_host* # or *alternate_host*, and if the *remote_dir* exists @@ -294,8 +294,8 @@ def provider_is_remote #:nodoc: @provider_is_remote end - def singularity_exec_prefix #:nodoc: - "cd #{self.real_remote_dir.to_s.bash_escape} && singularity -s exec #{self.local_bind_opt} #{SINGULARITY_IMAGE_BASENAME}" + def apptainer_exec_prefix #:nodoc: + "cd #{self.real_remote_dir.to_s.bash_escape} && singularity -s exec #{self.local_bind_opt} #{APPTAINER_IMAGE_BASENAME}" end def local_bind_opt #:nodoc: @@ -303,7 +303,7 @@ def local_bind_opt #:nodoc: end def remote_rsync_command #:nodoc: - "#{singularity_exec_prefix} rsync" + "#{apptainer_exec_prefix} rsync" end def real_remote_dir #:nodoc: @@ -334,7 +334,7 @@ def rsync_over_ssh_prefix end def remote_in_sing_bash_this(com) #:nodoc: - newcom = "#{singularity_exec_prefix} bash -c #{com.bash_escape}" + newcom = "#{apptainer_exec_prefix} bash -c #{com.bash_escape}" remote_bash_this(newcom) end diff --git a/BrainPortal/app/models/sing_squashfs_data_provider.rb b/BrainPortal/app/models/sing_squashfs_data_provider.rb index f2c352321..cf5efdd2f 100644 --- a/BrainPortal/app/models/sing_squashfs_data_provider.rb +++ b/BrainPortal/app/models/sing_squashfs_data_provider.rb @@ -22,8 +22,8 @@ # This class implements a Data Provider which fetches # files out of one or several SquashFS files. The -# implementation requires Singularity 3.2 or better to -# be installed on the host, as well as a singularity +# implementation requires Apptainer 1.1 / Singularity 3.7 or better to +# be installed on the host, as well as a singularity/apptainer # container image that contains the basic Linux # commands and the 'rsync' command too. class SingSquashfsDataProvider < SshDataProvider @@ -35,14 +35,14 @@ class SingSquashfsDataProvider < SshDataProvider # be forever, really. BROWSE_CACHE_EXPIRATION = 6.months #:nodoc: - # This is the basename of the singularity image + # This is the basename of the apptainer image # we use to access the squashfs filesystems; we # expect this image to be installed in the same # directory that contain them. Its minimum # requirements are that 1) basic UNIX commands # exist on it 2) the rsync command is installed # in it too. - SINGULARITY_IMAGE_BASENAME = 'sing_squashfs.simg' + APPTAINER_IMAGE_BASENAME = 'sing_squashfs.simg' # We use this to point to the directory INSIDE the container # were the root of the data is stored @@ -75,17 +75,17 @@ def impl_is_alive? #:nodoc: # Raise an exception with a message indicating what is wrong with the config. # This method is not part of the official method API def check_remote_config! #:nodoc: - # Check we have one singularity image file - remote_cmd = "cd #{self.remote_dir.bash_escape};test -f #{SINGULARITY_IMAGE_BASENAME} && echo OK-Exists" + # Check we have one Apptainer image file + remote_cmd = "cd #{self.remote_dir.bash_escape};test -f #{APPTAINER_IMAGE_BASENAME} && echo OK-Exists" text = self.remote_bash_this(remote_cmd) # The following check will also make sure the remote shell is clean! - cb_error "No installed singularity image #{SINGULARITY_IMAGE_BASENAME}, or remote shell is unclean" unless text =~ /\AOK-Exists\s*\z/ + cb_error "No installed Apptainer image #{APPTAINER_IMAGE_BASENAME}, or remote shell is unclean" unless text =~ /\AOK-Exists\s*\z/ # Check we have at least one .squashfs file in the remote_dir sq_files = get_squashfs_basenames cb_error "No .squashfs files found" unless sq_files.present? - # Check we have singularity version 3.2 or better + # Check we have singularity version 3.7 or better or apptainer 1.1 or better remote_cmd = "(singularity --version 2>/dev/null || apptainer --version 2>/dev/null)" text = self.remote_bash_this(remote_cmd) cb_error "Can't find singularity version number on remote host" unless text =~ /^((singularity|apptainer) version )?(\d+)\.(\d+)/ @@ -237,7 +237,7 @@ def impl_provider_rename(userfile,newname) #:nodoc: public # Returns the full paths to the overlays - def singularity_overlays_full_paths #:nodoc: + def apptainer_overlays_full_paths #:nodoc: self.get_squashfs_basenames.map do |basename| Pathname.new(self.remote_dir) + basename end.map(&:to_s) @@ -248,7 +248,7 @@ def singularity_overlays_full_paths #:nodoc: # Returns true if we have to use 'ssh' to # connect to the remote server. Returns false # when we can optimize requests by running - # singularity locally. The local situation is + # apptainer/singularity locally. The local situation is # detected pretty much like in the Smart DP # module: if the hostname is the same as *remote_host* # or *alternate_host*, and if the *remote_dir* exists @@ -288,14 +288,14 @@ def get_squashfs_basenames(force = false) #:nodoc: @sq_files end - def singularity_exec_prefix #:nodoc: + def apptainer_exec_prefix #:nodoc: sq_files = get_squashfs_basenames overlay_opts = sq_files.map { |f| "--overlay=#{f.bash_escape}:ro" }.join(" ") - "cd #{self.remote_dir.bash_escape} && singularity -s exec #{overlay_opts} #{SINGULARITY_IMAGE_BASENAME}" + "cd #{self.remote_dir.bash_escape} && singularity -s exec #{overlay_opts} #{APPTAINER_IMAGE_BASENAME}" end def remote_rsync_command #:nodoc: - "#{singularity_exec_prefix} rsync" + "#{apptainer_exec_prefix} rsync" end # Builds a prefix for a +rsync+ command, such as @@ -318,7 +318,7 @@ def rsync_over_ssh_prefix end def remote_in_sing_bash_this(com) #:nodoc: - newcom = "#{singularity_exec_prefix} bash -c #{com.bash_escape}" + newcom = "#{apptainer_exec_prefix} bash -c #{com.bash_escape}" remote_bash_this(newcom) end diff --git a/BrainPortal/app/models/tool_config.rb b/BrainPortal/app/models/tool_config.rb index 8f85347cc..5d41cd156 100644 --- a/BrainPortal/app/models/tool_config.rb +++ b/BrainPortal/app/models/tool_config.rb @@ -169,10 +169,10 @@ def extended_environment # Generates a partial BASH script that initializes environment # variables and is followed a the script prologue stored in the - # object. For singularity prologues, special prefixes are added to + # object. For apptainer prologues, special prefixes are added to # variable names to ensure they will be propagated to the container # even in presence of --cleanenv parameteres and such - def to_bash_prologue(singularity=false) + def to_bash_prologue(apptainer=false) tool = self.tool bourreau = self.bourreau group = self.group @@ -213,15 +213,15 @@ def to_bash_prologue(singularity=false) ENV_HEADER script += vars_to_export_script - if singularity + if apptainer script += <<-ENV_HEADER #--------------------------------------------------- # Ensuring that environment variables are propagated:#{env.size == 0 ? " (NONE DEFINED)" : ""} #--------------------------------------------------- ENV_HEADER - script += vars_to_export_script("SINGULARITYENV_") - script += vars_to_export_script("APPTAINERENV_") # SINGULARITYENV is to be depricated + script += vars_to_export_script("SINGULARITYENV_") # todo SINGULARITYENV is to be depricated + script += vars_to_export_script("APPTAINERENV_") end script += "\n" if env.size > 0 @@ -316,9 +316,10 @@ def is_at_least_version(version) end end - # true if singularity image is defined - def use_singularity? - return self.container_engine == "Singularity" && + # true if apptainer image is defined + def use_apptainer? + return (self.container_engine == "Singularity" || + self.container_engine == "Apptainer") && # Singularity will be probably deprecated by Boutiques ( self.containerhub_image_name.present? || self.container_image_userfile_id.present? ) end @@ -356,7 +357,7 @@ def cbrain_task_class self.tool.cbrain_task_class end - # Returns an array of full paths to the Singularity overlay files that + # Returns an array of full paths to the Apptainer overlay files that # need to be mounted, as configured by the admin. Some of them might # be patterns and will need to be resolved at run time. The dsl is # # A file located on the cluster @@ -367,7 +368,7 @@ def cbrain_task_class # userfile:1234 # # A ext3 capture filesystem, will NOT be returned here as an overlay # ext3capture:basename=12G - def singularity_overlays_full_paths + def apptainer_overlays_full_paths specs = parsed_overlay_specs specs.map do |knd, id_or_name| @@ -378,7 +379,7 @@ def singularity_overlays_full_paths when 'dp' dp = DataProvider.where_id_or_name(id_or_name).first cb_error "Can't find DataProvider #{id_or_name} for fetching overlays" if ! dp - dp_ovs = dp.singularity_overlays_full_paths rescue nil + dp_ovs = dp.apptainer_overlays_full_paths rescue nil cb_error "DataProvider #{id_or_name} does not have any overlays configured." if dp_ovs.blank? dp_ovs when 'file' @@ -399,7 +400,7 @@ def singularity_overlays_full_paths end # Returns an array of the data providers that are - # specified in the attribute singularity_overlays_specs, + # specified in the attribute apptainer_overlays_specs, # ignoring all other overlay specs for normal files. def data_providers_with_overlays return @_data_providers_with_overlays_ if @_data_providers_with_overlays_ @@ -427,7 +428,7 @@ def ext3capture_basenames # Validate some rules for container_engine, container_image_userfile_id, containerhub_image_name def validate_container_rules #:nodoc: # Should only have one container_engine of particular type - available_engine = ["Singularity","Docker"] + available_engine = ["Apptainer","Singularity","Docker"] if self.container_engine.present? && available_engine.exclude?(self.container_engine) errors[:container_engine] = "is not valid" end @@ -445,9 +446,9 @@ def validate_container_rules #:nodoc: errors[:container_engine] = "a container hub image name or a container image userfile ID should be set when the container engine is set" end - if self.container_engine.present? && self.container_engine == "Singularity" - if self.container_index_location.present? && self.container_index_location !~ /\A[a-z0-9]+\:\/\/\z/i - errors[:container_index_location] = "is invalid for container engine Singularity. Should end in '://'." + if self.container_engine.present? && (self.container_engine == "Singularity" || self.container_engine == "Apptainer") + if self.container_index_location.present? && self.container_index_location !~ /\A[a-z0-9]+\:\/\/\z/i + errors[:container_index_location] = "is invalid for container engine Apptainer. Should end in '://'." end elsif self.container_engine.present? && self.container_engine == "Docker" if self.container_index_location.present? && self.container_index_location !~ /\A[a-z0-9]+([\-\.]{1}[a-z0-9]+)*\.[a-z]{2,6}\z/i @@ -459,7 +460,7 @@ def validate_container_rules #:nodoc: # breaks down overlay spec onto a list of overlays def parsed_overlay_specs - specs = self.singularity_overlays_specs + specs = self.apptainer_overlays_specs return [] if specs.blank? lines = specs.split(/^#.*|\s+#.*|\n/) # split on lines and drop comments lines.map(&:presence).compact.map do |spec| @@ -494,24 +495,24 @@ def validate_overlays_specs #:nodoc: case kind # different validations rules for file, userfile and dp specs when 'file', 'old style file' # full path specification for a local file, e.g. "file:/myfiles/c.sqs" if id_or_name !~ /^\/\S+\.(sqs|sqfs|squashfs)$/ - self.errors.add(:singularity_overlays_specs, + self.errors.add(:apptainer_overlays_specs, " contains invalid #{kind} named '#{id_or_name}'. It should be a full path that ends in .squashfs, .sqs or .sqfs") end when 'userfile' # db-registered file spec, e.g. "userfile:42" if id_or_name !~ /\A\d+\z/ - self.errors.add(:singularity_overlays_specs, + self.errors.add(:apptainer_overlays_specs, %{" contains invalid userfile id '#{id_or_name}'. The userfile id should be an integer number."} ) else userfile = SingleFile.where(:id => id_or_name).first end if ! userfile - self.errors.add(:singularity_overlays_specs, + self.errors.add(:apptainer_overlays_specs, %{" contains invalid userfile id '#{id_or_name}'. The file with id '#{id_or_name}' is not found."} ) elsif userfile.name.to_s !~ /\.(sqs|sqfs|squashfs)$/ - self.errors.add(:singularity_overlays_specs, + self.errors.add(:apptainer_overlays_specs, " contains invalid userfile with id '#{id_or_name}' and name '#{userfile.name}'. File name should end in .squashfs, .sqs or .sqfs") # todo maybe or/and check file type? end @@ -519,20 +520,20 @@ def validate_overlays_specs #:nodoc: when 'dp' # DataProvider specs: "dp:name" or "dp:ID" dp = DataProvider.where_id_or_name(id_or_name).first if !dp - self.errors.add(:singularity_overlays_specs, "contains invalid DP '#{id_or_name}' (no such DP)") + self.errors.add(:apptainer_overlays_specs, "contains invalid DP '#{id_or_name}' (no such DP)") elsif ! dp.is_a?(SingSquashfsDataProvider) - self.errors.add(:singularity_overlays_specs, "DataProvider '#{id_or_name}' is not a SingSquashfsDataProvider") + self.errors.add(:apptainer_overlays_specs, "DataProvider '#{id_or_name}' is not a SingSquashfsDataProvider") end when 'ext3capture' # ext3 filesystem as a basename with an initial size # The basename is limited to letters, digits, numbers and dashes; the =SIZE suffix must end with G or M if id_or_name !~ /\A\w[\w\.-]+=([1-9]\d*)[mg]\z/i - self.errors.add(:singularity_overlays_specs, "contains invalid ext3capture specification (must be like ext3capture:basename=1g or 2m etc)") + self.errors.add(:apptainer_overlays_specs, "contains invalid ext3capture specification (must be like ext3capture:basename=1g or 2m etc)") end else # Other errors - self.errors.add(:singularity_overlays_specs, "contains invalid specification '#{kind}:#{id_or_name}'") + self.errors.add(:apptainer_overlays_specs, "contains invalid specification '#{kind}:#{id_or_name}'") end end @@ -630,7 +631,7 @@ def self.create_from_descriptor(bourreau, tool, descriptor, record_path=false) container_engine = container_info['type'].presence.try(:capitalize) container_engine = "Singularity" if (container_engine == "Docker" && !bourreau.docker_present? && - bourreau.singularity_present? + bourreau.apptainer_present? ) container_index = container_info['index'].presence container_index = 'docker://' if container_index == 'index.docker.io' # old convention diff --git a/BrainPortal/app/views/bourreaux/show.html.erb b/BrainPortal/app/views/bourreaux/show.html.erb index 1327a9cde..3ae48a90d 100644 --- a/BrainPortal/app/views/bourreaux/show.html.erb +++ b/BrainPortal/app/views/bourreaux/show.html.erb @@ -456,9 +456,9 @@
Name of the Docker executable available on the machines where tasks will run. It should always be set if Docker is present.
<% end %> - <% t.edit_cell :singularity_executable_name, :header => "Singularity executable" do |f| %> - <%= f.text_field :singularity_executable_name, :size => 60 %>
-
Name of the Singularity executable available on the machines where tasks will run. It should always be set if Singularity is present.
+ <% t.edit_cell :apptainer_executable_name, :header => "Apptainer executable" do |f| %> + <%= f.text_field :apptainer_executable_name, :size => 60 %>
+
Name of the Apptainer or Singularity executable available on the machines where tasks will run. It should always be set if Singularity is present.
<% end %> <% end %> <% end %> diff --git a/BrainPortal/app/views/data_providers/_dp_types_explained.html.erb b/BrainPortal/app/views/data_providers/_dp_types_explained.html.erb index 951970e11..26d76ec8f 100644 --- a/BrainPortal/app/views/data_providers/_dp_types_explained.html.erb +++ b/BrainPortal/app/views/data_providers/_dp_types_explained.html.erb @@ -114,12 +114,12 @@ acts like a FlatDataProvider.

-SingSquashfsDataProvider This class connects to a set of +AptainerSquashfsDataProvider This class connects to a set of one or several squashfs files (all named with .squashfs extensions) through -a Singularity container handler. The requirements are:
+a Apptainer container handler. The requirements are:

diff --git a/BrainPortal/app/views/tool_configs/show.html.erb b/BrainPortal/app/views/tool_configs/show.html.erb index 6b570a903..ff02fd003 100644 --- a/BrainPortal/app/views/tool_configs/show.html.erb +++ b/BrainPortal/app/views/tool_configs/show.html.erb @@ -69,9 +69,9 @@
 
-<%= @bourreau_glob_config.to_bash_prologue @tool_local_config&.use_singularity? if @bourreau_glob_config %>
-<%= @tool_glob_config.to_bash_prologue     @tool_local_config&.use_singularity? if @tool_glob_config     %>
-<%= @tool_local_config.to_bash_prologue    @tool_local_config&.use_singularity? if @tool_local_config    %>
+<%= @bourreau_glob_config.to_bash_prologue @tool_local_config&.use_apptainer? if @bourreau_glob_config %>
+<%= @tool_glob_config.to_bash_prologue     @tool_local_config&.use_apptainer? if @tool_glob_config     %>
+<%= @tool_local_config.to_bash_prologue    @tool_local_config&.use_apptainer? if @tool_local_config    %>
 ##########################################
 #### [Wrapped commands would be here] ####
 ##########################################
diff --git a/BrainPortal/config/console_rc/lib/pretty_view.rb b/BrainPortal/config/console_rc/lib/pretty_view.rb
index 8406bdd7a..17108175e 100644
--- a/BrainPortal/config/console_rc/lib/pretty_view.rb
+++ b/BrainPortal/config/console_rc/lib/pretty_view.rb
@@ -233,7 +233,7 @@ def pretview
   Created:  %s
   Updated:  %s
   Docker:   %s
-  Singularity: %s
+  Apptainer: %s
   Flags:    %s %s %s
     VIEW
     sprintf report,
@@ -265,7 +265,7 @@ def pretview
       ConsoleCtx.send(:pretty_past_date,created_at),
       ConsoleCtx.send(:pretty_past_date,updated_at),
       docker_executable_name.presence || "",
-      singularity_executable_name.presence || "",
+      apptainer_executable_name.presence || "",
       (online? ? "Online" : "Offline"),
       (read_only? ? "ReadOnly" : "R/W"),
       (portal_locked? ? "LOCKED" : "")
diff --git a/BrainPortal/db/migrate/20231214214407_rename_singularity_to_apptainer_overlays_specs.rb b/BrainPortal/db/migrate/20231214214407_rename_singularity_to_apptainer_overlays_specs.rb
new file mode 100644
index 000000000..720912b5b
--- /dev/null
+++ b/BrainPortal/db/migrate/20231214214407_rename_singularity_to_apptainer_overlays_specs.rb
@@ -0,0 +1,5 @@
+class RenameSingularityToApptainerOverlaysSpecs < ActiveRecord::Migration[5.0]
+  def change
+    rename_column :tool_configs, :singularity_overlays_specs, :apptainer_overlays_specs
+  end
+end
diff --git a/BrainPortal/db/migrate/20231214214526_rename_singularity_to_apptainer_use_short_workdir.rb b/BrainPortal/db/migrate/20231214214526_rename_singularity_to_apptainer_use_short_workdir.rb
new file mode 100644
index 000000000..c71b5b268
--- /dev/null
+++ b/BrainPortal/db/migrate/20231214214526_rename_singularity_to_apptainer_use_short_workdir.rb
@@ -0,0 +1,5 @@
+class RenameSingularityToApptainerUseShortWorkdir < ActiveRecord::Migration[5.0]
+  def change
+    rename_column :tool_configs, :singularity_use_short_workdir, :apptainer_use_short_workdir
+  end
+end
diff --git a/BrainPortal/db/migrate/20231214214549_rename_singularity_to_apptainer_executable_name.rb b/BrainPortal/db/migrate/20231214214549_rename_singularity_to_apptainer_executable_name.rb
new file mode 100644
index 000000000..55162093e
--- /dev/null
+++ b/BrainPortal/db/migrate/20231214214549_rename_singularity_to_apptainer_executable_name.rb
@@ -0,0 +1,5 @@
+class RenameSingularityToApptainerExecutableName < ActiveRecord::Migration[5.0]
+  def change
+    rename_column :remote_resources, :singularity_executable_name, :apptainer_executable_name
+  end
+end
diff --git a/BrainPortal/lib/cbrain_task_generators/schema_task_generator.rb b/BrainPortal/lib/cbrain_task_generators/schema_task_generator.rb
index 48edeed38..1c5a05fcc 100644
--- a/BrainPortal/lib/cbrain_task_generators/schema_task_generator.rb
+++ b/BrainPortal/lib/cbrain_task_generators/schema_task_generator.rb
@@ -259,10 +259,10 @@ def register(task)
       return if container_engine.blank?  # Singularity or Docker
       return if container_image.blank?   # Container name or url
                 container_engine.capitalize!
-      return if container_engine == "Singularity" && !resource.singularity_present?
-      return if container_engine == "Docker"      && (!resource.docker_present? && !resource.singularity_present?)
+      return if container_engine == ("Singularity" || "Apptainer") && !resource.apptainer_present?
+      return if container_engine == "Docker"      && (!resource.docker_present? && !resource.apptainer_present?)
 
-      # If Docker engine isn't present use Singularity
+      # If Docker engine isn't present use Singularity/Apptainer
       container_engine = "Singularity" if (container_engine == "Docker" && !resource.docker_present?)
       container_index  = 'docker://'   if container_index == 'index.docker.io' # old convention
 
diff --git a/BrainPortal/spec/models/tool_config_spec.rb b/BrainPortal/spec/models/tool_config_spec.rb
index 731851f1f..5a3785bee 100644
--- a/BrainPortal/spec/models/tool_config_spec.rb
+++ b/BrainPortal/spec/models/tool_config_spec.rb
@@ -159,13 +159,13 @@
    context "fill HEADER" do
       it "should print 'Configuration: tool_config.id'" do
         expect(tool_config.to_bash_prologue).to                    match(/Configuration\s?:\s+#\s+#{tool_config.id}/)
-        expect(tool_config.to_bash_prologue(singularity: true)).to match(/Configuration\s?:\s+#\s+#{tool_config.id}/)
+        expect(tool_config.to_bash_prologue(apptainer: true)).to match(/Configuration\s?:\s+#\s+#{tool_config.id}/)
       end
 
       it "should print 'Tool: ALL' if specific tool is not defined"  do
         tool_config.tool = nil
         expect(tool_config.to_bash_prologue).to                    match(/Tool\s?:\s+ALL/)
-        expect(tool_config.to_bash_prologue(singularity: true)).to match(/Tool\s?:\s+ALL/)
+        expect(tool_config.to_bash_prologue(apptainer: true)).to match(/Tool\s?:\s+ALL/)
       end
 
       it "should print 'Tool: tool_config.tool.name' if specific tool is defined"  do

From d091198f139392cb03ffba5a6ec8e6bcecfb2bc4 Mon Sep 17 00:00:00 2001
From: MontrealSergiy 
Date: Mon, 18 Dec 2023 10:56:33 -0500
Subject: [PATCH 02/12] rename singularity to apptainer in a plugin
 (SingularityImage) variables

---
 .../userfiles/singularity_image/singularity_image.rb | 12 ++++++------
 1 file changed, 6 insertions(+), 6 deletions(-)

diff --git a/BrainPortal/cbrain_plugins/cbrain-plugins-base/userfiles/singularity_image/singularity_image.rb b/BrainPortal/cbrain_plugins/cbrain-plugins-base/userfiles/singularity_image/singularity_image.rb
index 8953c769e..df9c64a09 100644
--- a/BrainPortal/cbrain_plugins/cbrain-plugins-base/userfiles/singularity_image/singularity_image.rb
+++ b/BrainPortal/cbrain_plugins/cbrain-plugins-base/userfiles/singularity_image/singularity_image.rb
@@ -32,7 +32,7 @@ def self.file_name_pattern #:nodoc:
   end
 
   def is_viewable? #:nodoc:
-    if ! self.has_singularity_support?
+    if ! self.has_apptainer_support?
       return [ "The local portal doesn't support inspecting Singularity images." ]
     elsif ! self.is_locally_synced?
       return [ "Singularity image file not yet synchronized" ]
@@ -41,18 +41,18 @@ def is_viewable? #:nodoc:
     end
   end
 
-  def has_singularity_support? #:nodoc:
-    self.class.has_singularity_support?
+  def has_apptainer_support? #:nodoc:
+    self.class.has_apptainer_support?
   end
 
   # Detects if the system has the 'singularity' command.
   # Caches the result in the class so it won't need to
   # be detected again after the first time, for the life
   # of the current process.
-  def self.has_singularity_support? #:nodoc:
-    return @_has_singularity_support if ! @_has_singularity_support.nil?
+  def self.has_apptainer_support? #:nodoc:
+    return @_has_apptainer_support if ! @_has_apptainer_support.nil?
     out = IO.popen("bash -c 'type -p singularity'","r") { |f| f.read }
-    @_has_singularity_support = out.present?
+    @_has_apptainer_support = out.present?
   end
 
 end

From 04de85b33120c0b81203040822c46d938afc43e9 Mon Sep 17 00:00:00 2001
From: MontrealSergiy 
Date: Mon, 18 Dec 2023 11:16:08 -0500
Subject: [PATCH 03/12] rename singularity to apptainer in variables - new db
 schema

---
 BrainPortal/db/schema.rb | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/BrainPortal/db/schema.rb b/BrainPortal/db/schema.rb
index bdb3f4dc2..038d480c7 100644
--- a/BrainPortal/db/schema.rb
+++ b/BrainPortal/db/schema.rb
@@ -10,7 +10,7 @@
 #
 # It's strongly recommended that you check this file into your version control system.
 
-ActiveRecord::Schema.define(version: 20230418205141) do
+ActiveRecord::Schema.define(version: 20231214214549) do
 
   create_table "access_profiles", force: :cascade, options: "ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci" do |t|
     t.string   "name",        null: false
@@ -290,7 +290,7 @@
     t.string   "nh_system_from_email"
     t.string   "external_status_page_url"
     t.string   "docker_executable_name"
-    t.string   "singularity_executable_name"
+    t.string   "apptainer_executable_name"
     t.string   "small_logo"
     t.string   "large_logo"
     t.index ["type"], name: "index_remote_resources_on_type", using: :btree
@@ -460,8 +460,8 @@
     t.string   "containerhub_image_name"
     t.string   "container_engine"
     t.string   "container_index_location"
-    t.text     "singularity_overlays_specs",    limit: 65535
-    t.boolean  "singularity_use_short_workdir",               default: false, null: false
+    t.text     "apptainer_overlays_specs",    limit: 65535
+    t.boolean  "apptainer_use_short_workdir",                 default: false, null: false
     t.string   "container_exec_args"
     t.boolean  "inputs_readonly",                             default: false
     t.string   "boutiques_descriptor_path"

From 15e384c4ee8362fac8c65dc65cf4f1ff8052bc2f Mon Sep 17 00:00:00 2001
From: MontrealSergiy 
Date: Mon, 18 Dec 2023 11:19:26 -0500
Subject: [PATCH 04/12] rename singularity to apptainer in variables (add
 whitespace to schema)

---
 BrainPortal/db/schema.rb | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/BrainPortal/db/schema.rb b/BrainPortal/db/schema.rb
index 038d480c7..79ff11cbc 100644
--- a/BrainPortal/db/schema.rb
+++ b/BrainPortal/db/schema.rb
@@ -460,7 +460,7 @@
     t.string   "containerhub_image_name"
     t.string   "container_engine"
     t.string   "container_index_location"
-    t.text     "apptainer_overlays_specs",    limit: 65535
+    t.text     "apptainer_overlays_specs",      limit: 65535
     t.boolean  "apptainer_use_short_workdir",                 default: false, null: false
     t.string   "container_exec_args"
     t.boolean  "inputs_readonly",                             default: false

From 1841b716bc8b862c47d9a3aa4b497c198d212323 Mon Sep 17 00:00:00 2001
From: MontrealSergiy 
Date: Fri, 22 Mar 2024 17:05:00 -0400
Subject: [PATCH 05/12] rename singularity to apptainer in variables (merge
 migrations)

---
 ...14407_rename_singularity_to_apptainer_overlays_specs.rb | 5 -----
 ...26_rename_singularity_to_apptainer_use_short_workdir.rb | 5 -----
 ...4549_rename_singularity_to_apptainer_executable_name.rb | 5 -----
 .../20240321134005_rename_singularity_to_apprainer.rb      | 7 +++++++
 4 files changed, 7 insertions(+), 15 deletions(-)
 delete mode 100644 BrainPortal/db/migrate/20231214214407_rename_singularity_to_apptainer_overlays_specs.rb
 delete mode 100644 BrainPortal/db/migrate/20231214214526_rename_singularity_to_apptainer_use_short_workdir.rb
 delete mode 100644 BrainPortal/db/migrate/20231214214549_rename_singularity_to_apptainer_executable_name.rb
 create mode 100644 BrainPortal/db/migrate/20240321134005_rename_singularity_to_apprainer.rb

diff --git a/BrainPortal/db/migrate/20231214214407_rename_singularity_to_apptainer_overlays_specs.rb b/BrainPortal/db/migrate/20231214214407_rename_singularity_to_apptainer_overlays_specs.rb
deleted file mode 100644
index 720912b5b..000000000
--- a/BrainPortal/db/migrate/20231214214407_rename_singularity_to_apptainer_overlays_specs.rb
+++ /dev/null
@@ -1,5 +0,0 @@
-class RenameSingularityToApptainerOverlaysSpecs < ActiveRecord::Migration[5.0]
-  def change
-    rename_column :tool_configs, :singularity_overlays_specs, :apptainer_overlays_specs
-  end
-end
diff --git a/BrainPortal/db/migrate/20231214214526_rename_singularity_to_apptainer_use_short_workdir.rb b/BrainPortal/db/migrate/20231214214526_rename_singularity_to_apptainer_use_short_workdir.rb
deleted file mode 100644
index c71b5b268..000000000
--- a/BrainPortal/db/migrate/20231214214526_rename_singularity_to_apptainer_use_short_workdir.rb
+++ /dev/null
@@ -1,5 +0,0 @@
-class RenameSingularityToApptainerUseShortWorkdir < ActiveRecord::Migration[5.0]
-  def change
-    rename_column :tool_configs, :singularity_use_short_workdir, :apptainer_use_short_workdir
-  end
-end
diff --git a/BrainPortal/db/migrate/20231214214549_rename_singularity_to_apptainer_executable_name.rb b/BrainPortal/db/migrate/20231214214549_rename_singularity_to_apptainer_executable_name.rb
deleted file mode 100644
index 55162093e..000000000
--- a/BrainPortal/db/migrate/20231214214549_rename_singularity_to_apptainer_executable_name.rb
+++ /dev/null
@@ -1,5 +0,0 @@
-class RenameSingularityToApptainerExecutableName < ActiveRecord::Migration[5.0]
-  def change
-    rename_column :remote_resources, :singularity_executable_name, :apptainer_executable_name
-  end
-end
diff --git a/BrainPortal/db/migrate/20240321134005_rename_singularity_to_apprainer.rb b/BrainPortal/db/migrate/20240321134005_rename_singularity_to_apprainer.rb
new file mode 100644
index 000000000..ee000e29d
--- /dev/null
+++ b/BrainPortal/db/migrate/20240321134005_rename_singularity_to_apprainer.rb
@@ -0,0 +1,7 @@
+class RenameSingularityToApprainer < ActiveRecord::Migration[5.0]
+  def change
+    rename_column :tool_configs, :singularity_overlays_specs, :apptainer_overlays_specs
+    rename_column :tool_configs, :singularity_use_short_workdir, :apptainer_use_short_workdir
+    rename_column :remote_resources, :singularity_executable_name, :apptainer_executable_name
+  end
+end

From e9e646ea2e10c66f266ce5a62776dd44b42489dd Mon Sep 17 00:00:00 2001
From: MontrealSergiy 
Date: Mon, 25 Mar 2024 11:26:00 -0400
Subject: [PATCH 06/12] rename singularity to apptainer in singularity image

---
 .../singularity_image/singularity_image.rb        | 15 ++++++++++-----
 .../singularity_image/views/_info.html.erb        |  8 ++++----
 2 files changed, 14 insertions(+), 9 deletions(-)

diff --git a/BrainPortal/cbrain_plugins/cbrain-plugins-base/userfiles/singularity_image/singularity_image.rb b/BrainPortal/cbrain_plugins/cbrain-plugins-base/userfiles/singularity_image/singularity_image.rb
index df9c64a09..96bf13d77 100644
--- a/BrainPortal/cbrain_plugins/cbrain-plugins-base/userfiles/singularity_image/singularity_image.rb
+++ b/BrainPortal/cbrain_plugins/cbrain-plugins-base/userfiles/singularity_image/singularity_image.rb
@@ -33,9 +33,9 @@ def self.file_name_pattern #:nodoc:
 
   def is_viewable? #:nodoc:
     if ! self.has_apptainer_support?
-      return [ "The local portal doesn't support inspecting Singularity images." ]
+      return [ "The local portal doesn't support inspecting Apptainer (aka Singularity) images." ]
     elsif ! self.is_locally_synced?
-      return [ "Singularity image file not yet synchronized" ]
+      return [ "Apptainer (aka Singularity) image file not yet synchronized" ]
     else
       true
     end
@@ -45,15 +45,20 @@ def has_apptainer_support? #:nodoc:
     self.class.has_apptainer_support?
   end
 
-  # Detects if the system has the 'singularity' command.
+  # caches apptainer_executable_name (which does not change that often)
+  def self.apptainer_executable  #:nodoc:
+    @_executable ||= RemoteResource.current_resource.apptainer_executable_name.presence || "apptainer"
+  end
+
+  # Detects if the system has the apptainer command.
+  # Singularity command is still supported on best effort basis
   # Caches the result in the class so it won't need to
   # be detected again after the first time, for the life
   # of the current process.
   def self.has_apptainer_support? #:nodoc:
     return @_has_apptainer_support if ! @_has_apptainer_support.nil?
-    out = IO.popen("bash -c 'type -p singularity'","r") { |f| f.read }
+    out = IO.popen("bash -c 'type -p #{apptainer_executable}'","r") { |f| f.read }
     @_has_apptainer_support = out.present?
   end
 
 end
-
diff --git a/BrainPortal/cbrain_plugins/cbrain-plugins-base/userfiles/singularity_image/views/_info.html.erb b/BrainPortal/cbrain_plugins/cbrain-plugins-base/userfiles/singularity_image/views/_info.html.erb
index 1f45c8ff0..0c49d8934 100644
--- a/BrainPortal/cbrain_plugins/cbrain-plugins-base/userfiles/singularity_image/views/_info.html.erb
+++ b/BrainPortal/cbrain_plugins/cbrain-plugins-base/userfiles/singularity_image/views/_info.html.erb
@@ -27,21 +27,21 @@
 
 
Image Inspect
-
<%= cat.("singularity inspect #{path}") %>
+
<%= cat.("#{SingularityImage.apptainer_executable} inspect #{path}") %>
SIF Header
-
<%= cat.("singularity sif header #{path}") %>
+
<%= cat.("#{SingularityImage.apptainer_executable} sif header #{path}") %>
SIF List
-
<%= text = cat.("singularity sif list #{path}") %>
+
<%= text = cat.("#{SingularityImage.apptainer_executable} sif list #{path}") %>
SIF Items
<% text.split(/\n/).map { |line| line[/^(\d+)/] }.compact.each do |id| %> -
<%= cat.("singularity sif info #{id} #{path}") %>

+

<%= cat.("#{SingularityImage.apptainer_executable} sif info #{id} #{path}") %>

<% end %> From bb7baf397fccc0ac052253c2668b77707dad0b24 Mon Sep 17 00:00:00 2001 From: MontrealSergiy Date: Mon, 25 Mar 2024 11:30:58 -0400 Subject: [PATCH 07/12] Rename Singularity to Apptainer in tool config Environment Variables, breaks auto-propagating envirment variables for old singularity-based setups, legacy users might need to adjust configs --- BrainPortal/app/models/tool_config.rb | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/BrainPortal/app/models/tool_config.rb b/BrainPortal/app/models/tool_config.rb index 5d41cd156..c1c8e5423 100644 --- a/BrainPortal/app/models/tool_config.rb +++ b/BrainPortal/app/models/tool_config.rb @@ -220,7 +220,6 @@ def to_bash_prologue(apptainer=false) #--------------------------------------------------- ENV_HEADER - script += vars_to_export_script("SINGULARITYENV_") # todo SINGULARITYENV is to be depricated script += vars_to_export_script("APPTAINERENV_") end @@ -318,8 +317,8 @@ def is_at_least_version(version) # true if apptainer image is defined def use_apptainer? - return (self.container_engine == "Singularity" || - self.container_engine == "Apptainer") && # Singularity will be probably deprecated by Boutiques + return (self.container_engine == "Singularity" || # Currently Boutiques keeps singularity only + self.container_engine == "Apptainer") && # Singularity will be probably deprecated by Boutiques soon ( self.containerhub_image_name.present? || self.container_image_userfile_id.present? ) end @@ -447,7 +446,7 @@ def validate_container_rules #:nodoc: end if self.container_engine.present? && (self.container_engine == "Singularity" || self.container_engine == "Apptainer") - if self.container_index_location.present? && self.container_index_location !~ /\A[a-z0-9]+\:\/\/\z/i + if self.container_index_location.present? && self.container_index_location !~ /\A[a-z0-9]+\:\/\/\z/i errors[:container_index_location] = "is invalid for container engine Apptainer. Should end in '://'." end elsif self.container_engine.present? && self.container_engine == "Docker" From 37bf8af39221631c4562d875d2cfff142650f66d Mon Sep 17 00:00:00 2001 From: MontrealSergiy Date: Mon, 25 Mar 2024 11:34:08 -0400 Subject: [PATCH 08/12] sing squashfs dp to apptainer squashfs dp --- .../app/models/sing_squashfs_data_provider.rb | 59 +++++++++++-------- 1 file changed, 35 insertions(+), 24 deletions(-) diff --git a/BrainPortal/app/models/sing_squashfs_data_provider.rb b/BrainPortal/app/models/sing_squashfs_data_provider.rb index cf5efdd2f..267ef48ed 100644 --- a/BrainPortal/app/models/sing_squashfs_data_provider.rb +++ b/BrainPortal/app/models/sing_squashfs_data_provider.rb @@ -22,8 +22,8 @@ # This class implements a Data Provider which fetches # files out of one or several SquashFS files. The -# implementation requires Apptainer 1.1 / Singularity 3.7 or better to -# be installed on the host, as well as a singularity/apptainer +# implementation requires Apptainer 1.1 or better to +# be installed on the host, as well as an apptainer # container image that contains the basic Linux # commands and the 'rsync' command too. class SingSquashfsDataProvider < SshDataProvider @@ -72,6 +72,31 @@ def impl_is_alive? #:nodoc: false end + # Check we have singularity version 3.7 or better or apptainer 1.1 or better + def apptainer_executable_name + return @_tool if @_tool # cached name of executable + + remote_cmd = "( apptainer --version 2>/dev/null || singularity --version 2>/dev/null )" + # apptainer is preferable so it comes first in the command + # also works if an old singularity and uptodate apptainer + # todo loop over list of several candidate executables + text = self.remote_bash_this(remote_cmd) + cb_error "Can't find apptainer version number on remote host" unless text =~ /^((singularity|apptainer) version )?(\d+)\.(\d+)/ + _, _, @_tool, major, minor = Regexp.last_match.to_a + major = major.to_i + minor = minor.to_i + if @_tool == 'singularity' + cb_error "singularity version number on remote host is less than 3.7" if major < 3 || (major == 3 && minor < 7) + else # tool == 'apptainer' + cb_error "apptainer version number on remote host is less than 1.1" if major < 1 || (major == 1 && minor < 1) + end + + return @_tool + + end + + + # Raise an exception with a message indicating what is wrong with the config. # This method is not part of the official method API def check_remote_config! #:nodoc: @@ -84,25 +109,11 @@ def check_remote_config! #:nodoc: # Check we have at least one .squashfs file in the remote_dir sq_files = get_squashfs_basenames cb_error "No .squashfs files found" unless sq_files.present? - - # Check we have singularity version 3.7 or better or apptainer 1.1 or better - remote_cmd = "(singularity --version 2>/dev/null || apptainer --version 2>/dev/null)" - text = self.remote_bash_this(remote_cmd) - cb_error "Can't find singularity version number on remote host" unless text =~ /^((singularity|apptainer) version )?(\d+)\.(\d+)/ - _, _, tool, major, minor = Regexp.last_match.to_a - major = major.to_i - minor = minor.to_i - if tool == 'singularity' - cb_error "singularity version number on remote host is less than 3.7" if major < 3 || (major == 3 && minor < 7) - else # tool == 'apptainer' - cb_error "apptainer version number on remote host is less than 1.1" if major < 1 || (major == 1 && minor < 1) - end - # Check that inside the container all_sq_files = @sq_files @sq_files = [ @sq_files.first ] # To speed up check, use only the first squashfs file checkdir = "test -d #{self.containerized_path.bash_escape} && echo OK-Exists" - text = remote_in_sing_bash_this(checkdir) + text = remote_in_apptainer_bash_this(checkdir) @sq_files = all_sq_files # return it to proper full list cb_error "No path '#{self.containerized_path}' inside container" unless text =~ /\AOK-Exists\s*\z/ @@ -150,7 +161,7 @@ def impl_provider_list_all(user=nil,browse_path=nil) #:nodoc: cache_key = "#{self.class}-#{self.id}-file_infos" file_infos = Rails.cache.fetch(cache_key, :expires_in => BROWSE_CACHE_EXPIRATION) do - text = remote_in_sing_stat_all(self.containerized_path,".",true) + text = remote_in_apptainer_stat_all(self.containerized_path,".",true) stat_reports_to_fileinfos(text) end @@ -182,7 +193,7 @@ def impl_provider_collection_index(userfile, directory = :all, allowed_types = : type_opt = allowed_types == [ :regular ] ? "f" : allowed_types == [ :directory ] ? "d" : nil # we can still filter for other combinations on Ruby side - text = remote_in_sing_stat_all(basedir, subdir, one_level, type_opt) + text = remote_in_apptainer_stat_all(basedir, subdir, one_level, type_opt) file_infos = stat_reports_to_fileinfos(text) # Apply more complex filters if necessary @@ -263,7 +274,7 @@ def provider_is_remote #:nodoc: .map(&:presence) .compact - # Or test is biased so that we try local only if we have a local dir + # Our test is biased so that we try local only if we have a local dir # and the hostname match. if dp_hostnames.include?(Socket.gethostname) && File.directory?(self.remote_dir) @provider_is_remote = false @@ -291,7 +302,7 @@ def get_squashfs_basenames(force = false) #:nodoc: def apptainer_exec_prefix #:nodoc: sq_files = get_squashfs_basenames overlay_opts = sq_files.map { |f| "--overlay=#{f.bash_escape}:ro" }.join(" ") - "cd #{self.remote_dir.bash_escape} && singularity -s exec #{overlay_opts} #{APPTAINER_IMAGE_BASENAME}" + "cd #{self.remote_dir.bash_escape} && #{apptainer_executable_name} -s exec #{overlay_opts} #{APPTAINER_IMAGE_BASENAME}" end def remote_rsync_command #:nodoc: @@ -317,12 +328,12 @@ def rsync_over_ssh_prefix rsync end - def remote_in_sing_bash_this(com) #:nodoc: + def remote_in_apptainer_bash_this(com) #:nodoc: newcom = "#{apptainer_exec_prefix} bash -c #{com.bash_escape}" remote_bash_this(newcom) end - def remote_in_sing_stat_all(basedir, subdir, one_level = true, find_type = nil) #:nodoc: + def remote_in_apptainer_stat_all(basedir, subdir, one_level = true, find_type = nil) #:nodoc: max_depth = one_level ? "-maxdepth 1" : "" type_opt = find_type ? "-type #{find_type}" : "" # Linux 'stat' command formats: @@ -338,7 +349,7 @@ def remote_in_sing_stat_all(basedir, subdir, one_level = true, find_type = nil) # Linux 'find' command format: find_format = "E=%y,%m,%s,%U,%u,%G,%g,%A@,%T@,%C@,%p\\n" com = "cd #{basedir.to_s.bash_escape} && find #{subdir.to_s.bash_escape} #{max_depth} #{type_opt} -printf \"#{find_format}\"" - remote_in_sing_bash_this(com) + remote_in_apptainer_bash_this(com) end # Given a text file report such as this: From 640e8a1cdc5e12f96bdaaa1e24a379bf3e9c600c Mon Sep 17 00:00:00 2001 From: MontrealSergiy Date: Mon, 25 Mar 2024 14:31:03 -0400 Subject: [PATCH 09/12] rename singularity to apptainer inside singularity bindmount dp --- .../app/models/sing_bindmount_data_provider.rb | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/BrainPortal/app/models/sing_bindmount_data_provider.rb b/BrainPortal/app/models/sing_bindmount_data_provider.rb index b0c3e0ada..8ea6f8397 100644 --- a/BrainPortal/app/models/sing_bindmount_data_provider.rb +++ b/BrainPortal/app/models/sing_bindmount_data_provider.rb @@ -134,7 +134,7 @@ def check_remote_config! #:nodoc: # Check that inside the container, the containerized path exists checkdir = "test -d #{self.containerized_path.bash_escape} && echo OK-Exists" - text = remote_in_sing_bash_this(checkdir) + text = remote_in_apptainer_bash_this(checkdir) cb_error "No path '#{self.containerized_path}' inside container" unless text =~ /\AOK-Exists\s*\z/ # Well, we passed all the tests @@ -179,7 +179,7 @@ def impl_provider_list_all(user=nil, browse_path=nil) #:nodoc: file_infos = Rails.cache.fetch(cache_key, :expires_in => BROWSE_CACHE_EXPIRATION) do dir = Pathname.new(self.containerized_path) + browse_path.to_s - text = remote_in_sing_stat_all(dir,".",true) + text = remote_in_apptainer_stat_all(dir,".",true) stat_reports_to_fileinfos(text) end @@ -211,7 +211,7 @@ def impl_provider_collection_index(userfile, directory = :all, allowed_types = : type_opt = allowed_types == [ :regular ] ? "f" : allowed_types == [ :directory ] ? "d" : nil # we can still filter for other combinations on Ruby side - text = remote_in_sing_stat_all(basedir, subdir, one_level, type_opt) + text = remote_in_apptainer_stat_all(basedir, subdir, one_level, type_opt) file_infos = stat_reports_to_fileinfos(text) # Apply more complex filters if necessary @@ -333,12 +333,12 @@ def rsync_over_ssh_prefix rsync end - def remote_in_sing_bash_this(com) #:nodoc: + def remote_in_apptainer_bash_this(com) #:nodoc: newcom = "#{apptainer_exec_prefix} bash -c #{com.bash_escape}" remote_bash_this(newcom) end - def remote_in_sing_stat_all(basedir, subdir, one_level = true, find_type = nil) #:nodoc: + def remote_in_apptainer_stat_all(basedir, subdir, one_level = true, find_type = nil) #:nodoc: max_depth = one_level ? "-maxdepth 1" : "" type_opt = find_type ? "-type #{find_type}" : "" # Linux 'stat' command formats: @@ -354,7 +354,7 @@ def remote_in_sing_stat_all(basedir, subdir, one_level = true, find_type = nil) # Linux 'find' command format: find_format = "E=%y,%m,%s,%U,%u,%G,%g,%A@,%T@,%C@,%p\\n" com = "cd #{basedir.to_s.bash_escape} && find #{subdir.to_s.bash_escape} #{max_depth} #{type_opt} -printf \"#{find_format}\"" - remote_in_sing_bash_this(com) + remote_in_apptainer_bash_this(com) end # Given a text file report such as this: From e12f840e10841b6898456b84ec20161bd81c2b3b Mon Sep 17 00:00:00 2001 From: MontrealSergiy Date: Mon, 25 Mar 2024 14:53:13 -0400 Subject: [PATCH 10/12] singularity to apptainer - unittest change --- BrainPortal/spec/models/tool_config_spec.rb | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/BrainPortal/spec/models/tool_config_spec.rb b/BrainPortal/spec/models/tool_config_spec.rb index 5a3785bee..9e788da6d 100644 --- a/BrainPortal/spec/models/tool_config_spec.rb +++ b/BrainPortal/spec/models/tool_config_spec.rb @@ -159,7 +159,7 @@ context "fill HEADER" do it "should print 'Configuration: tool_config.id'" do expect(tool_config.to_bash_prologue).to match(/Configuration\s?:\s+#\s+#{tool_config.id}/) - expect(tool_config.to_bash_prologue(apptainer: true)).to match(/Configuration\s?:\s+#\s+#{tool_config.id}/) + expect(tool_config.to_bash_prologue(true)).to match(/Configuration\s?:\s+#\s+#{tool_config.id}/) end it "should print 'Tool: ALL' if specific tool is not defined" do @@ -225,16 +225,16 @@ expect(tool_config.to_bash_prologue).to match(/Environment variables\s?:\n\#\-+\n\n#{script}/) end - it "should not print 'Environment variables: export SINGULARITYENV_name1=\"value1\".... if config has no singularity" do + it "should not print 'Environment variables: export APPTAINERENV_name1=\"value1\".... if config has no singularity" do tool_config.env_array = [["name1", "value1"],["name2","value2"]] expect(tool_config.to_bash_prologue).not_to match(/(SINGULARITYENV|APPTAINERENV)/) end - it "should print 'export SINGULARITYENV_name1=\"value1\".... if env is not empty and config uses singularity" do + it "should print 'export APPTAINERENV_name1=\"value1\".... if env is not empty and config uses singularity" do tool_config.env_array = [["name1", "value1"],["name2","value2"]] script = "" tool_config.env_array.each do |name_val| - name = "SINGULARITYENV_" + name_val[0].strip + name = "APPTAINERENV_" + name_val[0].strip val = name_val[1] script += "export #{name}=\\\"#{val}\\\"\\n" end @@ -279,4 +279,3 @@ end end - From 2ba1bf849bb08c6525a742ad1d29be1746f353f4 Mon Sep 17 00:00:00 2001 From: MontrealSergiy Date: Mon, 8 Apr 2024 11:56:41 -0400 Subject: [PATCH 11/12] Apptainer rename in a tool form --- .../views/tool_configs/_form_fields.html.erb | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/BrainPortal/app/views/tool_configs/_form_fields.html.erb b/BrainPortal/app/views/tool_configs/_form_fields.html.erb index fb166cb6a..e06ee4d5b 100644 --- a/BrainPortal/app/views/tool_configs/_form_fields.html.erb +++ b/BrainPortal/app/views/tool_configs/_form_fields.html.erb @@ -194,10 +194,10 @@ <% t.edit_cell(:container_index_location, :header => "Index of the container image") do |f| %> <%= f.text_field :container_index_location %>

- The index (url) of the container image in which the docker or singularity container is + The index (url) of the container image in which the docker or Apptainer/Singularity container is accessible through. Examples for Docker are: quay.io, index.docker.io (default). - Examples for Singularity are: docker://, shub:// (default). + Examples for Apptainer/Singularity are: docker://, shub:// (default).
<% end %> @@ -219,11 +219,11 @@ <% end %> - <% t.edit_cell(:singularity_overlays_specs, :header => "Singularity Overlays", :show_width => 2, :content => full_description(@tool_config.singularity_overlays_specs)) do |f| %> - <%= f.text_area :singularity_overlays_specs, :rows => 6, :cols => 120 %> + <% t.edit_cell(:apptainer_overlays_specs, :header => "Apptainer/Singularity Overlays", :show_width => 2, :content => full_description(@tool_config.apptainer_overlays_specs)) do |f| %> + <%= f.text_area :apptainer_overlays_specs, :rows => 6, :cols => 120 %>
This field can contain one or several specifications for data overlays - to be included when the task is started with Singularity. + to be included when the task is started with Apptainer (formerly known as Singularity). A specification can be either a full path (e.g. file:/a/b/data.squashfs), a path with a pattern (e.g. file:/a/b/data*.squashfs), @@ -237,18 +237,18 @@
<% end %> - <% t.edit_cell(:container_exec_args, :header => "Misc Singularity Options", :show_width => 2) do |f| %> + <% t.edit_cell(:container_exec_args, :header => "Misc Apptainter Options", :show_width => 2) do |f| %> <%= f.text_field :container_exec_args, :size => 60 %>
- This field can contain singularity exec command options. Please use appropriate quotation or escaping + This field can contain apptainer/singularity exec command options. Please use appropriate quotation or escaping For example, --cleanenv --env MYPATH='/My Documents'.
<% end %> - <% t.boolean_edit_cell('tool_config[singularity_use_short_workdir]', - (@tool_config.singularity_use_short_workdir ? "1" : "0"), + <% t.boolean_edit_cell('tool_config[apptainer_use_short_workdir]', + (@tool_config.apptainer_overlays_specs ? "1" : "0"), "1", "0", - :header => "Use short workdirs inside Singularity") + :header => "Use short workdirs inside Apptainer") %> <% end %> From 0c5c3598d5b6b5782258fd39231ddb63b77717e5 Mon Sep 17 00:00:00 2001 From: MontrealSergiy Date: Mon, 8 Apr 2024 17:14:21 -0400 Subject: [PATCH 12/12] Apptainer rename - timestamp update in schema.rb --- ...ner.rb => 20240408164549_rename_singularity_to_apprainer.rb} | 0 BrainPortal/db/schema.rb | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) rename BrainPortal/db/migrate/{20240321134005_rename_singularity_to_apprainer.rb => 20240408164549_rename_singularity_to_apprainer.rb} (100%) diff --git a/BrainPortal/db/migrate/20240321134005_rename_singularity_to_apprainer.rb b/BrainPortal/db/migrate/20240408164549_rename_singularity_to_apprainer.rb similarity index 100% rename from BrainPortal/db/migrate/20240321134005_rename_singularity_to_apprainer.rb rename to BrainPortal/db/migrate/20240408164549_rename_singularity_to_apprainer.rb diff --git a/BrainPortal/db/schema.rb b/BrainPortal/db/schema.rb index 79ff11cbc..0aa74b945 100644 --- a/BrainPortal/db/schema.rb +++ b/BrainPortal/db/schema.rb @@ -10,7 +10,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema.define(version: 20231214214549) do +ActiveRecord::Schema.define(version: 20240408164549) do create_table "access_profiles", force: :cascade, options: "ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci" do |t| t.string "name", null: false