157 lines
3.4 KiB
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
|
||
|
}
|