package woodpecker import ( "fmt" "io" "net/http" "os" "regexp" "strconv" "strings" "git.stinnesbeck.com/go/log" ) type Server struct { Host string `json:"host"` Token string `json:"token"` } type Stats struct { PendingJobs uint `json:"woodpecker_pending_jobs"` RunningJobs uint `json:"woodpecker_running_jobs"` IdlingWorkers uint `json:"woodpecker_worker_count"` } // NewServer returns a Server object for host func NewServer(host string, token string) Server { switch { case strings.HasPrefix(host, "http://"), strings.HasPrefix(host, "https://"): return Server{ Host: host, Token: token, } default: log.PrintError("host needs to start with 'http://' or 'https://'") os.Exit(1) return Server{} } } func (s Server) GetStats() (*Stats, error) { // prepare request req, err := http.NewRequest(http.MethodGet, s.Host+"/metrics/", nil) if err != nil { return nil, err } // add authorization header req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", s.Token)) c := http.DefaultClient response, err := c.Do(req) if err != nil { return nil, err } b, err := io.ReadAll(response.Body) if err != nil { return nil, err } lines := strings.Split(string(b), "\n") r := regexp.MustCompile(`^((woodpecker_)(worker_count|pending_jobs|running_jobs)) (\d+)`) var stats Stats for i := range lines { // skip rest of loop if this line doesn't match regex if !r.MatchString(lines[i]) { continue } // line matches regex matches := r.FindStringSubmatch(lines[i]) // assign stats to struct switch matches[3] { case "pending_jobs": stats.PendingJobs = parseStringToUint(matches[4]) case "running_jobs": stats.RunningJobs = parseStringToUint(matches[4]) case "worker_count": stats.IdlingWorkers = parseStringToUint(matches[4]) } } return &stats, nil } // parseStringToUint parses a string and returns the resulting uint func parseStringToUint(s string) uint { num, err := strconv.Atoi(s) if err != nil { log.PrintError("can't parse string to uint,", err) os.Exit(1) } return uint(num) }