13581: Available types reported to the user when CR is not satisfiable.
authorLucas Di Pentima <ldipentima@veritasgenetics.com>
Thu, 7 Jun 2018 17:11:00 +0000 (14:11 -0300)
committerLucas Di Pentima <ldipentima@veritasgenetics.com>
Tue, 12 Jun 2018 17:21:09 +0000 (14:21 -0300)
Arvados-DCO-1.1-Signed-off-by: Lucas Di Pentima <ldipentima@veritasgenetics.com>

lib/dispatchcloud/node_size.go
lib/dispatchcloud/node_size_test.go
services/crunch-dispatch-slurm/crunch-dispatch-slurm.go
services/crunch-dispatch-slurm/crunch-dispatch-slurm_test.go

index 2ca405060390c65df2f961f7c7a83e5a278d0687..facbc70fcbe037b1339d634446bf27f370eb7987 100644 (file)
@@ -8,6 +8,7 @@ import (
        "errors"
        "log"
        "os/exec"
+       "sort"
        "strings"
        "time"
 
@@ -15,11 +16,17 @@ import (
 )
 
 var (
-       ErrConstraintsNotSatisfiable  = errors.New("constraints not satisfiable by any configured instance type")
        ErrInstanceTypesNotConfigured = errors.New("site configuration does not list any instance types")
        discountConfiguredRAMPercent  = 5
 )
 
+// ConstraintsNotSatisfiableError includes a list of available instance types
+// to be reported back to the user.
+type ConstraintsNotSatisfiableError struct {
+       error
+       AvailableTypes []arvados.InstanceType
+}
+
 // ChooseInstanceType returns the cheapest available
 // arvados.InstanceType big enough to run ctr.
 func ChooseInstanceType(cc *arvados.Cluster, ctr *arvados.Container) (best arvados.InstanceType, err error) {
@@ -40,7 +47,15 @@ func ChooseInstanceType(cc *arvados.Cluster, ctr *arvados.Container) (best arvad
        needRAM := ctr.RuntimeConstraints.RAM + ctr.RuntimeConstraints.KeepCacheRAM
        needRAM = (needRAM * 100) / int64(100-discountConfiguredRAMPercent)
 
-       err = ErrConstraintsNotSatisfiable
+       availableTypes := make([]arvados.InstanceType, len(cc.InstanceTypes))
+       copy(availableTypes, cc.InstanceTypes)
+       sort.Slice(availableTypes, func(a, b int) bool {
+               return availableTypes[a].Price < availableTypes[b].Price
+       })
+       err = ConstraintsNotSatisfiableError{
+               errors.New("constraints not satisfiable by any configured instance type"),
+               availableTypes,
+       }
        for _, it := range cc.InstanceTypes {
                switch {
                case err == nil && it.Price > best.Price:
index 0c02a0e3e1be45bfeb6b2371287a4ce664de1d98..31944cfa5f12f6c6d99c18cdc1ae577733faab86 100644 (file)
@@ -32,7 +32,8 @@ func (*NodeSizeSuite) TestChooseUnsatisfiable(c *check.C) {
                        {Price: 2.2, RAM: 2000000000, VCPUs: 4, Name: "small2"},
                        {Price: 4.4, RAM: 4000000000, VCPUs: 8, Name: "small4", Scratch: GiB},
                }}, ctr)
-               c.Check(err, check.Equals, ErrConstraintsNotSatisfiable)
+               err, ok := err.(ConstraintsNotSatisfiableError)
+               c.Check(ok, check.Equals, true)
        }
 
        for _, rc := range []arvados.RuntimeConstraints{
index 9e3baab95080d8e578792b8cf4ab1beff15d4425..8c5b220b4f0df6bb87a1650516a2fba0e7f591e1 100644 (file)
@@ -7,6 +7,7 @@ package main
 // Dispatcher service for Crunch that submits containers to the slurm queue.
 
 import (
+       "bytes"
        "context"
        "flag"
        "fmt"
@@ -274,8 +275,19 @@ func (disp *Dispatcher) runContainer(_ *dispatch.Dispatcher, ctr arvados.Contain
                log.Printf("Submitting container %s to slurm", ctr.UUID)
                if err := disp.submit(ctr, disp.CrunchRunCommand); err != nil {
                        var text string
-                       if err == dispatchcloud.ErrConstraintsNotSatisfiable {
-                               text = fmt.Sprintf("cannot run container %s: %s", ctr.UUID, err)
+                       if err, ok := err.(dispatchcloud.ConstraintsNotSatisfiableError); ok {
+                               var logBuf bytes.Buffer
+                               logBuf.WriteString(fmt.Sprintf("cannot run container %s: %s\n", ctr.UUID, err))
+                               if len(err.AvailableTypes) > 0 {
+                                       logBuf.WriteString("Available instance types:\n")
+                               }
+                               for _, t := range err.AvailableTypes {
+                                       logBuf.WriteString(fmt.Sprintf(
+                                               "Type %q: %d VCPUs, %d RAM, %d Scratch, %f Price\n",
+                                               t.Name, t.VCPUs, t.RAM, t.Scratch, t.Price,
+                                       ))
+                               }
+                               text = logBuf.String()
                                disp.UpdateState(ctr.UUID, dispatch.Cancelled)
                        } else {
                                text = fmt.Sprintf("Error submitting container %s to slurm: %s", ctr.UUID, err)
index 85617cf1154c2f1e32c6cf5edd6f20dd1538762a..b4033e78b00abee87e2fb7423281021be5233577 100644 (file)
@@ -391,16 +391,18 @@ func (s *StubbedSuite) TestSbatchInstanceTypeConstraint(c *C) {
                        types: []arvados.InstanceType{
                                {Name: "a1.tiny", Price: 0.02, RAM: 128000000, VCPUs: 1},
                        },
-                       err: dispatchcloud.ErrConstraintsNotSatisfiable,
+                       err: dispatchcloud.ConstraintsNotSatisfiableError{},
                },
        } {
                c.Logf("%#v", trial)
                s.disp.cluster = &arvados.Cluster{InstanceTypes: trial.types}
 
                args, err := s.disp.sbatchArgs(container)
-               c.Check(err, Equals, trial.err)
+               c.Check(err == nil, Equals, trial.err == nil)
                if trial.err == nil {
                        c.Check(args, DeepEquals, append([]string{"--job-name=123", "--nice=10000"}, trial.sbatchArgs...))
+               } else {
+                       c.Check(len(err.(dispatchcloud.ConstraintsNotSatisfiableError).AvailableTypes), Equals, len(trial.types))
                }
        }
 }