Merge branch '17742-provide-custom-certs'
authorJavier Bértoli <jbertoli@curii.com>
Thu, 21 Oct 2021 19:00:13 +0000 (16:00 -0300)
committerJavier Bértoli <jbertoli@curii.com>
Thu, 21 Oct 2021 19:00:13 +0000 (16:00 -0300)
closes #17742
Arvados-DCO-1.1-Signed-off-by: Javier Bértoli <jbertoli@curii.com>

17 files changed:
doc/_config.yml
doc/_includes/_install_debian_key.liquid
doc/architecture/singularity.html.textile.liquid [new file with mode: 0644]
doc/install/arvados-on-kubernetes-GKE.html.textile.liquid
doc/install/crunch2-lsf/install-dispatch.html.textile.liquid
doc/install/singularity.html.textile.liquid
doc/sdk/python/sdk-python.html.textile.liquid
lib/config/config.default.yml
lib/config/generated_config.go
lib/crunchrun/singularity.go
sdk/cli/bin/arv
sdk/cwl/arvados_cwl/executor.py
sdk/cwl/arvados_cwl/runner.py
sdk/cwl/setup.py
sdk/python/arvados/commands/keepdocker.py
services/login-sync/bin/arvados-login-sync
tools/arvbox/lib/arvbox/docker/keep-setup.sh

index ff924e2f378381f4c8f4e586f3c2c3570d8202e2..6f1c90d9b0771bfa70ae5681ec142dbfbf9af21a 100644 (file)
@@ -162,6 +162,7 @@ navbar:
     - Computation with Crunch:
       - api/execution.html.textile.liquid
       - architecture/dispatchcloud.html.textile.liquid
+      - architecture/singularity.html.textile.liquid
     - Other:
       - api/permission-model.html.textile.liquid
       - architecture/federation.html.textile.liquid
index 1d5f73a4d28ef9d6863832668f6a8474f756204d..b25674c8ce4db0af2d1a2aac3c181693b418d86c 100644 (file)
@@ -6,7 +6,7 @@ SPDX-License-Identifier: CC-BY-SA-3.0
 
 <notextile>
 <pre><code># <span class="userinput">apt-get --no-install-recommends install curl gnupg2</span>
-# <span class="userinput">curl https://apt.arvados.org/pubkey.gpg -o /etc/apt/trusted.gpg.d/arvados.asc</span>
+# <span class="userinput">curl -s https://apt.arvados.org/pubkey.gpg -o /etc/apt/trusted.gpg.d/arvados.asc</span>
 </code></pre>
 </notextile>
 
diff --git a/doc/architecture/singularity.html.textile.liquid b/doc/architecture/singularity.html.textile.liquid
new file mode 100644 (file)
index 0000000..dc10d28
--- /dev/null
@@ -0,0 +1,35 @@
+---
+layout: default
+navsection: architecture
+title: Singularity
+...
+{% comment %}
+Copyright (C) The Arvados Authors. All rights reserved.
+
+SPDX-License-Identifier: CC-BY-SA-3.0
+{% endcomment %}
+
+Arvados can be configured to use "Singularity":https://sylabs.io/singularity/ instead of Docker to execute containers on cloud nodes or a SLURM/LSF cluster. Singularity may be preferable due to its simpler installation and lack of long-running daemon process and special system users/groups. See the "Singularity page in the installation guide":{{ site.baseurl }}/install/singularity.html for configuration details.
+
+h2. Design overview
+
+When Arvados is configured to use Singularity as the runtime engine for Crunch, containers are executed by Singularity. The images specified in workflows and tool definitions must be Docker images uploaded via @arv-keepdocker@ or @arvados-cwl-runner@. When Singularity is the runtime engine, these images are converted to Singularity format (@.sif@) at runtime, as needed.
+
+To avoid repeating this conversion work unnecessarily, the @.sif@ files are cached in @Keep@. This is done on a per-user basis. If it does not exist yet, a new Arvados project named @.cache@ is automatically created in the user's home project. Similarly, a subproject named @auto-generated singularity images@ will be created in the @.cache@ project. The automatically generated @.sif@ files are stored in collections in that project, with an expiration date two weeks in the future. If the cached image exists when Crunch runs a new container, the expiration date will be pushed out, so that it is always 2 weeks in the future from the most recent start of a container using the image.
+
+It is safe to empty out or even remove the .cache project or any of its contents; if necessary the cache projects and the @.sif@ files will automatically be regenerated.
+
+h2. Notes
+
+* Programs running in Singularity containers may behave differently than when run in Docker, due to differences between Singularity and Docker. For example, the root (image) filesystem is read-only in a Singularity container. Programs that attempt to write outside a designated output or temporary directory are likely to fail.
+
+* When using Singularity as the runtime engine, the compute node needs to have a compatible Singularity executable installed, as well as the @mksquashfs@ program used to convert Docker images to Singularity's @.sif@ format. The Arvados "compute node image build script":{{ site.baseurl }}/install/crunch2-cloud/install-compute-node.html includes these executables since Arvados 2.3.0.
+
+h2. Limitations
+
+Arvados @Singularity@ support is a work in progress. These are the current limitations of the implementation:
+
+* Even when using the Singularity runtime, users' container images are expected to be saved in Docker format. Specifying a @.sif@ file as an image when submitting a container request is not yet supported.
+* Arvados' Singularity implementation does not yet limit the amount of memory available in a container. Each container will have access to all memory on the host where it runs, unless memory use is restricted by SLURM/LSF.
+* The Docker ENTRYPOINT instruction is ignored.
+* Arvados is tested with Singularity version 3.7.4. Other versions may not work.
index 06280b467d61ad71f1af3c9acc7760b24dfee306..f7b7a1641526f8d19d007335dd020c78ebd648ed 100644 (file)
@@ -34,7 +34,7 @@ h3. Create the GKE cluster
 This can be done via the "cloud console":https://console.cloud.google.com/kubernetes/ or via the command line:
 
 <pre>
-$ gcloud container clusters create <CLUSTERNAME> --zone us-central1-a --machine-type n1-standard-2 --cluster-version 1.15
+$ gcloud container clusters create <CLUSTERNAME> --zone us-central1-a --machine-type n1-standard-2
 </pre>
 
 It takes a few minutes for the cluster to be initialized.
index 6fda506888f85692a03800269dec87a010178ba1..c38e6f20557f6bcaa4ef37d5b8d3390c339d6bee 100644 (file)
@@ -29,6 +29,15 @@ h2(#update-config). Update config.yml
 
 Arvados-dispatch-lsf reads the common configuration file at @/etc/arvados/config.yml@.
 
+Add a DispatchLSF entry to the Services section, using the hostname where @arvados-dispatch-lsf@ will run, and an available port:
+
+<notextile>
+<pre>    Services:
+      DispatchLSF:
+        InternalURLs:
+          "http://<code class="userinput">hostname.zzzzz.arvadosapi.com:9007</code>": {}</pre>
+</notextile>
+
 Review the following configuration parameters and adjust as needed.
 
 h3(#BsubSudoUser). Containers.LSF.BsubSudoUser
index bd990b491b77741172d59e773673d99d97fbdc1d..b2a39790d7c7bb1e0b373a6fc394faebd18b385d 100644 (file)
@@ -11,15 +11,7 @@ SPDX-License-Identifier: CC-BY-SA-3.0
 
 h2(#overview). Overview
 
-Arvados can be configured to use "Singularity":https://sylabs.io/singularity/ instead of Docker to execute containers on cloud nodes or a SLURM/LSF cluster. Singularity may be preferable due to its simpler installation and lack of long-running daemon process and special system users/groups.
-
-*Current limitations*:
-* Even when using the singularity runtime, users' container images are expected to be saved in Docker format using @arv keep docker@. Arvados converts the Docker image to Singularity format (@.sif@) at runtime as needed. Specifying a @.sif@ file as an image when submitting a container request is not yet supported.
-* Singularity does not limit the amount of memory available in a container. Each container will have access to all memory on the host where it runs, unless memory use is restricted by SLURM/LSF.
-* Programs running in containers may behave differently due to differences between Singularity and Docker.
-** The root (image) filesystem is read-only in a Singularity container. Programs that attempt to write outside a designated output or temporary directory are likely to fail.
-** The Docker ENTRYPOINT instruction is ignored.
-* Arvados is tested with Singularity version 3.7.4. Other versions may not work.
+Please refer to the "Singularity":{{site.baseurl}}/architecture/singularity.html documentation in the Architecture section.
 
 h2(#configuration). Configuration
 
@@ -43,3 +35,7 @@ Then update @Containers.RuntimeEngine@ in your cluster configuration:
 </notextile>
 
 Restart your dispatcher (@crunch-dispatch-slurm@, @arvados-dispatch-cloud@, or @arvados-dispatch-lsf@) after updating your configuration file.
+
+h2(#singularity_configuration). Singularity configuration
+
+Docker images are converted on the fly by @mksquashfs@, which can consume a considerable amount of RAM. The RAM usage of mksquashfs can be restricted in @/etc/singularity/singularity.conf@ with a line like @mksquashfs mem = 512M@. The amount of memory made available for mksquashfs should be configured lower than the smallest amount of memory requested by a container on the cluster to avoid the conversion being killed for using too much memory.
index 09eaa8e552c8e6a887cc8e4ce5b0047928024bbc..435f70e7bfda47efa06ccebe0bdf2263a8a20f13 100644 (file)
@@ -18,7 +18,7 @@ If you are logged in to an Arvados VM, the Python SDK should be installed.
 
 To use the Python SDK elsewhere, you can install from PyPI or a distribution package.
 
-As of Arvados 2.1, the Python SDK requires Python 3.5+.  The last version to support Python 2.7 is Arvados 2.0.4.
+As of Arvados 2.2, the Python SDK requires Python 3.6+.  The last version to support Python 2.7 is Arvados 2.0.4.
 
 h2. Option 1: Install from a distribution package
 
index 8cbe51d2045a28a0612a2bfc60ff092ed9c43338..4e2a0e26d4bb599314e6f3292aeaf6cc1b2fef85 100644 (file)
@@ -1091,13 +1091,25 @@ Clusters:
 
         # Maximum create/destroy-instance operations per second (0 =
         # unlimited).
-        MaxCloudOpsPerSecond: 0
+        MaxCloudOpsPerSecond: 10
 
-        # Maximum concurrent node creation operations (0 = unlimited). This is
-        # recommended by Azure in certain scenarios (see
-        # https://docs.microsoft.com/en-us/azure/virtual-machines/linux/capture-image)
-        # and can be used with other cloud providers too, if desired.
-        MaxConcurrentInstanceCreateOps: 0
+        # Maximum concurrent instance creation operations (0 = unlimited).
+        #
+        # MaxConcurrentInstanceCreateOps limits the number of instance creation
+        # requests that can be in flight at any one time, whereas
+        # MaxCloudOpsPerSecond limits the number of create/destroy operations
+        # that can be started per second.
+        #
+        # Because the API for instance creation on Azure is synchronous, it is
+        # recommended to increase MaxConcurrentInstanceCreateOps when running
+        # on Azure. When using managed images, a value of 20 would be
+        # appropriate. When using Azure Shared Image Galeries, it could be set
+        # higher. For more information, see
+        # https://docs.microsoft.com/en-us/azure/virtual-machines/linux/capture-image
+        #
+        # MaxConcurrentInstanceCreateOps can be increased for other cloud
+        # providers too, if desired.
+        MaxConcurrentInstanceCreateOps: 1
 
         # Interval between cloud provider syncs/updates ("list all
         # instances").
index 44a58f3593518ec6865dabfe894db02f3099a265..875939a3e191731d33892127f5376ed93c18af78 100644 (file)
@@ -1097,13 +1097,25 @@ Clusters:
 
         # Maximum create/destroy-instance operations per second (0 =
         # unlimited).
-        MaxCloudOpsPerSecond: 0
+        MaxCloudOpsPerSecond: 10
 
-        # Maximum concurrent node creation operations (0 = unlimited). This is
-        # recommended by Azure in certain scenarios (see
-        # https://docs.microsoft.com/en-us/azure/virtual-machines/linux/capture-image)
-        # and can be used with other cloud providers too, if desired.
-        MaxConcurrentInstanceCreateOps: 0
+        # Maximum concurrent instance creation operations (0 = unlimited).
+        #
+        # MaxConcurrentInstanceCreateOps limits the number of instance creation
+        # requests that can be in flight at any one time, whereas
+        # MaxCloudOpsPerSecond limits the number of create/destroy operations
+        # that can be started per second.
+        #
+        # Because the API for instance creation on Azure is synchronous, it is
+        # recommended to increase MaxConcurrentInstanceCreateOps when running
+        # on Azure. When using managed images, a value of 20 would be
+        # appropriate. When using Azure Shared Image Galeries, it could be set
+        # higher. For more information, see
+        # https://docs.microsoft.com/en-us/azure/virtual-machines/linux/capture-image
+        #
+        # MaxConcurrentInstanceCreateOps can be increased for other cloud
+        # providers too, if desired.
+        MaxConcurrentInstanceCreateOps: 1
 
         # Interval between cloud provider syncs/updates ("list all
         # instances").
index 70ad653b7d5a8934a49a33532789c4ee77da3e85..5af023a83dc2dc61506818c88ccfadc5b0c22514 100644 (file)
@@ -160,7 +160,22 @@ func (e *singularityExecutor) LoadImage(dockerImageID string, imageTarballPath s
                        return err
                }
 
+               // Set up a cache and tmp dir for singularity build
+               err = os.Mkdir(e.tmpdir+"/cache", 0700)
+               if err != nil {
+                       return err
+               }
+               defer os.RemoveAll(e.tmpdir + "/cache")
+               err = os.Mkdir(e.tmpdir+"/tmp", 0700)
+               if err != nil {
+                       return err
+               }
+               defer os.RemoveAll(e.tmpdir + "/tmp")
+
                build := exec.Command("singularity", "build", imageFilename, "docker-archive://"+e.tmpdir+"/image.tar")
+               build.Env = os.Environ()
+               build.Env = append(build.Env, "SINGULARITY_CACHEDIR="+e.tmpdir+"/cache")
+               build.Env = append(build.Env, "SINGULARITY_TMPDIR="+e.tmpdir+"/tmp")
                e.logf("%v", build.Args)
                out, err := build.CombinedOutput()
                // INFO:    Starting build...
index c9809fad6ab75e05ef20e716310a49a70ff07584..9d4a609377b3d0b6fe4eb9ed65d2a7df23a70f41 100755 (executable)
@@ -98,7 +98,7 @@ def init_config
 end
 
 
-subcommands = %w(copy create edit get keep pipeline run tag ws)
+subcommands = %w(copy create edit get keep tag ws)
 
 def exec_bin bin, opts
   bin_path = `which #{bin.shellescape}`.strip
@@ -116,7 +116,7 @@ def check_subcommands client, arvados, subcommand, global_opts, remaining_opts
     arv_edit client, arvados, global_opts, remaining_opts
   when 'get'
     arv_get client, arvados, global_opts, remaining_opts
-  when 'copy', 'tag', 'ws', 'run'
+  when 'copy', 'tag', 'ws'
     exec_bin "arv-#{subcommand}", remaining_opts
   when 'keep'
     @sub = remaining_opts.shift
index aa19633d8c7e86067a02d823d0c638abb318ad4d..6e23d80a85e2434c222e47d7a1589f1087a9a3d9 100644 (file)
@@ -577,8 +577,8 @@ The 'jobs' API is no longer supported.
 
         loadingContext = self.loadingContext.copy()
         loadingContext.do_validate = False
-        loadingContext.do_update = False
         if submitting:
+            loadingContext.do_update = False
             # Document may have been auto-updated. Reload the original
             # document with updating disabled because we want to
             # submit the document with its original CWL version, not
index ada64ae69aa62f0c1e4487bd28160c071fb3d0a1..145f1ad7f9d7572f1f4074da514cd0ea0f771d3e 100644 (file)
@@ -184,7 +184,10 @@ def set_secondary(fsaccess, builder, inputschema, secondaryspec, primary, discov
             elif isinstance(pattern, dict):
                 specs.append(pattern)
             elif isinstance(pattern, str):
-                specs.append({"pattern": pattern, "required": sf.get("required")})
+                if builder.cwlVersion == "v1.0":
+                    specs.append({"pattern": pattern, "required": True})
+                else:
+                    specs.append({"pattern": pattern, "required": sf.get("required")})
             else:
                 raise SourceLine(primary["secondaryFiles"], i, validate.ValidationException).makeError(
                     "Expression must return list, object, string or null")
@@ -194,6 +197,9 @@ def set_secondary(fsaccess, builder, inputschema, secondaryspec, primary, discov
             if isinstance(sf, dict):
                 if sf.get("class") == "File":
                     pattern = None
+                    if sf.get("location") is None:
+                        raise SourceLine(primary["secondaryFiles"], i, validate.ValidationException).makeError(
+                            "File object is missing 'location': %s" % sf)
                     sfpath = sf["location"]
                     required = True
                 else:
index 0bde76e92ff5d96974c93fd34c76fa19da3e003c..e39fdd8d9f2db8259a210649d279b08f163d054e 100644 (file)
@@ -39,8 +39,8 @@ setup(name='arvados-cwl-runner',
       # file to determine what version of cwltool and schema-salad to
       # build.
       install_requires=[
-          'cwltool==3.1.20210922203925',
-          'schema-salad==8.2.20210918131710',
+          'cwltool==3.1.20211020155521',
+          'schema-salad==8.2.20211020114435',
           'arvados-python-client{}'.format(pysdk_dep),
           'setuptools',
           'ciso8601 >= 2.0.0',
index f3df4e3f393bc15090dab3de80def1b50c595cc3..537ea3a9459f2ede6e5b5c87eada9c15fc778554 100644 (file)
@@ -87,9 +87,9 @@ def popen_docker(cmd, *args, **kwargs):
     kwargs.setdefault('stdin', subprocess.PIPE)
     kwargs.setdefault('stdout', sys.stderr)
     try:
-        docker_proc = subprocess.Popen(['docker.io'] + cmd, *args, **kwargs)
-    except OSError:  # No docker.io in $PATH
         docker_proc = subprocess.Popen(['docker'] + cmd, *args, **kwargs)
+    except OSError:  # No docker in $PATH, try docker.io
+        docker_proc = subprocess.Popen(['docker.io'] + cmd, *args, **kwargs)
     if manage_stdin:
         docker_proc.stdin.close()
     return docker_proc
@@ -146,20 +146,18 @@ def docker_images():
     check_docker(list_proc, "images")
 
 def find_image_hashes(image_search, image_tag=None):
-    # Given one argument, search for Docker images with matching hashes,
-    # and return their full hashes in a set.
-    # Given two arguments, also search for a Docker image with the
-    # same repository and tag.  If one is found, return its hash in a
-    # set; otherwise, fall back to the one-argument hash search.
-    # Returns None if no match is found, or a hash search is ambiguous.
-    hash_search = image_search.lower()
-    hash_matches = set()
-    for image in docker_images():
-        if (image.repo == image_search) and (image.tag == image_tag):
-            return set([image.hash])
-        elif image.hash.startswith(hash_search):
-            hash_matches.add(image.hash)
-    return hash_matches
+    # Query for a Docker images with the repository and tag and return
+    # the image ids in a list.  Returns empty list if no match is
+    # found.
+
+    list_proc = popen_docker(['inspect', "%s%s" % (image_search, ":"+image_tag if image_tag else "")], stdout=subprocess.PIPE)
+
+    inspect = list_proc.stdout.read()
+    list_proc.stdout.close()
+
+    imageinfo = json.loads(inspect)
+
+    return [i["Id"] for i in imageinfo]
 
 def find_one_image_hash(image_search, image_tag=None):
     hashes = find_image_hashes(image_search, image_tag)
index 8e5c6deb5dc8ca47a08dd169157116141aeb0518..da8a21efa37c8a8db91b925bc56040f7bff494b8 100755 (executable)
@@ -21,9 +21,10 @@ end
 options = {}
 OptionParser.new do |parser|
   parser.on('--exclusive', 'Manage SSH keys file exclusively.')
-  parser.on('--rotate-tokens', 'Always create new user tokens. Usually needed with --token-lifetime.')
+  parser.on('--rotate-tokens', 'Force a rotation of all user tokens.')
   parser.on('--skip-missing-users', "Don't try to create any local accounts.")
   parser.on('--token-lifetime SECONDS', 'Create user tokens that expire after SECONDS.', Integer)
+  parser.on('--debug', 'Enable debug output')
 end.parse!(into: options)
 
 exclusive_banner = "#######################################################################################
@@ -35,6 +36,10 @@ end_banner = "### END Arvados-managed keys -- changes between markers will be ov
 keys = ''
 
 begin
+  debug = false
+  if options[:"debug"]
+    debug = true
+  end
   arv = Arvados.new({ :suppress_ssl_warnings => false })
   logincluster_arv = Arvados.new({ :api_host => (ENV['LOGINCLUSTER_ARVADOS_API_HOST'] || ENV['ARVADOS_API_HOST']),
                                    :api_token => (ENV['LOGINCLUSTER_ARVADOS_API_TOKEN'] || ENV['ARVADOS_API_TOKEN']),
@@ -75,7 +80,7 @@ begin
         end
       else
         if pwnam[l[:username]].uid < uid_min
-          STDERR.puts "Account #{l[:username]} uid #{pwnam[l[:username]].uid} < uid_min #{uid_min}. Skipping"
+          STDERR.puts "Account #{l[:username]} uid #{pwnam[l[:username]].uid} < uid_min #{uid_min}. Skipping" if debug
           true
         end
       end
@@ -85,6 +90,7 @@ begin
 
   # Collect all keys
   logins.each do |l|
+    STDERR.puts("Considering #{l[:username]} ...") if debug
     keys[l[:username]] = Array.new() if not keys.has_key?(l[:username])
     key = l[:public_key]
     if !key.nil?
@@ -197,7 +203,32 @@ begin
     tokenfile = File.join(configarvados, "settings.conf")
 
     begin
-      if !File.exist?(tokenfile) || options[:"rotate-tokens"]
+      STDERR.puts "Processing #{tokenfile} ..." if debug
+      newToken = false
+      if File.exist?(tokenfile)
+        # check if the token is still valid
+        myToken = ENV["ARVADOS_API_TOKEN"]
+        userEnv = IO::read(tokenfile)
+        if (m = /^ARVADOS_API_TOKEN=(.*?\n)/m.match(userEnv))
+          begin
+            tmp_arv = Arvados.new({ :api_host => (ENV['LOGINCLUSTER_ARVADOS_API_HOST'] || ENV['ARVADOS_API_HOST']),
+                                   :api_token => (m[1]),
+                      :suppress_ssl_warnings => false })
+            tmp_arv.user.current
+          rescue Arvados::TransactionFailedError => e
+            if e.to_s =~ /401 Unauthorized/
+              STDERR.puts "Account #{l[:username]} token not valid, creating new token."
+              newToken = true
+            else
+              raise
+            end
+          end
+        end
+      elsif !File.exist?(tokenfile) || options[:"rotate-tokens"]
+        STDERR.puts "Account #{l[:username]} token file not found, creating new token."
+        newToken = true
+      end
+      if newToken
         aca_params = {owner_uuid: l[:user_uuid], api_client_id: 0}
         if options[:"token-lifetime"] && options[:"token-lifetime"] > 0
           aca_params.merge!(expires_at: (Time.now + options[:"token-lifetime"]))
index cb64f8406f5b13164b9aec4377b4cb7e07e0dd0f..7775ac51da6c9b207c2b39e522bb064f5feebb92 100755 (executable)
@@ -10,8 +10,7 @@ set -eux -o pipefail
 . /usr/local/lib/arvbox/common.sh
 . /usr/local/lib/arvbox/go-setup.sh
 
-flock /var/lib/gopath/gopath.lock go install "git.arvados.org/arvados.git/services/keepstore"
-install $GOPATH/bin/keepstore /usr/local/bin
+(cd /usr/local/bin && ln -sf arvados-server keepstore)
 
 if test "$1" = "--only-deps" ; then
     exit