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