@ -41,20 +41,26 @@ import (
"github.com/grafana/grafana/pkg/setting"
)
func NewGrafanaServer ( ) * GrafanaServerImpl {
// NewServer returns a new instance of Server.
func NewServer ( configFile , homePath , pidFile string ) * Server {
rootCtx , shutdownFn := context . WithCancel ( context . Background ( ) )
childRoutines , childCtx := errgroup . WithContext ( rootCtx )
return & Grafana ServerImpl {
return & Server {
context : childCtx ,
shutdownFn : shutdownFn ,
childRoutines : childRoutines ,
log : log . New ( "server" ) ,
cfg : setting . NewCfg ( ) ,
configFile : configFile ,
homePath : homePath ,
pidFile : pidFile ,
}
}
type GrafanaServerImpl struct {
// Server is responsible for managing the lifecycle of services.
type Server struct {
context context . Context
shutdownFn context . CancelFunc
childRoutines * errgroup . Group
@ -63,75 +69,48 @@ type GrafanaServerImpl struct {
shutdownReason string
shutdownInProgress bool
configFile string
homePath string
pidFile string
RouteRegister routing . RouteRegister ` inject:"" `
HttpServer * api . HTTPServer ` inject:"" `
HTTP Server * api . HTTPServer ` inject:"" `
}
func ( g * GrafanaServerImpl ) Run ( ) error {
var err error
g . loadConfiguration ( )
g . writePIDFile ( )
// Run initializes and starts services. This will block until all services have
// exited. To initiate shutdown, call the Shutdown method in another goroutine.
func ( s * Server ) Run ( ) error {
s . loadConfiguration ( )
s . writePIDFile ( )
login . Init ( )
social . NewOAuthService ( )
serviceGraph := inject . Graph { }
err = serviceGraph . Provide ( & inject . Object { Value : bus . GetBus ( ) } )
if err != nil {
return fmt . Errorf ( "Failed to provide object to the graph: %v" , err )
}
err = serviceGraph . Provide ( & inject . Object { Value : g . cfg } )
if err != nil {
return fmt . Errorf ( "Failed to provide object to the graph: %v" , err )
}
err = serviceGraph . Provide ( & inject . Object { Value : routing . NewRouteRegister ( middleware . RequestMetrics , middleware . RequestTracing ) } )
if err != nil {
return fmt . Errorf ( "Failed to provide object to the graph: %v" , err )
}
err = serviceGraph . Provide ( & inject . Object { Value : localcache . New ( 5 * time . Minute , 10 * time . Minute ) } )
if err != nil {
return fmt . Errorf ( "Failed to provide object to the graph: %v" , err )
}
// self registered services
services := registry . GetServices ( )
// Add all services to dependency graph
for _ , service := range services {
err = serviceGraph . Provide ( & inject . Object { Value : service . Instance } )
if err != nil {
return fmt . Errorf ( "Failed to provide object to the graph: %v" , err )
}
}
err = serviceGraph . Provide ( & inject . Object { Value : g } )
if err != nil {
return fmt . Errorf ( "Failed to provide object to the graph: %v" , err )
}
// Inject dependencies to services
if err := serviceGraph . Populate ( ) ; err != nil {
return fmt . Errorf ( "Failed to populate service dependency: %v" , err )
if err := s . buildServiceGraph ( services ) ; err != nil {
return err
}
// Init & start services
// Initialize services.
for _ , service := range services {
if registry . IsDisabled ( service . Instance ) {
continue
}
g . log . Info ( "Initializing " + service . Name )
s . log . Info ( "Initializing " + service . Name )
if err := service . Instance . Init ( ) ; err != nil {
return fmt . Errorf ( "Service init failed: %v" , err )
}
}
// Start background services
for _ , srv := range services {
// variable needed for accessing loop variable in function callback
descriptor := srv
service , ok := srv . Instance . ( registry . BackgroundService )
// Start background services.
for _ , svc := range services {
// Variable is needed for accessing loop variable in function callback
descriptor := svc
service , ok := svc . Instance . ( registry . BackgroundService )
if ! ok {
continue
}
@ -140,98 +119,140 @@ func (g *GrafanaServerImpl) Run() error {
continue
}
g . childRoutines . Go ( func ( ) error {
// Skip starting new service when shutting down
// Can happen when service stop/return during startup
if g . shutdownInProgress {
s . childRoutines . Go ( func ( ) error {
// Don't start new services when server is shutting down.
if s . shutdownInProgress {
return nil
}
err := service . Run ( g . context )
// If error is not canceled then the service crashed
if err != context . Canceled && err != nil {
g . log . Error ( "Stopped " + descriptor . Name , "reason" , err )
if err := service . Run ( s . context ) ; err != nil {
if err != context . Canceled {
// Server has crashed.
s . log . Error ( "Stopped " + descriptor . Name , "reason" , err )
} else {
g . log . Info ( "Stopped " + descriptor . Name , "reason" , err )
s . log . Info ( "Stopped " + descriptor . 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 . childRoutines . Wait ( )
}
s . shutdownInProgress = true
func ( g * GrafanaServerImpl ) loadConfiguration ( ) {
err := g . cfg . Load ( & setting . CommandLineArgs {
Config : * configFile ,
HomePath : * homePath ,
Args : flag . Args ( ) ,
return nil
} )
if err != nil {
fmt . Fprintf ( os . Stderr , "Failed to start grafana. error: %s\n" , err . Error ( ) )
os . Exit ( 1 )
}
g . log . Info ( "Starting " + setting . ApplicationName , "version" , version , "commit" , commit , "branch" , buildBranch , "compiled" , time . Unix ( setting . BuildStamp , 0 ) )
g . cfg . LogConfigSources ( )
notifySystemd ( "READY=1" )
return s . childRoutines . Wait ( )
}
func ( g * GrafanaServerImpl ) Shutdown ( reason string ) {
g . log . Info ( "Shutdown started" , "reason" , reason )
g . shutdownReason = reason
g . shutdownInProgress = true
// Shutdown initiates a shutdown of the services, and waits for all services to
// exit.
func ( s * Server ) Shutdown ( reason string ) {
s . log . Info ( "Shutdown started" , "reason" , reason )
s . shutdownReason = reason
s . shutdownInProgress = true
// call cancel func on root context
g . shutdownFn ( )
s . shutdownFn ( )
// wait for child routines
g . childRoutines . Wait ( )
s . childRoutines . Wait ( )
}
func ( g * GrafanaServerImpl ) Exit ( reason error ) int {
// default exit code is 1
// ExitCode returns an exit code for a given error.
func ( s * Server ) ExitCode ( reason error ) int {
code := 1
if reason == context . Canceled && g . shutdownReason != "" {
reason = fmt . Errorf ( g . shutdownReason )
if reason == context . Canceled && s . shutdownReason != "" {
reason = fmt . Errorf ( s . shutdownReason )
code = 0
}
g . log . Error ( "Server shutdown" , "reason" , reason )
s . log . Error ( "Server shutdown" , "reason" , reason )
return code
}
func ( g * GrafanaServerImpl ) writePIDFile ( ) {
if * pidFile == "" {
// writePIDFile retrieves the current process ID and writes it to file.
func ( s * Server ) writePIDFile ( ) {
if s . pidFile == "" {
return
}
// Ensure the required directory structure exists.
err := os . MkdirAll ( filepath . Dir ( * pidFile ) , 0700 )
err := os . MkdirAll ( filepath . Dir ( s . pidFile ) , 0700 )
if err != nil {
g . log . Error ( "Failed to verify pid directory" , "error" , err )
s . log . Error ( "Failed to verify pid directory" , "error" , err )
os . Exit ( 1 )
}
// Retrieve the PID and write it.
// Retrieve the PID and write it to file .
pid := strconv . Itoa ( os . Getpid ( ) )
if err := ioutil . WriteFile ( * pidFile , [ ] byte ( pid ) , 0644 ) ; err != nil {
g . log . Error ( "Failed to write pidfile" , "error" , err )
if err := ioutil . WriteFile ( s . pidFile , [ ] byte ( pid ) , 0644 ) ; err != nil {
s . log . Error ( "Failed to write pidfile" , "error" , err )
os . Exit ( 1 )
}
s . log . Info ( "Writing PID file" , "path" , s . pidFile , "pid" , pid )
}
// buildServiceGraph builds a graph of services and their dependencies.
func ( s * Server ) buildServiceGraph ( services [ ] * registry . Descriptor ) error {
// Specify service dependencies.
objs := [ ] interface { } {
bus . GetBus ( ) ,
s . cfg ,
routing . NewRouteRegister ( middleware . RequestMetrics , middleware . RequestTracing ) ,
localcache . New ( 5 * time . Minute , 10 * time . Minute ) ,
s ,
}
for _ , service := range services {
objs = append ( objs , service . Instance )
}
var serviceGraph inject . Graph
// Provide services and their dependencies to the graph.
for _ , obj := range objs {
if err := serviceGraph . Provide ( & inject . Object { Value : obj } ) ; err != nil {
return fmt . Errorf ( "Failed to provide object to the graph: %v" , err )
}
}
// Resolve services and their dependencies.
if err := serviceGraph . Populate ( ) ; err != nil {
return fmt . Errorf ( "Failed to populate service dependency: %v" , err )
}
return nil
}
// loadConfiguration loads settings and configuration from config files.
func ( s * Server ) loadConfiguration ( ) {
args := & setting . CommandLineArgs {
Config : s . configFile ,
HomePath : s . homePath ,
Args : flag . Args ( ) ,
}
if err := s . cfg . Load ( args ) ; err != nil {
fmt . Fprintf ( os . Stderr , "Failed to start grafana. error: %s\n" , err . Error ( ) )
os . Exit ( 1 )
}
g . log . Info ( "Writing PID file" , "path" , * pidFile , "pid" , pid )
s . log . Info ( "Starting " + setting . ApplicationName ,
"version" , version ,
"commit" , commit ,
"branch" , buildBranch ,
"compiled" , time . Unix ( setting . BuildStamp , 0 ) ,
)
s . cfg . LogConfigSources ( )
}
func sendSystemdNotification ( state string ) error {
// notifySystemd sends state notifications to systemd.
func notifySystemd ( state string ) error {
notifySocket := os . Getenv ( "NOTIFY_SOCKET" )
if notifySocket == "" {
@ -244,14 +265,12 @@ func sendSystemdNotification(state string) error {
}
conn , err := net . DialUnix ( socketAddr . Net , nil , socketAddr )
if err != nil {
return err
}
defer conn . Close ( )
_ , err = conn . Write ( [ ] byte ( state ) )
conn . Close ( )
return err
}