// Prepare sets up API and registers the host data to the Mackerel server. // Use returned values to call Run(). func Prepare(conf *config.Config) (*Context, error) { api, err := mackerel.NewAPI(conf.Apibase, conf.Apikey, conf.Verbose) if err != nil { return nil, fmt.Errorf("Failed to prepare an api: %s", err.Error()) } host, err := prepareHost(conf, api) if err != nil { return nil, fmt.Errorf("Failed to prepare host: %s", err.Error()) } return &Context{ Agent: NewAgent(conf), Config: conf, Host: host, API: api, }, nil }
// Prepare sets up API and registers the host data to the Mackerel server. // Use returned values to call Run(). func Prepare(conf *config.Config) (*Context, error) { api, err := mackerel.NewAPI(conf.Apibase, conf.Apikey, conf.Verbose) if err != nil { return nil, fmt.Errorf("Failed to prepare an api: %s", err.Error()) } host, err := prepareHost(conf.Root, api, conf.Roles, conf.CheckNames(), conf.DisplayName, conf.HostStatus.OnStart) if err != nil { return nil, fmt.Errorf("Failed to prepare host: %s", err.Error()) } return &Context{ ag: NewAgent(conf), conf: conf, host: host, api: api, }, nil }
func doRetire(argv []string) int { conf, force, err := resolveConfigForRetire(argv) if err != nil { return exitStatusError } if conf.Apikey == "" { logger.Criticalf("Apikey must be specified in the command-line flag or in the config file") return exitStatusError } hostID, err := command.LoadHostID(conf.Root) if err != nil { logger.Warningf("HostID file is not found") return exitStatusError } api, err := mackerel.NewAPI(conf.Apibase, conf.Apikey, conf.Verbose) if err != nil { logger.Errorf("failed to create api client: %s", err) return exitStatusError } if !force && !prompter.YN(fmt.Sprintf("retire this host? (hostID: %s)", hostID), false) { logger.Infof("Retirement is canceled.") return exitStatusError } err = api.RetireHost(hostID) if err != nil { logger.Errorf("failed to retire the host: %s", err) return exitStatusError } logger.Infof("This host (hostID: %s) has been retired.", hostID) // just to try to remove hostID file. err = command.RemoveIDFile(conf.Root) if err != nil { logger.Warningf("Failed to remove HostID file: %s", err) } return exitStatusOK }
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) } }