// Sample collects process statistcs and emits them. func (ps *ProcessStatCollector) Sample() error { // Start a race between the timeout (if applicable) and the message // collection. done := make(chan error, 1) go func() { done <- ps.collectSample() }() var timerDone <-chan time.Time if ps.msgTimeout > 0*time.Second { timerDone = time.After(ps.msgTimeout) } select { case err := <-done: return err case <-timerDone: log.Warnln("Sample collection exceeded timeout") return fmt.Errorf("Sample collection exceeded timeout") } }
func main() { if *debugMode { log.SetLevel(log.DebugLevel) } else if *noWarn { log.SetLevel(log.ErrorLevel) } // Create channel for ProcessStats to trigger a sample psChan := make(chan agents.ProcessStatCommand) // Incoming remote command channel (new with each reconnect) rcChan := make(chan chan agents.RemoteControlCommand) args := flag.Args() var cmdArgs []string var cmd string if len(args) > 0 { cmd = args[0] } else { log.Fatal("Did you forget a command to run?") return } log.Debugf("cmd: %s cmdArgs: %q\n", cmd, cmdArgs) done := make(chan error) // Initialize job job, err := agents.NewControlledProcess(cmd, args, done, *stdoutByteLimit) if err != nil { log.Fatalf("Failed to create a NewControlledProcess: %s\n", err) return } log.Debugf("%#v\n", job) signals := make(chan os.Signal, 1) signal.Notify(signals, syscall.SIGINT, syscall.SIGTERM, syscall.SIGHUP, syscall.SIGQUIT) timer, err := agents.NewTimer(*wallclockTimeout) if err != nil { log.Warnln("Error returned creating timeout agent", err) } log.Debugf("Starting timer with timeout of: %v\n", *wallclockTimeout) timer.Start() sess := session{ job: job, exchange: *exchange, rcRoutingKey: *rmtKey, psChan: psChan, rcChan: rcChan, amqpConfig: amqp.Config{ Properties: amqp.Table{ "product": "proc_box", "version": "master", }, }, multiRmtKey: *multiRmtKey, } go redial(sess) go monitor(signals, rcChan, job, psChan, timer, done) err = <-done elapsedTime, _ := timer.ElapsedTime() // Print to standard out fmt.Printf("Task elapsed time: %.2f seconds.\n", elapsedTime.Seconds()) if err != nil { if exiterr, ok := err.(*exec.ExitError); ok { // Non-zero exit code if status, ok := exiterr.Sys().(syscall.WaitStatus); ok { exitStatus := status.ExitStatus() log.Debugf("Exit Status: %d\n", exitStatus) os.Exit(exitStatus) } } else { log.Debugf("cmd.Wait: %v\n", err) } } else { os.Exit(0) } }
func redial(sess session) { var err error var stats agents.ProcessStats var rc *agents.RemoteControl // Initialize mini-router for incoming stats agent requests go func() { for s := range sess.psChan { if stats == nil { log.Warnln("No stats agents available (yet), dropping request") } else if s.TimeUpdate { stats.NewTicker(s.NewTime) } else { stats.Sample() } } }() rcKeys := []string{*rmtKey} if sess.multiRmtKey { rcKeys = deleteEmpty(strings.Split(*rmtKey, ",")) } for { sess.amqpConn, err = amqp.DialConfig(*uri, sess.amqpConfig) if err != nil { log.Warnf("Failed to connect to AMQP: %q", err) // Rate limit reconnection attempts time.Sleep(5 * time.Second) } else { rc, err = agents.NewRemoteControl(sess.amqpConn, rcKeys, *exchange) if err != nil { log.Warnf("Failed creating NewRemoteControl: %s", err) } else { sess.rcChan <- rc.Commands } if stats == nil { // initial setup stats, err = agents.NewProcessStats( sess.amqpConn, *procStatsKey, *exchange, &sess.job, *statsInterval, *msgTimeout, *userJSON, ) if err != nil { log.Warnf("Failed creating NewProcessStats: %s", err) } } else { err = stats.ReinitializeConnection(sess.amqpConn) if err != nil { log.Warnf("Failed to reinitialize process stats: %s", err) } } closings := sess.amqpConn.NotifyClose(make(chan *amqp.Error)) // Wait for close notification and loop back around to reconnect _ = <-closings log.Debugln("Saw a notification for closed connection, looping") } } }