// FileChecker checks the existence of a file and returns an error // if the file exists. func FileChecker(f string) health.Checker { return health.CheckFunc(func() error { if _, err := os.Stat(f); err == nil { return errors.New("file exists") } return nil }) }
// TCPChecker attempts to open a TCP connection. func TCPChecker(addr string, timeout time.Duration) health.Checker { return health.CheckFunc(func() error { conn, err := net.DialTimeout("tcp", addr, timeout) if err != nil { return errors.New("connection to " + addr + " failed") } conn.Close() return nil }) }
// HTTPChecker does a HEAD request and verifies if the HTTP status // code return is a 200, taking the application out of rotation if // otherwise func HTTPChecker(r string) health.Checker { return health.CheckFunc(func() error { response, err := http.Head(r) if err != nil { return errors.New("error while checking: " + r) } if response.StatusCode != http.StatusOK { return errors.New("downstream service returned unexpected status: " + string(response.StatusCode)) } return nil }) }
// Start checks the modules for the following interfaces and registers and/or starts: // Startable: // health.Checker: // Endpoint: Register the handler function of the Endpoint in the http service at prefix func (s *Service) Start() error { el := protocol.NewErrorList("service: errors occured while starting: ") if s.healthEndpoint != "" { loggerService.WithField("healthEndpoint", s.healthEndpoint).Info("Health endpoint") s.webserver.Handle(s.healthEndpoint, http.HandlerFunc(health.StatusHandler)) } else { loggerService.Info("Health endpoint disabled") } if s.metricsEndpoint != "" { loggerService.WithField("metricsEndpoint", s.metricsEndpoint).Info("Metrics endpoint") s.webserver.Handle(s.metricsEndpoint, http.HandlerFunc(metrics.HttpHandler)) } else { loggerService.Info("Metrics endpoint disabled") } for _, module := range s.modules { name := reflect.TypeOf(module).String() if startable, ok := module.(Startable); ok { loggerService.WithFields(log.Fields{ "name": name, }).Info("Starting module") if err := startable.Start(); err != nil { loggerService.WithFields(log.Fields{ "name": name, "err": err, }).Error("Error while starting module") el.Add(err) } } if checker, ok := module.(health.Checker); ok && s.healthEndpoint != "" { loggerService.WithField("name", name).Info("Registering module as Health-Checker") health.RegisterPeriodicThresholdFunc(name, s.healthFrequency, s.healthThreshold, health.CheckFunc(checker.Check)) } if endpoint, ok := module.(Endpoint); ok { prefix := endpoint.GetPrefix() loggerService.WithFields(log.Fields{ "name": name, "prefix": prefix, }).Info("Registering module as Endpoint") s.webserver.Handle(prefix, endpoint) } } return el.ErrorOrNil() }
// Start checks the modules for the following interfaces and registers and/or starts: // Startable: // health.Checker: // Endpoint: Register the handler function of the Endpoint in the http service at prefix func (s *Service) Start() error { var multierr *multierror.Error if s.healthEndpoint != "" { logger.WithField("healthEndpoint", s.healthEndpoint).Info("Health endpoint") s.webserver.Handle(s.healthEndpoint, http.HandlerFunc(health.StatusHandler)) } else { logger.Info("Health endpoint disabled") } if s.metricsEndpoint != "" { logger.WithField("metricsEndpoint", s.metricsEndpoint).Info("Metrics endpoint") s.webserver.Handle(s.metricsEndpoint, http.HandlerFunc(metrics.HttpHandler)) } else { logger.Info("Metrics endpoint disabled") } for order, iface := range s.ModulesSortedByStartOrder() { name := reflect.TypeOf(iface).String() if s, ok := iface.(Startable); ok { logger.WithFields(log.Fields{"name": name, "order": order}).Info("Starting module") if err := s.Start(); err != nil { logger.WithError(err).WithField("name", name).Error("Error while starting module") multierr = multierror.Append(multierr, err) } } else { logger.WithFields(log.Fields{"name": name, "order": order}).Debug("Module is not startable") } if c, ok := iface.(health.Checker); ok && s.healthEndpoint != "" { logger.WithField("name", name).Info("Registering module as Health-Checker") health.RegisterPeriodicThresholdFunc(name, s.healthFrequency, s.healthThreshold, health.CheckFunc(c.Check)) } if e, ok := iface.(Endpoint); ok { prefix := e.GetPrefix() logger.WithFields(log.Fields{"name": name, "prefix": prefix}).Info("Registering module as Endpoint") s.webserver.Handle(prefix, e) } } return multierr.ErrorOrNil() }
// HTTPChecker does a HEAD request and verifies that the HTTP status code // returned matches statusCode. func HTTPChecker(r string, statusCode int, timeout time.Duration, headers http.Header) health.Checker { return health.CheckFunc(func() error { client := http.Client{ Timeout: timeout, } req, err := http.NewRequest("HEAD", r, nil) if err != nil { return errors.New("error creating request: " + r) } for headerName, headerValues := range headers { for _, headerValue := range headerValues { req.Header.Add(headerName, headerValue) } } response, err := client.Do(req) if err != nil { return errors.New("error while checking: " + r) } if response.StatusCode != statusCode { return errors.New("downstream service returned unexpected status: " + strconv.Itoa(response.StatusCode)) } return nil }) }