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

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"log"
	"net/http"
LinuxSuRen's avatar
LinuxSuRen 已提交
9
	"net/url"
LinuxSuRen's avatar
LinuxSuRen 已提交
10 11
	"strconv"
	"strings"
12 13

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

16
// JobClient is client for operate jobs
LinuxSuRen's avatar
LinuxSuRen 已提交
17 18 19 20 21 22 23
type JobClient struct {
	JenkinsCore
}

// Search find a set of jobs by name
func (q *JobClient) Search(keyword string) (status *SearchResult, err error) {
	var (
24 25
		statusCode int
		data       []byte
LinuxSuRen's avatar
LinuxSuRen 已提交
26 27
	)

28 29 30
	if statusCode, data, err = q.Request("GET", fmt.Sprintf("/search/suggest?query=%s", keyword), nil, nil); err == nil {
		if statusCode == 200 {
			json.Unmarshal(data, &status)
LinuxSuRen's avatar
LinuxSuRen 已提交
31
		} else {
32
			err = fmt.Errorf("unexpected status code: %d", statusCode)
LinuxSuRen's avatar
LinuxSuRen 已提交
33 34 35 36
		}
	}
	return
}
37 38

// Build trigger a job
LinuxSuRen's avatar
LinuxSuRen 已提交
39
func (q *JobClient) Build(jobName string) (err error) {
40
	path := parseJobPath(jobName)
41

LinuxSuRen's avatar
LinuxSuRen 已提交
42
	var (
43 44
		statusCode int
		data       []byte
LinuxSuRen's avatar
LinuxSuRen 已提交
45 46
	)

47 48
	if statusCode, data, err = q.Request("POST", fmt.Sprintf("%s/build", path), nil, nil); err == nil {
		if statusCode == 201 {
LinuxSuRen's avatar
LinuxSuRen 已提交
49 50
			fmt.Println("build successfully")
		} else {
51 52 53 54
			err = fmt.Errorf("unexpected status code: %d", statusCode)
			if q.Debug {
				ioutil.WriteFile("debug.html", data, 0664)
			}
LinuxSuRen's avatar
LinuxSuRen 已提交
55 56 57 58 59
		}
	}
	return
}

60
// GetBuild get build information of a job
LinuxSuRen's avatar
LinuxSuRen 已提交
61
func (q *JobClient) GetBuild(jobName string, id int) (job *JobBuild, err error) {
62
	path := parseJobPath(jobName)
LinuxSuRen's avatar
LinuxSuRen 已提交
63 64
	var api string
	if id == -1 {
65
		api = fmt.Sprintf("%s/lastBuild/api/json", path)
LinuxSuRen's avatar
LinuxSuRen 已提交
66
	} else {
67
		api = fmt.Sprintf("%s/%d/api/json", path, id)
LinuxSuRen's avatar
LinuxSuRen 已提交
68
	}
69

LinuxSuRen's avatar
LinuxSuRen 已提交
70
	var (
71 72
		statusCode int
		data       []byte
LinuxSuRen's avatar
LinuxSuRen 已提交
73 74
	)

75 76
	if statusCode, data, err = q.Request("GET", api, nil, nil); err == nil {
		if statusCode == 200 {
LinuxSuRen's avatar
LinuxSuRen 已提交
77 78 79
			job = &JobBuild{}
			err = json.Unmarshal(data, job)
		} else {
80 81 82 83
			err = fmt.Errorf("unexpected status code: %d", statusCode)
			if q.Debug {
				ioutil.WriteFile("debug.html", data, 0664)
			}
LinuxSuRen's avatar
LinuxSuRen 已提交
84 85 86 87 88
		}
	}
	return
}

89
// BuildWithParams build a job which has params
90
func (q *JobClient) BuildWithParams(jobName string, parameters []ParameterDefinition) (err error) {
91
	path := parseJobPath(jobName)
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
	api := fmt.Sprintf("%s/%s/build", q.URL, path)
	var (
		req      *http.Request
		response *http.Response
	)

	var paramJSON []byte

	if len(parameters) == 1 {
		paramJSON, err = json.Marshal(parameters[0])
	} else {
		paramJSON, err = json.Marshal(parameters)
	}

	formData := url.Values{"json": {fmt.Sprintf("{\"parameter\": %s}", string(paramJSON))}}
	payload := strings.NewReader(formData.Encode())
	req, err = http.NewRequest("POST", api, payload)
	if err == nil {
		q.AuthHandle(req)
	} else {
		return
	}

	if err = q.CrumbHandle(req); err != nil {
		log.Fatal(err)
	}
118
	req.Header.Add(util.CONTENT_TYPE, util.APP_FORM)
119 120 121 122 123 124 125 126
	client := q.GetClient()
	if response, err = client.Do(req); err == nil {
		code := response.StatusCode
		var data []byte
		data, err = ioutil.ReadAll(response.Body)
		if code == 201 { // Jenkins will send redirect by this api
			fmt.Println("build successfully")
		} else {
127 128 129 130
			fmt.Println("Status code", code)
			if q.Debug {
				ioutil.WriteFile("debug.html", data, 0664)
			}
131 132 133 134 135 136 137
		}
	} else {
		log.Fatal(err)
	}
	return
}

138
// StopJob stops a job build
LinuxSuRen's avatar
LinuxSuRen 已提交
139
func (q *JobClient) StopJob(jobName string, num int) (err error) {
140
	path := parseJobPath(jobName)
141
	api := fmt.Sprintf("%s/%d/stop", path, num)
LinuxSuRen's avatar
LinuxSuRen 已提交
142
	var (
143 144
		statusCode int
		data       []byte
LinuxSuRen's avatar
LinuxSuRen 已提交
145 146
	)

147 148
	if statusCode, data, err = q.Request("POST", api, nil, nil); err == nil {
		if statusCode == 200 {
LinuxSuRen's avatar
LinuxSuRen 已提交
149 150
			fmt.Println("stoped successfully")
		} else {
151
			err = fmt.Errorf("unexpected status code: %d", statusCode)
LinuxSuRen's avatar
LinuxSuRen 已提交
152 153 154 155 156 157 158 159
			if q.Debug {
				ioutil.WriteFile("debug.html", data, 0664)
			}
		}
	}
	return
}

160
// GetJob returns the job info
LinuxSuRen's avatar
LinuxSuRen 已提交
161
func (q *JobClient) GetJob(name string) (job *Job, err error) {
162
	path := parseJobPath(name)
163
	api := fmt.Sprintf("%s/api/json", path)
LinuxSuRen's avatar
LinuxSuRen 已提交
164
	var (
165 166
		statusCode int
		data       []byte
LinuxSuRen's avatar
LinuxSuRen 已提交
167 168
	)

169 170
	if statusCode, data, err = q.Request("GET", api, nil, nil); err == nil {
		if statusCode == 200 {
LinuxSuRen's avatar
LinuxSuRen 已提交
171 172 173
			job = &Job{}
			err = json.Unmarshal(data, job)
		} else {
174 175 176 177
			err = fmt.Errorf("unexpected status code: %d", statusCode)
			if q.Debug {
				ioutil.WriteFile("debug.html", data, 0664)
			}
LinuxSuRen's avatar
LinuxSuRen 已提交
178 179 180 181 182
		}
	}
	return
}

183
// GetJobTypeCategories returns all categories of jobs
184 185
func (q *JobClient) GetJobTypeCategories() (jobCategories []JobCategory, err error) {
	var (
186 187
		statusCode int
		data       []byte
188 189
	)

190 191
	if statusCode, data, err = q.Request("GET", "/view/all/itemCategories?depth=3", nil, nil); err == nil {
		if statusCode == 200 {
192 193 194 195 196 197 198
			type innerJobCategories struct {
				Categories []JobCategory
			}
			result := &innerJobCategories{}
			err = json.Unmarshal(data, result)
			jobCategories = result.Categories
		} else {
199 200 201 202
			err = fmt.Errorf("unexpected status code: %d", statusCode)
			if q.Debug {
				ioutil.WriteFile("debug.html", data, 0664)
			}
203 204 205 206 207
		}
	}
	return
}

LinuxSuRen's avatar
LinuxSuRen 已提交
208
func (q *JobClient) UpdatePipeline(name, script string) (err error) {
209
	path := parseJobPath(name)
LinuxSuRen's avatar
LinuxSuRen 已提交
210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227
	api := fmt.Sprintf("%s/%s/wfapisu/update", q.URL, path)
	var (
		req      *http.Request
		response *http.Response
	)

	formData := url.Values{"script": {script}}
	payload := strings.NewReader(formData.Encode())
	req, err = http.NewRequest("POST", api, payload)
	if err == nil {
		q.AuthHandle(req)
	} else {
		return
	}

	if err = q.CrumbHandle(req); err != nil {
		log.Fatal(err)
	}
228
	req.Header.Add(util.CONTENT_TYPE, util.APP_FORM)
LinuxSuRen's avatar
LinuxSuRen 已提交
229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247
	client := q.GetClient()
	if response, err = client.Do(req); err == nil {
		code := response.StatusCode
		var data []byte
		data, err = ioutil.ReadAll(response.Body)
		if code == 200 {
			fmt.Println("updated")
		} else {
			fmt.Println("code", code)
			log.Fatal(string(data))
		}
	} else {
		fmt.Println("request is error")
		log.Fatal(err)
	}
	return
}

func (q *JobClient) GetPipeline(name string) (pipeline *Pipeline, err error) {
248
	path := parseJobPath(name)
LinuxSuRen's avatar
LinuxSuRen 已提交
249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278
	api := fmt.Sprintf("%s/%s/wfapisu/script", q.URL, path)
	var (
		req      *http.Request
		response *http.Response
	)

	req, err = http.NewRequest("GET", api, nil)
	if err == nil {
		q.AuthHandle(req)
	} else {
		return
	}

	client := q.GetClient()
	if response, err = client.Do(req); err == nil {
		code := response.StatusCode
		var data []byte
		data, err = ioutil.ReadAll(response.Body)
		if code == 200 {
			pipeline = &Pipeline{}
			err = json.Unmarshal(data, pipeline)
		} else {
			log.Fatal(string(data))
		}
	} else {
		log.Fatal(err)
	}
	return
}

279
// GetHistory returns the build history of a job
LinuxSuRen's avatar
LinuxSuRen 已提交
280 281 282 283
func (q *JobClient) GetHistory(name string) (builds []JobBuild, err error) {
	var job *Job
	if job, err = q.GetJob(name); err == nil {
		builds = job.Builds
284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312

		for i, build := range builds {
			api := fmt.Sprintf("%sapi/json", build.URL)
			var (
				req      *http.Request
				response *http.Response
			)

			req, err = http.NewRequest("GET", api, nil)
			if err == nil {
				q.AuthHandle(req)
			} else {
				return
			}
			client := q.GetClient()
			if response, err = client.Do(req); err == nil {
				code := response.StatusCode
				var data []byte
				data, err = ioutil.ReadAll(response.Body)
				if code == 200 {
					err = json.Unmarshal(data, &build)
					builds[i] = build
				} else {
					log.Fatal(string(data))
				}
			} else {
				log.Fatal(err)
			}
		}
LinuxSuRen's avatar
LinuxSuRen 已提交
313 314 315 316
	}
	return
}

LinuxSuRen's avatar
LinuxSuRen 已提交
317
// Log get the log of a job
LinuxSuRen's avatar
LinuxSuRen 已提交
318
func (q *JobClient) Log(jobName string, history int, start int64) (jobLog JobLog, err error) {
319
	path := parseJobPath(jobName)
LinuxSuRen's avatar
LinuxSuRen 已提交
320 321 322 323 324 325
	var api string
	if history == -1 {
		api = fmt.Sprintf("%s/%s/lastBuild/logText/progressiveText?start=%d", q.URL, path, start)
	} else {
		api = fmt.Sprintf("%s/%s/%d/logText/progressiveText?start=%d", q.URL, path, history, start)
	}
LinuxSuRen's avatar
LinuxSuRen 已提交
326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363
	var (
		req      *http.Request
		response *http.Response
	)

	req, err = http.NewRequest("GET", api, nil)
	if err == nil {
		q.AuthHandle(req)
	} else {
		return
	}

	client := q.GetClient()
	jobLog = JobLog{
		HasMore:   false,
		Text:      "",
		NextStart: int64(0),
	}
	if response, err = client.Do(req); err == nil {
		code := response.StatusCode
		var data []byte
		data, err = ioutil.ReadAll(response.Body)
		if code == 200 {
			jobLog.Text = string(data)

			if response.Header != nil {
				jobLog.HasMore = strings.ToLower(response.Header.Get("X-More-Data")) == "true"
				jobLog.NextStart, _ = strconv.ParseInt(response.Header.Get("X-Text-Size"), 10, 64)
			}
		} else {
			log.Fatal(string(data))
		}
	} else {
		log.Fatal(err)
	}
	return
}

364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397
func (q *JobClient) Create(jobName string, jobType string) (err error) {
	api := fmt.Sprintf("%s/view/all/createItem", q.URL)
	var (
		req      *http.Request
		response *http.Response
	)

	type playLoad struct {
		Name string `json:"name"`
		Mode string `json:"mode"`
		From string
	}

	playLoadObj := &playLoad{
		Name: jobName,
		Mode: jobType,
		From: "",
	}

	playLoadData, _ := json.Marshal(playLoadObj)

	formData := url.Values{
		"json": {string(playLoadData)},
		"name": {jobName},
		"mode": {jobType},
	}
	payload := strings.NewReader(formData.Encode())

	req, err = http.NewRequest("POST", api, payload)
	if err == nil {
		q.AuthHandle(req)
	} else {
		return
	}
398
	req.Header.Add(util.CONTENT_TYPE, util.APP_FORM)
399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416

	client := q.GetClient()
	if response, err = client.Do(req); err == nil {
		code := response.StatusCode
		var data []byte
		data, err = ioutil.ReadAll(response.Body)
		if code == 302 || code == 200 { // Jenkins will send redirect by this api
			fmt.Println("create successfully")
		} else {
			fmt.Printf("status code: %d\n", code)
			log.Fatal(string(data))
		}
	} else {
		log.Fatal(err)
	}
	return
}

417
// Delete will delete a job by name
418 419
func (q *JobClient) Delete(jobName string) (err error) {
	var (
420 421
		statusCode int
		data       []byte
422
	)
423 424 425 426

	api := fmt.Sprintf("/job/%s/doDelete", jobName)
	header := map[string]string{
		util.CONTENT_TYPE: util.APP_FORM,
427 428
	}

429 430
	if statusCode, data, err = q.Request("POST", api, header, nil); err == nil {
		if statusCode == 200 || statusCode == 302 {
431 432
			fmt.Println("delete successfully")
		} else {
433 434 435 436
			err = fmt.Errorf("unexpected status code: %d", statusCode)
			if q.Debug {
				ioutil.WriteFile("debug.html", data, 0664)
			}
437 438 439 440 441
		}
	}
	return
}

442
// parseJobPath leads with slash
443 444 445 446 447 448 449 450 451
func parseJobPath(jobName string) (path string) {
	jobItems := strings.Split(jobName, " ")
	path = ""
	for _, item := range jobItems {
		path = fmt.Sprintf("%s/job/%s", path, item)
	}
	return
}

452
// JobLog holds the log text
LinuxSuRen's avatar
LinuxSuRen 已提交
453 454 455 456 457 458
type JobLog struct {
	HasMore   bool
	NextStart int64
	Text      string
}

459
// SearchResult holds the result items
LinuxSuRen's avatar
LinuxSuRen 已提交
460 461 462 463 464 465 466
type SearchResult struct {
	Suggestions []SearchResultItem
}

type SearchResultItem struct {
	Name string
}
LinuxSuRen's avatar
LinuxSuRen 已提交
467 468

type Job struct {
469
	Type            string `json:"_class"`
LinuxSuRen's avatar
LinuxSuRen 已提交
470 471 472 473 474 475 476
	Builds          []JobBuild
	Color           string
	ConcurrentBuild bool
	Name            string
	NextBuildNumber int
	URL             string
	Buildable       bool
477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494

	Property []ParametersDefinitionProperty
}

type ParametersDefinitionProperty struct {
	ParameterDefinitions []ParameterDefinition
}

type ParameterDefinition struct {
	Description           string
	Name                  string `json:"name"`
	Type                  string
	Value                 string `json:"value"`
	DefaultParameterValue DefaultParameterValue
}

type DefaultParameterValue struct {
	Description string
495
	Value       interface{}
LinuxSuRen's avatar
LinuxSuRen 已提交
496 497
}

LinuxSuRen's avatar
LinuxSuRen 已提交
498
type SimpleJobBuild struct {
LinuxSuRen's avatar
LinuxSuRen 已提交
499 500 501
	Number int
	URL    string
}
LinuxSuRen's avatar
LinuxSuRen 已提交
502

LinuxSuRen's avatar
LinuxSuRen 已提交
503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519
type JobBuild struct {
	SimpleJobBuild
	Building          bool
	Description       string
	DisplayName       string
	Duration          int64
	EstimatedDuration int64
	FullDisplayName   string
	ID                string
	KeepLog           bool
	QueueID           int
	Result            string
	Timestamp         int64
	PreviousBuild     SimpleJobBuild
	NextBuild         SimpleJobBuild
}

LinuxSuRen's avatar
LinuxSuRen 已提交
520 521 522 523
type Pipeline struct {
	Script  string
	Sandbox bool
}
524 525 526 527 528 529 530 531 532 533 534 535 536 537

type JobCategory struct {
	Description string
	ID          string
	Items       []JobCategoryItem
	MinToShow   int
	Name        string
	Order       int
}

type JobCategoryItem struct {
	Description string
	DisplayName string
	Order       int
538
	Class       string
539
}