Explorar o código

Initial Baby Step to refactoring settings from global vars to instance (#11777)

* wip: start on refactoring settings

* settings: progress on settings refactor

* refactor: progress on settings refactoring

* fix: fixed failing test

* settings: moved smtp settings from global to instance
Torkel Ödegaard %!s(int64=7) %!d(string=hai) anos
pai
achega
fa7d7ed5df

+ 1 - 1
pkg/api/admin.go

@@ -12,7 +12,7 @@ import (
 func AdminGetSettings(c *m.ReqContext) {
 	settings := make(map[string]interface{})
 
-	for _, section := range setting.Cfg.Sections() {
+	for _, section := range setting.Raw.Sections() {
 		jsonSec := make(map[string]interface{})
 		settings[section.Name()] = jsonSec
 

+ 3 - 2
pkg/api/http_server.go

@@ -35,9 +35,10 @@ type HTTPServer struct {
 	context       context.Context
 	streamManager *live.StreamManager
 	cache         *gocache.Cache
-	RouteRegister RouteRegister `inject:""`
+	httpSrv       *http.Server
 
-	httpSrv *http.Server
+	RouteRegister RouteRegister `inject:""`
+	Bus           bus.Bus       `inject:""`
 }
 
 func (hs *HTTPServer) Init() {

+ 2 - 1
pkg/cmd/grafana-cli/commands/commands.go

@@ -15,7 +15,8 @@ func runDbCommand(command func(commandLine CommandLine) error) func(context *cli
 	return func(context *cli.Context) {
 		cmd := &contextCommandLine{context}
 
-		setting.NewConfigContext(&setting.CommandLineArgs{
+		cfg := setting.NewCfg()
+		cfg.Load(&setting.CommandLineArgs{
 			Config:   cmd.String("config"),
 			HomePath: cmd.String("homepath"),
 			Args:     flag.Args(),

+ 10 - 7
pkg/cmd/grafana-server/server.go

@@ -49,6 +49,7 @@ func NewGrafanaServer() *GrafanaServerImpl {
 		shutdownFn:    shutdownFn,
 		childRoutines: childRoutines,
 		log:           log.New("server"),
+		cfg:           setting.NewCfg(),
 	}
 }
 
@@ -57,28 +58,29 @@ type GrafanaServerImpl struct {
 	shutdownFn    context.CancelFunc
 	childRoutines *errgroup.Group
 	log           log.Logger
+	cfg           *setting.Cfg
 
 	RouteRegister api.RouteRegister `inject:""`
 	HttpServer    *api.HTTPServer   `inject:""`
 }
 
 func (g *GrafanaServerImpl) Start() error {
-	g.initLogging()
+	g.loadConfiguration()
 	g.writePIDFile()
 
 	// initSql
 	sqlstore.NewEngine() // TODO: this should return an error
 	sqlstore.EnsureAdminUser()
 
-	metrics.Init(setting.Cfg)
+	metrics.Init(g.cfg.Raw)
 	login.Init()
 	social.NewOAuthService()
 
-	if err := provisioning.Init(g.context, setting.HomePath, setting.Cfg); err != nil {
+	if err := provisioning.Init(g.context, setting.HomePath, g.cfg.Raw); err != nil {
 		return fmt.Errorf("Failed to provision Grafana from config. error: %v", err)
 	}
 
-	tracingCloser, err := tracing.Init(setting.Cfg)
+	tracingCloser, err := tracing.Init(g.cfg.Raw)
 	if err != nil {
 		return fmt.Errorf("Tracing settings is not valid. error: %v", err)
 	}
@@ -86,6 +88,7 @@ func (g *GrafanaServerImpl) Start() error {
 
 	serviceGraph := inject.Graph{}
 	serviceGraph.Provide(&inject.Object{Value: bus.GetBus()})
+	serviceGraph.Provide(&inject.Object{Value: g.cfg})
 	serviceGraph.Provide(&inject.Object{Value: dashboards.NewProvisioningService()})
 	serviceGraph.Provide(&inject.Object{Value: api.NewRouteRegister(middleware.RequestMetrics, middleware.RequestTracing)})
 	serviceGraph.Provide(&inject.Object{Value: api.HTTPServer{}})
@@ -138,8 +141,8 @@ func (g *GrafanaServerImpl) Start() error {
 	return g.startHttpServer()
 }
 
-func (g *GrafanaServerImpl) initLogging() {
-	err := setting.NewConfigContext(&setting.CommandLineArgs{
+func (g *GrafanaServerImpl) loadConfiguration() {
+	err := g.cfg.Load(&setting.CommandLineArgs{
 		Config:   *configFile,
 		HomePath: *homePath,
 		Args:     flag.Args(),
@@ -151,7 +154,7 @@ func (g *GrafanaServerImpl) initLogging() {
 	}
 
 	g.log.Info("Starting "+setting.ApplicationName, "version", version, "commit", commit, "compiled", time.Unix(setting.BuildStamp, 0))
-	setting.LogConfigurationInfo()
+	g.cfg.LogConfigSources()
 }
 
 func (g *GrafanaServerImpl) startHttpServer() error {

+ 2 - 1
pkg/components/imguploader/azureblobuploader_test.go

@@ -10,7 +10,8 @@ import (
 
 func TestUploadToAzureBlob(t *testing.T) {
 	SkipConvey("[Integration test] for external_image_store.azure_blob", t, func() {
-		err := setting.NewConfigContext(&setting.CommandLineArgs{
+		cfg := setting.NewCfg()
+		err := cfg.Load(&setting.CommandLineArgs{
 			HomePath: "../../../",
 		})
 		So(err, ShouldBeNil)

+ 2 - 1
pkg/components/imguploader/gcsuploader_test.go

@@ -10,7 +10,8 @@ import (
 
 func TestUploadToGCS(t *testing.T) {
 	SkipConvey("[Integration test] for external_image_store.gcs", t, func() {
-		setting.NewConfigContext(&setting.CommandLineArgs{
+		cfg := setting.NewCfg()
+		cfg.Load(&setting.CommandLineArgs{
 			HomePath: "../../../",
 		})
 

+ 6 - 5
pkg/components/imguploader/imguploader.go

@@ -3,9 +3,10 @@ package imguploader
 import (
 	"context"
 	"fmt"
-	"github.com/grafana/grafana/pkg/log"
 	"regexp"
 
+	"github.com/grafana/grafana/pkg/log"
+
 	"github.com/grafana/grafana/pkg/setting"
 )
 
@@ -24,7 +25,7 @@ func NewImageUploader() (ImageUploader, error) {
 
 	switch setting.ImageUploadProvider {
 	case "s3":
-		s3sec, err := setting.Cfg.GetSection("external_image_storage.s3")
+		s3sec, err := setting.Raw.GetSection("external_image_storage.s3")
 		if err != nil {
 			return nil, err
 		}
@@ -51,7 +52,7 @@ func NewImageUploader() (ImageUploader, error) {
 
 		return NewS3Uploader(region, bucket, path, "public-read", accessKey, secretKey), nil
 	case "webdav":
-		webdavSec, err := setting.Cfg.GetSection("external_image_storage.webdav")
+		webdavSec, err := setting.Raw.GetSection("external_image_storage.webdav")
 		if err != nil {
 			return nil, err
 		}
@@ -67,7 +68,7 @@ func NewImageUploader() (ImageUploader, error) {
 
 		return NewWebdavImageUploader(url, username, password, public_url)
 	case "gcs":
-		gcssec, err := setting.Cfg.GetSection("external_image_storage.gcs")
+		gcssec, err := setting.Raw.GetSection("external_image_storage.gcs")
 		if err != nil {
 			return nil, err
 		}
@@ -78,7 +79,7 @@ func NewImageUploader() (ImageUploader, error) {
 
 		return NewGCSUploader(keyFile, bucketName, path), nil
 	case "azure_blob":
-		azureBlobSec, err := setting.Cfg.GetSection("external_image_storage.azure_blob")
+		azureBlobSec, err := setting.Raw.GetSection("external_image_storage.azure_blob")
 		if err != nil {
 			return nil, err
 		}

+ 16 - 11
pkg/components/imguploader/imguploader_test.go

@@ -11,14 +11,15 @@ import (
 func TestImageUploaderFactory(t *testing.T) {
 	Convey("Can create image uploader for ", t, func() {
 		Convey("S3ImageUploader config", func() {
-			setting.NewConfigContext(&setting.CommandLineArgs{
+			cfg := setting.NewCfg()
+			cfg.Load(&setting.CommandLineArgs{
 				HomePath: "../../../",
 			})
 
 			setting.ImageUploadProvider = "s3"
 
 			Convey("with bucket url https://foo.bar.baz.s3-us-east-2.amazonaws.com", func() {
-				s3sec, err := setting.Cfg.GetSection("external_image_storage.s3")
+				s3sec, err := setting.Raw.GetSection("external_image_storage.s3")
 				So(err, ShouldBeNil)
 				s3sec.NewKey("bucket_url", "https://foo.bar.baz.s3-us-east-2.amazonaws.com")
 				s3sec.NewKey("access_key", "access_key")
@@ -37,7 +38,7 @@ func TestImageUploaderFactory(t *testing.T) {
 			})
 
 			Convey("with bucket url https://s3.amazonaws.com/mybucket", func() {
-				s3sec, err := setting.Cfg.GetSection("external_image_storage.s3")
+				s3sec, err := setting.Raw.GetSection("external_image_storage.s3")
 				So(err, ShouldBeNil)
 				s3sec.NewKey("bucket_url", "https://s3.amazonaws.com/my.bucket.com")
 				s3sec.NewKey("access_key", "access_key")
@@ -56,7 +57,7 @@ func TestImageUploaderFactory(t *testing.T) {
 			})
 
 			Convey("with bucket url https://s3-us-west-2.amazonaws.com/mybucket", func() {
-				s3sec, err := setting.Cfg.GetSection("external_image_storage.s3")
+				s3sec, err := setting.Raw.GetSection("external_image_storage.s3")
 				So(err, ShouldBeNil)
 				s3sec.NewKey("bucket_url", "https://s3-us-west-2.amazonaws.com/my.bucket.com")
 				s3sec.NewKey("access_key", "access_key")
@@ -77,13 +78,14 @@ func TestImageUploaderFactory(t *testing.T) {
 		Convey("Webdav uploader", func() {
 			var err error
 
-			setting.NewConfigContext(&setting.CommandLineArgs{
+			cfg := setting.NewCfg()
+			cfg.Load(&setting.CommandLineArgs{
 				HomePath: "../../../",
 			})
 
 			setting.ImageUploadProvider = "webdav"
 
-			webdavSec, err := setting.Cfg.GetSection("external_image_storage.webdav")
+			webdavSec, err := cfg.Raw.GetSection("external_image_storage.webdav")
 			So(err, ShouldBeNil)
 			webdavSec.NewKey("url", "webdavUrl")
 			webdavSec.NewKey("username", "username")
@@ -103,13 +105,14 @@ func TestImageUploaderFactory(t *testing.T) {
 		Convey("GCS uploader", func() {
 			var err error
 
-			setting.NewConfigContext(&setting.CommandLineArgs{
+			cfg := setting.NewCfg()
+			cfg.Load(&setting.CommandLineArgs{
 				HomePath: "../../../",
 			})
 
 			setting.ImageUploadProvider = "gcs"
 
-			gcpSec, err := setting.Cfg.GetSection("external_image_storage.gcs")
+			gcpSec, err := cfg.Raw.GetSection("external_image_storage.gcs")
 			So(err, ShouldBeNil)
 			gcpSec.NewKey("key_file", "/etc/secrets/project-79a52befa3f6.json")
 			gcpSec.NewKey("bucket", "project-grafana-east")
@@ -124,13 +127,14 @@ func TestImageUploaderFactory(t *testing.T) {
 		})
 
 		Convey("AzureBlobUploader config", func() {
-			setting.NewConfigContext(&setting.CommandLineArgs{
+			cfg := setting.NewCfg()
+			cfg.Load(&setting.CommandLineArgs{
 				HomePath: "../../../",
 			})
 			setting.ImageUploadProvider = "azure_blob"
 
 			Convey("with container name", func() {
-				azureBlobSec, err := setting.Cfg.GetSection("external_image_storage.azure_blob")
+				azureBlobSec, err := cfg.Raw.GetSection("external_image_storage.azure_blob")
 				So(err, ShouldBeNil)
 				azureBlobSec.NewKey("account_name", "account_name")
 				azureBlobSec.NewKey("account_key", "account_key")
@@ -150,7 +154,8 @@ func TestImageUploaderFactory(t *testing.T) {
 		Convey("Local uploader", func() {
 			var err error
 
-			setting.NewConfigContext(&setting.CommandLineArgs{
+			cfg := setting.NewCfg()
+			cfg.Load(&setting.CommandLineArgs{
 				HomePath: "../../../",
 			})
 

+ 2 - 1
pkg/components/imguploader/s3uploader_test.go

@@ -10,7 +10,8 @@ import (
 
 func TestUploadToS3(t *testing.T) {
 	SkipConvey("[Integration test] for external_image_store.s3", t, func() {
-		setting.NewConfigContext(&setting.CommandLineArgs{
+		cfg := setting.NewCfg()
+		cfg.Load(&setting.CommandLineArgs{
 			HomePath: "../../../",
 		})
 

+ 1 - 1
pkg/metrics/settings.go

@@ -46,7 +46,7 @@ func ReadSettings(file *ini.File) *MetricSettings {
 }
 
 func parseGraphiteSettings(settings *MetricSettings, file *ini.File) (*graphitebridge.Config, error) {
-	graphiteSection, err := setting.Cfg.GetSection("metrics.graphite")
+	graphiteSection, err := setting.Raw.GetSection("metrics.graphite")
 	if err != nil {
 		return nil, nil
 	}

+ 2 - 2
pkg/plugins/dashboard_importer_test.go

@@ -87,8 +87,8 @@ func TestDashboardImport(t *testing.T) {
 
 func pluginScenario(desc string, t *testing.T, fn func()) {
 	Convey("Given a plugin", t, func() {
-		setting.Cfg = ini.Empty()
-		sec, _ := setting.Cfg.NewSection("plugin.test-app")
+		setting.Raw = ini.Empty()
+		sec, _ := setting.Raw.NewSection("plugin.test-app")
 		sec.NewKey("path", "../../tests/test-app")
 
 		pm := &PluginManager{}

+ 2 - 2
pkg/plugins/dashboards_test.go

@@ -14,8 +14,8 @@ import (
 func TestPluginDashboards(t *testing.T) {
 
 	Convey("When asking plugin dashboard info", t, func() {
-		setting.Cfg = ini.Empty()
-		sec, _ := setting.Cfg.NewSection("plugin.test-app")
+		setting.Raw = ini.Empty()
+		sec, _ := setting.Raw.NewSection("plugin.test-app")
 		sec.NewKey("path", "../../tests/test-app")
 
 		pm := &PluginManager{}

+ 1 - 1
pkg/plugins/plugins.go

@@ -132,7 +132,7 @@ func (pm *PluginManager) Run(ctx context.Context) error {
 }
 
 func checkPluginPaths() error {
-	for _, section := range setting.Cfg.Sections() {
+	for _, section := range setting.Raw.Sections() {
 		if strings.HasPrefix(section.Name(), "plugin.") {
 			path := section.Key("path").String()
 			if path != "" {

+ 3 - 3
pkg/plugins/plugins_test.go

@@ -13,7 +13,7 @@ func TestPluginScans(t *testing.T) {
 
 	Convey("When scanning for plugins", t, func() {
 		setting.StaticRootPath, _ = filepath.Abs("../../public/")
-		setting.Cfg = ini.Empty()
+		setting.Raw = ini.Empty()
 
 		pm := &PluginManager{}
 		err := pm.Init()
@@ -28,8 +28,8 @@ func TestPluginScans(t *testing.T) {
 	})
 
 	Convey("When reading app plugin definition", t, func() {
-		setting.Cfg = ini.Empty()
-		sec, _ := setting.Cfg.NewSection("plugin.nginx-app")
+		setting.Raw = ini.Empty()
+		sec, _ := setting.Raw.NewSection("plugin.nginx-app")
 		sec.NewKey("path", "../../tests/test-app")
 
 		pm := &PluginManager{}

+ 26 - 25
pkg/services/cleanup/cleanup.go

@@ -16,42 +16,43 @@ import (
 
 type CleanUpService struct {
 	log log.Logger
+	Cfg *setting.Cfg `inject:""`
 }
 
 func init() {
 	registry.RegisterService(&CleanUpService{})
 }
 
-func (service *CleanUpService) Init() error {
-	service.log = log.New("cleanup")
+func (srv *CleanUpService) Init() error {
+	srv.log = log.New("cleanup")
 	return nil
 }
 
-func (service *CleanUpService) Run(ctx context.Context) error {
-	service.cleanUpTmpFiles()
+func (srv *CleanUpService) Run(ctx context.Context) error {
+	srv.cleanUpTmpFiles()
 
 	ticker := time.NewTicker(time.Minute * 10)
 	for {
 		select {
 		case <-ticker.C:
-			service.cleanUpTmpFiles()
-			service.deleteExpiredSnapshots()
-			service.deleteExpiredDashboardVersions()
-			service.deleteOldLoginAttempts()
+			srv.cleanUpTmpFiles()
+			srv.deleteExpiredSnapshots()
+			srv.deleteExpiredDashboardVersions()
+			srv.deleteOldLoginAttempts()
 		case <-ctx.Done():
 			return ctx.Err()
 		}
 	}
 }
 
-func (service *CleanUpService) cleanUpTmpFiles() {
-	if _, err := os.Stat(setting.ImagesDir); os.IsNotExist(err) {
+func (srv *CleanUpService) cleanUpTmpFiles() {
+	if _, err := os.Stat(srv.Cfg.ImagesDir); os.IsNotExist(err) {
 		return
 	}
 
-	files, err := ioutil.ReadDir(setting.ImagesDir)
+	files, err := ioutil.ReadDir(srv.Cfg.ImagesDir)
 	if err != nil {
-		service.log.Error("Problem reading image dir", "error", err)
+		srv.log.Error("Problem reading image dir", "error", err)
 		return
 	}
 
@@ -63,36 +64,36 @@ func (service *CleanUpService) cleanUpTmpFiles() {
 	}
 
 	for _, file := range toDelete {
-		fullPath := path.Join(setting.ImagesDir, file.Name())
+		fullPath := path.Join(srv.Cfg.ImagesDir, file.Name())
 		err := os.Remove(fullPath)
 		if err != nil {
-			service.log.Error("Failed to delete temp file", "file", file.Name(), "error", err)
+			srv.log.Error("Failed to delete temp file", "file", file.Name(), "error", err)
 		}
 	}
 
-	service.log.Debug("Found old rendered image to delete", "deleted", len(toDelete), "keept", len(files))
+	srv.log.Debug("Found old rendered image to delete", "deleted", len(toDelete), "keept", len(files))
 }
 
-func (service *CleanUpService) deleteExpiredSnapshots() {
+func (srv *CleanUpService) deleteExpiredSnapshots() {
 	cmd := m.DeleteExpiredSnapshotsCommand{}
 	if err := bus.Dispatch(&cmd); err != nil {
-		service.log.Error("Failed to delete expired snapshots", "error", err.Error())
+		srv.log.Error("Failed to delete expired snapshots", "error", err.Error())
 	} else {
-		service.log.Debug("Deleted expired snapshots", "rows affected", cmd.DeletedRows)
+		srv.log.Debug("Deleted expired snapshots", "rows affected", cmd.DeletedRows)
 	}
 }
 
-func (service *CleanUpService) deleteExpiredDashboardVersions() {
+func (srv *CleanUpService) deleteExpiredDashboardVersions() {
 	cmd := m.DeleteExpiredVersionsCommand{}
 	if err := bus.Dispatch(&cmd); err != nil {
-		service.log.Error("Failed to delete expired dashboard versions", "error", err.Error())
+		srv.log.Error("Failed to delete expired dashboard versions", "error", err.Error())
 	} else {
-		service.log.Debug("Deleted old/expired dashboard versions", "rows affected", cmd.DeletedRows)
+		srv.log.Debug("Deleted old/expired dashboard versions", "rows affected", cmd.DeletedRows)
 	}
 }
 
-func (service *CleanUpService) deleteOldLoginAttempts() {
-	if setting.DisableBruteForceLoginProtection {
+func (srv *CleanUpService) deleteOldLoginAttempts() {
+	if srv.Cfg.DisableBruteForceLoginProtection {
 		return
 	}
 
@@ -100,8 +101,8 @@ func (service *CleanUpService) deleteOldLoginAttempts() {
 		OlderThan: time.Now().Add(time.Minute * -10),
 	}
 	if err := bus.Dispatch(&cmd); err != nil {
-		service.log.Error("Problem deleting expired login attempts", "error", err.Error())
+		srv.log.Error("Problem deleting expired login attempts", "error", err.Error())
 	} else {
-		service.log.Debug("Deleted expired login attempts", "rows affected", cmd.DeletedRows)
+		srv.log.Debug("Deleted expired login attempts", "rows affected", cmd.DeletedRows)
 	}
 }

+ 14 - 13
pkg/services/notifications/mailer.go

@@ -17,8 +17,8 @@ import (
 	gomail "gopkg.in/mail.v2"
 )
 
-func send(msg *Message) (int, error) {
-	dialer, err := createDialer()
+func (ns *NotificationService) send(msg *Message) (int, error) {
+	dialer, err := ns.createDialer()
 	if err != nil {
 		return 0, err
 	}
@@ -42,8 +42,8 @@ func send(msg *Message) (int, error) {
 	return len(msg.To), nil
 }
 
-func createDialer() (*gomail.Dialer, error) {
-	host, port, err := net.SplitHostPort(setting.Smtp.Host)
+func (ns *NotificationService) createDialer() (*gomail.Dialer, error) {
+	host, port, err := net.SplitHostPort(ns.Cfg.Smtp.Host)
 
 	if err != nil {
 		return nil, err
@@ -54,30 +54,31 @@ func createDialer() (*gomail.Dialer, error) {
 	}
 
 	tlsconfig := &tls.Config{
-		InsecureSkipVerify: setting.Smtp.SkipVerify,
+		InsecureSkipVerify: ns.Cfg.Smtp.SkipVerify,
 		ServerName:         host,
 	}
 
-	if setting.Smtp.CertFile != "" {
-		cert, err := tls.LoadX509KeyPair(setting.Smtp.CertFile, setting.Smtp.KeyFile)
+	if ns.Cfg.Smtp.CertFile != "" {
+		cert, err := tls.LoadX509KeyPair(ns.Cfg.Smtp.CertFile, ns.Cfg.Smtp.KeyFile)
 		if err != nil {
 			return nil, fmt.Errorf("Could not load cert or key file. error: %v", err)
 		}
 		tlsconfig.Certificates = []tls.Certificate{cert}
 	}
 
-	d := gomail.NewDialer(host, iPort, setting.Smtp.User, setting.Smtp.Password)
+	d := gomail.NewDialer(host, iPort, ns.Cfg.Smtp.User, ns.Cfg.Smtp.Password)
 	d.TLSConfig = tlsconfig
-	if setting.Smtp.EhloIdentity != "" {
-		d.LocalName = setting.Smtp.EhloIdentity
+
+	if ns.Cfg.Smtp.EhloIdentity != "" {
+		d.LocalName = ns.Cfg.Smtp.EhloIdentity
 	} else {
 		d.LocalName = setting.InstanceName
 	}
 	return d, nil
 }
 
-func buildEmailMessage(cmd *m.SendEmailCommand) (*Message, error) {
-	if !setting.Smtp.Enabled {
+func (ns *NotificationService) buildEmailMessage(cmd *m.SendEmailCommand) (*Message, error) {
+	if !ns.Cfg.Smtp.Enabled {
 		return nil, m.ErrSmtpNotEnabled
 	}
 
@@ -121,7 +122,7 @@ func buildEmailMessage(cmd *m.SendEmailCommand) (*Message, error) {
 
 	return &Message{
 		To:           cmd.To,
-		From:         fmt.Sprintf("%s <%s>", setting.Smtp.FromName, setting.Smtp.FromAddress),
+		From:         fmt.Sprintf("%s <%s>", ns.Cfg.Smtp.FromName, ns.Cfg.Smtp.FromAddress),
 		Subject:      subject,
 		Body:         buffer.String(),
 		EmbededFiles: cmd.EmbededFiles,

+ 10 - 8
pkg/services/notifications/notifications.go

@@ -28,7 +28,9 @@ func init() {
 }
 
 type NotificationService struct {
-	Bus          bus.Bus `inject:""`
+	Bus bus.Bus      `inject:""`
+	Cfg *setting.Cfg `inject:""`
+
 	mailQueue    chan *Message
 	webhookQueue chan *Webhook
 	log          log.Logger
@@ -54,13 +56,13 @@ func (ns *NotificationService) Init() error {
 		"Subject": subjectTemplateFunc,
 	})
 
-	templatePattern := filepath.Join(setting.StaticRootPath, setting.Smtp.TemplatesPattern)
+	templatePattern := filepath.Join(setting.StaticRootPath, ns.Cfg.Smtp.TemplatesPattern)
 	_, err := mailTemplates.ParseGlob(templatePattern)
 	if err != nil {
 		return err
 	}
 
-	if !util.IsEmail(setting.Smtp.FromAddress) {
+	if !util.IsEmail(ns.Cfg.Smtp.FromAddress) {
 		return errors.New("Invalid email address for SMTP from_address config")
 	}
 
@@ -81,7 +83,7 @@ func (ns *NotificationService) Run(ctx context.Context) error {
 				ns.log.Error("Failed to send webrequest ", "error", err)
 			}
 		case msg := <-ns.mailQueue:
-			num, err := send(msg)
+			num, err := ns.send(msg)
 			tos := strings.Join(msg.To, "; ")
 			info := ""
 			if err != nil {
@@ -117,7 +119,7 @@ func subjectTemplateFunc(obj map[string]interface{}, value string) string {
 }
 
 func (ns *NotificationService) sendEmailCommandHandlerSync(ctx context.Context, cmd *m.SendEmailCommandSync) error {
-	message, err := buildEmailMessage(&m.SendEmailCommand{
+	message, err := ns.buildEmailMessage(&m.SendEmailCommand{
 		Data:         cmd.Data,
 		Info:         cmd.Info,
 		Template:     cmd.Template,
@@ -130,12 +132,12 @@ func (ns *NotificationService) sendEmailCommandHandlerSync(ctx context.Context,
 		return err
 	}
 
-	_, err = send(message)
+	_, err = ns.send(message)
 	return err
 }
 
 func (ns *NotificationService) sendEmailCommandHandler(cmd *m.SendEmailCommand) error {
-	message, err := buildEmailMessage(cmd)
+	message, err := ns.buildEmailMessage(cmd)
 
 	if err != nil {
 		return err
@@ -205,7 +207,7 @@ func (ns *NotificationService) signUpStartedHandler(evt *events.SignUpStarted) e
 }
 
 func (ns *NotificationService) signUpCompletedHandler(evt *events.SignUpCompleted) error {
-	if evt.Email == "" || !setting.Smtp.SendWelcomeEmailOnSignUp {
+	if evt.Email == "" || !ns.Cfg.Smtp.SendWelcomeEmailOnSignUp {
 		return nil
 	}
 

+ 5 - 4
pkg/services/notifications/notifications_test.go

@@ -19,13 +19,14 @@ func TestNotifications(t *testing.T) {
 
 	Convey("Given the notifications service", t, func() {
 		setting.StaticRootPath = "../../../public/"
-		setting.Smtp.Enabled = true
-		setting.Smtp.TemplatesPattern = "emails/*.html"
-		setting.Smtp.FromAddress = "from@address.com"
-		setting.Smtp.FromName = "Grafana Admin"
 
 		ns := &NotificationService{}
 		ns.Bus = bus.New()
+		ns.Cfg = setting.NewCfg()
+		ns.Cfg.Smtp.Enabled = true
+		ns.Cfg.Smtp.TemplatesPattern = "emails/*.html"
+		ns.Cfg.Smtp.FromAddress = "from@address.com"
+		ns.Cfg.Smtp.FromName = "Grafana Admin"
 
 		err := ns.Init()
 		So(err, ShouldBeNil)

+ 5 - 4
pkg/services/notifications/send_email_integration_test.go

@@ -13,14 +13,15 @@ import (
 func TestEmailIntegrationTest(t *testing.T) {
 	SkipConvey("Given the notifications service", t, func() {
 		setting.StaticRootPath = "../../../public/"
-		setting.Smtp.Enabled = true
-		setting.Smtp.TemplatesPattern = "emails/*.html"
-		setting.Smtp.FromAddress = "from@address.com"
-		setting.Smtp.FromName = "Grafana Admin"
 		setting.BuildVersion = "4.0.0"
 
 		ns := &NotificationService{}
 		ns.Bus = bus.New()
+		ns.Cfg = setting.NewCfg()
+		ns.Cfg.Smtp.Enabled = true
+		ns.Cfg.Smtp.TemplatesPattern = "emails/*.html"
+		ns.Cfg.Smtp.FromAddress = "from@address.com"
+		ns.Cfg.Smtp.FromName = "Grafana Admin"
 
 		err := ns.Init()
 		So(err, ShouldBeNil)

+ 2 - 2
pkg/services/sqlstore/sqlstore.go

@@ -168,7 +168,7 @@ func getEngine() (*xorm.Engine, error) {
 	engine.SetMaxOpenConns(DbCfg.MaxOpenConn)
 	engine.SetMaxIdleConns(DbCfg.MaxIdleConn)
 	engine.SetConnMaxLifetime(time.Second * time.Duration(DbCfg.ConnMaxLifetime))
-	debugSql := setting.Cfg.Section("database").Key("log_queries").MustBool(false)
+	debugSql := setting.Raw.Section("database").Key("log_queries").MustBool(false)
 	if !debugSql {
 		engine.SetLogger(&xorm.DiscardLogger{})
 	} else {
@@ -181,7 +181,7 @@ func getEngine() (*xorm.Engine, error) {
 }
 
 func LoadConfig() {
-	sec := setting.Cfg.Section("database")
+	sec := setting.Raw.Section("database")
 
 	cfgURL := sec.Key("url").String()
 	if len(cfgURL) != 0 {

+ 91 - 72
pkg/setting/setting.go

@@ -137,7 +137,7 @@ var (
 	SessionConnMaxLifetime int64
 
 	// Global setting objects.
-	Cfg          *ini.File
+	Raw          *ini.File
 	ConfRootPath string
 	IsWindows    bool
 
@@ -160,9 +160,6 @@ var (
 	LdapConfigFile  string
 	LdapAllowSignup = true
 
-	// SMTP email settings
-	Smtp SmtpSettings
-
 	// QUOTA
 	Quota QuotaSettings
 
@@ -187,6 +184,16 @@ var (
 	ImageUploadProvider string
 )
 
+type Cfg struct {
+	Raw *ini.File
+
+	// SMTP email settings
+	Smtp SmtpSettings
+
+	ImagesDir                        string
+	DisableBruteForceLoginProtection bool
+}
+
 type CommandLineArgs struct {
 	Config   string
 	HomePath string
@@ -228,9 +235,9 @@ func shouldRedactURLKey(s string) bool {
 	return strings.Contains(uppercased, "DATABASE_URL")
 }
 
-func applyEnvVariableOverrides() error {
+func applyEnvVariableOverrides(file *ini.File) error {
 	appliedEnvOverrides = make([]string, 0)
-	for _, section := range Cfg.Sections() {
+	for _, section := range file.Sections() {
 		for _, key := range section.Keys() {
 			sectionName := strings.ToUpper(strings.Replace(section.Name(), ".", "_", -1))
 			keyName := strings.ToUpper(strings.Replace(key.Name(), ".", "_", -1))
@@ -264,9 +271,9 @@ func applyEnvVariableOverrides() error {
 	return nil
 }
 
-func applyCommandLineDefaultProperties(props map[string]string) {
+func applyCommandLineDefaultProperties(props map[string]string, file *ini.File) {
 	appliedCommandLineProperties = make([]string, 0)
-	for _, section := range Cfg.Sections() {
+	for _, section := range file.Sections() {
 		for _, key := range section.Keys() {
 			keyString := fmt.Sprintf("default.%s.%s", section.Name(), key.Name())
 			value, exists := props[keyString]
@@ -281,8 +288,8 @@ func applyCommandLineDefaultProperties(props map[string]string) {
 	}
 }
 
-func applyCommandLineProperties(props map[string]string) {
-	for _, section := range Cfg.Sections() {
+func applyCommandLineProperties(props map[string]string, file *ini.File) {
+	for _, section := range file.Sections() {
 		sectionName := section.Name() + "."
 		if section.Name() == ini.DEFAULT_SECTION {
 			sectionName = ""
@@ -341,15 +348,15 @@ func evalEnvVarExpression(value string) string {
 	})
 }
 
-func evalConfigValues() {
-	for _, section := range Cfg.Sections() {
+func evalConfigValues(file *ini.File) {
+	for _, section := range file.Sections() {
 		for _, key := range section.Keys() {
 			key.SetValue(evalEnvVarExpression(key.Value()))
 		}
 	}
 }
 
-func loadSpecifedConfigFile(configFile string) error {
+func loadSpecifedConfigFile(configFile string, masterFile *ini.File) error {
 	if configFile == "" {
 		configFile = filepath.Join(HomePath, CustomInitPath)
 		// return without error if custom file does not exist
@@ -371,9 +378,9 @@ func loadSpecifedConfigFile(configFile string) error {
 				continue
 			}
 
-			defaultSec, err := Cfg.GetSection(section.Name())
+			defaultSec, err := masterFile.GetSection(section.Name())
 			if err != nil {
-				defaultSec, _ = Cfg.NewSection(section.Name())
+				defaultSec, _ = masterFile.NewSection(section.Name())
 			}
 			defaultKey, err := defaultSec.GetKey(key.Name())
 			if err != nil {
@@ -387,7 +394,7 @@ func loadSpecifedConfigFile(configFile string) error {
 	return nil
 }
 
-func loadConfiguration(args *CommandLineArgs) error {
+func loadConfiguration(args *CommandLineArgs) (*ini.File, error) {
 	var err error
 
 	// load config defaults
@@ -401,44 +408,44 @@ func loadConfiguration(args *CommandLineArgs) error {
 	}
 
 	// load defaults
-	Cfg, err = ini.Load(defaultConfigFile)
+	parsedFile, err := ini.Load(defaultConfigFile)
 	if err != nil {
 		fmt.Println(fmt.Sprintf("Failed to parse defaults.ini, %v", err))
 		os.Exit(1)
-		return err
+		return nil, err
 	}
 
-	Cfg.BlockMode = false
+	parsedFile.BlockMode = false
 
 	// command line props
 	commandLineProps := getCommandLineProperties(args.Args)
 	// load default overrides
-	applyCommandLineDefaultProperties(commandLineProps)
+	applyCommandLineDefaultProperties(commandLineProps, parsedFile)
 
 	// load specified config file
-	err = loadSpecifedConfigFile(args.Config)
+	err = loadSpecifedConfigFile(args.Config, parsedFile)
 	if err != nil {
-		initLogging()
+		initLogging(parsedFile)
 		log.Fatal(3, err.Error())
 	}
 
 	// apply environment overrides
-	err = applyEnvVariableOverrides()
+	err = applyEnvVariableOverrides(parsedFile)
 	if err != nil {
-		return err
+		return nil, err
 	}
 
 	// apply command line overrides
-	applyCommandLineProperties(commandLineProps)
+	applyCommandLineProperties(commandLineProps, parsedFile)
 
 	// evaluate config values containing environment variables
-	evalConfigValues()
+	evalConfigValues(parsedFile)
 
 	// update data path and logging config
-	DataPath = makeAbsolute(Cfg.Section("paths").Key("data").String(), HomePath)
-	initLogging()
+	DataPath = makeAbsolute(parsedFile.Section("paths").Key("data").String(), HomePath)
+	initLogging(parsedFile)
 
-	return err
+	return parsedFile, err
 }
 
 func pathExists(path string) bool {
@@ -484,23 +491,33 @@ func validateStaticRootPath() error {
 	return nil
 }
 
-func NewConfigContext(args *CommandLineArgs) error {
+func NewCfg() *Cfg {
+	return &Cfg{}
+}
+
+func (cfg *Cfg) Load(args *CommandLineArgs) error {
 	setHomePath(args)
-	err := loadConfiguration(args)
+
+	iniFile, err := loadConfiguration(args)
 	if err != nil {
 		return err
 	}
 
+	cfg.Raw = iniFile
+
+	// Temporary keep global, to make refactor in steps
+	Raw = cfg.Raw
+
 	ApplicationName = "Grafana"
 	if Enterprise {
 		ApplicationName += " Enterprise"
 	}
 
-	Env = Cfg.Section("").Key("app_mode").MustString("development")
-	InstanceName = Cfg.Section("").Key("instance_name").MustString("unknown_instance_name")
-	PluginsPath = makeAbsolute(Cfg.Section("paths").Key("plugins").String(), HomePath)
-	ProvisioningPath = makeAbsolute(Cfg.Section("paths").Key("provisioning").String(), HomePath)
-	server := Cfg.Section("server")
+	Env = iniFile.Section("").Key("app_mode").MustString("development")
+	InstanceName = iniFile.Section("").Key("instance_name").MustString("unknown_instance_name")
+	PluginsPath = makeAbsolute(iniFile.Section("paths").Key("plugins").String(), HomePath)
+	ProvisioningPath = makeAbsolute(iniFile.Section("paths").Key("provisioning").String(), HomePath)
+	server := iniFile.Section("server")
 	AppUrl, AppSubUrl = parseAppUrlAndSubUrl(server)
 
 	Protocol = HTTP
@@ -528,27 +545,28 @@ func NewConfigContext(args *CommandLineArgs) error {
 	}
 
 	// read data proxy settings
-	dataproxy := Cfg.Section("dataproxy")
+	dataproxy := iniFile.Section("dataproxy")
 	DataProxyLogging = dataproxy.Key("logging").MustBool(false)
 
 	// read security settings
-	security := Cfg.Section("security")
+	security := iniFile.Section("security")
 	SecretKey = security.Key("secret_key").String()
 	LogInRememberDays = security.Key("login_remember_days").MustInt()
 	CookieUserName = security.Key("cookie_username").String()
 	CookieRememberName = security.Key("cookie_remember_name").String()
 	DisableGravatar = security.Key("disable_gravatar").MustBool(true)
-	DisableBruteForceLoginProtection = security.Key("disable_brute_force_login_protection").MustBool(false)
+	cfg.DisableBruteForceLoginProtection = security.Key("disable_brute_force_login_protection").MustBool(false)
+	DisableBruteForceLoginProtection = cfg.DisableBruteForceLoginProtection
 
 	// read snapshots settings
-	snapshots := Cfg.Section("snapshots")
+	snapshots := iniFile.Section("snapshots")
 	ExternalSnapshotUrl = snapshots.Key("external_snapshot_url").String()
 	ExternalSnapshotName = snapshots.Key("external_snapshot_name").String()
 	ExternalEnabled = snapshots.Key("external_enabled").MustBool(true)
 	SnapShotRemoveExpired = snapshots.Key("snapshot_remove_expired").MustBool(true)
 
 	// read dashboard settings
-	dashboards := Cfg.Section("dashboards")
+	dashboards := iniFile.Section("dashboards")
 	DashboardVersionsToKeep = dashboards.Key("versions_to_keep").MustInt(20)
 
 	//  read data source proxy white list
@@ -561,7 +579,7 @@ func NewConfigContext(args *CommandLineArgs) error {
 	AdminUser = security.Key("admin_user").String()
 	AdminPassword = security.Key("admin_password").String()
 
-	users := Cfg.Section("users")
+	users := iniFile.Section("users")
 	AllowUserSignUp = users.Key("allow_sign_up").MustBool(true)
 	AllowUserOrgCreate = users.Key("allow_org_create").MustBool(true)
 	AutoAssignOrg = users.Key("auto_assign_org").MustBool(true)
@@ -575,17 +593,17 @@ func NewConfigContext(args *CommandLineArgs) error {
 	ViewersCanEdit = users.Key("viewers_can_edit").MustBool(false)
 
 	// auth
-	auth := Cfg.Section("auth")
+	auth := iniFile.Section("auth")
 	DisableLoginForm = auth.Key("disable_login_form").MustBool(false)
 	DisableSignoutMenu = auth.Key("disable_signout_menu").MustBool(false)
 
 	// anonymous access
-	AnonymousEnabled = Cfg.Section("auth.anonymous").Key("enabled").MustBool(false)
-	AnonymousOrgName = Cfg.Section("auth.anonymous").Key("org_name").String()
-	AnonymousOrgRole = Cfg.Section("auth.anonymous").Key("org_role").String()
+	AnonymousEnabled = iniFile.Section("auth.anonymous").Key("enabled").MustBool(false)
+	AnonymousOrgName = iniFile.Section("auth.anonymous").Key("org_name").String()
+	AnonymousOrgRole = iniFile.Section("auth.anonymous").Key("org_role").String()
 
 	// auth proxy
-	authProxy := Cfg.Section("auth.proxy")
+	authProxy := iniFile.Section("auth.proxy")
 	AuthProxyEnabled = authProxy.Key("enabled").MustBool(false)
 	AuthProxyHeaderName = authProxy.Key("header_name").String()
 	AuthProxyHeaderProperty = authProxy.Key("header_property").String()
@@ -594,63 +612,64 @@ func NewConfigContext(args *CommandLineArgs) error {
 	AuthProxyWhitelist = authProxy.Key("whitelist").String()
 
 	// basic auth
-	authBasic := Cfg.Section("auth.basic")
+	authBasic := iniFile.Section("auth.basic")
 	BasicAuthEnabled = authBasic.Key("enabled").MustBool(true)
 
 	// global plugin settings
-	PluginAppsSkipVerifyTLS = Cfg.Section("plugins").Key("app_tls_skip_verify_insecure").MustBool(false)
+	PluginAppsSkipVerifyTLS = iniFile.Section("plugins").Key("app_tls_skip_verify_insecure").MustBool(false)
 
 	// PhantomJS rendering
-	ImagesDir = filepath.Join(DataPath, "png")
+	cfg.ImagesDir = filepath.Join(DataPath, "png")
+	ImagesDir = cfg.ImagesDir
 	PhantomDir = filepath.Join(HomePath, "tools/phantomjs")
 
-	analytics := Cfg.Section("analytics")
+	analytics := iniFile.Section("analytics")
 	ReportingEnabled = analytics.Key("reporting_enabled").MustBool(true)
 	CheckForUpdates = analytics.Key("check_for_updates").MustBool(true)
 	GoogleAnalyticsId = analytics.Key("google_analytics_ua_id").String()
 	GoogleTagManagerId = analytics.Key("google_tag_manager_id").String()
 
-	ldapSec := Cfg.Section("auth.ldap")
+	ldapSec := iniFile.Section("auth.ldap")
 	LdapEnabled = ldapSec.Key("enabled").MustBool(false)
 	LdapConfigFile = ldapSec.Key("config_file").String()
 	LdapAllowSignup = ldapSec.Key("allow_sign_up").MustBool(true)
 
-	alerting := Cfg.Section("alerting")
+	alerting := iniFile.Section("alerting")
 	AlertingEnabled = alerting.Key("enabled").MustBool(true)
 	ExecuteAlerts = alerting.Key("execute_alerts").MustBool(true)
 
-	explore := Cfg.Section("explore")
+	explore := iniFile.Section("explore")
 	ExploreEnabled = explore.Key("enabled").MustBool(false)
 
-	readSessionConfig()
-	readSmtpSettings()
-	readQuotaSettings()
+	cfg.readSessionConfig()
+	cfg.readSmtpSettings()
+	cfg.readQuotaSettings()
 
-	if VerifyEmailEnabled && !Smtp.Enabled {
+	if VerifyEmailEnabled && !cfg.Smtp.Enabled {
 		log.Warn("require_email_validation is enabled but smtp is disabled")
 	}
 
 	// check old key  name
-	GrafanaComUrl = Cfg.Section("grafana_net").Key("url").MustString("")
+	GrafanaComUrl = iniFile.Section("grafana_net").Key("url").MustString("")
 	if GrafanaComUrl == "" {
-		GrafanaComUrl = Cfg.Section("grafana_com").Key("url").MustString("https://grafana.com")
+		GrafanaComUrl = iniFile.Section("grafana_com").Key("url").MustString("https://grafana.com")
 	}
 
-	imageUploadingSection := Cfg.Section("external_image_storage")
+	imageUploadingSection := iniFile.Section("external_image_storage")
 	ImageUploadProvider = imageUploadingSection.Key("provider").MustString("")
 	return nil
 }
 
-func readSessionConfig() {
-	sec := Cfg.Section("session")
+func (cfg *Cfg) readSessionConfig() {
+	sec := cfg.Raw.Section("session")
 	SessionOptions = session.Options{}
 	SessionOptions.Provider = sec.Key("provider").In("memory", []string{"memory", "file", "redis", "mysql", "postgres", "memcache"})
 	SessionOptions.ProviderConfig = strings.Trim(sec.Key("provider_config").String(), "\" ")
 	SessionOptions.CookieName = sec.Key("cookie_name").MustString("grafana_sess")
 	SessionOptions.CookiePath = AppSubUrl
 	SessionOptions.Secure = sec.Key("cookie_secure").MustBool()
-	SessionOptions.Gclifetime = Cfg.Section("session").Key("gc_interval_time").MustInt64(86400)
-	SessionOptions.Maxlifetime = Cfg.Section("session").Key("session_life_time").MustInt64(86400)
+	SessionOptions.Gclifetime = cfg.Raw.Section("session").Key("gc_interval_time").MustInt64(86400)
+	SessionOptions.Maxlifetime = cfg.Raw.Section("session").Key("session_life_time").MustInt64(86400)
 	SessionOptions.IDLength = 16
 
 	if SessionOptions.Provider == "file" {
@@ -662,21 +681,21 @@ func readSessionConfig() {
 		SessionOptions.CookiePath = "/"
 	}
 
-	SessionConnMaxLifetime = Cfg.Section("session").Key("conn_max_lifetime").MustInt64(14400)
+	SessionConnMaxLifetime = cfg.Raw.Section("session").Key("conn_max_lifetime").MustInt64(14400)
 }
 
-func initLogging() {
+func initLogging(file *ini.File) {
 	// split on comma
-	LogModes = strings.Split(Cfg.Section("log").Key("mode").MustString("console"), ",")
+	LogModes = strings.Split(file.Section("log").Key("mode").MustString("console"), ",")
 	// also try space
 	if len(LogModes) == 1 {
-		LogModes = strings.Split(Cfg.Section("log").Key("mode").MustString("console"), " ")
+		LogModes = strings.Split(file.Section("log").Key("mode").MustString("console"), " ")
 	}
-	LogsPath = makeAbsolute(Cfg.Section("paths").Key("logs").String(), HomePath)
-	log.ReadLoggingConfig(LogModes, LogsPath, Cfg)
+	LogsPath = makeAbsolute(file.Section("paths").Key("logs").String(), HomePath)
+	log.ReadLoggingConfig(LogModes, LogsPath, file)
 }
 
-func LogConfigurationInfo() {
+func (cfg *Cfg) LogConfigSources() {
 	var text bytes.Buffer
 
 	for _, file := range configFiles {

+ 2 - 2
pkg/setting/setting_quota.go

@@ -63,9 +63,9 @@ type QuotaSettings struct {
 	Global  *GlobalQuota
 }
 
-func readQuotaSettings() {
+func (cfg *Cfg) readQuotaSettings() {
 	// set global defaults.
-	quota := Cfg.Section("quota")
+	quota := cfg.Raw.Section("quota")
 	Quota.Enabled = quota.Key("enabled").MustBool(false)
 
 	// per ORG Limits

+ 15 - 15
pkg/setting/setting_smtp.go

@@ -16,20 +16,20 @@ type SmtpSettings struct {
 	TemplatesPattern         string
 }
 
-func readSmtpSettings() {
-	sec := Cfg.Section("smtp")
-	Smtp.Enabled = sec.Key("enabled").MustBool(false)
-	Smtp.Host = sec.Key("host").String()
-	Smtp.User = sec.Key("user").String()
-	Smtp.Password = sec.Key("password").String()
-	Smtp.CertFile = sec.Key("cert_file").String()
-	Smtp.KeyFile = sec.Key("key_file").String()
-	Smtp.FromAddress = sec.Key("from_address").String()
-	Smtp.FromName = sec.Key("from_name").String()
-	Smtp.EhloIdentity = sec.Key("ehlo_identity").String()
-	Smtp.SkipVerify = sec.Key("skip_verify").MustBool(false)
+func (cfg *Cfg) readSmtpSettings() {
+	sec := cfg.Raw.Section("smtp")
+	cfg.Smtp.Enabled = sec.Key("enabled").MustBool(false)
+	cfg.Smtp.Host = sec.Key("host").String()
+	cfg.Smtp.User = sec.Key("user").String()
+	cfg.Smtp.Password = sec.Key("password").String()
+	cfg.Smtp.CertFile = sec.Key("cert_file").String()
+	cfg.Smtp.KeyFile = sec.Key("key_file").String()
+	cfg.Smtp.FromAddress = sec.Key("from_address").String()
+	cfg.Smtp.FromName = sec.Key("from_name").String()
+	cfg.Smtp.EhloIdentity = sec.Key("ehlo_identity").String()
+	cfg.Smtp.SkipVerify = sec.Key("skip_verify").MustBool(false)
 
-	emails := Cfg.Section("emails")
-	Smtp.SendWelcomeEmailOnSignUp = emails.Key("welcome_email_on_sign_up").MustBool(false)
-	Smtp.TemplatesPattern = emails.Key("templates_pattern").MustString("emails/*.html")
+	emails := cfg.Raw.Section("emails")
+	cfg.Smtp.SendWelcomeEmailOnSignUp = emails.Key("welcome_email_on_sign_up").MustBool(false)
+	cfg.Smtp.TemplatesPattern = emails.Key("templates_pattern").MustString("emails/*.html")
 }

+ 34 - 15
pkg/setting/setting_test.go

@@ -15,7 +15,8 @@ func TestLoadingSettings(t *testing.T) {
 		skipStaticRootValidation = true
 
 		Convey("Given the default ini files", func() {
-			err := NewConfigContext(&CommandLineArgs{HomePath: "../../"})
+			cfg := NewCfg()
+			err := cfg.Load(&CommandLineArgs{HomePath: "../../"})
 			So(err, ShouldBeNil)
 
 			So(AdminUser, ShouldEqual, "admin")
@@ -23,7 +24,9 @@ func TestLoadingSettings(t *testing.T) {
 
 		Convey("Should be able to override via environment variables", func() {
 			os.Setenv("GF_SECURITY_ADMIN_USER", "superduper")
-			NewConfigContext(&CommandLineArgs{HomePath: "../../"})
+
+			cfg := NewCfg()
+			cfg.Load(&CommandLineArgs{HomePath: "../../"})
 
 			So(AdminUser, ShouldEqual, "superduper")
 			So(DataPath, ShouldEqual, filepath.Join(HomePath, "data"))
@@ -32,21 +35,27 @@ func TestLoadingSettings(t *testing.T) {
 
 		Convey("Should replace password when defined in environment", func() {
 			os.Setenv("GF_SECURITY_ADMIN_PASSWORD", "supersecret")
-			NewConfigContext(&CommandLineArgs{HomePath: "../../"})
+
+			cfg := NewCfg()
+			cfg.Load(&CommandLineArgs{HomePath: "../../"})
 
 			So(appliedEnvOverrides, ShouldContain, "GF_SECURITY_ADMIN_PASSWORD=*********")
 		})
 
 		Convey("Should return an error when url is invalid", func() {
 			os.Setenv("GF_DATABASE_URL", "postgres.%31://grafana:secret@postgres:5432/grafana")
-			err := NewConfigContext(&CommandLineArgs{HomePath: "../../"})
+
+			cfg := NewCfg()
+			err := cfg.Load(&CommandLineArgs{HomePath: "../../"})
 
 			So(err, ShouldNotBeNil)
 		})
 
 		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: "../../"})
+
+			cfg := NewCfg()
+			cfg.Load(&CommandLineArgs{HomePath: "../../"})
 
 			So(appliedEnvOverrides, ShouldContain, "GF_DATABASE_URL=mysql://user:-redacted-@localhost:3306/database")
 		})
@@ -61,14 +70,16 @@ func TestLoadingSettings(t *testing.T) {
 
 		Convey("Should be able to override via command line", func() {
 			if runtime.GOOS == "windows" {
-				NewConfigContext(&CommandLineArgs{
+				cfg := NewCfg()
+				cfg.Load(&CommandLineArgs{
 					HomePath: "../../",
 					Args:     []string{`cfg:paths.data=c:\tmp\data`, `cfg:paths.logs=c:\tmp\logs`},
 				})
 				So(DataPath, ShouldEqual, `c:\tmp\data`)
 				So(LogsPath, ShouldEqual, `c:\tmp\logs`)
 			} else {
-				NewConfigContext(&CommandLineArgs{
+				cfg := NewCfg()
+				cfg.Load(&CommandLineArgs{
 					HomePath: "../../",
 					Args:     []string{"cfg:paths.data=/tmp/data", "cfg:paths.logs=/tmp/logs"},
 				})
@@ -79,7 +90,8 @@ func TestLoadingSettings(t *testing.T) {
 		})
 
 		Convey("Should be able to override defaults via command line", func() {
-			NewConfigContext(&CommandLineArgs{
+			cfg := NewCfg()
+			cfg.Load(&CommandLineArgs{
 				HomePath: "../../",
 				Args: []string{
 					"cfg:default.server.domain=test2",
@@ -92,7 +104,8 @@ func TestLoadingSettings(t *testing.T) {
 
 		Convey("Defaults can be overridden in specified config file", func() {
 			if runtime.GOOS == "windows" {
-				NewConfigContext(&CommandLineArgs{
+				cfg := NewCfg()
+				cfg.Load(&CommandLineArgs{
 					HomePath: "../../",
 					Config:   filepath.Join(HomePath, "tests/config-files/override_windows.ini"),
 					Args:     []string{`cfg:default.paths.data=c:\tmp\data`},
@@ -100,7 +113,8 @@ func TestLoadingSettings(t *testing.T) {
 
 				So(DataPath, ShouldEqual, `c:\tmp\override`)
 			} else {
-				NewConfigContext(&CommandLineArgs{
+				cfg := NewCfg()
+				cfg.Load(&CommandLineArgs{
 					HomePath: "../../",
 					Config:   filepath.Join(HomePath, "tests/config-files/override.ini"),
 					Args:     []string{"cfg:default.paths.data=/tmp/data"},
@@ -112,7 +126,8 @@ func TestLoadingSettings(t *testing.T) {
 
 		Convey("Command line overrides specified config file", func() {
 			if runtime.GOOS == "windows" {
-				NewConfigContext(&CommandLineArgs{
+				cfg := NewCfg()
+				cfg.Load(&CommandLineArgs{
 					HomePath: "../../",
 					Config:   filepath.Join(HomePath, "tests/config-files/override_windows.ini"),
 					Args:     []string{`cfg:paths.data=c:\tmp\data`},
@@ -120,7 +135,8 @@ func TestLoadingSettings(t *testing.T) {
 
 				So(DataPath, ShouldEqual, `c:\tmp\data`)
 			} else {
-				NewConfigContext(&CommandLineArgs{
+				cfg := NewCfg()
+				cfg.Load(&CommandLineArgs{
 					HomePath: "../../",
 					Config:   filepath.Join(HomePath, "tests/config-files/override.ini"),
 					Args:     []string{"cfg:paths.data=/tmp/data"},
@@ -133,7 +149,8 @@ func TestLoadingSettings(t *testing.T) {
 		Convey("Can use environment variables in config values", func() {
 			if runtime.GOOS == "windows" {
 				os.Setenv("GF_DATA_PATH", `c:\tmp\env_override`)
-				NewConfigContext(&CommandLineArgs{
+				cfg := NewCfg()
+				cfg.Load(&CommandLineArgs{
 					HomePath: "../../",
 					Args:     []string{"cfg:paths.data=${GF_DATA_PATH}"},
 				})
@@ -141,7 +158,8 @@ func TestLoadingSettings(t *testing.T) {
 				So(DataPath, ShouldEqual, `c:\tmp\env_override`)
 			} else {
 				os.Setenv("GF_DATA_PATH", "/tmp/env_override")
-				NewConfigContext(&CommandLineArgs{
+				cfg := NewCfg()
+				cfg.Load(&CommandLineArgs{
 					HomePath: "../../",
 					Args:     []string{"cfg:paths.data=${GF_DATA_PATH}"},
 				})
@@ -151,7 +169,8 @@ func TestLoadingSettings(t *testing.T) {
 		})
 
 		Convey("instance_name default to hostname even if hostname env is empty", func() {
-			NewConfigContext(&CommandLineArgs{
+			cfg := NewCfg()
+			cfg.Load(&CommandLineArgs{
 				HomePath: "../../",
 			})
 

+ 1 - 1
pkg/social/social.go

@@ -58,7 +58,7 @@ func NewOAuthService() {
 	allOauthes := []string{"github", "google", "generic_oauth", "grafananet", "grafana_com"}
 
 	for _, name := range allOauthes {
-		sec := setting.Cfg.Section("auth." + name)
+		sec := setting.Raw.Section("auth." + name)
 		info := &setting.OAuthInfo{
 			ClientId:       sec.Key("client_id").String(),
 			ClientSecret:   sec.Key("client_secret").String(),

+ 1 - 1
pkg/tracing/tracing.go

@@ -32,7 +32,7 @@ func Init(file *ini.File) (io.Closer, error) {
 func parseSettings(file *ini.File) *TracingSettings {
 	settings := &TracingSettings{}
 
-	var section, err = setting.Cfg.GetSection("tracing.jaeger")
+	var section, err = setting.Raw.GetSection("tracing.jaeger")
 	if err != nil {
 		return settings
 	}

+ 2 - 1
pkg/tsdb/influxdb/response_parser_test.go

@@ -13,7 +13,8 @@ func TestInfluxdbResponseParser(t *testing.T) {
 		Convey("Response parser", func() {
 			parser := &ResponseParser{}
 
-			setting.NewConfigContext(&setting.CommandLineArgs{
+			cfg := setting.NewCfg()
+			cfg.Load(&setting.CommandLineArgs{
 				HomePath: "../../../",
 			})
 

+ 2 - 1
pkg/tsdb/interval_test.go

@@ -10,7 +10,8 @@ import (
 
 func TestInterval(t *testing.T) {
 	Convey("Default interval ", t, func() {
-		setting.NewConfigContext(&setting.CommandLineArgs{
+		cfg := setting.NewCfg()
+		cfg.Load(&setting.CommandLineArgs{
 			HomePath: "../../",
 		})