2014-10-05 22:50:04 +08:00
// Copyright 2014 Unknwon
// Copyright 2014 Torkel Ödegaard
2014-10-04 19:33:20 +08:00
package setting
import (
2015-04-09 18:16:59 +08:00
"bytes"
2021-07-21 23:54:05 +08:00
"encoding/json"
2020-10-13 18:30:09 +08:00
"errors"
2015-02-12 20:31:41 +08:00
"fmt"
2019-02-01 18:47:21 +08:00
"net/http"
2014-10-04 19:33:20 +08:00
"net/url"
"os"
"path"
"path/filepath"
2021-03-19 17:14:49 +08:00
"runtime"
2021-03-12 21:30:21 +08:00
"strconv"
2014-10-04 19:33:20 +08:00
"strings"
2018-07-02 19:33:39 +08:00
"time"
2021-07-01 14:30:09 +08:00
"github.com/gobwas/glob"
2020-10-19 22:58:16 +08:00
"github.com/prometheus/common/model"
2021-08-25 21:11:22 +08:00
"gopkg.in/ini.v1"
2019-04-22 23:58:24 +08:00
2021-03-12 21:30:21 +08:00
"github.com/grafana/grafana-aws-sdk/pkg/awsds"
2020-09-02 14:07:31 +08:00
"github.com/grafana/grafana/pkg/components/gtime"
2019-05-13 14:45:54 +08:00
"github.com/grafana/grafana/pkg/infra/log"
2015-04-19 15:14:50 +08:00
"github.com/grafana/grafana/pkg/util"
2014-10-04 19:33:20 +08:00
)
type Scheme string
const (
2020-10-02 21:45:45 +08:00
HTTPScheme Scheme = "http"
HTTPSScheme Scheme = "https"
HTTP2Scheme Scheme = "h2"
SocketScheme Scheme = "socket"
2014-10-04 19:33:20 +08:00
)
2014-12-16 19:04:08 +08:00
const (
2021-05-10 23:03:10 +08:00
RedactedPassword = "*********"
2020-10-02 21:45:45 +08:00
DefaultHTTPAddr = "0.0.0.0"
Dev = "development"
Prod = "production"
Test = "test"
2021-08-25 21:11:22 +08:00
ApplicationName = "Grafana"
2014-12-16 19:04:08 +08:00
)
2019-11-08 17:51:15 +08:00
// This constant corresponds to the default value for ldap_sync_ttl in .ini files
2020-04-30 03:37:21 +08:00
// it is used for comparison and has to be kept in sync
2019-11-08 17:51:15 +08:00
const (
2020-12-11 18:44:44 +08:00
authProxySyncTTL = 60
2019-11-08 17:51:15 +08:00
)
2021-03-19 17:14:49 +08:00
// zoneInfo names environment variable for setting the path to look for the timezone database in go
const zoneInfo = "ZONEINFO"
2014-10-04 19:33:20 +08:00
var (
// App settings.
2020-10-02 21:45:45 +08:00
Env = Dev
2019-05-27 23:47:29 +08:00
AppUrl string
AppSubUrl string
ServeFromSubPath bool
InstanceName string
2014-10-04 19:33:20 +08:00
2015-01-05 17:46:58 +08:00
// build
2021-08-25 21:11:22 +08:00
BuildVersion string
BuildCommit string
BuildBranch string
BuildStamp int64
IsEnterprise bool
2015-01-05 17:46:58 +08:00
2018-11-15 21:42:09 +08:00
// packaging
Packaging = "unknown"
2015-04-09 18:16:59 +08:00
// Paths
2018-05-01 21:51:15 +08:00
HomePath string
CustomInitPath = "conf/custom.ini"
2015-04-09 18:16:59 +08:00
2020-11-13 16:52:38 +08:00
// HTTP server options
2021-07-15 20:30:06 +08:00
StaticRootPath string
2014-10-05 22:50:04 +08:00
2015-01-27 17:09:54 +08:00
// Security settings.
2020-12-11 18:44:44 +08:00
SecretKey string
DisableGravatar bool
DataProxyWhiteList map [ string ] bool
CookieSecure bool
CookieSameSiteDisabled bool
CookieSameSiteMode http . SameSite
2015-01-27 17:09:54 +08:00
2015-10-14 22:39:57 +08:00
// Snapshots
2016-09-23 22:56:12 +08:00
ExternalSnapshotUrl string
ExternalSnapshotName string
ExternalEnabled bool
SnapShotRemoveExpired bool
2015-10-14 22:39:57 +08:00
2017-11-14 18:34:27 +08:00
// Dashboard history
DashboardVersionsToKeep int
2020-02-28 21:32:01 +08:00
MinRefreshInterval string
2017-11-14 18:34:27 +08:00
2015-03-11 23:19:29 +08:00
// User settings
2017-07-31 20:39:33 +08:00
AllowUserSignUp bool
AllowUserOrgCreate bool
AutoAssignOrg bool
2018-07-14 03:14:40 +08:00
AutoAssignOrgId int
2017-07-31 20:39:33 +08:00
AutoAssignOrgRole string
VerifyEmailEnabled bool
LoginHint string
2019-03-08 06:00:04 +08:00
PasswordHint string
2017-07-31 20:39:33 +08:00
DisableLoginForm bool
DisableSignoutMenu bool
2018-05-27 20:52:50 +08:00
SignoutRedirectUrl string
2017-07-31 20:39:33 +08:00
ExternalUserMngLinkUrl string
ExternalUserMngLinkName string
ExternalUserMngInfo string
2018-05-28 22:16:48 +08:00
OAuthAutoLogin bool
2017-12-14 01:53:42 +08:00
ViewersCanEdit bool
2015-01-27 22:14:53 +08:00
2020-12-11 18:44:44 +08:00
// HTTP auth
2020-10-08 16:03:20 +08:00
SigV4AuthEnabled bool
2015-01-27 22:45:27 +08:00
2015-02-24 03:07:49 +08:00
AnonymousEnabled bool
2015-01-07 23:37:24 +08:00
2015-05-01 17:55:59 +08:00
// Auth proxy settings
2020-12-11 18:44:44 +08:00
AuthProxyEnabled bool
AuthProxyHeaderProperty string
2015-05-01 17:55:59 +08:00
2015-06-30 15:37:52 +08:00
// Basic Auth
BasicAuthEnabled bool
2014-10-04 19:33:20 +08:00
// Global setting objects.
2020-11-17 18:51:31 +08:00
Raw * ini . File
2014-10-07 03:31:54 +08:00
2015-04-09 18:16:59 +08:00
// for logging purposes
configFiles [ ] string
appliedCommandLineProperties [ ] string
appliedEnvOverrides [ ] string
2015-03-23 03:14:00 +08:00
2021-02-10 17:07:32 +08:00
// analytics
2021-07-09 17:45:25 +08:00
GoogleAnalyticsId string
GoogleTagManagerId string
RudderstackDataPlaneUrl string
RudderstackWriteKey string
2015-06-04 15:34:42 +08:00
// LDAP
2019-05-22 20:30:03 +08:00
LDAPEnabled bool
LDAPConfigFile string
LDAPSyncCron string
LDAPAllowSignup bool
LDAPActiveSyncEnabled bool
2015-07-10 17:10:48 +08:00
2020-11-13 16:52:38 +08:00
// Quota
2015-09-11 01:47:33 +08:00
Quota QuotaSettings
2016-04-29 20:35:58 +08:00
// Alerting
2018-09-06 17:20:38 +08:00
AlertingEnabled bool
ExecuteAlerts bool
2018-09-25 18:17:04 +08:00
AlertingRenderLimit int
2018-09-06 17:20:38 +08:00
AlertingErrorOrTimeout string
AlertingNoDataOrNullValues string
2016-06-07 19:31:56 +08:00
2019-03-29 13:58:37 +08:00
AlertingEvaluationTimeout time . Duration
AlertingNotificationTimeout time . Duration
AlertingMaxAttempts int
2020-01-14 17:13:34 +08:00
AlertingMinInterval int64
2019-03-29 13:58:37 +08:00
2018-04-27 17:39:14 +08:00
// Explore UI
ExploreEnabled bool
2016-05-27 19:52:19 +08:00
// Grafana.NET URL
2017-05-22 20:56:50 +08:00
GrafanaComUrl string
2016-07-30 19:36:21 +08:00
2016-08-10 23:27:39 +08:00
ImageUploadProvider string
2014-10-04 19:33:20 +08:00
)
2020-11-21 00:01:10 +08:00
// AddChangePasswordLink returns if login form is disabled or not since
// the same intention can be used to hide both features.
func AddChangePasswordLink ( ) bool {
return ! DisableLoginForm
}
2018-10-12 13:55:36 +08:00
// TODO move all global vars to this struct
2018-04-30 22:21:04 +08:00
type Cfg struct {
2019-04-22 23:58:24 +08:00
Raw * ini . File
Logger log . Logger
2018-04-30 22:21:04 +08:00
2018-10-12 13:55:36 +08:00
// HTTP Server Settings
2021-03-10 19:41:29 +08:00
CertFile string
KeyFile string
HTTPAddr string
HTTPPort string
2020-11-13 16:52:38 +08:00
AppURL string
AppSubURL string
2019-05-27 23:47:29 +08:00
ServeFromSubPath bool
2020-06-23 00:00:39 +08:00
StaticRootPath string
2020-08-19 20:39:13 +08:00
Protocol Scheme
2020-12-11 18:44:44 +08:00
SocketPath string
RouterLogging bool
Domain string
2021-02-01 17:13:09 +08:00
CDNRootURL * url . URL
2021-03-19 18:21:52 +08:00
ReadTimeout time . Duration
2021-03-10 19:41:29 +08:00
EnableGzip bool
EnforceDomain bool
2018-10-12 13:55:36 +08:00
2021-08-25 21:11:22 +08:00
// Security settings
EmailCodeValidMinutes int
2020-06-11 22:14:05 +08:00
// build
BuildVersion string
BuildCommit string
BuildBranch string
BuildStamp int64
IsEnterprise bool
// packaging
Packaging string
2018-05-01 21:51:15 +08:00
// Paths
2021-08-25 21:11:22 +08:00
HomePath string
2020-05-04 16:57:55 +08:00
ProvisioningPath string
DataPath string
LogsPath string
2021-02-10 20:31:47 +08:00
PluginsPath string
2020-05-04 16:57:55 +08:00
BundledPluginsPath string
2018-05-01 21:51:15 +08:00
2018-04-30 22:21:04 +08:00
// SMTP email settings
Smtp SmtpSettings
2018-05-24 21:26:27 +08:00
// Rendering
2020-04-27 23:25:08 +08:00
ImagesDir string
2021-05-12 23:16:57 +08:00
CSVsDir string
2020-04-27 23:25:08 +08:00
RendererUrl string
RendererCallbackUrl string
RendererConcurrentRequestLimit int
2018-09-24 21:58:22 +08:00
2019-02-06 04:09:55 +08:00
// Security
2020-12-11 18:44:44 +08:00
DisableInitAdminCreation bool
DisableBruteForceLoginProtection bool
CookieSecure bool
CookieSameSiteDisabled bool
CookieSameSiteMode http . SameSite
AllowEmbedding bool
XSSProtectionHeader bool
ContentTypeProtectionHeader bool
StrictTransportSecurity bool
StrictTransportSecurityMaxAge int
StrictTransportSecurityPreload bool
StrictTransportSecuritySubDomains bool
2021-01-12 14:42:32 +08:00
// CSPEnabled toggles Content Security Policy support.
CSPEnabled bool
// CSPTemplate contains the Content Security Policy template.
CSPTemplate string
2019-02-06 04:09:55 +08:00
2021-05-27 18:45:06 +08:00
TempDataLifetime time . Duration
PluginsEnableAlpha bool
PluginsAppsSkipVerifyTLS bool
PluginSettings PluginSettings
PluginsAllowUnsigned [ ] string
PluginCatalogURL string
PluginAdminEnabled bool
PluginAdminExternalManageEnabled bool
DisableSanitizeHtml bool
EnterpriseLicensePath string
2020-10-19 22:58:16 +08:00
// Metrics
2018-11-01 19:07:11 +08:00
MetricsEndpointEnabled bool
2018-11-15 04:42:47 +08:00
MetricsEndpointBasicAuthUsername string
MetricsEndpointBasicAuthPassword string
2019-09-17 15:32:24 +08:00
MetricsEndpointDisableTotalStats bool
2020-10-19 22:58:16 +08:00
MetricsGrafanaEnvironmentInfo map [ string ] string
2019-01-22 21:06:44 +08:00
2020-06-23 00:00:39 +08:00
// Dashboards
DefaultHomeDashboardPath string
2019-02-06 04:09:55 +08:00
// Auth
2019-02-07 17:51:35 +08:00
LoginCookieName string
2020-09-14 21:57:38 +08:00
LoginMaxInactiveLifetime time . Duration
LoginMaxLifetime time . Duration
2019-02-07 17:51:35 +08:00
TokenRotationIntervalMinutes int
2020-10-08 16:03:20 +08:00
SigV4AuthEnabled bool
2020-12-11 18:44:44 +08:00
BasicAuthEnabled bool
2020-12-16 02:09:04 +08:00
AdminUser string
AdminPassword string
2020-12-11 18:44:44 +08:00
2021-02-25 01:08:13 +08:00
// AWS Plugin Auth
AWSAllowedAuthProviders [ ] string
AWSAssumeRoleEnabled bool
2021-03-10 14:41:22 +08:00
AWSListMetricsPageLimit int
2021-02-25 01:08:13 +08:00
2021-05-12 22:23:37 +08:00
// Azure Cloud settings
Azure AzureSettings
2020-12-11 18:44:44 +08:00
// Auth proxy settings
AuthProxyEnabled bool
AuthProxyHeaderName string
AuthProxyHeaderProperty string
AuthProxyAutoSignUp bool
AuthProxyEnableLoginToken bool
AuthProxyWhitelist string
AuthProxyHeaders map [ string ] string
AuthProxySyncTTL int
2019-02-22 19:11:26 +08:00
2020-03-30 22:44:58 +08:00
// OAuth
OAuthCookieMaxAge int
2021-03-31 23:40:44 +08:00
// JWT Auth
JWTAuthEnabled bool
JWTAuthHeaderName string
JWTAuthEmailClaim string
JWTAuthUsernameClaim string
JWTAuthExpectClaims string
JWTAuthJWKSetURL string
JWTAuthCacheTTL time . Duration
JWTAuthKeyFile string
JWTAuthJWKSetFile string
2019-03-14 20:04:47 +08:00
// Dataproxy
2021-07-15 20:30:06 +08:00
SendUserHeader bool
DataProxyLogging bool
DataProxyTimeout int
DataProxyDialTimeout int
DataProxyTLSHandshakeTimeout int
DataProxyExpectContinueTimeout int
DataProxyMaxConnsPerHost int
DataProxyMaxIdleConns int
DataProxyKeepAlive int
DataProxyIdleConnTimeout int
2021-09-10 21:51:06 +08:00
ResponseLimit int64
2019-03-14 20:04:47 +08:00
2019-03-04 04:48:00 +08:00
// DistributedCache
2019-03-09 03:49:16 +08:00
RemoteCacheOptions * RemoteCacheOptions
2019-03-12 14:32:47 +08:00
EditorsCanAdmin bool
2019-06-26 14:47:03 +08:00
ApiKeyMaxSecondsToLive int64
2019-09-09 14:58:57 +08:00
2020-03-25 19:25:39 +08:00
// Use to enable new features which may still be in alpha/beta stage.
2020-09-07 22:19:33 +08:00
FeatureToggles map [ string ] bool
2020-12-11 18:44:44 +08:00
AnonymousEnabled bool
AnonymousOrgName string
AnonymousOrgRole string
2020-06-17 13:39:50 +08:00
AnonymousHideVersion bool
2020-09-02 14:07:31 +08:00
2020-09-07 22:19:33 +08:00
DateFormats DateFormats
2020-10-13 18:30:09 +08:00
// User
UserInviteMaxLifetime time . Duration
2020-11-24 19:10:32 +08:00
HiddenUsers map [ string ] struct { }
2020-10-13 18:30:09 +08:00
2020-09-02 14:07:31 +08:00
// Annotations
2021-03-08 21:16:07 +08:00
AnnotationCleanupJobBatchSize int64
2020-09-02 14:07:31 +08:00
AlertingAnnotationCleanupSetting AnnotationCleanupSettings
DashboardAnnotationCleanupSettings AnnotationCleanupSettings
APIAnnotationCleanupSettings AnnotationCleanupSettings
2020-11-12 19:29:43 +08:00
// Sentry config
Sentry Sentry
2020-12-11 18:44:44 +08:00
2020-12-28 19:24:42 +08:00
// Data sources
DataSourceLimit int
2020-12-11 18:44:44 +08:00
// Snapshots
SnapshotPublicMode bool
ErrTemplateName string
Env string
2021-03-08 14:02:49 +08:00
// Analytics
2021-03-10 17:14:00 +08:00
CheckForUpdates bool
ReportingDistributor string
ReportingEnabled bool
2021-03-08 14:02:49 +08:00
2020-12-11 18:44:44 +08:00
// LDAP
LDAPEnabled bool
LDAPAllowSignup bool
Quota QuotaSettings
DefaultTheme string
2021-04-13 21:27:51 +08:00
HomePage string
2021-01-07 18:36:13 +08:00
AutoAssignOrg bool
AutoAssignOrgId int
AutoAssignOrgRole string
2021-01-23 01:27:33 +08:00
// ExpressionsEnabled specifies whether expressions are enabled.
ExpressionsEnabled bool
2021-03-10 19:41:29 +08:00
ImageUploadProvider string
2021-05-28 03:03:18 +08:00
// LiveMaxConnections is a maximum number of WebSocket connections to
// Grafana Live ws endpoint (per Grafana server instance). 0 disables
// Live, -1 means unlimited connections.
LiveMaxConnections int
2021-06-24 16:07:09 +08:00
// LiveHAEngine is a type of engine to use to achieve HA with Grafana Live.
// Zero value means in-memory single node setup.
LiveHAEngine string
// LiveHAEngineAddress is a connection address for Live HA engine.
LiveHAEngineAddress string
2021-07-01 14:30:09 +08:00
// LiveAllowedOrigins is a set of origins accepted by Live. If not provided
// then Live uses AppURL as the only allowed origin.
LiveAllowedOrigins [ ] string
2021-06-08 22:08:25 +08:00
// Grafana.com URL
GrafanaComURL string
2021-07-21 23:54:05 +08:00
2021-08-25 21:11:22 +08:00
// Alerting
// AlertingBaseInterval controls the alerting base interval in seconds.
// Only for internal use and not user configuration.
AlertingBaseInterval time . Duration
2021-07-22 04:48:20 +08:00
// Geomap base layer config
GeomapDefaultBaseLayerConfig map [ string ] interface { }
GeomapEnableCustomBaseLayers bool
2021-08-13 20:14:36 +08:00
// Unified Alerting
AdminConfigPollInterval time . Duration
2018-04-30 22:21:04 +08:00
}
2021-05-04 23:44:55 +08:00
// IsLiveConfigEnabled returns true if live should be able to save configs to SQL tables
func ( cfg Cfg ) IsLiveConfigEnabled ( ) bool {
return cfg . FeatureToggles [ "live-config" ]
2020-07-27 15:26:16 +08:00
}
2020-10-13 02:51:39 +08:00
// IsNgAlertEnabled returns whether the standalone alerts feature is enabled.
2020-11-05 22:37:11 +08:00
func ( cfg Cfg ) IsNgAlertEnabled ( ) bool {
return cfg . FeatureToggles [ "ngalert" ]
2020-10-13 02:51:39 +08:00
}
2021-05-29 04:29:30 +08:00
// IsTrimDefaultsEnabled returns whether the standalone trim dashboard default feature is enabled.
func ( cfg Cfg ) IsTrimDefaultsEnabled ( ) bool {
return cfg . FeatureToggles [ "trimDefaults" ]
}
2021-01-07 15:21:35 +08:00
// IsDatabaseMetricsEnabled returns whether the database instrumentation feature is enabled.
func ( cfg Cfg ) IsDatabaseMetricsEnabled ( ) bool {
return cfg . FeatureToggles [ "database_metrics" ]
}
2021-05-14 18:53:50 +08:00
// IsHTTPRequestHistogramDisabled returns whether the request historgrams is disabled.
// This feature toggle will be removed in Grafana 8.x but gives the operator
// some graceperiod to update all the monitoring tools.
func ( cfg Cfg ) IsHTTPRequestHistogramDisabled ( ) bool {
return cfg . FeatureToggles [ "disable_http_request_histogram" ]
2020-10-20 15:44:38 +08:00
}
2021-09-08 00:24:44 +08:00
func ( cfg Cfg ) IsNewNavigationEnabled ( ) bool {
return cfg . FeatureToggles [ "newNavigation" ]
}
2015-04-08 20:10:04 +08:00
type CommandLineArgs struct {
2015-04-12 15:15:49 +08:00
Config string
HomePath string
Args [ ] string
2015-04-08 20:10:04 +08:00
}
2019-04-25 14:29:07 +08:00
func parseAppUrlAndSubUrl ( section * ini . Section ) ( string , string , error ) {
2020-09-08 17:33:04 +08:00
appUrl := valueAsString ( section , "root_url" , "http://localhost:3000/" )
2015-01-27 17:09:54 +08:00
if appUrl [ len ( appUrl ) - 1 ] != '/' {
appUrl += "/"
}
// Check if has app suburl.
2015-01-30 21:21:32 +08:00
url , err := url . Parse ( appUrl )
2015-01-27 17:09:54 +08:00
if err != nil {
2020-07-23 14:14:39 +08:00
log . Fatalf ( 4 , "Invalid root_url(%s): %s" , appUrl , err )
2015-01-27 17:09:54 +08:00
}
2020-09-08 17:33:04 +08:00
appSubUrl := strings . TrimSuffix ( url . Path , "/" )
2019-04-25 14:29:07 +08:00
return appUrl , appSubUrl , nil
2015-01-27 17:09:54 +08:00
}
2015-02-06 21:17:40 +08:00
func ToAbsUrl ( relativeUrl string ) string {
2015-02-04 18:35:59 +08:00
return AppUrl + relativeUrl
}
2021-05-10 23:03:10 +08:00
func RedactedValue ( key , value string ) string {
uppercased := strings . ToUpper ( key )
// Sensitive information: password, secrets etc
for _ , pattern := range [ ] string {
"PASSWORD" ,
"SECRET" ,
"PROVIDER_CONFIG" ,
"PRIVATE_KEY" ,
"SECRET_KEY" ,
"CERTIFICATE" ,
2021-08-19 17:41:26 +08:00
"ACCOUNT_KEY" ,
2021-05-10 23:03:10 +08:00
} {
if strings . Contains ( uppercased , pattern ) {
return RedactedPassword
}
}
// Sensitive URLs that might contain username and password
for _ , pattern := range [ ] string {
"DATABASE_URL" ,
} {
if strings . Contains ( uppercased , pattern ) {
if u , err := url . Parse ( value ) ; err == nil {
return u . Redacted ( )
}
}
}
// Otherwise return unmodified value
return value
2016-06-29 00:37:59 +08:00
}
2018-04-30 22:21:04 +08:00
func applyEnvVariableOverrides ( file * ini . File ) error {
2015-04-09 18:16:59 +08:00
appliedEnvOverrides = make ( [ ] string , 0 )
2018-04-30 22:21:04 +08:00
for _ , section := range file . Sections ( ) {
2015-02-12 20:31:41 +08:00
for _ , key := range section . Keys ( ) {
2020-01-10 22:33:54 +08:00
envKey := envKey ( section . Name ( ) , key . Name ( ) )
2015-02-12 20:31:41 +08:00
envValue := os . Getenv ( envKey )
if len ( envValue ) > 0 {
key . SetValue ( envValue )
2021-05-10 23:03:10 +08:00
appliedEnvOverrides = append ( appliedEnvOverrides , fmt . Sprintf ( "%s=%s" , envKey , RedactedValue ( envKey , envValue ) ) )
2015-02-12 20:31:41 +08:00
}
}
}
2018-03-29 00:03:33 +08:00
return nil
2015-02-12 18:55:55 +08:00
}
2020-10-19 22:58:16 +08:00
func ( cfg * Cfg ) readGrafanaEnvironmentMetrics ( ) error {
environmentMetricsSection := cfg . Raw . Section ( "metrics.environment_info" )
keys := environmentMetricsSection . Keys ( )
cfg . MetricsGrafanaEnvironmentInfo = make ( map [ string ] string , len ( keys ) )
for _ , key := range keys {
labelName := model . LabelName ( key . Name ( ) )
labelValue := model . LabelValue ( key . Value ( ) )
if ! labelName . IsValid ( ) {
return fmt . Errorf ( "invalid label name in [metrics.environment_info] configuration. name %q" , labelName )
}
if ! labelValue . IsValid ( ) {
return fmt . Errorf ( "invalid label value in [metrics.environment_info] configuration. name %q value %q" , labelName , labelValue )
}
cfg . MetricsGrafanaEnvironmentInfo [ string ( labelName ) ] = string ( labelValue )
}
return nil
}
2020-09-02 14:07:31 +08:00
func ( cfg * Cfg ) readAnnotationSettings ( ) {
2021-03-08 21:16:07 +08:00
section := cfg . Raw . Section ( "annotations" )
cfg . AnnotationCleanupJobBatchSize = section . Key ( "cleanupjob_batchsize" ) . MustInt64 ( 100 )
2020-09-02 14:07:31 +08:00
dashboardAnnotation := cfg . Raw . Section ( "annotations.dashboard" )
apiIAnnotation := cfg . Raw . Section ( "annotations.api" )
alertingSection := cfg . Raw . Section ( "alerting" )
var newAnnotationCleanupSettings = func ( section * ini . Section , maxAgeField string ) AnnotationCleanupSettings {
2020-11-03 02:26:19 +08:00
maxAge , err := gtime . ParseDuration ( section . Key ( maxAgeField ) . MustString ( "" ) )
2020-09-02 14:07:31 +08:00
if err != nil {
maxAge = 0
}
return AnnotationCleanupSettings {
MaxAge : maxAge ,
MaxCount : section . Key ( "max_annotations_to_keep" ) . MustInt64 ( 0 ) ,
}
}
cfg . AlertingAnnotationCleanupSetting = newAnnotationCleanupSettings ( alertingSection , "max_annotation_age" )
cfg . DashboardAnnotationCleanupSettings = newAnnotationCleanupSettings ( dashboardAnnotation , "max_age" )
cfg . APIAnnotationCleanupSettings = newAnnotationCleanupSettings ( apiIAnnotation , "max_age" )
}
2021-01-23 01:27:33 +08:00
func ( cfg * Cfg ) readExpressionsSettings ( ) {
expressions := cfg . Raw . Section ( "expressions" )
cfg . ExpressionsEnabled = expressions . Key ( "enabled" ) . MustBool ( true )
}
2020-09-02 14:07:31 +08:00
type AnnotationCleanupSettings struct {
MaxAge time . Duration
MaxCount int64
}
2020-01-10 22:33:54 +08:00
func envKey ( sectionName string , keyName string ) string {
2020-09-22 22:22:19 +08:00
sN := strings . ToUpper ( strings . ReplaceAll ( sectionName , "." , "_" ) )
sN = strings . ReplaceAll ( sN , "-" , "_" )
kN := strings . ToUpper ( strings . ReplaceAll ( keyName , "." , "_" ) )
2020-01-10 22:33:54 +08:00
envKey := fmt . Sprintf ( "GF_%s_%s" , sN , kN )
return envKey
}
2018-04-30 22:21:04 +08:00
func applyCommandLineDefaultProperties ( props map [ string ] string , file * ini . File ) {
2015-04-09 18:16:59 +08:00
appliedCommandLineProperties = make ( [ ] string , 0 )
2018-04-30 22:21:04 +08:00
for _ , section := range file . Sections ( ) {
2015-04-09 18:16:59 +08:00
for _ , key := range section . Keys ( ) {
keyString := fmt . Sprintf ( "default.%s.%s" , section . Name ( ) , key . Name ( ) )
value , exists := props [ keyString ]
if exists {
key . SetValue ( value )
2021-05-10 23:03:10 +08:00
appliedCommandLineProperties = append ( appliedCommandLineProperties ,
fmt . Sprintf ( "%s=%s" , keyString , RedactedValue ( keyString , value ) ) )
2015-04-09 18:16:59 +08:00
}
2015-04-09 02:31:42 +08:00
}
}
}
2018-04-30 22:21:04 +08:00
func applyCommandLineProperties ( props map [ string ] string , file * ini . File ) {
for _ , section := range file . Sections ( ) {
2017-10-02 02:02:25 +08:00
sectionName := section . Name ( ) + "."
2019-09-30 21:16:04 +08:00
if section . Name ( ) == ini . DefaultSection {
2017-10-02 02:02:25 +08:00
sectionName = ""
}
2015-04-09 18:16:59 +08:00
for _ , key := range section . Keys ( ) {
2017-10-02 02:02:25 +08:00
keyString := sectionName + key . Name ( )
2015-04-09 18:16:59 +08:00
value , exists := props [ keyString ]
if exists {
appliedCommandLineProperties = append ( appliedCommandLineProperties , fmt . Sprintf ( "%s=%s" , keyString , value ) )
2017-10-02 02:02:25 +08:00
key . SetValue ( value )
2015-04-09 18:16:59 +08:00
}
}
}
}
2015-02-16 05:57:49 +08:00
2015-04-09 18:16:59 +08:00
func getCommandLineProperties ( args [ ] string ) map [ string ] string {
props := make ( map [ string ] string )
2014-10-04 19:33:20 +08:00
2015-04-09 18:16:59 +08:00
for _ , arg := range args {
if ! strings . HasPrefix ( arg , "cfg:" ) {
continue
}
trimmed := strings . TrimPrefix ( arg , "cfg:" )
parts := strings . Split ( trimmed , "=" )
if len ( parts ) != 2 {
2020-07-23 14:14:39 +08:00
log . Fatalf ( 3 , "Invalid command line argument. argument: %v" , arg )
2015-04-09 18:16:59 +08:00
return nil
2015-01-27 17:09:54 +08:00
}
2015-01-05 14:59:18 +08:00
2015-04-09 18:16:59 +08:00
props [ parts [ 0 ] ] = parts [ 1 ]
}
return props
}
func makeAbsolute ( path string , root string ) string {
if filepath . IsAbs ( path ) {
return path
}
return filepath . Join ( root , path )
}
2021-08-25 21:11:22 +08:00
func ( cfg * Cfg ) loadSpecifiedConfigFile ( configFile string , masterFile * ini . File ) error {
2015-04-12 15:15:49 +08:00
if configFile == "" {
2021-08-25 21:11:22 +08:00
configFile = filepath . Join ( cfg . HomePath , CustomInitPath )
2015-04-12 15:15:49 +08:00
// return without error if custom file does not exist
if ! pathExists ( configFile ) {
2016-07-01 07:37:06 +08:00
return nil
2015-04-12 15:15:49 +08:00
}
}
2015-04-10 16:58:32 +08:00
userConfig , err := ini . Load ( configFile )
if err != nil {
2020-11-05 20:07:06 +08:00
return fmt . Errorf ( "failed to parse %q: %w" , configFile , err )
2015-04-10 16:58:32 +08:00
}
2016-11-29 00:55:18 +08:00
userConfig . BlockMode = false
2015-04-10 16:58:32 +08:00
for _ , section := range userConfig . Sections ( ) {
for _ , key := range section . Keys ( ) {
if key . Value ( ) == "" {
continue
}
2018-04-30 22:21:04 +08:00
defaultSec , err := masterFile . GetSection ( section . Name ( ) )
2015-04-10 16:58:32 +08:00
if err != nil {
2018-04-30 22:21:04 +08:00
defaultSec , _ = masterFile . NewSection ( section . Name ( ) )
2015-04-10 16:58:32 +08:00
}
defaultKey , err := defaultSec . GetKey ( key . Name ( ) )
if err != nil {
2015-11-19 23:50:17 +08:00
defaultKey , _ = defaultSec . NewKey ( key . Name ( ) , key . Value ( ) )
2015-04-10 16:58:32 +08:00
}
defaultKey . SetValue ( key . Value ( ) )
}
}
configFiles = append ( configFiles , configFile )
2016-07-01 07:37:06 +08:00
return nil
2015-04-10 16:58:32 +08:00
}
2021-08-25 21:11:22 +08:00
func ( cfg * Cfg ) loadConfiguration ( args CommandLineArgs ) ( * ini . File , error ) {
2015-04-09 18:16:59 +08:00
// load config defaults
defaultConfigFile := path . Join ( HomePath , "conf/defaults.ini" )
configFiles = append ( configFiles , defaultConfigFile )
2016-12-06 14:36:10 +08:00
// check if config file exists
if _ , err := os . Stat ( defaultConfigFile ) ; os . IsNotExist ( err ) {
fmt . Println ( "Grafana-server Init Failed: Could not find config defaults, make sure homepath command line parameter is set or working directory is homepath" )
os . Exit ( 1 )
}
// load defaults
2018-04-30 22:21:04 +08:00
parsedFile , err := ini . Load ( defaultConfigFile )
2015-04-09 18:16:59 +08:00
if err != nil {
2020-03-23 20:37:53 +08:00
fmt . Printf ( "Failed to parse defaults.ini, %v\n" , err )
2016-12-06 14:36:10 +08:00
os . Exit ( 1 )
2018-04-30 22:21:04 +08:00
return nil , err
2015-04-09 18:16:59 +08:00
}
2018-04-30 22:21:04 +08:00
parsedFile . BlockMode = false
2016-11-18 23:43:08 +08:00
2015-04-09 18:16:59 +08:00
// command line props
commandLineProps := getCommandLineProperties ( args . Args )
// load default overrides
2018-04-30 22:21:04 +08:00
applyCommandLineDefaultProperties ( commandLineProps , parsedFile )
2015-04-09 18:16:59 +08:00
// load specified config file
2021-08-25 21:11:22 +08:00
err = cfg . loadSpecifiedConfigFile ( args . Config , parsedFile )
2016-07-01 07:37:06 +08:00
if err != nil {
2019-06-05 04:00:05 +08:00
err2 := cfg . initLogging ( parsedFile )
if err2 != nil {
return nil , err2
2019-04-25 14:29:07 +08:00
}
2020-07-23 14:14:39 +08:00
log . Fatalf ( 3 , err . Error ( ) )
2016-07-01 07:37:06 +08:00
}
2014-10-04 19:33:20 +08:00
2015-04-09 18:16:59 +08:00
// apply environment overrides
2018-04-30 22:21:04 +08:00
err = applyEnvVariableOverrides ( parsedFile )
2018-03-29 00:03:33 +08:00
if err != nil {
2018-04-30 22:21:04 +08:00
return nil , err
2018-03-29 00:03:33 +08:00
}
2015-04-09 18:16:59 +08:00
// apply command line overrides
2018-04-30 22:21:04 +08:00
applyCommandLineProperties ( commandLineProps , parsedFile )
2015-04-08 20:10:04 +08:00
2015-04-09 18:16:59 +08:00
// evaluate config values containing environment variables
2020-06-10 20:58:42 +08:00
err = expandConfig ( parsedFile )
if err != nil {
return nil , err
}
2015-05-14 16:15:46 +08:00
// update data path and logging config
2020-09-08 17:33:04 +08:00
dataPath := valueAsString ( parsedFile . Section ( "paths" ) , "data" , "" )
2019-04-25 14:29:07 +08:00
cfg . DataPath = makeAbsolute ( dataPath , HomePath )
err = cfg . initLogging ( parsedFile )
if err != nil {
return nil , err
}
2018-03-29 00:03:33 +08:00
2018-04-30 22:21:04 +08:00
return parsedFile , err
2015-04-09 18:16:59 +08:00
}
2015-04-12 15:15:49 +08:00
func pathExists ( path string ) bool {
_ , err := os . Stat ( path )
if err == nil {
return true
}
if os . IsNotExist ( err ) {
return false
}
return false
}
2021-08-25 21:11:22 +08:00
func ( cfg * Cfg ) setHomePath ( args CommandLineArgs ) {
2015-04-12 15:15:49 +08:00
if args . HomePath != "" {
2021-08-25 21:11:22 +08:00
cfg . HomePath = args . HomePath
HomePath = cfg . HomePath
2015-04-12 15:15:49 +08:00
return
}
2021-01-12 14:42:32 +08:00
var err error
2021-08-25 21:11:22 +08:00
cfg . HomePath , err = filepath . Abs ( "." )
2021-01-12 14:42:32 +08:00
if err != nil {
panic ( err )
}
2021-08-25 21:11:22 +08:00
HomePath = cfg . HomePath
2015-04-12 15:15:49 +08:00
// check if homepath is correct
2021-08-25 21:11:22 +08:00
if pathExists ( filepath . Join ( cfg . HomePath , "conf/defaults.ini" ) ) {
2015-04-12 15:15:49 +08:00
return
}
// try down one path
2021-08-25 21:11:22 +08:00
if pathExists ( filepath . Join ( cfg . HomePath , "../conf/defaults.ini" ) ) {
cfg . HomePath = filepath . Join ( cfg . HomePath , "../" )
HomePath = cfg . HomePath
2015-04-12 15:15:49 +08:00
}
}
2018-04-28 04:14:36 +08:00
var skipStaticRootValidation = false
2015-09-11 14:58:45 +08:00
2019-04-22 23:58:24 +08:00
func NewCfg ( ) * Cfg {
return & Cfg {
Logger : log . New ( "settings" ) ,
Raw : ini . Empty ( ) ,
}
}
2021-08-25 21:11:22 +08:00
func NewCfgFromArgs ( args CommandLineArgs ) ( * Cfg , error ) {
cfg := NewCfg ( )
if err := cfg . Load ( args ) ; err != nil {
return nil , err
}
return cfg , nil
}
2021-01-12 14:42:32 +08:00
var theCfg * Cfg
// GetCfg gets the Cfg singleton.
// XXX: This is only required for integration tests so that the configuration can be reset for each test,
// as due to how the current DI framework functions, we can't create a new Cfg object every time (the services
// constituting the DI graph, and referring to a Cfg instance, get created only once).
func GetCfg ( ) * Cfg {
if theCfg != nil {
return theCfg
}
2021-08-25 21:11:22 +08:00
theCfg , err := NewCfgFromArgs ( CommandLineArgs { } )
if err != nil {
panic ( err )
}
2021-01-12 14:42:32 +08:00
return theCfg
}
2019-04-22 23:58:24 +08:00
func ( cfg * Cfg ) validateStaticRootPath ( ) error {
2015-09-11 14:58:45 +08:00
if skipStaticRootValidation {
return nil
2015-09-10 19:34:32 +08:00
}
2017-10-02 02:02:25 +08:00
if _ , err := os . Stat ( path . Join ( StaticRootPath , "build" ) ) ; err != nil {
2019-04-22 23:58:24 +08:00
cfg . Logger . Error ( "Failed to detect generated javascript files in public/build" )
2015-09-10 19:34:32 +08:00
}
2017-10-02 02:02:25 +08:00
return nil
2015-09-10 19:34:32 +08:00
}
2021-08-25 21:11:22 +08:00
func ( cfg * Cfg ) Load ( args CommandLineArgs ) error {
cfg . setHomePath ( args )
2018-04-30 22:21:04 +08:00
2021-03-19 17:14:49 +08:00
// Fix for missing IANA db on Windows
_ , zoneInfoSet := os . LookupEnv ( zoneInfo )
if runtime . GOOS == "windows" && ! zoneInfoSet {
if err := os . Setenv ( zoneInfo , filepath . Join ( HomePath , "tools" , "zoneinfo.zip" ) ) ; err != nil {
cfg . Logger . Error ( "Can't set ZONEINFO environment variable" , "err" , err )
}
}
2018-10-12 13:55:36 +08:00
iniFile , err := cfg . loadConfiguration ( args )
2018-03-29 00:03:33 +08:00
if err != nil {
return err
}
2015-04-09 18:16:59 +08:00
2018-04-30 22:21:04 +08:00
cfg . Raw = iniFile
2020-11-13 16:52:38 +08:00
// Temporarily keep global, to make refactor in steps
2018-04-30 22:21:04 +08:00
Raw = cfg . Raw
2020-06-11 22:14:05 +08:00
cfg . BuildVersion = BuildVersion
cfg . BuildCommit = BuildCommit
cfg . BuildStamp = BuildStamp
cfg . BuildBranch = BuildBranch
cfg . IsEnterprise = IsEnterprise
cfg . Packaging = Packaging
2020-12-16 02:09:04 +08:00
cfg . ErrTemplateName = "error"
2020-12-11 18:44:44 +08:00
2020-09-08 17:33:04 +08:00
Env = valueAsString ( iniFile . Section ( "" ) , "app_mode" , "development" )
2020-12-11 18:44:44 +08:00
cfg . Env = Env
2020-09-08 17:33:04 +08:00
InstanceName = valueAsString ( iniFile . Section ( "" ) , "instance_name" , "unknown_instance_name" )
plugins := valueAsString ( iniFile . Section ( "paths" ) , "plugins" , "" )
2021-02-10 20:31:47 +08:00
cfg . PluginsPath = makeAbsolute ( plugins , HomePath )
2020-05-04 16:57:55 +08:00
cfg . BundledPluginsPath = makeAbsolute ( "plugins-bundled" , HomePath )
2020-09-08 17:33:04 +08:00
provisioning := valueAsString ( iniFile . Section ( "paths" ) , "provisioning" , "" )
2020-03-20 16:40:14 +08:00
cfg . ProvisioningPath = makeAbsolute ( provisioning , HomePath )
2020-09-08 17:33:04 +08:00
2021-02-01 17:13:09 +08:00
if err := cfg . readServerSettings ( iniFile ) ; err != nil {
2020-08-26 19:11:57 +08:00
return err
}
2021-07-15 20:30:06 +08:00
if err := readDataProxySettings ( iniFile , cfg ) ; err != nil {
return err
2021-07-07 18:13:53 +08:00
}
2020-08-26 19:11:57 +08:00
if err := readSecuritySettings ( iniFile , cfg ) ; err != nil {
return err
}
2020-12-11 18:44:44 +08:00
if err := readSnapshotsSettings ( cfg , iniFile ) ; err != nil {
2020-08-26 19:11:57 +08:00
return err
}
// read dashboard settings
dashboards := iniFile . Section ( "dashboards" )
DashboardVersionsToKeep = dashboards . Key ( "versions_to_keep" ) . MustInt ( 20 )
2020-09-08 17:33:04 +08:00
MinRefreshInterval = valueAsString ( dashboards , "min_refresh_interval" , "5s" )
2019-05-27 23:47:29 +08:00
2020-08-26 19:11:57 +08:00
cfg . DefaultHomeDashboardPath = dashboards . Key ( "default_home_dashboard_path" ) . MustString ( "" )
2014-10-04 19:33:20 +08:00
2020-08-26 19:11:57 +08:00
if err := readUserSettings ( iniFile , cfg ) ; err != nil {
return err
}
if err := readAuthSettings ( iniFile , cfg ) ; err != nil {
return err
}
2021-08-25 21:11:22 +08:00
if err := cfg . readRenderingSettings ( iniFile ) ; err != nil {
2020-08-26 19:11:57 +08:00
return err
}
cfg . TempDataLifetime = iniFile . Section ( "paths" ) . Key ( "temp_data_lifetime" ) . MustDuration ( time . Second * 3600 * 24 )
cfg . MetricsEndpointEnabled = iniFile . Section ( "metrics" ) . Key ( "enabled" ) . MustBool ( true )
2020-09-08 17:33:04 +08:00
cfg . MetricsEndpointBasicAuthUsername = valueAsString ( iniFile . Section ( "metrics" ) , "basic_auth_username" , "" )
cfg . MetricsEndpointBasicAuthPassword = valueAsString ( iniFile . Section ( "metrics" ) , "basic_auth_password" , "" )
2020-08-26 19:11:57 +08:00
cfg . MetricsEndpointDisableTotalStats = iniFile . Section ( "metrics" ) . Key ( "disable_total_stats" ) . MustBool ( false )
analytics := iniFile . Section ( "analytics" )
2021-03-08 14:02:49 +08:00
cfg . CheckForUpdates = analytics . Key ( "check_for_updates" ) . MustBool ( true )
2020-08-26 19:11:57 +08:00
GoogleAnalyticsId = analytics . Key ( "google_analytics_ua_id" ) . String ( )
GoogleTagManagerId = analytics . Key ( "google_tag_manager_id" ) . String ( )
2021-07-09 17:45:25 +08:00
RudderstackWriteKey = analytics . Key ( "rudderstack_write_key" ) . String ( )
RudderstackDataPlaneUrl = analytics . Key ( "rudderstack_data_plane_url" ) . String ( )
2021-03-10 17:14:00 +08:00
cfg . ReportingEnabled = analytics . Key ( "reporting_enabled" ) . MustBool ( true )
cfg . ReportingDistributor = analytics . Key ( "reporting_distributor" ) . MustString ( "grafana-labs" )
if len ( cfg . ReportingDistributor ) >= 100 {
cfg . ReportingDistributor = cfg . ReportingDistributor [ : 100 ]
2021-02-10 17:07:32 +08:00
}
2020-08-26 19:11:57 +08:00
if err := readAlertingSettings ( iniFile ) ; err != nil {
return err
2019-08-16 23:06:54 +08:00
}
2020-08-26 19:11:57 +08:00
2021-08-13 20:14:36 +08:00
if err := cfg . readUnifiedAlertingSettings ( iniFile ) ; err != nil {
return err
}
2020-08-26 19:11:57 +08:00
explore := iniFile . Section ( "explore" )
ExploreEnabled = explore . Key ( "enabled" ) . MustBool ( true )
panelsSection := iniFile . Section ( "panels" )
cfg . DisableSanitizeHtml = panelsSection . Key ( "disable_sanitize_html" ) . MustBool ( false )
pluginsSection := iniFile . Section ( "plugins" )
cfg . PluginsEnableAlpha = pluginsSection . Key ( "enable_alpha" ) . MustBool ( false )
cfg . PluginsAppsSkipVerifyTLS = pluginsSection . Key ( "app_tls_skip_verify_insecure" ) . MustBool ( false )
cfg . PluginSettings = extractPluginSettings ( iniFile . Sections ( ) )
pluginsAllowUnsigned := pluginsSection . Key ( "allow_loading_unsigned_plugins" ) . MustString ( "" )
for _ , plug := range strings . Split ( pluginsAllowUnsigned , "," ) {
plug = strings . TrimSpace ( plug )
cfg . PluginsAllowUnsigned = append ( cfg . PluginsAllowUnsigned , plug )
2017-04-27 14:54:21 +08:00
}
2021-05-20 16:42:26 +08:00
cfg . PluginCatalogURL = pluginsSection . Key ( "plugin_catalog_url" ) . MustString ( "https://grafana.com/grafana/plugins/" )
cfg . PluginAdminEnabled = pluginsSection . Key ( "plugin_admin_enabled" ) . MustBool ( false )
2021-05-27 18:45:06 +08:00
cfg . PluginAdminExternalManageEnabled = pluginsSection . Key ( "plugin_admin_external_manage_enabled" ) . MustBool ( false )
2015-01-27 17:09:54 +08:00
2020-08-26 19:11:57 +08:00
// Read and populate feature toggles list
featureTogglesSection := iniFile . Section ( "feature_toggles" )
cfg . FeatureToggles = make ( map [ string ] bool )
2020-09-08 17:33:04 +08:00
featuresTogglesStr := valueAsString ( featureTogglesSection , "enable" , "" )
2020-08-26 19:11:57 +08:00
for _ , feature := range util . SplitString ( featuresTogglesStr ) {
cfg . FeatureToggles [ feature ] = true
}
// check old location for this option
if panelsSection . Key ( "enable_alpha" ) . MustBool ( false ) {
cfg . PluginsEnableAlpha = true
}
cfg . readLDAPConfig ( )
2021-03-12 21:30:21 +08:00
cfg . handleAWSConfig ( )
2021-05-12 22:23:37 +08:00
cfg . readAzureSettings ( )
2020-08-26 19:11:57 +08:00
cfg . readSessionConfig ( )
cfg . readSmtpSettings ( )
cfg . readQuotaSettings ( )
2020-09-02 14:07:31 +08:00
cfg . readAnnotationSettings ( )
2021-01-23 01:27:33 +08:00
cfg . readExpressionsSettings ( )
2020-10-19 22:58:16 +08:00
if err := cfg . readGrafanaEnvironmentMetrics ( ) ; err != nil {
return err
}
2020-08-26 19:11:57 +08:00
2020-12-28 19:24:42 +08:00
cfg . readDataSourcesSettings ( )
2020-08-26 19:11:57 +08:00
if VerifyEmailEnabled && ! cfg . Smtp . Enabled {
log . Warnf ( "require_email_validation is enabled but smtp is disabled" )
}
// check old key name
2020-09-08 17:33:04 +08:00
GrafanaComUrl = valueAsString ( iniFile . Section ( "grafana_net" ) , "url" , "" )
2020-08-26 19:11:57 +08:00
if GrafanaComUrl == "" {
2020-09-08 17:33:04 +08:00
GrafanaComUrl = valueAsString ( iniFile . Section ( "grafana_com" ) , "url" , "https://grafana.com" )
2020-08-26 19:11:57 +08:00
}
2021-06-08 22:08:25 +08:00
cfg . GrafanaComURL = GrafanaComUrl
2020-08-26 19:11:57 +08:00
imageUploadingSection := iniFile . Section ( "external_image_storage" )
2021-03-10 19:41:29 +08:00
cfg . ImageUploadProvider = valueAsString ( imageUploadingSection , "provider" , "" )
ImageUploadProvider = cfg . ImageUploadProvider
2017-01-16 19:43:59 +08:00
2020-08-26 19:11:57 +08:00
enterprise := iniFile . Section ( "enterprise" )
2020-09-08 17:33:04 +08:00
cfg . EnterpriseLicensePath = valueAsString ( enterprise , "license_path" , filepath . Join ( cfg . DataPath , "license.jwt" ) )
2015-09-11 14:58:45 +08:00
2020-08-26 19:11:57 +08:00
cacheServer := iniFile . Section ( "remote_cache" )
2020-09-08 17:33:04 +08:00
dbName := valueAsString ( cacheServer , "type" , "database" )
connStr := valueAsString ( cacheServer , "connstr" , "" )
2020-08-26 19:11:57 +08:00
cfg . RemoteCacheOptions = & RemoteCacheOptions {
Name : dbName ,
ConnStr : connStr ,
}
2015-01-27 17:09:54 +08:00
2021-07-21 23:54:05 +08:00
geomapSection := iniFile . Section ( "geomap" )
2021-07-22 04:48:20 +08:00
basemapJSON := valueAsString ( geomapSection , "default_baselayer_config" , "" )
2021-07-21 23:54:05 +08:00
if basemapJSON != "" {
2021-07-22 04:48:20 +08:00
layer := make ( map [ string ] interface { } )
err = json . Unmarshal ( [ ] byte ( basemapJSON ) , & layer )
2021-07-21 23:54:05 +08:00
if err != nil {
2021-07-22 04:48:20 +08:00
cfg . Logger . Error ( "Error reading json from default_baselayer_config" , "error" , err )
} else {
cfg . GeomapDefaultBaseLayerConfig = layer
2021-07-21 23:54:05 +08:00
}
}
2021-07-22 04:48:20 +08:00
cfg . GeomapEnableCustomBaseLayers = geomapSection . Key ( "enable_custom_baselayers" ) . MustBool ( true )
2021-07-21 23:54:05 +08:00
2020-09-07 22:19:33 +08:00
cfg . readDateFormats ( )
2020-11-12 19:29:43 +08:00
cfg . readSentryConfig ( )
2020-09-07 22:19:33 +08:00
2021-05-28 03:03:18 +08:00
if err := cfg . readLiveSettings ( iniFile ) ; err != nil {
return err
}
2021-08-25 21:11:22 +08:00
cfg . LogConfigSources ( )
2020-08-26 19:11:57 +08:00
return nil
}
2020-09-08 17:33:04 +08:00
func valueAsString ( section * ini . Section , keyName string , defaultValue string ) string {
return section . Key ( keyName ) . MustString ( defaultValue )
2020-08-26 19:11:57 +08:00
}
type RemoteCacheOptions struct {
Name string
ConnStr string
}
func ( cfg * Cfg ) readLDAPConfig ( ) {
ldapSec := cfg . Raw . Section ( "auth.ldap" )
LDAPConfigFile = ldapSec . Key ( "config_file" ) . String ( )
LDAPSyncCron = ldapSec . Key ( "sync_cron" ) . String ( )
LDAPEnabled = ldapSec . Key ( "enabled" ) . MustBool ( false )
2020-12-11 18:44:44 +08:00
cfg . LDAPEnabled = LDAPEnabled
2020-08-26 19:11:57 +08:00
LDAPActiveSyncEnabled = ldapSec . Key ( "active_sync_enabled" ) . MustBool ( false )
LDAPAllowSignup = ldapSec . Key ( "allow_sign_up" ) . MustBool ( true )
2020-12-11 18:44:44 +08:00
cfg . LDAPAllowSignup = LDAPAllowSignup
2020-08-26 19:11:57 +08:00
}
2021-03-12 21:30:21 +08:00
func ( cfg * Cfg ) handleAWSConfig ( ) {
2021-02-25 01:08:13 +08:00
awsPluginSec := cfg . Raw . Section ( "aws" )
cfg . AWSAssumeRoleEnabled = awsPluginSec . Key ( "assume_role_enabled" ) . MustBool ( true )
2021-03-12 21:30:21 +08:00
allowedAuthProviders := awsPluginSec . Key ( "allowed_auth_providers" ) . MustString ( "default,keys,credentials" )
2021-02-25 01:08:13 +08:00
for _ , authProvider := range strings . Split ( allowedAuthProviders , "," ) {
authProvider = strings . TrimSpace ( authProvider )
if authProvider != "" {
cfg . AWSAllowedAuthProviders = append ( cfg . AWSAllowedAuthProviders , authProvider )
}
}
2021-03-10 14:41:22 +08:00
cfg . AWSListMetricsPageLimit = awsPluginSec . Key ( "list_metrics_page_limit" ) . MustInt ( 500 )
2021-03-12 21:30:21 +08:00
// Also set environment variables that can be used by core plugins
err := os . Setenv ( awsds . AssumeRoleEnabledEnvVarKeyName , strconv . FormatBool ( cfg . AWSAssumeRoleEnabled ) )
if err != nil {
cfg . Logger . Error ( fmt . Sprintf ( "could not set environment variable '%s'" , awsds . AssumeRoleEnabledEnvVarKeyName ) , err )
}
err = os . Setenv ( awsds . AllowedAuthProvidersEnvVarKeyName , allowedAuthProviders )
if err != nil {
cfg . Logger . Error ( fmt . Sprintf ( "could not set environment variable '%s'" , awsds . AllowedAuthProvidersEnvVarKeyName ) , err )
}
2021-02-25 01:08:13 +08:00
}
2020-08-26 19:11:57 +08:00
func ( cfg * Cfg ) readSessionConfig ( ) {
sec , _ := cfg . Raw . GetSection ( "session" )
if sec != nil {
cfg . Logger . Warn (
"[Removed] Session setting was removed in v6.2, use remote_cache option instead" ,
)
}
}
func ( cfg * Cfg ) initLogging ( file * ini . File ) error {
2020-09-08 17:33:04 +08:00
logModeStr := valueAsString ( file . Section ( "log" ) , "mode" , "console" )
2020-08-26 19:11:57 +08:00
// split on comma
logModes := strings . Split ( logModeStr , "," )
// also try space
if len ( logModes ) == 1 {
logModes = strings . Split ( logModeStr , " " )
}
2020-09-08 17:33:04 +08:00
logsPath := valueAsString ( file . Section ( "paths" ) , "logs" , "" )
2020-08-26 19:11:57 +08:00
cfg . LogsPath = makeAbsolute ( logsPath , HomePath )
return log . ReadLoggingConfig ( logModes , cfg . LogsPath , file )
}
func ( cfg * Cfg ) LogConfigSources ( ) {
var text bytes . Buffer
for _ , file := range configFiles {
cfg . Logger . Info ( "Config loaded from" , "file" , file )
}
if len ( appliedCommandLineProperties ) > 0 {
for _ , prop := range appliedCommandLineProperties {
cfg . Logger . Info ( "Config overridden from command line" , "arg" , prop )
}
}
if len ( appliedEnvOverrides ) > 0 {
text . WriteString ( "\tEnvironment variables used:\n" )
for _ , prop := range appliedEnvOverrides {
cfg . Logger . Info ( "Config overridden from Environment variable" , "var" , prop )
}
}
cfg . Logger . Info ( "Path Home" , "path" , HomePath )
cfg . Logger . Info ( "Path Data" , "path" , cfg . DataPath )
cfg . Logger . Info ( "Path Logs" , "path" , cfg . LogsPath )
2021-02-10 20:31:47 +08:00
cfg . Logger . Info ( "Path Plugins" , "path" , cfg . PluginsPath )
2020-08-26 19:11:57 +08:00
cfg . Logger . Info ( "Path Provisioning" , "path" , cfg . ProvisioningPath )
2020-12-11 18:44:44 +08:00
cfg . Logger . Info ( "App mode " + cfg . Env )
2020-08-26 19:11:57 +08:00
}
type DynamicSection struct {
section * ini . Section
Logger log . Logger
}
// Key dynamically overrides keys with environment variables.
// As a side effect, the value of the setting key will be updated if an environment variable is present.
func ( s * DynamicSection ) Key ( k string ) * ini . Key {
envKey := envKey ( s . section . Name ( ) , k )
envValue := os . Getenv ( envKey )
key := s . section . Key ( k )
if len ( envValue ) == 0 {
return key
}
2017-01-16 19:43:59 +08:00
2020-08-26 19:11:57 +08:00
key . SetValue ( envValue )
2021-05-10 23:03:10 +08:00
s . Logger . Info ( "Config overridden from Environment variable" , "var" , fmt . Sprintf ( "%s=%s" , envKey , RedactedValue ( envKey , envValue ) ) )
2020-08-26 19:11:57 +08:00
return key
}
// SectionWithEnvOverrides dynamically overrides keys with environment variables.
// As a side effect, the value of the setting key will be updated if an environment variable is present.
func ( cfg * Cfg ) SectionWithEnvOverrides ( s string ) * DynamicSection {
return & DynamicSection { cfg . Raw . Section ( s ) , cfg . Logger }
}
func readSecuritySettings ( iniFile * ini . File , cfg * Cfg ) error {
2018-04-30 22:21:04 +08:00
security := iniFile . Section ( "security" )
2020-09-08 17:33:04 +08:00
SecretKey = valueAsString ( security , "secret_key" , "" )
2015-05-01 14:40:13 +08:00
DisableGravatar = security . Key ( "disable_gravatar" ) . MustBool ( true )
2018-04-30 22:21:04 +08:00
cfg . DisableBruteForceLoginProtection = security . Key ( "disable_brute_force_login_protection" ) . MustBool ( false )
2015-05-01 14:40:13 +08:00
2019-02-06 04:09:55 +08:00
CookieSecure = security . Key ( "cookie_secure" ) . MustBool ( false )
cfg . CookieSecure = CookieSecure
2020-09-08 17:33:04 +08:00
samesiteString := valueAsString ( security , "cookie_samesite" , "lax" )
2019-02-06 04:09:55 +08:00
2020-01-15 00:41:54 +08:00
if samesiteString == "disabled" {
CookieSameSiteDisabled = true
cfg . CookieSameSiteDisabled = CookieSameSiteDisabled
2019-02-06 04:09:55 +08:00
} else {
2020-01-15 00:41:54 +08:00
validSameSiteValues := map [ string ] http . SameSite {
"lax" : http . SameSiteLaxMode ,
"strict" : http . SameSiteStrictMode ,
"none" : http . SameSiteNoneMode ,
}
if samesite , ok := validSameSiteValues [ samesiteString ] ; ok {
CookieSameSiteMode = samesite
cfg . CookieSameSiteMode = CookieSameSiteMode
} else {
CookieSameSiteMode = http . SameSiteLaxMode
cfg . CookieSameSiteMode = CookieSameSiteMode
}
2019-02-06 04:09:55 +08:00
}
2020-12-11 18:44:44 +08:00
cfg . AllowEmbedding = security . Key ( "allow_embedding" ) . MustBool ( false )
2019-05-06 15:56:23 +08:00
2020-12-11 18:44:44 +08:00
cfg . ContentTypeProtectionHeader = security . Key ( "x_content_type_options" ) . MustBool ( true )
cfg . XSSProtectionHeader = security . Key ( "x_xss_protection" ) . MustBool ( true )
cfg . StrictTransportSecurity = security . Key ( "strict_transport_security" ) . MustBool ( false )
cfg . StrictTransportSecurityMaxAge = security . Key ( "strict_transport_security_max_age_seconds" ) . MustInt ( 86400 )
cfg . StrictTransportSecurityPreload = security . Key ( "strict_transport_security_preload" ) . MustBool ( false )
cfg . StrictTransportSecuritySubDomains = security . Key ( "strict_transport_security_subdomains" ) . MustBool ( false )
2021-01-12 14:42:32 +08:00
cfg . CSPEnabled = security . Key ( "content_security_policy" ) . MustBool ( false )
cfg . CSPTemplate = security . Key ( "content_security_policy_template" ) . MustString ( "" )
2019-06-12 19:15:50 +08:00
2020-08-26 19:11:57 +08:00
// read data source proxy whitelist
2015-09-09 23:21:25 +08:00
DataProxyWhiteList = make ( map [ string ] bool )
2020-09-08 17:33:04 +08:00
securityStr := valueAsString ( security , "data_source_proxy_whitelist" , "" )
2020-08-26 19:11:57 +08:00
for _ , hostAndIP := range util . SplitString ( securityStr ) {
DataProxyWhiteList [ hostAndIP ] = true
2015-09-09 23:21:25 +08:00
}
2015-01-27 22:45:27 +08:00
// admin
2019-11-08 18:11:03 +08:00
cfg . DisableInitAdminCreation = security . Key ( "disable_initial_admin_creation" ) . MustBool ( false )
2020-12-16 02:09:04 +08:00
cfg . AdminUser = valueAsString ( security , "admin_user" , "" )
cfg . AdminPassword = valueAsString ( security , "admin_password" , "" )
2015-01-27 22:45:27 +08:00
2020-08-26 19:11:57 +08:00
return nil
}
2016-09-28 21:27:08 +08:00
2020-09-14 21:57:38 +08:00
func readAuthSettings ( iniFile * ini . File , cfg * Cfg ) ( err error ) {
2018-04-30 22:21:04 +08:00
auth := iniFile . Section ( "auth" )
2019-02-06 04:09:55 +08:00
2020-12-16 02:09:04 +08:00
cfg . LoginCookieName = valueAsString ( auth , "login_cookie_name" , "grafana_session" )
2020-09-14 21:57:38 +08:00
maxInactiveDaysVal := auth . Key ( "login_maximum_inactive_lifetime_days" ) . MustString ( "" )
if maxInactiveDaysVal != "" {
maxInactiveDaysVal = fmt . Sprintf ( "%sd" , maxInactiveDaysVal )
cfg . Logger . Warn ( "[Deprecated] the configuration setting 'login_maximum_inactive_lifetime_days' is deprecated, please use 'login_maximum_inactive_lifetime_duration' instead" )
} else {
maxInactiveDaysVal = "7d"
}
maxInactiveDurationVal := valueAsString ( auth , "login_maximum_inactive_lifetime_duration" , maxInactiveDaysVal )
2020-11-03 02:26:19 +08:00
cfg . LoginMaxInactiveLifetime , err = gtime . ParseDuration ( maxInactiveDurationVal )
2020-09-14 21:57:38 +08:00
if err != nil {
return err
}
maxLifetimeDaysVal := auth . Key ( "login_maximum_lifetime_days" ) . MustString ( "" )
if maxLifetimeDaysVal != "" {
maxLifetimeDaysVal = fmt . Sprintf ( "%sd" , maxLifetimeDaysVal )
cfg . Logger . Warn ( "[Deprecated] the configuration setting 'login_maximum_lifetime_days' is deprecated, please use 'login_maximum_lifetime_duration' instead" )
} else {
2021-01-07 15:58:46 +08:00
maxLifetimeDaysVal = "30d"
2020-09-14 21:57:38 +08:00
}
maxLifetimeDurationVal := valueAsString ( auth , "login_maximum_lifetime_duration" , maxLifetimeDaysVal )
2020-11-03 02:26:19 +08:00
cfg . LoginMaxLifetime , err = gtime . ParseDuration ( maxLifetimeDurationVal )
2020-09-14 21:57:38 +08:00
if err != nil {
return err
}
2019-02-06 04:09:55 +08:00
2019-06-26 14:47:03 +08:00
cfg . ApiKeyMaxSecondsToLive = auth . Key ( "api_key_max_seconds_to_live" ) . MustInt64 ( - 1 )
2019-02-06 04:09:55 +08:00
cfg . TokenRotationIntervalMinutes = auth . Key ( "token_rotation_interval_minutes" ) . MustInt ( 10 )
if cfg . TokenRotationIntervalMinutes < 2 {
cfg . TokenRotationIntervalMinutes = 2
}
2016-09-28 21:27:08 +08:00
DisableLoginForm = auth . Key ( "disable_login_form" ) . MustBool ( false )
2017-03-29 17:33:28 +08:00
DisableSignoutMenu = auth . Key ( "disable_signout_menu" ) . MustBool ( false )
2018-05-28 22:16:48 +08:00
OAuthAutoLogin = auth . Key ( "oauth_auto_login" ) . MustBool ( false )
2020-09-01 16:57:43 +08:00
cfg . OAuthCookieMaxAge = auth . Key ( "oauth_state_cookie_max_age" ) . MustInt ( 600 )
2020-09-08 17:33:04 +08:00
SignoutRedirectUrl = valueAsString ( auth , "signout_redirect_url" , "" )
2015-01-27 22:14:53 +08:00
2020-10-08 16:03:20 +08:00
// SigV4
SigV4AuthEnabled = auth . Key ( "sigv4_auth_enabled" ) . MustBool ( false )
cfg . SigV4AuthEnabled = SigV4AuthEnabled
2015-01-27 22:45:27 +08:00
// anonymous access
2018-04-30 22:21:04 +08:00
AnonymousEnabled = iniFile . Section ( "auth.anonymous" ) . Key ( "enabled" ) . MustBool ( false )
2020-12-11 18:44:44 +08:00
cfg . AnonymousEnabled = AnonymousEnabled
cfg . AnonymousOrgName = valueAsString ( iniFile . Section ( "auth.anonymous" ) , "org_name" , "" )
cfg . AnonymousOrgRole = valueAsString ( iniFile . Section ( "auth.anonymous" ) , "org_role" , "" )
2020-06-17 13:39:50 +08:00
cfg . AnonymousHideVersion = iniFile . Section ( "auth.anonymous" ) . Key ( "hide_version" ) . MustBool ( false )
2015-01-07 23:37:24 +08:00
2020-08-26 19:11:57 +08:00
// basic auth
authBasic := iniFile . Section ( "auth.basic" )
BasicAuthEnabled = authBasic . Key ( "enabled" ) . MustBool ( true )
2020-12-11 18:44:44 +08:00
cfg . BasicAuthEnabled = BasicAuthEnabled
2020-08-26 19:11:57 +08:00
2021-03-31 23:40:44 +08:00
// JWT auth
authJWT := iniFile . Section ( "auth.jwt" )
cfg . JWTAuthEnabled = authJWT . Key ( "enabled" ) . MustBool ( false )
cfg . JWTAuthHeaderName = valueAsString ( authJWT , "header_name" , "" )
cfg . JWTAuthEmailClaim = valueAsString ( authJWT , "email_claim" , "" )
cfg . JWTAuthUsernameClaim = valueAsString ( authJWT , "username_claim" , "" )
cfg . JWTAuthExpectClaims = valueAsString ( authJWT , "expect_claims" , "{}" )
cfg . JWTAuthJWKSetURL = valueAsString ( authJWT , "jwk_set_url" , "" )
cfg . JWTAuthCacheTTL = authJWT . Key ( "cache_ttl" ) . MustDuration ( time . Minute * 60 )
cfg . JWTAuthKeyFile = valueAsString ( authJWT , "key_file" , "" )
cfg . JWTAuthJWKSetFile = valueAsString ( authJWT , "jwk_set_file" , "" )
2018-04-30 22:21:04 +08:00
authProxy := iniFile . Section ( "auth.proxy" )
2015-05-01 17:55:59 +08:00
AuthProxyEnabled = authProxy . Key ( "enabled" ) . MustBool ( false )
2020-12-11 18:44:44 +08:00
cfg . AuthProxyEnabled = AuthProxyEnabled
2019-05-17 19:57:26 +08:00
2020-12-11 18:44:44 +08:00
cfg . AuthProxyHeaderName = valueAsString ( authProxy , "header_name" , "" )
2020-09-08 17:33:04 +08:00
AuthProxyHeaderProperty = valueAsString ( authProxy , "header_property" , "" )
2020-12-11 18:44:44 +08:00
cfg . AuthProxyHeaderProperty = AuthProxyHeaderProperty
cfg . AuthProxyAutoSignUp = authProxy . Key ( "auto_sign_up" ) . MustBool ( true )
cfg . AuthProxyEnableLoginToken = authProxy . Key ( "enable_login_token" ) . MustBool ( false )
2020-08-26 19:11:57 +08:00
ldapSyncVal := authProxy . Key ( "ldap_sync_ttl" ) . MustInt ( )
syncVal := authProxy . Key ( "sync_ttl" ) . MustInt ( )
2020-12-11 18:44:44 +08:00
if ldapSyncVal != authProxySyncTTL {
cfg . AuthProxySyncTTL = ldapSyncVal
2020-08-26 19:11:57 +08:00
cfg . Logger . Warn ( "[Deprecated] the configuration setting 'ldap_sync_ttl' is deprecated, please use 'sync_ttl' instead" )
} else {
2020-12-11 18:44:44 +08:00
cfg . AuthProxySyncTTL = syncVal
2020-08-26 19:11:57 +08:00
}
2020-12-11 18:44:44 +08:00
cfg . AuthProxyWhitelist = valueAsString ( authProxy , "whitelist" , "" )
2020-08-26 19:11:57 +08:00
2020-12-11 18:44:44 +08:00
cfg . AuthProxyHeaders = make ( map [ string ] string )
2020-09-08 17:33:04 +08:00
headers := valueAsString ( authProxy , "headers" , "" )
2020-08-26 19:11:57 +08:00
for _ , propertyAndHeader := range util . SplitString ( headers ) {
split := strings . SplitN ( propertyAndHeader , ":" , 2 )
if len ( split ) == 2 {
2020-12-11 18:44:44 +08:00
cfg . AuthProxyHeaders [ split [ 0 ] ] = split [ 1 ]
2020-08-26 19:11:57 +08:00
}
}
return nil
}
func readUserSettings ( iniFile * ini . File , cfg * Cfg ) error {
users := iniFile . Section ( "users" )
AllowUserSignUp = users . Key ( "allow_sign_up" ) . MustBool ( true )
AllowUserOrgCreate = users . Key ( "allow_org_create" ) . MustBool ( true )
2021-01-07 18:36:13 +08:00
cfg . AutoAssignOrg = users . Key ( "auto_assign_org" ) . MustBool ( true )
AutoAssignOrg = cfg . AutoAssignOrg
cfg . AutoAssignOrgId = users . Key ( "auto_assign_org_id" ) . MustInt ( 1 )
AutoAssignOrgId = cfg . AutoAssignOrgId
cfg . AutoAssignOrgRole = users . Key ( "auto_assign_org_role" ) . In ( "Editor" , [ ] string { "Editor" , "Admin" , "Viewer" } )
AutoAssignOrgRole = cfg . AutoAssignOrgRole
2020-08-26 19:11:57 +08:00
VerifyEmailEnabled = users . Key ( "verify_email_enabled" ) . MustBool ( false )
2020-09-08 17:33:04 +08:00
LoginHint = valueAsString ( users , "login_hint" , "" )
PasswordHint = valueAsString ( users , "password_hint" , "" )
2020-12-11 18:44:44 +08:00
cfg . DefaultTheme = valueAsString ( users , "default_theme" , "" )
2021-04-13 21:27:51 +08:00
cfg . HomePage = valueAsString ( users , "home_page" , "" )
2020-09-08 17:33:04 +08:00
ExternalUserMngLinkUrl = valueAsString ( users , "external_manage_link_url" , "" )
ExternalUserMngLinkName = valueAsString ( users , "external_manage_link_name" , "" )
ExternalUserMngInfo = valueAsString ( users , "external_manage_info" , "" )
2020-08-26 19:11:57 +08:00
ViewersCanEdit = users . Key ( "viewers_can_edit" ) . MustBool ( false )
cfg . EditorsCanAdmin = users . Key ( "editors_can_admin" ) . MustBool ( false )
2018-05-07 16:39:16 +08:00
2020-10-13 18:30:09 +08:00
userInviteMaxLifetimeVal := valueAsString ( users , "user_invite_max_lifetime_duration" , "24h" )
2020-11-03 02:26:19 +08:00
userInviteMaxLifetimeDuration , err := gtime . ParseDuration ( userInviteMaxLifetimeVal )
2020-10-13 18:30:09 +08:00
if err != nil {
return err
}
cfg . UserInviteMaxLifetime = userInviteMaxLifetimeDuration
if cfg . UserInviteMaxLifetime < time . Minute * 15 {
return errors . New ( "the minimum supported value for the `user_invite_max_lifetime_duration` configuration is 15m (15 minutes)" )
}
2020-11-24 19:10:32 +08:00
cfg . HiddenUsers = make ( map [ string ] struct { } )
hiddenUsers := users . Key ( "hidden_users" ) . MustString ( "" )
for _ , user := range strings . Split ( hiddenUsers , "," ) {
user = strings . TrimSpace ( user )
2021-02-12 23:08:18 +08:00
if user != "" {
cfg . HiddenUsers [ user ] = struct { } { }
}
2020-11-24 19:10:32 +08:00
}
2020-08-26 19:11:57 +08:00
return nil
}
2015-06-30 15:37:52 +08:00
2021-08-25 21:11:22 +08:00
func ( cfg * Cfg ) readRenderingSettings ( iniFile * ini . File ) error {
2018-05-24 21:26:27 +08:00
renderSec := iniFile . Section ( "rendering" )
2020-09-08 17:33:04 +08:00
cfg . RendererUrl = valueAsString ( renderSec , "server_url" , "" )
cfg . RendererCallbackUrl = valueAsString ( renderSec , "callback_url" , "" )
2018-09-04 19:42:55 +08:00
if cfg . RendererCallbackUrl == "" {
cfg . RendererCallbackUrl = AppUrl
} else {
if cfg . RendererCallbackUrl [ len ( cfg . RendererCallbackUrl ) - 1 ] != '/' {
cfg . RendererCallbackUrl += "/"
}
_ , err := url . Parse ( cfg . RendererCallbackUrl )
if err != nil {
2020-08-26 19:11:57 +08:00
// XXX: Should return an error?
2020-07-23 14:14:39 +08:00
log . Fatalf ( 4 , "Invalid callback_url(%s): %s" , cfg . RendererCallbackUrl , err )
2018-09-04 19:42:55 +08:00
}
}
2020-04-27 23:25:08 +08:00
2020-09-08 17:33:04 +08:00
cfg . RendererConcurrentRequestLimit = renderSec . Key ( "concurrent_render_request_limit" ) . MustInt ( 30 )
2018-10-12 13:55:36 +08:00
cfg . ImagesDir = filepath . Join ( cfg . DataPath , "png" )
2021-05-12 23:16:57 +08:00
cfg . CSVsDir = filepath . Join ( cfg . DataPath , "csv" )
2014-11-15 00:13:33 +08:00
2020-08-26 19:11:57 +08:00
return nil
}
2015-03-23 03:14:00 +08:00
2021-08-13 20:14:36 +08:00
func ( cfg * Cfg ) readUnifiedAlertingSettings ( iniFile * ini . File ) error {
ua := iniFile . Section ( "unified_alerting" )
s := ua . Key ( "admin_config_poll_interval_seconds" ) . MustInt ( 60 )
cfg . AdminConfigPollInterval = time . Second * time . Duration ( s )
return nil
}
2020-08-26 19:11:57 +08:00
func readAlertingSettings ( iniFile * ini . File ) error {
2018-04-30 22:21:04 +08:00
alerting := iniFile . Section ( "alerting" )
2017-01-25 20:32:26 +08:00
AlertingEnabled = alerting . Key ( "enabled" ) . MustBool ( true )
2016-10-10 19:09:16 +08:00
ExecuteAlerts = alerting . Key ( "execute_alerts" ) . MustBool ( true )
2018-09-25 18:17:04 +08:00
AlertingRenderLimit = alerting . Key ( "concurrent_render_limit" ) . MustInt ( 5 )
2020-09-08 17:33:04 +08:00
AlertingErrorOrTimeout = valueAsString ( alerting , "error_or_timeout" , "alerting" )
AlertingNoDataOrNullValues = valueAsString ( alerting , "nodata_or_nullvalues" , "no_data" )
2016-04-29 20:35:58 +08:00
2019-04-30 18:05:38 +08:00
evaluationTimeoutSeconds := alerting . Key ( "evaluation_timeout_seconds" ) . MustInt64 ( 30 )
AlertingEvaluationTimeout = time . Second * time . Duration ( evaluationTimeoutSeconds )
notificationTimeoutSeconds := alerting . Key ( "notification_timeout_seconds" ) . MustInt64 ( 30 )
AlertingNotificationTimeout = time . Second * time . Duration ( notificationTimeoutSeconds )
2019-03-29 13:58:37 +08:00
AlertingMaxAttempts = alerting . Key ( "max_attempts" ) . MustInt ( 3 )
2020-01-14 17:13:34 +08:00
AlertingMinInterval = alerting . Key ( "min_interval_seconds" ) . MustInt64 ( 1 )
2019-03-29 13:58:37 +08:00
2020-08-26 19:11:57 +08:00
return nil
}
2019-04-12 19:46:42 +08:00
2020-12-11 18:44:44 +08:00
func readSnapshotsSettings ( cfg * Cfg , iniFile * ini . File ) error {
2020-08-26 19:11:57 +08:00
snapshots := iniFile . Section ( "snapshots" )
2020-09-08 17:33:04 +08:00
ExternalSnapshotUrl = valueAsString ( snapshots , "external_snapshot_url" , "" )
ExternalSnapshotName = valueAsString ( snapshots , "external_snapshot_name" , "" )
2020-08-26 19:11:57 +08:00
ExternalEnabled = snapshots . Key ( "external_enabled" ) . MustBool ( true )
SnapShotRemoveExpired = snapshots . Key ( "snapshot_remove_expired" ) . MustBool ( true )
2020-12-11 18:44:44 +08:00
cfg . SnapshotPublicMode = snapshots . Key ( "public_mode" ) . MustBool ( false )
2016-05-27 19:52:19 +08:00
2020-08-26 19:11:57 +08:00
return nil
}
2018-11-01 19:07:11 +08:00
2021-02-01 17:13:09 +08:00
func ( cfg * Cfg ) readServerSettings ( iniFile * ini . File ) error {
2020-08-26 19:11:57 +08:00
server := iniFile . Section ( "server" )
var err error
AppUrl , AppSubUrl , err = parseAppUrlAndSubUrl ( server )
2019-04-25 14:29:07 +08:00
if err != nil {
return err
}
2020-08-26 19:11:57 +08:00
ServeFromSubPath = server . Key ( "serve_from_sub_path" ) . MustBool ( false )
2018-11-01 19:07:11 +08:00
2020-11-13 16:52:38 +08:00
cfg . AppURL = AppUrl
cfg . AppSubURL = AppSubUrl
2020-08-26 19:11:57 +08:00
cfg . ServeFromSubPath = ServeFromSubPath
2020-12-11 18:44:44 +08:00
cfg . Protocol = HTTPScheme
2021-02-01 17:13:09 +08:00
2020-09-08 17:33:04 +08:00
protocolStr := valueAsString ( server , "protocol" , "http" )
2020-08-26 19:11:57 +08:00
if protocolStr == "https" {
2020-12-11 18:44:44 +08:00
cfg . Protocol = HTTPSScheme
2021-03-10 19:41:29 +08:00
cfg . CertFile = server . Key ( "cert_file" ) . String ( )
cfg . KeyFile = server . Key ( "cert_key" ) . String ( )
2019-04-25 14:29:07 +08:00
}
2020-08-26 19:11:57 +08:00
if protocolStr == "h2" {
2020-12-11 18:44:44 +08:00
cfg . Protocol = HTTP2Scheme
2021-03-10 19:41:29 +08:00
cfg . CertFile = server . Key ( "cert_file" ) . String ( )
cfg . KeyFile = server . Key ( "cert_key" ) . String ( )
2019-03-04 04:48:00 +08:00
}
2020-08-26 19:11:57 +08:00
if protocolStr == "socket" {
2020-12-11 18:44:44 +08:00
cfg . Protocol = SocketScheme
cfg . SocketPath = server . Key ( "socket" ) . String ( )
2015-04-08 14:59:12 +08:00
}
2015-03-03 17:18:24 +08:00
2020-12-11 18:44:44 +08:00
cfg . Domain = valueAsString ( server , "domain" , "localhost" )
2021-03-10 19:41:29 +08:00
cfg . HTTPAddr = valueAsString ( server , "http_addr" , DefaultHTTPAddr )
cfg . HTTPPort = valueAsString ( server , "http_port" , "3000" )
2020-12-11 18:44:44 +08:00
cfg . RouterLogging = server . Key ( "router_logging" ) . MustBool ( false )
2015-04-09 18:16:59 +08:00
2021-03-10 19:41:29 +08:00
cfg . EnableGzip = server . Key ( "enable_gzip" ) . MustBool ( false )
cfg . EnforceDomain = server . Key ( "enforce_domain" ) . MustBool ( false )
2020-09-08 17:33:04 +08:00
staticRoot := valueAsString ( server , "static_root_path" , "" )
2020-08-26 19:11:57 +08:00
StaticRootPath = makeAbsolute ( staticRoot , HomePath )
cfg . StaticRootPath = StaticRootPath
2020-01-10 22:33:54 +08:00
2020-08-26 19:11:57 +08:00
if err := cfg . validateStaticRootPath ( ) ; err != nil {
return err
2020-01-10 22:33:54 +08:00
}
2021-02-01 17:13:09 +08:00
cdnURL := valueAsString ( server , "cdn_url" , "" )
if cdnURL != "" {
cfg . CDNRootURL , err = url . Parse ( cdnURL )
if err != nil {
return err
}
}
2021-03-19 18:21:52 +08:00
cfg . ReadTimeout = server . Key ( "read_timeout" ) . MustDuration ( 0 )
2020-08-26 19:11:57 +08:00
return nil
2020-01-10 22:33:54 +08:00
}
2020-12-28 19:24:42 +08:00
2021-02-01 17:13:09 +08:00
// GetContentDeliveryURL returns full content delivery URL with /<edition>/<version> added to URL
func ( cfg * Cfg ) GetContentDeliveryURL ( prefix string ) string {
if cfg . CDNRootURL != nil {
url := * cfg . CDNRootURL
preReleaseFolder := ""
url . Path = path . Join ( url . Path , prefix , preReleaseFolder , cfg . BuildVersion )
2021-02-02 19:34:59 +08:00
return url . String ( ) + "/"
2021-02-01 17:13:09 +08:00
}
return ""
}
2020-12-28 19:24:42 +08:00
func ( cfg * Cfg ) readDataSourcesSettings ( ) {
datasources := cfg . Raw . Section ( "datasources" )
cfg . DataSourceLimit = datasources . Key ( "datasource_limit" ) . MustInt ( 5000 )
}
2021-05-28 03:03:18 +08:00
2021-07-01 14:30:09 +08:00
func GetAllowedOriginGlobs ( originPatterns [ ] string ) ( [ ] glob . Glob , error ) {
var originGlobs [ ] glob . Glob
allowedOrigins := originPatterns
for _ , originPattern := range allowedOrigins {
g , err := glob . Compile ( originPattern )
if err != nil {
return nil , fmt . Errorf ( "error parsing origin pattern: %v" , err )
}
originGlobs = append ( originGlobs , g )
}
return originGlobs , nil
}
2021-05-28 03:03:18 +08:00
func ( cfg * Cfg ) readLiveSettings ( iniFile * ini . File ) error {
section := iniFile . Section ( "live" )
cfg . LiveMaxConnections = section . Key ( "max_connections" ) . MustInt ( 100 )
if cfg . LiveMaxConnections < - 1 {
return fmt . Errorf ( "unexpected value %d for [live] max_connections" , cfg . LiveMaxConnections )
}
2021-06-24 16:07:09 +08:00
cfg . LiveHAEngine = section . Key ( "ha_engine" ) . MustString ( "" )
switch cfg . LiveHAEngine {
case "" , "redis" :
default :
return fmt . Errorf ( "unsupported live HA engine type: %s" , cfg . LiveHAEngine )
}
cfg . LiveHAEngineAddress = section . Key ( "ha_engine_address" ) . MustString ( "127.0.0.1:6379" )
2021-07-01 14:30:09 +08:00
var originPatterns [ ] string
allowedOrigins := section . Key ( "allowed_origins" ) . MustString ( "" )
for _ , originPattern := range strings . Split ( allowedOrigins , "," ) {
originPattern = strings . TrimSpace ( originPattern )
if originPattern == "" {
continue
}
originPatterns = append ( originPatterns , originPattern )
}
_ , err := GetAllowedOriginGlobs ( originPatterns )
if err != nil {
return err
}
cfg . LiveAllowedOrigins = originPatterns
2021-05-28 03:03:18 +08:00
return nil
}