forked from appleboy/drone-jenkins
-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathjenkins.go
226 lines (181 loc) · 5.17 KB
/
jenkins.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
package main
import (
"encoding/json"
"errors"
"fmt"
"io"
"io/ioutil"
"log"
"net/http"
"net/http/cookiejar"
"net/url"
"strings"
)
type (
// Auth contain username and token
Auth struct {
Username string
Token string
}
// Jenkins contain Auth and BaseURL
Jenkins struct {
Auth *Auth
BaseURL string
Data map[string]string
client http.Client
}
// Crumb contain the jenkins XSRF token header-name and header-value
Crumb struct {
Class string `json:"_class"`
Crumb string `json:"crumb"`
CrumbRequestField string `json:"crumbRequestField"`
}
)
// NewJenkins is initial Jenkins object
func NewJenkins(auth *Auth, url string, data map[string]string) *Jenkins {
url = strings.TrimRight(url, "/")
var j = Jenkins{
Auth: auth,
BaseURL: url,
Data: data,
}
j.initClient()
return &j
}
func (jenkins *Jenkins) buildURL(path string, queryParams url.Values) (requestURL string) {
requestURL = jenkins.BaseURL + path
if queryParams != nil {
queryString := queryParams.Encode()
if queryString != "" {
requestURL = requestURL + "?" + queryString
}
}
return
}
func (jenkins *Jenkins) initClient() {
jar, err := cookiejar.New(nil)
if err != nil {
log.Printf("Got error while creating cookie jar %s", err.Error())
}
jenkins.client = http.Client{
Jar: jar,
}
}
func (jenkins *Jenkins) sendRequest(req *http.Request) (*http.Response, error) {
if jenkins.Auth != nil {
req.SetBasicAuth(jenkins.Auth.Username, jenkins.Auth.Token)
}
resp, err := jenkins.client.Do(req)
if err != nil {
log.Println(err)
} else {
// check for response error 401
if resp.StatusCode == 401 {
return resp, errors.New("HTTP 401 - invalid password/token for user")
}
for _, cookie := range resp.Cookies() {
log.Printf("trace - parseResponse - SET COOKIE: %s \n", cookie.Name)
}
}
return resp, err
}
func (jenkins *Jenkins) parseResponse(resp *http.Response, body interface{}) (err error) {
defer resp.Body.Close()
data, err := ioutil.ReadAll(resp.Body)
if err != nil {
return
}
// for debug if you would like to show the raw json data
log.Printf("trace - parseResponse - raw data: %s \n", data)
if body == nil {
return
}
return json.Unmarshal(data, body)
}
func (jenkins *Jenkins) loadXSRFtoken(body interface{}) (err error) {
// call the json endpoint of jenkins API for the XSRF Token
requestURL := jenkins.buildURL("/crumbIssuer/api/json", nil)
req, err := http.NewRequest("GET", requestURL, nil)
if err != nil {
return
}
resp, err := jenkins.sendRequest(req)
if err != nil {
return
}
if resp.StatusCode == 404 {
log.Print("info - loadXSRFtoken - XSRF is not enabled in remote jenkins")
return
}
jsonError := jenkins.parseResponse(resp, body)
if jsonError != nil {
log.Println(err)
} else {
log.Print("trace - loadXSRFtoken - convert into jenkinsCrumb: ", body)
}
return jsonError
}
func (jenkins *Jenkins) post(path string, queryParams url.Values, body interface{}, jenkinsCrumb *Crumb, postData io.Reader) (err error) {
requestURL := jenkins.buildURL(path, queryParams)
req, err := http.NewRequest("POST", requestURL, postData)
if err != nil {
return
}
if postData != nil {
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
}
// if exists add the XSRF token as header to the POST request
if jenkinsCrumb != nil {
if len(jenkinsCrumb.Class) > 0 {
log.Print("trace - add an XSRF-Token-Header to a POST request")
req.Header.Set(jenkinsCrumb.CrumbRequestField, jenkinsCrumb.Crumb)
}
}
log.Printf("trace - send a POST request to %q \n", path)
resp, err := jenkins.sendRequest(req)
if err != nil {
return
}
//TODO check response code if ok
// POST return the Location to the new Job
// https://github.com/jenkinsci/parameterized-remote-trigger-plugin
// https://wiki.jenkins.io/display/JENKINS/Parameterized+Remote+Trigger+Plugin
locationHeader := resp.Header.Get("Location")
log.Println("HEADER Location:", locationHeader)
// it is a link to the queue, executable.url link to the job build
return jenkins.parseResponse(resp, body)
}
func (jenkins *Jenkins) parseJobPath(job string) string {
var path string
jobs := strings.Split(strings.TrimPrefix(job, "/"), "/")
for _, value := range jobs {
value = strings.Trim(value, " ")
if len(value) == 0 {
continue
}
path = fmt.Sprintf("%s/job/%s", path, value)
}
return path
}
func (jenkins *Jenkins) trigger(job string, queryParams url.Values) error {
path := jenkins.parseJobPath(job) + "/build"
log.Printf("info - trigger - set api job path to %q\n", path)
jenkinsCrumb := Crumb{}
// load XSRF token for the following POST request
jenkins.loadXSRFtoken(&jenkinsCrumb)
var postData io.Reader
if jenkins.Data != nil {
// convert the map into the jenkins json DTO and make url encoding too
var json string
for name, value := range jenkins.Data {
json += fmt.Sprintf("{\"name\":\"%s\",\"value\":\"%s\"}", name, value) + ","
}
json = strings.TrimRight(json, ",")
data := url.Values{}
data.Set("json", fmt.Sprintf("{\"parameter\": [%s]}", json))
postData = strings.NewReader(data.Encode())
} else {
postData = nil
}
return jenkins.post(path, queryParams, nil, &jenkinsCrumb, postData)
}