devops refactor (#1739)

* add devops client interface

Signed-off-by: runzexia <runzexia@yunify.com>

* direct return jenkins

Signed-off-by: runzexia <runzexia@yunify.com>

* add some interface

Signed-off-by: runzexia <runzexia@yunify.com>

* update

Signed-off-by: runzexia <runzexia@yunify.com>

* update interface

Signed-off-by: runzexia <runzexia@yunify.com>

* update

Signed-off-by: runzexia <runzexia@yunify.com>

* credential op structs

Signed-off-by: runzexia <runzexia@yunify.com>

* status

Signed-off-by: zhuxiaoyang <sunzhu@yunify.com>

* update interface

Signed-off-by: zhuxiaoyang <sunzhu@yunify.com>

* credential handler

Signed-off-by: runzexia <runzexia@yunify.com>

* update devopsoperator func

Signed-off-by: zhuxiaoyang <sunzhu@yunify.com>

* get build sonar

Signed-off-by: runzexia <runzexia@yunify.com>

* sonar handler

* mv code to cilent

Signed-off-by: runzexia <runzexia@yunify.com>

* update

Signed-off-by: runzexia <runzexia@yunify.com>

* project member handler

Signed-off-by: runzexia <runzexia@yunify.com>

* update pipeline operator interface

Signed-off-by: zhuxiaoyang <sunzhu@yunify.com>

* add tenant devops handler

Signed-off-by: runzexia <runzexia@yunify.com>

* update merge

Signed-off-by: runzexia <runzexia@yunify.com>

* clean

Signed-off-by: runzexia <runzexia@yunify.com>

* fmt

Signed-off-by: runzexia <runzexia@yunify.com>

* update ListPipelineRuns

Signed-off-by: zhuxiaoyang <sunzhu@yunify.com>

* complate pipelineOperator interface

Signed-off-by: zhuxiaoyang <sunzhu@yunify.com>

* update HttpParameters

Signed-off-by: zhuxiaoyang <sunzhu@yunify.com>

* add pipeline steps interface

Signed-off-by: zhuxiaoyang <sunzhu@yunify.com>

* update pipeline GetNodesDetail

Signed-off-by: zhuxiaoyang <sunzhu@yunify.com>

* add s2i api

Signed-off-by: runzexia <runzexia@yunify.com>

* add branch pipeline interface and update handler

Signed-off-by: zhuxiaoyang <sunzhu@yunify.com>

* add scan branch interface and update handler

Signed-off-by: zhuxiaoyang <sunzhu@yunify.com>

* add common interface and update handler

Signed-off-by: zhuxiaoyang <sunzhu@yunify.com>

* add SCM interface and update handler

Signed-off-by: zhuxiaoyang <sunzhu@yunify.com>

* add handler

Signed-off-by: runzexia <runzexia@yunify.com>

* add fake s3

Signed-off-by: runzexia <runzexia@yunify.com>

* add webhook&check interface and update handler

Signed-off-by: zhuxiaoyang <sunzhu@yunify.com>

* clean

Signed-off-by: zhuxiaoyang <sunzhu@yunify.com>

* clean

Signed-off-by: zhuxiaoyang <sunzhu@yunify.com>

* format

Signed-off-by: zhuxiaoyang <sunzhu@yunify.com>

* add some func

Signed-off-by: runzexia <runzexia@yunify.com>

* clean code

Signed-off-by: runzexia <runzexia@yunify.com>

* implement interface

Signed-off-by: zhuxiaoyang <sunzhu@yunify.com>

* fix interface GetBranchArtifacts

Signed-off-by: zhuxiaoyang <sunzhu@yunify.com>

* add s2ibinary upload test

Signed-off-by: runzexia <runzexia@yunify.com>

* tenant devops

Signed-off-by: runzexia <runzexia@yunify.com>

* update tenant

Signed-off-by: runzexia <runzexia@yunify.com>

* fake

Signed-off-by: zhuxiaoyang <sunzhu@yunify.com>

* add some unit test

Signed-off-by: zhuxiaoyang <sunzhu@yunify.com>

* add devops tenant handler

Signed-off-by: runzexia <runzexia@yunify.com>

* status

Signed-off-by: zhuxiaoyang <sunzhu@yunify.com>

* status

Signed-off-by: zhuxiaoyang <sunzhu@yunify.com>

* status

Signed-off-by: zhuxiaoyang <sunzhu@yunify.com>

* update fake test

Signed-off-by: zhuxiaoyang <sunzhu@yunify.com>

* update unit test and fake data

Signed-off-by: zhuxiaoyang <sunzhu@yunify.com>

* update

Co-authored-by: Xiaoyang Zhu <sunzhu@yunify.com>
This commit is contained in:
runzexia
2020-02-04 10:40:36 +08:00
committed by GitHub
parent 71849f028f
commit c5a340a2b4
101 changed files with 6923 additions and 6972 deletions

View File

@@ -20,4 +20,4 @@ type Interface interface {
// Expires updates object's expiration time, return err if key doesn't exist
Expire(key string, duration time.Duration) error
}
}

View File

@@ -3,38 +3,38 @@ package cache
import "time"
type simpleObject struct {
value string
expire time.Time
value string
expire time.Time
}
type SimpleCache struct {
store map[string]simpleObject
store map[string]simpleObject
}
func NewSimpleCache() Interface {
return &SimpleCache{store: make(map[string]simpleObject)}
return &SimpleCache{store: make(map[string]simpleObject)}
}
func (s *SimpleCache) Keys(pattern string) ([]string, error) {
panic("implement me")
panic("implement me")
}
func (s *SimpleCache) Set(key string, value string, duration time.Duration) error {
panic("implement me")
panic("implement me")
}
func (s *SimpleCache) Del(key string) error {
panic("implement me")
panic("implement me")
}
func (s *SimpleCache) Get(key string) (string, error) {
return "", nil
return "", nil
}
func (s *SimpleCache) Exists(key string) (bool, error) {
panic("implement me")
panic("implement me")
}
func (s *SimpleCache) Expire(key string, duration time.Duration) error {
panic("implement me")
panic("implement me")
}

View File

@@ -0,0 +1,10 @@
approvers:
- runzexia
- soulseen
reviewers:
- runzexia
- soulseen
labels:
- area/devops

View File

@@ -0,0 +1,115 @@
package devops
const (
LastBuild = "lastBuild"
LastCompletedBuild = "lastCompletedBuild"
LastFailedBuild = "lastFailedBuild"
LastStableBuild = "lastStableBuild"
LastSuccessfulBuild = "lastSuccessfulBuild"
LastUnstableBuild = "lastUnstableBuild"
LastUnsuccessfulBuild = "lastUnsuccessfulBuild"
FirstBuild = "firstBuild"
)
type GeneralParameter 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 GeneralAction struct {
Parameters []GeneralParameter `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"`
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 Build struct {
Actions []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 []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"`
Runs []struct {
Number int64
URL string
} `json:"runs"`
}
type BuildGetter interface {
// GetProjectPipelineBuildByType get the last build of the pipeline, status can specify the status of the last build.
GetProjectPipelineBuildByType(projectId, pipelineId string, status string) (*Build, error)
// GetMultiBranchPipelineBuildByType get the last build of the pipeline, status can specify the status of the last build.
GetMultiBranchPipelineBuildByType(projectId, pipelineId, branch string, status string) (*Build, error)
}

View File

@@ -0,0 +1,77 @@
package devops
import (
"time"
)
type Credential struct {
Id string `json:"id" description:"Id of Credential, e.g. dockerhub-id"`
Type string `json:"type" description:"Type of Credential, e.g. ssh/kubeconfig"`
DisplayName string `json:"display_name,omitempty" description:"Credential's display name"`
Fingerprint *struct {
FileName string `json:"file_name,omitempty" description:"Credential's display name and description"`
Hash string `json:"hash,omitempty" description:"Credential's hash"`
Usage []*struct {
Name string `json:"name,omitempty" description:"pipeline full name"`
Ranges struct {
Ranges []*struct {
Start int `json:"start,omitempty" description:"Start build number"`
End int `json:"end,omitempty" description:"End build number"`
} `json:"ranges,omitempty"`
} `json:"ranges,omitempty" description:"The build number of all pipelines that use this credential"`
} `json:"usage,omitempty" description:"all usage of Credential"`
} `json:"fingerprint,omitempty" description:"usage of the Credential"`
Description string `json:"description,omitempty" description:"Credential's description'"`
Domain string `json:"domain,omitempty" description:"Credential's domain,In ks we only use the default domain, default '_''"`
CreateTime *time.Time `json:"create_time,omitempty" description:"Credential's create_time'"`
Creator string `json:"creator,omitempty" description:"Creator's username"`
UsernamePasswordCredential *UsernamePasswordCredential `json:"username_password,omitempty" description:"username password Credential struct"`
SshCredential *SshCredential `json:"ssh,omitempty" description:"ssh Credential struct"`
SecretTextCredential *SecretTextCredential `json:"secret_text,omitempty" description:"secret_text Credential struct"`
KubeconfigCredential *KubeconfigCredential `json:"kubeconfig,omitempty" description:"kubeconfig Credential struct"`
}
type UsernamePasswordCredential struct {
Username string `json:"username,omitempty" description:"username of username_password credential"`
Password string `json:"password,omitempty" description:"password of username_password credential"`
}
type SshCredential struct {
Username string `json:"username,omitempty" description:"username of ssh credential"`
Passphrase string `json:"passphrase,omitempty" description:"passphrase of ssh credential, password of ssh credential"`
PrivateKey string `json:"private_key,omitempty" mapstructure:"private_key" description:"private key of ssh credential"`
}
type SecretTextCredential struct {
Secret string `json:"secret,omitempty" description:"secret content of credential"`
}
type KubeconfigCredential struct {
Content string `json:"content,omitempty" description:"content of kubeconfig"`
}
const (
CredentialTypeUsernamePassword = "username_password"
CredentialTypeSsh = "ssh"
CredentialTypeSecretText = "secret_text"
CredentialTypeKubeConfig = "kubeconfig"
)
var CredentialTypeMap = map[string]string{
"SSH Username with private key": CredentialTypeSsh,
"Username with password": CredentialTypeUsernamePassword,
"Secret text": CredentialTypeSecretText,
"Kubernetes configuration (kubeconfig)": CredentialTypeKubeConfig,
}
type CredentialOperator interface {
CreateCredentialInProject(projectId string, credential *Credential) (*string, error)
UpdateCredentialInProject(projectId string, credential *Credential) (*string, error)
GetCredentialInProject(projectId, id string, content bool) (*Credential, error)
GetCredentialsInProject(projectId string) ([]*Credential, error)
DeleteCredentialInProject(projectId, id string) (*string, error)
}

View File

@@ -0,0 +1,206 @@
package fake
import (
"kubesphere.io/kubesphere/pkg/simple/client/devops"
"net/http"
"strings"
)
type FakeDevops struct {
Data map[string]interface{}
}
func NewFakeDevops(data map[string]interface{}) *FakeDevops {
var fakeData FakeDevops
fakeData.Data = data
return &fakeData
}
// Pipelinne operator interface
func (d *FakeDevops) GetPipeline(projectName, pipelineName string, httpParameters *devops.HttpParameters) (*devops.Pipeline, error) {
return nil, nil
}
func (d *FakeDevops) ListPipelines(httpParameters *devops.HttpParameters) (*devops.PipelineList, error) {
return nil, nil
}
func (d *FakeDevops) GetPipelineRun(projectName, pipelineName, runId string, httpParameters *devops.HttpParameters) (*devops.PipelineRun, error) {
return nil, nil
}
func (d *FakeDevops) ListPipelineRuns(projectName, pipelineName string, httpParameters *devops.HttpParameters) (*devops.PipelineRunList, error) {
return nil, nil
}
func (d *FakeDevops) StopPipeline(projectName, pipelineName, runId string, httpParameters *devops.HttpParameters) (*devops.StopPipeline, error) {
return nil, nil
}
func (d *FakeDevops) ReplayPipeline(projectName, pipelineName, runId string, httpParameters *devops.HttpParameters) (*devops.ReplayPipeline, error) {
return nil, nil
}
func (d *FakeDevops) RunPipeline(projectName, pipelineName string, httpParameters *devops.HttpParameters) (*devops.RunPipeline, error) {
return nil, nil
}
func (d *FakeDevops) GetArtifacts(projectName, pipelineName, runId string, httpParameters *devops.HttpParameters) ([]devops.Artifacts, error) {
return nil, nil
}
func (d *FakeDevops) GetRunLog(projectName, pipelineName, runId string, httpParameters *devops.HttpParameters) ([]byte, error) {
return nil, nil
}
func (d *FakeDevops) GetStepLog(projectName, pipelineName, runId, nodeId, stepId string, httpParameters *devops.HttpParameters) ([]byte, http.Header, error) {
return nil, nil, nil
}
func (d *FakeDevops) GetNodeSteps(projectName, pipelineName, runId, nodeId string, httpParameters *devops.HttpParameters) ([]devops.NodeSteps, error) {
s := []string{projectName, pipelineName, runId, nodeId}
key := strings.Join(s, "-")
res := d.Data[key].([]devops.NodeSteps)
return res, nil
}
func (d *FakeDevops) GetPipelineRunNodes(projectName, pipelineName, runId string, httpParameters *devops.HttpParameters) ([]devops.PipelineRunNodes, error) {
s := []string{projectName, pipelineName, runId}
key := strings.Join(s, "-")
res := d.Data[key].([]devops.PipelineRunNodes)
return res, nil
}
func (d *FakeDevops) SubmitInputStep(projectName, pipelineName, runId, nodeId, stepId string, httpParameters *devops.HttpParameters) ([]byte, error) {
return nil, nil
}
//BranchPipelinne operator interface
func (d *FakeDevops) GetBranchPipeline(projectName, pipelineName, branchName string, httpParameters *devops.HttpParameters) (*devops.BranchPipeline, error) {
return nil, nil
}
func (d *FakeDevops) GetBranchPipelineRun(projectName, pipelineName, branchName, runId string, httpParameters *devops.HttpParameters) (*devops.PipelineRun, error) {
return nil, nil
}
func (d *FakeDevops) StopBranchPipeline(projectName, pipelineName, branchName, runId string, httpParameters *devops.HttpParameters) (*devops.StopPipeline, error) {
return nil, nil
}
func (d *FakeDevops) ReplayBranchPipeline(projectName, pipelineName, branchName, runId string, httpParameters *devops.HttpParameters) (*devops.ReplayPipeline, error) {
return nil, nil
}
func (d *FakeDevops) RunBranchPipeline(projectName, pipelineName, branchName string, httpParameters *devops.HttpParameters) (*devops.RunPipeline, error) {
return nil, nil
}
func (d *FakeDevops) GetBranchArtifacts(projectName, pipelineName, branchName, runId string, httpParameters *devops.HttpParameters) ([]devops.Artifacts, error) {
return nil, nil
}
func (d *FakeDevops) GetBranchRunLog(projectName, pipelineName, branchName, runId string, httpParameters *devops.HttpParameters) ([]byte, error) {
return nil, nil
}
func (d *FakeDevops) GetBranchStepLog(projectName, pipelineName, branchName, runId, nodeId, stepId string, httpParameters *devops.HttpParameters) ([]byte, http.Header, error) {
return nil, nil, nil
}
func (d *FakeDevops) GetBranchNodeSteps(projectName, pipelineName, branchName, runId, nodeId string, httpParameters *devops.HttpParameters) ([]devops.NodeSteps, error) {
s := []string{projectName, pipelineName, branchName, runId, nodeId}
key := strings.Join(s, "-")
res := d.Data[key].([]devops.NodeSteps)
return res, nil
}
func (d *FakeDevops) GetBranchPipelineRunNodes(projectName, pipelineName, branchName, runId string, httpParameters *devops.HttpParameters) ([]devops.BranchPipelineRunNodes, error) {
s := []string{projectName, pipelineName, branchName, runId}
key := strings.Join(s, "-")
res := d.Data[key].([]devops.BranchPipelineRunNodes)
return res, nil
}
func (d *FakeDevops) SubmitBranchInputStep(projectName, pipelineName, branchName, runId, nodeId, stepId string, httpParameters *devops.HttpParameters) ([]byte, error) {
return nil, nil
}
func (d *FakeDevops) GetPipelineBranch(projectName, pipelineName string, httpParameters *devops.HttpParameters) (*devops.PipelineBranch, error) {
return nil, nil
}
func (d *FakeDevops) ScanBranch(projectName, pipelineName string, httpParameters *devops.HttpParameters) ([]byte, error) {
return nil, nil
}
// Common pipeline operator interface
func (d *FakeDevops) GetConsoleLog(projectName, pipelineName string, httpParameters *devops.HttpParameters) ([]byte, error) {
return nil, nil
}
func (d *FakeDevops) GetCrumb(httpParameters *devops.HttpParameters) (*devops.Crumb, error) {
return nil, nil
}
// SCM operator interface
func (d *FakeDevops) GetSCMServers(scmId string, httpParameters *devops.HttpParameters) ([]devops.SCMServer, error) {
return nil, nil
}
func (d *FakeDevops) GetSCMOrg(scmId string, httpParameters *devops.HttpParameters) ([]devops.SCMOrg, error) {
return nil, nil
}
func (d *FakeDevops) GetOrgRepo(scmId, organizationId string, httpParameters *devops.HttpParameters) ([]devops.OrgRepo, error) {
return nil, nil
}
func (d *FakeDevops) CreateSCMServers(scmId string, httpParameters *devops.HttpParameters) (*devops.SCMServer, error) {
return nil, nil
}
func (d *FakeDevops) Validate(scmId string, httpParameters *devops.HttpParameters) (*devops.Validates, error) {
return nil, nil
}
//Webhook operator interface
func (d *FakeDevops) GetNotifyCommit(httpParameters *devops.HttpParameters) ([]byte, error) {
return nil, nil
}
func (d *FakeDevops) GithubWebhook(httpParameters *devops.HttpParameters) ([]byte, error) {
return nil, nil
}
func (d *FakeDevops) CheckScriptCompile(projectName, pipelineName string, httpParameters *devops.HttpParameters) (*devops.CheckScript, error) {
return nil, nil
}
func (d *FakeDevops) CheckCron(projectName string, httpParameters *devops.HttpParameters) (*devops.CheckCronRes, error) {
return nil, nil
}
func (d *FakeDevops) ToJenkinsfile(httpParameters *devops.HttpParameters) (*devops.ResJenkinsfile, error) {
return nil, nil
}
func (d *FakeDevops) ToJson(httpParameters *devops.HttpParameters) (*devops.ResJson, error) {
return nil, nil
}
// CredentialOperator
func (d *FakeDevops) CreateCredentialInProject(projectId string, credential *devops.Credential) (*string, error) {
return nil, nil
}
func (d *FakeDevops) UpdateCredentialInProject(projectId string, credential *devops.Credential) (*string, error) {
return nil, nil
}
func (d *FakeDevops) GetCredentialInProject(projectId, id string, content bool) (*devops.Credential, error) {
return nil, nil
}
func (d *FakeDevops) GetCredentialsInProject(projectId string) ([]*devops.Credential, error) {
return nil, nil
}
func (d *FakeDevops) DeleteCredentialInProject(projectId, id string) (*string, error) { return nil, nil }
// BuildGetter
func (d *FakeDevops) GetProjectPipelineBuildByType(projectId, pipelineId string, status string) (*devops.Build, error) {
return nil, nil
}
func (d *FakeDevops) GetMultiBranchPipelineBuildByType(projectId, pipelineId, branch string, status string) (*devops.Build, error) {
return nil, nil
}
// ProjectMemberOperator
func (d *FakeDevops) AddProjectMember(membership *devops.ProjectMembership) (*devops.ProjectMembership, error) {
return nil, nil
}
func (d *FakeDevops) UpdateProjectMember(oldMembership, newMembership *devops.ProjectMembership) (*devops.ProjectMembership, error) {
return nil, nil
}
func (d *FakeDevops) DeleteProjectMember(membership *devops.ProjectMembership) (*devops.ProjectMembership, error) {
return nil, nil
}
// ProjectPipelineOperator
func (d *FakeDevops) CreateProjectPipeline(projectId string, pipeline *devops.ProjectPipeline) (string, error) {
return "", nil
}
func (d *FakeDevops) DeleteProjectPipeline(projectId string, pipelineId string) (string, error) {
return "", nil
}
func (d *FakeDevops) UpdateProjectPipeline(projectId string, pipeline *devops.ProjectPipeline) (string, error) {
return "", nil
}
func (d *FakeDevops) GetProjectPipelineConfig(projectId, pipelineId string) (*devops.ProjectPipeline, error) {
return nil, nil
}

View File

@@ -1,19 +1,13 @@
package devops
type Job struct {
}
type Interface interface {
GetJob(projectId, pipelineName string) (*Job, error)
CredentialOperator
DeleteJob(projectId, pipelineId string) (bool, error)
BuildGetter
CreateJobInFolder()
PipelineOperator
GetGlobalRole(roleName string)
ProjectMemberOperator
AddGlobalRole(roleName string, permission string)
GetProjectRole(roleName string)
ProjectPipelineOperator
}

View File

@@ -0,0 +1,24 @@
# Jenkins API Client for Go
## About
Jenkins is the most popular Open Source Continuous Integration system. This Library will help you interact with Jenkins in a more developer-friendly way.
Fork From https://github.com/bndr/gojenkins
These are some of the features that are currently implemented:
* Get information on test-results of completed/failed build
* Ability to query Nodes, and manipulate them. Start, Stop, set Offline.
* Ability to query Jobs, and manipulate them.
* Get Plugins, Builds, Artifacts, Fingerprints
* Validate Fingerprints of Artifacts
* Get Current Queue, Cancel Tasks
* etc. For all methods go to GoDoc Reference.
Add some features:
* Credentials Management
* Pipeline Model Converter
* RBAC control

View File

@@ -0,0 +1,414 @@
// 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(GetJenkinsStatusCode(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(GetJenkinsStatusCode(err), err.Error())
}
build, err := job.getBuildByType(status)
return build.Raw, nil
}

View File

@@ -0,0 +1,20 @@
package jenkins
const (
STATUS_FAIL = "FAIL"
STATUS_ERROR = "ERROR"
STATUS_ABORTED = "ABORTED"
STATUS_REGRESSION = "REGRESSION"
STATUS_SUCCESS = "SUCCESS"
STATUS_FIXED = "FIXED"
STATUS_PASSED = "PASSED"
RESULT_STATUS_FAILURE = "FAILURE"
RESULT_STATUS_FAILED = "FAILED"
RESULT_STATUS_SKIPPED = "SKIPPED"
STR_RE_SPLIT_VIEW = "(.*)/view/([^/]*)/?"
)
const (
GLOBAL_ROLE = "globalRoles"
PROJECT_ROLE = "projectRoles"
)

View File

@@ -0,0 +1,341 @@
/*
Copyright 2018 The KubeSphere Authors.
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 (
"errors"
"fmt"
"github.com/PuerkitoBio/goquery"
"github.com/emicklei/go-restful"
"k8s.io/klog"
"kubesphere.io/kubesphere/pkg/simple/client/devops"
"net/http"
"strconv"
"strings"
)
const SSHCrenditalStaplerClass = "com.cloudbees.jenkins.plugins.sshcredentials.impl.BasicSSHUserPrivateKey"
const DirectSSHCrenditalStaplerClass = "com.cloudbees.jenkins.plugins.sshcredentials.impl.BasicSSHUserPrivateKey$DirectEntryPrivateKeySource"
const UsernamePassswordCredentialStaplerClass = "com.cloudbees.plugins.credentials.impl.UsernamePasswordCredentialsImpl"
const SecretTextCredentialStaplerClass = "org.jenkinsci.plugins.plaincredentials.impl.StringCredentialsImpl"
const KubeconfigCredentialStaplerClass = "com.microsoft.jenkins.kubernetes.credentials.KubeconfigCredentials"
const DirectKubeconfigCredentialStaperClass = "com.microsoft.jenkins.kubernetes.credentials.KubeconfigCredentials$DirectEntryKubeconfigSource"
const GLOBALScope = "GLOBAL"
type UsernamePasswordCredential struct {
Scope string `json:"scope"`
Id string `json:"id"`
Username string `json:"username"`
Password string `json:"password"`
Description string `json:"description"`
StaplerClass string `json:"stapler-class"`
}
type SshCredential struct {
Scope string `json:"scope"`
Id string `json:"id"`
Username string `json:"username"`
Passphrase string `json:"passphrase"`
KeySource PrivateKeySource `json:"privateKeySource"`
Description string `json:"description"`
StaplerClass string `json:"stapler-class"`
}
type SecretTextCredential struct {
Scope string `json:"scope"`
Id string `json:"id"`
Secret string `json:"secret"`
Description string `json:"description"`
StaplerClass string `json:"stapler-class"`
}
type KubeconfigCredential struct {
Scope string `json:"scope"`
Id string `json:"id"`
Description string `json:"description"`
KubeconfigSource KubeconfigSource `json:"kubeconfigSource"`
StaplerClass string `json:"stapler-class"`
}
type PrivateKeySource struct {
StaplerClass string `json:"stapler-class"`
PrivateKey string `json:"privateKey"`
}
type KubeconfigSource struct {
StaplerClass string `json:"stapler-class"`
Content string `json:"content"`
}
type CredentialResponse struct {
Id string `json:"id"`
TypeName string `json:"typeName"`
DisplayName string `json:"displayName"`
Fingerprint *struct {
FileName string `json:"file_name,omitempty" description:"Credential's display name and description"`
Hash string `json:"hash,omitempty" description:"Credential's hash"`
Usage []*struct {
Name string `json:"name,omitempty" description:"Jenkins pipeline full name"`
Ranges struct {
Ranges []*struct {
Start int `json:"start,omitempty" description:"Start build number"`
End int `json:"end,omitempty" description:"End build number"`
} `json:"ranges,omitempty"`
} `json:"ranges,omitempty" description:"The build number of all pipelines that use this credential"`
} `json:"usage,omitempty" description:"all usage of Credential"`
} `json:"fingerprint,omitempty" description:"usage of the Credential"`
Description string `json:"description,omitempty"`
Domain string `json:"domain"`
}
func NewSshCredential(id, username, passphrase, privateKey, description string) *SshCredential {
keySource := PrivateKeySource{
StaplerClass: DirectSSHCrenditalStaplerClass,
PrivateKey: privateKey,
}
return &SshCredential{
Scope: GLOBALScope,
Id: id,
Username: username,
Passphrase: passphrase,
KeySource: keySource,
Description: description,
StaplerClass: SSHCrenditalStaplerClass,
}
}
func NewUsernamePasswordCredential(id, username, password, description string) *UsernamePasswordCredential {
return &UsernamePasswordCredential{
Scope: GLOBALScope,
Id: id,
Username: username,
Password: password,
Description: description,
StaplerClass: UsernamePassswordCredentialStaplerClass,
}
}
func NewSecretTextCredential(id, secret, description string) *SecretTextCredential {
return &SecretTextCredential{
Scope: GLOBALScope,
Id: id,
Secret: secret,
Description: description,
StaplerClass: SecretTextCredentialStaplerClass,
}
}
func NewKubeconfigCredential(id, content, description string) *KubeconfigCredential {
credentialSource := KubeconfigSource{
StaplerClass: DirectKubeconfigCredentialStaperClass,
Content: content,
}
return &KubeconfigCredential{
Scope: GLOBALScope,
Id: id,
Description: description,
KubeconfigSource: credentialSource,
StaplerClass: KubeconfigCredentialStaplerClass,
}
}
func (j *Jenkins) GetCredentialInProject(projectId, id string, content bool) (*devops.Credential, error) {
responseStruct := &devops.Credential{}
domain := "_"
response, err := j.Requester.GetJSON(
fmt.Sprintf("/job/%s/credentials/store/folder/domain/_/credential/%s", projectId, id),
responseStruct, map[string]string{
"depth": "2",
})
if err != nil {
return nil, err
}
if response.StatusCode != http.StatusOK {
return nil, errors.New(strconv.Itoa(response.StatusCode))
}
responseStruct.Domain = domain
if content {
}
contentString := ""
response, err = j.Requester.GetHtml(
fmt.Sprintf("/job/%s/credentials/store/folder/domain/%s/credential/%s/update", projectId, domain, id),
&contentString, nil)
if err != nil {
return nil, err
}
if response.StatusCode != http.StatusOK {
return nil, errors.New(strconv.Itoa(response.StatusCode))
}
stringReader := strings.NewReader(contentString)
doc, err := goquery.NewDocumentFromReader(stringReader)
if err != nil {
klog.Errorf("%+v", err)
return nil, restful.NewError(http.StatusInternalServerError, err.Error())
}
switch responseStruct.Type {
case devops.CredentialTypeKubeConfig:
content := &devops.KubeconfigCredential{}
doc.Find("textarea[name*=content]").Each(func(i int, selection *goquery.Selection) {
value := selection.Text()
content.Content = value
})
responseStruct.KubeconfigCredential = content
case devops.CredentialTypeUsernamePassword:
content := &devops.UsernamePasswordCredential{}
doc.Find("input[name*=username]").Each(func(i int, selection *goquery.Selection) {
value, _ := selection.Attr("value")
content.Username = value
})
responseStruct.UsernamePasswordCredential = content
case devops.CredentialTypeSsh:
content := &devops.SshCredential{}
doc.Find("input[name*=username]").Each(func(i int, selection *goquery.Selection) {
value, _ := selection.Attr("value")
content.Username = value
})
doc.Find("textarea[name*=privateKey]").Each(func(i int, selection *goquery.Selection) {
value := selection.Text()
content.PrivateKey = value
})
responseStruct.SshCredential = content
}
return responseStruct, nil
}
func (j *Jenkins) GetCredentialsInProject(projectId string) ([]*devops.Credential, error) {
domain := "_"
var responseStruct = &struct {
Credentials []*devops.Credential `json:"credentials"`
}{}
response, err := j.Requester.GetJSON(
fmt.Sprintf("/job/%s/credentials/store/folder/domain/_", projectId),
responseStruct, map[string]string{
"depth": "2",
})
if err != nil {
return nil, err
}
if response.StatusCode != http.StatusOK {
return nil, errors.New(strconv.Itoa(response.StatusCode))
}
for _, credential := range responseStruct.Credentials {
credential.Domain = domain
}
return responseStruct.Credentials, nil
}
func (j *Jenkins) CreateCredentialInProject(projectId string, credential *devops.Credential) (*string, error) {
var request interface{}
responseString := ""
switch credential.Type {
case devops.CredentialTypeUsernamePassword:
request = NewUsernamePasswordCredential(credential.Id,
credential.UsernamePasswordCredential.Username, credential.UsernamePasswordCredential.Password,
credential.Description)
case devops.CredentialTypeSsh:
request = NewSshCredential(credential.Id,
credential.SshCredential.Username, credential.SshCredential.Passphrase,
credential.SshCredential.PrivateKey, credential.Description)
case devops.CredentialTypeSecretText:
request = NewSecretTextCredential(credential.Id,
credential.SecretTextCredential.Secret, credential.Description)
case devops.CredentialTypeKubeConfig:
request = NewKubeconfigCredential(credential.Id,
credential.KubeconfigCredential.Content, credential.Description)
default:
err := fmt.Errorf("error unsupport credential type")
klog.Errorf("%+v", err)
return nil, restful.NewError(http.StatusBadRequest, err.Error())
}
response, err := j.Requester.Post(
fmt.Sprintf("/job/%s/credentials/store/folder/domain/_/createCredentials", projectId),
nil, &responseString, map[string]string{
"json": makeJson(map[string]interface{}{
"credentials": request,
}),
})
if err != nil {
return nil, err
}
if response.StatusCode != http.StatusOK {
return nil, errors.New(strconv.Itoa(response.StatusCode))
}
return &credential.Id, nil
}
func (j *Jenkins) UpdateCredentialInProject(projectId string, credential *devops.Credential) (*string, error) {
requestContent := ""
switch credential.Type {
case devops.CredentialTypeUsernamePassword:
requestStruct := NewUsernamePasswordCredential(credential.Id,
credential.UsernamePasswordCredential.Username, credential.UsernamePasswordCredential.Password,
credential.Description)
requestContent = makeJson(requestStruct)
case devops.CredentialTypeSsh:
requestStruct := NewSshCredential(credential.Id,
credential.SshCredential.Username, credential.SshCredential.Passphrase,
credential.SshCredential.PrivateKey, credential.Description)
requestContent = makeJson(requestStruct)
case devops.CredentialTypeSecretText:
requestStruct := NewSecretTextCredential(credential.Id,
credential.SecretTextCredential.Secret, credential.Description)
requestContent = makeJson(requestStruct)
case devops.CredentialTypeKubeConfig:
requestStruct := NewKubeconfigCredential(credential.Id,
credential.KubeconfigCredential.Content, credential.Description)
requestContent = makeJson(requestStruct)
default:
err := fmt.Errorf("error unsupport credential type")
klog.Errorf("%+v", err)
return nil, restful.NewError(http.StatusBadRequest, err.Error())
}
response, err := j.Requester.Post(
fmt.Sprintf("/job/%s/credentials/store/folder/domain/_/credential/%s/updateSubmit", projectId, credential.Id),
nil, nil, map[string]string{
"json": requestContent,
})
if err != nil {
return nil, err
}
if response.StatusCode != http.StatusOK {
return nil, errors.New(strconv.Itoa(response.StatusCode))
}
return &credential.Id, nil
}
func (j *Jenkins) DeleteCredentialInProject(projectId, id string) (*string, error) {
response, err := j.Requester.Post(
fmt.Sprintf("/job/%s/credentials/store/folder/domain/_/credential/%s/doDelete", projectId, id),
nil, nil, nil)
if err != nil {
return nil, err
}
if response.StatusCode != http.StatusOK {
return nil, errors.New(strconv.Itoa(response.StatusCode))
}
return &id, nil
}

View File

@@ -11,12 +11,11 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
package devops
package jenkins
import (
"fmt"
"k8s.io/klog"
"kubesphere.io/kubesphere/pkg/gojenkins"
"sync"
)
@@ -25,13 +24,13 @@ const (
)
type Client struct {
jenkinsClient *gojenkins.Jenkins
jenkinsClient *Jenkins
}
func NewDevopsClient(options *Options) (*Client, error) {
var d Client
jenkins := gojenkins.CreateJenkins(nil, options.Host, options.MaxConnections, options.Username, options.Password)
jenkins := CreateJenkins(nil, options.Host, options.MaxConnections, options.Username, options.Password)
jenkins, err := jenkins.Init()
if err != nil {
klog.Errorf("failed to connecto to jenkins role, %+v", err)
@@ -49,7 +48,7 @@ func NewDevopsClient(options *Options) (*Client, error) {
return &d, nil
}
func (c *Client) Jenkins() *gojenkins.Jenkins {
func (c *Client) Jenkins() *Jenkins {
return c.jenkinsClient
}
@@ -71,14 +70,14 @@ func (c *Client) initializeJenkins() error {
// Jenkins uninitialized, create global role
if globalRole == nil {
_, err := c.jenkinsClient.AddGlobalRole(jenkinsAllUserRoleName, gojenkins.GlobalPermissionIds{GlobalRead: true}, true)
_, err := c.jenkinsClient.AddGlobalRole(jenkinsAllUserRoleName, GlobalPermissionIds{GlobalRead: true}, true)
if err != nil {
klog.Error(err)
return err
}
}
_, err = c.jenkinsClient.AddProjectRole(jenkinsAllUserRoleName, "\\n\\s*\\r", gojenkins.ProjectPermissionIds{SCMTag: true}, true)
_, err = c.jenkinsClient.AddProjectRole(jenkinsAllUserRoleName, "\\n\\s*\\r", ProjectPermissionIds{SCMTag: true}, true)
if err != nil {
klog.Error(err)
return err

View File

@@ -0,0 +1,40 @@
package jenkins
import (
"testing"
)
func Test_parseCronJobTime(t *testing.T) {
type Except struct {
Last string
Next string
}
Items := []struct {
Input string
Expected Except
}{
{"上次运行的时间 Tuesday, September 10, 2019 8:59:09 AM UTC; 下次运行的时间 Tuesday, September 10, 2019 9:14:09 AM UTC.", Except{Last: "2019-09-10T08:59:09Z", Next: "2019-09-10T09:14:09Z"}},
{"上次运行的时间 Thursday, January 3, 2019 11:56:30 PM UTC; 下次运行的时间 Friday, January 3, 2020 12:11:30 AM UTC.", Except{Last: "2019-01-03T23:56:30Z", Next: "2020-01-03T00:11:30Z"}},
{"上次运行的时间 Tuesday, September 10, 2019 8:41:34 AM UTC; 下次运行的时间 Tuesday, September 10, 2019 9:41:34 AM UTC.", Except{Last: "2019-09-10T08:41:34Z", Next: "2019-09-10T09:41:34Z"}},
{"上次运行的时间 Tuesday, September 10, 2019 9:15:26 AM UTC; 下次运行的时间 Tuesday, September 10, 2019 10:03:26 AM UTC.", Except{Last: "2019-09-10T09:15:26Z", Next: "2019-09-10T10:03:26Z"}},
{"Would last have run at Tuesday, September 10, 2019 9:15:26 AM UTC; would next run at Tuesday, September 10, 2019 10:03:26 AM UTC.", Except{Last: "2019-09-10T09:15:26Z", Next: "2019-09-10T10:03:26Z"}},
{"Would last have run at Tuesday, September 10, 2019 8:41:34 AM UTC; would next run at Tuesday, September 10, 2019 9:41:34 AM UTC.", Except{Last: "2019-09-10T08:41:34Z", Next: "2019-09-10T09:41:34Z"}},
}
for _, item := range Items {
last, next, err := parseCronJobTime(item.Input)
if err != nil {
t.Fatalf("should not get error %+v", err)
}
if last != item.Expected.Last {
t.Errorf("got %#v, expected %#v", last, item.Expected.Last)
}
if next != item.Expected.Next {
t.Errorf("got %#v, expected %#v", next, item.Expected.Next)
}
}
}

View File

@@ -0,0 +1,75 @@
// 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 (
"errors"
"strconv"
"strings"
)
type Folder struct {
Raw *FolderResponse
Jenkins *Jenkins
Base string
}
type FolderResponse struct {
Actions []GeneralObj
Description string `json:"description"`
DisplayName string `json:"displayName"`
Name string `json:"name"`
URL string `json:"url"`
Jobs []InnerJob `json:"jobs"`
}
func (f *Folder) parentBase() string {
return f.Base[:strings.LastIndex(f.Base, "/job")]
}
func (f *Folder) GetName() string {
return f.Raw.Name
}
func (f *Folder) Create(name, description string) (*Folder, error) {
mode := "com.cloudbees.hudson.plugins.folder.Folder"
data := map[string]string{
"name": name,
"mode": mode,
"Submit": "OK",
"json": makeJson(map[string]string{
"name": name,
"mode": mode,
"description": description,
}),
}
r, err := f.Jenkins.Requester.Post(f.parentBase()+"/createItem", nil, f.Raw, data)
if err != nil {
return nil, err
}
if r.StatusCode == 200 {
f.Poll()
return f, nil
}
return nil, errors.New(strconv.Itoa(r.StatusCode))
}
func (f *Folder) Poll() (int, error) {
response, err := f.Jenkins.Requester.GetJSON(f.Base, f.Raw, nil)
if err != nil {
return 0, err
}
return response.StatusCode, nil
}

View File

@@ -0,0 +1,803 @@
// 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.
// Gojenkins is a Jenkins Client in Go, that exposes the jenkins REST api in a more developer friendly way.
package jenkins
import (
"encoding/json"
"errors"
"fmt"
"io"
"io/ioutil"
"k8s.io/klog"
"kubesphere.io/kubesphere/pkg/simple/client/devops"
"log"
"net/http"
"os"
"reflect"
"strconv"
"strings"
)
// Basic Authentication
type BasicAuth struct {
Username string
Password string
}
type Jenkins struct {
Server string
Version string
Requester *Requester
}
// Loggers
var (
Info *log.Logger
Warning *log.Logger
Error *log.Logger
)
// Init Method. Should be called after creating a Jenkins Instance.
// e.g jenkins := CreateJenkins("url").Init()
// HTTP Client is set here, Connection to jenkins is tested here.
func (j *Jenkins) Init() (*Jenkins, error) {
j.initLoggers()
rsp, err := j.Requester.GetJSON("/", nil, nil)
if err != nil {
return nil, err
}
j.Version = rsp.Header.Get("X-Jenkins")
//if j.Raw == nil {
// return nil, errors.New("Connection Failed, Please verify that the host and credentials are correct.")
//}
return j, nil
}
func (j *Jenkins) initLoggers() {
Info = log.New(os.Stdout,
"INFO: ",
log.Ldate|log.Ltime|log.Lshortfile)
Warning = log.New(os.Stdout,
"WARNING: ",
log.Ldate|log.Ltime|log.Lshortfile)
Error = log.New(os.Stderr,
"ERROR: ",
log.Ldate|log.Ltime|log.Lshortfile)
}
// Create a new folder
// This folder can be nested in other parent folders
// Example: jenkins.CreateFolder("newFolder", "grandparentFolder", "parentFolder")
func (j *Jenkins) CreateFolder(name, description string, parents ...string) (*Folder, error) {
folderObj := &Folder{Jenkins: j, Raw: new(FolderResponse), Base: "/job/" + strings.Join(append(parents, name), "/job/")}
folder, err := folderObj.Create(name, description)
if err != nil {
return nil, err
}
return folder, nil
}
// Create a new job in the folder
// Example: jenkins.CreateJobInFolder("<config></config>", "newJobName", "myFolder", "parentFolder")
func (j *Jenkins) CreateJobInFolder(config string, jobName string, parentIDs ...string) (*Job, error) {
jobObj := Job{Jenkins: j, Raw: new(JobResponse), Base: "/job/" + strings.Join(append(parentIDs, jobName), "/job/")}
qr := map[string]string{
"name": jobName,
}
job, err := jobObj.Create(config, qr)
if err != nil {
return nil, err
}
return job, nil
}
// Create a new job from config File
// Method takes XML string as first Parameter, and if the name is not specified in the config file
// takes name as string as second Parameter
// e.g jenkins.CreateJob("<config></config>","newJobName")
func (j *Jenkins) CreateJob(config string, options ...interface{}) (*Job, error) {
qr := make(map[string]string)
if len(options) > 0 {
qr["name"] = options[0].(string)
} else {
return nil, errors.New("Error Creating Job, job name is missing")
}
jobObj := Job{Jenkins: j, Raw: new(JobResponse), Base: "/job/" + qr["name"]}
job, err := jobObj.Create(config, qr)
if err != nil {
return nil, err
}
return job, nil
}
// Rename a job.
// First Parameter job old name, Second Parameter job new name.
func (j *Jenkins) RenameJob(job string, name string) *Job {
jobObj := Job{Jenkins: j, Raw: new(JobResponse), Base: "/job/" + job}
jobObj.Rename(name)
return &jobObj
}
// Create a copy of a job.
// First Parameter Name of the job to copy from, Second Parameter new job name.
func (j *Jenkins) CopyJob(copyFrom string, newName string) (*Job, error) {
job := Job{Jenkins: j, Raw: new(JobResponse), Base: "/job/" + copyFrom}
_, err := job.Poll()
if err != nil {
return nil, err
}
return job.Copy(newName)
}
// Delete a job.
func (j *Jenkins) DeleteJob(name string, parentIDs ...string) (bool, error) {
job := Job{Jenkins: j, Raw: new(JobResponse), Base: "/job/" + strings.Join(append(parentIDs, name), "/job/")}
return job.Delete()
}
// Invoke a job.
// First Parameter job name, second Parameter is optional Build parameters.
func (j *Jenkins) BuildJob(name string, options ...interface{}) (int64, error) {
job := Job{Jenkins: j, Raw: new(JobResponse), Base: "/job/" + name}
var params map[string]string
if len(options) > 0 {
params, _ = options[0].(map[string]string)
}
return job.InvokeSimple(params)
}
func (j *Jenkins) GetBuild(jobName string, number int64) (*Build, error) {
job, err := j.GetJob(jobName)
if err != nil {
return nil, err
}
build, err := job.GetBuild(number)
if err != nil {
return nil, err
}
return build, nil
}
func (j *Jenkins) GetJob(id string, parentIDs ...string) (*Job, error) {
job := Job{Jenkins: j, Raw: new(JobResponse), Base: "/job/" + strings.Join(append(parentIDs, id), "/job/")}
status, err := job.Poll()
if err != nil {
return nil, err
}
if status == 200 {
return &job, nil
}
return nil, errors.New(strconv.Itoa(status))
}
func (j *Jenkins) GetFolder(id string, parents ...string) (*Folder, error) {
folder := Folder{Jenkins: j, Raw: new(FolderResponse), Base: "/job/" + strings.Join(append(parents, id), "/job/")}
status, err := folder.Poll()
if err != nil {
return nil, fmt.Errorf("trouble polling folder: %v", err)
}
if status == 200 {
return &folder, nil
}
return nil, errors.New(strconv.Itoa(status))
}
// Get all builds Numbers and URLS for a specific job.
// There are only build IDs here,
// To get all the other info of the build use jenkins.GetBuild(job,buildNumber)
// or job.GetBuild(buildNumber)
func (j *Jenkins) Poll() (int, error) {
resp, err := j.Requester.GetJSON("/", nil, nil)
if err != nil {
return 0, err
}
return resp.StatusCode, nil
}
func (j *Jenkins) GetGlobalRole(roleName string) (*GlobalRole, error) {
roleResponse := &GlobalRoleResponse{
RoleName: roleName,
}
stringResponse := ""
response, err := j.Requester.Get("/role-strategy/strategy/getRole",
&stringResponse,
map[string]string{
"roleName": roleName,
"type": GLOBAL_ROLE,
})
if err != nil {
return nil, err
}
if response.StatusCode != http.StatusOK {
return nil, errors.New(strconv.Itoa(response.StatusCode))
}
if stringResponse == "{}" {
return nil, nil
}
err = json.Unmarshal([]byte(stringResponse), roleResponse)
if err != nil {
return nil, err
}
return &GlobalRole{
Jenkins: j,
Raw: *roleResponse,
}, nil
}
func (j *Jenkins) GetProjectRole(roleName string) (*ProjectRole, error) {
roleResponse := &ProjectRoleResponse{
RoleName: roleName,
}
stringResponse := ""
response, err := j.Requester.Get("/role-strategy/strategy/getRole",
&stringResponse,
map[string]string{
"roleName": roleName,
"type": PROJECT_ROLE,
})
if err != nil {
return nil, err
}
if response.StatusCode != http.StatusOK {
return nil, errors.New(strconv.Itoa(response.StatusCode))
}
if stringResponse == "{}" {
return nil, nil
}
err = json.Unmarshal([]byte(stringResponse), roleResponse)
if err != nil {
return nil, err
}
return &ProjectRole{
Jenkins: j,
Raw: *roleResponse,
}, nil
}
func (j *Jenkins) AddGlobalRole(roleName string, ids GlobalPermissionIds, overwrite bool) (*GlobalRole, error) {
responseRole := &GlobalRole{
Jenkins: j,
Raw: GlobalRoleResponse{
RoleName: roleName,
PermissionIds: ids,
}}
var idArray []string
values := reflect.ValueOf(ids)
for i := 0; i < values.NumField(); i++ {
field := values.Field(i)
if field.Bool() {
idArray = append(idArray, values.Type().Field(i).Tag.Get("json"))
}
}
param := map[string]string{
"roleName": roleName,
"type": GLOBAL_ROLE,
"permissionIds": strings.Join(idArray, ","),
"overwrite": strconv.FormatBool(overwrite),
}
responseString := ""
response, err := j.Requester.Post("/role-strategy/strategy/addRole", nil, &responseString, param)
if err != nil {
return nil, err
}
if response.StatusCode != http.StatusOK {
return nil, errors.New(strconv.Itoa(response.StatusCode))
}
return responseRole, nil
}
func (j *Jenkins) DeleteProjectRoles(roleName ...string) error {
responseString := ""
response, err := j.Requester.Post("/role-strategy/strategy/removeRoles", nil, &responseString, map[string]string{
"type": PROJECT_ROLE,
"roleNames": strings.Join(roleName, ","),
})
if err != nil {
return err
}
if response.StatusCode != http.StatusOK {
fmt.Println(responseString)
return errors.New(strconv.Itoa(response.StatusCode))
}
return nil
}
func (j *Jenkins) AddProjectRole(roleName string, pattern string, ids ProjectPermissionIds, overwrite bool) (*ProjectRole, error) {
responseRole := &ProjectRole{
Jenkins: j,
Raw: ProjectRoleResponse{
RoleName: roleName,
PermissionIds: ids,
Pattern: pattern,
}}
var idArray []string
values := reflect.ValueOf(ids)
for i := 0; i < values.NumField(); i++ {
field := values.Field(i)
if field.Bool() {
idArray = append(idArray, values.Type().Field(i).Tag.Get("json"))
}
}
param := map[string]string{
"roleName": roleName,
"type": PROJECT_ROLE,
"permissionIds": strings.Join(idArray, ","),
"overwrite": strconv.FormatBool(overwrite),
"pattern": pattern,
}
responseString := ""
response, err := j.Requester.Post("/role-strategy/strategy/addRole", nil, &responseString, param)
if err != nil {
return nil, err
}
if response.StatusCode != http.StatusOK {
return nil, errors.New(strconv.Itoa(response.StatusCode))
}
return responseRole, nil
}
func (j *Jenkins) DeleteUserInProject(username string) error {
param := map[string]string{
"type": PROJECT_ROLE,
"sid": username,
}
responseString := ""
response, err := j.Requester.Post("/role-strategy/strategy/deleteSid", nil, &responseString, param)
if err != nil {
return err
}
if response.StatusCode != http.StatusOK {
return errors.New(strconv.Itoa(response.StatusCode))
}
return nil
}
func (j *Jenkins) GetPipeline(projectName, pipelineName string, httpParameters *devops.HttpParameters) (*devops.Pipeline, error) {
PipelineOjb := &Pipeline{
HttpParameters: httpParameters,
Jenkins: j,
Path: fmt.Sprintf(GetPipelineUrl, projectName, pipelineName),
}
res, err := PipelineOjb.GetPipeline()
return res, err
}
func (j *Jenkins) ListPipelines(httpParameters *devops.HttpParameters) (*devops.PipelineList, error) {
PipelineOjb := &Pipeline{
HttpParameters: httpParameters,
Jenkins: j,
Path: ListPipelinesUrl + httpParameters.Url.RawQuery,
}
res, err := PipelineOjb.ListPipelines()
return res, err
}
func (j *Jenkins) GetPipelineRun(projectName, pipelineName, runId string, httpParameters *devops.HttpParameters) (*devops.PipelineRun, error) {
PipelineOjb := &Pipeline{
HttpParameters: httpParameters,
Jenkins: j,
Path: fmt.Sprintf(GetPipelineRunUrl, projectName, pipelineName, runId),
}
res, err := PipelineOjb.GetPipelineRun()
return res, err
}
func (j *Jenkins) ListPipelineRuns(projectName, pipelineName string, httpParameters *devops.HttpParameters) (*devops.PipelineRunList, error) {
PipelineOjb := &Pipeline{
HttpParameters: httpParameters,
Jenkins: j,
Path: ListPipelineRunUrl + httpParameters.Url.RawQuery,
}
res, err := PipelineOjb.ListPipelineRuns()
return res, err
}
func (j *Jenkins) StopPipeline(projectName, pipelineName, runId string, httpParameters *devops.HttpParameters) (*devops.StopPipeline, error) {
PipelineOjb := &Pipeline{
HttpParameters: httpParameters,
Jenkins: j,
Path: fmt.Sprintf(StopPipelineUrl, projectName, pipelineName, runId),
}
res, err := PipelineOjb.StopPipeline()
return res, err
}
func (j *Jenkins) ReplayPipeline(projectName, pipelineName, runId string, httpParameters *devops.HttpParameters) (*devops.ReplayPipeline, error) {
PipelineOjb := &Pipeline{
HttpParameters: httpParameters,
Jenkins: j,
Path: fmt.Sprintf(ReplayPipelineUrl+httpParameters.Url.RawQuery, projectName, pipelineName, runId),
}
res, err := PipelineOjb.ReplayPipeline()
return res, err
}
func (j *Jenkins) RunPipeline(projectName, pipelineName string, httpParameters *devops.HttpParameters) (*devops.RunPipeline, error) {
PipelineOjb := &Pipeline{
HttpParameters: httpParameters,
Jenkins: j,
Path: fmt.Sprintf(RunPipelineUrl+httpParameters.Url.RawQuery, projectName, pipelineName),
}
res, err := PipelineOjb.RunPipeline()
return res, err
}
func (j *Jenkins) GetArtifacts(projectName, pipelineName, runId string, httpParameters *devops.HttpParameters) ([]devops.Artifacts, error) {
PipelineOjb := &Pipeline{
HttpParameters: httpParameters,
Jenkins: j,
Path: fmt.Sprintf(GetArtifactsUrl+httpParameters.Url.RawQuery, projectName, pipelineName, runId),
}
res, err := PipelineOjb.GetArtifacts()
return res, err
}
func (j *Jenkins) GetRunLog(projectName, pipelineName, runId string, httpParameters *devops.HttpParameters) ([]byte, error) {
PipelineOjb := &Pipeline{
HttpParameters: httpParameters,
Jenkins: j,
Path: fmt.Sprintf(GetRunLogUrl+httpParameters.Url.RawQuery, projectName, pipelineName, runId),
}
res, err := PipelineOjb.GetRunLog()
return res, err
}
func (j *Jenkins) GetStepLog(projectName, pipelineName, runId, nodeId, stepId string, httpParameters *devops.HttpParameters) ([]byte, http.Header, error) {
PipelineOjb := &Pipeline{
HttpParameters: httpParameters,
Jenkins: j,
Path: fmt.Sprintf(GetStepLogUrl+httpParameters.Url.RawQuery, projectName, pipelineName, runId, nodeId, stepId),
}
res, header, err := PipelineOjb.GetStepLog()
return res, header, err
}
func (j *Jenkins) GetNodeSteps(projectName, pipelineName, runId, nodeId string, httpParameters *devops.HttpParameters) ([]devops.NodeSteps, error) {
PipelineOjb := &Pipeline{
HttpParameters: httpParameters,
Jenkins: j,
Path: fmt.Sprintf(GetNodeStepsUrl+httpParameters.Url.RawQuery, projectName, pipelineName, runId, nodeId),
}
res, err := PipelineOjb.GetNodeSteps()
return res, err
}
func (j *Jenkins) GetPipelineRunNodes(projectName, pipelineName, runId string, httpParameters *devops.HttpParameters) ([]devops.PipelineRunNodes, error) {
PipelineOjb := &Pipeline{
HttpParameters: httpParameters,
Jenkins: j,
Path: fmt.Sprintf(GetPipelineRunNodesUrl+httpParameters.Url.RawQuery, projectName, pipelineName, runId),
}
res, err := PipelineOjb.GetPipelineRunNodes()
return res, err
}
func (j *Jenkins) SubmitInputStep(projectName, pipelineName, runId, nodeId, stepId string, httpParameters *devops.HttpParameters) ([]byte, error) {
PipelineOjb := &Pipeline{
HttpParameters: httpParameters,
Jenkins: j,
Path: fmt.Sprintf(SubmitInputStepUrl+httpParameters.Url.RawQuery, projectName, pipelineName, runId, nodeId, stepId),
}
res, err := PipelineOjb.SubmitInputStep()
return res, err
}
func (j *Jenkins) GetBranchPipeline(projectName, pipelineName, branchName string, httpParameters *devops.HttpParameters) (*devops.BranchPipeline, error) {
PipelineOjb := &Pipeline{
HttpParameters: httpParameters,
Jenkins: j,
Path: fmt.Sprintf(GetBranchPipelineUrl, projectName, pipelineName, branchName),
}
res, err := PipelineOjb.GetBranchPipeline()
return res, err
}
func (j *Jenkins) GetBranchPipelineRun(projectName, pipelineName, branchName, runId string, httpParameters *devops.HttpParameters) (*devops.PipelineRun, error) {
PipelineOjb := &Pipeline{
HttpParameters: httpParameters,
Jenkins: j,
Path: fmt.Sprintf(GetBranchPipelineRunUrl, projectName, pipelineName, branchName, runId),
}
res, err := PipelineOjb.GetBranchPipelineRun()
return res, err
}
func (j *Jenkins) StopBranchPipeline(projectName, pipelineName, branchName, runId string, httpParameters *devops.HttpParameters) (*devops.StopPipeline, error) {
PipelineOjb := &Pipeline{
HttpParameters: httpParameters,
Jenkins: j,
Path: fmt.Sprintf(StopBranchPipelineUrl+httpParameters.Url.RawQuery, projectName, pipelineName, branchName, runId),
}
res, err := PipelineOjb.StopBranchPipeline()
return res, err
}
func (j *Jenkins) ReplayBranchPipeline(projectName, pipelineName, branchName, runId string, httpParameters *devops.HttpParameters) (*devops.ReplayPipeline, error) {
PipelineOjb := &Pipeline{
HttpParameters: httpParameters,
Jenkins: j,
Path: fmt.Sprintf(ReplayBranchPipelineUrl+httpParameters.Url.RawQuery, projectName, pipelineName, branchName, runId),
}
res, err := PipelineOjb.ReplayBranchPipeline()
return res, err
}
func (j *Jenkins) RunBranchPipeline(projectName, pipelineName, branchName string, httpParameters *devops.HttpParameters) (*devops.RunPipeline, error) {
PipelineOjb := &Pipeline{
HttpParameters: httpParameters,
Jenkins: j,
Path: fmt.Sprintf(RunBranchPipelineUrl+httpParameters.Url.RawQuery, projectName, pipelineName, branchName),
}
res, err := PipelineOjb.RunBranchPipeline()
return res, err
}
func (j *Jenkins) GetBranchArtifacts(projectName, pipelineName, branchName, runId string, httpParameters *devops.HttpParameters) ([]devops.Artifacts, error) {
PipelineOjb := &Pipeline{
HttpParameters: httpParameters,
Jenkins: j,
Path: fmt.Sprintf(GetBranchArtifactsUrl+httpParameters.Url.RawQuery, projectName, pipelineName, branchName, runId),
}
res, err := PipelineOjb.GetBranchArtifacts()
return res, err
}
func (j *Jenkins) GetBranchRunLog(projectName, pipelineName, branchName, runId string, httpParameters *devops.HttpParameters) ([]byte, error) {
PipelineOjb := &Pipeline{
HttpParameters: httpParameters,
Jenkins: j,
Path: fmt.Sprintf(GetBranchRunLogUrl+httpParameters.Url.RawQuery, projectName, pipelineName, branchName, runId),
}
res, err := PipelineOjb.GetBranchRunLog()
return res, err
}
func (j *Jenkins) GetBranchStepLog(projectName, pipelineName, branchName, runId, nodeId, stepId string, httpParameters *devops.HttpParameters) ([]byte, http.Header, error) {
PipelineOjb := &Pipeline{
HttpParameters: httpParameters,
Jenkins: j,
Path: fmt.Sprintf(GetBranchStepLogUrl+httpParameters.Url.RawQuery, projectName, pipelineName, branchName, runId, nodeId, stepId),
}
res, header, err := PipelineOjb.GetBranchStepLog()
return res, header, err
}
func (j *Jenkins) GetBranchNodeSteps(projectName, pipelineName, branchName, runId, nodeId string, httpParameters *devops.HttpParameters) ([]devops.NodeSteps, error) {
PipelineOjb := &Pipeline{
HttpParameters: httpParameters,
Jenkins: j,
Path: fmt.Sprintf(GetBranchNodeStepsUrl+httpParameters.Url.RawQuery, projectName, pipelineName, branchName, runId, nodeId),
}
res, err := PipelineOjb.GetBranchNodeSteps()
return res, err
}
func (j *Jenkins) GetBranchPipelineRunNodes(projectName, pipelineName, branchName, runId string, httpParameters *devops.HttpParameters) ([]devops.BranchPipelineRunNodes, error) {
PipelineOjb := &Pipeline{
HttpParameters: httpParameters,
Jenkins: j,
Path: fmt.Sprintf(GetBranchPipeRunNodesUrl+httpParameters.Url.RawQuery, projectName, pipelineName, branchName, runId),
}
res, err := PipelineOjb.GetBranchPipelineRunNodes()
return res, err
}
func (j *Jenkins) SubmitBranchInputStep(projectName, pipelineName, branchName, runId, nodeId, stepId string, httpParameters *devops.HttpParameters) ([]byte, error) {
PipelineOjb := &Pipeline{
HttpParameters: httpParameters,
Jenkins: j,
Path: fmt.Sprintf(CheckBranchPipelineUrl+httpParameters.Url.RawQuery, projectName, pipelineName, branchName, runId, nodeId, stepId),
}
res, err := PipelineOjb.SubmitBranchInputStep()
return res, err
}
func (j *Jenkins) GetPipelineBranch(projectName, pipelineName string, httpParameters *devops.HttpParameters) (*devops.PipelineBranch, error) {
path := fmt.Sprintf(GetPipeBranchUrl, projectName, pipelineName) + httpParameters.Url.RawQuery
PipelineOjb := &Pipeline{
HttpParameters: httpParameters,
Jenkins: j,
Path: path,
}
res, err := PipelineOjb.GetPipelineBranch()
return res, err
}
func (j *Jenkins) ScanBranch(projectName, pipelineName string, httpParameters *devops.HttpParameters) ([]byte, error) {
PipelineOjb := &Pipeline{
HttpParameters: httpParameters,
Jenkins: j,
Path: fmt.Sprintf(ScanBranchUrl+httpParameters.Url.RawQuery, projectName, pipelineName),
}
res, err := PipelineOjb.ScanBranch()
return res, err
}
func (j *Jenkins) GetConsoleLog(projectName, pipelineName string, httpParameters *devops.HttpParameters) ([]byte, error) {
PipelineOjb := &Pipeline{
HttpParameters: httpParameters,
Jenkins: j,
Path: fmt.Sprintf(GetConsoleLogUrl+httpParameters.Url.RawQuery, projectName, pipelineName),
}
res, err := PipelineOjb.GetConsoleLog()
return res, err
}
func (j *Jenkins) GetCrumb(httpParameters *devops.HttpParameters) (*devops.Crumb, error) {
PipelineOjb := &Pipeline{
HttpParameters: httpParameters,
Jenkins: j,
Path: GetCrumbUrl,
}
res, err := PipelineOjb.GetCrumb()
return res, err
}
func (j *Jenkins) GetSCMServers(scmId string, httpParameters *devops.HttpParameters) ([]devops.SCMServer, error) {
PipelineOjb := &Pipeline{
HttpParameters: httpParameters,
Jenkins: j,
Path: GetSCMServersUrl,
}
res, err := PipelineOjb.GetSCMServers()
return res, err
}
func (j *Jenkins) GetSCMOrg(scmId string, httpParameters *devops.HttpParameters) ([]devops.SCMOrg, error) {
PipelineOjb := &Pipeline{
HttpParameters: httpParameters,
Jenkins: j,
Path: fmt.Sprintf(GetSCMOrgUrl+httpParameters.Url.RawQuery, scmId),
}
res, err := PipelineOjb.GetSCMOrg()
return res, err
}
func (j *Jenkins) GetOrgRepo(scmId, organizationId string, httpParameters *devops.HttpParameters) ([]devops.OrgRepo, error) {
PipelineOjb := &Pipeline{
HttpParameters: httpParameters,
Jenkins: j,
Path: fmt.Sprintf(GetOrgRepoUrl+httpParameters.Url.RawQuery, scmId, organizationId),
}
res, err := PipelineOjb.GetOrgRepo()
return res, err
}
func (j *Jenkins) CreateSCMServers(scmId string, httpParameters *devops.HttpParameters) (*devops.SCMServer, error) {
PipelineOjb := &Pipeline{
HttpParameters: httpParameters,
Jenkins: j,
Path: fmt.Sprintf(CreateSCMServersUrl, scmId),
}
res, err := PipelineOjb.CreateSCMServers()
return res, err
}
func (j *Jenkins) GetNotifyCommit(httpParameters *devops.HttpParameters) ([]byte, error) {
PipelineOjb := &Pipeline{
HttpParameters: httpParameters,
Jenkins: j,
Path: GetNotifyCommitUrl + httpParameters.Url.RawQuery,
}
res, err := PipelineOjb.GetNotifyCommit()
return res, err
}
func (j *Jenkins) GithubWebhook(httpParameters *devops.HttpParameters) ([]byte, error) {
PipelineOjb := &Pipeline{
HttpParameters: httpParameters,
Jenkins: j,
Path: GithubWebhookUrl + httpParameters.Url.RawQuery,
}
res, err := PipelineOjb.GithubWebhook()
return res, err
}
func (j *Jenkins) Validate(scmId string, httpParameters *devops.HttpParameters) (*devops.Validates, error) {
PipelineOjb := &Pipeline{
HttpParameters: httpParameters,
Jenkins: j,
Path: fmt.Sprintf(ValidateUrl, scmId),
}
res, err := PipelineOjb.Validate()
return res, err
}
func (j *Jenkins) CheckScriptCompile(projectName, pipelineName string, httpParameters *devops.HttpParameters) (*devops.CheckScript, error) {
PipelineOjb := &Pipeline{
HttpParameters: httpParameters,
Jenkins: j,
Path: fmt.Sprintf(CheckScriptCompileUrl, projectName, pipelineName),
}
res, err := PipelineOjb.CheckScriptCompile()
return res, err
}
func (j *Jenkins) CheckCron(projectName string, httpParameters *devops.HttpParameters) (*devops.CheckCronRes, error) {
var cron = new(devops.CronData)
var reader io.ReadCloser
var path string
reader = httpParameters.Body
cronData, err := ioutil.ReadAll(reader)
err = json.Unmarshal(cronData, cron)
if err != nil {
klog.Error(err)
return nil, err
}
if cron.PipelineName != "" {
path = fmt.Sprintf(CheckPipelienCronUrl, projectName, cron.PipelineName, cron.Cron)
} else {
path = fmt.Sprintf(CheckCronUrl, projectName, cron.Cron)
}
PipelineOjb := &Pipeline{
HttpParameters: httpParameters,
Jenkins: j,
Path: path,
}
res, err := PipelineOjb.CheckCron()
return res, err
}
func (j *Jenkins) ToJenkinsfile(httpParameters *devops.HttpParameters) (*devops.ResJenkinsfile, error) {
PipelineOjb := &Pipeline{
HttpParameters: httpParameters,
Jenkins: j,
Path: ToJenkinsfileUrl,
}
res, err := PipelineOjb.ToJenkinsfile()
return res, err
}
func (j *Jenkins) ToJson(httpParameters *devops.HttpParameters) (*devops.ResJson, error) {
PipelineOjb := &Pipeline{
HttpParameters: httpParameters,
Jenkins: j,
Path: ToJsonUrl,
}
res, err := PipelineOjb.ToJson()
return res, err
}
// Creates a new Jenkins Instance
// Optional parameters are: client, username, password
// After creating an instance call init method.
func CreateJenkins(client *http.Client, base string, maxConnection int, auth ...interface{}) *Jenkins {
j := &Jenkins{}
if strings.HasSuffix(base, "/") {
base = base[:len(base)-1]
}
j.Server = base
j.Requester = &Requester{Base: base, SslVerify: true, Client: client, connControl: make(chan struct{}, maxConnection)}
if j.Requester.Client == nil {
j.Requester.Client = http.DefaultClient
}
if len(auth) == 2 {
j.Requester.BasicAuth = &BasicAuth{Username: auth[0].(string), Password: auth[1].(string)}
}
return j
}

View File

@@ -0,0 +1,504 @@
// 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"
"encoding/json"
"errors"
"fmt"
"kubesphere.io/kubesphere/pkg/simple/client/devops"
"net/url"
"path"
"strconv"
"strings"
)
type Job struct {
Raw *JobResponse
Jenkins *Jenkins
Base string
}
type JobBuild struct {
Number int64
URL string
}
type JobBuildStatus struct {
Number int64
Building bool
Result string
}
type InnerJob struct {
Name string `json:"name"`
Url string `json:"url"`
Color string `json:"color"`
}
type ParameterDefinition struct {
DefaultParameterValue struct {
Name string `json:"name"`
Value interface{} `json:"value"`
} `json:"defaultParameterValue"`
Description string `json:"description"`
Name string `json:"name"`
Type string `json:"type"`
}
type JobResponse struct {
Class string `json:"_class"`
Actions []devops.GeneralAction
Buildable bool `json:"buildable"`
Builds []JobBuild
Color string `json:"color"`
ConcurrentBuild bool `json:"concurrentBuild"`
Description string `json:"description"`
DisplayName string `json:"displayName"`
DisplayNameOrNull interface{} `json:"displayNameOrNull"`
DownstreamProjects []InnerJob `json:"downstreamProjects"`
FirstBuild JobBuild
HealthReport []struct {
Description string `json:"description"`
IconClassName string `json:"iconClassName"`
IconUrl string `json:"iconUrl"`
Score int64 `json:"score"`
} `json:"healthReport"`
InQueue bool `json:"inQueue"`
KeepDependencies bool `json:"keepDependencies"`
LastBuild JobBuild `json:"lastBuild"`
LastCompletedBuild JobBuild `json:"lastCompletedBuild"`
LastFailedBuild JobBuild `json:"lastFailedBuild"`
LastStableBuild JobBuild `json:"lastStableBuild"`
LastSuccessfulBuild JobBuild `json:"lastSuccessfulBuild"`
LastUnstableBuild JobBuild `json:"lastUnstableBuild"`
LastUnsuccessfulBuild JobBuild `json:"lastUnsuccessfulBuild"`
Name string `json:"name"`
SubJobs []InnerJob `json:"subJobs"`
NextBuildNumber int64 `json:"nextBuildNumber"`
Property []struct {
ParameterDefinitions []ParameterDefinition `json:"parameterDefinitions"`
} `json:"property"`
QueueItem interface{} `json:"queueItem"`
Scm struct{} `json:"scm"`
UpstreamProjects []InnerJob `json:"upstreamProjects"`
URL string `json:"url"`
Jobs []InnerJob `json:"jobs"`
}
func (j *Job) parentBase() string {
return j.Base[:strings.LastIndex(j.Base, "/job/")]
}
type History struct {
BuildNumber int
BuildStatus string
BuildTimestamp int64
}
func (j *Job) GetName() string {
return j.Raw.Name
}
func (j *Job) GetDescription() string {
return j.Raw.Description
}
func (j *Job) GetDetails() *JobResponse {
return j.Raw
}
func (j *Job) GetBuild(id int64) (*Build, error) {
build := Build{Jenkins: j.Jenkins, Job: j, Raw: new(devops.Build), Depth: 1, Base: "/job/" + j.GetName() + "/" + strconv.FormatInt(id, 10)}
status, err := build.Poll()
if err != nil {
return nil, err
}
if status == 200 {
return &build, nil
}
return nil, errors.New(strconv.Itoa(status))
}
func (j *Job) getBuildByType(buildType string) (*Build, error) {
allowed := map[string]JobBuild{
"lastStableBuild": j.Raw.LastStableBuild,
"lastSuccessfulBuild": j.Raw.LastSuccessfulBuild,
"lastBuild": j.Raw.LastBuild,
"lastCompletedBuild": j.Raw.LastCompletedBuild,
"firstBuild": j.Raw.FirstBuild,
"lastFailedBuild": j.Raw.LastFailedBuild,
}
number := ""
if val, ok := allowed[buildType]; ok {
number = strconv.FormatInt(val.Number, 10)
} else {
panic("No Such Build")
}
build := Build{
Jenkins: j.Jenkins,
Depth: 1,
Job: j,
Raw: new(devops.Build),
Base: j.Base + "/" + number}
status, err := build.Poll()
if err != nil {
return nil, err
}
if status == 200 {
return &build, nil
}
return nil, errors.New(strconv.Itoa(status))
}
func (j *Job) GetLastSuccessfulBuild() (*Build, error) {
return j.getBuildByType("lastSuccessfulBuild")
}
func (j *Job) GetFirstBuild() (*Build, error) {
return j.getBuildByType("firstBuild")
}
func (j *Job) GetLastBuild() (*Build, error) {
return j.getBuildByType("lastBuild")
}
func (j *Job) GetLastStableBuild() (*Build, error) {
return j.getBuildByType("lastStableBuild")
}
func (j *Job) GetLastFailedBuild() (*Build, error) {
return j.getBuildByType("lastFailedBuild")
}
func (j *Job) GetLastCompletedBuild() (*Build, error) {
return j.getBuildByType("lastCompletedBuild")
}
// Returns All Builds with Number and URL
func (j *Job) GetAllBuildIds() ([]JobBuild, error) {
var buildsResp struct {
Builds []JobBuild `json:"allBuilds"`
}
_, err := j.Jenkins.Requester.GetJSON(j.Base, &buildsResp, map[string]string{"tree": "allBuilds[number,url]"})
if err != nil {
return nil, err
}
return buildsResp.Builds, nil
}
func (j *Job) GetAllBuildStatus() ([]JobBuildStatus, error) {
var buildsResp struct {
Builds []JobBuildStatus `json:"allBuilds"`
}
_, err := j.Jenkins.Requester.GetJSON(j.Base, &buildsResp, map[string]string{"tree": "allBuilds[number,building,result]"})
if err != nil {
return nil, err
}
return buildsResp.Builds, nil
}
func (j *Job) GetUpstreamJobsMetadata() []InnerJob {
return j.Raw.UpstreamProjects
}
func (j *Job) GetDownstreamJobsMetadata() []InnerJob {
return j.Raw.DownstreamProjects
}
func (j *Job) GetInnerJobsMetadata() []InnerJob {
return j.Raw.Jobs
}
func (j *Job) GetUpstreamJobs() ([]*Job, error) {
jobs := make([]*Job, len(j.Raw.UpstreamProjects))
for i, job := range j.Raw.UpstreamProjects {
ji, err := j.Jenkins.GetJob(job.Name)
if err != nil {
return nil, err
}
jobs[i] = ji
}
return jobs, nil
}
func (j *Job) GetDownstreamJobs() ([]*Job, error) {
jobs := make([]*Job, len(j.Raw.DownstreamProjects))
for i, job := range j.Raw.DownstreamProjects {
ji, err := j.Jenkins.GetJob(job.Name)
if err != nil {
return nil, err
}
jobs[i] = ji
}
return jobs, nil
}
func (j *Job) GetInnerJob(id string) (*Job, error) {
job := Job{Jenkins: j.Jenkins, Raw: new(JobResponse), Base: j.Base + "/job/" + id}
status, err := job.Poll()
if err != nil {
return nil, err
}
if status == 200 {
return &job, nil
}
return nil, errors.New(strconv.Itoa(status))
}
func (j *Job) GetInnerJobs() ([]*Job, error) {
jobs := make([]*Job, len(j.Raw.Jobs))
for i, job := range j.Raw.Jobs {
ji, err := j.GetInnerJob(job.Name)
if err != nil {
return nil, err
}
jobs[i] = ji
}
return jobs, nil
}
func (j *Job) Enable() (bool, error) {
resp, err := j.Jenkins.Requester.Post(j.Base+"/enable", nil, nil, nil)
if err != nil {
return false, err
}
if resp.StatusCode != 200 {
return false, errors.New(strconv.Itoa(resp.StatusCode))
}
return true, nil
}
func (j *Job) Disable() (bool, error) {
resp, err := j.Jenkins.Requester.Post(j.Base+"/disable", nil, nil, nil)
if err != nil {
return false, err
}
if resp.StatusCode != 200 {
return false, errors.New(strconv.Itoa(resp.StatusCode))
}
return true, nil
}
func (j *Job) Delete() (bool, error) {
resp, err := j.Jenkins.Requester.Post(j.Base+"/doDelete", nil, nil, nil)
if err != nil {
return false, err
}
if resp.StatusCode != 200 {
return false, errors.New(strconv.Itoa(resp.StatusCode))
}
return true, nil
}
func (j *Job) Rename(name string) (bool, error) {
data := url.Values{}
data.Set("newName", name)
_, err := j.Jenkins.Requester.Post(j.Base+"/doRename", bytes.NewBufferString(data.Encode()), nil, nil)
if err != nil {
return false, err
}
j.Base = "/job/" + name
j.Poll()
return true, nil
}
func (j *Job) Create(config string, qr ...interface{}) (*Job, error) {
var querystring map[string]string
if len(qr) > 0 {
querystring = qr[0].(map[string]string)
}
resp, err := j.Jenkins.Requester.PostXML(j.parentBase()+"/createItem", config, j.Raw, querystring)
if err != nil {
return nil, err
}
if resp.StatusCode == 200 {
j.Poll()
return j, nil
}
return nil, errors.New(strconv.Itoa(resp.StatusCode))
}
func (j *Job) Copy(destinationName string) (*Job, error) {
qr := map[string]string{"name": destinationName, "from": j.GetName(), "mode": "copy"}
resp, err := j.Jenkins.Requester.Post(j.parentBase()+"/createItem", nil, nil, qr)
if err != nil {
return nil, err
}
if resp.StatusCode == 200 {
newJob := &Job{Jenkins: j.Jenkins, Raw: new(JobResponse), Base: "/job/" + destinationName}
_, err := newJob.Poll()
if err != nil {
return nil, err
}
return newJob, nil
}
return nil, errors.New(strconv.Itoa(resp.StatusCode))
}
func (j *Job) UpdateConfig(config string) error {
var querystring map[string]string
resp, err := j.Jenkins.Requester.PostXML(j.Base+"/config.xml", config, nil, querystring)
if err != nil {
return err
}
if resp.StatusCode == 200 {
j.Poll()
return nil
}
return errors.New(strconv.Itoa(resp.StatusCode))
}
func (j *Job) GetConfig() (string, error) {
var data string
_, err := j.Jenkins.Requester.GetXML(j.Base+"/config.xml", &data, nil)
if err != nil {
return "", err
}
return data, nil
}
func (j *Job) GetParameters() ([]ParameterDefinition, error) {
_, err := j.Poll()
if err != nil {
return nil, err
}
var parameters []ParameterDefinition
for _, property := range j.Raw.Property {
parameters = append(parameters, property.ParameterDefinitions...)
}
return parameters, nil
}
func (j *Job) IsQueued() (bool, error) {
if _, err := j.Poll(); err != nil {
return false, err
}
return j.Raw.InQueue, nil
}
func (j *Job) IsRunning() (bool, error) {
if _, err := j.Poll(); err != nil {
return false, err
}
lastBuild, err := j.GetLastBuild()
if err != nil {
return false, err
}
return lastBuild.IsRunning(), nil
}
func (j *Job) IsEnabled() (bool, error) {
if _, err := j.Poll(); err != nil {
return false, err
}
return j.Raw.Color != "disabled", nil
}
func (j *Job) HasQueuedBuild() {
panic("Not Implemented yet")
}
func (j *Job) InvokeSimple(params map[string]string) (int64, error) {
endpoint := "/build"
parameters, err := j.GetParameters()
if err != nil {
return 0, err
}
if len(parameters) > 0 {
endpoint = "/buildWithParameters"
}
data := url.Values{}
for k, v := range params {
data.Set(k, v)
}
resp, err := j.Jenkins.Requester.Post(j.Base+endpoint, bytes.NewBufferString(data.Encode()), nil, nil)
if err != nil {
return 0, err
}
if resp.StatusCode != 200 && resp.StatusCode != 201 {
return 0, errors.New("Could not invoke job " + j.GetName())
}
location := resp.Header.Get("Location")
if location == "" {
return 0, errors.New("Don't have key \"Location\" in response of header")
}
u, err := url.Parse(location)
if err != nil {
return 0, err
}
number, err := strconv.ParseInt(path.Base(u.Path), 10, 64)
if err != nil {
return 0, err
}
return number, nil
}
func (j *Job) Invoke(files []string, skipIfRunning bool, params map[string]string, cause string, securityToken string) (bool, error) {
isRunning, err := j.IsRunning()
if err != nil {
return false, err
}
if isRunning && skipIfRunning {
return false, fmt.Errorf("Will not request new build because %s is already running", j.GetName())
}
base := "/build"
// If parameters are specified - url is /builWithParameters
if params != nil {
base = "/buildWithParameters"
} else {
params = make(map[string]string)
}
// If files are specified - url is /build
if files != nil {
base = "/build"
}
reqParams := map[string]string{}
buildParams := map[string]string{}
if securityToken != "" {
reqParams["token"] = securityToken
}
buildParams["json"] = string(makeJson(params))
b, _ := json.Marshal(buildParams)
resp, err := j.Jenkins.Requester.PostFiles(j.Base+base, bytes.NewBuffer(b), nil, reqParams, files)
if err != nil {
return false, err
}
if resp.StatusCode == 200 || resp.StatusCode == 201 {
return true, nil
}
return false, errors.New(strconv.Itoa(resp.StatusCode))
}
func (j *Job) Poll() (int, error) {
response, err := j.Jenkins.Requester.GetJSON(j.Base, j.Raw, nil)
if err != nil {
return 0, err
}
return response.StatusCode, nil
}

View File

@@ -0,0 +1,322 @@
package jenkins
import (
"fmt"
"github.com/emicklei/go-restful"
"k8s.io/klog"
"kubesphere.io/kubesphere/pkg/simple/client/devops"
)
const (
JenkinsAllUserRoleName = "kubesphere-user"
)
func GetProjectRoleName(projectId, role string) string {
return fmt.Sprintf("%s-%s-project", projectId, role)
}
func GetPipelineRoleName(projectId, role string) string {
return fmt.Sprintf("%s-%s-pipeline", projectId, role)
}
func GetProjectRolePattern(projectId string) string {
return fmt.Sprintf("^%s$", projectId)
}
func GetPipelineRolePattern(projectId string) string {
return fmt.Sprintf("^%s/.*", projectId)
}
var JenkinsOwnerProjectPermissionIds = &ProjectPermissionIds{
CredentialCreate: true,
CredentialDelete: true,
CredentialManageDomains: true,
CredentialUpdate: true,
CredentialView: true,
ItemBuild: true,
ItemCancel: true,
ItemConfigure: true,
ItemCreate: true,
ItemDelete: true,
ItemDiscover: true,
ItemMove: true,
ItemRead: true,
ItemWorkspace: true,
RunDelete: true,
RunReplay: true,
RunUpdate: true,
SCMTag: true,
}
var JenkinsProjectPermissionMap = map[string]ProjectPermissionIds{
devops.ProjectOwner: {
CredentialCreate: true,
CredentialDelete: true,
CredentialManageDomains: true,
CredentialUpdate: true,
CredentialView: true,
ItemBuild: true,
ItemCancel: true,
ItemConfigure: true,
ItemCreate: true,
ItemDelete: true,
ItemDiscover: true,
ItemMove: true,
ItemRead: true,
ItemWorkspace: true,
RunDelete: true,
RunReplay: true,
RunUpdate: true,
SCMTag: true,
},
devops.ProjectMaintainer: {
CredentialCreate: true,
CredentialDelete: true,
CredentialManageDomains: true,
CredentialUpdate: true,
CredentialView: true,
ItemBuild: true,
ItemCancel: true,
ItemConfigure: false,
ItemCreate: true,
ItemDelete: false,
ItemDiscover: true,
ItemMove: false,
ItemRead: true,
ItemWorkspace: true,
RunDelete: true,
RunReplay: true,
RunUpdate: true,
SCMTag: true,
},
devops.ProjectDeveloper: {
CredentialCreate: false,
CredentialDelete: false,
CredentialManageDomains: false,
CredentialUpdate: false,
CredentialView: false,
ItemBuild: true,
ItemCancel: true,
ItemConfigure: false,
ItemCreate: false,
ItemDelete: false,
ItemDiscover: true,
ItemMove: false,
ItemRead: true,
ItemWorkspace: true,
RunDelete: true,
RunReplay: true,
RunUpdate: true,
SCMTag: false,
},
devops.ProjectReporter: {
CredentialCreate: false,
CredentialDelete: false,
CredentialManageDomains: false,
CredentialUpdate: false,
CredentialView: false,
ItemBuild: false,
ItemCancel: false,
ItemConfigure: false,
ItemCreate: false,
ItemDelete: false,
ItemDiscover: true,
ItemMove: false,
ItemRead: true,
ItemWorkspace: false,
RunDelete: false,
RunReplay: false,
RunUpdate: false,
SCMTag: false,
},
}
var JenkinsPipelinePermissionMap = map[string]ProjectPermissionIds{
devops.ProjectOwner: {
CredentialCreate: true,
CredentialDelete: true,
CredentialManageDomains: true,
CredentialUpdate: true,
CredentialView: true,
ItemBuild: true,
ItemCancel: true,
ItemConfigure: true,
ItemCreate: true,
ItemDelete: true,
ItemDiscover: true,
ItemMove: true,
ItemRead: true,
ItemWorkspace: true,
RunDelete: true,
RunReplay: true,
RunUpdate: true,
SCMTag: true,
},
devops.ProjectMaintainer: {
CredentialCreate: true,
CredentialDelete: true,
CredentialManageDomains: true,
CredentialUpdate: true,
CredentialView: true,
ItemBuild: true,
ItemCancel: true,
ItemConfigure: true,
ItemCreate: true,
ItemDelete: true,
ItemDiscover: true,
ItemMove: true,
ItemRead: true,
ItemWorkspace: true,
RunDelete: true,
RunReplay: true,
RunUpdate: true,
SCMTag: true,
},
devops.ProjectDeveloper: {
CredentialCreate: false,
CredentialDelete: false,
CredentialManageDomains: false,
CredentialUpdate: false,
CredentialView: false,
ItemBuild: true,
ItemCancel: true,
ItemConfigure: false,
ItemCreate: false,
ItemDelete: false,
ItemDiscover: true,
ItemMove: false,
ItemRead: true,
ItemWorkspace: true,
RunDelete: true,
RunReplay: true,
RunUpdate: true,
SCMTag: false,
},
devops.ProjectReporter: {
CredentialCreate: false,
CredentialDelete: false,
CredentialManageDomains: false,
CredentialUpdate: false,
CredentialView: false,
ItemBuild: false,
ItemCancel: false,
ItemConfigure: false,
ItemCreate: false,
ItemDelete: false,
ItemDiscover: true,
ItemMove: false,
ItemRead: true,
ItemWorkspace: false,
RunDelete: false,
RunReplay: false,
RunUpdate: false,
SCMTag: false,
},
}
func (j *Jenkins) AddProjectMember(membership *devops.ProjectMembership) (*devops.ProjectMembership, error) {
globalRole, err := j.GetGlobalRole(JenkinsAllUserRoleName)
if err != nil {
klog.Errorf("%+v", err)
return nil, restful.NewError(GetJenkinsStatusCode(err), err.Error())
}
if globalRole == nil {
_, err := j.AddGlobalRole(JenkinsAllUserRoleName, GlobalPermissionIds{
GlobalRead: true,
}, true)
if err != nil {
klog.Errorf("failed to create jenkins global role %+v", err)
return nil, restful.NewError(GetJenkinsStatusCode(err), err.Error())
}
}
err = globalRole.AssignRole(membership.Username)
if err != nil {
klog.Errorf("%+v", err)
return nil, restful.NewError(GetJenkinsStatusCode(err), err.Error())
}
projectRole, err := j.GetProjectRole(GetProjectRoleName(membership.ProjectId, membership.Role))
if err != nil {
klog.Errorf("%+v", err)
return nil, restful.NewError(GetJenkinsStatusCode(err), err.Error())
}
err = projectRole.AssignRole(membership.Username)
if err != nil {
klog.Errorf("%+v", err)
return nil, restful.NewError(GetJenkinsStatusCode(err), err.Error())
}
pipelineRole, err := j.GetProjectRole(GetPipelineRoleName(membership.ProjectId, membership.Role))
if err != nil {
klog.Errorf("%+v", err)
return nil, restful.NewError(GetJenkinsStatusCode(err), err.Error())
}
err = pipelineRole.AssignRole(membership.Username)
if err != nil {
klog.Errorf("%+v", err)
return nil, restful.NewError(GetJenkinsStatusCode(err), err.Error())
}
return membership, nil
}
func (j *Jenkins) UpdateProjectMember(oldMembership, newMembership *devops.ProjectMembership) (*devops.ProjectMembership, error) {
oldProjectRole, err := j.GetProjectRole(GetProjectRoleName(oldMembership.ProjectId, oldMembership.Role))
if err != nil {
return nil, restful.NewError(GetJenkinsStatusCode(err), err.Error())
}
err = oldProjectRole.UnAssignRole(newMembership.Username)
if err != nil {
return nil, restful.NewError(GetJenkinsStatusCode(err), err.Error())
}
oldPipelineRole, err := j.GetProjectRole(GetPipelineRoleName(oldMembership.ProjectId, oldMembership.Role))
if err != nil {
return nil, restful.NewError(GetJenkinsStatusCode(err), err.Error())
}
err = oldPipelineRole.UnAssignRole(newMembership.Username)
if err != nil {
return nil, restful.NewError(GetJenkinsStatusCode(err), err.Error())
}
projectRole, err := j.GetProjectRole(GetProjectRoleName(oldMembership.ProjectId, newMembership.Role))
if err != nil {
return nil, restful.NewError(GetJenkinsStatusCode(err), err.Error())
}
err = projectRole.AssignRole(newMembership.Username)
if err != nil {
return nil, restful.NewError(GetJenkinsStatusCode(err), err.Error())
}
pipelineRole, err := j.GetProjectRole(GetPipelineRoleName(oldMembership.ProjectId, newMembership.Role))
if err != nil {
return nil, restful.NewError(GetJenkinsStatusCode(err), err.Error())
}
err = pipelineRole.AssignRole(newMembership.Username)
if err != nil {
return nil, restful.NewError(GetJenkinsStatusCode(err), err.Error())
}
return newMembership, nil
}
func (j *Jenkins) DeleteProjectMember(membership *devops.ProjectMembership) (*devops.ProjectMembership, error) {
oldProjectRole, err := j.GetProjectRole(GetProjectRoleName(membership.ProjectId, membership.Role))
if err != nil {
return nil, restful.NewError(GetJenkinsStatusCode(err), err.Error())
}
err = oldProjectRole.UnAssignRole(membership.Username)
if err != nil {
return nil, restful.NewError(GetJenkinsStatusCode(err), err.Error())
}
oldPipelineRole, err := j.GetProjectRole(GetPipelineRoleName(membership.ProjectId, membership.Role))
if err != nil {
return nil, restful.NewError(GetJenkinsStatusCode(err), err.Error())
}
err = oldPipelineRole.UnAssignRole(membership.Username)
if err != nil {
return nil, restful.NewError(GetJenkinsStatusCode(err), err.Error())
}
return membership, nil
}

View File

@@ -1,4 +1,4 @@
package devops
package jenkins
import (
"fmt"

View File

@@ -0,0 +1,722 @@
package jenkins
import (
"encoding/json"
"github.com/PuerkitoBio/goquery"
"k8s.io/klog"
"kubesphere.io/kubesphere/pkg/simple/client/devops"
"net/http"
"net/url"
"strings"
"time"
)
type Pipeline struct {
HttpParameters *devops.HttpParameters
Jenkins *Jenkins
Path string
}
const (
GetPipelineUrl = "/blue/rest/organizations/jenkins/pipelines/%s/pipelines/%s/"
ListPipelinesUrl = "/blue/rest/search/?"
GetPipelineRunUrl = "/blue/rest/organizations/jenkins/pipelines/%s/pipelines/%s/runs/%s/"
ListPipelineRunUrl = "/blue/rest/organizations/jenkins/pipelines/%s/pipelines/%s/runs/?"
StopPipelineUrl = "/blue/rest/organizations/jenkins/pipelines/%s/pipelines/%s/runs/%s/stop/?"
ReplayPipelineUrl = "/blue/rest/organizations/jenkins/pipelines/%s/pipelines/%s/runs/%s/replay/"
RunPipelineUrl = "/blue/rest/organizations/jenkins/pipelines/%s/pipelines/%s/runs/"
GetArtifactsUrl = "/blue/rest/organizations/jenkins/pipelines/%s/pipelines/%s/runs/%s/artifacts/?"
GetRunLogUrl = "/blue/rest/organizations/jenkins/pipelines/%s/pipelines/%s/runs/%s/log/?"
GetStepLogUrl = "/blue/rest/organizations/jenkins/pipelines/%s/pipelines/%s/runs/%s/nodes/%s/steps/%s/log/?"
GetPipelineRunNodesUrl = "/blue/rest/organizations/jenkins/pipelines/%s/pipelines/%s/runs/%s/nodes/?"
SubmitInputStepUrl = "/blue/rest/organizations/jenkins/pipelines/%s/pipelines/%s/runs/%s/nodes/%s/steps/%s/"
GetNodeStepsUrl = "/blue/rest/organizations/jenkins/pipelines/%s/pipelines/%s/runs/%s/nodes/%s/steps/?"
GetBranchPipelineUrl = "/blue/rest/organizations/jenkins/pipelines/%s/pipelines/%s/branches/%s/"
GetBranchPipelineRunUrl = "/blue/rest/organizations/jenkins/pipelines/%s/pipelines/%s/branches/%s/runs/%s/"
StopBranchPipelineUrl = "/blue/rest/organizations/jenkins/pipelines/%s/pipelines/%s/branches/%s/runs/%s/stop/?"
ReplayBranchPipelineUrl = "/blue/rest/organizations/jenkins/pipelines/%s/pipelines/%s/branches/%s/runs/%s/replay/"
RunBranchPipelineUrl = "/blue/rest/organizations/jenkins/pipelines/%s/pipelines/%s/branches/%s/runs/"
GetBranchArtifactsUrl = "/blue/rest/organizations/jenkins/pipelines/%s/pipelines/%s/branches/%s/runs/%s/artifacts/?"
GetBranchRunLogUrl = "/blue/rest/organizations/jenkins/pipelines/%s/pipelines/%s/branches/%s/runs/%s/log/?"
GetBranchStepLogUrl = "/blue/rest/organizations/jenkins/pipelines/%s/pipelines/%s/branches/%s/runs/%s/nodes/%s/steps/%s/log/?"
GetBranchNodeStepsUrl = "/blue/rest/organizations/jenkins/pipelines/%s/pipelines/%s/branches/%s/runs/%s/nodes/%s/steps/?"
GetBranchPipeRunNodesUrl = "/blue/rest/organizations/jenkins/pipelines/%s/pipelines/%s/branches/%s/runs/%s/nodes/?"
CheckBranchPipelineUrl = "/blue/rest/organizations/jenkins/pipelines/%s/pipelines/%s/branches/%s/runs/%s/nodes/%s/steps/%s/"
GetPipeBranchUrl = "/blue/rest/organizations/jenkins/pipelines/%s/pipelines/%s/branches/?"
ScanBranchUrl = "/job/%s/job/%s/build?"
GetConsoleLogUrl = "/job/%s/job/%s/indexing/consoleText"
GetCrumbUrl = "/crumbIssuer/api/json/"
GetSCMServersUrl = "/blue/rest/organizations/jenkins/scm/%s/servers/"
GetSCMOrgUrl = "/blue/rest/organizations/jenkins/scm/%s/organizations/?"
GetOrgRepoUrl = "/blue/rest/organizations/jenkins/scm/%s/organizations/%s/repositories/?"
CreateSCMServersUrl = "/blue/rest/organizations/jenkins/scm/%s/servers/"
ValidateUrl = "/blue/rest/organizations/jenkins/scm/%s/validate"
GetNotifyCommitUrl = "/git/notifyCommit/?"
GithubWebhookUrl = "/github-webhook/"
CheckScriptCompileUrl = "/job/%s/job/%s/descriptorByName/org.jenkinsci.plugins.workflow.cps.CpsFlowDefinition/checkScriptCompile"
CheckPipelienCronUrl = "/job/%s/job/%s/descriptorByName/hudson.triggers.TimerTrigger/checkSpec?value=%s"
CheckCronUrl = "/job/%s/descriptorByName/hudson.triggers.TimerTrigger/checkSpec?value=%s"
ToJenkinsfileUrl = "/pipeline-model-converter/toJenkinsfile"
ToJsonUrl = "/pipeline-model-converter/toJson"
cronJobLayout = "Monday, January 2, 2006 15:04:05 PM"
)
func (p *Pipeline) GetPipeline() (*devops.Pipeline, error) {
res, err := p.Jenkins.SendPureRequest(p.Path, p.HttpParameters)
if err != nil {
klog.Error(err)
}
var pipeline devops.Pipeline
err = json.Unmarshal(res, &pipeline)
if err != nil {
klog.Error(err)
return nil, err
}
return &pipeline, err
}
func (p *Pipeline) ListPipelines() (*devops.PipelineList, error) {
res, err := p.Jenkins.SendPureRequest(p.Path, p.HttpParameters)
if err != nil {
klog.Error(err)
return nil, err
}
count, err := p.searchPipelineCount()
if err != nil {
klog.Error(err)
return nil, err
}
pipelienList := devops.PipelineList{Total: count}
err = json.Unmarshal(res, &pipelienList.Items)
if err != nil {
klog.Error(err)
return nil, err
}
return &pipelienList, err
}
func (p *Pipeline) searchPipelineCount() (int, error) {
query, _ := parseJenkinsQuery(p.HttpParameters.Url.RawQuery)
query.Set("start", "0")
query.Set("limit", "1000")
query.Set("depth", "-1")
formatUrl := ListPipelinesUrl + query.Encode()
res, err := p.Jenkins.SendPureRequest(formatUrl, p.HttpParameters)
if err != nil {
klog.Error(err)
return 0, err
}
var pipelines []devops.Pipeline
err = json.Unmarshal(res, &pipelines)
if err != nil {
klog.Error(err)
return 0, err
}
return len(pipelines), nil
}
func (p *Pipeline) GetPipelineRun() (*devops.PipelineRun, error) {
res, err := p.Jenkins.SendPureRequest(p.Path, p.HttpParameters)
if err != nil {
klog.Error(err)
}
var pipelineRun devops.PipelineRun
err = json.Unmarshal(res, &pipelineRun)
if err != nil {
klog.Error(err)
return nil, err
}
return &pipelineRun, err
}
func (p *Pipeline) ListPipelineRuns() (*devops.PipelineRunList, error) {
res, err := p.Jenkins.SendPureRequest(p.Path, p.HttpParameters)
if err != nil {
klog.Error(err)
}
var pipelineRunList devops.PipelineRunList
err = json.Unmarshal(res, &pipelineRunList)
if err != nil {
klog.Error(err)
return nil, err
}
return &pipelineRunList, err
}
func (p *Pipeline) searchPipelineRunsCount() (int, error) {
query, _ := parseJenkinsQuery(p.HttpParameters.Url.RawQuery)
query.Set("start", "0")
query.Set("limit", "1000")
query.Set("depth", "-1")
//formatUrl := fmt.Sprintf(SearchPipelineRunUrl, projectName, pipelineName)
res, err := p.Jenkins.SendPureRequest(ListPipelineRunUrl+query.Encode(), p.HttpParameters)
if err != nil {
klog.Error(err)
return 0, err
}
var runs []devops.PipelineRun
err = json.Unmarshal(res, &runs)
if err != nil {
klog.Error(err)
return 0, err
}
return len(runs), nil
}
func (p *Pipeline) StopPipeline() (*devops.StopPipeline, error) {
res, err := p.Jenkins.SendPureRequest(p.Path, p.HttpParameters)
if err != nil {
klog.Error(err)
}
var stopPipeline devops.StopPipeline
err = json.Unmarshal(res, &stopPipeline)
if err != nil {
klog.Error(err)
return nil, err
}
return &stopPipeline, err
}
func (p *Pipeline) ReplayPipeline() (*devops.ReplayPipeline, error) {
res, err := p.Jenkins.SendPureRequest(p.Path, p.HttpParameters)
if err != nil {
klog.Error(err)
}
var replayPipeline devops.ReplayPipeline
err = json.Unmarshal(res, &replayPipeline)
if err != nil {
klog.Error(err)
return nil, err
}
return &replayPipeline, err
}
func (p *Pipeline) RunPipeline() (*devops.RunPipeline, error) {
res, err := p.Jenkins.SendPureRequest(p.Path, p.HttpParameters)
if err != nil {
klog.Error(err)
}
var runPipeline devops.RunPipeline
err = json.Unmarshal(res, &runPipeline)
if err != nil {
klog.Error(err)
return nil, err
}
return &runPipeline, err
}
func (p *Pipeline) GetArtifacts() ([]devops.Artifacts, error) {
res, err := p.Jenkins.SendPureRequest(p.Path, p.HttpParameters)
if err != nil {
klog.Error(err)
}
var artifacts []devops.Artifacts
err = json.Unmarshal(res, &artifacts)
if err != nil {
klog.Error(err)
return nil, err
}
return artifacts, err
}
func (p *Pipeline) GetRunLog() ([]byte, error) {
res, err := p.Jenkins.SendPureRequest(p.Path, p.HttpParameters)
if err != nil {
klog.Error(err)
}
return res, err
}
func (p *Pipeline) GetStepLog() ([]byte, http.Header, error) {
res, header, err := p.Jenkins.SendPureRequestWithHeaderResp(p.Path, p.HttpParameters)
if err != nil {
klog.Error(err)
}
return res, header, err
}
func (p *Pipeline) GetNodeSteps() ([]devops.NodeSteps, error) {
res, err := p.Jenkins.SendPureRequest(p.Path, p.HttpParameters)
if err != nil {
klog.Error(err)
}
var nodeSteps []devops.NodeSteps
err = json.Unmarshal(res, &nodeSteps)
if err != nil {
klog.Error(err)
return nil, err
}
return nodeSteps, err
}
func (p *Pipeline) GetPipelineRunNodes() ([]devops.PipelineRunNodes, error) {
res, err := p.Jenkins.SendPureRequest(p.Path, p.HttpParameters)
if err != nil {
klog.Error(err)
}
var pipelineRunNodes []devops.PipelineRunNodes
err = json.Unmarshal(res, &pipelineRunNodes)
if err != nil {
klog.Error(err)
return nil, err
}
return pipelineRunNodes, err
}
func (p *Pipeline) SubmitInputStep() ([]byte, error) {
res, err := p.Jenkins.SendPureRequest(p.Path, p.HttpParameters)
if err != nil {
klog.Error(err)
}
return res, err
}
func (p *Pipeline) GetBranchPipeline() (*devops.BranchPipeline, error) {
res, err := p.Jenkins.SendPureRequest(p.Path, p.HttpParameters)
if err != nil {
klog.Error(err)
}
var branchPipeline devops.BranchPipeline
err = json.Unmarshal(res, &branchPipeline)
if err != nil {
klog.Error(err)
return nil, err
}
return &branchPipeline, err
}
func (p *Pipeline) GetBranchPipelineRun() (*devops.PipelineRun, error) {
res, err := p.Jenkins.SendPureRequest(p.Path, p.HttpParameters)
if err != nil {
klog.Error(err)
}
var branchPipelineRun devops.PipelineRun
err = json.Unmarshal(res, &branchPipelineRun)
if err != nil {
klog.Error(err)
return nil, err
}
return &branchPipelineRun, err
}
func (p *Pipeline) StopBranchPipeline() (*devops.StopPipeline, error) {
res, err := p.Jenkins.SendPureRequest(p.Path, p.HttpParameters)
if err != nil {
klog.Error(err)
}
var branchStopPipeline devops.StopPipeline
err = json.Unmarshal(res, &branchStopPipeline)
if err != nil {
klog.Error(err)
return nil, err
}
return &branchStopPipeline, err
}
func (p *Pipeline) ReplayBranchPipeline() (*devops.ReplayPipeline, error) {
res, err := p.Jenkins.SendPureRequest(p.Path, p.HttpParameters)
if err != nil {
klog.Error(err)
}
var branchReplayPipeline devops.ReplayPipeline
err = json.Unmarshal(res, &branchReplayPipeline)
if err != nil {
klog.Error(err)
return nil, err
}
return &branchReplayPipeline, err
}
func (p *Pipeline) RunBranchPipeline() (*devops.RunPipeline, error) {
res, err := p.Jenkins.SendPureRequest(p.Path, p.HttpParameters)
if err != nil {
klog.Error(err)
}
var branchRunPipeline devops.RunPipeline
err = json.Unmarshal(res, &branchRunPipeline)
if err != nil {
klog.Error(err)
return nil, err
}
return &branchRunPipeline, err
}
func (p *Pipeline) GetBranchArtifacts() ([]devops.Artifacts, error) {
res, err := p.Jenkins.SendPureRequest(p.Path, p.HttpParameters)
if err != nil {
klog.Error(err)
}
var artifacts []devops.Artifacts
err = json.Unmarshal(res, &artifacts)
if err != nil {
klog.Error(err)
return nil, err
}
return artifacts, err
}
func (p *Pipeline) GetBranchRunLog() ([]byte, error) {
res, err := p.Jenkins.SendPureRequest(p.Path, p.HttpParameters)
if err != nil {
klog.Error(err)
}
return res, err
}
func (p *Pipeline) GetBranchStepLog() ([]byte, http.Header, error) {
res, header, err := p.Jenkins.SendPureRequestWithHeaderResp(p.Path, p.HttpParameters)
if err != nil {
klog.Error(err)
}
return res, header, err
}
func (p *Pipeline) GetBranchNodeSteps() ([]devops.NodeSteps, error) {
res, err := p.Jenkins.SendPureRequest(p.Path, p.HttpParameters)
if err != nil {
klog.Error(err)
}
var branchNodeSteps []devops.NodeSteps
err = json.Unmarshal(res, &branchNodeSteps)
if err != nil {
klog.Error(err)
return nil, err
}
return branchNodeSteps, err
}
func (p *Pipeline) GetBranchPipelineRunNodes() ([]devops.BranchPipelineRunNodes, error) {
res, err := p.Jenkins.SendPureRequest(p.Path, p.HttpParameters)
if err != nil {
klog.Error(err)
}
var branchPipelineRunNodes []devops.BranchPipelineRunNodes
err = json.Unmarshal(res, &branchPipelineRunNodes)
if err != nil {
klog.Error(err)
return nil, err
}
return branchPipelineRunNodes, err
}
func (p *Pipeline) SubmitBranchInputStep() ([]byte, error) {
res, err := p.Jenkins.SendPureRequest(p.Path, p.HttpParameters)
if err != nil {
klog.Error(err)
}
return res, err
}
func (p *Pipeline) GetPipelineBranch() (*devops.PipelineBranch, error) {
res, err := p.Jenkins.SendPureRequest(p.Path, p.HttpParameters)
if err != nil {
klog.Error(err)
}
var pipelineBranch devops.PipelineBranch
err = json.Unmarshal(res, &pipelineBranch)
if err != nil {
klog.Error(err)
return nil, err
}
return &pipelineBranch, err
}
func (p *Pipeline) ScanBranch() ([]byte, error) {
res, err := p.Jenkins.SendPureRequest(p.Path, p.HttpParameters)
if err != nil {
klog.Error(err)
}
return res, err
}
func (p *Pipeline) GetConsoleLog() ([]byte, error) {
res, err := p.Jenkins.SendPureRequest(p.Path, p.HttpParameters)
if err != nil {
klog.Error(err)
}
return res, err
}
func (p *Pipeline) GetCrumb() (*devops.Crumb, error) {
res, err := p.Jenkins.SendPureRequest(p.Path, p.HttpParameters)
if err != nil {
klog.Error(err)
}
var crumb devops.Crumb
err = json.Unmarshal(res, &crumb)
if err != nil {
klog.Error(err)
return nil, err
}
return &crumb, err
}
func (p *Pipeline) GetSCMServers() ([]devops.SCMServer, error) {
res, err := p.Jenkins.SendPureRequest(p.Path, p.HttpParameters)
if err != nil {
klog.Error(err)
}
var SCMServer []devops.SCMServer
err = json.Unmarshal(res, &SCMServer)
if err != nil {
klog.Error(err)
return nil, err
}
return SCMServer, err
}
func (p *Pipeline) GetSCMOrg() ([]devops.SCMOrg, error) {
res, err := p.Jenkins.SendPureRequest(p.Path, p.HttpParameters)
if err != nil {
klog.Error(err)
}
var SCMOrg []devops.SCMOrg
err = json.Unmarshal(res, &SCMOrg)
if err != nil {
klog.Error(err)
return nil, err
}
return SCMOrg, err
}
func (p *Pipeline) GetOrgRepo() ([]devops.OrgRepo, error) {
res, err := p.Jenkins.SendPureRequest(p.Path, p.HttpParameters)
if err != nil {
klog.Error(err)
}
var OrgRepo []devops.OrgRepo
err = json.Unmarshal(res, &OrgRepo)
if err != nil {
klog.Error(err)
return nil, err
}
return OrgRepo, err
}
func (p *Pipeline) CreateSCMServers() (*devops.SCMServer, error) {
res, err := p.Jenkins.SendPureRequest(p.Path, p.HttpParameters)
if err != nil {
klog.Error(err)
}
var SCMServer devops.SCMServer
err = json.Unmarshal(res, &SCMServer)
if err != nil {
klog.Error(err)
return nil, err
}
return &SCMServer, err
}
func (p *Pipeline) GetNotifyCommit() ([]byte, error) {
res, err := p.Jenkins.SendPureRequest(p.Path, p.HttpParameters)
if err != nil {
klog.Error(err)
}
return res, err
}
func (p *Pipeline) GithubWebhook() ([]byte, error) {
res, err := p.Jenkins.SendPureRequest(p.Path, p.HttpParameters)
if err != nil {
klog.Error(err)
}
return res, err
}
func (p *Pipeline) Validate() (*devops.Validates, error) {
res, err := p.Jenkins.SendPureRequest(p.Path, p.HttpParameters)
if err != nil {
klog.Error(err)
}
var validates devops.Validates
err = json.Unmarshal(res, &validates)
if err != nil {
klog.Error(err)
return nil, err
}
return &validates, err
}
func (p *Pipeline) CheckScriptCompile() (*devops.CheckScript, error) {
res, err := p.Jenkins.SendPureRequest(p.Path, p.HttpParameters)
if err != nil {
klog.Error(err)
}
// Jenkins will return different struct according to different results.
var checkScript devops.CheckScript
ok := json.Unmarshal(res, &checkScript)
if ok != nil {
var resJson []*devops.CheckScript
err := json.Unmarshal(res, &resJson)
if err != nil {
klog.Error(err)
return nil, err
}
return resJson[0], nil
}
return &checkScript, err
}
func (p *Pipeline) CheckCron() (*devops.CheckCronRes, error) {
var res = new(devops.CheckCronRes)
Url, err := url.Parse(p.Jenkins.Server + p.Path)
reqJenkins := &http.Request{
Method: http.MethodGet,
URL: Url,
Header: p.HttpParameters.Header,
}
client := &http.Client{Timeout: 30 * time.Second}
resp, err := client.Do(reqJenkins)
if resp != nil && resp.StatusCode != http.StatusOK {
resBody, _ := getRespBody(resp)
return &devops.CheckCronRes{
Result: "error",
Message: string(resBody),
}, err
}
if err != nil {
klog.Error(err)
return nil, err
}
defer resp.Body.Close()
doc, err := goquery.NewDocumentFromReader(resp.Body)
if err != nil {
klog.Error(err)
return nil, err
}
doc.Find("div").Each(func(i int, selection *goquery.Selection) {
res.Message = selection.Text()
res.Result, _ = selection.Attr("class")
})
if res.Result == "ok" {
res.LastTime, res.NextTime, err = parseCronJobTime(res.Message)
if err != nil {
klog.Error(err)
return nil, err
}
}
return res, err
}
func parseCronJobTime(msg string) (string, string, error) {
times := strings.Split(msg, ";")
lastTmp := strings.Split(times[0], " ")
lastCount := len(lastTmp)
lastTmp = lastTmp[lastCount-7 : lastCount-1]
lastTime := strings.Join(lastTmp, " ")
lastUinx, err := time.Parse(cronJobLayout, lastTime)
if err != nil {
klog.Error(err)
return "", "", err
}
last := lastUinx.Format(time.RFC3339)
nextTmp := strings.Split(times[1], " ")
nextCount := len(nextTmp)
nextTmp = nextTmp[nextCount-7 : nextCount-1]
nextTime := strings.Join(nextTmp, " ")
nextUinx, err := time.Parse(cronJobLayout, nextTime)
if err != nil {
klog.Error(err)
return "", "", err
}
next := nextUinx.Format(time.RFC3339)
return last, next, nil
}
func (p *Pipeline) ToJenkinsfile() (*devops.ResJenkinsfile, error) {
res, err := p.Jenkins.SendPureRequest(p.Path, p.HttpParameters)
if err != nil {
klog.Error(err)
}
var jenkinsfile devops.ResJenkinsfile
err = json.Unmarshal(res, &jenkinsfile)
if err != nil {
klog.Error(err)
return nil, err
}
return &jenkinsfile, err
}
func (p *Pipeline) ToJson() (*devops.ResJson, error) {
res, err := p.Jenkins.SendPureRequest(p.Path, p.HttpParameters)
if err != nil {
klog.Error(err)
}
var toJson devops.ResJson
err = json.Unmarshal(res, &toJson)
if err != nil {
klog.Error(err)
return nil, err
}
return &toJson, err
}

View File

@@ -0,0 +1,893 @@
package jenkins
import (
"fmt"
"github.com/beevik/etree"
"kubesphere.io/kubesphere/pkg/simple/client/devops"
"strconv"
"strings"
"time"
)
func replaceXmlVersion(config, oldVersion, targetVersion string) string {
lines := strings.Split(config, "\n")
lines[0] = strings.Replace(lines[0], oldVersion, targetVersion, -1)
output := strings.Join(lines, "\n")
return output
}
func createPipelineConfigXml(pipeline *devops.NoScmPipeline) (string, error) {
doc := etree.NewDocument()
xmlString := `<?xml version='1.0' encoding='UTF-8'?>
<flow-definition plugin="workflow-job">
<actions>
<org.jenkinsci.plugins.pipeline.modeldefinition.actions.DeclarativeJobAction plugin="pipeline-model-definition"/>
<org.jenkinsci.plugins.pipeline.modeldefinition.actions.DeclarativeJobPropertyTrackerAction plugin="pipeline-model-definition">
<jobProperties/>
<triggers/>
<parameters/>
<options/>
</org.jenkinsci.plugins.pipeline.modeldefinition.actions.DeclarativeJobPropertyTrackerAction>
</actions>
</flow-definition>
`
doc.ReadFromString(xmlString)
flow := doc.SelectElement("flow-definition")
flow.CreateElement("description").SetText(pipeline.Description)
properties := flow.CreateElement("properties")
if pipeline.DisableConcurrent {
properties.CreateElement("org.jenkinsci.plugins.workflow.job.properties.DisableConcurrentBuildsJobProperty")
}
if pipeline.Discarder != nil {
discarder := properties.CreateElement("jenkins.model.BuildDiscarderProperty")
strategy := discarder.CreateElement("strategy")
strategy.CreateAttr("class", "hudson.tasks.LogRotator")
strategy.CreateElement("daysToKeep").SetText(pipeline.Discarder.DaysToKeep)
strategy.CreateElement("numToKeep").SetText(pipeline.Discarder.NumToKeep)
strategy.CreateElement("artifactDaysToKeep").SetText("-1")
strategy.CreateElement("artifactNumToKeep").SetText("-1")
}
if pipeline.Parameters != nil {
appendParametersToEtree(properties, pipeline.Parameters)
}
if pipeline.TimerTrigger != nil {
triggers := properties.
CreateElement("org.jenkinsci.plugins.workflow.job.properties.PipelineTriggersJobProperty").
CreateElement("triggers")
triggers.CreateElement("hudson.triggers.TimerTrigger").CreateElement("spec").SetText(pipeline.TimerTrigger.Cron)
}
pipelineDefine := flow.CreateElement("definition")
pipelineDefine.CreateAttr("class", "org.jenkinsci.plugins.workflow.cps.CpsFlowDefinition")
pipelineDefine.CreateAttr("plugin", "workflow-cps")
pipelineDefine.CreateElement("script").SetText(pipeline.Jenkinsfile)
pipelineDefine.CreateElement("sandbox").SetText("true")
flow.CreateElement("triggers")
if pipeline.RemoteTrigger != nil {
flow.CreateElement("authToken").SetText(pipeline.RemoteTrigger.Token)
}
flow.CreateElement("disabled").SetText("false")
doc.Indent(2)
stringXml, err := doc.WriteToString()
if err != nil {
return "", err
}
return replaceXmlVersion(stringXml, "1.0", "1.1"), err
}
func parsePipelineConfigXml(config string) (*devops.NoScmPipeline, error) {
pipeline := &devops.NoScmPipeline{}
config = replaceXmlVersion(config, "1.1", "1.0")
doc := etree.NewDocument()
err := doc.ReadFromString(config)
if err != nil {
return nil, err
}
flow := doc.SelectElement("flow-definition")
if flow == nil {
return nil, fmt.Errorf("can not find pipeline definition")
}
pipeline.Description = flow.SelectElement("description").Text()
properties := flow.SelectElement("properties")
if properties.
SelectElement(
"org.jenkinsci.plugins.workflow.job.properties.DisableConcurrentBuildsJobProperty") != nil {
pipeline.DisableConcurrent = true
}
if properties.SelectElement("jenkins.model.BuildDiscarderProperty") != nil {
strategy := properties.
SelectElement("jenkins.model.BuildDiscarderProperty").
SelectElement("strategy")
pipeline.Discarder = &devops.DiscarderProperty{
DaysToKeep: strategy.SelectElement("daysToKeep").Text(),
NumToKeep: strategy.SelectElement("numToKeep").Text(),
}
}
pipeline.Parameters = &devops.Parameters{}
pipeline.Parameters = getParametersfromEtree(properties)
if len(*pipeline.Parameters) == 0 {
pipeline.Parameters = nil
}
if triggerProperty := properties.
SelectElement(
"org.jenkinsci.plugins.workflow.job.properties.PipelineTriggersJobProperty"); triggerProperty != nil {
triggers := triggerProperty.SelectElement("triggers")
if timerTrigger := triggers.SelectElement("hudson.triggers.TimerTrigger"); timerTrigger != nil {
pipeline.TimerTrigger = &devops.TimerTrigger{
Cron: timerTrigger.SelectElement("spec").Text(),
}
}
}
if authToken := flow.SelectElement("authToken"); authToken != nil {
pipeline.RemoteTrigger = &devops.RemoteTrigger{
Token: authToken.Text(),
}
}
if definition := flow.SelectElement("definition"); definition != nil {
if script := definition.SelectElement("script"); script != nil {
pipeline.Jenkinsfile = script.Text()
}
}
return pipeline, nil
}
func appendParametersToEtree(properties *etree.Element, parameters *devops.Parameters) {
parameterDefinitions := properties.CreateElement("hudson.model.ParametersDefinitionProperty").
CreateElement("parameterDefinitions")
for _, parameter := range *parameters {
for className, typeName := range devops.ParameterTypeMap {
if typeName == parameter.Type {
paramDefine := parameterDefinitions.CreateElement(className)
paramDefine.CreateElement("name").SetText(parameter.Name)
paramDefine.CreateElement("description").SetText(parameter.Description)
switch parameter.Type {
case "choice":
choices := paramDefine.CreateElement("choices")
choices.CreateAttr("class", "java.util.Arrays$ArrayList")
a := choices.CreateElement("a")
a.CreateAttr("class", "string-array")
choiceValues := strings.Split(parameter.DefaultValue, "\n")
for _, choiceValue := range choiceValues {
a.CreateElement("string").SetText(choiceValue)
}
case "file":
break
default:
paramDefine.CreateElement("defaultValue").SetText(parameter.DefaultValue)
}
}
}
}
}
func getParametersfromEtree(properties *etree.Element) *devops.Parameters {
var parameters devops.Parameters
if parametersProperty := properties.SelectElement("hudson.model.ParametersDefinitionProperty"); parametersProperty != nil {
params := parametersProperty.SelectElement("parameterDefinitions").ChildElements()
for _, param := range params {
switch param.Tag {
case "hudson.model.StringParameterDefinition":
parameters = append(parameters, &devops.Parameter{
Name: param.SelectElement("name").Text(),
Description: param.SelectElement("description").Text(),
DefaultValue: param.SelectElement("defaultValue").Text(),
Type: devops.ParameterTypeMap["hudson.model.StringParameterDefinition"],
})
case "hudson.model.BooleanParameterDefinition":
parameters = append(parameters, &devops.Parameter{
Name: param.SelectElement("name").Text(),
Description: param.SelectElement("description").Text(),
DefaultValue: param.SelectElement("defaultValue").Text(),
Type: devops.ParameterTypeMap["hudson.model.BooleanParameterDefinition"],
})
case "hudson.model.TextParameterDefinition":
parameters = append(parameters, &devops.Parameter{
Name: param.SelectElement("name").Text(),
Description: param.SelectElement("description").Text(),
DefaultValue: param.SelectElement("defaultValue").Text(),
Type: devops.ParameterTypeMap["hudson.model.TextParameterDefinition"],
})
case "hudson.model.FileParameterDefinition":
parameters = append(parameters, &devops.Parameter{
Name: param.SelectElement("name").Text(),
Description: param.SelectElement("description").Text(),
Type: devops.ParameterTypeMap["hudson.model.FileParameterDefinition"],
})
case "hudson.model.PasswordParameterDefinition":
parameters = append(parameters, &devops.Parameter{
Name: param.SelectElement("name").Text(),
Description: param.SelectElement("description").Text(),
DefaultValue: param.SelectElement("name").Text(),
Type: devops.ParameterTypeMap["hudson.model.PasswordParameterDefinition"],
})
case "hudson.model.ChoiceParameterDefinition":
choiceParameter := &devops.Parameter{
Name: param.SelectElement("name").Text(),
Description: param.SelectElement("description").Text(),
Type: devops.ParameterTypeMap["hudson.model.ChoiceParameterDefinition"],
}
choices := param.SelectElement("choices").SelectElement("a").SelectElements("string")
for _, choice := range choices {
choiceParameter.DefaultValue += fmt.Sprintf("%s\n", choice.Text())
}
choiceParameter.DefaultValue = strings.TrimSpace(choiceParameter.DefaultValue)
parameters = append(parameters, choiceParameter)
default:
parameters = append(parameters, &devops.Parameter{
Name: param.SelectElement("name").Text(),
Description: param.SelectElement("description").Text(),
DefaultValue: "unknown",
Type: param.Tag,
})
}
}
}
return &parameters
}
func appendGitSourceToEtree(source *etree.Element, gitSource *devops.GitSource) {
source.CreateAttr("class", "jenkins.plugins.git.GitSCMSource")
source.CreateAttr("plugin", "git")
source.CreateElement("id").SetText(gitSource.ScmId)
source.CreateElement("remote").SetText(gitSource.Url)
if gitSource.CredentialId != "" {
source.CreateElement("credentialsId").SetText(gitSource.CredentialId)
}
traits := source.CreateElement("traits")
if gitSource.DiscoverBranches {
traits.CreateElement("jenkins.plugins.git.traits.BranchDiscoveryTrait")
}
if gitSource.CloneOption != nil {
cloneExtension := traits.CreateElement("jenkins.plugins.git.traits.CloneOptionTrait").CreateElement("extension")
cloneExtension.CreateAttr("class", "hudson.plugins.git.extensions.impl.CloneOption")
cloneExtension.CreateElement("shallow").SetText(strconv.FormatBool(gitSource.CloneOption.Shallow))
cloneExtension.CreateElement("noTags").SetText(strconv.FormatBool(false))
cloneExtension.CreateElement("honorRefspec").SetText(strconv.FormatBool(true))
cloneExtension.CreateElement("reference")
if gitSource.CloneOption.Timeout >= 0 {
cloneExtension.CreateElement("timeout").SetText(strconv.Itoa(gitSource.CloneOption.Timeout))
} else {
cloneExtension.CreateElement("timeout").SetText(strconv.Itoa(10))
}
if gitSource.CloneOption.Depth >= 0 {
cloneExtension.CreateElement("depth").SetText(strconv.Itoa(gitSource.CloneOption.Depth))
} else {
cloneExtension.CreateElement("depth").SetText(strconv.Itoa(1))
}
}
if gitSource.RegexFilter != "" {
regexTraits := traits.CreateElement("jenkins.scm.impl.trait.RegexSCMHeadFilterTrait")
regexTraits.CreateAttr("plugin", "scm-api@2.4.0")
regexTraits.CreateElement("regex").SetText(gitSource.RegexFilter)
}
return
}
func getGitSourcefromEtree(source *etree.Element) *devops.GitSource {
var gitSource devops.GitSource
if credential := source.SelectElement("credentialsId"); credential != nil {
gitSource.CredentialId = credential.Text()
}
if remote := source.SelectElement("remote"); remote != nil {
gitSource.Url = remote.Text()
}
traits := source.SelectElement("traits")
if branchDiscoverTrait := traits.SelectElement(
"jenkins.plugins.git.traits.BranchDiscoveryTrait"); branchDiscoverTrait != nil {
gitSource.DiscoverBranches = true
}
if cloneTrait := traits.SelectElement(
"jenkins.plugins.git.traits.CloneOptionTrait"); cloneTrait != nil {
if cloneExtension := cloneTrait.SelectElement(
"extension"); cloneExtension != nil {
gitSource.CloneOption = &devops.GitCloneOption{}
if value, err := strconv.ParseBool(cloneExtension.SelectElement("shallow").Text()); err == nil {
gitSource.CloneOption.Shallow = value
}
if value, err := strconv.ParseInt(cloneExtension.SelectElement("timeout").Text(), 10, 32); err == nil {
gitSource.CloneOption.Timeout = int(value)
}
if value, err := strconv.ParseInt(cloneExtension.SelectElement("depth").Text(), 10, 32); err == nil {
gitSource.CloneOption.Depth = int(value)
}
}
}
if regexTrait := traits.SelectElement(
"jenkins.scm.impl.trait.RegexSCMHeadFilterTrait"); regexTrait != nil {
if regex := regexTrait.SelectElement("regex"); regex != nil {
gitSource.RegexFilter = regex.Text()
}
}
return &gitSource
}
func getGithubSourcefromEtree(source *etree.Element) *devops.GithubSource {
var githubSource devops.GithubSource
if credential := source.SelectElement("credentialsId"); credential != nil {
githubSource.CredentialId = credential.Text()
}
if repoOwner := source.SelectElement("repoOwner"); repoOwner != nil {
githubSource.Owner = repoOwner.Text()
}
if repository := source.SelectElement("repository"); repository != nil {
githubSource.Repo = repository.Text()
}
if apiUri := source.SelectElement("apiUri"); apiUri != nil {
githubSource.ApiUri = apiUri.Text()
}
traits := source.SelectElement("traits")
if branchDiscoverTrait := traits.SelectElement(
"org.jenkinsci.plugins.github__branch__source.BranchDiscoveryTrait"); branchDiscoverTrait != nil {
strategyId, _ := strconv.Atoi(branchDiscoverTrait.SelectElement("strategyId").Text())
githubSource.DiscoverBranches = strategyId
}
if originPRDiscoverTrait := traits.SelectElement(
"org.jenkinsci.plugins.github__branch__source.OriginPullRequestDiscoveryTrait"); originPRDiscoverTrait != nil {
strategyId, _ := strconv.Atoi(originPRDiscoverTrait.SelectElement("strategyId").Text())
githubSource.DiscoverPRFromOrigin = strategyId
}
if forkPRDiscoverTrait := traits.SelectElement(
"org.jenkinsci.plugins.github__branch__source.ForkPullRequestDiscoveryTrait"); forkPRDiscoverTrait != nil {
strategyId, _ := strconv.Atoi(forkPRDiscoverTrait.SelectElement("strategyId").Text())
trustClass := forkPRDiscoverTrait.SelectElement("trust").SelectAttr("class").Value
trust := strings.Split(trustClass, "$")
switch trust[1] {
case "TrustContributors":
githubSource.DiscoverPRFromForks = &devops.DiscoverPRFromForks{
Strategy: strategyId,
Trust: 1,
}
case "TrustEveryone":
githubSource.DiscoverPRFromForks = &devops.DiscoverPRFromForks{
Strategy: strategyId,
Trust: 2,
}
case "TrustPermission":
githubSource.DiscoverPRFromForks = &devops.DiscoverPRFromForks{
Strategy: strategyId,
Trust: 3,
}
case "TrustNobody":
githubSource.DiscoverPRFromForks = &devops.DiscoverPRFromForks{
Strategy: strategyId,
Trust: 4,
}
}
if cloneTrait := traits.SelectElement(
"jenkins.plugins.git.traits.CloneOptionTrait"); cloneTrait != nil {
if cloneExtension := cloneTrait.SelectElement(
"extension"); cloneExtension != nil {
githubSource.CloneOption = &devops.GitCloneOption{}
if value, err := strconv.ParseBool(cloneExtension.SelectElement("shallow").Text()); err == nil {
githubSource.CloneOption.Shallow = value
}
if value, err := strconv.ParseInt(cloneExtension.SelectElement("timeout").Text(), 10, 32); err == nil {
githubSource.CloneOption.Timeout = int(value)
}
if value, err := strconv.ParseInt(cloneExtension.SelectElement("depth").Text(), 10, 32); err == nil {
githubSource.CloneOption.Depth = int(value)
}
}
}
if regexTrait := traits.SelectElement(
"jenkins.scm.impl.trait.RegexSCMHeadFilterTrait"); regexTrait != nil {
if regex := regexTrait.SelectElement("regex"); regex != nil {
githubSource.RegexFilter = regex.Text()
}
}
}
return &githubSource
}
func appendGithubSourceToEtree(source *etree.Element, githubSource *devops.GithubSource) {
source.CreateAttr("class", "org.jenkinsci.plugins.github_branch_source.GitHubSCMSource")
source.CreateAttr("plugin", "github-branch-source")
source.CreateElement("id").SetText(githubSource.ScmId)
source.CreateElement("credentialsId").SetText(githubSource.CredentialId)
source.CreateElement("repoOwner").SetText(githubSource.Owner)
source.CreateElement("repository").SetText(githubSource.Repo)
if githubSource.ApiUri != "" {
source.CreateElement("apiUri").SetText(githubSource.ApiUri)
}
traits := source.CreateElement("traits")
if githubSource.DiscoverBranches != 0 {
traits.CreateElement("org.jenkinsci.plugins.github__branch__source.BranchDiscoveryTrait").
CreateElement("strategyId").SetText(strconv.Itoa(githubSource.DiscoverBranches))
}
if githubSource.DiscoverPRFromOrigin != 0 {
traits.CreateElement("org.jenkinsci.plugins.github__branch__source.OriginPullRequestDiscoveryTrait").
CreateElement("strategyId").SetText(strconv.Itoa(githubSource.DiscoverPRFromOrigin))
}
if githubSource.DiscoverPRFromForks != nil {
forkTrait := traits.CreateElement("org.jenkinsci.plugins.github__branch__source.ForkPullRequestDiscoveryTrait")
forkTrait.CreateElement("strategyId").SetText(strconv.Itoa(githubSource.DiscoverPRFromForks.Strategy))
trustClass := "org.jenkinsci.plugins.github_branch_source.ForkPullRequestDiscoveryTrait$"
switch githubSource.DiscoverPRFromForks.Trust {
case 1:
trustClass += "TrustContributors"
case 2:
trustClass += "TrustEveryone"
case 3:
trustClass += "TrustPermission"
case 4:
trustClass += "TrustNobody"
default:
}
forkTrait.CreateElement("trust").CreateAttr("class", trustClass)
}
if githubSource.CloneOption != nil {
cloneExtension := traits.CreateElement("jenkins.plugins.git.traits.CloneOptionTrait").CreateElement("extension")
cloneExtension.CreateAttr("class", "hudson.plugins.git.extensions.impl.CloneOption")
cloneExtension.CreateElement("shallow").SetText(strconv.FormatBool(githubSource.CloneOption.Shallow))
cloneExtension.CreateElement("noTags").SetText(strconv.FormatBool(false))
cloneExtension.CreateElement("honorRefspec").SetText(strconv.FormatBool(true))
cloneExtension.CreateElement("reference")
if githubSource.CloneOption.Timeout >= 0 {
cloneExtension.CreateElement("timeout").SetText(strconv.Itoa(githubSource.CloneOption.Timeout))
} else {
cloneExtension.CreateElement("timeout").SetText(strconv.Itoa(10))
}
if githubSource.CloneOption.Depth >= 0 {
cloneExtension.CreateElement("depth").SetText(strconv.Itoa(githubSource.CloneOption.Depth))
} else {
cloneExtension.CreateElement("depth").SetText(strconv.Itoa(1))
}
}
if githubSource.RegexFilter != "" {
regexTraits := traits.CreateElement("jenkins.scm.impl.trait.RegexSCMHeadFilterTrait")
regexTraits.CreateAttr("plugin", "scm-api@2.4.0")
regexTraits.CreateElement("regex").SetText(githubSource.RegexFilter)
}
return
}
func getBitbucketServerSourceFromEtree(source *etree.Element) *devops.BitbucketServerSource {
var s devops.BitbucketServerSource
if credential := source.SelectElement("credentialsId"); credential != nil {
s.CredentialId = credential.Text()
}
if repoOwner := source.SelectElement("repoOwner"); repoOwner != nil {
s.Owner = repoOwner.Text()
}
if repository := source.SelectElement("repository"); repository != nil {
s.Repo = repository.Text()
}
if apiUri := source.SelectElement("serverUrl"); apiUri != nil {
s.ApiUri = apiUri.Text()
}
traits := source.SelectElement("traits")
if branchDiscoverTrait := traits.SelectElement(
"com.cloudbees.jenkins.plugins.bitbucket.BranchDiscoveryTrait"); branchDiscoverTrait != nil {
strategyId, _ := strconv.Atoi(branchDiscoverTrait.SelectElement("strategyId").Text())
s.DiscoverBranches = strategyId
}
if originPRDiscoverTrait := traits.SelectElement(
"com.cloudbees.jenkins.plugins.bitbucket.OriginPullRequestDiscoveryTrait"); originPRDiscoverTrait != nil {
strategyId, _ := strconv.Atoi(originPRDiscoverTrait.SelectElement("strategyId").Text())
s.DiscoverPRFromOrigin = strategyId
}
if forkPRDiscoverTrait := traits.SelectElement(
"com.cloudbees.jenkins.plugins.bitbucket.ForkPullRequestDiscoveryTrait"); forkPRDiscoverTrait != nil {
strategyId, _ := strconv.Atoi(forkPRDiscoverTrait.SelectElement("strategyId").Text())
trustClass := forkPRDiscoverTrait.SelectElement("trust").SelectAttr("class").Value
trust := strings.Split(trustClass, "$")
switch trust[1] {
case "TrustEveryone":
s.DiscoverPRFromForks = &devops.DiscoverPRFromForks{
Strategy: strategyId,
Trust: 1,
}
case "TrustTeamForks":
s.DiscoverPRFromForks = &devops.DiscoverPRFromForks{
Strategy: strategyId,
Trust: 2,
}
case "TrustNobody":
s.DiscoverPRFromForks = &devops.DiscoverPRFromForks{
Strategy: strategyId,
Trust: 3,
}
}
if cloneTrait := traits.SelectElement(
"jenkins.plugins.git.traits.CloneOptionTrait"); cloneTrait != nil {
if cloneExtension := cloneTrait.SelectElement(
"extension"); cloneExtension != nil {
s.CloneOption = &devops.GitCloneOption{}
if value, err := strconv.ParseBool(cloneExtension.SelectElement("shallow").Text()); err == nil {
s.CloneOption.Shallow = value
}
if value, err := strconv.ParseInt(cloneExtension.SelectElement("timeout").Text(), 10, 32); err == nil {
s.CloneOption.Timeout = int(value)
}
if value, err := strconv.ParseInt(cloneExtension.SelectElement("depth").Text(), 10, 32); err == nil {
s.CloneOption.Depth = int(value)
}
}
}
if regexTrait := traits.SelectElement(
"jenkins.scm.impl.trait.RegexSCMHeadFilterTrait"); regexTrait != nil {
if regex := regexTrait.SelectElement("regex"); regex != nil {
s.RegexFilter = regex.Text()
}
}
}
return &s
}
func appendBitbucketServerSourceToEtree(source *etree.Element, s *devops.BitbucketServerSource) {
source.CreateAttr("class", "com.cloudbees.jenkins.plugins.bitbucket.BitbucketSCMSource")
source.CreateAttr("plugin", "cloudbees-bitbucket-branch-source")
source.CreateElement("id").SetText(s.ScmId)
source.CreateElement("credentialsId").SetText(s.CredentialId)
source.CreateElement("repoOwner").SetText(s.Owner)
source.CreateElement("repository").SetText(s.Repo)
source.CreateElement("serverUrl").SetText(s.ApiUri)
traits := source.CreateElement("traits")
if s.DiscoverBranches != 0 {
traits.CreateElement("com.cloudbees.jenkins.plugins.bitbucket.BranchDiscoveryTrait>").
CreateElement("strategyId").SetText(strconv.Itoa(s.DiscoverBranches))
}
if s.DiscoverPRFromOrigin != 0 {
traits.CreateElement("com.cloudbees.jenkins.plugins.bitbucket.OriginPullRequestDiscoveryTrait").
CreateElement("strategyId").SetText(strconv.Itoa(s.DiscoverPRFromOrigin))
}
if s.DiscoverPRFromForks != nil {
forkTrait := traits.CreateElement("com.cloudbees.jenkins.plugins.bitbucket.ForkPullRequestDiscoveryTrait")
forkTrait.CreateElement("strategyId").SetText(strconv.Itoa(s.DiscoverPRFromForks.Strategy))
trustClass := "com.cloudbees.jenkins.plugins.bitbucket.ForkPullRequestDiscoveryTrait$"
switch s.DiscoverPRFromForks.Trust {
case 1:
trustClass += "TrustEveryone"
case 2:
trustClass += "TrustTeamForks"
case 3:
trustClass += "TrustNobody"
default:
trustClass += "TrustEveryone"
}
forkTrait.CreateElement("trust").CreateAttr("class", trustClass)
}
if s.CloneOption != nil {
cloneExtension := traits.CreateElement("jenkins.plugins.git.traits.CloneOptionTrait").CreateElement("extension")
cloneExtension.CreateAttr("class", "hudson.plugins.git.extensions.impl.CloneOption")
cloneExtension.CreateElement("shallow").SetText(strconv.FormatBool(s.CloneOption.Shallow))
cloneExtension.CreateElement("noTags").SetText(strconv.FormatBool(false))
cloneExtension.CreateElement("honorRefspec").SetText(strconv.FormatBool(true))
cloneExtension.CreateElement("reference")
if s.CloneOption.Timeout >= 0 {
cloneExtension.CreateElement("timeout").SetText(strconv.Itoa(s.CloneOption.Timeout))
} else {
cloneExtension.CreateElement("timeout").SetText(strconv.Itoa(10))
}
if s.CloneOption.Depth >= 0 {
cloneExtension.CreateElement("depth").SetText(strconv.Itoa(s.CloneOption.Depth))
} else {
cloneExtension.CreateElement("depth").SetText(strconv.Itoa(1))
}
}
if s.RegexFilter != "" {
regexTraits := traits.CreateElement("jenkins.scm.impl.trait.RegexSCMHeadFilterTrait")
regexTraits.CreateAttr("plugin", "scm-api@2.4.0")
regexTraits.CreateElement("regex").SetText(s.RegexFilter)
}
return
}
func getSvnSourcefromEtree(source *etree.Element) *devops.SvnSource {
var s devops.SvnSource
if remote := source.SelectElement("remoteBase"); remote != nil {
s.Remote = remote.Text()
}
if credentialsId := source.SelectElement("credentialsId"); credentialsId != nil {
s.CredentialId = credentialsId.Text()
}
if includes := source.SelectElement("includes"); includes != nil {
s.Includes = includes.Text()
}
if excludes := source.SelectElement("excludes"); excludes != nil {
s.Excludes = excludes.Text()
}
return &s
}
func appendSvnSourceToEtree(source *etree.Element, s *devops.SvnSource) {
source.CreateAttr("class", "jenkins.scm.impl.subversion.SubversionSCMSource")
source.CreateAttr("plugin", "subversion")
source.CreateElement("id").SetText(s.ScmId)
if s.CredentialId != "" {
source.CreateElement("credentialsId").SetText(s.CredentialId)
}
if s.Remote != "" {
source.CreateElement("remoteBase").SetText(s.Remote)
}
if s.Includes != "" {
source.CreateElement("includes").SetText(s.Includes)
}
if s.Excludes != "" {
source.CreateElement("excludes").SetText(s.Excludes)
}
return
}
func getSingleSvnSourceFromEtree(source *etree.Element) *devops.SingleSvnSource {
var s devops.SingleSvnSource
if scm := source.SelectElement("scm"); scm != nil {
if locations := scm.SelectElement("locations"); locations != nil {
if moduleLocations := locations.SelectElement("hudson.scm.SubversionSCM_-ModuleLocation"); moduleLocations != nil {
if remote := moduleLocations.SelectElement("remote"); remote != nil {
s.Remote = remote.Text()
}
if credentialId := moduleLocations.SelectElement("credentialsId"); credentialId != nil {
s.CredentialId = credentialId.Text()
}
}
}
}
return &s
}
func appendSingleSvnSourceToEtree(source *etree.Element, s *devops.SingleSvnSource) {
source.CreateAttr("class", "jenkins.scm.impl.SingleSCMSource")
source.CreateAttr("plugin", "scm-api")
source.CreateElement("id").SetText(s.ScmId)
source.CreateElement("name").SetText("master")
scm := source.CreateElement("scm")
scm.CreateAttr("class", "hudson.scm.SubversionSCM")
scm.CreateAttr("plugin", "subversion")
location := scm.CreateElement("locations").CreateElement("hudson.scm.SubversionSCM_-ModuleLocation")
if s.Remote != "" {
location.CreateElement("remote").SetText(s.Remote)
}
if s.CredentialId != "" {
location.CreateElement("credentialsId").SetText(s.CredentialId)
}
location.CreateElement("local").SetText(".")
location.CreateElement("depthOption").SetText("infinity")
location.CreateElement("ignoreExternalsOption").SetText("true")
location.CreateElement("cancelProcessOnExternalsFail").SetText("true")
source.CreateElement("excludedRegions")
source.CreateElement("includedRegions")
source.CreateElement("excludedUsers")
source.CreateElement("excludedRevprop")
source.CreateElement("excludedCommitMessages")
source.CreateElement("workspaceUpdater").CreateAttr("class", "hudson.scm.subversion.UpdateUpdater")
source.CreateElement("ignoreDirPropChanges").SetText("false")
source.CreateElement("filterChangelog").SetText("false")
source.CreateElement("quietOperation").SetText("true")
return
}
func appendMultiBranchJobTriggerToEtree(properties *etree.Element, s *devops.MultiBranchJobTrigger) {
triggerProperty := properties.CreateElement("org.jenkinsci.plugins.workflow.multibranch.PipelineTriggerProperty")
triggerProperty.CreateAttr("plugin", "multibranch-action-triggers")
triggerProperty.CreateElement("createActionJobsToTrigger").SetText(s.CreateActionJobsToTrigger)
triggerProperty.CreateElement("deleteActionJobsToTrigger").SetText(s.DeleteActionJobsToTrigger)
return
}
func getMultiBranchJobTriggerfromEtree(properties *etree.Element) *devops.MultiBranchJobTrigger {
var s devops.MultiBranchJobTrigger
triggerProperty := properties.SelectElement("org.jenkinsci.plugins.workflow.multibranch.PipelineTriggerProperty")
if triggerProperty != nil {
s.CreateActionJobsToTrigger = triggerProperty.SelectElement("createActionJobsToTrigger").Text()
s.DeleteActionJobsToTrigger = triggerProperty.SelectElement("deleteActionJobsToTrigger").Text()
}
return &s
}
func createMultiBranchPipelineConfigXml(projectName string, pipeline *devops.MultiBranchPipeline) (string, error) {
doc := etree.NewDocument()
xmlString := `
<?xml version='1.0' encoding='UTF-8'?>
<org.jenkinsci.plugins.workflow.multibranch.WorkflowMultiBranchProject plugin="workflow-multibranch">
<actions/>
<properties>
<org.jenkinsci.plugins.pipeline.modeldefinition.config.FolderConfig plugin="pipeline-model-definition">
<dockerLabel></dockerLabel>
<registry plugin="docker-commons"/>
</org.jenkinsci.plugins.pipeline.modeldefinition.config.FolderConfig>
</properties>
<folderViews class="jenkins.branch.MultiBranchProjectViewHolder" plugin="branch-api">
<owner class="org.jenkinsci.plugins.workflow.multibranch.WorkflowMultiBranchProject" reference="../.."/>
</folderViews>
<healthMetrics>
<com.cloudbees.hudson.plugins.folder.health.WorstChildHealthMetric plugin="cloudbees-folder">
<nonRecursive>false</nonRecursive>
</com.cloudbees.hudson.plugins.folder.health.WorstChildHealthMetric>
</healthMetrics>
<icon class="jenkins.branch.MetadataActionFolderIcon" plugin="branch-api">
<owner class="org.jenkinsci.plugins.workflow.multibranch.WorkflowMultiBranchProject" reference="../.."/>
</icon>
</org.jenkinsci.plugins.workflow.multibranch.WorkflowMultiBranchProject>`
err := doc.ReadFromString(xmlString)
if err != nil {
return "", err
}
project := doc.SelectElement("org.jenkinsci.plugins.workflow.multibranch.WorkflowMultiBranchProject")
project.CreateElement("description").SetText(pipeline.Description)
if pipeline.MultiBranchJobTrigger != nil {
properties := project.SelectElement("properties")
appendMultiBranchJobTriggerToEtree(properties, pipeline.MultiBranchJobTrigger)
}
if pipeline.Discarder != nil {
discarder := project.CreateElement("orphanedItemStrategy")
discarder.CreateAttr("class", "com.cloudbees.hudson.plugins.folder.computed.DefaultOrphanedItemStrategy")
discarder.CreateAttr("plugin", "cloudbees-folder")
discarder.CreateElement("pruneDeadBranches").SetText("true")
discarder.CreateElement("daysToKeep").SetText(pipeline.Discarder.DaysToKeep)
discarder.CreateElement("numToKeep").SetText(pipeline.Discarder.NumToKeep)
}
triggers := project.CreateElement("triggers")
if pipeline.TimerTrigger != nil {
timeTrigger := triggers.CreateElement(
"com.cloudbees.hudson.plugins.folder.computed.PeriodicFolderTrigger")
timeTrigger.CreateAttr("plugin", "cloudbees-folder")
millis, err := strconv.ParseInt(pipeline.TimerTrigger.Interval, 10, 64)
if err != nil {
return "", err
}
timeTrigger.CreateElement("spec").SetText(toCrontab(millis))
timeTrigger.CreateElement("interval").SetText(pipeline.TimerTrigger.Interval)
triggers.CreateElement("disabled").SetText("false")
}
sources := project.CreateElement("sources")
sources.CreateAttr("class", "jenkins.branch.MultiBranchProject$BranchSourceList")
sources.CreateAttr("plugin", "branch-api")
sourcesOwner := sources.CreateElement("owner")
sourcesOwner.CreateAttr("class", "org.jenkinsci.plugins.workflow.multibranch.WorkflowMultiBranchProject")
sourcesOwner.CreateAttr("reference", "../..")
branchSource := sources.CreateElement("data").CreateElement("jenkins.branch.BranchSource")
branchSourceStrategy := branchSource.CreateElement("strategy")
branchSourceStrategy.CreateAttr("class", "jenkins.branch.NamedExceptionsBranchPropertyStrategy")
branchSourceStrategy.CreateElement("defaultProperties").CreateAttr("class", "empty-list")
branchSourceStrategy.CreateElement("namedExceptions").CreateAttr("class", "empty-list")
source := branchSource.CreateElement("source")
switch pipeline.SourceType {
case "git":
appendGitSourceToEtree(source, pipeline.GitSource)
case "github":
appendGithubSourceToEtree(source, pipeline.GitHubSource)
case "svn":
appendSvnSourceToEtree(source, pipeline.SvnSource)
case "single_svn":
appendSingleSvnSourceToEtree(source, pipeline.SingleSvnSource)
case "bitbucket_server":
appendBitbucketServerSourceToEtree(source, pipeline.BitbucketServerSource)
default:
return "", fmt.Errorf("unsupport source type")
}
factory := project.CreateElement("factory")
factory.CreateAttr("class", "org.jenkinsci.plugins.workflow.multibranch.WorkflowBranchProjectFactory")
factoryOwner := factory.CreateElement("owner")
factoryOwner.CreateAttr("class", "org.jenkinsci.plugins.workflow.multibranch.WorkflowMultiBranchProject")
factoryOwner.CreateAttr("reference", "../..")
factory.CreateElement("scriptPath").SetText(pipeline.ScriptPath)
doc.Indent(2)
stringXml, err := doc.WriteToString()
return replaceXmlVersion(stringXml, "1.0", "1.1"), err
}
func parseMultiBranchPipelineConfigXml(config string) (*devops.MultiBranchPipeline, error) {
pipeline := &devops.MultiBranchPipeline{}
config = replaceXmlVersion(config, "1.1", "1.0")
doc := etree.NewDocument()
err := doc.ReadFromString(config)
if err != nil {
return nil, err
}
project := doc.SelectElement("org.jenkinsci.plugins.workflow.multibranch.WorkflowMultiBranchProject")
if project == nil {
return nil, fmt.Errorf("can not parse mutibranch pipeline config")
}
if properties := project.SelectElement("properties"); properties != nil {
if multibranchTrigger := properties.SelectElement(
"org.jenkinsci.plugins.workflow.multibranch.PipelineTriggerProperty"); multibranchTrigger != nil {
pipeline.MultiBranchJobTrigger = getMultiBranchJobTriggerfromEtree(properties)
}
}
pipeline.Description = project.SelectElement("description").Text()
if discarder := project.SelectElement("orphanedItemStrategy"); discarder != nil {
pipeline.Discarder = &devops.DiscarderProperty{
DaysToKeep: discarder.SelectElement("daysToKeep").Text(),
NumToKeep: discarder.SelectElement("numToKeep").Text(),
}
}
if triggers := project.SelectElement("triggers"); triggers != nil {
if timerTrigger := triggers.SelectElement(
"com.cloudbees.hudson.plugins.folder.computed.PeriodicFolderTrigger"); timerTrigger != nil {
pipeline.TimerTrigger = &devops.TimerTrigger{
Interval: timerTrigger.SelectElement("interval").Text(),
}
}
}
if sources := project.SelectElement("sources"); sources != nil {
if sourcesData := sources.SelectElement("data"); sourcesData != nil {
if branchSource := sourcesData.SelectElement("jenkins.branch.BranchSource"); branchSource != nil {
source := branchSource.SelectElement("source")
switch source.SelectAttr("class").Value {
case "org.jenkinsci.plugins.github_branch_source.GitHubSCMSource":
pipeline.GitHubSource = getGithubSourcefromEtree(source)
pipeline.SourceType = "github"
case "com.cloudbees.jenkins.plugins.bitbucket.BitbucketSCMSource":
pipeline.BitbucketServerSource = getBitbucketServerSourceFromEtree(source)
pipeline.SourceType = "bitbucket_server"
case "jenkins.plugins.git.GitSCMSource":
pipeline.SourceType = "git"
pipeline.GitSource = getGitSourcefromEtree(source)
case "jenkins.scm.impl.SingleSCMSource":
pipeline.SourceType = "single_svn"
pipeline.SingleSvnSource = getSingleSvnSourceFromEtree(source)
case "jenkins.scm.impl.subversion.SubversionSCMSource":
pipeline.SourceType = "svn"
pipeline.SvnSource = getSvnSourcefromEtree(source)
}
}
}
}
pipeline.ScriptPath = project.SelectElement("factory").SelectElement("scriptPath").Text()
return pipeline, nil
}
func toCrontab(millis int64) string {
if millis*time.Millisecond.Nanoseconds() <= 5*time.Minute.Nanoseconds() {
return "* * * * *"
}
if millis*time.Millisecond.Nanoseconds() <= 30*time.Minute.Nanoseconds() {
return "H/5 * * * *"
}
if millis*time.Millisecond.Nanoseconds() <= 1*time.Hour.Nanoseconds() {
return "H/15 * * * *"
}
if millis*time.Millisecond.Nanoseconds() <= 8*time.Hour.Nanoseconds() {
return "H/30 * * * *"
}
if millis*time.Millisecond.Nanoseconds() <= 24*time.Hour.Nanoseconds() {
return "H H/4 * * *"
}
if millis*time.Millisecond.Nanoseconds() <= 48*time.Hour.Nanoseconds() {
return "H H/12 * * *"
}
return "H H * * *"
}

View File

@@ -0,0 +1,621 @@
package jenkins
import (
"kubesphere.io/kubesphere/pkg/simple/client/devops"
"reflect"
"testing"
)
func Test_NoScmPipelineConfig(t *testing.T) {
inputs := []*devops.NoScmPipeline{
{
Name: "",
Description: "for test",
Jenkinsfile: "node{echo 'hello'}",
},
{
Name: "",
Description: "",
Jenkinsfile: "node{echo 'hello'}",
},
{
Name: "",
Description: "",
Jenkinsfile: "node{echo 'hello'}",
DisableConcurrent: true,
},
}
for _, input := range inputs {
outputString, err := createPipelineConfigXml(input)
if err != nil {
t.Fatalf("should not get error %+v", err)
}
output, err := parsePipelineConfigXml(outputString)
if err != nil {
t.Fatalf("should not get error %+v", err)
}
if !reflect.DeepEqual(input, output) {
t.Fatalf("input [%+v] output [%+v] should equal ", input, output)
}
}
}
func Test_NoScmPipelineConfig_Discarder(t *testing.T) {
inputs := []*devops.NoScmPipeline{
{
Name: "",
Description: "for test",
Jenkinsfile: "node{echo 'hello'}",
Discarder: &devops.DiscarderProperty{
"3", "5",
},
},
{
Name: "",
Description: "for test",
Jenkinsfile: "node{echo 'hello'}",
Discarder: &devops.DiscarderProperty{
"3", "",
},
},
{
Name: "",
Description: "for test",
Jenkinsfile: "node{echo 'hello'}",
Discarder: &devops.DiscarderProperty{
"", "21321",
},
},
{
Name: "",
Description: "for test",
Jenkinsfile: "node{echo 'hello'}",
Discarder: &devops.DiscarderProperty{
"", "",
},
},
}
for _, input := range inputs {
outputString, err := createPipelineConfigXml(input)
if err != nil {
t.Fatalf("should not get error %+v", err)
}
output, err := parsePipelineConfigXml(outputString)
if err != nil {
t.Fatalf("should not get error %+v", err)
}
if !reflect.DeepEqual(input, output) {
t.Fatalf("input [%+v] output [%+v] should equal ", input, output)
}
}
}
func Test_NoScmPipelineConfig_Param(t *testing.T) {
inputs := []*devops.NoScmPipeline{
{
Name: "",
Description: "for test",
Jenkinsfile: "node{echo 'hello'}",
Parameters: &devops.Parameters{
&devops.Parameter{
Name: "d",
DefaultValue: "a\nb",
Type: "choice",
Description: "fortest",
},
},
},
{
Name: "",
Description: "for test",
Jenkinsfile: "node{echo 'hello'}",
Parameters: &devops.Parameters{
&devops.Parameter{
Name: "a",
DefaultValue: "abc",
Type: "string",
Description: "fortest",
},
&devops.Parameter{
Name: "b",
DefaultValue: "false",
Type: "boolean",
Description: "fortest",
},
&devops.Parameter{
Name: "c",
DefaultValue: "password \n aaa",
Type: "text",
Description: "fortest",
},
&devops.Parameter{
Name: "d",
DefaultValue: "a\nb",
Type: "choice",
Description: "fortest",
},
},
},
}
for _, input := range inputs {
outputString, err := createPipelineConfigXml(input)
if err != nil {
t.Fatalf("should not get error %+v", err)
}
output, err := parsePipelineConfigXml(outputString)
if err != nil {
t.Fatalf("should not get error %+v", err)
}
if !reflect.DeepEqual(input, output) {
t.Fatalf("input [%+v] output [%+v] should equal ", input, output)
}
}
}
func Test_NoScmPipelineConfig_Trigger(t *testing.T) {
inputs := []*devops.NoScmPipeline{
{
Name: "",
Description: "for test",
Jenkinsfile: "node{echo 'hello'}",
TimerTrigger: &devops.TimerTrigger{
Cron: "1 1 1 * * *",
},
},
{
Name: "",
Description: "for test",
Jenkinsfile: "node{echo 'hello'}",
RemoteTrigger: &devops.RemoteTrigger{
Token: "abc",
},
},
{
Name: "",
Description: "for test",
Jenkinsfile: "node{echo 'hello'}",
TimerTrigger: &devops.TimerTrigger{
Cron: "1 1 1 * * *",
},
RemoteTrigger: &devops.RemoteTrigger{
Token: "abc",
},
},
}
for _, input := range inputs {
outputString, err := createPipelineConfigXml(input)
if err != nil {
t.Fatalf("should not get error %+v", err)
}
output, err := parsePipelineConfigXml(outputString)
if err != nil {
t.Fatalf("should not get error %+v", err)
}
if !reflect.DeepEqual(input, output) {
t.Fatalf("input [%+v] output [%+v] should equal ", input, output)
}
}
}
func Test_MultiBranchPipelineConfig(t *testing.T) {
inputs := []*devops.MultiBranchPipeline{
{
Name: "",
Description: "for test",
ScriptPath: "Jenkinsfile",
SourceType: "git",
GitSource: &devops.GitSource{},
},
{
Name: "",
Description: "for test",
ScriptPath: "Jenkinsfile",
SourceType: "github",
GitHubSource: &devops.GithubSource{},
},
{
Name: "",
Description: "for test",
ScriptPath: "Jenkinsfile",
SourceType: "single_svn",
SingleSvnSource: &devops.SingleSvnSource{},
},
{
Name: "",
Description: "for test",
ScriptPath: "Jenkinsfile",
SourceType: "svn",
SvnSource: &devops.SvnSource{},
},
}
for _, input := range inputs {
outputString, err := createMultiBranchPipelineConfigXml("", input)
if err != nil {
t.Fatalf("should not get error %+v", err)
}
output, err := parseMultiBranchPipelineConfigXml(outputString)
if err != nil {
t.Fatalf("should not get error %+v", err)
}
if !reflect.DeepEqual(input, output) {
t.Fatalf("input [%+v] output [%+v] should equal ", input, output)
}
}
}
func Test_MultiBranchPipelineConfig_Discarder(t *testing.T) {
inputs := []*devops.MultiBranchPipeline{
{
Name: "",
Description: "for test",
ScriptPath: "Jenkinsfile",
SourceType: "git",
Discarder: &devops.DiscarderProperty{
DaysToKeep: "1",
NumToKeep: "2",
},
GitSource: &devops.GitSource{},
},
}
for _, input := range inputs {
outputString, err := createMultiBranchPipelineConfigXml("", input)
if err != nil {
t.Fatalf("should not get error %+v", err)
}
output, err := parseMultiBranchPipelineConfigXml(outputString)
if err != nil {
t.Fatalf("should not get error %+v", err)
}
if !reflect.DeepEqual(input, output) {
t.Fatalf("input [%+v] output [%+v] should equal ", input, output)
}
}
}
func Test_MultiBranchPipelineConfig_TimerTrigger(t *testing.T) {
inputs := []*devops.MultiBranchPipeline{
{
Name: "",
Description: "for test",
ScriptPath: "Jenkinsfile",
SourceType: "git",
TimerTrigger: &devops.TimerTrigger{
Interval: "12345566",
},
GitSource: &devops.GitSource{},
},
}
for _, input := range inputs {
outputString, err := createMultiBranchPipelineConfigXml("", input)
if err != nil {
t.Fatalf("should not get error %+v", err)
}
output, err := parseMultiBranchPipelineConfigXml(outputString)
if err != nil {
t.Fatalf("should not get error %+v", err)
}
if !reflect.DeepEqual(input, output) {
t.Fatalf("input [%+v] output [%+v] should equal ", input, output)
}
}
}
func Test_MultiBranchPipelineConfig_Source(t *testing.T) {
inputs := []*devops.MultiBranchPipeline{
{
Name: "",
Description: "for test",
ScriptPath: "Jenkinsfile",
SourceType: "git",
TimerTrigger: &devops.TimerTrigger{
Interval: "12345566",
},
GitSource: &devops.GitSource{
Url: "https://github.com/kubesphere/devops",
CredentialId: "git",
DiscoverBranches: true,
},
},
{
Name: "",
Description: "for test",
ScriptPath: "Jenkinsfile",
SourceType: "github",
TimerTrigger: &devops.TimerTrigger{
Interval: "12345566",
},
GitHubSource: &devops.GithubSource{
Owner: "kubesphere",
Repo: "devops",
CredentialId: "github",
ApiUri: "https://api.github.com",
DiscoverBranches: 1,
DiscoverPRFromOrigin: 2,
DiscoverPRFromForks: &devops.DiscoverPRFromForks{
Strategy: 1,
Trust: 1,
},
},
},
{
Name: "",
Description: "for test",
ScriptPath: "Jenkinsfile",
SourceType: "bitbucket_server",
TimerTrigger: &devops.TimerTrigger{
Interval: "12345566",
},
BitbucketServerSource: &devops.BitbucketServerSource{
Owner: "kubesphere",
Repo: "devops",
CredentialId: "github",
ApiUri: "https://api.github.com",
DiscoverBranches: 1,
DiscoverPRFromOrigin: 2,
DiscoverPRFromForks: &devops.DiscoverPRFromForks{
Strategy: 1,
Trust: 1,
},
},
},
{
Name: "",
Description: "for test",
ScriptPath: "Jenkinsfile",
SourceType: "svn",
TimerTrigger: &devops.TimerTrigger{
Interval: "12345566",
},
SvnSource: &devops.SvnSource{
Remote: "https://api.svn.com/bcd",
CredentialId: "svn",
Excludes: "truck",
Includes: "tag/*",
},
},
{
Name: "",
Description: "for test",
ScriptPath: "Jenkinsfile",
SourceType: "single_svn",
TimerTrigger: &devops.TimerTrigger{
Interval: "12345566",
},
SingleSvnSource: &devops.SingleSvnSource{
Remote: "https://api.svn.com/bcd",
CredentialId: "svn",
},
},
}
for _, input := range inputs {
outputString, err := createMultiBranchPipelineConfigXml("", input)
if err != nil {
t.Fatalf("should not get error %+v", err)
}
output, err := parseMultiBranchPipelineConfigXml(outputString)
if err != nil {
t.Fatalf("should not get error %+v", err)
}
if !reflect.DeepEqual(input, output) {
t.Fatalf("input [%+v] output [%+v] should equal ", input, output)
}
}
}
func Test_MultiBranchPipelineCloneConfig(t *testing.T) {
inputs := []*devops.MultiBranchPipeline{
{
Name: "",
Description: "for test",
ScriptPath: "Jenkinsfile",
SourceType: "git",
GitSource: &devops.GitSource{
Url: "https://github.com/kubesphere/devops",
CredentialId: "git",
DiscoverBranches: true,
CloneOption: &devops.GitCloneOption{
Shallow: false,
Depth: 3,
Timeout: 20,
},
},
},
{
Name: "",
Description: "for test",
ScriptPath: "Jenkinsfile",
SourceType: "github",
GitHubSource: &devops.GithubSource{
Owner: "kubesphere",
Repo: "devops",
CredentialId: "github",
ApiUri: "https://api.github.com",
DiscoverBranches: 1,
DiscoverPRFromOrigin: 2,
DiscoverPRFromForks: &devops.DiscoverPRFromForks{
Strategy: 1,
Trust: 1,
},
CloneOption: &devops.GitCloneOption{
Shallow: false,
Depth: 3,
Timeout: 20,
},
},
},
}
for _, input := range inputs {
outputString, err := createMultiBranchPipelineConfigXml("", input)
if err != nil {
t.Fatalf("should not get error %+v", err)
}
output, err := parseMultiBranchPipelineConfigXml(outputString)
if err != nil {
t.Fatalf("should not get error %+v", err)
}
if !reflect.DeepEqual(input, output) {
t.Fatalf("input [%+v] output [%+v] should equal ", input, output)
}
}
}
func Test_MultiBranchPipelineRegexFilter(t *testing.T) {
inputs := []*devops.MultiBranchPipeline{
{
Name: "",
Description: "for test",
ScriptPath: "Jenkinsfile",
SourceType: "git",
GitSource: &devops.GitSource{
Url: "https://github.com/kubesphere/devops",
CredentialId: "git",
DiscoverBranches: true,
RegexFilter: ".*",
},
},
{
Name: "",
Description: "for test",
ScriptPath: "Jenkinsfile",
SourceType: "github",
GitHubSource: &devops.GithubSource{
Owner: "kubesphere",
Repo: "devops",
CredentialId: "github",
ApiUri: "https://api.github.com",
DiscoverBranches: 1,
DiscoverPRFromOrigin: 2,
DiscoverPRFromForks: &devops.DiscoverPRFromForks{
Strategy: 1,
Trust: 1,
},
RegexFilter: ".*",
},
},
}
for _, input := range inputs {
outputString, err := createMultiBranchPipelineConfigXml("", input)
if err != nil {
t.Fatalf("should not get error %+v", err)
}
output, err := parseMultiBranchPipelineConfigXml(outputString)
if err != nil {
t.Fatalf("should not get error %+v", err)
}
if !reflect.DeepEqual(input, output) {
t.Fatalf("input [%+v] output [%+v] should equal ", input, output)
}
}
}
func Test_MultiBranchPipelineMultibranchTrigger(t *testing.T) {
inputs := []*devops.MultiBranchPipeline{
{
Name: "",
Description: "for test",
ScriptPath: "Jenkinsfile",
SourceType: "github",
GitHubSource: &devops.GithubSource{
Owner: "kubesphere",
Repo: "devops",
CredentialId: "github",
ApiUri: "https://api.github.com",
DiscoverBranches: 1,
DiscoverPRFromOrigin: 2,
DiscoverPRFromForks: &devops.DiscoverPRFromForks{
Strategy: 1,
Trust: 1,
},
RegexFilter: ".*",
},
MultiBranchJobTrigger: &devops.MultiBranchJobTrigger{
CreateActionJobsToTrigger: "abc",
DeleteActionJobsToTrigger: "ddd",
},
},
{
Name: "",
Description: "for test",
ScriptPath: "Jenkinsfile",
SourceType: "github",
GitHubSource: &devops.GithubSource{
Owner: "kubesphere",
Repo: "devops",
CredentialId: "github",
ApiUri: "https://api.github.com",
DiscoverBranches: 1,
DiscoverPRFromOrigin: 2,
DiscoverPRFromForks: &devops.DiscoverPRFromForks{
Strategy: 1,
Trust: 1,
},
RegexFilter: ".*",
},
MultiBranchJobTrigger: &devops.MultiBranchJobTrigger{
CreateActionJobsToTrigger: "abc",
},
},
{
Name: "",
Description: "for test",
ScriptPath: "Jenkinsfile",
SourceType: "github",
GitHubSource: &devops.GithubSource{
Owner: "kubesphere",
Repo: "devops",
CredentialId: "github",
ApiUri: "https://api.github.com",
DiscoverBranches: 1,
DiscoverPRFromOrigin: 2,
DiscoverPRFromForks: &devops.DiscoverPRFromForks{
Strategy: 1,
Trust: 1,
},
RegexFilter: ".*",
},
MultiBranchJobTrigger: &devops.MultiBranchJobTrigger{
DeleteActionJobsToTrigger: "ddd",
},
},
}
for _, input := range inputs {
outputString, err := createMultiBranchPipelineConfigXml("", input)
if err != nil {
t.Fatalf("should not get error %+v", err)
}
output, err := parseMultiBranchPipelineConfigXml(outputString)
if err != nil {
t.Fatalf("should not get error %+v", err)
}
if !reflect.DeepEqual(input, output) {
t.Fatalf("input [%+v] output [%+v] should equal ", input, output)
}
}
}

View File

@@ -0,0 +1,162 @@
/*
Copyright 2018 The KubeSphere Authors.
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 (
"errors"
"net/http"
"strconv"
)
type ValidateJenkinsfileResponse struct {
Status string `json:"status"`
Data struct {
Result string `json:"result"`
Errors []map[string]interface{} `json:"errors"`
} `json:"data"`
}
type ValidatePipelineJsonResponse struct {
Status string `json:"status"`
Data struct {
Result string `json:"result"`
Errors []map[string]interface{} `json:"errors"`
}
}
type PipelineJsonToJenkinsfileResponse struct {
Status string `json:"status"`
Data struct {
Result string `json:"result"`
Errors []map[string]interface{} `json:"errors"`
Jenkinsfile string `json:"jenkinsfile"`
} `json:"data"`
}
type JenkinsfileToPipelineJsonResponse struct {
Status string `json:"status"`
Data struct {
Result string `json:"result"`
Errors []map[string]interface{} `json:"errors"`
Json map[string]interface{} `json:"json"`
} `json:"data"`
}
type StepJsonToJenkinsfileResponse struct {
Status string `json:"status"`
Data struct {
Result string `json:"result"`
Errors []map[string]interface{} `json:"errors"`
Jenkinsfile string `json:"jenkinsfile"`
} `json:"data"`
}
type StepsJenkinsfileToJsonResponse struct {
Status string `json:"status"`
Data struct {
Result string `json:"result"`
Errors []map[string]interface{} `json:"errors"`
Json []map[string]interface{} `json:"json"`
} `json:"data"`
}
func (j *Jenkins) ValidateJenkinsfile(jenkinsfile string) (*ValidateJenkinsfileResponse, error) {
responseStrut := &ValidateJenkinsfileResponse{}
query := map[string]string{
"jenkinsfile": jenkinsfile,
}
response, err := j.Requester.PostForm("/pipeline-model-converter/validateJenkinsfile", nil, responseStrut, query)
if err != nil {
return nil, err
}
if response.StatusCode != http.StatusOK {
return nil, errors.New(strconv.Itoa(response.StatusCode))
}
return responseStrut, nil
}
func (j *Jenkins) ValidatePipelineJson(json string) (*ValidatePipelineJsonResponse, error) {
responseStruct := &ValidatePipelineJsonResponse{}
query := map[string]string{
"json": json,
}
response, err := j.Requester.PostForm("/pipeline-model-converter/validateJson", nil, responseStruct, query)
if err != nil {
return nil, err
}
if response.StatusCode != http.StatusOK {
return nil, errors.New(strconv.Itoa(response.StatusCode))
}
return responseStruct, nil
}
func (j *Jenkins) PipelineJsonToJenkinsfile(json string) (*PipelineJsonToJenkinsfileResponse, error) {
responseStrut := &PipelineJsonToJenkinsfileResponse{}
query := map[string]string{
"json": json,
}
response, err := j.Requester.PostForm("/pipeline-model-converter/toJenkinsfile", nil, responseStrut, query)
if err != nil {
return nil, err
}
if response.StatusCode != http.StatusOK {
return nil, errors.New(strconv.Itoa(response.StatusCode))
}
return responseStrut, nil
}
func (j *Jenkins) JenkinsfileToPipelineJson(jenkinsfile string) (*JenkinsfileToPipelineJsonResponse, error) {
responseStrut := &JenkinsfileToPipelineJsonResponse{}
query := map[string]string{
"jenkinsfile": jenkinsfile,
}
response, err := j.Requester.PostForm("/pipeline-model-converter/toJson", nil, responseStrut, query)
if err != nil {
return nil, err
}
if response.StatusCode != http.StatusOK {
return nil, errors.New(strconv.Itoa(response.StatusCode))
}
return responseStrut, nil
}
func (j *Jenkins) StepsJsonToJenkinsfile(json string) (*StepJsonToJenkinsfileResponse, error) {
responseStrut := &StepJsonToJenkinsfileResponse{}
query := map[string]string{
"json": json,
}
response, err := j.Requester.PostForm("/pipeline-model-converter/stepsToJenkinsfile", nil, responseStrut, query)
if err != nil {
return nil, err
}
if response.StatusCode != http.StatusOK {
return nil, errors.New(strconv.Itoa(response.StatusCode))
}
return responseStrut, nil
}
func (j *Jenkins) StepsJenkinsfileToJson(jenkinsfile string) (*StepsJenkinsfileToJsonResponse, error) {
responseStrut := &StepsJenkinsfileToJsonResponse{}
query := map[string]string{
"jenkinsfile": jenkinsfile,
}
response, err := j.Requester.PostForm("/pipeline-model-converter/stepsToJson", nil, responseStrut, query)
if err != nil {
return nil, err
}
if response.StatusCode != http.StatusOK {
return nil, errors.New(strconv.Itoa(response.StatusCode))
}
return responseStrut, nil
}

View File

@@ -0,0 +1,109 @@
package jenkins
import (
"github.com/emicklei/go-restful"
"k8s.io/klog"
"kubesphere.io/kubesphere/pkg/api/devops/v1alpha2"
"kubesphere.io/kubesphere/pkg/simple/client/devops"
"net/http"
"sync"
)
type DevOpsProjectRoleResponse struct {
ProjectRole *ProjectRole
Err error
}
func (j *Jenkins) CreateDevOpsProject(username string, project *v1alpha2.DevOpsProject) (*v1alpha2.DevOpsProject, error) {
_, err := j.CreateFolder(project.ProjectId, project.Description)
if err != nil {
klog.Errorf("%+v", err)
return nil, restful.NewError(GetJenkinsStatusCode(err), err.Error())
}
var addRoleCh = make(chan *DevOpsProjectRoleResponse, 8)
var addRoleWg sync.WaitGroup
for role, permission := range JenkinsProjectPermissionMap {
addRoleWg.Add(1)
go func(role string, permission ProjectPermissionIds) {
_, err := j.AddProjectRole(GetProjectRoleName(project.ProjectId, role),
GetProjectRolePattern(project.ProjectId), permission, true)
addRoleCh <- &DevOpsProjectRoleResponse{nil, err}
addRoleWg.Done()
}(role, permission)
}
for role, permission := range JenkinsPipelinePermissionMap {
addRoleWg.Add(1)
go func(role string, permission ProjectPermissionIds) {
_, err := j.AddProjectRole(GetPipelineRoleName(project.ProjectId, role),
GetPipelineRolePattern(project.ProjectId), permission, true)
addRoleCh <- &DevOpsProjectRoleResponse{nil, err}
addRoleWg.Done()
}(role, permission)
}
addRoleWg.Wait()
close(addRoleCh)
for addRoleResponse := range addRoleCh {
if addRoleResponse.Err != nil {
klog.Errorf("%+v", addRoleResponse.Err)
return nil, restful.NewError(GetJenkinsStatusCode(addRoleResponse.Err), addRoleResponse.Err.Error())
}
}
globalRole, err := j.GetGlobalRole(JenkinsAllUserRoleName)
if err != nil {
return nil, restful.NewError(GetJenkinsStatusCode(err), err.Error())
}
if globalRole == nil {
_, err := j.AddGlobalRole(JenkinsAllUserRoleName, GlobalPermissionIds{
GlobalRead: true,
}, true)
if err != nil {
return nil, restful.NewError(GetJenkinsStatusCode(err), err.Error())
}
}
err = globalRole.AssignRole(username)
if err != nil {
return nil, restful.NewError(GetJenkinsStatusCode(err), err.Error())
}
projectRole, err := j.GetProjectRole(GetProjectRoleName(project.ProjectId, devops.ProjectOwner))
if err != nil {
return nil, restful.NewError(GetJenkinsStatusCode(err), err.Error())
}
err = projectRole.AssignRole(username)
if err != nil {
return nil, restful.NewError(GetJenkinsStatusCode(err), err.Error())
}
pipelineRole, err := j.GetProjectRole(GetPipelineRoleName(project.ProjectId, devops.ProjectOwner))
if err != nil {
return nil, restful.NewError(GetJenkinsStatusCode(err), err.Error())
}
err = pipelineRole.AssignRole(username)
if err != nil {
return nil, restful.NewError(GetJenkinsStatusCode(err), err.Error())
}
return project, nil
}
func (j *Jenkins) DeleteDevOpsProject(projectId string) error {
_, err := j.DeleteJob(projectId)
if err != nil && GetJenkinsStatusCode(err) != http.StatusNotFound {
klog.Errorf("%+v", err)
return restful.NewError(GetJenkinsStatusCode(err), err.Error())
}
roleNames := make([]string, 0)
for role := range JenkinsProjectPermissionMap {
roleNames = append(roleNames, GetProjectRoleName(projectId, role))
roleNames = append(roleNames, GetPipelineRoleName(projectId, role))
}
err = j.DeleteProjectRoles(roleNames...)
if err != nil {
klog.Errorf("%+v", err)
return restful.NewError(GetJenkinsStatusCode(err), err.Error())
}
return nil
}

View File

@@ -0,0 +1,165 @@
package jenkins
import (
"fmt"
"github.com/emicklei/go-restful"
"k8s.io/klog"
"kubesphere.io/kubesphere/pkg/simple/client/devops"
"net/http"
)
func (j *Jenkins) CreateProjectPipeline(projectId string, pipeline *devops.ProjectPipeline) (string, error) {
switch pipeline.Type {
case devops.NoScmPipelineType:
config, err := createPipelineConfigXml(pipeline.Pipeline)
if err != nil {
return "", restful.NewError(http.StatusInternalServerError, err.Error())
}
job, err := j.GetJob(pipeline.Pipeline.Name, projectId)
if job != nil {
err := fmt.Errorf("job name [%s] has been used", job.GetName())
return "", restful.NewError(http.StatusConflict, err.Error())
}
if err != nil && GetJenkinsStatusCode(err) != http.StatusNotFound {
return "", restful.NewError(GetJenkinsStatusCode(err), err.Error())
}
_, err = j.CreateJobInFolder(config, pipeline.Pipeline.Name, projectId)
if err != nil {
return "", restful.NewError(GetJenkinsStatusCode(err), err.Error())
}
return pipeline.Pipeline.Name, nil
case devops.MultiBranchPipelineType:
config, err := createMultiBranchPipelineConfigXml(projectId, pipeline.MultiBranchPipeline)
if err != nil {
return "", restful.NewError(http.StatusInternalServerError, err.Error())
}
job, err := j.GetJob(pipeline.MultiBranchPipeline.Name, projectId)
if job != nil {
err := fmt.Errorf("job name [%s] has been used", job.GetName())
return "", restful.NewError(http.StatusConflict, err.Error())
}
if err != nil && GetJenkinsStatusCode(err) != http.StatusNotFound {
return "", restful.NewError(GetJenkinsStatusCode(err), err.Error())
}
_, err = j.CreateJobInFolder(config, pipeline.MultiBranchPipeline.Name, projectId)
if err != nil {
return "", restful.NewError(GetJenkinsStatusCode(err), err.Error())
}
return pipeline.MultiBranchPipeline.Name, nil
default:
err := fmt.Errorf("error unsupport job type")
klog.Errorf("%+v", err)
return "", restful.NewError(http.StatusBadRequest, err.Error())
}
}
func (j *Jenkins) DeleteProjectPipeline(projectId string, pipelineId string) (string, error) {
_, err := j.DeleteJob(pipelineId, projectId)
if err != nil {
return "", restful.NewError(GetJenkinsStatusCode(err), err.Error())
}
return pipelineId, nil
}
func (j *Jenkins) UpdateProjectPipeline(projectId string, pipeline *devops.ProjectPipeline) (string, error) {
switch pipeline.Type {
case devops.NoScmPipelineType:
config, err := createPipelineConfigXml(pipeline.Pipeline)
if err != nil {
return "", restful.NewError(http.StatusInternalServerError, err.Error())
}
job, err := j.GetJob(pipeline.Pipeline.Name, projectId)
if err != nil {
return "", restful.NewError(GetJenkinsStatusCode(err), err.Error())
}
err = job.UpdateConfig(config)
if err != nil {
return "", restful.NewError(GetJenkinsStatusCode(err), err.Error())
}
return pipeline.Pipeline.Name, nil
case devops.MultiBranchPipelineType:
config, err := createMultiBranchPipelineConfigXml(projectId, pipeline.MultiBranchPipeline)
if err != nil {
klog.Errorf("%+v", err)
return "", restful.NewError(http.StatusInternalServerError, err.Error())
}
job, err := j.GetJob(pipeline.MultiBranchPipeline.Name, projectId)
if err != nil {
return "", restful.NewError(GetJenkinsStatusCode(err), err.Error())
}
err = job.UpdateConfig(config)
if err != nil {
klog.Errorf("%+v", err)
return "", restful.NewError(GetJenkinsStatusCode(err), err.Error())
}
return pipeline.MultiBranchPipeline.Name, nil
default:
err := fmt.Errorf("error unsupport job type")
klog.Errorf("%+v", err)
return "", restful.NewError(http.StatusBadRequest, err.Error())
}
}
func (j *Jenkins) GetProjectPipelineConfig(projectId, pipelineId string) (*devops.ProjectPipeline, error) {
job, err := j.GetJob(pipelineId, projectId)
if err != nil {
klog.Errorf("%+v", err)
return nil, restful.NewError(GetJenkinsStatusCode(err), err.Error())
}
switch job.Raw.Class {
case "org.jenkinsci.plugins.workflow.job.WorkflowJob":
config, err := job.GetConfig()
if err != nil {
return nil, restful.NewError(GetJenkinsStatusCode(err), err.Error())
}
pipeline, err := parsePipelineConfigXml(config)
if err != nil {
return nil, restful.NewError(GetJenkinsStatusCode(err), err.Error())
}
pipeline.Name = pipelineId
return &devops.ProjectPipeline{
Type: devops.NoScmPipelineType,
Pipeline: pipeline,
}, nil
case "org.jenkinsci.plugins.workflow.multibranch.WorkflowMultiBranchProject":
config, err := job.GetConfig()
if err != nil {
return nil, restful.NewError(GetJenkinsStatusCode(err), err.Error())
}
pipeline, err := parseMultiBranchPipelineConfigXml(config)
if err != nil {
return nil, restful.NewError(GetJenkinsStatusCode(err), err.Error())
}
pipeline.Name = pipelineId
return &devops.ProjectPipeline{
Type: devops.MultiBranchPipelineType,
MultiBranchPipeline: pipeline,
}, nil
default:
klog.Errorf("%+v", err)
return nil, restful.NewError(http.StatusBadRequest, err.Error())
}
}

View File

@@ -0,0 +1,54 @@
package jenkins
import (
"k8s.io/klog"
"kubesphere.io/kubesphere/pkg/simple/client/devops"
"net/http"
"net/url"
"time"
)
// TODO: deprecated, use SendJenkinsRequestWithHeaderResp() instead
func (j *Jenkins) SendPureRequest(path string, httpParameters *devops.HttpParameters) ([]byte, error) {
resBody, _, err := j.SendPureRequestWithHeaderResp(path, httpParameters)
return resBody, err
}
func (j *Jenkins) SendPureRequestWithHeaderResp(path string, httpParameters *devops.HttpParameters) ([]byte, http.Header, error) {
Url, err := url.Parse(j.Server + path)
if err != nil {
klog.Error(err)
return nil, nil, err
}
client := &http.Client{Timeout: 30 * time.Second}
newRequest := &http.Request{
Method: httpParameters.Method,
URL: Url,
Header: httpParameters.Header,
Body: httpParameters.Body,
Form: httpParameters.Form,
PostForm: httpParameters.PostForm,
}
resp, err := client.Do(newRequest)
if err != nil {
klog.Error(err)
return nil, nil, err
}
resBody, _ := getRespBody(resp)
defer resp.Body.Close()
if resp.StatusCode >= http.StatusBadRequest {
klog.Errorf("%+v", string(resBody))
jkerr := new(JkError)
jkerr.Code = resp.StatusCode
jkerr.Message = string(resBody)
return nil, nil, jkerr
}
return resBody, resp.Header, nil
}

View File

@@ -0,0 +1,468 @@
// 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"
"encoding/json"
"errors"
"fmt"
"io"
"io/ioutil"
"mime/multipart"
"net/http"
"net/url"
"os"
"path/filepath"
"strings"
)
// Request Methods
type APIRequest struct {
Method string
Endpoint string
Payload io.Reader
Headers http.Header
Suffix string
}
func (ar *APIRequest) SetHeader(key string, value string) *APIRequest {
ar.Headers.Set(key, value)
return ar
}
func NewAPIRequest(method string, endpoint string, payload io.Reader) *APIRequest {
var headers = http.Header{}
var suffix string
ar := &APIRequest{method, endpoint, payload, headers, suffix}
return ar
}
type Requester struct {
Base string
BasicAuth *BasicAuth
Client *http.Client
CACert []byte
SslVerify bool
connControl chan struct{}
}
func (r *Requester) SetCrumb(ar *APIRequest) error {
crumbData := map[string]string{}
response, err := r.GetJSON("/crumbIssuer/api/json", &crumbData, nil)
if err != nil {
jenkinsError, ok := err.(*ErrorResponse)
if ok && jenkinsError.Response.StatusCode == http.StatusNotFound {
return nil
}
return err
}
if response.StatusCode == 200 && crumbData["crumbRequestField"] != "" {
ar.SetHeader(crumbData["crumbRequestField"], crumbData["crumb"])
}
return nil
}
func (r *Requester) PostJSON(endpoint string, payload io.Reader, responseStruct interface{}, querystring map[string]string) (*http.Response, error) {
ar := NewAPIRequest("POST", endpoint, payload)
if err := r.SetCrumb(ar); err != nil {
return nil, err
}
ar.SetHeader("Content-Type", "application/x-www-form-urlencoded")
ar.Suffix = "api/json"
return r.Do(ar, responseStruct, querystring)
}
func (r *Requester) Post(endpoint string, payload io.Reader, responseStruct interface{}, querystring map[string]string) (*http.Response, error) {
ar := NewAPIRequest("POST", endpoint, payload)
if err := r.SetCrumb(ar); err != nil {
return nil, err
}
ar.SetHeader("Content-Type", "application/x-www-form-urlencoded")
ar.Suffix = ""
return r.Do(ar, responseStruct, querystring)
}
func (r *Requester) PostForm(endpoint string, payload io.Reader, responseStruct interface{}, formString map[string]string) (*http.Response, error) {
ar := NewAPIRequest("POST", endpoint, payload)
if err := r.SetCrumb(ar); err != nil {
return nil, err
}
ar.SetHeader("Content-Type", "application/x-www-form-urlencoded")
ar.Suffix = ""
return r.DoPostForm(ar, responseStruct, formString)
}
func (r *Requester) PostFiles(endpoint string, payload io.Reader, responseStruct interface{}, querystring map[string]string, files []string) (*http.Response, error) {
ar := NewAPIRequest("POST", endpoint, payload)
if err := r.SetCrumb(ar); err != nil {
return nil, err
}
return r.Do(ar, responseStruct, querystring, files)
}
func (r *Requester) PostXML(endpoint string, xml string, responseStruct interface{}, querystring map[string]string) (*http.Response, error) {
payload := bytes.NewBuffer([]byte(xml))
ar := NewAPIRequest("POST", endpoint, payload)
if err := r.SetCrumb(ar); err != nil {
return nil, err
}
ar.SetHeader("Content-Type", "application/xml;charset=utf-8")
ar.Suffix = ""
return r.Do(ar, responseStruct, querystring)
}
func (r *Requester) GetJSON(endpoint string, responseStruct interface{}, query map[string]string) (*http.Response, error) {
ar := NewAPIRequest("GET", endpoint, nil)
ar.SetHeader("Content-Type", "application/json")
ar.Suffix = "api/json"
return r.Do(ar, responseStruct, query)
}
func (r *Requester) GetXML(endpoint string, responseStruct interface{}, query map[string]string) (*http.Response, error) {
ar := NewAPIRequest("GET", endpoint, nil)
ar.SetHeader("Content-Type", "application/xml")
ar.Suffix = ""
return r.Do(ar, responseStruct, query)
}
func (r *Requester) Get(endpoint string, responseStruct interface{}, querystring map[string]string) (*http.Response, error) {
ar := NewAPIRequest("GET", endpoint, nil)
ar.Suffix = ""
return r.Do(ar, responseStruct, querystring)
}
func (r *Requester) GetHtml(endpoint string, responseStruct interface{}, querystring map[string]string) (*http.Response, error) {
ar := NewAPIRequest("GET", endpoint, nil)
ar.Suffix = ""
return r.DoGet(ar, responseStruct, querystring)
}
func (r *Requester) SetClient(client *http.Client) *Requester {
r.Client = client
return r
}
//Add auth on redirect if required.
func (r *Requester) redirectPolicyFunc(req *http.Request, via []*http.Request) error {
if r.BasicAuth != nil {
req.SetBasicAuth(r.BasicAuth.Username, r.BasicAuth.Password)
}
return nil
}
func (r *Requester) DoGet(ar *APIRequest, responseStruct interface{}, options ...interface{}) (*http.Response, error) {
fileUpload := false
var files []string
URL, err := url.Parse(r.Base + ar.Endpoint + ar.Suffix)
if err != nil {
return nil, err
}
for _, o := range options {
switch v := o.(type) {
case map[string]string:
querystring := make(url.Values)
for key, val := range v {
querystring.Set(key, val)
}
URL.RawQuery = querystring.Encode()
case []string:
fileUpload = true
files = v
}
}
var req *http.Request
if fileUpload {
body := &bytes.Buffer{}
writer := multipart.NewWriter(body)
for _, file := range files {
fileData, err := os.Open(file)
if err != nil {
Error.Println(err.Error())
return nil, err
}
part, err := writer.CreateFormFile("file", filepath.Base(file))
if err != nil {
Error.Println(err.Error())
return nil, err
}
if _, err = io.Copy(part, fileData); err != nil {
return nil, err
}
defer fileData.Close()
}
var params map[string]string
json.NewDecoder(ar.Payload).Decode(&params)
for key, val := range params {
if err = writer.WriteField(key, val); err != nil {
return nil, err
}
}
if err = writer.Close(); err != nil {
return nil, err
}
req, err = http.NewRequest(ar.Method, URL.String(), body)
if err != nil {
return nil, err
}
req.Header.Set("Content-Type", writer.FormDataContentType())
} else {
req, err = http.NewRequest(ar.Method, URL.String(), ar.Payload)
if err != nil {
return nil, err
}
}
if r.BasicAuth != nil {
req.SetBasicAuth(r.BasicAuth.Username, r.BasicAuth.Password)
}
req.Close = true
req.Header.Add("Accept", "*/*")
for k := range ar.Headers {
req.Header.Add(k, ar.Headers.Get(k))
}
r.connControl <- struct{}{}
if response, err := r.Client.Do(req); err != nil {
<-r.connControl
return nil, err
} else {
<-r.connControl
errorText := response.Header.Get("X-Error")
if errorText != "" {
return nil, errors.New(errorText)
}
err := CheckResponse(response)
if err != nil {
return nil, err
}
switch responseStruct.(type) {
case *string:
return r.ReadRawResponse(response, responseStruct)
default:
return r.ReadJSONResponse(response, responseStruct)
}
}
}
func (r *Requester) Do(ar *APIRequest, responseStruct interface{}, options ...interface{}) (*http.Response, error) {
if !strings.HasSuffix(ar.Endpoint, "/") && ar.Method != "POST" {
ar.Endpoint += "/"
}
fileUpload := false
var files []string
URL, err := url.Parse(r.Base + ar.Endpoint + ar.Suffix)
if err != nil {
return nil, err
}
for _, o := range options {
switch v := o.(type) {
case map[string]string:
querystring := make(url.Values)
for key, val := range v {
querystring.Set(key, val)
}
URL.RawQuery = querystring.Encode()
case []string:
fileUpload = true
files = v
}
}
var req *http.Request
if fileUpload {
body := &bytes.Buffer{}
writer := multipart.NewWriter(body)
for _, file := range files {
fileData, err := os.Open(file)
if err != nil {
Error.Println(err.Error())
return nil, err
}
part, err := writer.CreateFormFile("file", filepath.Base(file))
if err != nil {
Error.Println(err.Error())
return nil, err
}
if _, err = io.Copy(part, fileData); err != nil {
return nil, err
}
defer fileData.Close()
}
var params map[string]string
json.NewDecoder(ar.Payload).Decode(&params)
for key, val := range params {
if err = writer.WriteField(key, val); err != nil {
return nil, err
}
}
if err = writer.Close(); err != nil {
return nil, err
}
req, err = http.NewRequest(ar.Method, URL.String(), body)
if err != nil {
return nil, err
}
req.Header.Set("Content-Type", writer.FormDataContentType())
} else {
req, err = http.NewRequest(ar.Method, URL.String(), ar.Payload)
if err != nil {
return nil, err
}
}
if r.BasicAuth != nil {
req.SetBasicAuth(r.BasicAuth.Username, r.BasicAuth.Password)
}
req.Close = true
req.Header.Add("Accept", "*/*")
for k := range ar.Headers {
req.Header.Add(k, ar.Headers.Get(k))
}
r.connControl <- struct{}{}
if response, err := r.Client.Do(req); err != nil {
<-r.connControl
return nil, err
} else {
<-r.connControl
errorText := response.Header.Get("X-Error")
if errorText != "" {
return nil, errors.New(errorText)
}
err := CheckResponse(response)
if err != nil {
return nil, err
}
switch responseStruct.(type) {
case *string:
return r.ReadRawResponse(response, responseStruct)
default:
return r.ReadJSONResponse(response, responseStruct)
}
}
}
func (r *Requester) DoPostForm(ar *APIRequest, responseStruct interface{}, form map[string]string) (*http.Response, error) {
if !strings.HasSuffix(ar.Endpoint, "/") && ar.Method != "POST" {
ar.Endpoint += "/"
}
URL, err := url.Parse(r.Base + ar.Endpoint + ar.Suffix)
if err != nil {
return nil, err
}
formValue := make(url.Values)
for k, v := range form {
formValue.Set(k, v)
}
req, err := http.NewRequest("POST", URL.String(), strings.NewReader(formValue.Encode()))
if r.BasicAuth != nil {
req.SetBasicAuth(r.BasicAuth.Username, r.BasicAuth.Password)
}
req.Close = true
req.Header.Add("Accept", "*/*")
for k := range ar.Headers {
req.Header.Add(k, ar.Headers.Get(k))
}
r.connControl <- struct{}{}
if response, err := r.Client.Do(req); err != nil {
<-r.connControl
return nil, err
} else {
<-r.connControl
errorText := response.Header.Get("X-Error")
if errorText != "" {
return nil, errors.New(errorText)
}
err := CheckResponse(response)
if err != nil {
return nil, err
}
switch responseStruct.(type) {
case *string:
return r.ReadRawResponse(response, responseStruct)
default:
return r.ReadJSONResponse(response, responseStruct)
}
}
}
func (r *Requester) ReadRawResponse(response *http.Response, responseStruct interface{}) (*http.Response, error) {
defer response.Body.Close()
content, err := ioutil.ReadAll(response.Body)
if err != nil {
return nil, err
}
if str, ok := responseStruct.(*string); ok {
*str = string(content)
} else {
return nil, fmt.Errorf("Could not cast responseStruct to *string")
}
return response, nil
}
func (r *Requester) ReadJSONResponse(response *http.Response, responseStruct interface{}) (*http.Response, error) {
defer response.Body.Close()
err := json.NewDecoder(response.Body).Decode(responseStruct)
if err != nil && err.Error() == "EOF" {
return response, nil
}
return response, nil
}
type ErrorResponse struct {
Body []byte
Response *http.Response
Message string
}
func (e *ErrorResponse) Error() string {
u := fmt.Sprintf("%s://%s%s", e.Response.Request.URL.Scheme, e.Response.Request.URL.Host, e.Response.Request.URL.RequestURI())
return fmt.Sprintf("%s %s: %d %s", e.Response.Request.Method, u, e.Response.StatusCode, e.Message)
}
func CheckResponse(r *http.Response) error {
switch r.StatusCode {
case http.StatusOK, http.StatusCreated, http.StatusAccepted, http.StatusNoContent, http.StatusFound, http.StatusNotModified:
return nil
}
defer r.Body.Close()
errorResponse := &ErrorResponse{Response: r}
data, err := ioutil.ReadAll(r.Body)
if err == nil && data != nil {
errorResponse.Body = data
errorResponse.Message = string(data)
}
return errorResponse
}

View File

@@ -0,0 +1,204 @@
package jenkins
import (
"errors"
"net/http"
"reflect"
"strconv"
"strings"
)
type GlobalRoleResponse struct {
RoleName string `json:"roleName"`
PermissionIds GlobalPermissionIds `json:"permissionIds"`
}
type GlobalRole struct {
Jenkins *Jenkins
Raw GlobalRoleResponse
}
type GlobalPermissionIds struct {
Administer bool `json:"hudson.model.Hudson.Administer"`
GlobalRead bool `json:"hudson.model.Hudson.Read"`
CredentialCreate bool `json:"com.cloudbees.plugins.credentials.CredentialsProvider.Create"`
CredentialUpdate bool `json:"com.cloudbees.plugins.credentials.CredentialsProvider.Update"`
CredentialView bool `json:"com.cloudbees.plugins.credentials.CredentialsProvider.View"`
CredentialDelete bool `json:"com.cloudbees.plugins.credentials.CredentialsProvider.Delete"`
CredentialManageDomains bool `json:"com.cloudbees.plugins.credentials.CredentialsProvider.ManageDomains"`
SlaveCreate bool `json:"hudson.model.Computer.Create"`
SlaveConfigure bool `json:"hudson.model.Computer.Configure"`
SlaveDelete bool `json:"hudson.model.Computer.Delete"`
SlaveBuild bool `json:"hudson.model.Computer.Build"`
SlaveConnect bool `json:"hudson.model.Computer.Connect"`
SlaveDisconnect bool `json:"hudson.model.Computer.Disconnect"`
ItemBuild bool `json:"hudson.model.Item.Build"`
ItemCreate bool `json:"hudson.model.Item.Create"`
ItemRead bool `json:"hudson.model.Item.Read"`
ItemConfigure bool `json:"hudson.model.Item.Configure"`
ItemCancel bool `json:"hudson.model.Item.Cancel"`
ItemMove bool `json:"hudson.model.Item.Move"`
ItemDiscover bool `json:"hudson.model.Item.Discover"`
ItemWorkspace bool `json:"hudson.model.Item.Workspace"`
ItemDelete bool `json:"hudson.model.Item.Delete"`
RunUpdate bool `json:"hudson.model.Run.Update"`
RunDelete bool `json:"hudson.model.Run.Delete"`
ViewCreate bool `json:"hudson.model.View.Create"`
ViewConfigure bool `json:"hudson.model.View.Configure"`
ViewRead bool `json:"hudson.model.View.Read"`
ViewDelete bool `json:"hudson.model.View.Delete"`
SCMTag bool `json:"hudson.scm.SCM.Tag"`
}
type ProjectRole struct {
Jenkins *Jenkins
Raw ProjectRoleResponse
}
type ProjectRoleResponse struct {
RoleName string `json:"roleName"`
PermissionIds ProjectPermissionIds `json:"permissionIds"`
Pattern string `json:"pattern"`
}
type ProjectPermissionIds struct {
CredentialCreate bool `json:"com.cloudbees.plugins.credentials.CredentialsProvider.Create"`
CredentialUpdate bool `json:"com.cloudbees.plugins.credentials.CredentialsProvider.Update"`
CredentialView bool `json:"com.cloudbees.plugins.credentials.CredentialsProvider.View"`
CredentialDelete bool `json:"com.cloudbees.plugins.credentials.CredentialsProvider.Delete"`
CredentialManageDomains bool `json:"com.cloudbees.plugins.credentials.CredentialsProvider.ManageDomains"`
ItemBuild bool `json:"hudson.model.Item.Build"`
ItemCreate bool `json:"hudson.model.Item.Create"`
ItemRead bool `json:"hudson.model.Item.Read"`
ItemConfigure bool `json:"hudson.model.Item.Configure"`
ItemCancel bool `json:"hudson.model.Item.Cancel"`
ItemMove bool `json:"hudson.model.Item.Move"`
ItemDiscover bool `json:"hudson.model.Item.Discover"`
ItemWorkspace bool `json:"hudson.model.Item.Workspace"`
ItemDelete bool `json:"hudson.model.Item.Delete"`
RunUpdate bool `json:"hudson.model.Run.Update"`
RunDelete bool `json:"hudson.model.Run.Delete"`
RunReplay bool `json:"hudson.model.Run.Replay"`
SCMTag bool `json:"hudson.scm.SCM.Tag"`
}
func (j *GlobalRole) Update(ids GlobalPermissionIds) error {
var idArray []string
values := reflect.ValueOf(ids)
for i := 0; i < values.NumField(); i++ {
field := values.Field(i)
if field.Bool() {
idArray = append(idArray, values.Type().Field(i).Tag.Get("json"))
}
}
param := map[string]string{
"roleName": j.Raw.RoleName,
"type": GLOBAL_ROLE,
"permissionIds": strings.Join(idArray, ","),
"overwrite": strconv.FormatBool(true),
}
responseString := ""
response, err := j.Jenkins.Requester.Post("/role-strategy/strategy/addRole", nil, &responseString, param)
if err != nil {
return err
}
if response.StatusCode != http.StatusOK {
return errors.New(strconv.Itoa(response.StatusCode))
}
return nil
}
func (j *GlobalRole) AssignRole(sid string) error {
param := map[string]string{
"type": GLOBAL_ROLE,
"roleName": j.Raw.RoleName,
"sid": sid,
}
responseString := ""
response, err := j.Jenkins.Requester.Post("/role-strategy/strategy/assignRole", nil, &responseString, param)
if err != nil {
return err
}
if response.StatusCode != http.StatusOK {
return errors.New(strconv.Itoa(response.StatusCode))
}
return nil
}
func (j *GlobalRole) UnAssignRole(sid string) error {
param := map[string]string{
"type": GLOBAL_ROLE,
"roleName": j.Raw.RoleName,
"sid": sid,
}
responseString := ""
response, err := j.Jenkins.Requester.Post("/role-strategy/strategy/unassignRole", nil, &responseString, param)
if err != nil {
return err
}
if response.StatusCode != http.StatusOK {
return errors.New(strconv.Itoa(response.StatusCode))
}
return nil
}
func (j *ProjectRole) Update(pattern string, ids ProjectPermissionIds) error {
var idArray []string
values := reflect.ValueOf(ids)
for i := 0; i < values.NumField(); i++ {
field := values.Field(i)
if field.Bool() {
idArray = append(idArray, values.Type().Field(i).Tag.Get("json"))
}
}
param := map[string]string{
"roleName": j.Raw.RoleName,
"type": PROJECT_ROLE,
"permissionIds": strings.Join(idArray, ","),
"overwrite": strconv.FormatBool(true),
"pattern": pattern,
}
responseString := ""
response, err := j.Jenkins.Requester.Post("/role-strategy/strategy/addRole", nil, &responseString, param)
if err != nil {
return err
}
if response.StatusCode != http.StatusOK {
return errors.New(strconv.Itoa(response.StatusCode))
}
return nil
}
func (j *ProjectRole) AssignRole(sid string) error {
param := map[string]string{
"type": PROJECT_ROLE,
"roleName": j.Raw.RoleName,
"sid": sid,
}
responseString := ""
response, err := j.Jenkins.Requester.Post("/role-strategy/strategy/assignRole", nil, &responseString, param)
if err != nil {
return err
}
if response.StatusCode != http.StatusOK {
return errors.New(strconv.Itoa(response.StatusCode))
}
return nil
}
func (j *ProjectRole) UnAssignRole(sid string) error {
param := map[string]string{
"type": PROJECT_ROLE,
"roleName": j.Raw.RoleName,
"sid": sid,
}
responseString := ""
response, err := j.Jenkins.Requester.Post("/role-strategy/strategy/unassignRole", nil, &responseString, param)
if err != nil {
return err
}
if response.StatusCode != http.StatusOK {
return errors.New(strconv.Itoa(response.StatusCode))
}
return nil
}

View File

@@ -0,0 +1,146 @@
// 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 (
"compress/gzip"
"encoding/json"
"github.com/asaskevich/govalidator"
"io"
"io/ioutil"
"k8s.io/klog"
"net/http"
"net/url"
"strconv"
"strings"
"time"
"unicode/utf8"
)
func makeJson(data interface{}) string {
str, err := json.Marshal(data)
if err != nil {
return ""
}
return string(json.RawMessage(str))
}
func Reverse(s string) string {
size := len(s)
buf := make([]byte, size)
for start := 0; start < size; {
r, n := utf8.DecodeRuneInString(s[start:])
start += n
utf8.EncodeRune(buf[size-start:], r)
}
return string(buf)
}
type JkError struct {
Message string `json:"message"`
Code int `json:"code"`
}
func (err *JkError) Error() string {
return err.Message
}
// Decompress response.body of JenkinsAPIResponse
func getRespBody(resp *http.Response) ([]byte, error) {
var reader io.ReadCloser
if resp.Header.Get("Content-Encoding") == "gzip" {
reader, _ = gzip.NewReader(resp.Body)
} else {
reader = resp.Body
}
resBody, err := ioutil.ReadAll(reader)
if err != nil {
klog.Error(err)
return nil, err
}
return resBody, err
}
// parseJenkinsQuery Parse the special query of jenkins.
// ParseQuery in the standard library makes the query not re-encode
func parseJenkinsQuery(query string) (url.Values, error) {
m := make(url.Values)
err := error(nil)
for query != "" {
key := query
if i := strings.IndexAny(key, "&"); i >= 0 {
key, query = key[:i], key[i+1:]
} else {
query = ""
}
if key == "" {
continue
}
value := ""
if i := strings.Index(key, "="); i >= 0 {
key, value = key[:i], key[i+1:]
}
key, err1 := url.QueryUnescape(key)
if err1 != nil {
if err == nil {
err = err1
}
continue
}
value, err1 = url.QueryUnescape(value)
if err1 != nil {
if err == nil {
err = err1
}
continue
}
m[key] = append(m[key], value)
}
return m, err
}
type JenkinsBlueTime time.Time
func (t *JenkinsBlueTime) UnmarshalJSON(b []byte) error {
if b == nil || strings.Trim(string(b), "\"") == "null" {
*t = JenkinsBlueTime(time.Time{})
return nil
}
j, err := time.Parse("2006-01-02T15:04:05.000-0700", strings.Trim(string(b), "\""))
if err != nil {
return err
}
*t = JenkinsBlueTime(j)
return nil
}
func (t JenkinsBlueTime) MarshalJSON() ([]byte, error) {
return json.Marshal(time.Time(t))
}
func GetJenkinsStatusCode(jenkinsErr error) int {
if code, err := strconv.Atoi(jenkinsErr.Error()); err == nil {
message := http.StatusText(code)
if !govalidator.IsNull(message) {
return code
}
}
if jErr, ok := jenkinsErr.(*ErrorResponse); ok {
return jErr.Response.StatusCode
}
return http.StatusInternalServerError
}

View File

@@ -0,0 +1,48 @@
package devops
type ProjectMembership struct {
Username string `json:"username" description:"Member's usernameusername can uniquely identify a user"`
ProjectId string `json:"project_id" db:"project_id" description:"the DevOps Projects which project membership belongs to"`
Role string `json:"role" description:"DevOps Project membership's role type. e.g. owner '"`
Status string `json:"status" description:"Deprecated, Status of project membership. e.g. active "`
GrantBy string `json:"grand_by,omitempty" description:"Username of the user who assigned the role"`
}
type ProjectMemberOperator interface {
AddProjectMember(membership *ProjectMembership) (*ProjectMembership, error)
UpdateProjectMember(oldMembership, newMembership *ProjectMembership) (*ProjectMembership, error)
DeleteProjectMember(membership *ProjectMembership) (*ProjectMembership, error)
}
var DefaultRoles = []*Role{
{
Name: ProjectOwner,
Description: "Owner have access to do all the operations of a DevOps project and own the highest permissions as well.",
},
{
Name: ProjectMaintainer,
Description: "Maintainer have access to manage pipeline and credential configuration in a DevOps project.",
},
{
Name: ProjectDeveloper,
Description: "Developer is able to view and trigger the pipeline.",
},
{
Name: ProjectReporter,
Description: "Reporter is only allowed to view the status of the pipeline.",
},
}
var AllRoleSlice = []string{ProjectDeveloper, ProjectReporter, ProjectMaintainer, ProjectOwner}
const (
ProjectOwner = "owner"
ProjectMaintainer = "maintainer"
ProjectDeveloper = "developer"
ProjectReporter = "reporter"
)
type Role struct {
Name string `json:"name" description:"role's name e.g. owner'"`
Description string `json:"description" description:"role 's description'"`
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,8 @@
package devops
import "kubesphere.io/kubesphere/pkg/api/devops/v1alpha2"
type ProjectOperator interface {
CreateDevOpsProject(username string, project *v1alpha2.DevOpsProject) (*v1alpha2.DevOpsProject, error)
DeleteDevOpsProject(projectId string) error
}

View File

@@ -0,0 +1,144 @@
package devops
const (
NoScmPipelineType = "pipeline"
MultiBranchPipelineType = "multi-branch-pipeline"
)
type Parameters []*Parameter
var ParameterTypeMap = map[string]string{
"hudson.model.StringParameterDefinition": "string",
"hudson.model.ChoiceParameterDefinition": "choice",
"hudson.model.TextParameterDefinition": "text",
"hudson.model.BooleanParameterDefinition": "boolean",
"hudson.model.FileParameterDefinition": "file",
"hudson.model.PasswordParameterDefinition": "password",
}
type ProjectPipeline struct {
Type string `json:"type" description:"type of devops pipeline, in scm or no scm"`
Pipeline *NoScmPipeline `json:"pipeline,omitempty" description:"no scm pipeline structs"`
MultiBranchPipeline *MultiBranchPipeline `json:"multi_branch_pipeline,omitempty" description:"in scm pipeline structs"`
}
type NoScmPipeline struct {
Name string `json:"name" description:"name of pipeline"`
Description string `json:"descriptio,omitempty" description:"description of pipeline"`
Discarder *DiscarderProperty `json:"discarder,omitempty" description:"Discarder of pipeline, managing when to drop a pipeline"`
Parameters *Parameters `json:"parameters,omitempty" description:"Parameters define of pipeline,user could pass param when run pipeline"`
DisableConcurrent bool `json:"disable_concurrent,omitempty" mapstructure:"disable_concurrent" description:"Whether to prohibit the pipeline from running in parallel"`
TimerTrigger *TimerTrigger `json:"timer_trigger,omitempty" mapstructure:"timer_trigger" description:"Timer to trigger pipeline run"`
RemoteTrigger *RemoteTrigger `json:"remote_trigger,omitempty" mapstructure:"remote_trigger" description:"Remote api define to trigger pipeline run"`
Jenkinsfile string `json:"jenkinsfile,omitempty" description:"Jenkinsfile's content'"`
}
type MultiBranchPipeline struct {
Name string `json:"name" description:"name of pipeline"`
Description string `json:"descriptio,omitempty" description:"description of pipeline"`
Discarder *DiscarderProperty `json:"discarder,omitempty" description:"Discarder of pipeline, managing when to drop a pipeline"`
TimerTrigger *TimerTrigger `json:"timer_trigger,omitempty" mapstructure:"timer_trigger" description:"Timer to trigger pipeline run"`
SourceType string `json:"source_type" description:"type of scm, such as github/git/svn"`
GitSource *GitSource `json:"git_source,omitempty" description:"git scm define"`
GitHubSource *GithubSource `json:"github_source,omitempty" description:"github scm define"`
SvnSource *SvnSource `json:"svn_source,omitempty" description:"multi branch svn scm define"`
SingleSvnSource *SingleSvnSource `json:"single_svn_source,omitempty" description:"single branch svn scm define"`
BitbucketServerSource *BitbucketServerSource `json:"bitbucket_server_source,omitempty" description:"bitbucket server scm defile"`
ScriptPath string `json:"script_path" mapstructure:"script_path" description:"script path in scm"`
MultiBranchJobTrigger *MultiBranchJobTrigger `json:"multibranch_job_trigger,omitempty" mapstructure:"multibranch_job_trigger" description:"Pipeline tasks that need to be triggered when branch creation/deletion"`
}
type GitSource struct {
ScmId string `json:"scm_id,omitempty" description:"uid of scm"`
Url string `json:"url,omitempty" mapstructure:"url" description:"url of git source"`
CredentialId string `json:"credential_id,omitempty" mapstructure:"credential_id" description:"credential id to access git source"`
DiscoverBranches bool `json:"discover_branches,omitempty" mapstructure:"discover_branches" description:"Whether to discover a branch"`
CloneOption *GitCloneOption `json:"git_clone_option,omitempty" mapstructure:"git_clone_option" description:"advavced git clone options"`
RegexFilter string `json:"regex_filter,omitempty" mapstructure:"regex_filter" description:"Regex used to match the name of the branch that needs to be run"`
}
type GithubSource struct {
ScmId string `json:"scm_id,omitempty" description:"uid of scm"`
Owner string `json:"owner,omitempty" mapstructure:"owner" description:"owner of github repo"`
Repo string `json:"repo,omitempty" mapstructure:"repo" description:"repo name of github repo"`
CredentialId string `json:"credential_id,omitempty" mapstructure:"credential_id" description:"credential id to access github source"`
ApiUri string `json:"api_uri,omitempty" mapstructure:"api_uri" description:"The api url can specify the location of the github apiserver.For private cloud configuration"`
DiscoverBranches int `json:"discover_branches,omitempty" mapstructure:"discover_branches" description:"Discover branch configuration"`
DiscoverPRFromOrigin int `json:"discover_pr_from_origin,omitempty" mapstructure:"discover_pr_from_origin" description:"Discover origin PR configuration"`
DiscoverPRFromForks *DiscoverPRFromForks `json:"discover_pr_from_forks,omitempty" mapstructure:"discover_pr_from_forks" description:"Discover fork PR configuration"`
CloneOption *GitCloneOption `json:"git_clone_option,omitempty" mapstructure:"git_clone_option" description:"advavced git clone options"`
RegexFilter string `json:"regex_filter,omitempty" mapstructure:"regex_filter" description:"Regex used to match the name of the branch that needs to be run"`
}
type MultiBranchJobTrigger struct {
CreateActionJobsToTrigger string `json:"create_action_job_to_trigger,omitempty" description:"pipeline name to trigger"`
DeleteActionJobsToTrigger string `json:"delete_action_job_to_trigger,omitempty" description:"pipeline name to trigger"`
}
type BitbucketServerSource struct {
ScmId string `json:"scm_id,omitempty" description:"uid of scm"`
Owner string `json:"owner,omitempty" mapstructure:"owner" description:"owner of github repo"`
Repo string `json:"repo,omitempty" mapstructure:"repo" description:"repo name of github repo"`
CredentialId string `json:"credential_id,omitempty" mapstructure:"credential_id" description:"credential id to access github source"`
ApiUri string `json:"api_uri,omitempty" mapstructure:"api_uri" description:"The api url can specify the location of the github apiserver.For private cloud configuration"`
DiscoverBranches int `json:"discover_branches,omitempty" mapstructure:"discover_branches" description:"Discover branch configuration"`
DiscoverPRFromOrigin int `json:"discover_pr_from_origin,omitempty" mapstructure:"discover_pr_from_origin" description:"Discover origin PR configuration"`
DiscoverPRFromForks *DiscoverPRFromForks `json:"discover_pr_from_forks,omitempty" mapstructure:"discover_pr_from_forks" description:"Discover fork PR configuration"`
CloneOption *GitCloneOption `json:"git_clone_option,omitempty" mapstructure:"git_clone_option" description:"advavced git clone options"`
RegexFilter string `json:"regex_filter,omitempty" mapstructure:"regex_filter" description:"Regex used to match the name of the branch that needs to be run"`
}
type GitCloneOption struct {
Shallow bool `json:"shallow,omitempty" mapstructure:"shallow" description:"Whether to use git shallow clone"`
Timeout int `json:"timeout,omitempty" mapstructure:"timeout" description:"git clone timeout mins"`
Depth int `json:"depth,omitempty" mapstructure:"depth" description:"git clone depth"`
}
type SvnSource struct {
ScmId string `json:"scm_id,omitempty" description:"uid of scm"`
Remote string `json:"remote,omitempty" description:"remote address url"`
CredentialId string `json:"credential_id,omitempty" mapstructure:"credential_id" description:"credential id to access svn source"`
Includes string `json:"includes,omitempty" description:"branches to run pipeline"`
Excludes string `json:"excludes,omitempty" description:"branches do not run pipeline"`
}
type SingleSvnSource struct {
ScmId string `json:"scm_id,omitempty" description:"uid of scm"`
Remote string `json:"remote,omitempty" description:"remote address url"`
CredentialId string `json:"credential_id,omitempty" mapstructure:"credential_id" description:"credential id to access svn source"`
}
type DiscoverPRFromForks struct {
Strategy int `json:"strategy,omitempty" mapstructure:"strategy" description:"github discover strategy"`
Trust int `json:"trust,omitempty" mapstructure:"trust" description:"trust user type"`
}
type DiscarderProperty struct {
DaysToKeep string `json:"days_to_keep,omitempty" mapstructure:"days_to_keep" description:"days to keep pipeline"`
NumToKeep string `json:"num_to_keep,omitempty" mapstructure:"num_to_keep" description:"nums to keep pipeline"`
}
type Parameter struct {
Name string `json:"name" description:"name of param"`
DefaultValue string `json:"default_value,omitempty" mapstructure:"default_value" description:"default value of param"`
Type string `json:"type" description:"type of param"`
Description string `json:"description,omitempty" description:"description of pipeline"`
}
type TimerTrigger struct {
// user in no scm job
Cron string `json:"cron,omitempty" description:"jenkins cron script"`
// use in multi-branch job
Interval string `json:"interval,omitempty" description:"interval ms"`
}
type RemoteTrigger struct {
Token string `json:"token,omitempty" description:"remote trigger token"`
}
type ProjectPipelineOperator interface {
CreateProjectPipeline(projectId string, pipeline *ProjectPipeline) (string, error)
DeleteProjectPipeline(projectId string, pipelineId string) (string, error)
UpdateProjectPipeline(projectId string, pipeline *ProjectPipeline) (string, error)
GetProjectPipelineConfig(projectId, pipelineId string) (*ProjectPipeline, error)
}

View File

@@ -4,6 +4,7 @@ import (
"errors"
"kubesphere.io/kubesphere/pkg/simple/client/cache"
"kubesphere.io/kubesphere/pkg/simple/client/devops"
"kubesphere.io/kubesphere/pkg/simple/client/devops/jenkins"
esclient "kubesphere.io/kubesphere/pkg/simple/client/elasticsearch"
"kubesphere.io/kubesphere/pkg/simple/client/k8s"
"kubesphere.io/kubesphere/pkg/simple/client/kubesphere"
@@ -22,7 +23,7 @@ type ClientSetOptions struct {
mySQLOptions *mysql.Options
redisOptions *cache.Options
kubernetesOptions *k8s.KubernetesOptions
devopsOptions *devops.Options
devopsOptions *jenkins.Options
sonarqubeOptions *sonarqube.Options
ldapOptions *ldap.Options
s3Options *s3.Options
@@ -38,7 +39,7 @@ func NewClientSetOptions() *ClientSetOptions {
redisOptions: cache.NewRedisOptions(),
kubernetesOptions: k8s.NewKubernetesOptions(),
ldapOptions: ldap.NewLdapOptions(),
devopsOptions: devops.NewDevopsOptions(),
devopsOptions: jenkins.NewDevopsOptions(),
sonarqubeOptions: sonarqube.NewSonarQubeOptions(),
s3Options: s3.NewS3Options(),
openPitrixOptions: openpitrix.NewOpenPitrixOptions(),
@@ -63,7 +64,7 @@ func (c *ClientSetOptions) SetKubernetesOptions(options *k8s.KubernetesOptions)
return c
}
func (c *ClientSetOptions) SetDevopsOptions(options *devops.Options) *ClientSetOptions {
func (c *ClientSetOptions) SetDevopsOptions(options *jenkins.Options) *ClientSetOptions {
c.devopsOptions = options
return c
}
@@ -114,7 +115,7 @@ type ClientSet struct {
k8sClient k8s.Client
ldapClient *ldap.Client
devopsClient *devops.Client
devopsClient *jenkins.Client
sonarQubeClient *sonarqube.Client
redisClient cache.Interface
s3Client s3.Interface
@@ -194,27 +195,28 @@ func (cs *ClientSet) Cache() (cache.Interface, error) {
}
}
func (cs *ClientSet) Devops() (*devops.Client, error) {
var err error
if cs.csoptions.devopsOptions == nil || cs.csoptions.devopsOptions.Host == "" {
return nil, ErrClientSetNotEnabled
}
if cs.devopsClient != nil {
return cs.devopsClient, nil
} else {
mutex.Lock()
defer mutex.Unlock()
if cs.devopsClient == nil {
cs.devopsClient, err = devops.NewDevopsClient(cs.csoptions.devopsOptions)
if err != nil {
return nil, err
}
}
return cs.devopsClient, nil
}
func (cs *ClientSet) Devops() (devops.Interface, error) {
//var err error
//
//if cs.csoptions.devopsOptions == nil || cs.csoptions.devopsOptions.Host == "" {
// return nil, ErrClientSetNotEnabled
//}
//
//if cs.devopsClient != nil {
// return cs.devopsClient, nil
//} else {
// mutex.Lock()
// defer mutex.Unlock()
//
// if cs.devopsClient == nil {
// cs.devopsClient, err = jenkins.NewDevopsClient(cs.csoptions.devopsOptions)
// if err != nil {
// return nil, err
// }
// }
// return cs.devopsClient, nil
//}
return nil, nil
}
func (cs *ClientSet) SonarQube() (*sonarqube.Client, error) {

View File

@@ -1,39 +1,32 @@
package monitoring
type ClusterQuery struct {
}
type ClusterMetrics struct {
}
type WorkspaceQuery struct {
}
type WorkspaceMetrics struct {
}
type NamespaceQuery struct {
}
type NamespaceMetrics struct {
}
// Interface defines all the abstract behaviors of monitoring
type Interface interface {
// Get
GetClusterMetrics(query ClusterQuery) ClusterMetrics
// Get
GetClusterMetrics(query ClusterQuery) ClusterMetrics
//
GetWorkspaceMetrics(query WorkspaceQuery) WorkspaceMetrics
//
GetWorkspaceMetrics(query WorkspaceQuery) WorkspaceMetrics
//
GetNamespaceMetrics(query NamespaceQuery) NamespaceMetrics
//
GetNamespaceMetrics(query NamespaceQuery) NamespaceMetrics
}

View File

@@ -1,31 +1,31 @@
package monitoring
import (
"net/http"
"time"
"net/http"
"time"
)
// prometheus implements monitoring interface backed by Prometheus
type prometheus struct {
options *Options
client *http.Client
options *Options
client *http.Client
}
func NewPrometheus(options *Options) Interface {
return &prometheus{
options:options,
client: &http.Client{ Timeout: 10 * time.Second },
}
return &prometheus{
options: options,
client: &http.Client{Timeout: 10 * time.Second},
}
}
func (p prometheus) GetClusterMetrics(query ClusterQuery) ClusterMetrics {
panic("implement me")
panic("implement me")
}
func (p prometheus) GetWorkspaceMetrics(query WorkspaceQuery) WorkspaceMetrics {
panic("implement me")
panic("implement me")
}
func (p prometheus) GetNamespaceMetrics(query NamespaceQuery) NamespaceMetrics {
panic("implement me")
panic("implement me")
}

View File

@@ -1,41 +1,41 @@
package monitoring
import (
"github.com/spf13/pflag"
"github.com/spf13/pflag"
)
type Options struct {
Endpoint string `json:"endpoint,omitempty" yaml:"endpoint"`
SecondaryEndpoint string `json:"secondaryEndpoint,omitempty" yaml:"secondaryEndpoint"`
Endpoint string `json:"endpoint,omitempty" yaml:"endpoint"`
SecondaryEndpoint string `json:"secondaryEndpoint,omitempty" yaml:"secondaryEndpoint"`
}
func NewPrometheusOptions() *Options {
return &Options{
Endpoint: "",
SecondaryEndpoint: "",
}
return &Options{
Endpoint: "",
SecondaryEndpoint: "",
}
}
func (s *Options) Validate() []error {
var errs []error
return errs
var errs []error
return errs
}
func (s *Options) ApplyTo(options *Options) {
if s.Endpoint != "" {
options.Endpoint = s.Endpoint
}
if s.Endpoint != "" {
options.Endpoint = s.Endpoint
}
if s.SecondaryEndpoint != "" {
options.SecondaryEndpoint = s.SecondaryEndpoint
}
if s.SecondaryEndpoint != "" {
options.SecondaryEndpoint = s.SecondaryEndpoint
}
}
func (s *Options) AddFlags(fs *pflag.FlagSet, c *Options) {
fs.StringVar(&s.Endpoint, "prometheus-endpoint", c.Endpoint, ""+
"Prometheus service endpoint which stores KubeSphere monitoring data, if left "+
"blank, will use builtin metrics-server as data source.")
fs.StringVar(&s.Endpoint, "prometheus-endpoint", c.Endpoint, ""+
"Prometheus service endpoint which stores KubeSphere monitoring data, if left "+
"blank, will use builtin metrics-server as data source.")
fs.StringVar(&s.SecondaryEndpoint, "prometheus-secondary-endpoint", c.SecondaryEndpoint, ""+
"Prometheus secondary service endpoint, if left empty and endpoint is set, will use endpoint instead.")
fs.StringVar(&s.SecondaryEndpoint, "prometheus-secondary-endpoint", c.SecondaryEndpoint, ""+
"Prometheus secondary service endpoint, if left empty and endpoint is set, will use endpoint instead.")
}

View File

@@ -27,7 +27,6 @@ import (
"time"
)
type Client struct {
client *http.Client
endpoint string

View File

@@ -0,0 +1,43 @@
package fake
import (
"fmt"
"github.com/aws/aws-sdk-go/aws/awserr"
"github.com/aws/aws-sdk-go/service/s3"
"io"
)
type FakeS3 struct {
Storage map[string]*object
}
func NewFakeS3() *FakeS3 {
return &FakeS3{Storage: map[string]*object{}}
}
type object struct {
key string
fileName string
body io.Reader
}
func (s *FakeS3) Upload(key, fileName string, body io.Reader) error {
s.Storage[key] = &object{
key: key,
fileName: fileName,
body: body,
}
return nil
}
func (s *FakeS3) GetDownloadURL(key string, fileName string) (string, error) {
if o, ok := s.Storage[key]; ok {
return fmt.Sprintf("http://%s/%s", o.key, fileName), nil
}
return "", awserr.New(s3.ErrCodeNoSuchKey, "no such object", nil)
}
func (s *FakeS3) Delete(key string) error {
delete(s.Storage, key)
return nil
}

View File

@@ -0,0 +1,52 @@
package fake
import (
"fmt"
"testing"
)
func TestFakeS3(t *testing.T) {
s3 := NewFakeS3()
key := "hello"
fileName := "world"
err := s3.Upload(key, fileName, nil)
if err != nil {
t.Fatal(err)
}
o, ok := s3.storage["hello"]
if !ok {
t.Fatal("should have hello object")
}
if o.key != key || o.fileName != fileName {
t.Fatalf("key should be %s, fileName should be %s", key, fileName)
}
url, err := s3.GetDownloadURL(key, fileName+"1")
if err != nil {
t.Fatal(err)
}
if url != fmt.Sprintf("http://%s/%s", key, fileName+"1") {
t.Fatalf("url should be %s", fmt.Sprintf("http://%s/%s", key, fileName+"1"))
}
url, err = s3.GetDownloadURL(key, fileName+"2")
if err != nil {
t.Fatal(err)
}
if url != fmt.Sprintf("http://%s/%s", key, fileName+"2") {
t.Fatalf("url should be %s", fmt.Sprintf("http://%s/%s", key, fileName+"2"))
}
err = s3.Delete(key)
if err != nil {
t.Fatal(err)
}
_, ok = s3.storage["hello"]
if ok {
t.Fatal("should not have hello object")
}
err = s3.Delete(key)
if err != nil {
t.Fatal(err)
}
}

View File

@@ -2,15 +2,13 @@ package s3
import (
"io"
"time"
)
type Interface interface {
// Upload uploads a object to storage and returns object location if succeeded
Upload(key string, body io.Reader) (string, error)
Upload(key, fileName string, body io.Reader) error
// Get retrieves and object's downloadable location if succeeded
Get(key string, fileName string, expire time.Duration) (string, error)
GetDownloadURL(key string, fileName string) (string, error)
// Delete deletes an object by its key
Delete(key string) error

View File

@@ -1,10 +1,13 @@
package s3
import (
"code.cloudfoundry.org/bytefmt"
"fmt"
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/credentials"
"github.com/aws/aws-sdk-go/aws/session"
"github.com/aws/aws-sdk-go/service/s3"
"github.com/aws/aws-sdk-go/service/s3/s3manager"
"io"
"k8s.io/klog"
"time"
@@ -16,16 +19,38 @@ type Client struct {
bucket string
}
func (s *Client) Upload(key string, body io.Reader) (string, error) {
panic("implement me")
func (s *Client) Upload(key, fileName string, body io.Reader) error {
uploader := s3manager.NewUploader(s.s3Session, func(uploader *s3manager.Uploader) {
uploader.PartSize = 5 * bytefmt.MEGABYTE
uploader.LeavePartsOnError = true
})
_, err := uploader.Upload(&s3manager.UploadInput{
Bucket: aws.String(s.bucket),
Key: aws.String(key),
Body: body,
ContentDisposition: aws.String(fmt.Sprintf("attachment; filename=\"%s\"", fileName)),
})
return err
}
func (s *Client) Get(key string, fileName string, expire time.Duration) (string, error) {
panic("implement me")
func (s *Client) GetDownloadURL(key string, fileName string) (string, error) {
req, _ := s.s3Client.GetObjectRequest(&s3.GetObjectInput{
Bucket: aws.String(s.bucket),
Key: aws.String(key),
ResponseContentDisposition: aws.String(fmt.Sprintf("attachment; filename=\"%s\"", fileName)),
})
return req.Presign(5 * time.Minute)
}
func (s *Client) Delete(key string) error {
panic("implement me")
_, err := s.s3Client.DeleteObject(
&s3.DeleteObjectInput{Bucket: aws.String(s.bucket),
Key: aws.String(key),
})
if err != nil {
return err
}
return nil
}
func NewS3Client(options *Options) (Interface, error) {
@@ -55,7 +80,6 @@ func NewS3Client(options *Options) (Interface, error) {
}
func (s *Client) Client() *s3.S3 {
return s.s3Client
}
func (s *Client) Session() *session.Session {

View File

@@ -1,8 +1,68 @@
package sonarqube
type Interface interface {
//
GetIssues()
import (
sonargo "github.com/kubesphere/sonargo/sonar"
"k8s.io/klog"
"kubesphere.io/kubesphere/pkg/simple/client/devops"
)
//
type SonarInterface interface {
GetSonarResultsByTaskIds(taskId ...string) ([]*SonarStatus, error)
}
type sonarQube struct {
client *sonargo.Client
}
const (
SonarAnalysisActionClass = "hudson.plugins.sonar.action.SonarAnalysisAction"
SonarMetricKeys = "alert_status,quality_gate_details,bugs,new_bugs,reliability_rating,new_reliability_rating,vulnerabilities,new_vulnerabilities,security_rating,new_security_rating,code_smells,new_code_smells,sqale_rating,new_maintainability_rating,sqale_index,new_technical_debt,coverage,new_coverage,new_lines_to_cover,tests,duplicated_lines_density,new_duplicated_lines_density,duplicated_blocks,ncloc,ncloc_language_distribution,projects,new_lines"
SonarAdditionalFields = "metrics,periods"
)
type SonarStatus struct {
Measures *sonargo.MeasuresComponentObject `json:"measures,omitempty"`
Issues *sonargo.IssuesSearchObject `json:"issues,omitempty"`
GeneralAction *devops.GeneralAction `json:"generalAction,omitempty"`
Task *sonargo.CeTaskObject `json:"task,omitempty"`
}
func (s *sonarQube) GetSonarResultsByTaskIds(taskIds ...string) ([]*SonarStatus, error) {
sonarStatuses := make([]*SonarStatus, 0)
for _, taskId := range taskIds {
sonarStatus := &SonarStatus{}
taskOptions := &sonargo.CeTaskOption{
Id: taskId,
}
ceTask, _, err := s.client.Ce.Task(taskOptions)
if err != nil {
klog.Errorf("get sonar task error [%+v]", err)
continue
}
sonarStatus.Task = ceTask
measuresComponentOption := &sonargo.MeasuresComponentOption{
Component: ceTask.Task.ComponentKey,
AdditionalFields: SonarAdditionalFields,
MetricKeys: SonarMetricKeys,
}
measures, _, err := s.client.Measures.Component(measuresComponentOption)
if err != nil {
klog.Errorf("get sonar task error [%+v]", err)
continue
}
sonarStatus.Measures = measures
issuesSearchOption := &sonargo.IssuesSearchOption{
AdditionalFields: "_all",
ComponentKeys: ceTask.Task.ComponentKey,
Resolved: "false",
Ps: "10",
S: "FILE_LINE",
Facets: "severities,types",
}
issuesSearch, _, err := s.client.Issues.Search(issuesSearchOption)
sonarStatus.Issues = issuesSearch
sonarStatuses = append(sonarStatuses, sonarStatus)
}
return sonarStatuses, nil
}