1 // Copyright (C) The Arvados Authors. All rights reserved.
3 // SPDX-License-Identifier: Apache-2.0
14 "git.curoverse.com/arvados.git/sdk/go/config"
17 const DefaultConfigFile = "/etc/arvados/config.yml"
20 Clusters map[string]Cluster
23 // GetConfig returns the current system config, loading it from
24 // configFile if needed.
25 func GetConfig(configFile string) (*Config, error) {
27 err := config.LoadFile(&cfg, configFile)
31 // GetCluster returns the cluster ID and config for the given
32 // cluster, or the default/only configured cluster if clusterID is "".
33 func (sc *Config) GetCluster(clusterID string) (*Cluster, error) {
35 if len(sc.Clusters) == 0 {
36 return nil, fmt.Errorf("no clusters configured")
37 } else if len(sc.Clusters) > 1 {
38 return nil, fmt.Errorf("multiple clusters configured, cannot choose")
40 for id, cc := range sc.Clusters {
46 if cc, ok := sc.Clusters[clusterID]; !ok {
47 return nil, fmt.Errorf("cluster %q is not configured", clusterID)
49 cc.ClusterID = clusterID
54 type RequestLimits struct {
55 MaxItemsPerResponse int
56 MultiClusterRequestConcurrency int
60 ClusterID string `json:"-"`
61 ManagementToken string
62 SystemRootToken string
64 NodeProfiles map[string]NodeProfile
65 InstanceTypes InstanceTypeMap
68 HTTPRequestTimeout Duration
69 RemoteClusters map[string]RemoteCluster
71 RequestLimits RequestLimits
76 type Services struct {
91 InternalURLs map[URL]ServiceInstance
95 // URL is a url.URL that is also usable as a JSON key/value.
98 // UnmarshalText implements encoding.TextUnmarshaler so URL can be
99 // used as a JSON key/value.
100 func (su *URL) UnmarshalText(text []byte) error {
101 u, err := url.Parse(string(text))
108 func (su URL) MarshalText() ([]byte, error) {
109 return []byte(fmt.Sprintf("%s", (*url.URL)(&su).String())), nil
112 type ServiceInstance struct{}
114 type Logging struct {
119 type PostgreSQL struct {
120 Connection PostgreSQLConnection
124 type PostgreSQLConnection map[string]string
126 type RemoteCluster struct {
127 // API endpoint host or host:port; default is {id}.arvadosapi.com
129 // Perform a proxy request when a local client requests an
130 // object belonging to this remote.
132 // Scheme, default "https". Can be set to "http" for testing.
134 // Disable TLS verify. Can be set to true for testing.
138 type InstanceType struct {
144 IncludedScratch ByteSize
145 AddedScratch ByteSize
150 type Dispatch struct {
151 // PEM encoded SSH key (RSA, DSA, or ECDSA) able to log in to
155 // Max time for workers to come up before abandoning stale
156 // locks from previous run
157 StaleLockTimeout Duration
159 // Interval between queue polls
160 PollInterval Duration
162 // Interval between probes to each worker
163 ProbeInterval Duration
165 // Maximum total worker probes per second
166 MaxProbesPerSecond int
168 // Time before repeating SIGTERM when killing a container
169 TimeoutSignal Duration
171 // Time to give up on SIGTERM and write off the worker
175 type CloudVMs struct {
176 // Shell command that exits zero IFF the VM is fully booted
177 // and ready to run containers, e.g., "mount | grep
179 BootProbeCommand string
181 // Listening port (name or number) of SSH servers on worker
185 SyncInterval Duration
187 // Maximum idle time before automatic shutdown
190 // Maximum booting time before automatic shutdown
191 TimeoutBooting Duration
193 // Maximum time with no successful probes before automatic shutdown
194 TimeoutProbe Duration
196 // Time after shutdown to retry shutdown
197 TimeoutShutdown Duration
199 // Maximum create/destroy-instance operations per second
200 MaxCloudOpsPerSecond int
205 DriverParameters json.RawMessage
208 type InstanceTypeMap map[string]InstanceType
210 var errDuplicateInstanceTypeName = errors.New("duplicate instance type name")
212 // UnmarshalJSON handles old config files that provide an array of
213 // instance types instead of a hash.
214 func (it *InstanceTypeMap) UnmarshalJSON(data []byte) error {
215 if len(data) > 0 && data[0] == '[' {
216 var arr []InstanceType
217 err := json.Unmarshal(data, &arr)
225 *it = make(map[string]InstanceType, len(arr))
226 for _, t := range arr {
227 if _, ok := (*it)[t.Name]; ok {
228 return errDuplicateInstanceTypeName
230 if t.ProviderType == "" {
231 t.ProviderType = t.Name
234 t.Scratch = t.IncludedScratch + t.AddedScratch
235 } else if t.AddedScratch == 0 {
236 t.AddedScratch = t.Scratch - t.IncludedScratch
237 } else if t.IncludedScratch == 0 {
238 t.IncludedScratch = t.Scratch - t.AddedScratch
241 if t.Scratch != (t.IncludedScratch + t.AddedScratch) {
242 return fmt.Errorf("%v: Scratch != (IncludedScratch + AddedScratch)", t.Name)
248 var hash map[string]InstanceType
249 err := json.Unmarshal(data, &hash)
253 // Fill in Name field (and ProviderType field, if not
254 // specified) using hash key.
255 *it = InstanceTypeMap(hash)
256 for name, t := range *it {
258 if t.ProviderType == "" {
259 t.ProviderType = name
266 // GetNodeProfile returns a NodeProfile for the given hostname. An
267 // error is returned if the appropriate configuration can't be
268 // determined (e.g., this does not appear to be a system node). If
269 // node is empty, use the OS-reported hostname.
270 func (cc *Cluster) GetNodeProfile(node string) (*NodeProfile, error) {
272 hostname, err := os.Hostname()
278 if cfg, ok := cc.NodeProfiles[node]; ok {
281 // If node is not listed, but "*" gives a default system node
282 // config, use the default config.
283 if cfg, ok := cc.NodeProfiles["*"]; ok {
286 return nil, fmt.Errorf("config does not provision host %q as a system node", node)
289 type NodeProfile struct {
290 Controller SystemServiceInstance `json:"arvados-controller"`
291 Health SystemServiceInstance `json:"arvados-health"`
292 Keepbalance SystemServiceInstance `json:"keep-balance"`
293 Keepproxy SystemServiceInstance `json:"keepproxy"`
294 Keepstore SystemServiceInstance `json:"keepstore"`
295 Keepweb SystemServiceInstance `json:"keep-web"`
296 Nodemanager SystemServiceInstance `json:"arvados-node-manager"`
297 DispatchCloud SystemServiceInstance `json:"arvados-dispatch-cloud"`
298 RailsAPI SystemServiceInstance `json:"arvados-api-server"`
299 Websocket SystemServiceInstance `json:"arvados-ws"`
300 Workbench SystemServiceInstance `json:"arvados-workbench"`
303 type ServiceName string
306 ServiceNameRailsAPI ServiceName = "arvados-api-server"
307 ServiceNameController ServiceName = "arvados-controller"
308 ServiceNameDispatchCloud ServiceName = "arvados-dispatch-cloud"
309 ServiceNameNodemanager ServiceName = "arvados-node-manager"
310 ServiceNameWorkbench ServiceName = "arvados-workbench"
311 ServiceNameWebsocket ServiceName = "arvados-ws"
312 ServiceNameKeepbalance ServiceName = "keep-balance"
313 ServiceNameKeepweb ServiceName = "keep-web"
314 ServiceNameKeepproxy ServiceName = "keepproxy"
315 ServiceNameKeepstore ServiceName = "keepstore"
318 // ServicePorts returns the configured listening address (or "" if
319 // disabled) for each service on the node.
320 func (np *NodeProfile) ServicePorts() map[ServiceName]string {
321 return map[ServiceName]string{
322 ServiceNameRailsAPI: np.RailsAPI.Listen,
323 ServiceNameController: np.Controller.Listen,
324 ServiceNameDispatchCloud: np.DispatchCloud.Listen,
325 ServiceNameNodemanager: np.Nodemanager.Listen,
326 ServiceNameWorkbench: np.Workbench.Listen,
327 ServiceNameWebsocket: np.Websocket.Listen,
328 ServiceNameKeepbalance: np.Keepbalance.Listen,
329 ServiceNameKeepweb: np.Keepweb.Listen,
330 ServiceNameKeepproxy: np.Keepproxy.Listen,
331 ServiceNameKeepstore: np.Keepstore.Listen,
335 func (h RequestLimits) GetMultiClusterRequestConcurrency() int {
336 if h.MultiClusterRequestConcurrency == 0 {
339 return h.MultiClusterRequestConcurrency
342 func (h RequestLimits) GetMaxItemsPerResponse() int {
343 if h.MaxItemsPerResponse == 0 {
346 return h.MaxItemsPerResponse
349 type SystemServiceInstance struct {