Просмотр исходного кода

Merge branch 'Scalingo-database_url'

bergquist 9 лет назад
Родитель
Сommit
8d49b66dae
6 измененных файлов с 71 добавлено и 7 удалено
  1. 1 0
      CHANGELOG.md
  2. 6 0
      conf/defaults.ini
  3. 7 0
      conf/sample.ini
  4. 27 7
      pkg/services/sqlstore/sqlstore.go
  5. 16 0
      pkg/setting/setting.go
  6. 14 0
      pkg/setting/setting_test.go

+ 1 - 0
CHANGELOG.md

@@ -7,6 +7,7 @@
 * **Templating**: Update panel repeats for variables that change on time refresh, closes [#5021](https://github.com/grafana/grafana/issues/5021)
 * **Templating**: Update panel repeats for variables that change on time refresh, closes [#5021](https://github.com/grafana/grafana/issues/5021)
 * **Elasticsearch**: Support to set Precision Threshold for Unique Count metric, closes [#4689](https://github.com/grafana/grafana/issues/4689)
 * **Elasticsearch**: Support to set Precision Threshold for Unique Count metric, closes [#4689](https://github.com/grafana/grafana/issues/4689)
 * **Navigation**: Add search to org swithcer, closes [#2609](https://github.com/grafana/grafana/issues/2609)
 * **Navigation**: Add search to org swithcer, closes [#2609](https://github.com/grafana/grafana/issues/2609)
+* **Database**: Allow database config using one propertie, closes [#5456](https://github.com/grafana/grafana/pull/5456)
 
 
 # 3.1.2 (unreleased)
 # 3.1.2 (unreleased)
 * **Templating**: Fixed issue when combining row & panel repeats, fixes [#5790](https://github.com/grafana/grafana/issues/5790)
 * **Templating**: Fixed issue when combining row & panel repeats, fixes [#5790](https://github.com/grafana/grafana/issues/5790)

+ 6 - 0
conf/defaults.ini

@@ -59,12 +59,18 @@ cert_key =
 
 
 #################################### Database ####################################
 #################################### Database ####################################
 [database]
 [database]
+# You can configure the database connection by specifying type, host, name, user and password 
+# as seperate properties or as on string using the url propertie.
+
 # Either "mysql", "postgres" or "sqlite3", it's your choice
 # Either "mysql", "postgres" or "sqlite3", it's your choice
 type = sqlite3
 type = sqlite3
 host = 127.0.0.1:3306
 host = 127.0.0.1:3306
 name = grafana
 name = grafana
 user = root
 user = root
 password =
 password =
+# Use either URL or the previous fields to configure the database
+# Example: mysql://user:secret@host:port/database
+url =
 
 
 # For "postgres", use either "disable", "require" or "verify-full"
 # For "postgres", use either "disable", "require" or "verify-full"
 # For "mysql", use either "true", "false", or "skip-verify".
 # For "mysql", use either "true", "false", or "skip-verify".

+ 7 - 0
conf/sample.ini

@@ -61,6 +61,9 @@
 
 
 #################################### Database ####################################
 #################################### Database ####################################
 [database]
 [database]
+# You can configure the database connection by specifying type, host, name, user and password 
+# as seperate properties or as on string using the url propertie.
+
 # Either "mysql", "postgres" or "sqlite3", it's your choice
 # Either "mysql", "postgres" or "sqlite3", it's your choice
 ;type = sqlite3
 ;type = sqlite3
 ;host = 127.0.0.1:3306
 ;host = 127.0.0.1:3306
@@ -68,6 +71,10 @@
 ;user = root
 ;user = root
 ;password =
 ;password =
 
 
+# Use either URL or the previous fields to configure the database
+# Example: mysql://user:secret@host:port/database
+;url =
+
 # For "postgres" only, either "disable", "require" or "verify-full"
 # For "postgres" only, either "disable", "require" or "verify-full"
 ;ssl_mode = disable
 ;ssl_mode = disable
 
 

+ 27 - 7
pkg/services/sqlstore/sqlstore.go

@@ -2,6 +2,7 @@ package sqlstore
 
 
 import (
 import (
 	"fmt"
 	"fmt"
+	"net/url"
 	"os"
 	"os"
 	"path"
 	"path"
 	"path/filepath"
 	"path/filepath"
@@ -155,16 +156,35 @@ func getEngine() (*xorm.Engine, error) {
 func LoadConfig() {
 func LoadConfig() {
 	sec := setting.Cfg.Section("database")
 	sec := setting.Cfg.Section("database")
 
 
-	DbCfg.Type = sec.Key("type").String()
+	cfgURL := sec.Key("url").String()
+	if len(cfgURL) != 0 {
+		dbURL, _ := url.Parse(cfgURL)
+		DbCfg.Type = dbURL.Scheme
+		DbCfg.Host = dbURL.Host
+
+		pathSplit := strings.Split(dbURL.Path, "/")
+		if len(pathSplit) > 1 {
+			DbCfg.Name = pathSplit[1]
+		}
+
+		userInfo := dbURL.User
+		if userInfo != nil {
+			DbCfg.User = userInfo.Username()
+			DbCfg.Pwd, _ = userInfo.Password()
+		}
+	} else {
+		DbCfg.Type = sec.Key("type").String()
+		DbCfg.Host = sec.Key("host").String()
+		DbCfg.Name = sec.Key("name").String()
+		DbCfg.User = sec.Key("user").String()
+		if len(DbCfg.Pwd) == 0 {
+			DbCfg.Pwd = sec.Key("password").String()
+		}
+	}
+
 	if DbCfg.Type == "sqlite3" {
 	if DbCfg.Type == "sqlite3" {
 		UseSQLite3 = true
 		UseSQLite3 = true
 	}
 	}
-	DbCfg.Host = sec.Key("host").String()
-	DbCfg.Name = sec.Key("name").String()
-	DbCfg.User = sec.Key("user").String()
-	if len(DbCfg.Pwd) == 0 {
-		DbCfg.Pwd = sec.Key("password").String()
-	}
 	DbCfg.SslMode = sec.Key("ssl_mode").String()
 	DbCfg.SslMode = sec.Key("ssl_mode").String()
 	DbCfg.Path = sec.Key("path").MustString("data/grafana.db")
 	DbCfg.Path = sec.Key("path").MustString("data/grafana.db")
 
 

+ 16 - 0
pkg/setting/setting.go

@@ -183,6 +183,11 @@ func shouldRedactKey(s string) bool {
 	return strings.Contains(uppercased, "PASSWORD") || strings.Contains(uppercased, "SECRET")
 	return strings.Contains(uppercased, "PASSWORD") || strings.Contains(uppercased, "SECRET")
 }
 }
 
 
+func shouldRedactURLKey(s string) bool {
+	uppercased := strings.ToUpper(s)
+	return strings.Contains(uppercased, "DATABASE_URL")
+}
+
 func applyEnvVariableOverrides() {
 func applyEnvVariableOverrides() {
 	appliedEnvOverrides = make([]string, 0)
 	appliedEnvOverrides = make([]string, 0)
 	for _, section := range Cfg.Sections() {
 	for _, section := range Cfg.Sections() {
@@ -197,6 +202,17 @@ func applyEnvVariableOverrides() {
 				if shouldRedactKey(envKey) {
 				if shouldRedactKey(envKey) {
 					envValue = "*********"
 					envValue = "*********"
 				}
 				}
+				if shouldRedactURLKey(envKey) {
+					u, _ := url.Parse(envValue)
+					ui := u.User
+					if ui != nil {
+						_, exists := ui.Password()
+						if exists {
+							u.User = url.UserPassword(ui.Username(), "-redacted-")
+							envValue = u.String()
+						}
+					}
+				}
 				appliedEnvOverrides = append(appliedEnvOverrides, fmt.Sprintf("%s=%s", envKey, envValue))
 				appliedEnvOverrides = append(appliedEnvOverrides, fmt.Sprintf("%s=%s", envKey, envValue))
 			}
 			}
 		}
 		}

+ 14 - 0
pkg/setting/setting_test.go

@@ -29,6 +29,20 @@ func TestLoadingSettings(t *testing.T) {
 			So(LogsPath, ShouldEqual, filepath.Join(DataPath, "log"))
 			So(LogsPath, ShouldEqual, filepath.Join(DataPath, "log"))
 		})
 		})
 
 
+		Convey("Should replace password when defined in environment", func() {
+			os.Setenv("GF_SECURITY_ADMIN_PASSWORD", "supersecret")
+			NewConfigContext(&CommandLineArgs{HomePath: "../../"})
+
+			So(appliedEnvOverrides, ShouldContain, "GF_SECURITY_ADMIN_PASSWORD=*********")
+		})
+
+		Convey("Should replace password in URL when url environment is defined", func() {
+			os.Setenv("GF_DATABASE_URL", "mysql://user:secret@localhost:3306/database")
+			NewConfigContext(&CommandLineArgs{HomePath: "../../"})
+
+			So(appliedEnvOverrides, ShouldContain, "GF_DATABASE_URL=mysql://user:-redacted-@localhost:3306/database")
+		})
+
 		Convey("Should get property map from command line args array", func() {
 		Convey("Should get property map from command line args array", func() {
 			props := getCommandLineProperties([]string{"cfg:test=value", "cfg:map.test=1"})
 			props := getCommandLineProperties([]string{"cfg:test=value", "cfg:map.test=1"})