func start(conf *config.Config, termCh chan struct{}) error { if conf.Silent { logging.SetLogLevel(logging.ERROR) } if conf.Verbose { logging.SetLogLevel(logging.DEBUG) } logger.Infof("Starting mackerel-agent version:%s, rev:%s, apibase:%s", version.VERSION, version.GITCOMMIT, conf.Apibase) if err := createPidFile(conf.Pidfile); err != nil { return fmt.Errorf("createPidFile(%q) failed: %s", conf.Pidfile, err) } defer removePidFile(conf.Pidfile) ctx, err := command.Prepare(conf) if err != nil { return fmt.Errorf("command.Prepare failed: %s", err) } c := make(chan os.Signal, 1) signal.Notify(c, os.Interrupt, syscall.SIGTERM, syscall.SIGQUIT, syscall.SIGHUP) go signalHandler(c, ctx, termCh) return command.Run(ctx, termCh) }
func doMain(argv []string) int { conf, otherOpts := resolveConfig(argv) if conf == nil { return exitStatusError } if otherOpts != nil && otherOpts.printVersion { return doVersion([]string{}) } if conf.Verbose { logging.SetLogLevel(logging.DEBUG) } logger.Infof("Starting mackerel-agent version:%s, rev:%s, apibase:%s", version.VERSION, version.GITCOMMIT, conf.Apibase) if otherOpts != nil && otherOpts.runOnce { command.RunOnce(conf) return exitStatusOK } if conf.Apikey == "" { logger.Criticalf("Apikey must be specified in the command-line flag or in the config file") return exitStatusError } return start(conf) }
func TestLoop(t *testing.T) { if testing.Verbose() { logging.SetLogLevel(logging.DEBUG) } conf, mockHandlers, ts := newMockAPIServer(t) defer ts.Close() if testing.Short() { // Shrink time scale originalPostMetricsInterval := config.PostMetricsInterval config.PostMetricsInterval = 10 * time.Second ratio := config.PostMetricsInterval.Seconds() / originalPostMetricsInterval.Seconds() conf.Connection.PostMetricsDequeueDelaySeconds = int(float64(config.DefaultConfig.Connection.PostMetricsRetryDelaySeconds) * ratio) conf.Connection.PostMetricsRetryDelaySeconds = int(float64(config.DefaultConfig.Connection.PostMetricsRetryDelaySeconds) * ratio) defer func() { config.PostMetricsInterval = originalPostMetricsInterval }() } /// Simulate the situation that mackerel.io is down for 3 min // Strategy: // counterGenerator generates values 1,2,3,4,... // when we got value 3, the server will start responding 503 for three times (inclusive) // so the agent should queue the generated values and retry sending. // // status: o . o . x . x . x . o o o o o // send: 1 . 2 . 3 . 3 . 3 . 3 4 5 6 7 // collect: 1 . 2 . 3 . 4 . 5 . 6 . 7 . 8 // ^ // 30s const ( totalFailures = 3 totalPosts = 7 ) failureCount := 0 receivedDataPoints := []mackerel.CreatingMetricsValue{} done := make(chan struct{}) mockHandlers["POST /api/v0/tsdb"] = func(req *http.Request) (int, jsonObject) { payload := []mackerel.CreatingMetricsValue{} json.NewDecoder(req.Body).Decode(&payload) for _, p := range payload { value := p.Value.(float64) if value == 3 { failureCount++ if failureCount <= totalFailures { return 503, jsonObject{ "failure": failureCount, // just for DEBUG logging } } } if value == totalPosts { defer func() { done <- struct{}{} }() } } receivedDataPoints = append(receivedDataPoints, payload...) return 200, jsonObject{ "success": true, } } mockHandlers["PUT /api/v0/hosts/xyzabc12345"] = func(req *http.Request) (int, jsonObject) { return 200, jsonObject{ "result": "OK", } } // Prepare required objects... ag := &agent.Agent{ MetricsGenerators: []metrics.Generator{ &counterGenerator{}, }, } api, err := mackerel.NewAPI(conf.Apibase, conf.Apikey, true) if err != nil { t.Fatal(err) } host := &mackerel.Host{ID: "xyzabc12345"} termCh := make(chan struct{}) exitCh := make(chan int) c := &Context{ ag: ag, conf: &conf, api: api, host: host, } // Start looping! go func() { exitCh <- loop(c, termCh) }() <-done // Verify results if len(receivedDataPoints) != totalPosts { t.Errorf("the agent should have sent %d datapoints, got: %+v", totalPosts, receivedDataPoints) } sort.Sort(byTime(receivedDataPoints)) for i := 0; i < totalPosts; i++ { value := receivedDataPoints[i].Value.(float64) if value != float64(i+1) { t.Errorf("the %dth datapoint should have value %d, got: %+v", i, i+1, receivedDataPoints) } } termCh <- struct{}{} exitCode := <-exitCh if exitCode != 0 { t.Errorf("exit code should be 0, got: %d", exitCode) } }