Merge branch '15370-loopback-dispatchcloud'
authorTom Clegg <tom@curii.com>
Thu, 2 Jun 2022 18:39:18 +0000 (14:39 -0400)
committerTom Clegg <tom@curii.com>
Thu, 2 Jun 2022 18:39:18 +0000 (14:39 -0400)
closes #15370

Arvados-DCO-1.1-Signed-off-by: Tom Clegg <tom@curii.com>

1  2 
lib/config/load.go
lib/config/load_test.go

diff --combined lib/config/load.go
index a547d1406940960ac9fd88564e0e7c6ee3ce63be,acc54cf92df10d3a16c2e8e811f6b46e1f4670ac..fbd01488a0be51c430c0c6efc9ef7862ebb88fe5
@@@ -16,6 -16,7 +16,7 @@@ import 
        "io/ioutil"
        "os"
        "regexp"
+       "runtime"
        "strconv"
        "strings"
        "time"
@@@ -25,6 -26,7 +26,7 @@@
        "github.com/imdario/mergo"
        "github.com/prometheus/client_golang/prometheus"
        "github.com/sirupsen/logrus"
+       "golang.org/x/sys/unix"
  )
  
  //go:embed config.default.yml
@@@ -297,7 -299,10 +299,10 @@@ func (ldr *Loader) Load() (*arvados.Con
                        ldr.loadOldKeepBalanceConfig,
                )
        }
-       loadFuncs = append(loadFuncs, ldr.setImplicitStorageClasses)
+       loadFuncs = append(loadFuncs,
+               ldr.setImplicitStorageClasses,
+               ldr.setLoopbackInstanceType,
+       )
        for _, f := range loadFuncs {
                err = f(&cfg)
                if err != nil {
@@@ -362,7 -367,7 +367,7 @@@ func (ldr *Loader) checkClusterID(label
        if emptyStringOk && clusterID == "" {
                return nil
        } else if !acceptableClusterIDRe.MatchString(clusterID) {
 -              return fmt.Errorf("%s: cluster ID should be 5 alphanumeric characters", label)
 +              return fmt.Errorf("%s: cluster ID should be 5 lowercase alphanumeric characters", label)
        }
        return nil
  }
@@@ -415,6 -420,67 +420,67 @@@ func (ldr *Loader) checkEnum(label, val
        return fmt.Errorf("%s: unacceptable value %q: must be one of %q", label, value, accepted)
  }
  
+ func (ldr *Loader) setLoopbackInstanceType(cfg *arvados.Config) error {
+       for id, cc := range cfg.Clusters {
+               if !cc.Containers.CloudVMs.Enable || cc.Containers.CloudVMs.Driver != "loopback" {
+                       continue
+               }
+               if len(cc.InstanceTypes) == 1 {
+                       continue
+               }
+               if len(cc.InstanceTypes) > 1 {
+                       return fmt.Errorf("Clusters.%s.InstanceTypes: cannot use multiple InstanceTypes with loopback driver", id)
+               }
+               // No InstanceTypes configured. Fill in implicit
+               // default.
+               hostram, err := getHostRAM()
+               if err != nil {
+                       return err
+               }
+               scratch, err := getFilesystemSize(os.TempDir())
+               if err != nil {
+                       return err
+               }
+               cc.InstanceTypes = arvados.InstanceTypeMap{"localhost": {
+                       Name:            "localhost",
+                       ProviderType:    "localhost",
+                       VCPUs:           runtime.NumCPU(),
+                       RAM:             hostram,
+                       Scratch:         scratch,
+                       IncludedScratch: scratch,
+               }}
+               cfg.Clusters[id] = cc
+       }
+       return nil
+ }
+ func getFilesystemSize(path string) (arvados.ByteSize, error) {
+       var stat unix.Statfs_t
+       err := unix.Statfs(path, &stat)
+       if err != nil {
+               return 0, err
+       }
+       return arvados.ByteSize(stat.Blocks * uint64(stat.Bsize)), nil
+ }
+ var reMemTotal = regexp.MustCompile(`(^|\n)MemTotal: *(\d+) kB\n`)
+ func getHostRAM() (arvados.ByteSize, error) {
+       buf, err := os.ReadFile("/proc/meminfo")
+       if err != nil {
+               return 0, err
+       }
+       m := reMemTotal.FindSubmatch(buf)
+       if m == nil {
+               return 0, errors.New("error parsing /proc/meminfo: no MemTotal")
+       }
+       kb, err := strconv.ParseInt(string(m[2]), 10, 64)
+       if err != nil {
+               return 0, fmt.Errorf("error parsing /proc/meminfo: %q: %w", m[2], err)
+       }
+       return arvados.ByteSize(kb) * 1024, nil
+ }
  func (ldr *Loader) setImplicitStorageClasses(cfg *arvados.Config) error {
  cluster:
        for id, cc := range cfg.Clusters {
diff --combined lib/config/load_test.go
index fd2921bd4506cd5eab697f08d87e4782a573f179,fb9792632c5193576d0d9f52d65c09208196928e..a19400c191df1db7a36e9e1ad8d242a0cbb301cc
@@@ -13,6 -13,7 +13,7 @@@ import 
        "os/exec"
        "reflect"
        "regexp"
+       "runtime"
        "strings"
        "testing"
        "time"
@@@ -406,7 -407,7 +407,7 @@@ Clusters
                if v != nil {
                        c.Logf("%#v", v.Clusters)
                }
 -              c.Check(err, check.ErrorMatches, `.*cluster ID should be 5 alphanumeric characters.*`)
 +              c.Check(err, check.ErrorMatches, `.*cluster ID should be 5 lowercase alphanumeric characters.*`)
        }
  }
  
@@@ -601,6 -602,55 +602,55 @@@ func (s *LoadSuite) TestListKeys(c *che
        }
  }
  
+ func (s *LoadSuite) TestLoopbackInstanceTypes(c *check.C) {
+       ldr := testLoader(c, `
+ Clusters:
+  z1111:
+   Containers:
+    CloudVMs:
+     Enable: true
+     Driver: loopback
+   InstanceTypes:
+    a: {}
+    b: {}
+ `, nil)
+       cfg, err := ldr.Load()
+       c.Check(err, check.ErrorMatches, `Clusters\.z1111\.InstanceTypes: cannot use multiple InstanceTypes with loopback driver`)
+       ldr = testLoader(c, `
+ Clusters:
+  z1111:
+   Containers:
+    CloudVMs:
+     Enable: true
+     Driver: loopback
+ `, nil)
+       cfg, err = ldr.Load()
+       c.Assert(err, check.IsNil)
+       cc, err := cfg.GetCluster("")
+       c.Assert(err, check.IsNil)
+       c.Check(cc.InstanceTypes, check.HasLen, 1)
+       c.Check(cc.InstanceTypes["localhost"].VCPUs, check.Equals, runtime.NumCPU())
+       ldr = testLoader(c, `
+ Clusters:
+  z1111:
+   Containers:
+    CloudVMs:
+     Enable: true
+     Driver: loopback
+   InstanceTypes:
+    a:
+     VCPUs: 9
+ `, nil)
+       cfg, err = ldr.Load()
+       c.Assert(err, check.IsNil)
+       cc, err = cfg.GetCluster("")
+       c.Assert(err, check.IsNil)
+       c.Check(cc.InstanceTypes, check.HasLen, 1)
+       c.Check(cc.InstanceTypes["a"].VCPUs, check.Equals, 9)
+ }
  func (s *LoadSuite) TestWarnUnusedLocalKeep(c *check.C) {
        var logbuf bytes.Buffer
        _, err := testLoader(c, `
@@@ -849,3 -899,16 +899,16 @@@ arvados_config_source_timestamp_seconds
  `)
        }
  }
+ func (s *LoadSuite) TestGetHostRAM(c *check.C) {
+       hostram, err := getHostRAM()
+       c.Check(err, check.IsNil)
+       c.Logf("getHostRAM() == %v", hostram)
+ }
+ func (s *LoadSuite) TestGetFilesystemSize(c *check.C) {
+       path := c.MkDir()
+       size, err := getFilesystemSize(path)
+       c.Check(err, check.IsNil)
+       c.Logf("getFilesystemSize(%q) == %v", path, size)
+ }