Merge branch 'master' into WPH95-feature/add_es_alerting
This commit is contained in:
+19
-10
@@ -26,9 +26,14 @@ import (
|
||||
"github.com/grafana/grafana/pkg/middleware"
|
||||
"github.com/grafana/grafana/pkg/models"
|
||||
"github.com/grafana/grafana/pkg/plugins"
|
||||
"github.com/grafana/grafana/pkg/registry"
|
||||
"github.com/grafana/grafana/pkg/setting"
|
||||
)
|
||||
|
||||
func init() {
|
||||
registry.RegisterService(&HTTPServer{})
|
||||
}
|
||||
|
||||
type HTTPServer struct {
|
||||
log log.Logger
|
||||
macaron *macaron.Macaron
|
||||
@@ -41,12 +46,14 @@ type HTTPServer struct {
|
||||
Bus bus.Bus `inject:""`
|
||||
}
|
||||
|
||||
func (hs *HTTPServer) Init() {
|
||||
func (hs *HTTPServer) Init() error {
|
||||
hs.log = log.New("http.server")
|
||||
hs.cache = gocache.New(5*time.Minute, 10*time.Minute)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (hs *HTTPServer) Start(ctx context.Context) error {
|
||||
func (hs *HTTPServer) Run(ctx context.Context) error {
|
||||
var err error
|
||||
|
||||
hs.context = ctx
|
||||
@@ -57,17 +64,18 @@ func (hs *HTTPServer) Start(ctx context.Context) error {
|
||||
hs.streamManager.Run(ctx)
|
||||
|
||||
listenAddr := fmt.Sprintf("%s:%s", setting.HttpAddr, setting.HttpPort)
|
||||
hs.log.Info("Initializing HTTP Server", "address", listenAddr, "protocol", setting.Protocol, "subUrl", setting.AppSubUrl, "socket", setting.SocketPath)
|
||||
hs.log.Info("HTTP Server Listen", "address", listenAddr, "protocol", setting.Protocol, "subUrl", setting.AppSubUrl, "socket", setting.SocketPath)
|
||||
|
||||
hs.httpSrv = &http.Server{Addr: listenAddr, Handler: hs.macaron}
|
||||
|
||||
// handle http shutdown on server context done
|
||||
go func() {
|
||||
<-ctx.Done()
|
||||
// Hacky fix for race condition between ListenAndServe and Shutdown
|
||||
time.Sleep(time.Millisecond * 100)
|
||||
if err := hs.httpSrv.Shutdown(context.Background()); err != nil {
|
||||
hs.log.Error("Failed to shutdown server", "error", err)
|
||||
}
|
||||
hs.log.Info("Stopped HTTP Server")
|
||||
}()
|
||||
|
||||
switch setting.Protocol {
|
||||
@@ -106,12 +114,6 @@ func (hs *HTTPServer) Start(ctx context.Context) error {
|
||||
return err
|
||||
}
|
||||
|
||||
func (hs *HTTPServer) Shutdown(ctx context.Context) error {
|
||||
err := hs.httpSrv.Shutdown(ctx)
|
||||
hs.log.Info("Stopped HTTP server")
|
||||
return err
|
||||
}
|
||||
|
||||
func (hs *HTTPServer) listenAndServeTLS(certfile, keyfile string) error {
|
||||
if certfile == "" {
|
||||
return fmt.Errorf("cert_file cannot be empty when using HTTPS")
|
||||
@@ -172,6 +174,7 @@ func (hs *HTTPServer) newMacaron() *macaron.Macaron {
|
||||
hs.mapStatic(m, route.Directory, "", pluginRoute)
|
||||
}
|
||||
|
||||
hs.mapStatic(m, setting.StaticRootPath, "build", "public/build")
|
||||
hs.mapStatic(m, setting.StaticRootPath, "", "public")
|
||||
hs.mapStatic(m, setting.StaticRootPath, "robots.txt", "robots.txt")
|
||||
|
||||
@@ -239,6 +242,12 @@ func (hs *HTTPServer) mapStatic(m *macaron.Macaron, rootDir string, dir string,
|
||||
c.Resp.Header().Set("Cache-Control", "public, max-age=3600")
|
||||
}
|
||||
|
||||
if prefix == "public/build" {
|
||||
headers = func(c *macaron.Context) {
|
||||
c.Resp.Header().Set("Cache-Control", "public, max-age=31536000")
|
||||
}
|
||||
}
|
||||
|
||||
if setting.Env == setting.DEV {
|
||||
headers = func(c *macaron.Context) {
|
||||
c.Resp.Header().Set("Cache-Control", "max-age=0, must-revalidate, no-cache")
|
||||
|
||||
@@ -40,7 +40,6 @@ var enterprise string
|
||||
var configFile = flag.String("config", "", "path to config file")
|
||||
var homePath = flag.String("homepath", "", "path to grafana install/home path, defaults to working directory")
|
||||
var pidFile = flag.String("pidfile", "", "path to pid file")
|
||||
var exitChan = make(chan int)
|
||||
|
||||
func main() {
|
||||
v := flag.Bool("v", false, "prints current version and exits")
|
||||
@@ -82,29 +81,20 @@ func main() {
|
||||
setting.Enterprise, _ = strconv.ParseBool(enterprise)
|
||||
|
||||
metrics.M_Grafana_Version.WithLabelValues(version).Set(1)
|
||||
shutdownCompleted := make(chan int)
|
||||
|
||||
server := NewGrafanaServer()
|
||||
|
||||
go listenToSystemSignals(server, shutdownCompleted)
|
||||
go listenToSystemSignals(server)
|
||||
|
||||
go func() {
|
||||
code := 0
|
||||
if err := server.Start(); err != nil {
|
||||
log.Error2("Startup failed", "error", err)
|
||||
code = 1
|
||||
}
|
||||
err := server.Run()
|
||||
|
||||
exitChan <- code
|
||||
}()
|
||||
|
||||
code := <-shutdownCompleted
|
||||
log.Info2("Grafana shutdown completed.", "code", code)
|
||||
trace.Stop()
|
||||
log.Close()
|
||||
os.Exit(code)
|
||||
|
||||
server.Exit(err)
|
||||
}
|
||||
|
||||
func listenToSystemSignals(server *GrafanaServerImpl, shutdownCompleted chan int) {
|
||||
var code int
|
||||
func listenToSystemSignals(server *GrafanaServerImpl) {
|
||||
signalChan := make(chan os.Signal, 1)
|
||||
ignoreChan := make(chan os.Signal, 1)
|
||||
|
||||
@@ -113,12 +103,6 @@ func listenToSystemSignals(server *GrafanaServerImpl, shutdownCompleted chan int
|
||||
|
||||
select {
|
||||
case sig := <-signalChan:
|
||||
trace.Stop() // Stops trace if profiling has been enabled
|
||||
server.Shutdown(0, fmt.Sprintf("system signal: %s", sig))
|
||||
shutdownCompleted <- 0
|
||||
case code = <-exitChan:
|
||||
trace.Stop() // Stops trace if profiling has been enabled
|
||||
server.Shutdown(code, "startup error")
|
||||
shutdownCompleted <- code
|
||||
server.Shutdown(fmt.Sprintf("System signal: %s", sig))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,14 +17,12 @@ import (
|
||||
"github.com/grafana/grafana/pkg/middleware"
|
||||
"github.com/grafana/grafana/pkg/registry"
|
||||
"github.com/grafana/grafana/pkg/services/dashboards"
|
||||
"github.com/grafana/grafana/pkg/services/provisioning"
|
||||
|
||||
"golang.org/x/sync/errgroup"
|
||||
|
||||
"github.com/grafana/grafana/pkg/api"
|
||||
"github.com/grafana/grafana/pkg/log"
|
||||
"github.com/grafana/grafana/pkg/login"
|
||||
"github.com/grafana/grafana/pkg/metrics"
|
||||
"github.com/grafana/grafana/pkg/services/sqlstore"
|
||||
"github.com/grafana/grafana/pkg/setting"
|
||||
|
||||
@@ -33,10 +31,12 @@ import (
|
||||
|
||||
// self registering services
|
||||
_ "github.com/grafana/grafana/pkg/extensions"
|
||||
_ "github.com/grafana/grafana/pkg/metrics"
|
||||
_ "github.com/grafana/grafana/pkg/plugins"
|
||||
_ "github.com/grafana/grafana/pkg/services/alerting"
|
||||
_ "github.com/grafana/grafana/pkg/services/cleanup"
|
||||
_ "github.com/grafana/grafana/pkg/services/notifications"
|
||||
_ "github.com/grafana/grafana/pkg/services/provisioning"
|
||||
_ "github.com/grafana/grafana/pkg/services/search"
|
||||
)
|
||||
|
||||
@@ -54,17 +54,19 @@ func NewGrafanaServer() *GrafanaServerImpl {
|
||||
}
|
||||
|
||||
type GrafanaServerImpl struct {
|
||||
context context.Context
|
||||
shutdownFn context.CancelFunc
|
||||
childRoutines *errgroup.Group
|
||||
log log.Logger
|
||||
cfg *setting.Cfg
|
||||
context context.Context
|
||||
shutdownFn context.CancelFunc
|
||||
childRoutines *errgroup.Group
|
||||
log log.Logger
|
||||
cfg *setting.Cfg
|
||||
shutdownReason string
|
||||
shutdownInProgress bool
|
||||
|
||||
RouteRegister api.RouteRegister `inject:""`
|
||||
HttpServer *api.HTTPServer `inject:""`
|
||||
}
|
||||
|
||||
func (g *GrafanaServerImpl) Start() error {
|
||||
func (g *GrafanaServerImpl) Run() error {
|
||||
g.loadConfiguration()
|
||||
g.writePIDFile()
|
||||
|
||||
@@ -72,14 +74,9 @@ func (g *GrafanaServerImpl) Start() error {
|
||||
sqlstore.NewEngine() // TODO: this should return an error
|
||||
sqlstore.EnsureAdminUser()
|
||||
|
||||
metrics.Init(g.cfg.Raw)
|
||||
login.Init()
|
||||
social.NewOAuthService()
|
||||
|
||||
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(g.cfg.Raw)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Tracing settings is not valid. error: %v", err)
|
||||
@@ -91,7 +88,6 @@ func (g *GrafanaServerImpl) Start() error {
|
||||
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{}})
|
||||
|
||||
// self registered services
|
||||
services := registry.GetServices()
|
||||
@@ -117,7 +113,7 @@ func (g *GrafanaServerImpl) Start() error {
|
||||
g.log.Info("Initializing " + reflect.TypeOf(service).Elem().Name())
|
||||
|
||||
if err := service.Init(); err != nil {
|
||||
return fmt.Errorf("Service init failed %v", err)
|
||||
return fmt.Errorf("Service init failed: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -133,14 +129,31 @@ func (g *GrafanaServerImpl) Start() error {
|
||||
}
|
||||
|
||||
g.childRoutines.Go(func() error {
|
||||
// Skip starting new service when shutting down
|
||||
// Can happen when service stop/return during startup
|
||||
if g.shutdownInProgress {
|
||||
return nil
|
||||
}
|
||||
|
||||
err := service.Run(g.context)
|
||||
g.log.Info("Stopped "+reflect.TypeOf(service).Elem().Name(), "reason", err)
|
||||
|
||||
// If error is not canceled then the service crashed
|
||||
if err != context.Canceled && err != nil {
|
||||
g.log.Error("Stopped "+reflect.TypeOf(service).Elem().Name(), "reason", err)
|
||||
} else {
|
||||
g.log.Info("Stopped "+reflect.TypeOf(service).Elem().Name(), "reason", err)
|
||||
}
|
||||
|
||||
// Mark that we are in shutdown mode
|
||||
// So more services are not started
|
||||
g.shutdownInProgress = true
|
||||
return err
|
||||
})
|
||||
}
|
||||
|
||||
sendSystemdNotification("READY=1")
|
||||
return g.startHttpServer()
|
||||
|
||||
return g.childRoutines.Wait()
|
||||
}
|
||||
|
||||
func (g *GrafanaServerImpl) loadConfiguration() {
|
||||
@@ -159,28 +172,29 @@ func (g *GrafanaServerImpl) loadConfiguration() {
|
||||
g.cfg.LogConfigSources()
|
||||
}
|
||||
|
||||
func (g *GrafanaServerImpl) startHttpServer() error {
|
||||
g.HttpServer.Init()
|
||||
|
||||
err := g.HttpServer.Start(g.context)
|
||||
|
||||
if err != nil {
|
||||
return fmt.Errorf("Fail to start server. error: %v", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (g *GrafanaServerImpl) Shutdown(code int, reason string) {
|
||||
g.log.Info("Shutdown started", "code", code, "reason", reason)
|
||||
func (g *GrafanaServerImpl) Shutdown(reason string) {
|
||||
g.log.Info("Shutdown started", "reason", reason)
|
||||
g.shutdownReason = reason
|
||||
g.shutdownInProgress = true
|
||||
|
||||
// call cancel func on root context
|
||||
g.shutdownFn()
|
||||
|
||||
// wait for child routines
|
||||
if err := g.childRoutines.Wait(); err != nil && err != context.Canceled {
|
||||
g.log.Error("Server shutdown completed", "error", err)
|
||||
g.childRoutines.Wait()
|
||||
}
|
||||
|
||||
func (g *GrafanaServerImpl) Exit(reason error) {
|
||||
// default exit code is 1
|
||||
code := 1
|
||||
|
||||
if reason == context.Canceled && g.shutdownReason != "" {
|
||||
reason = fmt.Errorf(g.shutdownReason)
|
||||
code = 0
|
||||
}
|
||||
|
||||
g.log.Error("Server shutdown", "reason", reason)
|
||||
os.Exit(code)
|
||||
}
|
||||
|
||||
func (g *GrafanaServerImpl) writePIDFile() {
|
||||
|
||||
@@ -106,6 +106,15 @@ func (f Float) String() string {
|
||||
return fmt.Sprintf("%1.3f", f.Float64)
|
||||
}
|
||||
|
||||
// FullString returns float as string in full precision
|
||||
func (f Float) FullString() string {
|
||||
if !f.Valid {
|
||||
return "null"
|
||||
}
|
||||
|
||||
return fmt.Sprintf("%f", f.Float64)
|
||||
}
|
||||
|
||||
// SetValid changes this Float's value and also sets it to be non-null.
|
||||
func (f *Float) SetValid(n float64) {
|
||||
f.Float64 = n
|
||||
|
||||
@@ -53,6 +53,20 @@ func TestLdapAuther(t *testing.T) {
|
||||
So(result, ShouldEqual, user1)
|
||||
})
|
||||
|
||||
ldapAutherScenario("Given group match with different case", func(sc *scenarioContext) {
|
||||
ldapAuther := NewLdapAuthenticator(&LdapServerConf{
|
||||
LdapGroups: []*LdapGroupToOrgRole{
|
||||
{GroupDN: "cn=users", OrgRole: "Admin"},
|
||||
},
|
||||
})
|
||||
|
||||
sc.userQueryReturns(user1)
|
||||
|
||||
result, err := ldapAuther.GetGrafanaUserFor(nil, &LdapUserInfo{MemberOf: []string{"CN=users"}})
|
||||
So(err, ShouldBeNil)
|
||||
So(result, ShouldEqual, user1)
|
||||
})
|
||||
|
||||
ldapAutherScenario("Given no existing grafana user", func(sc *scenarioContext) {
|
||||
ldapAuther := NewLdapAuthenticator(&LdapServerConf{
|
||||
LdapGroups: []*LdapGroupToOrgRole{
|
||||
|
||||
@@ -1,5 +1,9 @@
|
||||
package login
|
||||
|
||||
import (
|
||||
"strings"
|
||||
)
|
||||
|
||||
type LdapUserInfo struct {
|
||||
DN string
|
||||
FirstName string
|
||||
@@ -15,7 +19,7 @@ func (u *LdapUserInfo) isMemberOf(group string) bool {
|
||||
}
|
||||
|
||||
for _, member := range u.MemberOf {
|
||||
if member == group {
|
||||
if strings.EqualFold(member, group) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,38 +0,0 @@
|
||||
package metrics
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
ini "gopkg.in/ini.v1"
|
||||
|
||||
"github.com/grafana/grafana/pkg/log"
|
||||
"github.com/grafana/grafana/pkg/metrics/graphitebridge"
|
||||
)
|
||||
|
||||
var metricsLogger log.Logger = log.New("metrics")
|
||||
|
||||
type logWrapper struct {
|
||||
logger log.Logger
|
||||
}
|
||||
|
||||
func (lw *logWrapper) Println(v ...interface{}) {
|
||||
lw.logger.Info("graphite metric bridge", v...)
|
||||
}
|
||||
|
||||
func Init(file *ini.File) {
|
||||
cfg := ReadSettings(file)
|
||||
internalInit(cfg)
|
||||
}
|
||||
|
||||
func internalInit(settings *MetricSettings) {
|
||||
initMetricVars(settings)
|
||||
|
||||
if settings.GraphiteBridgeConfig != nil {
|
||||
bridge, err := graphitebridge.NewBridge(settings.GraphiteBridgeConfig)
|
||||
if err != nil {
|
||||
metricsLogger.Error("failed to create graphite bridge", "error", err)
|
||||
} else {
|
||||
go bridge.Run(context.Background())
|
||||
}
|
||||
}
|
||||
}
|
||||
+1
-23
@@ -279,7 +279,7 @@ func init() {
|
||||
}, []string{"version"})
|
||||
}
|
||||
|
||||
func initMetricVars(settings *MetricSettings) {
|
||||
func initMetricVars() {
|
||||
prometheus.MustRegister(
|
||||
M_Instance_Start,
|
||||
M_Page_Status,
|
||||
@@ -316,28 +316,6 @@ func initMetricVars(settings *MetricSettings) {
|
||||
M_StatTotal_Playlists,
|
||||
M_Grafana_Version)
|
||||
|
||||
go instrumentationLoop(settings)
|
||||
}
|
||||
|
||||
func instrumentationLoop(settings *MetricSettings) chan struct{} {
|
||||
M_Instance_Start.Inc()
|
||||
|
||||
// set the total stats gauges before we publishing metrics
|
||||
updateTotalStats()
|
||||
|
||||
onceEveryDayTick := time.NewTicker(time.Hour * 24)
|
||||
everyMinuteTicker := time.NewTicker(time.Minute)
|
||||
defer onceEveryDayTick.Stop()
|
||||
defer everyMinuteTicker.Stop()
|
||||
|
||||
for {
|
||||
select {
|
||||
case <-onceEveryDayTick.C:
|
||||
sendUsageStats()
|
||||
case <-everyMinuteTicker.C:
|
||||
updateTotalStats()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func updateTotalStats() {
|
||||
|
||||
@@ -0,0 +1,71 @@
|
||||
package metrics
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"github.com/grafana/grafana/pkg/log"
|
||||
"github.com/grafana/grafana/pkg/metrics/graphitebridge"
|
||||
"github.com/grafana/grafana/pkg/registry"
|
||||
"github.com/grafana/grafana/pkg/setting"
|
||||
)
|
||||
|
||||
var metricsLogger log.Logger = log.New("metrics")
|
||||
|
||||
type logWrapper struct {
|
||||
logger log.Logger
|
||||
}
|
||||
|
||||
func (lw *logWrapper) Println(v ...interface{}) {
|
||||
lw.logger.Info("graphite metric bridge", v...)
|
||||
}
|
||||
|
||||
func init() {
|
||||
registry.RegisterService(&InternalMetricsService{})
|
||||
initMetricVars()
|
||||
}
|
||||
|
||||
type InternalMetricsService struct {
|
||||
Cfg *setting.Cfg `inject:""`
|
||||
|
||||
enabled bool
|
||||
intervalSeconds int64
|
||||
graphiteCfg *graphitebridge.Config
|
||||
}
|
||||
|
||||
func (im *InternalMetricsService) Init() error {
|
||||
return im.readSettings()
|
||||
}
|
||||
|
||||
func (im *InternalMetricsService) Run(ctx context.Context) error {
|
||||
// Start Graphite Bridge
|
||||
if im.graphiteCfg != nil {
|
||||
bridge, err := graphitebridge.NewBridge(im.graphiteCfg)
|
||||
if err != nil {
|
||||
metricsLogger.Error("failed to create graphite bridge", "error", err)
|
||||
} else {
|
||||
go bridge.Run(ctx)
|
||||
}
|
||||
}
|
||||
|
||||
M_Instance_Start.Inc()
|
||||
|
||||
// set the total stats gauges before we publishing metrics
|
||||
updateTotalStats()
|
||||
|
||||
onceEveryDayTick := time.NewTicker(time.Hour * 24)
|
||||
everyMinuteTicker := time.NewTicker(time.Minute)
|
||||
defer onceEveryDayTick.Stop()
|
||||
defer everyMinuteTicker.Stop()
|
||||
|
||||
for {
|
||||
select {
|
||||
case <-onceEveryDayTick.C:
|
||||
sendUsageStats()
|
||||
case <-everyMinuteTicker.C:
|
||||
updateTotalStats()
|
||||
case <-ctx.Done():
|
||||
return ctx.Err()
|
||||
}
|
||||
}
|
||||
}
|
||||
+23
-35
@@ -1,67 +1,53 @@
|
||||
package metrics
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/grafana/grafana/pkg/metrics/graphitebridge"
|
||||
"github.com/grafana/grafana/pkg/setting"
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
ini "gopkg.in/ini.v1"
|
||||
)
|
||||
|
||||
type MetricSettings struct {
|
||||
Enabled bool
|
||||
IntervalSeconds int64
|
||||
GraphiteBridgeConfig *graphitebridge.Config
|
||||
}
|
||||
|
||||
func ReadSettings(file *ini.File) *MetricSettings {
|
||||
var settings = &MetricSettings{
|
||||
Enabled: false,
|
||||
func (im *InternalMetricsService) readSettings() error {
|
||||
var section, err = im.Cfg.Raw.GetSection("metrics")
|
||||
if err != nil {
|
||||
return fmt.Errorf("Unable to find metrics config section %v", err)
|
||||
}
|
||||
|
||||
var section, err = file.GetSection("metrics")
|
||||
if err != nil {
|
||||
metricsLogger.Crit("Unable to find metrics config section", "error", err)
|
||||
im.enabled = section.Key("enabled").MustBool(false)
|
||||
im.intervalSeconds = section.Key("interval_seconds").MustInt64(10)
|
||||
|
||||
if !im.enabled {
|
||||
return nil
|
||||
}
|
||||
|
||||
settings.Enabled = section.Key("enabled").MustBool(false)
|
||||
settings.IntervalSeconds = section.Key("interval_seconds").MustInt64(10)
|
||||
|
||||
if !settings.Enabled {
|
||||
return settings
|
||||
if err := im.parseGraphiteSettings(); err != nil {
|
||||
return fmt.Errorf("Unable to parse metrics graphite section, %v", err)
|
||||
}
|
||||
|
||||
cfg, err := parseGraphiteSettings(settings, file)
|
||||
if err != nil {
|
||||
metricsLogger.Crit("Unable to parse metrics graphite section", "error", err)
|
||||
return nil
|
||||
}
|
||||
|
||||
settings.GraphiteBridgeConfig = cfg
|
||||
|
||||
return settings
|
||||
return nil
|
||||
}
|
||||
|
||||
func parseGraphiteSettings(settings *MetricSettings, file *ini.File) (*graphitebridge.Config, error) {
|
||||
graphiteSection, err := setting.Raw.GetSection("metrics.graphite")
|
||||
func (im *InternalMetricsService) parseGraphiteSettings() error {
|
||||
graphiteSection, err := im.Cfg.Raw.GetSection("metrics.graphite")
|
||||
|
||||
if err != nil {
|
||||
return nil, nil
|
||||
return nil
|
||||
}
|
||||
|
||||
address := graphiteSection.Key("address").String()
|
||||
if address == "" {
|
||||
return nil, nil
|
||||
return nil
|
||||
}
|
||||
|
||||
cfg := &graphitebridge.Config{
|
||||
bridgeCfg := &graphitebridge.Config{
|
||||
URL: address,
|
||||
Prefix: graphiteSection.Key("prefix").MustString("prod.grafana.%(instance_name)s"),
|
||||
CountersAsDelta: true,
|
||||
Gatherer: prometheus.DefaultGatherer,
|
||||
Interval: time.Duration(settings.IntervalSeconds) * time.Second,
|
||||
Interval: time.Duration(im.intervalSeconds) * time.Second,
|
||||
Timeout: 10 * time.Second,
|
||||
Logger: &logWrapper{logger: metricsLogger},
|
||||
ErrorHandling: graphitebridge.ContinueOnError,
|
||||
@@ -74,6 +60,8 @@ func parseGraphiteSettings(settings *MetricSettings, file *ini.File) (*graphiteb
|
||||
prefix = "prod.grafana.%(instance_name)s."
|
||||
}
|
||||
|
||||
cfg.Prefix = strings.Replace(prefix, "%(instance_name)s", safeInstanceName, -1)
|
||||
return cfg, nil
|
||||
bridgeCfg.Prefix = strings.Replace(prefix, "%(instance_name)s", safeInstanceName, -1)
|
||||
|
||||
im.graphiteCfg = bridgeCfg
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ import (
|
||||
"fmt"
|
||||
"net"
|
||||
"net/mail"
|
||||
"reflect"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
@@ -111,6 +112,16 @@ func initContextWithAuthProxy(ctx *m.ReqContext, orgID int64) bool {
|
||||
return true
|
||||
}
|
||||
|
||||
for _, field := range []string{"Name", "Email", "Login"} {
|
||||
if setting.AuthProxyHeaders[field] == "" {
|
||||
continue
|
||||
}
|
||||
|
||||
if val := ctx.Req.Header.Get(setting.AuthProxyHeaders[field]); val != "" {
|
||||
reflect.ValueOf(extUser).Elem().FieldByName(field).SetString(val)
|
||||
}
|
||||
}
|
||||
|
||||
// add/update user in grafana
|
||||
cmd := &m.UpsertUserCommand{
|
||||
ReqContext: ctx,
|
||||
|
||||
@@ -19,12 +19,13 @@ type SendEmailCommandSync struct {
|
||||
}
|
||||
|
||||
type SendWebhookSync struct {
|
||||
Url string
|
||||
User string
|
||||
Password string
|
||||
Body string
|
||||
HttpMethod string
|
||||
HttpHeader map[string]string
|
||||
Url string
|
||||
User string
|
||||
Password string
|
||||
Body string
|
||||
HttpMethod string
|
||||
HttpHeader map[string]string
|
||||
ContentType string
|
||||
}
|
||||
|
||||
type SendResetPasswordEmailCommand struct {
|
||||
|
||||
@@ -16,7 +16,7 @@ import (
|
||||
"golang.org/x/sync/errgroup"
|
||||
)
|
||||
|
||||
type Engine struct {
|
||||
type AlertingService struct {
|
||||
execQueue chan *Job
|
||||
//clock clock.Clock
|
||||
ticker *Ticker
|
||||
@@ -28,20 +28,20 @@ type Engine struct {
|
||||
}
|
||||
|
||||
func init() {
|
||||
registry.RegisterService(&Engine{})
|
||||
registry.RegisterService(&AlertingService{})
|
||||
}
|
||||
|
||||
func NewEngine() *Engine {
|
||||
e := &Engine{}
|
||||
func NewEngine() *AlertingService {
|
||||
e := &AlertingService{}
|
||||
e.Init()
|
||||
return e
|
||||
}
|
||||
|
||||
func (e *Engine) IsDisabled() bool {
|
||||
func (e *AlertingService) IsDisabled() bool {
|
||||
return !setting.AlertingEnabled || !setting.ExecuteAlerts
|
||||
}
|
||||
|
||||
func (e *Engine) Init() error {
|
||||
func (e *AlertingService) Init() error {
|
||||
e.ticker = NewTicker(time.Now(), time.Second*0, clock.New())
|
||||
e.execQueue = make(chan *Job, 1000)
|
||||
e.scheduler = NewScheduler()
|
||||
@@ -52,7 +52,7 @@ func (e *Engine) Init() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (e *Engine) Run(ctx context.Context) error {
|
||||
func (e *AlertingService) Run(ctx context.Context) error {
|
||||
alertGroup, ctx := errgroup.WithContext(ctx)
|
||||
alertGroup.Go(func() error { return e.alertingTicker(ctx) })
|
||||
alertGroup.Go(func() error { return e.runJobDispatcher(ctx) })
|
||||
@@ -61,7 +61,7 @@ func (e *Engine) Run(ctx context.Context) error {
|
||||
return err
|
||||
}
|
||||
|
||||
func (e *Engine) alertingTicker(grafanaCtx context.Context) error {
|
||||
func (e *AlertingService) alertingTicker(grafanaCtx context.Context) error {
|
||||
defer func() {
|
||||
if err := recover(); err != nil {
|
||||
e.log.Error("Scheduler Panic: stopping alertingTicker", "error", err, "stack", log.Stack(1))
|
||||
@@ -86,7 +86,7 @@ func (e *Engine) alertingTicker(grafanaCtx context.Context) error {
|
||||
}
|
||||
}
|
||||
|
||||
func (e *Engine) runJobDispatcher(grafanaCtx context.Context) error {
|
||||
func (e *AlertingService) runJobDispatcher(grafanaCtx context.Context) error {
|
||||
dispatcherGroup, alertCtx := errgroup.WithContext(grafanaCtx)
|
||||
|
||||
for {
|
||||
@@ -106,7 +106,7 @@ var (
|
||||
alertMaxAttempts = 3
|
||||
)
|
||||
|
||||
func (e *Engine) processJobWithRetry(grafanaCtx context.Context, job *Job) error {
|
||||
func (e *AlertingService) processJobWithRetry(grafanaCtx context.Context, job *Job) error {
|
||||
defer func() {
|
||||
if err := recover(); err != nil {
|
||||
e.log.Error("Alert Panic", "error", err, "stack", log.Stack(1))
|
||||
@@ -141,7 +141,7 @@ func (e *Engine) processJobWithRetry(grafanaCtx context.Context, job *Job) error
|
||||
}
|
||||
}
|
||||
|
||||
func (e *Engine) endJob(err error, cancelChan chan context.CancelFunc, job *Job) error {
|
||||
func (e *AlertingService) endJob(err error, cancelChan chan context.CancelFunc, job *Job) error {
|
||||
job.Running = false
|
||||
close(cancelChan)
|
||||
for cancelFn := range cancelChan {
|
||||
@@ -150,7 +150,7 @@ func (e *Engine) endJob(err error, cancelChan chan context.CancelFunc, job *Job)
|
||||
return err
|
||||
}
|
||||
|
||||
func (e *Engine) processJob(attemptID int, attemptChan chan int, cancelChan chan context.CancelFunc, job *Job) {
|
||||
func (e *AlertingService) processJob(attemptID int, attemptChan chan int, cancelChan chan context.CancelFunc, job *Job) {
|
||||
defer func() {
|
||||
if err := recover(); err != nil {
|
||||
e.log.Error("Alert Panic", "error", err, "stack", log.Stack(1))
|
||||
|
||||
@@ -0,0 +1,173 @@
|
||||
package notifiers
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"io"
|
||||
"mime/multipart"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/grafana/grafana/pkg/bus"
|
||||
"github.com/grafana/grafana/pkg/components/simplejson"
|
||||
"github.com/grafana/grafana/pkg/log"
|
||||
m "github.com/grafana/grafana/pkg/models"
|
||||
"github.com/grafana/grafana/pkg/services/alerting"
|
||||
"github.com/grafana/grafana/pkg/setting"
|
||||
)
|
||||
|
||||
func init() {
|
||||
alerting.RegisterNotifier(&alerting.NotifierPlugin{
|
||||
Type: "discord",
|
||||
Name: "Discord",
|
||||
Description: "Sends notifications to Discord",
|
||||
Factory: NewDiscordNotifier,
|
||||
OptionsTemplate: `
|
||||
<h3 class="page-heading">Discord settings</h3>
|
||||
<div class="gf-form">
|
||||
<span class="gf-form-label width-14">Webhook URL</span>
|
||||
<input type="text" required class="gf-form-input max-width-22" ng-model="ctrl.model.settings.url" placeholder="Discord webhook URL"></input>
|
||||
</div>
|
||||
`,
|
||||
})
|
||||
}
|
||||
|
||||
func NewDiscordNotifier(model *m.AlertNotification) (alerting.Notifier, error) {
|
||||
url := model.Settings.Get("url").MustString()
|
||||
if url == "" {
|
||||
return nil, alerting.ValidationError{Reason: "Could not find webhook url property in settings"}
|
||||
}
|
||||
|
||||
return &DiscordNotifier{
|
||||
NotifierBase: NewNotifierBase(model.Id, model.IsDefault, model.Name, model.Type, model.Settings),
|
||||
WebhookURL: url,
|
||||
log: log.New("alerting.notifier.discord"),
|
||||
}, nil
|
||||
}
|
||||
|
||||
type DiscordNotifier struct {
|
||||
NotifierBase
|
||||
WebhookURL string
|
||||
log log.Logger
|
||||
}
|
||||
|
||||
func (this *DiscordNotifier) Notify(evalContext *alerting.EvalContext) error {
|
||||
this.log.Info("Sending alert notification to", "webhook_url", this.WebhookURL)
|
||||
|
||||
ruleUrl, err := evalContext.GetRuleUrl()
|
||||
if err != nil {
|
||||
this.log.Error("Failed get rule link", "error", err)
|
||||
return err
|
||||
}
|
||||
|
||||
bodyJSON := simplejson.New()
|
||||
bodyJSON.Set("username", "Grafana")
|
||||
|
||||
fields := make([]map[string]interface{}, 0)
|
||||
|
||||
for _, evt := range evalContext.EvalMatches {
|
||||
|
||||
fields = append(fields, map[string]interface{}{
|
||||
"name": evt.Metric,
|
||||
"value": evt.Value.FullString(),
|
||||
"inline": true,
|
||||
})
|
||||
}
|
||||
|
||||
footer := map[string]interface{}{
|
||||
"text": "Grafana v" + setting.BuildVersion,
|
||||
"icon_url": "https://grafana.com/assets/img/fav32.png",
|
||||
}
|
||||
|
||||
color, _ := strconv.ParseInt(strings.TrimLeft(evalContext.GetStateModel().Color, "#"), 16, 0)
|
||||
|
||||
embed := simplejson.New()
|
||||
embed.Set("title", evalContext.GetNotificationTitle())
|
||||
//Discord takes integer for color
|
||||
embed.Set("color", color)
|
||||
embed.Set("url", ruleUrl)
|
||||
embed.Set("description", evalContext.Rule.Message)
|
||||
embed.Set("type", "rich")
|
||||
embed.Set("fields", fields)
|
||||
embed.Set("footer", footer)
|
||||
|
||||
var image map[string]interface{}
|
||||
var embeddedImage = false
|
||||
|
||||
if evalContext.ImagePublicUrl != "" {
|
||||
image = map[string]interface{}{
|
||||
"url": evalContext.ImagePublicUrl,
|
||||
}
|
||||
embed.Set("image", image)
|
||||
} else {
|
||||
image = map[string]interface{}{
|
||||
"url": "attachment://graph.png",
|
||||
}
|
||||
embed.Set("image", image)
|
||||
embeddedImage = true
|
||||
}
|
||||
|
||||
bodyJSON.Set("embeds", []interface{}{embed})
|
||||
|
||||
json, _ := bodyJSON.MarshalJSON()
|
||||
|
||||
content_type := "application/json"
|
||||
|
||||
var body []byte
|
||||
|
||||
if embeddedImage {
|
||||
|
||||
var b bytes.Buffer
|
||||
|
||||
w := multipart.NewWriter(&b)
|
||||
|
||||
f, err := os.Open(evalContext.ImageOnDiskPath)
|
||||
|
||||
if err != nil {
|
||||
this.log.Error("Can't open graph file", err)
|
||||
return err
|
||||
}
|
||||
|
||||
defer f.Close()
|
||||
|
||||
fw, err := w.CreateFormField("payload_json")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if _, err = fw.Write([]byte(string(json))); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fw, err = w.CreateFormFile("file", "graph.png")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if _, err = io.Copy(fw, f); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
w.Close()
|
||||
|
||||
body = b.Bytes()
|
||||
content_type = w.FormDataContentType()
|
||||
|
||||
} else {
|
||||
body = json
|
||||
}
|
||||
|
||||
cmd := &m.SendWebhookSync{
|
||||
Url: this.WebhookURL,
|
||||
Body: string(body),
|
||||
HttpMethod: "POST",
|
||||
ContentType: content_type,
|
||||
}
|
||||
|
||||
if err := bus.DispatchCtx(evalContext.Ctx, cmd); err != nil {
|
||||
this.log.Error("Failed to send notification to Discord", "error", err)
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
package notifiers
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/grafana/grafana/pkg/components/simplejson"
|
||||
m "github.com/grafana/grafana/pkg/models"
|
||||
. "github.com/smartystreets/goconvey/convey"
|
||||
)
|
||||
|
||||
func TestDiscordNotifier(t *testing.T) {
|
||||
Convey("Telegram notifier tests", t, func() {
|
||||
|
||||
Convey("Parsing alert notification from settings", func() {
|
||||
Convey("empty settings should return error", func() {
|
||||
json := `{ }`
|
||||
|
||||
settingsJSON, _ := simplejson.NewJson([]byte(json))
|
||||
model := &m.AlertNotification{
|
||||
Name: "discord_testing",
|
||||
Type: "discord",
|
||||
Settings: settingsJSON,
|
||||
}
|
||||
|
||||
_, err := NewDiscordNotifier(model)
|
||||
So(err, ShouldNotBeNil)
|
||||
})
|
||||
|
||||
Convey("settings should trigger incident", func() {
|
||||
json := `
|
||||
{
|
||||
"url": "https://web.hook/"
|
||||
}`
|
||||
|
||||
settingsJSON, _ := simplejson.NewJson([]byte(json))
|
||||
model := &m.AlertNotification{
|
||||
Name: "discord_testing",
|
||||
Type: "discord",
|
||||
Settings: settingsJSON,
|
||||
}
|
||||
|
||||
not, err := NewDiscordNotifier(model)
|
||||
discordNotifier := not.(*DiscordNotifier)
|
||||
|
||||
So(err, ShouldBeNil)
|
||||
So(discordNotifier.Name, ShouldEqual, "discord_testing")
|
||||
So(discordNotifier.Type, ShouldEqual, "discord")
|
||||
So(discordNotifier.WebhookURL, ShouldEqual, "https://web.hook/")
|
||||
})
|
||||
})
|
||||
})
|
||||
}
|
||||
@@ -104,12 +104,13 @@ func (ns *NotificationService) Run(ctx context.Context) error {
|
||||
|
||||
func (ns *NotificationService) SendWebhookSync(ctx context.Context, cmd *m.SendWebhookSync) error {
|
||||
return ns.sendWebRequestSync(ctx, &Webhook{
|
||||
Url: cmd.Url,
|
||||
User: cmd.User,
|
||||
Password: cmd.Password,
|
||||
Body: cmd.Body,
|
||||
HttpMethod: cmd.HttpMethod,
|
||||
HttpHeader: cmd.HttpHeader,
|
||||
Url: cmd.Url,
|
||||
User: cmd.User,
|
||||
Password: cmd.Password,
|
||||
Body: cmd.Body,
|
||||
HttpMethod: cmd.HttpMethod,
|
||||
HttpHeader: cmd.HttpHeader,
|
||||
ContentType: cmd.ContentType,
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -15,12 +15,13 @@ import (
|
||||
)
|
||||
|
||||
type Webhook struct {
|
||||
Url string
|
||||
User string
|
||||
Password string
|
||||
Body string
|
||||
HttpMethod string
|
||||
HttpHeader map[string]string
|
||||
Url string
|
||||
User string
|
||||
Password string
|
||||
Body string
|
||||
HttpMethod string
|
||||
HttpHeader map[string]string
|
||||
ContentType string
|
||||
}
|
||||
|
||||
var netTransport = &http.Transport{
|
||||
@@ -48,8 +49,13 @@ func (ns *NotificationService) sendWebRequestSync(ctx context.Context, webhook *
|
||||
return err
|
||||
}
|
||||
|
||||
request.Header.Add("Content-Type", "application/json")
|
||||
if webhook.ContentType == "" {
|
||||
webhook.ContentType = "application/json"
|
||||
}
|
||||
|
||||
request.Header.Add("Content-Type", webhook.ContentType)
|
||||
request.Header.Add("User-Agent", "Grafana")
|
||||
|
||||
if webhook.User != "" && webhook.Password != "" {
|
||||
request.Header.Add("Authorization", util.GetBasicAuthHeader(webhook.User, webhook.Password))
|
||||
}
|
||||
|
||||
@@ -69,7 +69,7 @@ func (cr *configReader) readConfig() ([]*DashboardsAsConfig, error) {
|
||||
|
||||
parsedDashboards, err := cr.parseConfigs(file)
|
||||
if err != nil {
|
||||
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if len(parsedDashboards) > 0 {
|
||||
|
||||
@@ -10,19 +10,16 @@ import (
|
||||
type DashboardProvisioner struct {
|
||||
cfgReader *configReader
|
||||
log log.Logger
|
||||
ctx context.Context
|
||||
}
|
||||
|
||||
func Provision(ctx context.Context, configDirectory string) (*DashboardProvisioner, error) {
|
||||
func NewDashboardProvisioner(configDirectory string) *DashboardProvisioner {
|
||||
log := log.New("provisioning.dashboard")
|
||||
d := &DashboardProvisioner{
|
||||
cfgReader: &configReader{path: configDirectory, log: log},
|
||||
log: log,
|
||||
ctx: ctx,
|
||||
}
|
||||
|
||||
err := d.Provision(ctx)
|
||||
return d, err
|
||||
return d
|
||||
}
|
||||
|
||||
func (provider *DashboardProvisioner) Provision(ctx context.Context) error {
|
||||
|
||||
@@ -2,30 +2,40 @@ package provisioning
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"path"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/grafana/grafana/pkg/registry"
|
||||
"github.com/grafana/grafana/pkg/services/provisioning/dashboards"
|
||||
"github.com/grafana/grafana/pkg/services/provisioning/datasources"
|
||||
ini "gopkg.in/ini.v1"
|
||||
"github.com/grafana/grafana/pkg/setting"
|
||||
)
|
||||
|
||||
func Init(ctx context.Context, homePath string, cfg *ini.File) error {
|
||||
provisioningPath := makeAbsolute(cfg.Section("paths").Key("provisioning").String(), homePath)
|
||||
func init() {
|
||||
registry.RegisterService(&ProvisioningService{})
|
||||
}
|
||||
|
||||
datasourcePath := path.Join(provisioningPath, "datasources")
|
||||
type ProvisioningService struct {
|
||||
Cfg *setting.Cfg `inject:""`
|
||||
}
|
||||
|
||||
func (ps *ProvisioningService) Init() error {
|
||||
datasourcePath := path.Join(ps.Cfg.ProvisioningPath, "datasources")
|
||||
if err := datasources.Provision(datasourcePath); err != nil {
|
||||
return fmt.Errorf("Datasource provisioning error: %v", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (ps *ProvisioningService) Run(ctx context.Context) error {
|
||||
dashboardPath := path.Join(ps.Cfg.ProvisioningPath, "dashboards")
|
||||
dashProvisioner := dashboards.NewDashboardProvisioner(dashboardPath)
|
||||
|
||||
if err := dashProvisioner.Provision(ctx); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
dashboardPath := path.Join(provisioningPath, "dashboards")
|
||||
_, err := dashboards.Provision(ctx, dashboardPath)
|
||||
return err
|
||||
}
|
||||
|
||||
func makeAbsolute(path string, root string) string {
|
||||
if filepath.IsAbs(path) {
|
||||
return path
|
||||
}
|
||||
return filepath.Join(root, path)
|
||||
<-ctx.Done()
|
||||
return ctx.Err()
|
||||
}
|
||||
|
||||
@@ -21,4 +21,9 @@ func addUserAuthMigrations(mg *Migrator) {
|
||||
mg.AddMigration("create user auth table", NewAddTableMigration(userAuthV1))
|
||||
// add indices
|
||||
addTableIndicesMigrations(mg, "v1", userAuthV1)
|
||||
|
||||
mg.AddMigration("alter user_auth.auth_id to length 190", new(RawSqlMigration).
|
||||
Sqlite("SELECT 0 WHERE 0;").
|
||||
Postgres("ALTER TABLE user_auth ALTER COLUMN auth_id TYPE VARCHAR(190);").
|
||||
Mysql("ALTER TABLE user_auth MODIFY auth_id VARCHAR(190);"))
|
||||
}
|
||||
|
||||
@@ -123,7 +123,7 @@ func getEngine() (*xorm.Engine, error) {
|
||||
}
|
||||
|
||||
cnnstr = fmt.Sprintf("%s:%s@%s(%s)/%s?collation=utf8mb4_unicode_ci&allowNativePasswords=true",
|
||||
DbCfg.User, DbCfg.Pwd, protocol, DbCfg.Host, DbCfg.Name)
|
||||
url.QueryEscape(DbCfg.User), url.QueryEscape(DbCfg.Pwd), protocol, DbCfg.Host, url.PathEscape(DbCfg.Name))
|
||||
|
||||
if DbCfg.SslMode == "true" || DbCfg.SslMode == "skip-verify" {
|
||||
tlsCert, err := makeCert("custom", DbCfg)
|
||||
@@ -142,13 +142,17 @@ func getEngine() (*xorm.Engine, error) {
|
||||
if len(fields) > 1 && len(strings.TrimSpace(fields[1])) > 0 {
|
||||
port = fields[1]
|
||||
}
|
||||
if DbCfg.Pwd == "" {
|
||||
DbCfg.Pwd = "''"
|
||||
}
|
||||
if DbCfg.User == "" {
|
||||
DbCfg.User = "''"
|
||||
}
|
||||
cnnstr = fmt.Sprintf("user=%s password=%s host=%s port=%s dbname=%s sslmode=%s sslcert=%s sslkey=%s sslrootcert=%s", DbCfg.User, DbCfg.Pwd, host, port, DbCfg.Name, DbCfg.SslMode, DbCfg.ClientCertPath, DbCfg.ClientKeyPath, DbCfg.CaCertPath)
|
||||
cnnstr = fmt.Sprintf("user='%s' password='%s' host='%s' port='%s' dbname='%s' sslmode='%s' sslcert='%s' sslkey='%s' sslrootcert='%s'",
|
||||
strings.Replace(DbCfg.User, `'`, `\'`, -1),
|
||||
strings.Replace(DbCfg.Pwd, `'`, `\'`, -1),
|
||||
strings.Replace(host, `'`, `\'`, -1),
|
||||
strings.Replace(port, `'`, `\'`, -1),
|
||||
strings.Replace(DbCfg.Name, `'`, `\'`, -1),
|
||||
strings.Replace(DbCfg.SslMode, `'`, `\'`, -1),
|
||||
strings.Replace(DbCfg.ClientCertPath, `'`, `\'`, -1),
|
||||
strings.Replace(DbCfg.ClientKeyPath, `'`, `\'`, -1),
|
||||
strings.Replace(DbCfg.CaCertPath, `'`, `\'`, -1),
|
||||
)
|
||||
case "sqlite3":
|
||||
if !filepath.IsAbs(DbCfg.Path) {
|
||||
DbCfg.Path = filepath.Join(setting.DataPath, DbCfg.Path)
|
||||
|
||||
+19
-8
@@ -52,12 +52,11 @@ var (
|
||||
ApplicationName string
|
||||
|
||||
// Paths
|
||||
LogsPath string
|
||||
HomePath string
|
||||
DataPath string
|
||||
PluginsPath string
|
||||
ProvisioningPath string
|
||||
CustomInitPath = "conf/custom.ini"
|
||||
LogsPath string
|
||||
HomePath string
|
||||
DataPath string
|
||||
PluginsPath string
|
||||
CustomInitPath = "conf/custom.ini"
|
||||
|
||||
// Log settings.
|
||||
LogModes []string
|
||||
@@ -125,6 +124,7 @@ var (
|
||||
AuthProxyAutoSignUp bool
|
||||
AuthProxyLdapSyncTtl int
|
||||
AuthProxyWhitelist string
|
||||
AuthProxyHeaders map[string]string
|
||||
|
||||
// Basic Auth
|
||||
BasicAuthEnabled bool
|
||||
@@ -187,6 +187,9 @@ var (
|
||||
type Cfg struct {
|
||||
Raw *ini.File
|
||||
|
||||
// Paths
|
||||
ProvisioningPath string
|
||||
|
||||
// SMTP email settings
|
||||
Smtp SmtpSettings
|
||||
|
||||
@@ -516,7 +519,7 @@ func (cfg *Cfg) Load(args *CommandLineArgs) error {
|
||||
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)
|
||||
cfg.ProvisioningPath = makeAbsolute(iniFile.Section("paths").Key("provisioning").String(), HomePath)
|
||||
server := iniFile.Section("server")
|
||||
AppUrl, AppSubUrl = parseAppUrlAndSubUrl(server)
|
||||
|
||||
@@ -611,6 +614,14 @@ func (cfg *Cfg) Load(args *CommandLineArgs) error {
|
||||
AuthProxyLdapSyncTtl = authProxy.Key("ldap_sync_ttl").MustInt()
|
||||
AuthProxyWhitelist = authProxy.Key("whitelist").String()
|
||||
|
||||
AuthProxyHeaders = make(map[string]string)
|
||||
for _, propertyAndHeader := range util.SplitString(authProxy.Key("headers").String()) {
|
||||
split := strings.SplitN(propertyAndHeader, ":", 2)
|
||||
if len(split) == 2 {
|
||||
AuthProxyHeaders[split[0]] = split[1]
|
||||
}
|
||||
}
|
||||
|
||||
// basic auth
|
||||
authBasic := iniFile.Section("auth.basic")
|
||||
BasicAuthEnabled = authBasic.Key("enabled").MustBool(true)
|
||||
@@ -719,6 +730,6 @@ func (cfg *Cfg) LogConfigSources() {
|
||||
logger.Info("Path Data", "path", DataPath)
|
||||
logger.Info("Path Logs", "path", LogsPath)
|
||||
logger.Info("Path Plugins", "path", PluginsPath)
|
||||
logger.Info("Path Provisioning", "path", ProvisioningPath)
|
||||
logger.Info("Path Provisioning", "path", cfg.ProvisioningPath)
|
||||
logger.Info("App mode " + Env)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user