func (c *Client) GetBuildLog(job string, id int) (string, error) {
	url := fmt.Sprintf("%s/job/%s/%d/consoleText", c.Baseurl, job, id)
	resp, err := http.Get(url)
	if err != nil {
		return "", err
	}
	defer resp.Body.Close()
	if resp.StatusCode != 200 {
		return "", fmt.Errorf("jenkins get logs request to %s responded with status %d", url, resp.StatusCode)
	}
	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return "", fmt.Errorf("reading body from logs response to %s failed: %v", url, err)
	}
	return parseFailedBuildLog(job, url, string(body)), nil
}
GetBuildLog returns the consoleText for a Jenkins build.
package jenkins
import (
	"bytes"
	"encoding/json"
	"errors"
	"fmt"
	"io/ioutil"
	"net/http"
	"net/url"
	"os"
	"regexp"
	"strconv"
	"strings"
	"time"
	"github.com/Sirupsen/logrus"
)
type Client struct {
	Baseurl  string `json:"base_url"`
	Username string `json:"username"`
	Token    string `json:"token"`
}
type Response struct {
	Name  string `json:"name"`
	Build Build  `json:"build"`
}
type Build struct {
	Number     int             `json:"number"`
	URL        string          `json:"full_url"`
	Phase      string          `json:"phase"`
	Status     string          `json:"status"`
	Parameters BuildParameters `json:"parameters"`
}
type BuildParameters struct {
	GitBaseRepo string `json:"GIT_BASE_REPO"`
	GitSha      string `json:"GIT_SHA1"`
	PR          string `json:"PR"`
}
type Request struct {
	Parameters []map[string]string `json:"parameter,omitempty"`
}
type JobBuildsResponse struct {
	Builds []RecentBuild `json:"builds,omitempty"`
}
type RecentBuild struct {
	ID        string    `json:"id,omitempty"`
	Actions   []Action  `json:"actions,omitempty"`
	Building  bool      `json:"building,omitempty"`
	Timestamp time.Time `json:"timstamp,omitempty"`
	NodeName  string    `json:"builtOn,omitempty"`
}
type Action struct {
	Parameters []Parameter `json:"parameters,omitempty"`
}
type Parameter struct {
	Name  string `json:"name,omitempty"`
	Value string `json:"value,omitempty"`
}
type QueuedBuildsResponse struct {
	Builds []QueuedBuild `json:"items,omitempty"`
}
type QueuedBuild struct {
	ID      int       `json:"id,omitempty"`
	Actions []Action  `json:"actions,omitempty"`
	Task    QueueTask `json:"task,omitempty"`
}
type QueueTask struct {
	Name string `json:"name,omitempty"`
}
func New(uri, username, token string) *Client {
	return &Client{
		Baseurl:  uri,
		Username: username,
		Token:    token,
	}
}
func (c *Client) Build(job string, data Request) error {
	d, err := json.Marshal(data)
	if err != nil {
		return err
	}
	url := fmt.Sprintf("%s/job/%s/build", c.Baseurl, job)
	req, err := http.NewRequest("POST", url, bytes.NewBuffer(d))
	if err != nil {
		return err
	}
	req.SetBasicAuth(c.Username, c.Token)
	client := &http.Client{}
	resp, err := client.Do(req)
	if err != nil {
		return err
	}
	if resp.StatusCode != 201 {
		return fmt.Errorf("jenkins post to %s responded with status %d, data: %s", url, resp.StatusCode, string(d))
	}
	return nil
}
func (c *Client) BuildWithParameters(job string, parameters string) error {
	url := fmt.Sprintf("%s/job/%s/buildWithParameters?%s", c.Baseurl, job, parameters)
	req, err := http.NewRequest("POST", url, bytes.NewBuffer([]byte{}))
	if err != nil {
		return err
	}
	req.SetBasicAuth(c.Username, c.Token)
	client := &http.Client{}
	resp, err := client.Do(req)
	if err != nil {
		return err
	}
	if resp.StatusCode != 201 {
		return fmt.Errorf("jenkins post to %s responded with status %d", url, resp.StatusCode)
	}
	return nil
}
func (c *Client) BuildPipeline(job string, prNumber int, prRef string) error {
	subJobName := prRef
	if prNumber != 0 {
		subJobName = fmt.Sprintf("PR-%d", prNumber)
	}
	url := fmt.Sprintf("%s/job/%s/job/%s/build", c.Baseurl, job, subJobName)
	req, err := http.NewRequest("POST", url, bytes.NewBuffer([]byte{}))
	if err != nil {
		return err
	}
	req.SetBasicAuth(c.Username, c.Token)
	client := &http.Client{}
	resp, err := client.Do(req)
	if err != nil {
		return err
	}
	if resp.StatusCode != 201 {
		return fmt.Errorf("jenkins post to %s responded with status %d", url, resp.StatusCode)
	}
	return nil
}
func (c *Client) CancelBuildsForPR(job, pr string) error {
	if env := os.Getenv("LEEROY_KEEP_OLD_BUILD_RUNNING"); env != "" {
		return errors.New("LEEROY_KEEP_OLD_BUILD_RUNNING is set")
	}
	var e string
	q, err := c.GetQueuedBuildForPR(job, pr)
	if err != nil {
		e = fmt.Sprintf("Getting queued build for job %s, pr %s failed: %v; ", job, pr, err)
	} else if q != nil {
		if err := c.CancelBuild(job, strconv.Itoa(q.ID), true); err != nil {
			e = fmt.Sprintf("cancelling queued build for job %s, pr %s failed: %v; ", job, pr, err)
		}
		logrus.Infof("Cancelled queued build (%d) for job %s, pr %s", q.ID, job, pr)
	}
	b, err := c.GetRunningBuildForPR(job, pr)
	if err != nil {
		e += fmt.Sprintf("Getting running build for job %s, pr %s failed: %v;", job, pr, err)
	} else if b != nil {
		if err := c.CancelBuild(job, b.ID, false); err != nil {
			e += fmt.Sprintf("cancelling running build for job %s, pr %s failed: %v;", job, pr, err)
		}
		logrus.Infof("Cancelled running build (%s) for job %s, pr %s", b.ID, job, pr)
	}
	if e != "" {
		return errors.New(e)
	}
	return nil
}
func (c *Client) CancelBuild(job, id string, isQueued bool) error {
	url := fmt.Sprintf("%s/job/%s/%s/stop", c.Baseurl, job, id)
	if isQueued {
		url = fmt.Sprintf("%s/queue/cancelItem?id=%s", c.Baseurl, id)
	}
	req, err := http.NewRequest("POST", url, bytes.NewBuffer([]byte{}))
	if err != nil {
		return err
	}
	req.SetBasicAuth(c.Username, c.Token)
	client := &http.Client{}
	resp, err := client.Do(req)
	if err != nil {
		return err
	}
	if resp.StatusCode != 201 && resp.StatusCode != 200 {
		return fmt.Errorf("jenkins post to %s responded with status %d", url, resp.StatusCode)
	}
	return nil
}
func (c *Client) GetRunningBuildForPR(job, pr string) (*RecentBuild, error) {
	builds, err := c.GetBuilds(job)
	if err != nil {
		return nil, err
	}
	for _, build := range builds {
		if build.Building {
			for _, a := range build.Actions {
				for _, p := range a.Parameters {
					if p.Name == "PR" && p.Value == pr {
						return &build, nil
					}
				}
			}
		}
	}
	return nil, nil
}
func (c *Client) GetBuilds(job string) (b []RecentBuild, err error) {
	url := fmt.Sprintf("%s/job/%s/api/json?tree=%s", c.Baseurl, job, url.QueryEscape("builds[builtOn,actions[parameters[name,value]],timestamp,id,building]"))
	req, err := http.NewRequest("GET", url, bytes.NewBuffer([]byte{}))
	if err != nil {
		return b, err
	}
	req.SetBasicAuth(c.Username, c.Token)
	client := &http.Client{}
	resp, err := client.Do(req)
	if err != nil {
		return b, err
	}
	defer resp.Body.Close()
	if resp.StatusCode != 200 {
		return b, fmt.Errorf("jenkins get builds for %s request to %s responded with status %d", job, url, resp.StatusCode)
	}
	var r JobBuildsResponse
	if err := json.NewDecoder(resp.Body).Decode(&r); err != nil {
		return b, fmt.Errorf("decoding json response from builds from %s failed: %v", url, err)
	}
	return r.Builds, nil
}
func (c *Client) GetQueuedBuildForPR(job, pr string) (*QueuedBuild, error) {
	url := fmt.Sprintf("%s/queue/api/json?tree=%s", c.Baseurl, url.QueryEscape("items[id,task[name]]"))
	req, err := http.NewRequest("GET", url, bytes.NewBuffer([]byte{}))
	if err != nil {
		return nil, err
	}
	req.SetBasicAuth(c.Username, c.Token)
	client := &http.Client{}
	resp, err := client.Do(req)
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()
	if resp.StatusCode != 200 {
		return nil, fmt.Errorf("jenkins get queued builds request to %s responded with status %d", url, resp.StatusCode)
	}
	var r QueuedBuildsResponse
	if err := json.NewDecoder(resp.Body).Decode(&r); err != nil {
		return nil, fmt.Errorf("decoding json response from queued builds from %s failed: %v", url, err)
	}
	for _, build := range r.Builds {
		if build.Task.Name == job {
			for _, a := range build.Actions {
				for _, p := range a.Parameters {
					if p.Name == "PR" && p.Value == pr {
						return &build, nil
					}
				}
			}
		}
	}
	return nil, nil
}