github.com/pyroscope-io/pyroscope@v0.37.3-0.20230725203016-5f6947968bd0/pkg/adhoc/push.go (about) 1 package adhoc 2 3 import ( 4 "fmt" 5 "net" 6 "net/http" 7 "os" 8 "os/exec" 9 "os/signal" 10 "time" 11 12 kitLogrus "github.com/go-kit/kit/log/logrus" 13 "github.com/prometheus/client_golang/prometheus" 14 "github.com/sirupsen/logrus" 15 16 "github.com/pyroscope-io/pyroscope/pkg/config" 17 "github.com/pyroscope-io/pyroscope/pkg/exporter" 18 "github.com/pyroscope-io/pyroscope/pkg/ingestion" 19 "github.com/pyroscope-io/pyroscope/pkg/parser" 20 "github.com/pyroscope-io/pyroscope/pkg/server" 21 "github.com/pyroscope-io/pyroscope/pkg/server/httputils" 22 "github.com/pyroscope-io/pyroscope/pkg/storage" 23 "github.com/pyroscope-io/pyroscope/pkg/util/process" 24 ) 25 26 type push struct { 27 args []string 28 handler http.Handler 29 logger *logrus.Logger 30 } 31 32 func newPush(_ *config.Adhoc, args []string, st *storage.Storage, logger *logrus.Logger) (runner, error) { 33 e, err := exporter.NewExporter(config.MetricsExportRules{}, prometheus.DefaultRegisterer) 34 if err != nil { 35 return nil, err 36 } 37 p := parser.New(logger, st, e) 38 return push{ 39 args: args, 40 handler: server.NewIngestHandler(kitLogrus.NewLogger(logger), p, func(*ingestion.IngestInput) {}, httputils.NewDefaultHelper(logger)), 41 logger: logger, 42 }, nil 43 } 44 45 func (p push) Run() error { 46 http.Handle("/ingest", p.handler) 47 listener, err := net.Listen("tcp", ":0") 48 if err != nil { 49 return err 50 } 51 p.logger.Debugf("Ingester listening to port %d", listener.Addr().(*net.TCPAddr).Port) 52 53 done := make(chan error) 54 go func() { 55 done <- http.Serve(listener, nil) 56 }() 57 58 // start command 59 c := make(chan os.Signal, 10) 60 // Note that we don't specify which signals to be sent: any signal to be 61 // relayed to the child process (including SIGINT and SIGTERM). 62 signal.Notify(c) 63 env := fmt.Sprintf("PYROSCOPE_ADHOC_SERVER_ADDRESS=http://localhost:%d", listener.Addr().(*net.TCPAddr).Port) 64 cmd := exec.Command(p.args[0], p.args[1:]...) 65 cmd.Env = append(os.Environ(), env) 66 cmd.Stderr = os.Stderr 67 cmd.Stdout = os.Stdout 68 cmd.Stdin = os.Stdin 69 if err := cmd.Start(); err != nil { 70 return err 71 } 72 defer func() { 73 signal.Stop(c) 74 close(c) 75 }() 76 ticker := time.NewTicker(time.Second) 77 defer ticker.Stop() 78 for { 79 select { 80 case s := <-c: 81 _ = process.SendSignal(cmd.Process, s) 82 case err := <-done: 83 return err 84 case <-ticker.C: 85 if !process.Exists(cmd.Process.Pid) { 86 logrus.Debug("child process exited") 87 return cmd.Wait() 88 } 89 } 90 } 91 }