setting.go 15 KB

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