setting.go 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564
  1. // Copyright 2014 Unknwon
  2. // Copyright 2014 Torkel Ödegaard
  3. package setting
  4. import (
  5. "bytes"
  6. "encoding/json"
  7. "fmt"
  8. "net/url"
  9. "os"
  10. "path"
  11. "path/filepath"
  12. "regexp"
  13. "runtime"
  14. "strings"
  15. "github.com/macaron-contrib/session"
  16. "gopkg.in/ini.v1"
  17. "github.com/grafana/grafana/pkg/log"
  18. "github.com/grafana/grafana/pkg/util"
  19. )
  20. type Scheme string
  21. const (
  22. HTTP Scheme = "http"
  23. HTTPS Scheme = "https"
  24. )
  25. const (
  26. DEV string = "development"
  27. PROD string = "production"
  28. TEST string = "test"
  29. )
  30. var (
  31. // App settings.
  32. Env string = DEV
  33. AppUrl string
  34. AppSubUrl string
  35. // build
  36. BuildVersion string
  37. BuildCommit string
  38. BuildStamp int64
  39. // Paths
  40. LogsPath string
  41. HomePath string
  42. DataPath string
  43. // Log settings.
  44. LogModes []string
  45. LogConfigs []util.DynMap
  46. // Http server options
  47. Protocol Scheme
  48. Domain string
  49. HttpAddr, HttpPort string
  50. SshPort int
  51. CertFile, KeyFile string
  52. RouterLogging bool
  53. StaticRootPath string
  54. EnableGzip bool
  55. EnforceDomain bool
  56. // Security settings.
  57. SecretKey string
  58. LogInRememberDays int
  59. CookieUserName string
  60. CookieRememberName string
  61. DisableGravatar bool
  62. EmailCodeValidMinutes int
  63. // User settings
  64. AllowUserSignUp bool
  65. AllowUserOrgCreate bool
  66. AutoAssignOrg bool
  67. AutoAssignOrgRole string
  68. VerifyEmailEnabled bool
  69. // Http auth
  70. AdminUser string
  71. AdminPassword string
  72. AnonymousEnabled bool
  73. AnonymousOrgName string
  74. AnonymousOrgRole string
  75. // Auth proxy settings
  76. AuthProxyEnabled bool
  77. AuthProxyHeaderName string
  78. AuthProxyHeaderProperty string
  79. AuthProxyAutoSignUp bool
  80. // Basic Auth
  81. BasicAuthEnabled bool
  82. // Session settings.
  83. SessionOptions session.Options
  84. // Global setting objects.
  85. Cfg *ini.File
  86. ConfRootPath string
  87. IsWindows bool
  88. // PhantomJs Rendering
  89. ImagesDir string
  90. PhantomDir string
  91. // for logging purposes
  92. configFiles []string
  93. appliedCommandLineProperties []string
  94. appliedEnvOverrides []string
  95. ReportingEnabled bool
  96. GoogleAnalyticsId string
  97. GoogleTagManagerId string
  98. // LDAP
  99. LdapEnabled bool
  100. LdapConfigFile string
  101. // SMTP email settings
  102. Smtp SmtpSettings
  103. )
  104. type CommandLineArgs struct {
  105. Config string
  106. HomePath string
  107. Args []string
  108. }
  109. func init() {
  110. IsWindows = runtime.GOOS == "windows"
  111. log.NewLogger(0, "console", `{"level": 0}`)
  112. }
  113. func parseAppUrlAndSubUrl(section *ini.Section) (string, string) {
  114. appUrl := section.Key("root_url").MustString("http://localhost:3000/")
  115. if appUrl[len(appUrl)-1] != '/' {
  116. appUrl += "/"
  117. }
  118. // Check if has app suburl.
  119. url, err := url.Parse(appUrl)
  120. if err != nil {
  121. log.Fatal(4, "Invalid root_url(%s): %s", appUrl, err)
  122. }
  123. appSubUrl := strings.TrimSuffix(url.Path, "/")
  124. return appUrl, appSubUrl
  125. }
  126. func ToAbsUrl(relativeUrl string) string {
  127. return AppUrl + relativeUrl
  128. }
  129. func applyEnvVariableOverrides() {
  130. appliedEnvOverrides = make([]string, 0)
  131. for _, section := range Cfg.Sections() {
  132. for _, key := range section.Keys() {
  133. sectionName := strings.ToUpper(strings.Replace(section.Name(), ".", "_", -1))
  134. keyName := strings.ToUpper(strings.Replace(key.Name(), ".", "_", -1))
  135. envKey := fmt.Sprintf("GF_%s_%s", sectionName, keyName)
  136. envValue := os.Getenv(envKey)
  137. if len(envValue) > 0 {
  138. key.SetValue(envValue)
  139. appliedEnvOverrides = append(appliedEnvOverrides, fmt.Sprintf("%s=%s", envKey, envValue))
  140. }
  141. }
  142. }
  143. }
  144. func applyCommandLineDefaultProperties(props map[string]string) {
  145. appliedCommandLineProperties = make([]string, 0)
  146. for _, section := range Cfg.Sections() {
  147. for _, key := range section.Keys() {
  148. keyString := fmt.Sprintf("default.%s.%s", section.Name(), key.Name())
  149. value, exists := props[keyString]
  150. if exists {
  151. key.SetValue(value)
  152. appliedCommandLineProperties = append(appliedCommandLineProperties, fmt.Sprintf("%s=%s", keyString, value))
  153. }
  154. }
  155. }
  156. }
  157. func applyCommandLineProperties(props map[string]string) {
  158. for _, section := range Cfg.Sections() {
  159. for _, key := range section.Keys() {
  160. keyString := fmt.Sprintf("%s.%s", section.Name(), key.Name())
  161. value, exists := props[keyString]
  162. if exists {
  163. key.SetValue(value)
  164. appliedCommandLineProperties = append(appliedCommandLineProperties, fmt.Sprintf("%s=%s", keyString, value))
  165. }
  166. }
  167. }
  168. }
  169. func getCommandLineProperties(args []string) map[string]string {
  170. props := make(map[string]string)
  171. for _, arg := range args {
  172. if !strings.HasPrefix(arg, "cfg:") {
  173. continue
  174. }
  175. trimmed := strings.TrimPrefix(arg, "cfg:")
  176. parts := strings.Split(trimmed, "=")
  177. if len(parts) != 2 {
  178. log.Fatal(3, "Invalid command line argument", arg)
  179. return nil
  180. }
  181. props[parts[0]] = parts[1]
  182. }
  183. return props
  184. }
  185. func makeAbsolute(path string, root string) string {
  186. if filepath.IsAbs(path) {
  187. return path
  188. }
  189. return filepath.Join(root, path)
  190. }
  191. func evalEnvVarExpression(value string) string {
  192. regex := regexp.MustCompile(`\${(\w+)}`)
  193. return regex.ReplaceAllStringFunc(value, func(envVar string) string {
  194. envVar = strings.TrimPrefix(envVar, "${")
  195. envVar = strings.TrimSuffix(envVar, "}")
  196. envValue := os.Getenv(envVar)
  197. return envValue
  198. })
  199. }
  200. func evalConfigValues() {
  201. for _, section := range Cfg.Sections() {
  202. for _, key := range section.Keys() {
  203. key.SetValue(evalEnvVarExpression(key.Value()))
  204. }
  205. }
  206. }
  207. func loadSpecifedConfigFile(configFile string) {
  208. if configFile == "" {
  209. configFile = filepath.Join(HomePath, "conf/custom.ini")
  210. // return without error if custom file does not exist
  211. if !pathExists(configFile) {
  212. return
  213. }
  214. }
  215. userConfig, err := ini.Load(configFile)
  216. userConfig.BlockMode = false
  217. if err != nil {
  218. log.Fatal(3, "Failed to parse %v, %v", configFile, err)
  219. }
  220. for _, section := range userConfig.Sections() {
  221. for _, key := range section.Keys() {
  222. if key.Value() == "" {
  223. continue
  224. }
  225. defaultSec, err := Cfg.GetSection(section.Name())
  226. if err != nil {
  227. log.Error(3, "Unknown config section %s defined in %s", section.Name(), configFile)
  228. continue
  229. }
  230. defaultKey, err := defaultSec.GetKey(key.Name())
  231. if err != nil {
  232. log.Error(3, "Unknown config key %s defined in section %s, in file %s", key.Name(), section.Name(), configFile)
  233. continue
  234. }
  235. defaultKey.SetValue(key.Value())
  236. }
  237. }
  238. configFiles = append(configFiles, configFile)
  239. }
  240. func loadConfiguration(args *CommandLineArgs) {
  241. var err error
  242. // load config defaults
  243. defaultConfigFile := path.Join(HomePath, "conf/defaults.ini")
  244. configFiles = append(configFiles, defaultConfigFile)
  245. Cfg, err = ini.Load(defaultConfigFile)
  246. Cfg.BlockMode = false
  247. if err != nil {
  248. log.Fatal(3, "Failed to parse defaults.ini, %v", err)
  249. }
  250. // command line props
  251. commandLineProps := getCommandLineProperties(args.Args)
  252. // load default overrides
  253. applyCommandLineDefaultProperties(commandLineProps)
  254. // init logging before specific config so we can log errors from here on
  255. DataPath = makeAbsolute(Cfg.Section("paths").Key("data").String(), HomePath)
  256. initLogging(args)
  257. // load specified config file
  258. loadSpecifedConfigFile(args.Config)
  259. // apply environment overrides
  260. applyEnvVariableOverrides()
  261. // apply command line overrides
  262. applyCommandLineProperties(commandLineProps)
  263. // evaluate config values containing environment variables
  264. evalConfigValues()
  265. // update data path and logging config
  266. DataPath = makeAbsolute(Cfg.Section("paths").Key("data").String(), HomePath)
  267. initLogging(args)
  268. }
  269. func pathExists(path string) bool {
  270. _, err := os.Stat(path)
  271. if err == nil {
  272. return true
  273. }
  274. if os.IsNotExist(err) {
  275. return false
  276. }
  277. return false
  278. }
  279. func setHomePath(args *CommandLineArgs) {
  280. if args.HomePath != "" {
  281. HomePath = args.HomePath
  282. return
  283. }
  284. HomePath, _ = filepath.Abs(".")
  285. // check if homepath is correct
  286. if pathExists(filepath.Join(HomePath, "conf/defaults.ini")) {
  287. return
  288. }
  289. // try down one path
  290. if pathExists(filepath.Join(HomePath, "../conf/defaults.ini")) {
  291. HomePath = filepath.Join(HomePath, "../")
  292. }
  293. }
  294. func NewConfigContext(args *CommandLineArgs) {
  295. setHomePath(args)
  296. loadConfiguration(args)
  297. Env = Cfg.Section("").Key("app_mode").MustString("development")
  298. server := Cfg.Section("server")
  299. AppUrl, AppSubUrl = parseAppUrlAndSubUrl(server)
  300. Protocol = HTTP
  301. if server.Key("protocol").MustString("http") == "https" {
  302. Protocol = HTTPS
  303. CertFile = server.Key("cert_file").String()
  304. KeyFile = server.Key("cert_key").String()
  305. }
  306. Domain = server.Key("domain").MustString("localhost")
  307. HttpAddr = server.Key("http_addr").MustString("0.0.0.0")
  308. HttpPort = server.Key("http_port").MustString("3000")
  309. StaticRootPath = makeAbsolute(server.Key("static_root_path").String(), HomePath)
  310. RouterLogging = server.Key("router_logging").MustBool(false)
  311. EnableGzip = server.Key("enable_gzip").MustBool(false)
  312. EnforceDomain = server.Key("enforce_domain").MustBool(false)
  313. security := Cfg.Section("security")
  314. SecretKey = security.Key("secret_key").String()
  315. LogInRememberDays = security.Key("login_remember_days").MustInt()
  316. CookieUserName = security.Key("cookie_username").String()
  317. CookieRememberName = security.Key("cookie_remember_name").String()
  318. DisableGravatar = security.Key("disable_gravatar").MustBool(true)
  319. // admin
  320. AdminUser = security.Key("admin_user").String()
  321. AdminPassword = security.Key("admin_password").String()
  322. users := Cfg.Section("users")
  323. AllowUserSignUp = users.Key("allow_sign_up").MustBool(true)
  324. AllowUserOrgCreate = users.Key("allow_org_create").MustBool(true)
  325. AutoAssignOrg = users.Key("auto_assign_org").MustBool(true)
  326. AutoAssignOrgRole = users.Key("auto_assign_org_role").In("Editor", []string{"Editor", "Admin", "Read Only Editor", "Viewer"})
  327. VerifyEmailEnabled = users.Key("verify_email_enabled").MustBool(false)
  328. // anonymous access
  329. AnonymousEnabled = Cfg.Section("auth.anonymous").Key("enabled").MustBool(false)
  330. AnonymousOrgName = Cfg.Section("auth.anonymous").Key("org_name").String()
  331. AnonymousOrgRole = Cfg.Section("auth.anonymous").Key("org_role").String()
  332. // auth proxy
  333. authProxy := Cfg.Section("auth.proxy")
  334. AuthProxyEnabled = authProxy.Key("enabled").MustBool(false)
  335. AuthProxyHeaderName = authProxy.Key("header_name").String()
  336. AuthProxyHeaderProperty = authProxy.Key("header_property").String()
  337. AuthProxyAutoSignUp = authProxy.Key("auto_sign_up").MustBool(true)
  338. authBasic := Cfg.Section("auth.basic")
  339. BasicAuthEnabled = authBasic.Key("enabled").MustBool(true)
  340. // PhantomJS rendering
  341. ImagesDir = filepath.Join(DataPath, "png")
  342. PhantomDir = filepath.Join(HomePath, "vendor/phantomjs")
  343. analytics := Cfg.Section("analytics")
  344. ReportingEnabled = analytics.Key("reporting_enabled").MustBool(true)
  345. GoogleAnalyticsId = analytics.Key("google_analytics_ua_id").String()
  346. GoogleTagManagerId = analytics.Key("google_tag_manager_id").String()
  347. ldapSec := Cfg.Section("auth.ldap")
  348. LdapEnabled = ldapSec.Key("enabled").MustBool(false)
  349. LdapConfigFile = ldapSec.Key("config_file").String()
  350. readSessionConfig()
  351. readSmtpSettings()
  352. if VerifyEmailEnabled && !Smtp.Enabled {
  353. log.Warn("require_email_validation is enabled but smpt is disabled")
  354. }
  355. }
  356. func readSessionConfig() {
  357. sec := Cfg.Section("session")
  358. SessionOptions = session.Options{}
  359. SessionOptions.Provider = sec.Key("provider").In("memory", []string{"memory", "file", "redis", "mysql", "postgres"})
  360. SessionOptions.ProviderConfig = strings.Trim(sec.Key("provider_config").String(), "\" ")
  361. SessionOptions.CookieName = sec.Key("cookie_name").MustString("grafana_sess")
  362. SessionOptions.CookiePath = AppSubUrl
  363. SessionOptions.Secure = sec.Key("cookie_secure").MustBool()
  364. SessionOptions.Gclifetime = Cfg.Section("session").Key("gc_interval_time").MustInt64(86400)
  365. SessionOptions.Maxlifetime = Cfg.Section("session").Key("session_life_time").MustInt64(86400)
  366. SessionOptions.IDLength = 16
  367. if SessionOptions.Provider == "file" {
  368. SessionOptions.ProviderConfig = makeAbsolute(SessionOptions.ProviderConfig, DataPath)
  369. os.MkdirAll(path.Dir(SessionOptions.ProviderConfig), os.ModePerm)
  370. }
  371. if SessionOptions.CookiePath == "" {
  372. SessionOptions.CookiePath = "/"
  373. }
  374. }
  375. var logLevels = map[string]int{
  376. "Trace": 0,
  377. "Debug": 1,
  378. "Info": 2,
  379. "Warn": 3,
  380. "Error": 4,
  381. "Critical": 5,
  382. }
  383. func initLogging(args *CommandLineArgs) {
  384. //close any existing log handlers.
  385. log.Close()
  386. // Get and check log mode.
  387. LogModes = strings.Split(Cfg.Section("log").Key("mode").MustString("console"), ",")
  388. LogsPath = makeAbsolute(Cfg.Section("paths").Key("logs").String(), HomePath)
  389. LogConfigs = make([]util.DynMap, len(LogModes))
  390. for i, mode := range LogModes {
  391. mode = strings.TrimSpace(mode)
  392. sec, err := Cfg.GetSection("log." + mode)
  393. if err != nil {
  394. log.Fatal(4, "Unknown log mode: %s", mode)
  395. }
  396. // Log level.
  397. levelName := Cfg.Section("log."+mode).Key("level").In("Trace",
  398. []string{"Trace", "Debug", "Info", "Warn", "Error", "Critical"})
  399. level, ok := logLevels[levelName]
  400. if !ok {
  401. log.Fatal(4, "Unknown log level: %s", levelName)
  402. }
  403. // Generate log configuration.
  404. switch mode {
  405. case "console":
  406. LogConfigs[i] = util.DynMap{"level": level}
  407. case "file":
  408. logPath := sec.Key("file_name").MustString(filepath.Join(LogsPath, "grafana.log"))
  409. os.MkdirAll(filepath.Dir(logPath), os.ModePerm)
  410. LogConfigs[i] = util.DynMap{
  411. "level": level,
  412. "filename": logPath,
  413. "rotate": sec.Key("log_rotate").MustBool(true),
  414. "maxlines": sec.Key("max_lines").MustInt(1000000),
  415. "maxsize": 1 << uint(sec.Key("max_size_shift").MustInt(28)),
  416. "daily": sec.Key("daily_rotate").MustBool(true),
  417. "maxdays": sec.Key("max_days").MustInt(7),
  418. }
  419. case "conn":
  420. LogConfigs[i] = util.DynMap{
  421. "level": level,
  422. "reconnectOnMsg": sec.Key("reconnect_on_msg").MustBool(),
  423. "reconnect": sec.Key("reconnect").MustBool(),
  424. "net": sec.Key("protocol").In("tcp", []string{"tcp", "unix", "udp"}),
  425. "addr": sec.Key("addr").MustString(":7020"),
  426. }
  427. case "smtp":
  428. LogConfigs[i] = util.DynMap{
  429. "level": level,
  430. "user": sec.Key("user").MustString("example@example.com"),
  431. "passwd": sec.Key("passwd").MustString("******"),
  432. "host": sec.Key("host").MustString("127.0.0.1:25"),
  433. "receivers": sec.Key("receivers").MustString("[]"),
  434. "subject": sec.Key("subject").MustString("Diagnostic message from serve"),
  435. }
  436. case "database":
  437. LogConfigs[i] = util.DynMap{
  438. "level": level,
  439. "driver": sec.Key("driver").String(),
  440. "conn": sec.Key("conn").String(),
  441. }
  442. }
  443. cfgJsonBytes, _ := json.Marshal(LogConfigs[i])
  444. log.NewLogger(Cfg.Section("log").Key("buffer_len").MustInt64(10000), mode, string(cfgJsonBytes))
  445. }
  446. }
  447. func LogConfigurationInfo() {
  448. var text bytes.Buffer
  449. text.WriteString("Configuration Info\n")
  450. text.WriteString("Config files:\n")
  451. for i, file := range configFiles {
  452. text.WriteString(fmt.Sprintf(" [%d]: %s\n", i, file))
  453. }
  454. if len(appliedCommandLineProperties) > 0 {
  455. text.WriteString("Command lines overrides:\n")
  456. for i, prop := range appliedCommandLineProperties {
  457. text.WriteString(fmt.Sprintf(" [%d]: %s\n", i, prop))
  458. }
  459. }
  460. if len(appliedEnvOverrides) > 0 {
  461. text.WriteString("\tEnvironment variables used:\n")
  462. for i, prop := range appliedCommandLineProperties {
  463. text.WriteString(fmt.Sprintf(" [%d]: %s\n", i, prop))
  464. }
  465. }
  466. text.WriteString("Paths:\n")
  467. text.WriteString(fmt.Sprintf(" home: %s\n", HomePath))
  468. text.WriteString(fmt.Sprintf(" data: %s\n", DataPath))
  469. text.WriteString(fmt.Sprintf(" logs: %s\n", LogsPath))
  470. log.Info(text.String())
  471. }