Files
kubesphere/pkg/simple/client/devops/jenkins/build.go
runzexia 1a6f563da1 Merge branch 'dev' into devops-refactor
# Conflicts:
#	cmd/controller-manager/app/controllers.go
#	hack/generate_client.sh
#	pkg/client/clientset/versioned/clientset.go
#	pkg/client/clientset/versioned/fake/clientset_generated.go
#	pkg/client/clientset/versioned/fake/register.go
#	pkg/client/clientset/versioned/scheme/register.go
#	pkg/client/informers/externalversions/generic.go
2020-04-01 11:04:09 +08:00

415 lines
11 KiB
Go

// Copyright 2015 Vadim Kravcenko
//
// Licensed under the Apache License, Version 2.0 (the "License"): you may
// not use this file except in compliance with the License. You may obtain
// a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
// License for the specific language governing permissions and limitations
// under the License.
package jenkins
import (
"bytes"
"errors"
"github.com/emicklei/go-restful"
"kubesphere.io/kubesphere/pkg/simple/client/devops"
"net/http"
"net/url"
"strconv"
"time"
)
const (
Git = "git"
Hg = "hg"
Svn = "svc"
)
type Build struct {
Raw *devops.Build
Job *Job
Jenkins *Jenkins
Base string
Depth int
}
type Parameter struct {
Name string
Value string
}
type Branch struct {
SHA1 string `json:",omitempty"`
Name string `json:",omitempty"`
}
type BuildRevision struct {
SHA1 string `json:"SHA1,omitempty"`
Branch []Branch `json:"Branch,omitempty"`
}
type Builds struct {
BuildNumber int64 `json:"buildNumber"`
BuildResult interface{} `json:"buildResult"`
Marked BuildRevision `json:"marked"`
Revision BuildRevision `json:"revision"`
}
type Culprit struct {
AbsoluteUrl string
FullName string
}
type GeneralObj struct {
Parameters []Parameter `json:"parameters,omitempty"`
Causes []map[string]interface{} `json:"causes,omitempty"`
BuildsByBranchName map[string]Builds `json:"buildsByBranchName,omitempty"`
LastBuiltRevision *BuildRevision `json:"lastBuiltRevision,omitempty"`
RemoteUrls []string `json:"remoteUrls,omitempty"`
ScmName string `json:"scmName,omitempty"`
MercurialNodeName string `json:"mercurialNodeName,omitempty"`
MercurialRevisionNumber string `json:"mercurialRevisionNumber,omitempty"`
Subdir interface{} `json:"subdir,omitempty"`
ClassName string `json:"_class,omitempty"`
SonarTaskId string `json:"ceTaskId,omitempty"`
SonarServerUrl string `json:"serverUrl,omitempty"`
SonarDashboardUrl string `json:"sonarqubeDashboardUrl,omitempty"`
TotalCount int64 `json:",omitempty"`
UrlName string `json:",omitempty"`
}
type TestResult struct {
Duration int64 `json:"duration"`
Empty bool `json:"empty"`
FailCount int64 `json:"failCount"`
PassCount int64 `json:"passCount"`
SkipCount int64 `json:"skipCount"`
Suites []struct {
Cases []struct {
Age int64 `json:"age"`
ClassName string `json:"className"`
Duration int64 `json:"duration"`
ErrorDetails interface{} `json:"errorDetails"`
ErrorStackTrace interface{} `json:"errorStackTrace"`
FailedSince int64 `json:"failedSince"`
Name string `json:"name"`
Skipped bool `json:"skipped"`
SkippedMessage interface{} `json:"skippedMessage"`
Status string `json:"status"`
Stderr interface{} `json:"stderr"`
Stdout interface{} `json:"stdout"`
} `json:"cases"`
Duration int64 `json:"duration"`
ID interface{} `json:"id"`
Name string `json:"name"`
Stderr interface{} `json:"stderr"`
Stdout interface{} `json:"stdout"`
Timestamp interface{} `json:"timestamp"`
} `json:"suites"`
}
type BuildResponse struct {
Actions []devops.GeneralAction
Artifacts []struct {
DisplayPath string `json:"displayPath"`
FileName string `json:"fileName"`
RelativePath string `json:"relativePath"`
} `json:"artifacts"`
Building bool `json:"building"`
BuiltOn string `json:"builtOn"`
ChangeSet struct {
Items []struct {
AffectedPaths []string `json:"affectedPaths"`
Author struct {
AbsoluteUrl string `json:"absoluteUrl"`
FullName string `json:"fullName"`
} `json:"author"`
Comment string `json:"comment"`
CommitID string `json:"commitId"`
Date string `json:"date"`
ID string `json:"id"`
Msg string `json:"msg"`
Paths []struct {
EditType string `json:"editType"`
File string `json:"file"`
} `json:"paths"`
Timestamp int64 `json:"timestamp"`
} `json:"items"`
Kind string `json:"kind"`
Revisions []struct {
Module string
Revision int
} `json:"revision"`
} `json:"changeSet"`
Culprits []devops.Culprit `json:"culprits"`
Description interface{} `json:"description"`
Duration int64 `json:"duration"`
EstimatedDuration int64 `json:"estimatedDuration"`
Executor interface{} `json:"executor"`
FullDisplayName string `json:"fullDisplayName"`
ID string `json:"id"`
KeepLog bool `json:"keepLog"`
Number int64 `json:"number"`
QueueID int64 `json:"queueId"`
Result string `json:"result"`
Timestamp int64 `json:"timestamp"`
URL string `json:"url"`
MavenArtifacts interface{} `json:"mavenArtifacts"`
MavenVersionUsed string `json:"mavenVersionUsed"`
Runs []struct {
Number int64
URL string
} `json:"runs"`
}
// Builds
func (b *Build) Info() *devops.Build {
return b.Raw
}
func (b *Build) GetUrl() string {
return b.Raw.URL
}
func (b *Build) GetBuildNumber() int64 {
return b.Raw.Number
}
func (b *Build) GetResult() string {
return b.Raw.Result
}
func (b *Build) Stop() (bool, error) {
if b.IsRunning() {
response, err := b.Jenkins.Requester.Post(b.Base+"/stop", nil, nil, nil)
if err != nil {
return false, err
}
if response.StatusCode != http.StatusOK {
return false, errors.New(strconv.Itoa(response.StatusCode))
}
}
return true, nil
}
func (b *Build) GetConsoleOutput() string {
url := b.Base + "/consoleText"
var content string
b.Jenkins.Requester.GetXML(url, &content, nil)
return content
}
func (b *Build) GetCauses() ([]map[string]interface{}, error) {
_, err := b.Poll()
if err != nil {
return nil, err
}
for _, a := range b.Raw.Actions {
if a.Causes != nil {
return a.Causes, nil
}
}
return nil, errors.New("No Causes")
}
func (b *Build) GetInjectedEnvVars() (map[string]string, error) {
var envVars struct {
EnvMap map[string]string `json:"envMap"`
}
endpoint := b.Base + "/injectedEnvVars"
_, err := b.Jenkins.Requester.GetJSON(endpoint, &envVars, nil)
if err != nil {
return envVars.EnvMap, err
}
return envVars.EnvMap, nil
}
func (b *Build) GetDownstreamBuilds() ([]*Build, error) {
result := make([]*Build, 0)
downstreamJobs, err := b.Job.GetDownstreamJobs()
if err != nil {
return nil, err
}
for _, job := range downstreamJobs {
allBuildIDs, err := job.GetAllBuildIds()
if err != nil {
return nil, err
}
for _, buildID := range allBuildIDs {
build, err := job.GetBuild(buildID.Number)
if err != nil {
return nil, err
}
upstreamBuild, _ := build.GetUpstreamBuild()
// cannot compare only id, it can be from different job
if b.GetUrl() == upstreamBuild.GetUrl() {
result = append(result, build)
break
}
}
}
return result, nil
}
func (b *Build) GetUpstreamJob() (*Job, error) {
causes, err := b.GetCauses()
if err != nil {
return nil, err
}
if len(causes) > 0 {
if job, ok := causes[0]["upstreamProject"]; ok {
return b.Jenkins.GetJob(job.(string))
}
}
return nil, errors.New("Unable to get Upstream Job")
}
func (b *Build) GetUpstreamBuildNumber() (int64, error) {
causes, err := b.GetCauses()
if err != nil {
return 0, err
}
if len(causes) > 0 {
if build, ok := causes[0]["upstreamBuild"]; ok {
switch t := build.(type) {
default:
return t.(int64), nil
case float64:
return int64(t), nil
}
}
}
return 0, nil
}
func (b *Build) GetUpstreamBuild() (*Build, error) {
job, err := b.GetUpstreamJob()
if err != nil {
return nil, err
}
if job != nil {
buildNumber, err := b.GetUpstreamBuildNumber()
if err == nil {
return job.GetBuild(buildNumber)
}
}
return nil, errors.New("Build not found")
}
func (b *Build) GetResultSet() (*TestResult, error) {
url := b.Base + "/testReport"
var report TestResult
_, err := b.Jenkins.Requester.GetJSON(url, &report, nil)
if err != nil {
return nil, err
}
return &report, nil
}
func (b *Build) GetTimestamp() time.Time {
msInt := int64(b.Raw.Timestamp)
return time.Unix(0, msInt*int64(time.Millisecond))
}
func (b *Build) GetDuration() int64 {
return b.Raw.Duration
}
func (b *Build) GetRevisionBranch() string {
vcs := b.Raw.ChangeSet.Kind
if vcs == Git {
for _, a := range b.Raw.Actions {
if len(a.LastBuiltRevision.Branch) > 0 && a.LastBuiltRevision.Branch[0].SHA1 != "" {
return a.LastBuiltRevision.Branch[0].SHA1
}
}
} else {
panic("Not implemented")
}
return ""
}
func (b *Build) IsGood() bool {
return !b.IsRunning() && b.Raw.Result == STATUS_SUCCESS
}
func (b *Build) IsRunning() bool {
_, err := b.Poll()
if err != nil {
return false
}
return b.Raw.Building
}
func (b *Build) SetDescription(description string) error {
data := url.Values{}
data.Set("description", description)
_, err := b.Jenkins.Requester.Post(b.Base+"/submitDescription", bytes.NewBufferString(data.Encode()), nil, nil)
return err
}
func (b *Build) PauseToggle() error {
response, err := b.Jenkins.Requester.Post(b.Base+"/pause/toggle", nil, nil, nil)
if err != nil {
return err
}
if response.StatusCode != http.StatusOK {
return errors.New(strconv.Itoa(response.StatusCode))
}
return nil
}
// Poll for current data. Optional Parameter - depth.
// More about depth here: https://wiki.jenkins-ci.org/display/JENKINS/Remote+access+API
func (b *Build) Poll(options ...interface{}) (int, error) {
depth := "-1"
for _, o := range options {
switch v := o.(type) {
case string:
depth = v
case int:
depth = strconv.Itoa(v)
case int64:
depth = strconv.FormatInt(v, 10)
}
}
if depth == "-1" {
depth = strconv.Itoa(b.Depth)
}
qr := map[string]string{
"depth": depth,
}
response, err := b.Jenkins.Requester.GetJSON(b.Base, b.Raw, qr)
if err != nil {
return 0, err
}
return response.StatusCode, nil
}
func (j *Jenkins) GetProjectPipelineBuildByType(projectId, pipelineId string, status string) (*devops.Build, error) {
job, err := j.GetJob(pipelineId, projectId)
if err != nil {
return nil, restful.NewError(devops.GetDevOpsStatusCode(err), err.Error())
}
build, err := job.getBuildByType(status)
return build.Raw, nil
}
func (j *Jenkins) GetMultiBranchPipelineBuildByType(projectId, pipelineId, branch string, status string) (*devops.Build, error) {
job, err := j.GetJob(pipelineId, projectId, branch)
if err != nil {
return nil, restful.NewError(devops.GetDevOpsStatusCode(err), err.Error())
}
build, err := job.getBuildByType(status)
return build.Raw, nil
}