// Copyright (C) The Arvados Authors. All rights reserved. // // SPDX-License-Identifier: Apache-2.0 package arvados import ( "encoding/json" "errors" "fmt" "net/url" "os" "git.arvados.org/arvados.git/sdk/go/config" ) var DefaultConfigFile = func() string { if path := os.Getenv("ARVADOS_CONFIG"); path != "" { return path } return "/etc/arvados/config.yml" }() type Config struct { Clusters map[string]Cluster AutoReloadConfig bool } // GetConfig returns the current system config, loading it from // configFile if needed. func GetConfig(configFile string) (*Config, error) { var cfg Config err := config.LoadFile(&cfg, configFile) return &cfg, err } // GetCluster returns the cluster ID and config for the given // cluster, or the default/only configured cluster if clusterID is "". func (sc *Config) GetCluster(clusterID string) (*Cluster, error) { if clusterID == "" { 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 { cc.ClusterID = id return &cc, nil } } } cc, ok := sc.Clusters[clusterID] if !ok { return nil, fmt.Errorf("cluster %q is not configured", clusterID) } cc.ClusterID = clusterID return &cc, nil } type WebDAVCacheConfig struct { TTL Duration UUIDTTL Duration MaxBlockEntries int MaxCollectionEntries int MaxCollectionBytes int64 MaxUUIDEntries int MaxSessions int } type UploadDownloadPermission struct { Upload bool Download bool } type UploadDownloadRolePermissions struct { User UploadDownloadPermission Admin UploadDownloadPermission } type Cluster struct { ClusterID string `json:"-"` ManagementToken string SystemRootToken string Services Services InstanceTypes InstanceTypeMap Containers ContainersConfig RemoteClusters map[string]RemoteCluster PostgreSQL PostgreSQL API struct { AsyncPermissionsUpdateInterval Duration DisabledAPIs StringSet MaxIndexDatabaseRead int MaxItemsPerResponse int MaxConcurrentRequests int MaxKeepBlobBuffers int MaxRequestAmplification int MaxRequestSize int MaxTokenLifetime Duration RequestTimeout Duration SendTimeout Duration WebsocketClientEventQueue int WebsocketServerEventQueue int KeepServiceRequestTimeout Duration } AuditLogs struct { MaxAge Duration MaxDeleteBatch int UnloggedAttributes StringSet } Collections struct { BlobSigning bool BlobSigningKey string BlobSigningTTL Duration BlobTrash bool BlobTrashLifetime Duration BlobTrashCheckInterval Duration BlobTrashConcurrency int BlobDeleteConcurrency int BlobReplicateConcurrency int CollectionVersioning bool DefaultTrashLifetime Duration DefaultReplication int ManagedProperties map[string]struct { Value interface{} Function string Protected bool } PreserveVersionIfIdle Duration TrashSweepInterval Duration TrustAllContent bool ForwardSlashNameSubstitution string S3FolderObjects bool BlobMissingReport string BalancePeriod Duration BalanceCollectionBatch int BalanceCollectionBuffers int BalanceTimeout Duration BalanceUpdateLimit int WebDAVCache WebDAVCacheConfig KeepproxyPermission UploadDownloadRolePermissions WebDAVPermission UploadDownloadRolePermissions WebDAVLogEvents bool } Git struct { GitCommand string GitoliteHome string Repositories string } Login struct { LDAP struct { Enable bool URL URL StartTLS bool InsecureTLS bool StripDomain string AppendDomain string SearchAttribute string SearchBindUser string SearchBindPassword string SearchBase string SearchFilters string EmailAttribute string UsernameAttribute string } Google struct { Enable bool ClientID string ClientSecret string AlternateEmailAddresses bool AuthenticationRequestParameters map[string]string } OpenIDConnect struct { Enable bool Issuer string ClientID string ClientSecret string EmailClaim string EmailVerifiedClaim string UsernameClaim string AcceptAccessToken bool AcceptAccessTokenScope string AuthenticationRequestParameters map[string]string } PAM struct { Enable bool Service string DefaultEmailDomain string } Test struct { Enable bool Users map[string]TestUser } LoginCluster string RemoteTokenRefresh Duration TokenLifetime Duration TrustedClients map[string]struct{} IssueTrustedTokens bool } Mail struct { MailchimpAPIKey string MailchimpListID string SendUserSetupNotificationEmail bool IssueReporterEmailFrom string IssueReporterEmailTo string SupportEmailAddress string EmailFrom string } SystemLogs struct { LogLevel string Format string MaxRequestLogParamsSize int } TLS struct { Certificate string Key string Insecure bool } Users struct { AnonymousUserToken string AdminNotifierEmailFrom string AutoAdminFirstUser bool AutoAdminUserWithEmail string AutoSetupNewUsers bool AutoSetupNewUsersWithRepository bool AutoSetupNewUsersWithVmUUID string AutoSetupUsernameBlacklist StringSet EmailSubjectPrefix string NewInactiveUserNotificationRecipients StringSet NewUserNotificationRecipients StringSet NewUsersAreActive bool UserNotifierEmailFrom string UserNotifierEmailBcc StringSet UserProfileNotificationAddress string PreferDomainForUsername string UserSetupMailText string } StorageClasses map[string]StorageClassConfig Volumes map[string]Volume Workbench struct { ActivationContactLink string APIClientConnectTimeout Duration APIClientReceiveTimeout Duration APIResponseCompression bool ApplicationMimetypesWithViewIcon StringSet ArvadosDocsite string ArvadosPublicDataDocURL string DefaultOpenIdPrefix string EnableGettingStartedPopup bool EnablePublicProjectsPage bool FileViewersConfigURL string LogViewerMaxBytes ByteSize MultiSiteSearch string ProfilingEnabled bool Repositories bool RepositoryCache string RunningJobLogRecordsToFetch int SecretKeyBase string ShowRecentCollectionsOnDashboard bool ShowUserAgreementInline bool ShowUserNotifications bool SiteName string Theme string UserProfileFormFields map[string]struct { Type string FormFieldTitle string FormFieldDescription string Required bool Position int Options map[string]struct{} } UserProfileFormMessage string VocabularyURL string WelcomePageHTML string InactivePageHTML string SSHHelpPageHTML string SSHHelpHostSuffix string IdleTimeout Duration } } type StorageClassConfig struct { Default bool Priority int } type Volume struct { AccessViaHosts map[URL]VolumeAccess ReadOnly bool Replication int StorageClasses map[string]bool Driver string DriverParameters json.RawMessage } type S3VolumeDriverParameters struct { IAMRole string AccessKeyID string SecretAccessKey string Endpoint string Region string Bucket string LocationConstraint bool V2Signature bool UseAWSS3v2Driver bool IndexPageSize int ConnectTimeout Duration ReadTimeout Duration RaceWindow Duration UnsafeDelete bool PrefixLength int } type AzureVolumeDriverParameters struct { StorageAccountName string StorageAccountKey string StorageBaseURL string ContainerName string RequestTimeout Duration ListBlobsRetryDelay Duration ListBlobsMaxAttempts int } type DirectoryVolumeDriverParameters struct { Root string Serialize bool } type VolumeAccess struct { ReadOnly bool } type Services struct { Composer Service Controller Service DispatchCloud Service DispatchLSF Service GitHTTP Service GitSSH Service Health Service Keepbalance Service Keepproxy Service Keepstore Service RailsAPI Service WebDAVDownload Service WebDAV Service WebShell Service Websocket Service Workbench1 Service Workbench2 Service } type Service struct { InternalURLs map[URL]ServiceInstance ExternalURL URL } type TestUser struct { Email string Password string } // URL is a url.URL that is also usable as a JSON key/value. type URL url.URL // UnmarshalText implements encoding.TextUnmarshaler so URL can be // used as a JSON key/value. func (su *URL) UnmarshalText(text []byte) error { u, err := url.Parse(string(text)) if err == nil { *su = URL(*u) if su.Path == "" && su.Host != "" { // http://example really means http://example/ su.Path = "/" } } return err } func (su URL) MarshalText() ([]byte, error) { return []byte(fmt.Sprintf("%s", (*url.URL)(&su).String())), nil } func (su URL) String() string { return (*url.URL)(&su).String() } type ServiceInstance struct { Rendezvous string `json:",omitempty"` } type PostgreSQL struct { Connection PostgreSQLConnection ConnectionPool int } type PostgreSQLConnection map[string]string type RemoteCluster struct { Host string Proxy bool Scheme string Insecure bool ActivateUsers bool } type InstanceType struct { Name string ProviderType string VCPUs int RAM ByteSize Scratch ByteSize IncludedScratch ByteSize AddedScratch ByteSize Price float64 Preemptible bool } type ContainersConfig struct { CloudVMs CloudVMsConfig CrunchRunCommand string CrunchRunArgumentsList []string DefaultKeepCacheRAM ByteSize DispatchPrivateKey string LogReuseDecisions bool MaxComputeVMs int MaxDispatchAttempts int MaxRetryAttempts int MinRetryPeriod Duration ReserveExtraRAM ByteSize StaleLockTimeout Duration SupportedDockerImageFormats StringSet UsePreemptibleInstances bool RuntimeEngine string LocalKeepBlobBuffersPerVCPU int JobsAPI struct { Enable string GitInternalDir string } Logging struct { MaxAge Duration LogBytesPerEvent int LogSecondsBetweenEvents Duration LogThrottlePeriod Duration LogThrottleBytes int LogThrottleLines int LimitLogBytesPerJob int LogPartialLineThrottlePeriod Duration LogUpdatePeriod Duration LogUpdateSize ByteSize } ShellAccess struct { Admin bool User bool } SLURM struct { PrioritySpread int64 SbatchArgumentsList []string SbatchEnvironmentVariables map[string]string Managed struct { DNSServerConfDir string DNSServerConfTemplate string DNSServerReloadCommand string DNSServerUpdateCommand string ComputeNodeDomain string ComputeNodeNameservers StringSet AssignNodeHostname string } } LSF struct { BsubSudoUser string BsubArgumentsList []string } } type CloudVMsConfig struct { Enable bool BootProbeCommand string DeployRunnerBinary string ImageID string MaxCloudOpsPerSecond int MaxProbesPerSecond int MaxConcurrentInstanceCreateOps int PollInterval Duration ProbeInterval Duration SSHPort string SyncInterval Duration TimeoutBooting Duration TimeoutIdle Duration TimeoutProbe Duration TimeoutShutdown Duration TimeoutSignal Duration TimeoutStaleRunLock Duration TimeoutTERM Duration ResourceTags map[string]string TagKeyPrefix string Driver string DriverParameters json.RawMessage } 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 { fixup := func(t InstanceType) (InstanceType, error) { if t.ProviderType == "" { t.ProviderType = t.Name } if t.Scratch == 0 { t.Scratch = t.IncludedScratch + t.AddedScratch } else if t.AddedScratch == 0 { t.AddedScratch = t.Scratch - t.IncludedScratch } else if t.IncludedScratch == 0 { t.IncludedScratch = t.Scratch - t.AddedScratch } if t.Scratch != (t.IncludedScratch + t.AddedScratch) { return t, fmt.Errorf("InstanceType %q: Scratch != (IncludedScratch + AddedScratch)", t.Name) } return t, nil } 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 } t, err := fixup(t) if err != nil { return err } (*it)[t.Name] = t } return nil } var hash map[string]InstanceType err := json.Unmarshal(data, &hash) if err != nil { return err } // Fill in Name field (and ProviderType field, if not // specified) using hash key. *it = InstanceTypeMap(hash) for name, t := range *it { t.Name = name t, err := fixup(t) if err != nil { return err } (*it)[name] = t } return nil } type StringSet map[string]struct{} // UnmarshalJSON handles old config files that provide an array of // instance types instead of a hash. func (ss *StringSet) UnmarshalJSON(data []byte) error { if len(data) > 0 && data[0] == '[' { var arr []string err := json.Unmarshal(data, &arr) if err != nil { return err } if len(arr) == 0 { *ss = nil return nil } *ss = make(map[string]struct{}, len(arr)) for _, t := range arr { (*ss)[t] = struct{}{} } return nil } var hash map[string]struct{} err := json.Unmarshal(data, &hash) if err != nil { return err } *ss = make(map[string]struct{}, len(hash)) for t := range hash { (*ss)[t] = struct{}{} } return nil } type ServiceName string const ( ServiceNameRailsAPI ServiceName = "arvados-api-server" ServiceNameController ServiceName = "arvados-controller" ServiceNameDispatchCloud ServiceName = "arvados-dispatch-cloud" ServiceNameDispatchLSF ServiceName = "arvados-dispatch-lsf" ServiceNameHealth ServiceName = "arvados-health" ServiceNameWorkbench1 ServiceName = "arvados-workbench1" ServiceNameWorkbench2 ServiceName = "arvados-workbench2" ServiceNameWebsocket ServiceName = "arvados-ws" ServiceNameKeepbalance ServiceName = "keep-balance" ServiceNameKeepweb ServiceName = "keep-web" ServiceNameKeepproxy ServiceName = "keepproxy" ServiceNameKeepstore ServiceName = "keepstore" ) // Map returns all services as a map, suitable for iterating over all // services or looking up a service by name. func (svcs Services) Map() map[ServiceName]Service { return map[ServiceName]Service{ ServiceNameRailsAPI: svcs.RailsAPI, ServiceNameController: svcs.Controller, ServiceNameDispatchCloud: svcs.DispatchCloud, ServiceNameDispatchLSF: svcs.DispatchLSF, ServiceNameHealth: svcs.Health, ServiceNameWorkbench1: svcs.Workbench1, ServiceNameWorkbench2: svcs.Workbench2, ServiceNameWebsocket: svcs.Websocket, ServiceNameKeepbalance: svcs.Keepbalance, ServiceNameKeepweb: svcs.WebDAV, ServiceNameKeepproxy: svcs.Keepproxy, ServiceNameKeepstore: svcs.Keepstore, } }