diff --git a/pkg/cmd/grafana-server/commands/cli.go b/pkg/cmd/grafana-server/commands/cli.go index 6821aa61548..de6e102244b 100644 --- a/pkg/cmd/grafana-server/commands/cli.go +++ b/pkg/cmd/grafana-server/commands/cli.go @@ -44,6 +44,7 @@ func ServerCommand(version, commit, buildBranch, buildstamp string) *cli.Command Context: context, }) }, + Subcommands: []*cli.Command{TargetCommand(version, commit, buildBranch, buildstamp)}, } } @@ -132,7 +133,12 @@ func validPackaging(packaging string) string { return "unknown" } -func listenToSystemSignals(ctx context.Context, s *server.Server) { +// a small interface satisfied by the server and moduleserver +type gserver interface { + Shutdown(context.Context, string) error +} + +func listenToSystemSignals(ctx context.Context, s gserver) { signalChan := make(chan os.Signal, 1) sighupChan := make(chan os.Signal, 1) diff --git a/pkg/cmd/grafana-server/commands/target.go b/pkg/cmd/grafana-server/commands/target.go new file mode 100644 index 00000000000..5843927a4c1 --- /dev/null +++ b/pkg/cmd/grafana-server/commands/target.go @@ -0,0 +1,108 @@ +package commands + +import ( + "context" + "fmt" + "os" + "runtime/debug" + "strings" + + "github.com/urfave/cli/v2" + + "github.com/grafana/grafana/pkg/api" + "github.com/grafana/grafana/pkg/infra/log" + "github.com/grafana/grafana/pkg/server" + "github.com/grafana/grafana/pkg/setting" +) + +func TargetCommand(version, commit, buildBranch, buildstamp string) *cli.Command { + return &cli.Command{ + Name: "target", + Usage: "target specific grafana dskit services", + Flags: commonFlags, + Action: func(context *cli.Context) error { + return RunTargetServer(ServerOptions{ + Version: version, + Commit: commit, + BuildBranch: buildBranch, + BuildStamp: buildstamp, + Context: context, + }) + }, + } +} + +func RunTargetServer(opts ServerOptions) error { + if Version || VerboseVersion { + fmt.Printf("Version %s (commit: %s, branch: %s)\n", opts.Version, opts.Commit, opts.BuildBranch) + if VerboseVersion { + fmt.Println("Dependencies:") + if info, ok := debug.ReadBuildInfo(); ok { + for _, dep := range info.Deps { + fmt.Println(dep.Path, dep.Version) + } + } + } + return nil + } + + logger := log.New("cli") + defer func() { + if err := log.Close(); err != nil { + fmt.Fprintf(os.Stderr, "Failed to close log: %s\n", err) + } + }() + + if err := setupProfiling(Profile, ProfileAddr, ProfilePort); err != nil { + return err + } + if err := setupTracing(Tracing, TracingFile, logger); err != nil { + return err + } + + defer func() { + // If we've managed to initialize them, this is the last place + // where we're able to log anything that'll end up in Grafana's + // log files. + // Since operators are not always looking at stderr, we'll try + // to log any and all panics that are about to crash Grafana to + // our regular log locations before exiting. + if r := recover(); r != nil { + reason := fmt.Sprintf("%v", r) + logger.Error("Critical error", "reason", reason, "stackTrace", string(debug.Stack())) + panic(r) + } + }() + + setBuildInfo(opts) + checkPrivileges() + + configOptions := strings.Split(ConfigOverrides, " ") + cfg, err := setting.NewCfgFromArgs(setting.CommandLineArgs{ + Config: ConfigFile, + HomePath: HomePath, + // tailing arguments have precedence over the options string + Args: append(configOptions, opts.Context.Args().Slice()...), + }) + if err != nil { + return err + } + + s, err := server.InitializeModuleServer( + cfg, + server.Options{ + PidFile: PidFile, + Version: opts.Version, + Commit: opts.Commit, + BuildBranch: opts.BuildBranch, + }, + api.ServerOptions{}, + ) + if err != nil { + return err + } + + ctx := context.Background() + go listenToSystemSignals(ctx, s) + return s.Run() +}