1 // Copyright (C) The Arvados Authors. All rights reserved.
3 // SPDX-License-Identifier: Apache-2.0
13 "git.curoverse.com/arvados.git/sdk/go/config"
16 const DefaultConfigFile = "/etc/arvados/config.yml"
19 Clusters map[string]Cluster
22 // GetConfig returns the current system config, loading it from
23 // configFile if needed.
24 func GetConfig(configFile string) (*Config, error) {
26 err := config.LoadFile(&cfg, configFile)
30 // GetCluster returns the cluster ID and config for the given
31 // cluster, or the default/only configured cluster if clusterID is "".
32 func (sc *Config) GetCluster(clusterID string) (*Cluster, error) {
34 if len(sc.Clusters) == 0 {
35 return nil, fmt.Errorf("no clusters configured")
36 } else if len(sc.Clusters) > 1 {
37 return nil, fmt.Errorf("multiple clusters configured, cannot choose")
39 for id, cc := range sc.Clusters {
45 if cc, ok := sc.Clusters[clusterID]; !ok {
46 return nil, fmt.Errorf("cluster %q is not configured", clusterID)
48 cc.ClusterID = clusterID
53 type RequestLimits struct {
54 MaxItemsPerResponse int
55 MultiClusterRequestConcurrency int
59 ClusterID string `json:"-"`
60 ManagementToken string
61 NodeProfiles map[string]NodeProfile
62 InstanceTypes InstanceTypeMap
65 HTTPRequestTimeout Duration
66 RemoteClusters map[string]RemoteCluster
68 RequestLimits RequestLimits
77 type PostgreSQL struct {
78 Connection PostgreSQLConnection
82 type PostgreSQLConnection map[string]string
84 type RemoteCluster struct {
85 // API endpoint host or host:port; default is {id}.arvadosapi.com
87 // Perform a proxy request when a local client requests an
88 // object belonging to this remote.
90 // Scheme, default "https". Can be set to "http" for testing.
92 // Disable TLS verify. Can be set to true for testing.
96 type InstanceType struct {
102 IncludedScratch ByteSize
103 AddedScratch ByteSize
108 type Dispatch struct {
109 // PEM encoded SSH key (RSA, DSA, or ECDSA) able to log in to
113 // Max time for workers to come up before abandoning stale
114 // locks from previous run
115 StaleLockTimeout Duration
117 // Interval between queue polls
118 PollInterval Duration
120 // Interval between probes to each worker
121 ProbeInterval Duration
123 // Maximum total worker probes per second
124 MaxProbesPerSecond int
126 // Time before repeating TERM/KILL signal
127 TimeoutSignal Duration
129 // Time to give up on TERM and move to KILL
132 // Time to give up on KILL and write off the worker
136 type CloudVMs struct {
137 // Shell command that exits zero IFF the VM is fully booted
138 // and ready to run containers, e.g., "mount | grep
140 BootProbeCommand string
142 // Listening port (name or number) of SSH servers on worker
146 SyncInterval Duration
148 // Maximum idle time before automatic shutdown
151 // Maximum booting time before automatic shutdown
152 TimeoutBooting Duration
154 // Maximum time with no successful probes before automatic shutdown
155 TimeoutProbe Duration
157 // Time after shutdown to retry shutdown
158 TimeoutShutdown Duration
163 DriverParameters json.RawMessage
166 type InstanceTypeMap map[string]InstanceType
168 var errDuplicateInstanceTypeName = errors.New("duplicate instance type name")
170 // UnmarshalJSON handles old config files that provide an array of
171 // instance types instead of a hash.
172 func (it *InstanceTypeMap) UnmarshalJSON(data []byte) error {
173 if len(data) > 0 && data[0] == '[' {
174 var arr []InstanceType
175 err := json.Unmarshal(data, &arr)
183 *it = make(map[string]InstanceType, len(arr))
184 for _, t := range arr {
185 if _, ok := (*it)[t.Name]; ok {
186 return errDuplicateInstanceTypeName
188 if t.ProviderType == "" {
189 t.ProviderType = t.Name
192 t.Scratch = t.IncludedScratch + t.AddedScratch
193 } else if t.AddedScratch == 0 {
194 t.AddedScratch = t.Scratch - t.IncludedScratch
195 } else if t.IncludedScratch == 0 {
196 t.IncludedScratch = t.Scratch - t.AddedScratch
199 if t.Scratch != (t.IncludedScratch + t.AddedScratch) {
200 return fmt.Errorf("%v: Scratch != (IncludedScratch + AddedScratch)", t.Name)
206 var hash map[string]InstanceType
207 err := json.Unmarshal(data, &hash)
211 // Fill in Name field (and ProviderType field, if not
212 // specified) using hash key.
213 *it = InstanceTypeMap(hash)
214 for name, t := range *it {
216 if t.ProviderType == "" {
217 t.ProviderType = name
224 // GetNodeProfile returns a NodeProfile for the given hostname. An
225 // error is returned if the appropriate configuration can't be
226 // determined (e.g., this does not appear to be a system node). If
227 // node is empty, use the OS-reported hostname.
228 func (cc *Cluster) GetNodeProfile(node string) (*NodeProfile, error) {
230 hostname, err := os.Hostname()
236 if cfg, ok := cc.NodeProfiles[node]; ok {
239 // If node is not listed, but "*" gives a default system node
240 // config, use the default config.
241 if cfg, ok := cc.NodeProfiles["*"]; ok {
244 return nil, fmt.Errorf("config does not provision host %q as a system node", node)
247 type NodeProfile struct {
248 Controller SystemServiceInstance `json:"arvados-controller"`
249 Health SystemServiceInstance `json:"arvados-health"`
250 Keepbalance SystemServiceInstance `json:"keep-balance"`
251 Keepproxy SystemServiceInstance `json:"keepproxy"`
252 Keepstore SystemServiceInstance `json:"keepstore"`
253 Keepweb SystemServiceInstance `json:"keep-web"`
254 Nodemanager SystemServiceInstance `json:"arvados-node-manager"`
255 DispatchCloud SystemServiceInstance `json:"arvados-dispatch-cloud"`
256 RailsAPI SystemServiceInstance `json:"arvados-api-server"`
257 Websocket SystemServiceInstance `json:"arvados-ws"`
258 Workbench SystemServiceInstance `json:"arvados-workbench"`
261 type ServiceName string
264 ServiceNameRailsAPI ServiceName = "arvados-api-server"
265 ServiceNameController ServiceName = "arvados-controller"
266 ServiceNameDispatchCloud ServiceName = "arvados-dispatch-cloud"
267 ServiceNameNodemanager ServiceName = "arvados-node-manager"
268 ServiceNameWorkbench ServiceName = "arvados-workbench"
269 ServiceNameWebsocket ServiceName = "arvados-ws"
270 ServiceNameKeepbalance ServiceName = "keep-balance"
271 ServiceNameKeepweb ServiceName = "keep-web"
272 ServiceNameKeepproxy ServiceName = "keepproxy"
273 ServiceNameKeepstore ServiceName = "keepstore"
276 // ServicePorts returns the configured listening address (or "" if
277 // disabled) for each service on the node.
278 func (np *NodeProfile) ServicePorts() map[ServiceName]string {
279 return map[ServiceName]string{
280 ServiceNameRailsAPI: np.RailsAPI.Listen,
281 ServiceNameController: np.Controller.Listen,
282 ServiceNameDispatchCloud: np.DispatchCloud.Listen,
283 ServiceNameNodemanager: np.Nodemanager.Listen,
284 ServiceNameWorkbench: np.Workbench.Listen,
285 ServiceNameWebsocket: np.Websocket.Listen,
286 ServiceNameKeepbalance: np.Keepbalance.Listen,
287 ServiceNameKeepweb: np.Keepweb.Listen,
288 ServiceNameKeepproxy: np.Keepproxy.Listen,
289 ServiceNameKeepstore: np.Keepstore.Listen,
293 func (h RequestLimits) GetMultiClusterRequestConcurrency() int {
294 if h.MultiClusterRequestConcurrency == 0 {
297 return h.MultiClusterRequestConcurrency
300 func (h RequestLimits) GetMaxItemsPerResponse() int {
301 if h.MaxItemsPerResponse == 0 {
304 return h.MaxItemsPerResponse
307 type SystemServiceInstance struct {