Fix map write when only RLock held.
[arvados.git] / sdk / go / arvados / config.go
1 // Copyright (C) The Arvados Authors. All rights reserved.
2 //
3 // SPDX-License-Identifier: Apache-2.0
4
5 package arvados
6
7 import (
8         "encoding/json"
9         "errors"
10         "fmt"
11         "net/url"
12         "os"
13
14         "git.arvados.org/arvados.git/sdk/go/config"
15 )
16
17 var DefaultConfigFile = func() string {
18         if path := os.Getenv("ARVADOS_CONFIG"); path != "" {
19                 return path
20         }
21         return "/etc/arvados/config.yml"
22 }()
23
24 type Config struct {
25         Clusters         map[string]Cluster
26         AutoReloadConfig bool
27 }
28
29 // GetConfig returns the current system config, loading it from
30 // configFile if needed.
31 func GetConfig(configFile string) (*Config, error) {
32         var cfg Config
33         err := config.LoadFile(&cfg, configFile)
34         return &cfg, err
35 }
36
37 // GetCluster returns the cluster ID and config for the given
38 // cluster, or the default/only configured cluster if clusterID is "".
39 func (sc *Config) GetCluster(clusterID string) (*Cluster, error) {
40         if clusterID == "" {
41                 if len(sc.Clusters) == 0 {
42                         return nil, fmt.Errorf("no clusters configured")
43                 } else if len(sc.Clusters) > 1 {
44                         return nil, fmt.Errorf("multiple clusters configured, cannot choose")
45                 } else {
46                         for id, cc := range sc.Clusters {
47                                 cc.ClusterID = id
48                                 return &cc, nil
49                         }
50                 }
51         }
52         cc, ok := sc.Clusters[clusterID]
53         if !ok {
54                 return nil, fmt.Errorf("cluster %q is not configured", clusterID)
55         }
56         cc.ClusterID = clusterID
57         return &cc, nil
58 }
59
60 type WebDAVCacheConfig struct {
61         TTL                  Duration
62         UUIDTTL              Duration
63         MaxBlockEntries      int
64         MaxCollectionEntries int
65         MaxCollectionBytes   int64
66         MaxPermissionEntries int
67         MaxUUIDEntries       int
68         MaxSessions          int
69 }
70
71 type UploadDownloadPermission struct {
72         Upload   bool
73         Download bool
74 }
75
76 type UploadDownloadRolePermissions struct {
77         User  UploadDownloadPermission
78         Admin UploadDownloadPermission
79 }
80
81 type Cluster struct {
82         ClusterID       string `json:"-"`
83         ManagementToken string
84         SystemRootToken string
85         Services        Services
86         InstanceTypes   InstanceTypeMap
87         Containers      ContainersConfig
88         RemoteClusters  map[string]RemoteCluster
89         PostgreSQL      PostgreSQL
90
91         API struct {
92                 AsyncPermissionsUpdateInterval Duration
93                 DisabledAPIs                   StringSet
94                 MaxIndexDatabaseRead           int
95                 MaxItemsPerResponse            int
96                 MaxConcurrentRequests          int
97                 MaxKeepBlobBuffers             int
98                 MaxRequestAmplification        int
99                 MaxRequestSize                 int
100                 MaxTokenLifetime               Duration
101                 RequestTimeout                 Duration
102                 SendTimeout                    Duration
103                 WebsocketClientEventQueue      int
104                 WebsocketServerEventQueue      int
105                 KeepServiceRequestTimeout      Duration
106         }
107         AuditLogs struct {
108                 MaxAge             Duration
109                 MaxDeleteBatch     int
110                 UnloggedAttributes StringSet
111         }
112         Collections struct {
113                 BlobSigning              bool
114                 BlobSigningKey           string
115                 BlobSigningTTL           Duration
116                 BlobTrash                bool
117                 BlobTrashLifetime        Duration
118                 BlobTrashCheckInterval   Duration
119                 BlobTrashConcurrency     int
120                 BlobDeleteConcurrency    int
121                 BlobReplicateConcurrency int
122                 CollectionVersioning     bool
123                 DefaultTrashLifetime     Duration
124                 DefaultReplication       int
125                 ManagedProperties        map[string]struct {
126                         Value     interface{}
127                         Function  string
128                         Protected bool
129                 }
130                 PreserveVersionIfIdle        Duration
131                 TrashSweepInterval           Duration
132                 TrustAllContent              bool
133                 ForwardSlashNameSubstitution string
134                 S3FolderObjects              bool
135
136                 BlobMissingReport        string
137                 BalancePeriod            Duration
138                 BalanceCollectionBatch   int
139                 BalanceCollectionBuffers int
140                 BalanceTimeout           Duration
141
142                 WebDAVCache WebDAVCacheConfig
143
144                 KeepproxyPermission UploadDownloadRolePermissions
145                 WebDAVPermission    UploadDownloadRolePermissions
146                 WebDAVLogEvents     bool
147         }
148         Git struct {
149                 GitCommand   string
150                 GitoliteHome string
151                 Repositories string
152         }
153         Login struct {
154                 LDAP struct {
155                         Enable             bool
156                         URL                URL
157                         StartTLS           bool
158                         InsecureTLS        bool
159                         StripDomain        string
160                         AppendDomain       string
161                         SearchAttribute    string
162                         SearchBindUser     string
163                         SearchBindPassword string
164                         SearchBase         string
165                         SearchFilters      string
166                         EmailAttribute     string
167                         UsernameAttribute  string
168                 }
169                 Google struct {
170                         Enable                          bool
171                         ClientID                        string
172                         ClientSecret                    string
173                         AlternateEmailAddresses         bool
174                         AuthenticationRequestParameters map[string]string
175                 }
176                 OpenIDConnect struct {
177                         Enable                          bool
178                         Issuer                          string
179                         ClientID                        string
180                         ClientSecret                    string
181                         EmailClaim                      string
182                         EmailVerifiedClaim              string
183                         UsernameClaim                   string
184                         AcceptAccessToken               bool
185                         AcceptAccessTokenScope          string
186                         AuthenticationRequestParameters map[string]string
187                 }
188                 PAM struct {
189                         Enable             bool
190                         Service            string
191                         DefaultEmailDomain string
192                 }
193                 Test struct {
194                         Enable bool
195                         Users  map[string]TestUser
196                 }
197                 LoginCluster       string
198                 RemoteTokenRefresh Duration
199                 TokenLifetime      Duration
200                 TrustedClients     map[string]struct{}
201                 IssueTrustedTokens bool
202         }
203         Mail struct {
204                 MailchimpAPIKey                string
205                 MailchimpListID                string
206                 SendUserSetupNotificationEmail bool
207                 IssueReporterEmailFrom         string
208                 IssueReporterEmailTo           string
209                 SupportEmailAddress            string
210                 EmailFrom                      string
211         }
212         SystemLogs struct {
213                 LogLevel                string
214                 Format                  string
215                 MaxRequestLogParamsSize int
216         }
217         TLS struct {
218                 Certificate string
219                 Key         string
220                 Insecure    bool
221         }
222         Users struct {
223                 AnonymousUserToken                    string
224                 AdminNotifierEmailFrom                string
225                 AutoAdminFirstUser                    bool
226                 AutoAdminUserWithEmail                string
227                 AutoSetupNewUsers                     bool
228                 AutoSetupNewUsersWithRepository       bool
229                 AutoSetupNewUsersWithVmUUID           string
230                 AutoSetupUsernameBlacklist            StringSet
231                 EmailSubjectPrefix                    string
232                 NewInactiveUserNotificationRecipients StringSet
233                 NewUserNotificationRecipients         StringSet
234                 NewUsersAreActive                     bool
235                 UserNotifierEmailFrom                 string
236                 UserProfileNotificationAddress        string
237                 PreferDomainForUsername               string
238                 UserSetupMailText                     string
239         }
240         Volumes   map[string]Volume
241         Workbench struct {
242                 ActivationContactLink            string
243                 APIClientConnectTimeout          Duration
244                 APIClientReceiveTimeout          Duration
245                 APIResponseCompression           bool
246                 ApplicationMimetypesWithViewIcon StringSet
247                 ArvadosDocsite                   string
248                 ArvadosPublicDataDocURL          string
249                 DefaultOpenIdPrefix              string
250                 EnableGettingStartedPopup        bool
251                 EnablePublicProjectsPage         bool
252                 FileViewersConfigURL             string
253                 LogViewerMaxBytes                ByteSize
254                 MultiSiteSearch                  string
255                 ProfilingEnabled                 bool
256                 Repositories                     bool
257                 RepositoryCache                  string
258                 RunningJobLogRecordsToFetch      int
259                 SecretKeyBase                    string
260                 ShowRecentCollectionsOnDashboard bool
261                 ShowUserAgreementInline          bool
262                 ShowUserNotifications            bool
263                 SiteName                         string
264                 Theme                            string
265                 UserProfileFormFields            map[string]struct {
266                         Type                 string
267                         FormFieldTitle       string
268                         FormFieldDescription string
269                         Required             bool
270                         Position             int
271                         Options              map[string]struct{}
272                 }
273                 UserProfileFormMessage string
274                 VocabularyURL          string
275                 WelcomePageHTML        string
276                 InactivePageHTML       string
277                 SSHHelpPageHTML        string
278                 SSHHelpHostSuffix      string
279                 IdleTimeout            Duration
280         }
281 }
282
283 type Volume struct {
284         AccessViaHosts   map[URL]VolumeAccess
285         ReadOnly         bool
286         Replication      int
287         StorageClasses   map[string]bool
288         Driver           string
289         DriverParameters json.RawMessage
290 }
291
292 type S3VolumeDriverParameters struct {
293         IAMRole            string
294         AccessKeyID        string
295         SecretAccessKey    string
296         Endpoint           string
297         Region             string
298         Bucket             string
299         LocationConstraint bool
300         V2Signature        bool
301         UseAWSS3v2Driver   bool
302         IndexPageSize      int
303         ConnectTimeout     Duration
304         ReadTimeout        Duration
305         RaceWindow         Duration
306         UnsafeDelete       bool
307 }
308
309 type AzureVolumeDriverParameters struct {
310         StorageAccountName   string
311         StorageAccountKey    string
312         StorageBaseURL       string
313         ContainerName        string
314         RequestTimeout       Duration
315         ListBlobsRetryDelay  Duration
316         ListBlobsMaxAttempts int
317 }
318
319 type DirectoryVolumeDriverParameters struct {
320         Root      string
321         Serialize bool
322 }
323
324 type VolumeAccess struct {
325         ReadOnly bool
326 }
327
328 type Services struct {
329         Composer       Service
330         Controller     Service
331         DispatchCloud  Service
332         DispatchLSF    Service
333         GitHTTP        Service
334         GitSSH         Service
335         Health         Service
336         Keepbalance    Service
337         Keepproxy      Service
338         Keepstore      Service
339         RailsAPI       Service
340         WebDAVDownload Service
341         WebDAV         Service
342         WebShell       Service
343         Websocket      Service
344         Workbench1     Service
345         Workbench2     Service
346 }
347
348 type Service struct {
349         InternalURLs map[URL]ServiceInstance
350         ExternalURL  URL
351 }
352
353 type TestUser struct {
354         Email    string
355         Password string
356 }
357
358 // URL is a url.URL that is also usable as a JSON key/value.
359 type URL url.URL
360
361 // UnmarshalText implements encoding.TextUnmarshaler so URL can be
362 // used as a JSON key/value.
363 func (su *URL) UnmarshalText(text []byte) error {
364         u, err := url.Parse(string(text))
365         if err == nil {
366                 *su = URL(*u)
367                 if su.Path == "" && su.Host != "" {
368                         // http://example really means http://example/
369                         su.Path = "/"
370                 }
371         }
372         return err
373 }
374
375 func (su URL) MarshalText() ([]byte, error) {
376         return []byte(fmt.Sprintf("%s", (*url.URL)(&su).String())), nil
377 }
378
379 func (su URL) String() string {
380         return (*url.URL)(&su).String()
381 }
382
383 type ServiceInstance struct {
384         Rendezvous string `json:",omitempty"`
385 }
386
387 type PostgreSQL struct {
388         Connection     PostgreSQLConnection
389         ConnectionPool int
390 }
391
392 type PostgreSQLConnection map[string]string
393
394 type RemoteCluster struct {
395         Host          string
396         Proxy         bool
397         Scheme        string
398         Insecure      bool
399         ActivateUsers bool
400 }
401
402 type InstanceType struct {
403         Name            string
404         ProviderType    string
405         VCPUs           int
406         RAM             ByteSize
407         Scratch         ByteSize
408         IncludedScratch ByteSize
409         AddedScratch    ByteSize
410         Price           float64
411         Preemptible     bool
412 }
413
414 type ContainersConfig struct {
415         CloudVMs                    CloudVMsConfig
416         CrunchRunCommand            string
417         CrunchRunArgumentsList      []string
418         DefaultKeepCacheRAM         ByteSize
419         DispatchPrivateKey          string
420         LogReuseDecisions           bool
421         MaxComputeVMs               int
422         MaxDispatchAttempts         int
423         MaxRetryAttempts            int
424         MinRetryPeriod              Duration
425         ReserveExtraRAM             ByteSize
426         StaleLockTimeout            Duration
427         SupportedDockerImageFormats StringSet
428         UsePreemptibleInstances     bool
429         RuntimeEngine               string
430
431         JobsAPI struct {
432                 Enable         string
433                 GitInternalDir string
434         }
435         Logging struct {
436                 MaxAge                       Duration
437                 LogBytesPerEvent             int
438                 LogSecondsBetweenEvents      Duration
439                 LogThrottlePeriod            Duration
440                 LogThrottleBytes             int
441                 LogThrottleLines             int
442                 LimitLogBytesPerJob          int
443                 LogPartialLineThrottlePeriod Duration
444                 LogUpdatePeriod              Duration
445                 LogUpdateSize                ByteSize
446         }
447         ShellAccess struct {
448                 Admin bool
449                 User  bool
450         }
451         SLURM struct {
452                 PrioritySpread             int64
453                 SbatchArgumentsList        []string
454                 SbatchEnvironmentVariables map[string]string
455                 Managed                    struct {
456                         DNSServerConfDir       string
457                         DNSServerConfTemplate  string
458                         DNSServerReloadCommand string
459                         DNSServerUpdateCommand string
460                         ComputeNodeDomain      string
461                         ComputeNodeNameservers StringSet
462                         AssignNodeHostname     string
463                 }
464         }
465         LSF struct {
466                 BsubSudoUser      string
467                 BsubArgumentsList []string
468         }
469 }
470
471 type CloudVMsConfig struct {
472         Enable bool
473
474         BootProbeCommand               string
475         DeployRunnerBinary             string
476         ImageID                        string
477         MaxCloudOpsPerSecond           int
478         MaxProbesPerSecond             int
479         MaxConcurrentInstanceCreateOps int
480         PollInterval                   Duration
481         ProbeInterval                  Duration
482         SSHPort                        string
483         SyncInterval                   Duration
484         TimeoutBooting                 Duration
485         TimeoutIdle                    Duration
486         TimeoutProbe                   Duration
487         TimeoutShutdown                Duration
488         TimeoutSignal                  Duration
489         TimeoutStaleRunLock            Duration
490         TimeoutTERM                    Duration
491         ResourceTags                   map[string]string
492         TagKeyPrefix                   string
493
494         Driver           string
495         DriverParameters json.RawMessage
496 }
497
498 type InstanceTypeMap map[string]InstanceType
499
500 var errDuplicateInstanceTypeName = errors.New("duplicate instance type name")
501
502 // UnmarshalJSON handles old config files that provide an array of
503 // instance types instead of a hash.
504 func (it *InstanceTypeMap) UnmarshalJSON(data []byte) error {
505         fixup := func(t InstanceType) (InstanceType, error) {
506                 if t.ProviderType == "" {
507                         t.ProviderType = t.Name
508                 }
509                 if t.Scratch == 0 {
510                         t.Scratch = t.IncludedScratch + t.AddedScratch
511                 } else if t.AddedScratch == 0 {
512                         t.AddedScratch = t.Scratch - t.IncludedScratch
513                 } else if t.IncludedScratch == 0 {
514                         t.IncludedScratch = t.Scratch - t.AddedScratch
515                 }
516
517                 if t.Scratch != (t.IncludedScratch + t.AddedScratch) {
518                         return t, fmt.Errorf("InstanceType %q: Scratch != (IncludedScratch + AddedScratch)", t.Name)
519                 }
520                 return t, nil
521         }
522
523         if len(data) > 0 && data[0] == '[' {
524                 var arr []InstanceType
525                 err := json.Unmarshal(data, &arr)
526                 if err != nil {
527                         return err
528                 }
529                 if len(arr) == 0 {
530                         *it = nil
531                         return nil
532                 }
533                 *it = make(map[string]InstanceType, len(arr))
534                 for _, t := range arr {
535                         if _, ok := (*it)[t.Name]; ok {
536                                 return errDuplicateInstanceTypeName
537                         }
538                         t, err := fixup(t)
539                         if err != nil {
540                                 return err
541                         }
542                         (*it)[t.Name] = t
543                 }
544                 return nil
545         }
546         var hash map[string]InstanceType
547         err := json.Unmarshal(data, &hash)
548         if err != nil {
549                 return err
550         }
551         // Fill in Name field (and ProviderType field, if not
552         // specified) using hash key.
553         *it = InstanceTypeMap(hash)
554         for name, t := range *it {
555                 t.Name = name
556                 t, err := fixup(t)
557                 if err != nil {
558                         return err
559                 }
560                 (*it)[name] = t
561         }
562         return nil
563 }
564
565 type StringSet map[string]struct{}
566
567 // UnmarshalJSON handles old config files that provide an array of
568 // instance types instead of a hash.
569 func (ss *StringSet) UnmarshalJSON(data []byte) error {
570         if len(data) > 0 && data[0] == '[' {
571                 var arr []string
572                 err := json.Unmarshal(data, &arr)
573                 if err != nil {
574                         return err
575                 }
576                 if len(arr) == 0 {
577                         *ss = nil
578                         return nil
579                 }
580                 *ss = make(map[string]struct{}, len(arr))
581                 for _, t := range arr {
582                         (*ss)[t] = struct{}{}
583                 }
584                 return nil
585         }
586         var hash map[string]struct{}
587         err := json.Unmarshal(data, &hash)
588         if err != nil {
589                 return err
590         }
591         *ss = make(map[string]struct{}, len(hash))
592         for t := range hash {
593                 (*ss)[t] = struct{}{}
594         }
595
596         return nil
597 }
598
599 type ServiceName string
600
601 const (
602         ServiceNameRailsAPI      ServiceName = "arvados-api-server"
603         ServiceNameController    ServiceName = "arvados-controller"
604         ServiceNameDispatchCloud ServiceName = "arvados-dispatch-cloud"
605         ServiceNameDispatchLSF   ServiceName = "arvados-dispatch-lsf"
606         ServiceNameHealth        ServiceName = "arvados-health"
607         ServiceNameWorkbench1    ServiceName = "arvados-workbench1"
608         ServiceNameWorkbench2    ServiceName = "arvados-workbench2"
609         ServiceNameWebsocket     ServiceName = "arvados-ws"
610         ServiceNameKeepbalance   ServiceName = "keep-balance"
611         ServiceNameKeepweb       ServiceName = "keep-web"
612         ServiceNameKeepproxy     ServiceName = "keepproxy"
613         ServiceNameKeepstore     ServiceName = "keepstore"
614 )
615
616 // Map returns all services as a map, suitable for iterating over all
617 // services or looking up a service by name.
618 func (svcs Services) Map() map[ServiceName]Service {
619         return map[ServiceName]Service{
620                 ServiceNameRailsAPI:      svcs.RailsAPI,
621                 ServiceNameController:    svcs.Controller,
622                 ServiceNameDispatchCloud: svcs.DispatchCloud,
623                 ServiceNameDispatchLSF:   svcs.DispatchLSF,
624                 ServiceNameHealth:        svcs.Health,
625                 ServiceNameWorkbench1:    svcs.Workbench1,
626                 ServiceNameWorkbench2:    svcs.Workbench2,
627                 ServiceNameWebsocket:     svcs.Websocket,
628                 ServiceNameKeepbalance:   svcs.Keepbalance,
629                 ServiceNameKeepweb:       svcs.WebDAV,
630                 ServiceNameKeepproxy:     svcs.Keepproxy,
631                 ServiceNameKeepstore:     svcs.Keepstore,
632         }
633 }