telegraf/plugins/inputs/jenkins/client.go

157 lines
3.4 KiB
Go

package jenkins
import (
"context"
"encoding/json"
"fmt"
"net/http"
"strings"
)
type client struct {
baseURL string
httpClient *http.Client
username string
password string
sessionCookie *http.Cookie
semaphore chan struct{}
}
func newClient(httpClient *http.Client, url, username, password string, maxConnections int) *client {
return &client{
baseURL: url,
httpClient: httpClient,
username: username,
password: password,
semaphore: make(chan struct{}, maxConnections),
}
}
func (c *client) init() error {
// get session cookie
req, err := http.NewRequest("GET", c.baseURL, nil)
if err != nil {
return err
}
if c.username != "" && c.password != "" {
// set auth
req.SetBasicAuth(c.username, c.password)
}
resp, err := c.httpClient.Do(req)
if err != nil {
return err
}
defer resp.Body.Close()
for _, cc := range resp.Cookies() {
if strings.Contains(cc.Name, "JSESSIONID") {
c.sessionCookie = cc
break
}
}
// first api fetch
if err := c.doGet(context.Background(), jobPath, new(jobResponse)); err != nil {
return err
}
return nil
}
func (c *client) doGet(ctx context.Context, url string, v interface{}) error {
req, err := createGetRequest(c.baseURL+url, c.username, c.password, c.sessionCookie)
if err != nil {
return err
}
select {
case c.semaphore <- struct{}{}:
break
case <-ctx.Done():
return ctx.Err()
}
resp, err := c.httpClient.Do(req.WithContext(ctx))
if err != nil {
<-c.semaphore
return err
}
defer func() {
resp.Body.Close()
<-c.semaphore
}()
// Clear invalid token if unauthorized
if resp.StatusCode == http.StatusUnauthorized {
c.sessionCookie = nil
return APIError{
URL: url,
StatusCode: resp.StatusCode,
Title: resp.Status,
}
}
if resp.StatusCode < 200 || resp.StatusCode >= 300 {
return APIError{
URL: url,
StatusCode: resp.StatusCode,
Title: resp.Status,
}
}
if resp.StatusCode == http.StatusNoContent {
return APIError{
URL: url,
StatusCode: resp.StatusCode,
Title: resp.Status,
}
}
if err = json.NewDecoder(resp.Body).Decode(v); err != nil {
return err
}
return nil
}
type APIError struct {
URL string
StatusCode int
Title string
Description string
}
func (e APIError) Error() string {
if e.Description != "" {
return fmt.Sprintf("[%s] %s: %s", e.URL, e.Title, e.Description)
}
return fmt.Sprintf("[%s] %s", e.URL, e.Title)
}
func createGetRequest(url string, username, password string, sessionCookie *http.Cookie) (*http.Request, error) {
req, err := http.NewRequest("GET", url, nil)
if err != nil {
return nil, err
}
if sessionCookie != nil {
req.AddCookie(sessionCookie)
} else if username != "" && password != "" {
req.SetBasicAuth(username, password)
}
req.Header.Add("Accept", "application/json")
return req, nil
}
func (c *client) getJobs(ctx context.Context, jr *jobRequest) (js *jobResponse, err error) {
js = new(jobResponse)
url := jobPath
if jr != nil {
url = jr.URL()
}
err = c.doGet(ctx, url, js)
return js, err
}
func (c *client) getBuild(ctx context.Context, jr jobRequest, number int64) (b *buildResponse, err error) {
b = new(buildResponse)
url := jr.buildURL(number)
err = c.doGet(ctx, url, b)
return b, err
}
func (c *client) getAllNodes(ctx context.Context) (nodeResp *nodeResponse, err error) {
nodeResp = new(nodeResponse)
err = c.doGet(ctx, nodePath, nodeResp)
return nodeResp, err
}