common.go 5.0 KB
Newer Older
LinuxSuRen's avatar
LinuxSuRen 已提交
1 2 3 4 5 6 7
package client

import (
	"crypto/tls"
	"encoding/base64"
	"encoding/json"
	"fmt"
8
	"io"
LinuxSuRen's avatar
LinuxSuRen 已提交
9 10 11 12
	"io/ioutil"
	"log"
	"net/http"
	"net/url"
13 14

	"github.com/jenkins-zh/jenkins-cli/app"
LinuxSuRen's avatar
LinuxSuRen 已提交
15 16
)

17
// JenkinsCore core informations of Jenkins
LinuxSuRen's avatar
LinuxSuRen 已提交
18 19 20 21 22 23 24
type JenkinsCore struct {
	JenkinsCrumb
	URL       string
	UserName  string
	Token     string
	Proxy     string
	ProxyAuth string
25

26
	Debug        bool
LinuxSuRen's avatar
LinuxSuRen 已提交
27
	Output       io.Writer
28
	RoundTripper http.RoundTripper
LinuxSuRen's avatar
LinuxSuRen 已提交
29 30
}

31
// JenkinsCrumb crumb for Jenkins
LinuxSuRen's avatar
LinuxSuRen 已提交
32 33 34 35 36
type JenkinsCrumb struct {
	CrumbRequestField string
	Crumb             string
}

37
// GetClient get the default http Jenkins client
LinuxSuRen's avatar
LinuxSuRen 已提交
38
func (j *JenkinsCore) GetClient() (client *http.Client) {
39 40 41 42 43 44
	var roundTripper http.RoundTripper
	if j.RoundTripper != nil {
		roundTripper = j.RoundTripper
	} else {
		tr := &http.Transport{
			TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
LinuxSuRen's avatar
LinuxSuRen 已提交
45
		}
46 47 48 49 50 51
		if j.Proxy != "" {
			if proxyURL, err := url.Parse(j.Proxy); err == nil {
				tr.Proxy = http.ProxyURL(proxyURL)
			} else {
				log.Fatal(err)
			}
LinuxSuRen's avatar
LinuxSuRen 已提交
52

53 54 55 56 57
			if j.ProxyAuth != "" {
				basicAuth := "Basic " + base64.StdEncoding.EncodeToString([]byte(j.ProxyAuth))
				tr.ProxyConnectHeader = http.Header{}
				tr.ProxyConnectHeader.Add("Proxy-Authorization", basicAuth)
			}
LinuxSuRen's avatar
LinuxSuRen 已提交
58
		}
59
		roundTripper = tr
LinuxSuRen's avatar
LinuxSuRen 已提交
60
	}
61
	client = &http.Client{Transport: roundTripper}
LinuxSuRen's avatar
LinuxSuRen 已提交
62 63 64
	return
}

65
// ProxyHandle takes care of the proxy setting
LinuxSuRen's avatar
LinuxSuRen 已提交
66 67 68 69 70 71 72
func (j *JenkinsCore) ProxyHandle(request *http.Request) {
	if j.ProxyAuth != "" {
		basicAuth := "Basic " + base64.StdEncoding.EncodeToString([]byte(j.ProxyAuth))
		request.Header.Add("Proxy-Authorization", basicAuth)
	}
}

73
// AuthHandle takes care of the auth
LinuxSuRen's avatar
LinuxSuRen 已提交
74
func (j *JenkinsCore) AuthHandle(request *http.Request) (err error) {
75 76 77 78
	if j.UserName != "" && j.Token != "" {
		request.SetBasicAuth(j.UserName, j.Token)
	}

79 80 81 82 83
	// not add the User-Agent for tests
	if j.RoundTripper == nil {
		request.Header.Set("User-Agent", app.GetCombinedVersion())
	}

LinuxSuRen's avatar
LinuxSuRen 已提交
84 85
	j.ProxyHandle(request)

86
	// all post request to Jenkins must be has the crumb
LinuxSuRen's avatar
LinuxSuRen 已提交
87 88 89 90 91 92
	if request.Method == "POST" {
		err = j.CrumbHandle(request)
	}
	return
}

93
// CrumbHandle handle crum with http request
LinuxSuRen's avatar
LinuxSuRen 已提交
94 95 96 97 98 99 100 101 102 103 104 105 106
func (j *JenkinsCore) CrumbHandle(request *http.Request) error {
	if c, err := j.GetCrumb(); err == nil && c != nil {
		// cannot get the crumb could be a noraml situation
		j.CrumbRequestField = c.CrumbRequestField
		j.Crumb = c.Crumb
		request.Header.Add(j.CrumbRequestField, j.Crumb)
	} else {
		return err
	}

	return nil
}

107 108 109 110 111 112
// GetCrumb get the crumb from Jenkins
func (j *JenkinsCore) GetCrumb() (crumbIssuer *JenkinsCrumb, err error) {
	var (
		statusCode int
		data       []byte
	)
LinuxSuRen's avatar
LinuxSuRen 已提交
113

114 115 116 117 118 119 120 121 122 123 124 125 126
	if statusCode, data, err = j.Request("GET", "/crumbIssuer/api/json", nil, nil); err == nil {
		if statusCode == 200 {
			json.Unmarshal(data, &crumbIssuer)
		} else if statusCode == 404 {
			// return 404 if Jenkins does no have crumb
		} else {
			err = fmt.Errorf("unexpected status code: %d", statusCode)
		}
	}

	return
}

LinuxSuRen's avatar
LinuxSuRen 已提交
127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146
// RequestWithData requests the api and parse the data into an interface
func (j *JenkinsCore) RequestWithData(method, api string, headers map[string]string,
	payload io.Reader, successCode int, obj interface{}) (err error) {
	var (
		statusCode int
		data       []byte
	)

	if statusCode, data, err = j.Request(method, api, headers, payload); err == nil {
		if statusCode == successCode {
			json.Unmarshal(data, obj)
		} else {
			err = j.ErrorHandle(statusCode, data)
		}
	}
	return
}

// RequestWithoutData requests the api without handling data
func (j *JenkinsCore) RequestWithoutData(method, api string, headers map[string]string,
147
	payload io.Reader, successCode int) (statusCode int, err error) {
LinuxSuRen's avatar
LinuxSuRen 已提交
148
	var (
149
		data []byte
LinuxSuRen's avatar
LinuxSuRen 已提交
150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167
	)

	if statusCode, data, err = j.Request(method, api, headers, payload); err == nil &&
		statusCode != successCode {
		err = j.ErrorHandle(statusCode, data)
	}
	return
}

// ErrorHandle handles the error cases
func (j *JenkinsCore) ErrorHandle(statusCode int, data []byte) (err error) {
	err = fmt.Errorf("unexpected status code: %d", statusCode)
	if j.Debug {
		ioutil.WriteFile("debug.html", data, 0664)
	}
	return
}

168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187
// RequestWithResponse make a common request
func (j *JenkinsCore) RequestWithResponse(method, api string, headers map[string]string, payload io.Reader) (
	response *http.Response, err error) {
	var (
		req      *http.Request
	)

	if req, err = http.NewRequest(method, fmt.Sprintf("%s%s", j.URL, api), payload); err != nil {
		return
	}
	j.AuthHandle(req)

	for k, v := range headers {
		req.Header.Add(k, v)
	}

	client := j.GetClient()
	return client.Do(req)
}

188 189 190 191 192 193 194 195 196 197
// Request make a common request
func (j *JenkinsCore) Request(method, api string, headers map[string]string, payload io.Reader) (
	statusCode int, data []byte, err error) {
	var (
		req      *http.Request
		response *http.Response
	)

	if req, err = http.NewRequest(method, fmt.Sprintf("%s%s", j.URL, api), payload); err != nil {
		return
LinuxSuRen's avatar
LinuxSuRen 已提交
198 199 200
	}
	j.AuthHandle(req)

201 202 203 204
	for k, v := range headers {
		req.Header.Add(k, v)
	}

LinuxSuRen's avatar
LinuxSuRen 已提交
205
	client := j.GetClient()
206 207 208
	if response, err = client.Do(req); err == nil {
		statusCode = response.StatusCode
		data, err = ioutil.ReadAll(response.Body)
LinuxSuRen's avatar
LinuxSuRen 已提交
209
	}
210
	return
LinuxSuRen's avatar
LinuxSuRen 已提交
211
}