X-Git-Url: https://git.arvados.org/arvados.git/blobdiff_plain/91db2d8fd32bc3f6c2a26ffc37f6591b1e5f380b..f159fab8f9d6bc4254192ce43432defd5bd400aa:/sdk/go/arvados/config.go diff --git a/sdk/go/arvados/config.go b/sdk/go/arvados/config.go index 537de2ac17..6edd18418b 100644 --- a/sdk/go/arvados/config.go +++ b/sdk/go/arvados/config.go @@ -1,22 +1,29 @@ +// Copyright (C) The Arvados Authors. All rights reserved. +// +// SPDX-License-Identifier: Apache-2.0 + package arvados import ( + "encoding/json" + "errors" "fmt" "os" - "strings" "git.curoverse.com/arvados.git/sdk/go/config" ) +const DefaultConfigFile = "/etc/arvados/config.yml" + type Config struct { Clusters map[string]Cluster } // GetConfig returns the current system config, loading it from -// /etc if needed. -func GetConfig() (*Config, error) { +// configFile if needed. +func GetConfig(configFile string) (*Config, error) { var cfg Config - err := config.LoadFile(&cfg, "/etc/arvados/config.yml") + err := config.LoadFile(&cfg, configFile) return &cfg, err } @@ -24,7 +31,9 @@ func GetConfig() (*Config, error) { // cluster, or the default/only configured cluster if clusterID is "". func (sc *Config) GetCluster(clusterID string) (*Cluster, error) { if clusterID == "" { - if len(sc.Clusters) != 1 { + if len(sc.Clusters) == 0 { + return nil, fmt.Errorf("no clusters configured") + } else if len(sc.Clusters) > 1 { return nil, fmt.Errorf("multiple clusters configured, cannot choose") } else { for id, cc := range sc.Clusters { @@ -42,46 +51,149 @@ func (sc *Config) GetCluster(clusterID string) (*Cluster, error) { } type Cluster struct { - ClusterID string `json:"-"` - ManagementToken string - SystemNodes map[string]SystemNode + ClusterID string `json:"-"` + ManagementToken string + NodeProfiles map[string]NodeProfile + InstanceTypes InstanceTypeMap + HTTPRequestTimeout Duration + RemoteClusters map[string]RemoteCluster + PostgreSQL PostgreSQL +} + +type PostgreSQL struct { + Connection PostgreSQLConnection + ConnectionPool int +} + +type PostgreSQLConnection map[string]string + +type RemoteCluster struct { + // API endpoint host or host:port; default is {id}.arvadosapi.com + Host string + // Perform a proxy request when a local client requests an + // object belonging to this remote. + Proxy bool + // Scheme, default "https". Can be set to "http" for testing. + Scheme string + // Disable TLS verify. Can be set to true for testing. + Insecure bool } -// GetThisSystemNodeConfig returns a SystemNode for the node we're -// running on right now. -func (cc *Cluster) GetThisSystemNode() (*SystemNode, error) { - hostname, err := os.Hostname() +type InstanceType struct { + Name string + ProviderType string + VCPUs int + RAM ByteSize + Scratch ByteSize + Price float64 + Preemptible bool +} + +type InstanceTypeMap map[string]InstanceType + +var errDuplicateInstanceTypeName = errors.New("duplicate instance type name") + +// UnmarshalJSON handles old config files that provide an array of +// instance types instead of a hash. +func (it *InstanceTypeMap) UnmarshalJSON(data []byte) error { + if len(data) > 0 && data[0] == '[' { + var arr []InstanceType + err := json.Unmarshal(data, &arr) + if err != nil { + return err + } + if len(arr) == 0 { + *it = nil + return nil + } + *it = make(map[string]InstanceType, len(arr)) + for _, t := range arr { + if _, ok := (*it)[t.Name]; ok { + return errDuplicateInstanceTypeName + } + (*it)[t.Name] = t + } + return nil + } + var hash map[string]InstanceType + err := json.Unmarshal(data, &hash) if err != nil { - return nil, err + return err } - return cc.GetSystemNode(hostname) + // Fill in Name field using hash key. + *it = InstanceTypeMap(hash) + for name, t := range *it { + t.Name = name + (*it)[name] = t + } + return nil } -// GetSystemNodeConfig returns a NodeConfig for the given node. An +// GetNodeProfile returns a NodeProfile for the given hostname. An // error is returned if the appropriate configuration can't be -// determined (e.g., this does not appear to be a system node). -func (cc *Cluster) GetSystemNode(node string) (*SystemNode, error) { - // Generally node is "a.b.ca", use the first of {"a.b.ca", - // "a.b", "a"} that has an entry in SystemNodes. - labels := strings.Split(node, ".") - for j := len(labels); j > 0; j-- { - hostpart := strings.Join(labels[:j], ".") - if cfg, ok := cc.SystemNodes[hostpart]; ok { - return &cfg, nil +// determined (e.g., this does not appear to be a system node). If +// node is empty, use the OS-reported hostname. +func (cc *Cluster) GetNodeProfile(node string) (*NodeProfile, error) { + if node == "" { + hostname, err := os.Hostname() + if err != nil { + return nil, err } + node = hostname + } + if cfg, ok := cc.NodeProfiles[node]; ok { + return &cfg, nil } // If node is not listed, but "*" gives a default system node // config, use the default config. - if cfg, ok := cc.SystemNodes["*"]; ok { + if cfg, ok := cc.NodeProfiles["*"]; ok { return &cfg, nil } return nil, fmt.Errorf("config does not provision host %q as a system node", node) } -type SystemNode struct { - Keepstore Keepstore +type NodeProfile struct { + Controller SystemServiceInstance `json:"arvados-controller"` + Health SystemServiceInstance `json:"arvados-health"` + Keepproxy SystemServiceInstance `json:"keepproxy"` + Keepstore SystemServiceInstance `json:"keepstore"` + Keepweb SystemServiceInstance `json:"keep-web"` + Nodemanager SystemServiceInstance `json:"arvados-node-manager"` + RailsAPI SystemServiceInstance `json:"arvados-api-server"` + Websocket SystemServiceInstance `json:"arvados-ws"` + Workbench SystemServiceInstance `json:"arvados-workbench"` +} + +type ServiceName string + +const ( + ServiceNameRailsAPI ServiceName = "arvados-api-server" + ServiceNameController ServiceName = "arvados-controller" + ServiceNameNodemanager ServiceName = "arvados-node-manager" + ServiceNameWorkbench ServiceName = "arvados-workbench" + ServiceNameWebsocket ServiceName = "arvados-ws" + ServiceNameKeepweb ServiceName = "keep-web" + ServiceNameKeepproxy ServiceName = "keepproxy" + ServiceNameKeepstore ServiceName = "keepstore" +) + +// ServicePorts returns the configured listening address (or "" if +// disabled) for each service on the node. +func (np *NodeProfile) ServicePorts() map[ServiceName]string { + return map[ServiceName]string{ + ServiceNameRailsAPI: np.RailsAPI.Listen, + ServiceNameController: np.Controller.Listen, + ServiceNameNodemanager: np.Nodemanager.Listen, + ServiceNameWorkbench: np.Workbench.Listen, + ServiceNameWebsocket: np.Websocket.Listen, + ServiceNameKeepweb: np.Keepweb.Listen, + ServiceNameKeepproxy: np.Keepproxy.Listen, + ServiceNameKeepstore: np.Keepstore.Listen, + } } -type Keepstore struct { - Listen string +type SystemServiceInstance struct { + Listen string + TLS bool + Insecure bool }