[arvados] updated: 2.7.1-39-g6672bb30c6

git repository hosting git at public.arvados.org
Mon Apr 1 12:56:44 UTC 2024


Summary of changes:
 build/run-tests.sh                                 |  2 +-
 doc/admin/upgrading.html.textile.liquid            |  6 ++
 .../install-dispatch.html.textile.liquid           | 11 ++-
 lib/config/config.default.yml                      | 27 +++++++-
 lib/controller/integration_test.go                 | 81 ++++++++++++++++++----
 lib/lsf/dispatch.go                                | 21 +++++-
 lib/lsf/dispatch_test.go                           | 42 +++++++++--
 sdk/go/arvados/config.go                           |  8 ++-
 .../api/app/models/api_client_authorization.rb     |  4 ++
 services/api/app/models/container_request.rb       |  5 +-
 services/api/test/integration/remote_user_test.rb  | 34 ++++++++-
 11 files changed, 206 insertions(+), 35 deletions(-)

       via  6672bb30c62b840a18e1f83812ec65a098c19109 (commit)
       via  cd97f79249a180b6952587a58fd524ed21bee05a (commit)
       via  d308b870d530b37c5e1f5ce4c79a2031c10963fc (commit)
      from  7e82101003b5cb4055a8c1e870414f629e3955e6 (commit)

Those revisions listed above that are new to this repository have
not appeared on any other notification email; so we list those
revisions in full, below.


commit 6672bb30c62b840a18e1f83812ec65a098c19109
Author: Tom Clegg <tom at curii.com>
Date:   Fri Mar 29 16:39:09 2024 -0400

    Merge branch '21617-fed-content'
    
    fixes #21617
    fixes #21533
    
    Arvados-DCO-1.1-Signed-off-by: Tom Clegg <tom at curii.com>

diff --git a/lib/controller/integration_test.go b/lib/controller/integration_test.go
index 4bf7a03447..a76e707c8d 100644
--- a/lib/controller/integration_test.go
+++ b/lib/controller/integration_test.go
@@ -28,6 +28,7 @@ import (
 	"git.arvados.org/arvados.git/sdk/go/arvadostest"
 	"git.arvados.org/arvados.git/sdk/go/ctxlog"
 	"git.arvados.org/arvados.git/sdk/go/httpserver"
+	"git.arvados.org/arvados.git/sdk/go/keepclient"
 	check "gopkg.in/check.v1"
 )
 
@@ -167,6 +168,20 @@ func (s *IntegrationSuite) TestDefaultStorageClassesOnCollections(c *check.C) {
 	c.Assert(coll.StorageClassesDesired, check.DeepEquals, kc.DefaultStorageClasses)
 }
 
+func (s *IntegrationSuite) createTestCollectionManifest(c *check.C, ac *arvados.Client, kc *keepclient.KeepClient, content string) string {
+	fs, err := (&arvados.Collection{}).FileSystem(ac, kc)
+	c.Assert(err, check.IsNil)
+	f, err := fs.OpenFile("test.txt", os.O_CREATE|os.O_RDWR, 0777)
+	c.Assert(err, check.IsNil)
+	_, err = io.WriteString(f, content)
+	c.Assert(err, check.IsNil)
+	err = f.Close()
+	c.Assert(err, check.IsNil)
+	mtxt, err := fs.MarshalManifest(".")
+	c.Assert(err, check.IsNil)
+	return mtxt
+}
+
 func (s *IntegrationSuite) TestGetCollectionByPDH(c *check.C) {
 	conn1 := s.super.Conn("z1111")
 	rootctx1, _, _ := s.super.RootClients("z1111")
@@ -175,34 +190,70 @@ func (s *IntegrationSuite) TestGetCollectionByPDH(c *check.C) {
 
 	// Create the collection to find its PDH (but don't save it
 	// anywhere yet)
-	var coll1 arvados.Collection
-	fs1, err := coll1.FileSystem(ac1, kc1)
-	c.Assert(err, check.IsNil)
-	f, err := fs1.OpenFile("test.txt", os.O_CREATE|os.O_RDWR, 0777)
-	c.Assert(err, check.IsNil)
-	_, err = io.WriteString(f, "IntegrationSuite.TestGetCollectionByPDH")
-	c.Assert(err, check.IsNil)
-	err = f.Close()
-	c.Assert(err, check.IsNil)
-	mtxt, err := fs1.MarshalManifest(".")
-	c.Assert(err, check.IsNil)
+	mtxt := s.createTestCollectionManifest(c, ac1, kc1, c.TestName())
 	pdh := arvados.PortableDataHash(mtxt)
 
 	// Looking up the PDH before saving returns 404 if cycle
 	// detection is working.
-	_, err = conn1.CollectionGet(userctx1, arvados.GetOptions{UUID: pdh})
+	_, err := conn1.CollectionGet(userctx1, arvados.GetOptions{UUID: pdh})
 	c.Assert(err, check.ErrorMatches, `.*404 Not Found.*`)
 
 	// Save the collection on cluster z1111.
-	coll1, err = conn1.CollectionCreate(userctx1, arvados.CreateOptions{Attrs: map[string]interface{}{
+	_, err = conn1.CollectionCreate(userctx1, arvados.CreateOptions{Attrs: map[string]interface{}{
 		"manifest_text": mtxt,
 	}})
 	c.Assert(err, check.IsNil)
 
 	// Retrieve the collection from cluster z3333.
-	coll, err := conn3.CollectionGet(userctx1, arvados.GetOptions{UUID: pdh})
+	coll2, err := conn3.CollectionGet(userctx1, arvados.GetOptions{UUID: pdh})
 	c.Check(err, check.IsNil)
-	c.Check(coll.PortableDataHash, check.Equals, pdh)
+	c.Check(coll2.PortableDataHash, check.Equals, pdh)
+}
+
+func (s *IntegrationSuite) TestFederation_Write1Read2(c *check.C) {
+	s.testFederationCollectionAccess(c, "z1111", "z2222")
+}
+
+func (s *IntegrationSuite) TestFederation_Write2Read1(c *check.C) {
+	s.testFederationCollectionAccess(c, "z2222", "z1111")
+}
+
+func (s *IntegrationSuite) TestFederation_Write2Read3(c *check.C) {
+	s.testFederationCollectionAccess(c, "z2222", "z3333")
+}
+
+func (s *IntegrationSuite) testFederationCollectionAccess(c *check.C, writeCluster, readCluster string) {
+	conn1 := s.super.Conn("z1111")
+	rootctx1, _, _ := s.super.RootClients("z1111")
+	_, ac1, _, _ := s.super.UserClients("z1111", rootctx1, c, conn1, s.oidcprovider.AuthEmail, true)
+
+	connW := s.super.Conn(writeCluster)
+	userctxW, acW, kcW := s.super.ClientsWithToken(writeCluster, ac1.AuthToken)
+	kcW.DiskCacheSize = keepclient.DiskCacheDisabled
+	connR := s.super.Conn(readCluster)
+	userctxR, acR, kcR := s.super.ClientsWithToken(readCluster, ac1.AuthToken)
+	kcR.DiskCacheSize = keepclient.DiskCacheDisabled
+
+	filedata := fmt.Sprintf("%s: write to %s, read from %s", c.TestName(), writeCluster, readCluster)
+	mtxt := s.createTestCollectionManifest(c, acW, kcW, filedata)
+	collW, err := connW.CollectionCreate(userctxW, arvados.CreateOptions{Attrs: map[string]interface{}{
+		"manifest_text": mtxt,
+	}})
+	c.Assert(err, check.IsNil)
+
+	collR, err := connR.CollectionGet(userctxR, arvados.GetOptions{UUID: collW.UUID})
+	if !c.Check(err, check.IsNil) {
+		return
+	}
+	fsR, err := collR.FileSystem(acR, kcR)
+	if !c.Check(err, check.IsNil) {
+		return
+	}
+	buf, err := fs.ReadFile(arvados.FS(fsR), "test.txt")
+	if !c.Check(err, check.IsNil) {
+		return
+	}
+	c.Check(string(buf), check.Equals, filedata)
 }
 
 // Tests bug #18004
diff --git a/services/api/app/models/api_client_authorization.rb b/services/api/app/models/api_client_authorization.rb
index a03ce4f899..8aeaf2f9cb 100644
--- a/services/api/app/models/api_client_authorization.rb
+++ b/services/api/app/models/api_client_authorization.rb
@@ -294,6 +294,10 @@ class ApiClientAuthorization < ArvadosModel
         raise "remote cluster #{upstream_cluster_id} returned invalid token uuid #{token_uuid.inspect}"
       end
     rescue HTTPClient::BadResponseError => e
+      if e.res.status_code >= 400 && e.res.status_code < 500
+        # Remote cluster does not accept this token.
+        return nil
+      end
       # CurrentApiToken#call and ApplicationController#render_error will
       # propagate the status code from the #http_status method, so define
       # that here.
diff --git a/services/api/test/integration/remote_user_test.rb b/services/api/test/integration/remote_user_test.rb
index f42fda4150..1a67522f4d 100644
--- a/services/api/test/integration/remote_user_test.rb
+++ b/services/api/test/integration/remote_user_test.rb
@@ -593,15 +593,43 @@ class RemoteUsersTest < ActionDispatch::IntegrationTest
     assert_equal 'zzzzz-tpzed-anonymouspublic', json_response['uuid']
   end
 
-  [401, 403, 422, 500, 502, 503].each do |status|
-    test "propagate #{status} response from getting remote token" do
+  [400, 401, 403, 422, 500, 502, 503].each do |status|
+    test "handle #{status} response when checking remote-provided v2 token" do
       @stub_token_status = status
       get "/arvados/v1/users/#{@stub_content[:uuid]}",
           params: {format: "json"},
           headers: auth(remote: "zbbbb")
-      assert_response status
+      assert_response(status < 500 ? 401 : status)
     end
 
+    test "handle #{status} response when checking remote-provided v2 token at anonymously accessible endpoint" do
+      @stub_token_status = status
+      get "/arvados/v1/keep_services/accessible",
+          params: {format: "json"},
+          headers: auth(remote: "zbbbb")
+      assert_response(status < 500 ? :success : status)
+    end
+
+    test "handle #{status} response when checking token issued by login cluster" do
+      @stub_token_status = status
+      Rails.configuration.Login.LoginCluster = "zbbbb"
+      get "/arvados/v1/users/current",
+          params: {format: "json"},
+          headers: {'HTTP_AUTHORIZATION' => "Bearer badtoken"}
+      assert_response(status < 500 ? 401 : status)
+    end
+
+    test "handle #{status} response when checking token issued by login cluster at anonymously accessible endpoint" do
+      @stub_token_status = status
+      Rails.configuration.Login.LoginCluster = "zbbbb"
+      get "/arvados/v1/keep_services/accessible",
+          params: {format: "json"},
+          headers: {'HTTP_AUTHORIZATION' => "Bearer badtoken"}
+      assert_response(status < 500 ? :success : status)
+    end
+  end
+
+  [401, 403, 422, 500, 502, 503].each do |status|
     test "propagate #{status} response from getting uncached user" do
       @stub_status = status
       get "/arvados/v1/users/#{@stub_content[:uuid]}",

commit cd97f79249a180b6952587a58fd524ed21bee05a
Author: Peter Amstutz <peter.amstutz at curii.com>
Date:   Thu Mar 28 14:52:18 2024 -0400

    Add upgrading note about MaxRunTimeOverhead and MaxRunTimeDefault
    
    refs #21463
    
    Arvados-DCO-1.1-Signed-off-by: Peter Amstutz <peter.amstutz at curii.com>

diff --git a/doc/admin/upgrading.html.textile.liquid b/doc/admin/upgrading.html.textile.liquid
index fe15518253..9139739e71 100644
--- a/doc/admin/upgrading.html.textile.liquid
+++ b/doc/admin/upgrading.html.textile.liquid
@@ -36,6 +36,12 @@ h3. Check MaxGatewayTunnels config
 
 If you use the LSF or Slurm dispatcher, ensure the new @API.MaxGatewayTunnels@ config entry is high enough to support the size of your cluster. See "LSF docs":{{site.baseurl}}/install/crunch2-lsf/install-dispatch.html#MaxGatewayTunnels or "Slurm docs":{{site.baseurl}}/install/crunch2-slurm/install-dispatch.html#MaxGatewayTunnels for details.
 
+h3. New LSF dispatcher config items MaxRunTimeOverhead and MaxRunTimeDefault
+
+Now supports configuration parameter @Containers.LSF.MaxRunTimeDefault@ as the default value for @max_run_time@ for containers that do not specify a time limit (using CWL @ToolTimeLimit@).
+
+Now supports configuration parameter @Containers.LSF.MaxRunTimeOverhead@ so that when @scheduling_constraints.max_run_time@ or @MaxRunTimeDefault@ are non-zero, this adds time to account for crunch-run startup/shutdown overhead.
+
 h2(#2_7_1). v2.7.1 (2023-12-12)
 
 "previous: Upgrading to 2.7.0":#v2_7_0

commit d308b870d530b37c5e1f5ce4c79a2031c10963fc
Author: Tom Clegg <tom at curii.com>
Date:   Mon Mar 18 10:48:05 2024 -0400

    Merge branch '21449-lsf-maxruntime'
    
    closes #21449
    
    Arvados-DCO-1.1-Signed-off-by: Tom Clegg <tom at curii.com>

diff --git a/build/run-tests.sh b/build/run-tests.sh
index 28051318b2..3aa1d65a3d 100755
--- a/build/run-tests.sh
+++ b/build/run-tests.sh
@@ -1070,9 +1070,9 @@ install_deps() {
     # Install parts needed by test suites
     do_install env
     do_install cmd/arvados-server go
-    do_install sdk/cli
     do_install sdk/python pip "${VENV3DIR}/bin/"
     do_install sdk/ruby
+    do_install sdk/cli
     do_install services/api
     do_install services/keepproxy go
     do_install services/keep-web go
diff --git a/doc/install/crunch2-lsf/install-dispatch.html.textile.liquid b/doc/install/crunch2-lsf/install-dispatch.html.textile.liquid
index fc4393d0b6..6aeb11040c 100644
--- a/doc/install/crunch2-lsf/install-dispatch.html.textile.liquid
+++ b/doc/install/crunch2-lsf/install-dispatch.html.textile.liquid
@@ -75,6 +75,7 @@ Template variables starting with % will be substituted as follows:
 %M memory in MB
 %T tmp in MB
 %G number of GPU devices (@runtime_constraints.cuda.device_count@)
+%W maximum job run time in minutes, suitable for use with @-W@ or @-We@ flags (see MaxRunTimeOverhead MaxRunTimeDefault below)
 
 Use %% to express a literal %. The %%J in the default will be changed to %J, which is interpreted by @bsub@ itself.
 
@@ -83,7 +84,7 @@ For example:
 <notextile>
 <pre>    Containers:
       LSF:
-        <code class="userinput">BsubArgumentsList: <b>["-o", "/tmp/crunch-run.%%J.out", "-e", "/tmp/crunch-run.%%J.err", "-J", "%U", "-n", "%C", "-D", "%MMB", "-R", "rusage[mem=%MMB:tmp=%TMB] span[hosts=1]", "-R", "select[mem>=%MMB]", "-R", "select[tmp>=%TMB]", "-R", "select[ncpus>=%C]"]</b></code>
+        <code class="userinput">BsubArgumentsList: <b>["-o", "/tmp/crunch-run.%%J.out", "-e", "/tmp/crunch-run.%%J.err", "-J", "%U", "-n", "%C", "-D", "%MMB", "-R", "rusage[mem=%MMB:tmp=%TMB] span[hosts=1]", "-R", "select[mem>=%MMB]", "-R", "select[tmp>=%TMB]", "-R", "select[ncpus>=%C]", "-We", "%W"]</b></code>
 </pre>
 </notextile>
 
@@ -100,6 +101,14 @@ If the container requests access to GPUs (@runtime_constraints.cuda.device_count
 </pre>
 </notextile>
 
+h3(#MaxRunTimeOverhead). Containers.LSF.MaxRunTimeOverhead
+
+Extra time to add to each container's @scheduling_parameters.max_run_time@ value when substituting for @%W@ in @BsubArgumentsList@, to account for time spent setting up the container image, copying output files, etc.
+
+h3(#MaxRunTimeDefault). Containers.LSF.MaxRunTimeDefault
+
+Default @max_run_time@ value to use for containers that do not specify one in @scheduling_parameters.max_run_time at . If this is zero, and @BsubArgumentsList@ contains @"-W", "%W"@ or @"-We", "%W"@, those arguments will be dropped when submitting containers that do not specify @scheduling_parameters.max_run_time at .
+
 h3(#PollInterval). Containers.PollInterval
 
 arvados-dispatch-lsf polls the API server periodically for new containers to run.  The @PollInterval@ option controls how often this poll happens.  Set this to a string of numbers suffixed with one of the time units @s@, @m@, or @h at .  For example:
diff --git a/lib/config/config.default.yml b/lib/config/config.default.yml
index 8b434386ac..522a7ee4e0 100644
--- a/lib/config/config.default.yml
+++ b/lib/config/config.default.yml
@@ -1377,15 +1377,23 @@ Clusters:
         # %M memory in MB
         # %T tmp in MB
         # %G number of GPU devices (runtime_constraints.cuda.device_count)
+        # %W maximum run time in minutes (see MaxRunTimeOverhead and
+        #    MaxRunTimeDefault below)
         #
-        # Use %% to express a literal %. The %%J in the default will be changed
-        # to %J, which is interpreted by bsub itself.
+        # Use %% to express a literal %. For example, the %%J in the
+        # default argument list will be changed to %J, which is
+        # interpreted by bsub itself.
         #
         # Note that the default arguments cause LSF to write two files
         # in /tmp on the compute node each time an Arvados container
         # runs. Ensure you have something in place to delete old files
         # from /tmp, or adjust the "-o" and "-e" arguments accordingly.
-        BsubArgumentsList: ["-o", "/tmp/crunch-run.%%J.out", "-e", "/tmp/crunch-run.%%J.err", "-J", "%U", "-n", "%C", "-D", "%MMB", "-R", "rusage[mem=%MMB:tmp=%TMB] span[hosts=1]", "-R", "select[mem>=%MMB]", "-R", "select[tmp>=%TMB]", "-R", "select[ncpus>=%C]"]
+        #
+        # If ["-We", "%W"] or ["-W", "%W"] appear in this argument
+        # list, and MaxRunTimeDefault is not set (see below), both of
+        # those arguments will be dropped from the argument list when
+        # running a container that has no max_run_time value.
+        BsubArgumentsList: ["-o", "/tmp/crunch-run.%%J.out", "-e", "/tmp/crunch-run.%%J.err", "-J", "%U", "-n", "%C", "-D", "%MMB", "-R", "rusage[mem=%MMB:tmp=%TMB] span[hosts=1]", "-R", "select[mem>=%MMB]", "-R", "select[tmp>=%TMB]", "-R", "select[ncpus>=%C]", "-We", "%W"]
 
         # Arguments that will be appended to the bsub command line
         # when submitting Arvados containers as LSF jobs with
@@ -1400,6 +1408,19 @@ Clusters:
         # Arvados LSF dispatcher runs ("submission host").
         BsubSudoUser: "crunch"
 
+        # When passing the scheduling_constraints.max_run_time value
+        # to LSF via "%W", add this much time to account for
+        # crunch-run startup/shutdown overhead.
+        MaxRunTimeOverhead: 5m
+
+        # If non-zero, MaxRunTimeDefault is used as the default value
+        # for max_run_time for containers that do not specify a time
+        # limit.  MaxRunTimeOverhead will be added to this.
+        #
+        # Example:
+        # MaxRunTimeDefault: 2h
+        MaxRunTimeDefault: 0
+
       JobsAPI:
         # Enable the legacy 'jobs' API (crunch v1).  This value must be a string.
         #
diff --git a/lib/lsf/dispatch.go b/lib/lsf/dispatch.go
index d1408d23cb..897e5803f2 100644
--- a/lib/lsf/dispatch.go
+++ b/lib/lsf/dispatch.go
@@ -306,6 +306,15 @@ func (disp *dispatcher) bsubArgs(container arvados.Container) ([]string, error)
 		container.RuntimeConstraints.KeepCacheRAM+
 		int64(disp.Cluster.Containers.ReserveExtraRAM)) / 1048576))
 
+	maxruntime := time.Duration(container.SchedulingParameters.MaxRunTime) * time.Second
+	if maxruntime == 0 {
+		maxruntime = disp.Cluster.Containers.LSF.MaxRunTimeDefault.Duration()
+	}
+	if maxruntime > 0 {
+		maxruntime += disp.Cluster.Containers.LSF.MaxRunTimeOverhead.Duration()
+	}
+	maxrunminutes := int64(math.Ceil(float64(maxruntime.Seconds()) / 60))
+
 	repl := map[string]string{
 		"%%": "%",
 		"%C": fmt.Sprintf("%d", vcpus),
@@ -313,6 +322,7 @@ func (disp *dispatcher) bsubArgs(container arvados.Container) ([]string, error)
 		"%T": fmt.Sprintf("%d", tmp),
 		"%U": container.UUID,
 		"%G": fmt.Sprintf("%d", container.RuntimeConstraints.CUDA.DeviceCount),
+		"%W": fmt.Sprintf("%d", maxrunminutes),
 	}
 
 	re := regexp.MustCompile(`%.`)
@@ -321,7 +331,16 @@ func (disp *dispatcher) bsubArgs(container arvados.Container) ([]string, error)
 	if container.RuntimeConstraints.CUDA.DeviceCount > 0 {
 		argumentTemplate = append(argumentTemplate, disp.Cluster.Containers.LSF.BsubCUDAArguments...)
 	}
-	for _, a := range argumentTemplate {
+	for idx, a := range argumentTemplate {
+		if idx > 0 && (argumentTemplate[idx-1] == "-W" || argumentTemplate[idx-1] == "-We") && a == "%W" && maxrunminutes == 0 {
+			// LSF docs don't specify an argument to "-W"
+			// or "-We" that indicates "unknown", so
+			// instead we drop the "-W %W" part of the
+			// command line entirely when max runtime is
+			// unknown.
+			args = args[:len(args)-1]
+			continue
+		}
 		args = append(args, re.ReplaceAllStringFunc(a, func(s string) string {
 			subst := repl[s]
 			if len(subst) == 0 {
diff --git a/lib/lsf/dispatch_test.go b/lib/lsf/dispatch_test.go
index cd41071d2c..e1e0bcae31 100644
--- a/lib/lsf/dispatch_test.go
+++ b/lib/lsf/dispatch_test.go
@@ -34,6 +34,7 @@ type suite struct {
 	crTooBig      arvados.ContainerRequest
 	crPending     arvados.ContainerRequest
 	crCUDARequest arvados.ContainerRequest
+	crMaxRunTime  arvados.ContainerRequest
 }
 
 func (s *suite) TearDownTest(c *check.C) {
@@ -116,6 +117,25 @@ func (s *suite) SetUpTest(c *check.C) {
 	})
 	c.Assert(err, check.IsNil)
 
+	err = arvados.NewClientFromEnv().RequestAndDecode(&s.crMaxRunTime, "POST", "arvados/v1/container_requests", nil, map[string]interface{}{
+		"container_request": map[string]interface{}{
+			"runtime_constraints": arvados.RuntimeConstraints{
+				RAM:   1000000,
+				VCPUs: 1,
+			},
+			"scheduling_parameters": arvados.SchedulingParameters{
+				MaxRunTime: 124,
+			},
+			"container_image":     arvadostest.DockerImage112PDH,
+			"command":             []string{"sleep", "123"},
+			"mounts":              map[string]arvados.Mount{"/mnt/out": {Kind: "tmp", Capacity: 1000}},
+			"output_path":         "/mnt/out",
+			"state":               arvados.ContainerRequestStateCommitted,
+			"priority":            1,
+			"container_count_max": 1,
+		},
+	})
+	c.Assert(err, check.IsNil)
 }
 
 type lsfstub struct {
@@ -141,12 +161,7 @@ func (stub lsfstub) stubCommand(s *suite, c *check.C) func(prog string, args ...
 		}
 		switch prog {
 		case "bsub":
-			defaultArgs := s.disp.Cluster.Containers.LSF.BsubArgumentsList
-			if args[5] == s.crCUDARequest.ContainerUUID {
-				c.Assert(len(args), check.Equals, len(defaultArgs)+len(s.disp.Cluster.Containers.LSF.BsubCUDAArguments))
-			} else {
-				c.Assert(len(args), check.Equals, len(defaultArgs))
-			}
+			c.Assert(len(args) > 5, check.Equals, true)
 			// %%J must have been rewritten to %J
 			c.Check(args[1], check.Equals, "/tmp/crunch-run.%J.out")
 			args = args[4:]
@@ -204,6 +219,21 @@ func (stub lsfstub) stubCommand(s *suite, c *check.C) func(prog string, args ...
 				fakejobq[nextjobid] = args[1]
 				nextjobid++
 				mtx.Unlock()
+			case s.crMaxRunTime.ContainerUUID:
+				c.Check(args, check.DeepEquals, []string{
+					"-J", s.crMaxRunTime.ContainerUUID,
+					"-n", "1",
+					"-D", "257MB",
+					"-R", "rusage[mem=257MB:tmp=2304MB] span[hosts=1]",
+					"-R", "select[mem>=257MB]",
+					"-R", "select[tmp>=2304MB]",
+					"-R", "select[ncpus>=1]",
+					"-We", "8", // 124s + 5m overhead + roundup = 8m
+				})
+				mtx.Lock()
+				fakejobq[nextjobid] = args[1]
+				nextjobid++
+				mtx.Unlock()
 			default:
 				c.Errorf("unexpected uuid passed to bsub: args %q", args)
 				return exec.Command("false")
diff --git a/sdk/go/arvados/config.go b/sdk/go/arvados/config.go
index e39a1ff0aa..a0caea4f38 100644
--- a/sdk/go/arvados/config.go
+++ b/sdk/go/arvados/config.go
@@ -561,9 +561,11 @@ type ContainersConfig struct {
 		}
 	}
 	LSF struct {
-		BsubSudoUser      string
-		BsubArgumentsList []string
-		BsubCUDAArguments []string
+		BsubSudoUser       string
+		BsubArgumentsList  []string
+		BsubCUDAArguments  []string
+		MaxRunTimeOverhead Duration
+		MaxRunTimeDefault  Duration
 	}
 }
 
diff --git a/services/api/app/models/container_request.rb b/services/api/app/models/container_request.rb
index d72f00edc8..3a7dfdd81f 100644
--- a/services/api/app/models/container_request.rb
+++ b/services/api/app/models/container_request.rb
@@ -467,8 +467,9 @@ class ContainerRequest < ArvadosModel
 
   def validate_scheduling_parameters
     if self.state == Committed
-      if scheduling_parameters.include? 'partitions' and
-         (!scheduling_parameters['partitions'].is_a?(Array) ||
+      if scheduling_parameters.include?('partitions') and
+        !scheduling_parameters['partitions'].nil? and
+        (!scheduling_parameters['partitions'].is_a?(Array) ||
           scheduling_parameters['partitions'].reject{|x| !x.is_a?(String)}.size !=
             scheduling_parameters['partitions'].size)
             errors.add :scheduling_parameters, "partitions must be an array of strings"

-----------------------------------------------------------------------


hooks/post-receive
-- 




More information about the arvados-commits mailing list