X-Git-Url: https://git.arvados.org/arvados.git/blobdiff_plain/36cfafd6e7eae2784c22aefdd9df26783412d42a..7f0f12c40238f3eb12a51877a755cf22357e0767:/lib/config/load.go diff --git a/lib/config/load.go b/lib/config/load.go index dba7997870..93c175062e 100644 --- a/lib/config/load.go +++ b/lib/config/load.go @@ -16,15 +16,18 @@ import ( "io/ioutil" "os" "regexp" + "runtime" "strconv" "strings" "time" + "dario.cat/mergo" "git.arvados.org/arvados.git/sdk/go/arvados" "github.com/ghodss/yaml" - "github.com/imdario/mergo" "github.com/prometheus/client_golang/prometheus" "github.com/sirupsen/logrus" + "golang.org/x/crypto/ssh" + "golang.org/x/sys/unix" ) //go:embed config.default.yml @@ -45,7 +48,6 @@ type Loader struct { CrunchDispatchSlurmPath string WebsocketPath string KeepproxyPath string - GitHttpdPath string KeepBalancePath string configdata []byte @@ -85,7 +87,6 @@ func (ldr *Loader) SetupFlags(flagset *flag.FlagSet) { flagset.StringVar(&ldr.CrunchDispatchSlurmPath, "legacy-crunch-dispatch-slurm-config", defaultCrunchDispatchSlurmConfigPath, "Legacy crunch-dispatch-slurm configuration `file`") flagset.StringVar(&ldr.WebsocketPath, "legacy-ws-config", defaultWebsocketConfigPath, "Legacy arvados-ws configuration `file`") flagset.StringVar(&ldr.KeepproxyPath, "legacy-keepproxy-config", defaultKeepproxyConfigPath, "Legacy keepproxy configuration `file`") - flagset.StringVar(&ldr.GitHttpdPath, "legacy-git-httpd-config", defaultGitHttpdConfigPath, "Legacy arvados-git-httpd configuration `file`") flagset.StringVar(&ldr.KeepBalancePath, "legacy-keepbalance-config", defaultKeepBalanceConfigPath, "Legacy keep-balance configuration `file`") flagset.BoolVar(&ldr.SkipLegacy, "skip-legacy", false, "Don't load legacy config files") } @@ -165,9 +166,6 @@ func (ldr *Loader) MungeLegacyConfigArgs(lgr logrus.FieldLogger, args []string, if legacyConfigArg != "-legacy-keepproxy-config" { ldr.KeepproxyPath = "" } - if legacyConfigArg != "-legacy-git-httpd-config" { - ldr.GitHttpdPath = "" - } if legacyConfigArg != "-legacy-keepbalance-config" { ldr.KeepBalancePath = "" } @@ -293,11 +291,13 @@ func (ldr *Loader) Load() (*arvados.Config, error) { ldr.loadOldCrunchDispatchSlurmConfig, ldr.loadOldWebsocketConfig, ldr.loadOldKeepproxyConfig, - ldr.loadOldGitHttpdConfig, 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 +362,7 @@ func (ldr *Loader) checkClusterID(label, clusterID string, emptyStringOk bool) e 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 +415,68 @@ func (ldr *Loader) checkEnum(label, value string, accepted ...string) error { 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, + Price: 1.0, + }} + 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 { @@ -623,3 +685,17 @@ func (ldr *Loader) RegisterMetrics(reg *prometheus.Registry) { vec.WithLabelValues(hash).Set(float64(ldr.loadTimestamp.UnixNano()) / 1e9) reg.MustRegister(vec) } + +// Load an SSH private key from the given confvalue, which is either +// the literal key or an absolute path to a file containing the key. +func LoadSSHKey(confvalue string) (ssh.Signer, error) { + if fnm := strings.TrimPrefix(confvalue, "file://"); fnm != confvalue && strings.HasPrefix(fnm, "/") { + keydata, err := os.ReadFile(fnm) + if err != nil { + return nil, err + } + return ssh.ParsePrivateKey(keydata) + } else { + return ssh.ParsePrivateKey([]byte(confvalue)) + } +}