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

@@ -14,13 +14,7 @@ limitations under the License.
package devops
import (
"fmt"
"github.com/fatih/structs"
"k8s.io/klog"
"kubesphere.io/kubesphere/pkg/db"
"kubesphere.io/kubesphere/pkg/gojenkins"
"kubesphere.io/kubesphere/pkg/simple/client"
"kubesphere.io/kubesphere/pkg/utils/reflectutils"
"kubesphere.io/kubesphere/pkg/utils/stringutils"
)
@@ -67,298 +61,3 @@ const (
const (
KS_ADMIN = "admin"
)
const (
ProjectOwner = "owner"
ProjectMaintainer = "maintainer"
ProjectDeveloper = "developer"
ProjectReporter = "reporter"
)
const (
JenkinsAllUserRoleName = "kubesphere-user"
)
type Role struct {
Name string `json:"name" description:"role's name e.g. owner'"`
Description string `json:"description" description:"role 's description'"`
}
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}
var JenkinsOwnerProjectPermissionIds = &gojenkins.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]gojenkins.ProjectPermissionIds{
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,
},
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,
},
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,
},
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]gojenkins.ProjectPermissionIds{
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,
},
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,
},
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,
},
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 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)
}
func CheckProjectUserInRole(username, projectId string, roles []string) error {
if username == KS_ADMIN {
return nil
}
dbconn, err := client.ClientSets().MySQL()
if err != nil {
if _, ok := err.(client.ClientSetNotEnabledError); ok {
klog.Error("mysql is not enabled")
} else {
klog.Error("error creating mysql client", err)
}
return nil
}
membership := &DevOpsProjectMembership{}
err = dbconn.Select(DevOpsProjectMembershipColumns...).
From(DevOpsProjectMembershipTableName).
Where(db.And(
db.Eq(DevOpsProjectMembershipUsernameColumn, username),
db.Eq(DevOpsProjectMembershipProjectIdColumn, projectId))).LoadOne(membership)
if err != nil {
return err
}
if !reflectutils.In(membership.Role, roles) {
return fmt.Errorf("user [%s] in project [%s] role is not in %s", username, projectId, roles)
}
return nil
}
func GetProjectUserRole(username, projectId string) (string, error) {
if username == KS_ADMIN {
return ProjectOwner, nil
}
dbconn, err := client.ClientSets().MySQL()
if err != nil {
if _, ok := err.(client.ClientSetNotEnabledError); ok {
klog.Error("mysql is not enabled")
} else {
klog.Error("error creating mysql client", err)
}
return "", err
}
membership := &DevOpsProjectMembership{}
err = dbconn.Select(DevOpsProjectMembershipColumns...).
From(DevOpsProjectMembershipTableName).
Where(db.And(
db.Eq(DevOpsProjectMembershipUsernameColumn, username),
db.Eq(DevOpsProjectMembershipProjectIdColumn, projectId))).LoadOne(membership)
if err != nil {
return "", err
}
return membership.Role, nil
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,40 +1,113 @@
package devops
import (
"kubesphere.io/kubesphere/pkg/simple/client/devops"
"kubesphere.io/kubesphere/pkg/simple/client/devops/fake"
"net/http"
"testing"
)
func Test_parseCronJobTime(t *testing.T) {
type Except struct {
Last string
Next string
const baseUrl = "http://127.0.0.1/kapis/devops.kubesphere.io/v1alpha2/"
func TestGetNodesDetail(t *testing.T) {
fakeData := make(map[string]interface{})
PipelineRunNodes := []devops.PipelineRunNodes{
{
DisplayName: "Deploy to Kubernetes",
ID: "1",
Result: "SUCCESS",
},
{
DisplayName: "Deploy to Kubernetes",
ID: "2",
Result: "SUCCESS",
},
{
DisplayName: "Deploy to Kubernetes",
ID: "3",
Result: "SUCCESS",
},
}
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"}},
NodeSteps := []devops.NodeSteps{
{
DisplayName: "Deploy to Kubernetes",
ID: "1",
Result: "SUCCESS",
},
}
for _, item := range Items {
last, next, err := parseCronJobTime(item.Input)
if err != nil {
t.Fatalf("should not get error %+v", err)
}
fakeData["project1-pipeline1-run1"] = PipelineRunNodes
fakeData["project1-pipeline1-run1-1"] = NodeSteps
fakeData["project1-pipeline1-run1-2"] = NodeSteps
fakeData["project1-pipeline1-run1-3"] = NodeSteps
if last != item.Expected.Last {
t.Errorf("got %#v, expected %#v", last, item.Expected.Last)
}
devopsClient := fake.NewFakeDevops(fakeData)
if next != item.Expected.Next {
t.Errorf("got %#v, expected %#v", next, item.Expected.Next)
}
devopsOperator := NewDevopsOperator(devopsClient)
httpReq, _ := http.NewRequest(http.MethodGet, baseUrl+"devops/project1/pipelines/pipeline1/runs/run1/nodesdetail/?limit=10000", nil)
nodesDetails, err := devopsOperator.GetNodesDetail("project1", "pipeline1", "run1", httpReq)
if err != nil || nodesDetails == nil {
t.Fatalf("should not get error %+v", err)
}
for _, v := range nodesDetails {
if v.Steps[0].ID == "" {
t.Fatalf("Can not get any step.")
}
}
}
func TestGetBranchNodesDetail(t *testing.T) {
fakeData := make(map[string]interface{})
BranchPipelineRunNodes := []devops.BranchPipelineRunNodes{
{
DisplayName: "Deploy to Kubernetes",
ID: "1",
Result: "SUCCESS",
},
{
DisplayName: "Deploy to Kubernetes",
ID: "2",
Result: "SUCCESS",
},
{
DisplayName: "Deploy to Kubernetes",
ID: "3",
Result: "SUCCESS",
},
}
BranchNodeSteps := []devops.NodeSteps{
{
DisplayName: "Deploy to Kubernetes",
ID: "1",
Result: "SUCCESS",
},
}
fakeData["project1-pipeline1-branch1-run1"] = BranchPipelineRunNodes
fakeData["project1-pipeline1-branch1-run1-1"] = BranchNodeSteps
fakeData["project1-pipeline1-branch1-run1-2"] = BranchNodeSteps
fakeData["project1-pipeline1-branch1-run1-3"] = BranchNodeSteps
devopsClient := fake.NewFakeDevops(fakeData)
devopsOperator := NewDevopsOperator(devopsClient)
httpReq, _ := http.NewRequest(http.MethodGet, baseUrl+"devops/project1/pipelines/pipeline1/branchs/branch1/runs/run1/nodesdetail/?limit=10000", nil)
nodesDetails, err := devopsOperator.GetBranchNodesDetail("project1", "pipeline1", "branch1", "run1", httpReq)
if err != nil || nodesDetails == nil {
t.Fatalf("should not get error %+v", err)
}
for _, v := range nodesDetails {
if v.Steps[0].ID == "" {
t.Fatalf("Can not get any step.")
}
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -13,25 +13,19 @@ limitations under the License.
package devops
import "kubesphere.io/kubesphere/pkg/simple/client/devops"
const (
DevOpsProjectMembershipTableName = "project_membership"
DevOpsProjectMembershipUsernameColumn = "project_membership.username"
DevOpsProjectMembershipProjectIdColumn = "project_membership.project_id"
DevOpsProjectMembershipRoleColumn = "project_membership.role"
ProjectMembershipTableName = "project_membership"
ProjectMembershipUsernameColumn = "project_membership.username"
ProjectMembershipProjectIdColumn = "project_membership.project_id"
ProjectMembershipRoleColumn = "project_membership.role"
)
type DevOpsProjectMembership 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"`
}
var ProjectMembershipColumns = GetColumnsFromStruct(&devops.ProjectMembership{})
var DevOpsProjectMembershipColumns = GetColumnsFromStruct(&DevOpsProjectMembership{})
func NewDevOpsProjectMemberShip(username, projectId, role, grantBy string) *DevOpsProjectMembership {
return &DevOpsProjectMembership{
func NewDevOpsProjectMemberShip(username, projectId, role, grantBy string) *devops.ProjectMembership {
return &devops.ProjectMembership{
Username: username,
ProjectId: projectId,
Role: role,

View File

@@ -18,59 +18,6 @@ import (
"time"
)
const (
CredentialTypeUsernamePassword = "username_password"
CredentialTypeSsh = "ssh"
CredentialTypeSecretText = "secret_text"
CredentialTypeKubeConfig = "kubeconfig"
)
type JenkinsCredential 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:"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" 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 (
ProjectCredentialTableName = "project_credential"
ProjectCredentialIdColumn = "credential_id"
@@ -78,13 +25,6 @@ const (
ProjectCredentialProjectIdColumn = "project_id"
)
var CredentialTypeMap = map[string]string{
"SSH Username with private key": CredentialTypeSsh,
"Username with password": CredentialTypeUsernamePassword,
"Secret text": CredentialTypeSecretText,
"Kubernetes configuration (kubeconfig)": CredentialTypeKubeConfig,
}
type ProjectCredential struct {
ProjectId string `json:"project_id"`
CredentialId string `json:"credential_id"`

View File

@@ -15,389 +15,209 @@ package devops
import (
"fmt"
"github.com/PuerkitoBio/goquery"
"github.com/asaskevich/govalidator"
"github.com/emicklei/go-restful"
"github.com/gocraft/dbr"
"k8s.io/klog"
"kubesphere.io/kubesphere/pkg/simple/client/devops"
"kubesphere.io/kubesphere/pkg/simple/client/mysql"
"kubesphere.io/kubesphere/pkg/db"
"kubesphere.io/kubesphere/pkg/gojenkins"
"kubesphere.io/kubesphere/pkg/gojenkins/utils"
cs "kubesphere.io/kubesphere/pkg/simple/client"
"net/http"
"strings"
)
func CreateProjectCredential(projectId, username string, credentialRequest *JenkinsCredential) (string, error) {
devops, err := cs.ClientSets().Devops()
if err != nil {
return "", restful.NewError(http.StatusServiceUnavailable, err.Error())
type ProjectCredentialOperator interface {
CreateProjectCredential(projectId, username string, credentialRequest *devops.Credential) (string, error)
UpdateProjectCredential(projectId, credentialId string, credentialRequest *devops.Credential) (string, error)
DeleteProjectCredential(projectId, credentialId string) (string, error)
GetProjectCredential(projectId, credentialId, getContent string) (*devops.Credential, error)
GetProjectCredentials(projectId string) ([]*devops.Credential, error)
}
type projectCredentialOperator struct {
devopsClient devops.Interface
db *mysql.Database
}
func NewProjectCredentialOperator(devopsClient devops.Interface, dbClient *mysql.Database) ProjectCredentialOperator {
return &projectCredentialOperator{devopsClient: devopsClient, db: dbClient}
}
func (o *projectCredentialOperator) CreateProjectCredential(projectId, username string, credentialRequest *devops.Credential) (string, error) {
switch credentialRequest.Type {
case devops.CredentialTypeUsernamePassword:
if credentialRequest.UsernamePasswordCredential == nil {
err := fmt.Errorf("usename_password should not be nil")
klog.Error(err)
return "", restful.NewError(http.StatusBadRequest, err.Error())
}
case devops.CredentialTypeSsh:
if credentialRequest.SshCredential == nil {
err := fmt.Errorf("ssh should not be nil")
klog.Error(err)
return "", restful.NewError(http.StatusBadRequest, err.Error())
}
case devops.CredentialTypeSecretText:
if credentialRequest.SecretTextCredential == nil {
err := fmt.Errorf("secret_text should not be nil")
klog.Error(err)
return "", restful.NewError(http.StatusBadRequest, err.Error())
}
case devops.CredentialTypeKubeConfig:
if credentialRequest.KubeconfigCredential == nil {
err := fmt.Errorf("kubeconfig should not be nil")
klog.Error(err)
return "", restful.NewError(http.StatusBadRequest, err.Error())
}
default:
err := fmt.Errorf("error unsupport credential type")
klog.Errorf("%+v", err)
return "", restful.NewError(http.StatusBadRequest, err.Error())
}
credentialId, err := o.devopsClient.CreateCredentialInProject(projectId, credentialRequest)
if err != nil {
klog.Errorf("%+v", err)
return "", err
}
err = o.insertCredentialToDb(projectId, *credentialId, credentialRequest.Domain, username)
if err != nil {
klog.Errorf("%+v", err)
return "", err
}
return *credentialId, nil
jenkinsClient := devops.Jenkins()
}
err = checkJenkinsCredentialExists(projectId, credentialRequest.Domain, credentialRequest.Id)
func (o *projectCredentialOperator) UpdateProjectCredential(projectId, credentialId string, credentialRequest *devops.Credential) (string, error) {
credential, err := o.devopsClient.GetCredentialInProject(projectId,
credentialId, false)
if err != nil {
klog.Errorf("%+v", err)
return "", err
}
switch credential.Type {
case devops.CredentialTypeUsernamePassword:
if credentialRequest.UsernamePasswordCredential == nil {
err := fmt.Errorf("usename_password should not be nil")
klog.Error(err)
return "", restful.NewError(http.StatusBadRequest, err.Error())
}
case devops.CredentialTypeSsh:
if credentialRequest.SshCredential == nil {
err := fmt.Errorf("ssh should not be nil")
klog.Error(err)
return "", restful.NewError(http.StatusBadRequest, err.Error())
}
case devops.CredentialTypeSecretText:
if credentialRequest.SecretTextCredential == nil {
err := fmt.Errorf("secret_text should not be nil")
klog.Error(err)
return "", restful.NewError(http.StatusBadRequest, err.Error())
}
case devops.CredentialTypeKubeConfig:
if credentialRequest.KubeconfigCredential == nil {
err := fmt.Errorf("kubeconfig should not be nil")
klog.Error(err)
return "", restful.NewError(http.StatusBadRequest, err.Error())
}
default:
err := fmt.Errorf("error unsupport credential type")
klog.Errorf("%+v", err)
return "", restful.NewError(http.StatusBadRequest, err.Error())
}
credentialRequest.Id = credentialId
_, err = o.devopsClient.UpdateCredentialInProject(projectId, credentialRequest)
if err != nil {
klog.Errorf("%+v", err)
return "", restful.NewError(http.StatusBadRequest, err.Error())
}
return credentialId, nil
}
func (o *projectCredentialOperator) DeleteProjectCredential(projectId, credentialId string) (string, error) {
_, err := o.devopsClient.GetCredentialInProject(projectId,
credentialId, false)
if err != nil {
klog.Errorf("%+v", err)
return "", err
}
switch credentialRequest.Type {
case CredentialTypeUsernamePassword:
if credentialRequest.UsernamePasswordCredential == nil {
err := fmt.Errorf("usename_password should not be nil")
klog.Error(err)
return "", restful.NewError(http.StatusBadRequest, err.Error())
}
credentialId, err := jenkinsClient.CreateUsernamePasswordCredentialInFolder(credentialRequest.Domain,
credentialRequest.Id,
credentialRequest.UsernamePasswordCredential.Username,
credentialRequest.UsernamePasswordCredential.Password,
credentialRequest.Description,
projectId)
if err != nil {
klog.Errorf("%+v", err)
return "", restful.NewError(utils.GetJenkinsStatusCode(err), err.Error())
}
err = insertCredentialToDb(projectId, *credentialId, credentialRequest.Domain, username)
if err != nil {
klog.Errorf("%+v", err)
return "", err
}
return *credentialId, nil
case CredentialTypeSsh:
if credentialRequest.SshCredential == nil {
err := fmt.Errorf("ssh should not be nil")
klog.Error(err)
return "", restful.NewError(http.StatusBadRequest, err.Error())
}
credentialId, err := jenkinsClient.CreateSshCredentialInFolder(credentialRequest.Domain,
credentialRequest.Id,
credentialRequest.SshCredential.Username,
credentialRequest.SshCredential.Passphrase,
credentialRequest.SshCredential.PrivateKey,
credentialRequest.Description,
projectId)
if err != nil {
klog.Errorf("%+v", err)
return "", restful.NewError(utils.GetJenkinsStatusCode(err), err.Error())
}
err = insertCredentialToDb(projectId, *credentialId, credentialRequest.Domain, username)
if err != nil {
klog.Errorf("%+v", err)
return "", restful.NewError(http.StatusInternalServerError, err.Error())
}
return *credentialId, nil
case CredentialTypeSecretText:
if credentialRequest.SecretTextCredential == nil {
err := fmt.Errorf("secret_text should not be nil")
klog.Error(err)
return "", restful.NewError(http.StatusBadRequest, err.Error())
}
credentialId, err := jenkinsClient.CreateSecretTextCredentialInFolder(credentialRequest.Domain,
credentialRequest.Id,
credentialRequest.SecretTextCredential.Secret,
credentialRequest.Description,
projectId)
if err != nil {
klog.Errorf("%+v", err)
return "", restful.NewError(utils.GetJenkinsStatusCode(err), err.Error())
}
err = insertCredentialToDb(projectId, *credentialId, credentialRequest.Domain, username)
if err != nil {
klog.Errorf("%+v", err)
return "", restful.NewError(http.StatusInternalServerError, err.Error())
}
return *credentialId, nil
case CredentialTypeKubeConfig:
if credentialRequest.KubeconfigCredential == nil {
err := fmt.Errorf("kubeconfig should not be nil")
klog.Error(err)
return "", restful.NewError(http.StatusBadRequest, err.Error())
}
credentialId, err := jenkinsClient.CreateKubeconfigCredentialInFolder(credentialRequest.Domain,
credentialRequest.Id,
credentialRequest.KubeconfigCredential.Content,
credentialRequest.Description,
projectId)
if err != nil {
klog.Errorf("%+v", err)
return "", restful.NewError(utils.GetJenkinsStatusCode(err), err.Error())
}
err = insertCredentialToDb(projectId, *credentialId, credentialRequest.Domain, username)
if err != nil {
klog.Errorf("%+v", err)
return "", restful.NewError(http.StatusInternalServerError, err.Error())
}
return *credentialId, nil
default:
err := fmt.Errorf("error unsupport credential type")
klog.Errorf("%+v", err)
return "", restful.NewError(http.StatusBadRequest, err.Error())
}
}
func UpdateProjectCredential(projectId, credentialId string, credentialRequest *JenkinsCredential) (string, error) {
devops, err := cs.ClientSets().Devops()
if err != nil {
return "", restful.NewError(http.StatusServiceUnavailable, err.Error())
}
jenkinsClient := devops.Jenkins()
jenkinsCredential, err := jenkinsClient.GetCredentialInFolder(credentialRequest.Domain,
credentialId,
projectId)
id, err := o.devopsClient.DeleteCredentialInProject(projectId, credentialId)
if err != nil {
klog.Errorf("%+v", err)
return "", restful.NewError(utils.GetJenkinsStatusCode(err), err.Error())
}
credentialType := CredentialTypeMap[jenkinsCredential.TypeName]
switch credentialType {
case CredentialTypeUsernamePassword:
if credentialRequest.UsernamePasswordCredential == nil {
err := fmt.Errorf("usename_password should not be nil")
klog.Error(err)
return "", restful.NewError(http.StatusBadRequest, err.Error())
}
credentialId, err := jenkinsClient.UpdateUsernamePasswordCredentialInFolder(credentialRequest.Domain,
credentialId,
credentialRequest.UsernamePasswordCredential.Username,
credentialRequest.UsernamePasswordCredential.Password,
credentialRequest.Description,
projectId)
if err != nil {
klog.Errorf("%+v", err)
return "", restful.NewError(utils.GetJenkinsStatusCode(err), err.Error())
}
return *credentialId, nil
case CredentialTypeSsh:
if credentialRequest.SshCredential == nil {
err := fmt.Errorf("ssh should not be nil")
klog.Error(err)
return "", restful.NewError(http.StatusBadRequest, err.Error())
}
credentialId, err := jenkinsClient.UpdateSshCredentialInFolder(credentialRequest.Domain,
credentialId,
credentialRequest.SshCredential.Username,
credentialRequest.SshCredential.Passphrase,
credentialRequest.SshCredential.PrivateKey,
credentialRequest.Description,
projectId)
if err != nil {
klog.Errorf("%+v", err)
return "", restful.NewError(utils.GetJenkinsStatusCode(err), err.Error())
}
return *credentialId, nil
case CredentialTypeSecretText:
if credentialRequest.SecretTextCredential == nil {
err := fmt.Errorf("secret_text should not be nil")
klog.Error(err)
return "", restful.NewError(http.StatusBadRequest, err.Error())
}
credentialId, err := jenkinsClient.UpdateSecretTextCredentialInFolder(credentialRequest.Domain,
credentialId,
credentialRequest.SecretTextCredential.Secret,
credentialRequest.Description,
projectId)
if err != nil {
klog.Errorf("%+v", err)
return "", restful.NewError(utils.GetJenkinsStatusCode(err), err.Error())
}
return *credentialId, nil
case CredentialTypeKubeConfig:
if credentialRequest.KubeconfigCredential == nil {
err := fmt.Errorf("kubeconfig should not be nil")
klog.Error(err)
return "", restful.NewError(http.StatusBadRequest, err.Error())
}
credentialId, err := jenkinsClient.UpdateKubeconfigCredentialInFolder(credentialRequest.Domain,
credentialId,
credentialRequest.KubeconfigCredential.Content,
credentialRequest.Description,
projectId)
if err != nil {
klog.Errorf("%+v", err)
return "", restful.NewError(utils.GetJenkinsStatusCode(err), err.Error())
}
return *credentialId, nil
default:
err := fmt.Errorf("error unsupport credential type")
klog.Errorf("%+v", err)
return "", restful.NewError(http.StatusBadRequest, err.Error())
}
}
func DeleteProjectCredential(projectId, credentialId string, credentialRequest *JenkinsCredential) (string, error) {
devops, err := cs.ClientSets().Devops()
if err != nil {
return "", restful.NewError(http.StatusServiceUnavailable, err.Error())
}
jenkinsClient := devops.Jenkins()
dbClient, err := cs.ClientSets().MySQL()
if err != nil {
return "", restful.NewError(http.StatusServiceUnavailable, err.Error())
}
_, err = jenkinsClient.GetCredentialInFolder(credentialRequest.Domain,
credentialId,
projectId)
if err != nil {
klog.Errorf("%+v", err)
return "", restful.NewError(utils.GetJenkinsStatusCode(err), err.Error())
}
id, err := jenkinsClient.DeleteCredentialInFolder(credentialRequest.Domain, credentialId, projectId)
if err != nil {
klog.Errorf("%+v", err)
return "", restful.NewError(utils.GetJenkinsStatusCode(err), err.Error())
return "", err
}
deleteConditions := append(make([]dbr.Builder, 0), db.Eq(ProjectCredentialProjectIdColumn, projectId))
deleteConditions = append(deleteConditions, db.Eq(ProjectCredentialIdColumn, credentialId))
if !govalidator.IsNull(credentialRequest.Domain) {
deleteConditions = append(deleteConditions, db.Eq(ProjectCredentialDomainColumn, credentialRequest.Domain))
} else {
deleteConditions = append(deleteConditions, db.Eq(ProjectCredentialDomainColumn, "_"))
}
deleteConditions = append(deleteConditions, db.Eq(ProjectCredentialDomainColumn, "_"))
_, err = dbClient.DeleteFrom(ProjectCredentialTableName).
_, err = o.db.DeleteFrom(ProjectCredentialTableName).
Where(db.And(deleteConditions...)).Exec()
if err != nil && err != db.ErrNotFound {
klog.Errorf("%+v", err)
return "", restful.NewError(http.StatusInternalServerError, err.Error())
return "", err
}
return *id, nil
}
func GetProjectCredential(projectId, credentialId, domain, getContent string) (*JenkinsCredential, error) {
devops, err := cs.ClientSets().Devops()
if err != nil {
return nil, restful.NewError(http.StatusServiceUnavailable, err.Error())
}
jenkinsClient := devops.Jenkins()
func (o *projectCredentialOperator) GetProjectCredential(projectId, credentialId, getContent string) (*devops.Credential, error) {
dbClient, err := cs.ClientSets().MySQL()
if err != nil {
return nil, restful.NewError(http.StatusServiceUnavailable, err.Error())
content := false
if getContent != "" {
content = true
}
jenkinsResponse, err := jenkinsClient.GetCredentialInFolder(domain,
credential, err := o.devopsClient.GetCredentialInProject(projectId,
credentialId,
projectId)
content)
if err != nil {
klog.Errorf("%+v", err)
return nil, restful.NewError(utils.GetJenkinsStatusCode(err), err.Error())
return nil, err
}
projectCredential := &ProjectCredential{}
err = dbClient.Select(ProjectCredentialColumns...).
err = o.db.Select(ProjectCredentialColumns...).
From(ProjectCredentialTableName).Where(
db.And(db.Eq(ProjectCredentialProjectIdColumn, projectId),
db.Eq(ProjectCredentialIdColumn, credentialId),
db.Eq(ProjectCredentialDomainColumn, jenkinsResponse.Domain))).LoadOne(projectCredential)
db.Eq(ProjectCredentialDomainColumn, credential.Domain))).LoadOne(projectCredential)
if err != nil && err != db.ErrNotFound {
klog.Errorf("%+v", err)
return nil, restful.NewError(http.StatusInternalServerError, err.Error())
}
response := formatCredentialResponse(jenkinsResponse, projectCredential)
if getContent != "" {
stringBody, err := jenkinsClient.GetCredentialContentInFolder(jenkinsResponse.Domain, credentialId, projectId)
if err != nil {
klog.Errorf("%+v", err)
return nil, restful.NewError(utils.GetJenkinsStatusCode(err), err.Error())
}
stringReader := strings.NewReader(stringBody)
doc, err := goquery.NewDocumentFromReader(stringReader)
if err != nil {
klog.Errorf("%+v", err)
return nil, restful.NewError(http.StatusInternalServerError, err.Error())
}
switch response.Type {
case CredentialTypeKubeConfig:
content := &KubeconfigCredential{}
doc.Find("textarea[name*=content]").Each(func(i int, selection *goquery.Selection) {
value := selection.Text()
content.Content = value
})
response.KubeconfigCredential = content
case CredentialTypeUsernamePassword:
content := &UsernamePasswordCredential{}
doc.Find("input[name*=username]").Each(func(i int, selection *goquery.Selection) {
value, _ := selection.Attr("value")
content.Username = value
})
response.UsernamePasswordCredential = content
case CredentialTypeSsh:
content := &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
})
response.SshCredential = content
}
}
response := formatCredentialResponse(credential, projectCredential)
return response, nil
}
func GetProjectCredentials(projectId, domain string) ([]*JenkinsCredential, error) {
devops, err := cs.ClientSets().Devops()
if err != nil {
return nil, restful.NewError(http.StatusServiceUnavailable, err.Error())
}
jenkinsClient := devops.Jenkins()
func (o *projectCredentialOperator) GetProjectCredentials(projectId string) ([]*devops.Credential, error) {
dbClient, err := cs.ClientSets().MySQL()
if err != nil {
return nil, restful.NewError(http.StatusServiceUnavailable, err.Error())
}
jenkinsCredentialResponses, err := jenkinsClient.GetCredentialsInFolder(domain, projectId)
credentialResponses, err := o.devopsClient.GetCredentialsInProject(projectId)
if err != nil {
klog.Errorf("%+v", err)
return nil, restful.NewError(utils.GetJenkinsStatusCode(err), err.Error())
return nil, err
}
selectCondition := db.Eq(ProjectCredentialProjectIdColumn, projectId)
if !govalidator.IsNull(domain) {
selectCondition = db.And(selectCondition, db.Eq(ProjectCredentialDomainColumn, domain))
}
projectCredentials := make([]*ProjectCredential, 0)
_, err = dbClient.Select(ProjectCredentialColumns...).
_, err = o.db.Select(ProjectCredentialColumns...).
From(ProjectCredentialTableName).Where(selectCondition).Load(&projectCredentials)
if err != nil {
klog.Errorf("%+v", err)
return nil, restful.NewError(http.StatusInternalServerError, err.Error())
}
response := formatCredentialsResponse(jenkinsCredentialResponses, projectCredentials)
response := formatCredentialsResponse(credentialResponses, projectCredentials)
return response, nil
}
func insertCredentialToDb(projectId, credentialId, domain, username string) error {
dbClient, err := cs.ClientSets().MySQL()
if err != nil {
return err
}
func (o *projectCredentialOperator) insertCredentialToDb(projectId, credentialId, domain, username string) error {
projectCredential := NewProjectCredential(projectId, credentialId, domain, username)
_, err = dbClient.InsertInto(ProjectCredentialTableName).Columns(ProjectCredentialColumns...).
_, err := o.db.InsertInto(ProjectCredentialTableName).Columns(ProjectCredentialColumns...).
Record(projectCredential).Exec()
if err != nil {
klog.Errorf("%+v", err)
@@ -406,41 +226,19 @@ func insertCredentialToDb(projectId, credentialId, domain, username string) erro
return nil
}
func checkJenkinsCredentialExists(projectId, domain, credentialId string) error {
devops, err := cs.ClientSets().Devops()
if err != nil {
return restful.NewError(http.StatusServiceUnavailable, err.Error())
}
jenkinsClient := devops.Jenkins()
credential, err := jenkinsClient.GetCredentialInFolder(domain, credentialId, projectId)
if credential != nil {
err := fmt.Errorf("credential id [%s] has been used", credential.Id)
klog.Warning(err.Error())
return restful.NewError(http.StatusConflict, err.Error())
}
if err != nil && utils.GetJenkinsStatusCode(err) != http.StatusNotFound {
klog.Errorf("%+v", err)
return restful.NewError(http.StatusBadRequest, err.Error())
}
return nil
}
func formatCredentialResponse(
jenkinsCredentialResponse *gojenkins.CredentialResponse,
dbCredentialResponse *ProjectCredential) *JenkinsCredential {
response := &JenkinsCredential{}
response.Id = jenkinsCredentialResponse.Id
response.Description = jenkinsCredentialResponse.Description
response.DisplayName = jenkinsCredentialResponse.DisplayName
if jenkinsCredentialResponse.Fingerprint != nil && jenkinsCredentialResponse.Fingerprint.Hash != "" {
credentialResponse *devops.Credential,
dbCredentialResponse *ProjectCredential) *devops.Credential {
response := &devops.Credential{}
response.Id = credentialResponse.Id
response.Description = credentialResponse.Description
response.DisplayName = credentialResponse.DisplayName
if credentialResponse.Fingerprint != nil && credentialResponse.Fingerprint.Hash != "" {
response.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"`
Name string `json:"name,omitempty" description:"pipeline full name"`
Ranges struct {
Ranges []*struct {
Start int `json:"start,omitempty" description:"Start build number"`
@@ -449,40 +247,40 @@ func formatCredentialResponse(
} `json:"ranges,omitempty" description:"The build number of all pipelines that use this credential"`
} `json:"usage,omitempty" description:"all usage of Credential"`
}{}
response.Fingerprint.FileName = jenkinsCredentialResponse.Fingerprint.FileName
response.Fingerprint.Hash = jenkinsCredentialResponse.Fingerprint.Hash
for _, usage := range jenkinsCredentialResponse.Fingerprint.Usage {
response.Fingerprint.FileName = credentialResponse.Fingerprint.FileName
response.Fingerprint.Hash = credentialResponse.Fingerprint.Hash
for _, usage := range credentialResponse.Fingerprint.Usage {
response.Fingerprint.Usage = append(response.Fingerprint.Usage, usage)
}
}
response.Domain = jenkinsCredentialResponse.Domain
response.Domain = credentialResponse.Domain
if dbCredentialResponse != nil {
response.CreateTime = &dbCredentialResponse.CreateTime
response.Creator = dbCredentialResponse.Creator
}
credentialType, ok := CredentialTypeMap[jenkinsCredentialResponse.TypeName]
credentialType, ok := devops.CredentialTypeMap[credentialResponse.Type]
if ok {
response.Type = credentialType
return response
}
response.Type = jenkinsCredentialResponse.TypeName
response.Type = credentialResponse.Type
return response
}
func formatCredentialsResponse(jenkinsCredentialsResponse []*gojenkins.CredentialResponse,
projectCredentials []*ProjectCredential) []*JenkinsCredential {
responseSlice := make([]*JenkinsCredential, 0)
for _, jenkinsCredential := range jenkinsCredentialsResponse {
func formatCredentialsResponse(credentialsResponse []*devops.Credential,
projectCredentials []*ProjectCredential) []*devops.Credential {
responseSlice := make([]*devops.Credential, 0)
for _, credential := range credentialsResponse {
var dbCredential *ProjectCredential = nil
for _, projectCredential := range projectCredentials {
if projectCredential.CredentialId == jenkinsCredential.Id &&
projectCredential.Domain == jenkinsCredential.Domain {
if projectCredential.CredentialId == credential.Id &&
projectCredential.Domain == credential.Domain {
dbCredential = projectCredential
}
}
responseSlice = append(responseSlice, formatCredentialResponse(jenkinsCredential, dbCredential))
responseSlice = append(responseSlice, formatCredentialResponse(credential, dbCredential))
}
return responseSlice
}

View File

@@ -14,23 +14,37 @@ limitations under the License.
package devops
import (
"fmt"
"github.com/asaskevich/govalidator"
"github.com/emicklei/go-restful"
"github.com/gocraft/dbr"
"k8s.io/klog"
"kubesphere.io/kubesphere/pkg/api/devops/v1alpha2"
"kubesphere.io/kubesphere/pkg/db"
cs "kubesphere.io/kubesphere/pkg/simple/client"
"kubesphere.io/kubesphere/pkg/simple/client/devops"
"kubesphere.io/kubesphere/pkg/simple/client/mysql"
"kubesphere.io/kubesphere/pkg/utils/reflectutils"
"net/http"
)
func GetProject(projectId string) (*v1alpha2.DevOpsProject, error) {
dbconn, err := cs.ClientSets().MySQL()
if err != nil {
return nil, err
}
type ProjectOperator interface {
GetProject(projectId string) (*v1alpha2.DevOpsProject, error)
UpdateProject(project *v1alpha2.DevOpsProject) (*v1alpha2.DevOpsProject, error)
CheckProjectUserInRole(username, projectId string, roles []string) error
}
type projectOperator struct {
db *mysql.Database
}
func NewProjectOperator(dbClient *mysql.Database) ProjectOperator {
return &projectOperator{db: dbClient}
}
func (o *projectOperator) GetProject(projectId string) (*v1alpha2.DevOpsProject, error) {
project := &v1alpha2.DevOpsProject{}
err = dbconn.Select(DevOpsProjectColumns...).
err := o.db.Select(DevOpsProjectColumns...).
From(DevOpsProjectTableName).
Where(db.Eq(DevOpsProjectIdColumn, projectId)).
LoadOne(project)
@@ -46,13 +60,9 @@ func GetProject(projectId string) (*v1alpha2.DevOpsProject, error) {
return project, nil
}
func UpdateProject(project *v1alpha2.DevOpsProject) (*v1alpha2.DevOpsProject, error) {
dbconn, err := cs.ClientSets().MySQL()
if err != nil {
return nil, err
}
func (o *projectOperator) UpdateProject(project *v1alpha2.DevOpsProject) (*v1alpha2.DevOpsProject, error) {
query := dbconn.Update(DevOpsProjectTableName)
query := o.db.Update(DevOpsProjectTableName)
if !govalidator.IsNull(project.Description) {
query.Set(DevOpsProjectDescriptionColumn, project.Description)
}
@@ -73,7 +83,7 @@ func UpdateProject(project *v1alpha2.DevOpsProject) (*v1alpha2.DevOpsProject, er
}
}
newProject := &v1alpha2.DevOpsProject{}
err = dbconn.Select(DevOpsProjectColumns...).
err := o.db.Select(DevOpsProjectColumns...).
From(DevOpsProjectTableName).
Where(db.Eq(DevOpsProjectIdColumn, project.ProjectId)).
LoadOne(newProject)
@@ -83,3 +93,22 @@ func UpdateProject(project *v1alpha2.DevOpsProject) (*v1alpha2.DevOpsProject, er
}
return newProject, nil
}
func (o *projectOperator) CheckProjectUserInRole(username, projectId string, roles []string) error {
if username == KS_ADMIN {
return nil
}
membership := &devops.ProjectMembership{}
err := o.db.Select(ProjectMembershipColumns...).
From(ProjectMembershipTableName).
Where(db.And(
db.Eq(ProjectMembershipUsernameColumn, username),
db.Eq(ProjectMembershipProjectIdColumn, projectId))).LoadOne(membership)
if err != nil {
return err
}
if !reflectutils.In(membership.Role, roles) {
return fmt.Errorf("user [%s] in project [%s] role is not in %s", username, projectId, roles)
}
return nil
}

View File

@@ -15,43 +15,58 @@ package devops
import (
"fmt"
"k8s.io/klog"
"kubesphere.io/kubesphere/pkg/simple/client/devops"
"kubesphere.io/kubesphere/pkg/simple/client/mysql"
"net/http"
"github.com/emicklei/go-restful"
"github.com/gocraft/dbr"
"kubesphere.io/kubesphere/pkg/db"
"kubesphere.io/kubesphere/pkg/gojenkins"
"kubesphere.io/kubesphere/pkg/gojenkins/utils"
"kubesphere.io/kubesphere/pkg/models"
"kubesphere.io/kubesphere/pkg/server/params"
cs "kubesphere.io/kubesphere/pkg/simple/client"
)
func GetProjectMembers(projectId string, conditions *params.Conditions, orderBy string, reverse bool, limit int, offset int) (*models.PageableResponse, error) {
dbconn, err := cs.ClientSets().MySQL()
if err != nil {
return nil, err
type ProjectMemberOperator interface {
GetProjectMembers(projectId string, conditions *params.Conditions, orderBy string, reverse bool, limit int, offset int) (*models.PageableResponse, error)
GetProjectMember(projectId, username string) (*devops.ProjectMembership, error)
AddProjectMember(projectId string, membership *devops.ProjectMembership) (*devops.ProjectMembership, error)
UpdateProjectMember(projectId string, membership *devops.ProjectMembership) (*devops.ProjectMembership, error)
DeleteProjectMember(projectId, username string) (string, error)
}
type projectMemberOperator struct {
db *mysql.Database
projectMemberOperator devops.ProjectMemberOperator
}
func NewProjectMemberOperator(devopsClient devops.ProjectMemberOperator, dbClient *mysql.Database) ProjectMemberOperator {
return &projectMemberOperator{
db: dbClient,
projectMemberOperator: devopsClient,
}
memberships := make([]*DevOpsProjectMembership, 0)
}
func (o *projectMemberOperator) GetProjectMembers(projectId string, conditions *params.Conditions, orderBy string, reverse bool, limit int, offset int) (*models.PageableResponse, error) {
memberships := make([]*devops.ProjectMembership, 0)
var sqconditions []dbr.Builder
sqconditions = append(sqconditions, db.Eq(DevOpsProjectMembershipProjectIdColumn, projectId))
sqconditions = append(sqconditions, db.Eq(ProjectMembershipProjectIdColumn, projectId))
if keyword := conditions.Match["keyword"]; keyword != "" {
sqconditions = append(sqconditions, db.Like(DevOpsProjectMembershipUsernameColumn, keyword))
sqconditions = append(sqconditions, db.Like(ProjectMembershipUsernameColumn, keyword))
}
query := dbconn.Select(DevOpsProjectMembershipColumns...).
From(DevOpsProjectMembershipTableName)
query := *o.db.Select(ProjectMembershipColumns...).
From(ProjectMembershipTableName)
switch orderBy {
case "name":
if reverse {
query.OrderDesc(DevOpsProjectMembershipUsernameColumn)
query.OrderDesc(ProjectMembershipUsernameColumn)
} else {
query.OrderAsc(DevOpsProjectMembershipUsernameColumn)
query.OrderAsc(ProjectMembershipUsernameColumn)
}
default:
if reverse {
query.OrderDesc(DevOpsProjectMembershipRoleColumn)
query.OrderDesc(ProjectMembershipRoleColumn)
} else {
query.OrderAsc(DevOpsProjectMembershipRoleColumn)
query.OrderAsc(ProjectMembershipRoleColumn)
}
}
query.Limit(uint64(limit))
@@ -61,7 +76,7 @@ func GetProjectMembers(projectId string, conditions *params.Conditions, orderBy
} else {
query.Where(sqconditions[0])
}
_, err = query.Load(&memberships)
_, err := query.Load(&memberships)
if err != nil && err != dbr.ErrNotFound {
klog.Errorf("%+v", err)
return nil, restful.NewError(http.StatusInternalServerError, err.Error())
@@ -79,17 +94,13 @@ func GetProjectMembers(projectId string, conditions *params.Conditions, orderBy
return &models.PageableResponse{Items: result, TotalCount: int(count)}, nil
}
func GetProjectMember(projectId, username string) (*DevOpsProjectMembership, error) {
dbconn, err := cs.ClientSets().MySQL()
if err != nil {
return nil, err
}
func (o *projectMemberOperator) GetProjectMember(projectId, username string) (*devops.ProjectMembership, error) {
member := &DevOpsProjectMembership{}
err = dbconn.Select(DevOpsProjectMembershipColumns...).
From(DevOpsProjectMembershipTableName).
Where(db.And(db.Eq(DevOpsProjectMembershipProjectIdColumn, projectId),
db.Eq(DevOpsProjectMembershipUsernameColumn, username))).
member := &devops.ProjectMembership{}
err := o.db.Select(ProjectMembershipColumns...).
From(ProjectMembershipTableName).
Where(db.And(db.Eq(ProjectMembershipProjectIdColumn, projectId),
db.Eq(ProjectMembershipUsernameColumn, username))).
LoadOne(&member)
if err != nil && err != dbr.ErrNotFound {
klog.Errorf("%+v", err)
@@ -102,31 +113,17 @@ func GetProjectMember(projectId, username string) (*DevOpsProjectMembership, err
return member, nil
}
func AddProjectMember(projectId, operator string, member *DevOpsProjectMembership) (*DevOpsProjectMembership, error) {
dbconn, err := cs.ClientSets().MySQL()
if err != nil {
return nil, err
}
devops, err := cs.ClientSets().Devops()
if err != nil {
return nil, err
}
jenkinsClient := devops.Jenkins()
if jenkinsClient == nil {
err := fmt.Errorf("could not connect to jenkins")
klog.Error(err)
return nil, restful.NewError(http.StatusServiceUnavailable, err.Error())
}
func (o *projectMemberOperator) AddProjectMember(projectId string, membership *devops.ProjectMembership) (*devops.ProjectMembership, error) {
membership := &DevOpsProjectMembership{}
err = dbconn.Select(DevOpsProjectMembershipColumns...).
From(DevOpsProjectMembershipTableName).
dbmembership := &devops.ProjectMembership{}
err := o.db.Select(ProjectMembershipColumns...).
From(ProjectMembershipTableName).
Where(db.And(
db.Eq(DevOpsProjectMembershipUsernameColumn, member.Username),
db.Eq(DevOpsProjectMembershipProjectIdColumn, projectId))).LoadOne(membership)
db.Eq(ProjectMembershipUsernameColumn, membership.Username),
db.Eq(ProjectMembershipProjectIdColumn, projectId))).LoadOne(dbmembership)
// if user could be founded in db, user have been added to project
if err == nil {
err = fmt.Errorf("user [%s] have been added to project", member.Username)
err = fmt.Errorf("user [%s] have been added to project", membership.Username)
klog.Errorf("%+v", err)
return nil, restful.NewError(http.StatusBadRequest, err.Error())
}
@@ -136,161 +133,65 @@ func AddProjectMember(projectId, operator string, member *DevOpsProjectMembershi
return nil, restful.NewError(http.StatusInternalServerError, err.Error())
}
globalRole, err := jenkinsClient.GetGlobalRole(JenkinsAllUserRoleName)
_, err = o.projectMemberOperator.AddProjectMember(membership)
if err != nil {
klog.Errorf("%+v", err)
return nil, restful.NewError(utils.GetJenkinsStatusCode(err), err.Error())
return nil, err
}
if globalRole == nil {
_, err := jenkinsClient.AddGlobalRole(JenkinsAllUserRoleName, gojenkins.GlobalPermissionIds{
GlobalRead: true,
}, true)
if err != nil {
klog.Errorf("failed to create jenkins global role %+v", err)
return nil, restful.NewError(utils.GetJenkinsStatusCode(err), err.Error())
}
}
err = globalRole.AssignRole(member.Username)
if err != nil {
klog.Errorf("%+v", err)
return nil, restful.NewError(utils.GetJenkinsStatusCode(err), err.Error())
}
projectRole, err := jenkinsClient.GetProjectRole(GetProjectRoleName(projectId, member.Role))
if err != nil {
klog.Errorf("%+v", err)
return nil, restful.NewError(utils.GetJenkinsStatusCode(err), err.Error())
}
err = projectRole.AssignRole(member.Username)
if err != nil {
klog.Errorf("%+v", err)
return nil, restful.NewError(utils.GetJenkinsStatusCode(err), err.Error())
}
pipelineRole, err := jenkinsClient.GetProjectRole(GetPipelineRoleName(projectId, member.Role))
if err != nil {
klog.Errorf("%+v", err)
return nil, restful.NewError(utils.GetJenkinsStatusCode(err), err.Error())
}
err = pipelineRole.AssignRole(member.Username)
if err != nil {
klog.Errorf("%+v", err)
return nil, restful.NewError(utils.GetJenkinsStatusCode(err), err.Error())
}
projectMembership := NewDevOpsProjectMemberShip(member.Username, projectId, member.Role, operator)
_, err = dbconn.
InsertInto(DevOpsProjectMembershipTableName).
Columns(DevOpsProjectMembershipColumns...).
projectMembership := NewDevOpsProjectMemberShip(membership.Username, projectId, membership.Role, membership.GrantBy)
_, err = o.db.
InsertInto(ProjectMembershipTableName).
Columns(ProjectMembershipColumns...).
Record(projectMembership).Exec()
if err != nil {
klog.Errorf("%+v", err)
err = projectRole.UnAssignRole(member.Username)
_, err = o.projectMemberOperator.DeleteProjectMember(membership)
if err != nil {
klog.Errorf("%+v", err)
return nil, restful.NewError(utils.GetJenkinsStatusCode(err), err.Error())
}
err = pipelineRole.UnAssignRole(member.Username)
if err != nil {
klog.Errorf("%+v", err)
return nil, restful.NewError(utils.GetJenkinsStatusCode(err), err.Error())
return nil, err
}
return nil, restful.NewError(http.StatusInternalServerError, err.Error())
}
return projectMembership, nil
}
func UpdateProjectMember(projectId, operator string, member *DevOpsProjectMembership) (*DevOpsProjectMembership, error) {
dbconn, err := cs.ClientSets().MySQL()
if err != nil {
return nil, restful.NewError(http.StatusServiceUnavailable, err.Error())
}
devops, err := cs.ClientSets().Devops()
if err != nil {
return nil, err
}
func (o *projectMemberOperator) UpdateProjectMember(projectId string, membership *devops.ProjectMembership) (*devops.ProjectMembership, error) {
jenkinsClient := devops.Jenkins()
if jenkinsClient == nil {
err := fmt.Errorf("could not connect to jenkins")
klog.Error(err)
return nil, restful.NewError(http.StatusServiceUnavailable, err.Error())
}
oldMembership := &DevOpsProjectMembership{}
err = dbconn.Select(DevOpsProjectMembershipColumns...).
From(DevOpsProjectMembershipTableName).
oldMembership := &devops.ProjectMembership{}
err := o.db.Select(ProjectMembershipColumns...).
From(ProjectMembershipTableName).
Where(db.And(
db.Eq(DevOpsProjectMembershipUsernameColumn, member.Username),
db.Eq(DevOpsProjectMembershipProjectIdColumn, projectId),
db.Eq(ProjectMembershipUsernameColumn, membership.Username),
db.Eq(ProjectMembershipProjectIdColumn, projectId),
)).LoadOne(oldMembership)
if err != nil {
klog.Errorf("%+v", err)
return nil, restful.NewError(http.StatusBadRequest, err.Error())
}
oldProjectRole, err := jenkinsClient.GetProjectRole(GetProjectRoleName(projectId, oldMembership.Role))
_, err = o.projectMemberOperator.UpdateProjectMember(oldMembership, membership)
if err != nil {
klog.Errorf("%+v", err)
return nil, restful.NewError(utils.GetJenkinsStatusCode(err), err.Error())
return nil, err
}
err = oldProjectRole.UnAssignRole(member.Username)
if err != nil {
klog.Errorf("%+v", err)
return nil, restful.NewError(utils.GetJenkinsStatusCode(err), err.Error())
}
oldPipelineRole, err := jenkinsClient.GetProjectRole(GetPipelineRoleName(projectId, oldMembership.Role))
if err != nil {
klog.Errorf("%+v", err)
return nil, restful.NewError(utils.GetJenkinsStatusCode(err), err.Error())
}
err = oldPipelineRole.UnAssignRole(member.Username)
if err != nil {
klog.Errorf("%+v", err)
return nil, restful.NewError(utils.GetJenkinsStatusCode(err), err.Error())
}
projectRole, err := jenkinsClient.GetProjectRole(GetProjectRoleName(projectId, member.Role))
if err != nil {
klog.Errorf("%+v", err)
return nil, restful.NewError(utils.GetJenkinsStatusCode(err), err.Error())
}
err = projectRole.AssignRole(member.Username)
if err != nil {
klog.Errorf("%+v", err)
return nil, restful.NewError(utils.GetJenkinsStatusCode(err), err.Error())
}
pipelineRole, err := jenkinsClient.GetProjectRole(GetPipelineRoleName(projectId, member.Role))
if err != nil {
klog.Errorf("%+v", err)
return nil, restful.NewError(utils.GetJenkinsStatusCode(err), err.Error())
}
err = pipelineRole.AssignRole(member.Username)
if err != nil {
klog.Errorf("%+v", err)
return nil, restful.NewError(utils.GetJenkinsStatusCode(err), err.Error())
}
_, err = dbconn.Update(DevOpsProjectMembershipTableName).
Set(DevOpsProjectMembershipRoleColumn, member.Role).
_, err = o.db.Update(ProjectMembershipTableName).
Set(ProjectMembershipRoleColumn, membership.Role).
Where(db.And(
db.Eq(DevOpsProjectMembershipProjectIdColumn, projectId),
db.Eq(DevOpsProjectMembershipUsernameColumn, member.Username),
db.Eq(ProjectMembershipProjectIdColumn, projectId),
db.Eq(ProjectMembershipUsernameColumn, membership.Username),
)).Exec()
if err != nil {
klog.Errorf("%+v", err)
return nil, restful.NewError(http.StatusInternalServerError, err.Error())
}
responseMembership := &DevOpsProjectMembership{}
err = dbconn.Select(DevOpsProjectMembershipColumns...).
From(DevOpsProjectMembershipTableName).
responseMembership := &devops.ProjectMembership{}
err = o.db.Select(ProjectMembershipColumns...).
From(ProjectMembershipTableName).
Where(db.And(
db.Eq(DevOpsProjectMembershipUsernameColumn, member.Username),
db.Eq(DevOpsProjectMembershipProjectIdColumn, projectId),
db.Eq(ProjectMembershipUsernameColumn, membership.Username),
db.Eq(ProjectMembershipProjectIdColumn, projectId),
)).LoadOne(responseMembership)
if err != nil {
klog.Errorf("%+v", err)
@@ -299,25 +200,14 @@ func UpdateProjectMember(projectId, operator string, member *DevOpsProjectMember
return responseMembership, nil
}
func DeleteProjectMember(projectId, username string) (string, error) {
dbconn, err := cs.ClientSets().MySQL()
if err != nil {
return "", restful.NewError(http.StatusServiceUnavailable, err.Error())
}
func (o *projectMemberOperator) DeleteProjectMember(projectId, username string) (string, error) {
devops, err := cs.ClientSets().Devops()
if err != nil {
return "", restful.NewError(http.StatusServiceUnavailable, err.Error())
}
jenkinsClient := devops.Jenkins()
oldMembership := &DevOpsProjectMembership{}
err = dbconn.Select(DevOpsProjectMembershipColumns...).
From(DevOpsProjectMembershipTableName).
oldMembership := &devops.ProjectMembership{}
err := o.db.Select(ProjectMembershipColumns...).
From(ProjectMembershipTableName).
Where(db.And(
db.Eq(DevOpsProjectMembershipUsernameColumn, username),
db.Eq(DevOpsProjectMembershipProjectIdColumn, projectId),
db.Eq(ProjectMembershipUsernameColumn, username),
db.Eq(ProjectMembershipProjectIdColumn, projectId),
)).LoadOne(oldMembership)
if err != nil {
if err != db.ErrNotFound {
@@ -329,12 +219,12 @@ func DeleteProjectMember(projectId, username string) (string, error) {
}
}
if oldMembership.Role == ProjectOwner {
count, err := dbconn.Select(DevOpsProjectMembershipProjectIdColumn).
From(DevOpsProjectMembershipTableName).
if oldMembership.Role == devops.ProjectOwner {
count, err := o.db.Select(ProjectMembershipProjectIdColumn).
From(ProjectMembershipTableName).
Where(db.And(
db.Eq(DevOpsProjectMembershipProjectIdColumn, projectId),
db.Eq(DevOpsProjectMembershipRoleColumn, ProjectOwner))).Count()
db.Eq(ProjectMembershipProjectIdColumn, projectId),
db.Eq(ProjectMembershipRoleColumn, devops.ProjectOwner))).Count()
if err != nil {
klog.Errorf("%+v", err)
return "", restful.NewError(http.StatusInternalServerError, err.Error())
@@ -346,32 +236,16 @@ func DeleteProjectMember(projectId, username string) (string, error) {
}
}
oldProjectRole, err := jenkinsClient.GetProjectRole(GetProjectRoleName(projectId, oldMembership.Role))
_, err = o.projectMemberOperator.DeleteProjectMember(oldMembership)
if err != nil {
klog.Errorf("%+v", err)
return "", restful.NewError(utils.GetJenkinsStatusCode(err), err.Error())
}
err = oldProjectRole.UnAssignRole(username)
if err != nil {
klog.Errorf("%+v", err)
return "", restful.NewError(utils.GetJenkinsStatusCode(err), err.Error())
klog.Error(err)
return "", err
}
oldPipelineRole, err := jenkinsClient.GetProjectRole(GetPipelineRoleName(projectId, oldMembership.Role))
if err != nil {
klog.Errorf("%+v", err)
return "", restful.NewError(utils.GetJenkinsStatusCode(err), err.Error())
}
err = oldPipelineRole.UnAssignRole(username)
if err != nil {
klog.Errorf("%+v", err)
return "", restful.NewError(utils.GetJenkinsStatusCode(err), err.Error())
}
_, err = dbconn.DeleteFrom(DevOpsProjectMembershipTableName).
_, err = o.db.DeleteFrom(ProjectMembershipTableName).
Where(db.And(
db.Eq(DevOpsProjectMembershipProjectIdColumn, projectId),
db.Eq(DevOpsProjectMembershipUsernameColumn, username),
db.Eq(ProjectMembershipProjectIdColumn, projectId),
db.Eq(ProjectMembershipUsernameColumn, username),
)).Exec()
if err != nil {
klog.Errorf("%+v", err)

File diff suppressed because it is too large Load Diff

View File

@@ -17,295 +17,49 @@ import (
"fmt"
"github.com/emicklei/go-restful"
"k8s.io/klog"
"kubesphere.io/kubesphere/pkg/gojenkins/utils"
cs "kubesphere.io/kubesphere/pkg/simple/client"
"kubesphere.io/kubesphere/pkg/simple/client/devops"
"net/http"
)
func CreateProjectPipeline(projectId string, pipeline *ProjectPipeline) (string, error) {
devops, err := cs.ClientSets().Devops()
if err != nil {
return "", restful.NewError(http.StatusServiceUnavailable, err.Error())
type ProjectPipelineOperator interface {
CreateProjectPipeline(projectId string, pipeline *devops.ProjectPipeline) (string, error)
DeleteProjectPipeline(projectId string, pipelineId string) (string, error)
UpdateProjectPipeline(projectId, pipelineId string, pipeline *devops.ProjectPipeline) (string, error)
GetProjectPipelineConfig(projectId, pipelineId string) (*devops.ProjectPipeline, error)
}
type projectPipelineOperator struct {
pipelineOperator devops.ProjectPipelineOperator
}
func NewProjectPipelineOperator(devopsClient devops.ProjectPipelineOperator) ProjectPipelineOperator {
return &projectPipelineOperator{
pipelineOperator: devopsClient,
}
jenkinsClient := devops.Jenkins()
}
func (o *projectPipelineOperator) CreateProjectPipeline(projectId string, pipeline *devops.ProjectPipeline) (string, error) {
return o.pipelineOperator.CreateProjectPipeline(projectId, pipeline)
}
func (o *projectPipelineOperator) DeleteProjectPipeline(projectId string, pipelineId string) (string, error) {
return o.pipelineOperator.DeleteProjectPipeline(projectId, pipelineId)
}
func (o *projectPipelineOperator) UpdateProjectPipeline(projectId, pipelineId string, pipeline *devops.ProjectPipeline) (string, error) {
switch pipeline.Type {
case NoScmPipelineType:
config, err := createPipelineConfigXml(pipeline.Pipeline)
if err != nil {
klog.Errorf("%+v", err)
return "", restful.NewError(http.StatusInternalServerError, err.Error())
}
job, err := jenkinsClient.GetJob(pipeline.Pipeline.Name, projectId)
if job != nil {
err := fmt.Errorf("job name [%s] has been used", job.GetName())
klog.Warning(err.Error())
return "", restful.NewError(http.StatusConflict, err.Error())
}
if err != nil && utils.GetJenkinsStatusCode(err) != http.StatusNotFound {
klog.Errorf("%+v", err)
return "", restful.NewError(utils.GetJenkinsStatusCode(err), err.Error())
}
_, err = jenkinsClient.CreateJobInFolder(config, pipeline.Pipeline.Name, projectId)
if err != nil {
klog.Errorf("%+v", err)
return "", restful.NewError(utils.GetJenkinsStatusCode(err), err.Error())
}
return pipeline.Pipeline.Name, nil
case MultiBranchPipelineType:
config, err := createMultiBranchPipelineConfigXml(projectId, pipeline.MultiBranchPipeline)
if err != nil {
klog.Errorf("%+v", err)
return "", restful.NewError(http.StatusInternalServerError, err.Error())
}
job, err := jenkinsClient.GetJob(pipeline.MultiBranchPipeline.Name, projectId)
if job != nil {
err := fmt.Errorf("job name [%s] has been used", job.GetName())
klog.Warning(err.Error())
return "", restful.NewError(http.StatusConflict, err.Error())
}
if err != nil && utils.GetJenkinsStatusCode(err) != http.StatusNotFound {
klog.Errorf("%+v", err)
return "", restful.NewError(utils.GetJenkinsStatusCode(err), err.Error())
}
_, err = jenkinsClient.CreateJobInFolder(config, pipeline.MultiBranchPipeline.Name, projectId)
if err != nil {
klog.Errorf("%+v", err)
return "", restful.NewError(utils.GetJenkinsStatusCode(err), err.Error())
}
return pipeline.MultiBranchPipeline.Name, nil
case devops.NoScmPipelineType:
pipeline.Pipeline.Name = pipelineId
case devops.MultiBranchPipelineType:
pipeline.MultiBranchPipeline.Name = pipelineId
default:
err := fmt.Errorf("error unsupport job type")
err := fmt.Errorf("error unsupport pipeline type")
klog.Errorf("%+v", err)
return "", restful.NewError(http.StatusBadRequest, err.Error())
}
return o.pipelineOperator.UpdateProjectPipeline(projectId, pipeline)
}
func DeleteProjectPipeline(projectId string, pipelineId string) (string, error) {
devops, err := cs.ClientSets().Devops()
if err != nil {
return "", restful.NewError(http.StatusServiceUnavailable, err.Error())
}
jenkinsClient := devops.Jenkins()
_, err = jenkinsClient.DeleteJob(pipelineId, projectId)
if err != nil {
klog.Errorf("%+v", err)
return "", restful.NewError(utils.GetJenkinsStatusCode(err), err.Error())
}
return pipelineId, nil
}
func UpdateProjectPipeline(projectId, pipelineId string, pipeline *ProjectPipeline) (string, error) {
devops, err := cs.ClientSets().Devops()
if err != nil {
return "", restful.NewError(http.StatusServiceUnavailable, err.Error())
}
jenkinsClient := devops.Jenkins()
switch pipeline.Type {
case NoScmPipelineType:
config, err := createPipelineConfigXml(pipeline.Pipeline)
if err != nil {
klog.Errorf("%+v", err)
return "", restful.NewError(http.StatusInternalServerError, err.Error())
}
job, err := jenkinsClient.GetJob(pipelineId, projectId)
if err != nil {
klog.Errorf("%+v", err)
return "", restful.NewError(utils.GetJenkinsStatusCode(err), err.Error())
}
err = job.UpdateConfig(config)
if err != nil {
klog.Errorf("%+v", err)
return "", restful.NewError(utils.GetJenkinsStatusCode(err), err.Error())
}
return pipeline.Pipeline.Name, nil
case MultiBranchPipelineType:
config, err := createMultiBranchPipelineConfigXml(projectId, pipeline.MultiBranchPipeline)
if err != nil {
klog.Errorf("%+v", err)
return "", restful.NewError(http.StatusInternalServerError, err.Error())
}
job, err := jenkinsClient.GetJob(pipelineId, projectId)
if err != nil {
klog.Errorf("%+v", err)
return "", restful.NewError(utils.GetJenkinsStatusCode(err), err.Error())
}
err = job.UpdateConfig(config)
if err != nil {
klog.Errorf("%+v", err)
return "", restful.NewError(utils.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 GetProjectPipeline(projectId, pipelineId string) (*ProjectPipeline, error) {
devops, err := cs.ClientSets().Devops()
if err != nil {
return nil, restful.NewError(http.StatusServiceUnavailable, err.Error())
}
jenkinsClient := devops.Jenkins()
job, err := jenkinsClient.GetJob(pipelineId, projectId)
if err != nil {
klog.Errorf("%+v", err)
return nil, restful.NewError(utils.GetJenkinsStatusCode(err), err.Error())
}
switch job.Raw.Class {
case "org.jenkinsci.plugins.workflow.job.WorkflowJob":
config, err := job.GetConfig()
if err != nil {
klog.Errorf("%+v", err)
return nil, restful.NewError(utils.GetJenkinsStatusCode(err), err.Error())
}
pipeline, err := parsePipelineConfigXml(config)
if err != nil {
klog.Errorf("%+v", err)
return nil, restful.NewError(utils.GetJenkinsStatusCode(err), err.Error())
}
pipeline.Name = pipelineId
return &ProjectPipeline{
Type: NoScmPipelineType,
Pipeline: pipeline,
}, nil
case "org.jenkinsci.plugins.workflow.multibranch.WorkflowMultiBranchProject":
config, err := job.GetConfig()
if err != nil {
klog.Errorf("%+v", err)
return nil, restful.NewError(utils.GetJenkinsStatusCode(err), err.Error())
}
pipeline, err := parseMultiBranchPipelineConfigXml(config)
if err != nil {
klog.Errorf("%+v", err)
return nil, restful.NewError(utils.GetJenkinsStatusCode(err), err.Error())
}
pipeline.Name = pipelineId
return &ProjectPipeline{
Type: MultiBranchPipelineType,
MultiBranchPipeline: pipeline,
}, nil
default:
err := fmt.Errorf("error unsupport job type")
klog.Errorf("%+v", err)
return nil, restful.NewError(http.StatusBadRequest, err.Error())
}
}
func GetPipelineSonar(projectId, pipelineId string) ([]*SonarStatus, error) {
devops, err := cs.ClientSets().Devops()
if err != nil {
return nil, restful.NewError(http.StatusServiceUnavailable, err.Error())
}
jenkinsClient := devops.Jenkins()
job, err := jenkinsClient.GetJob(pipelineId, projectId)
if err != nil {
klog.Errorf("%+v", err)
return nil, restful.NewError(utils.GetJenkinsStatusCode(err), err.Error())
}
build, err := job.GetLastBuild()
if err != nil && utils.GetJenkinsStatusCode(err) != http.StatusNotFound {
klog.Errorf("%+v", err)
return nil, restful.NewError(utils.GetJenkinsStatusCode(err), err.Error())
} else if err != nil {
klog.Errorf("%+v", err)
return nil, nil
}
sonarStatus, err := getBuildSonarResults(build)
if err != nil {
klog.Errorf("%+v", err)
return nil, restful.NewError(http.StatusBadRequest, err.Error())
}
if len(sonarStatus) == 0 {
build, err := job.GetLastCompletedBuild()
if err != nil && utils.GetJenkinsStatusCode(err) != http.StatusNotFound {
klog.Errorf("%+v", err)
return nil, restful.NewError(utils.GetJenkinsStatusCode(err), err.Error())
} else if err != nil {
klog.Errorf("%+v", err)
return nil, nil
}
sonarStatus, err = getBuildSonarResults(build)
if err != nil {
klog.Errorf("%+v", err)
return nil, restful.NewError(http.StatusBadRequest, err.Error())
}
}
return sonarStatus, nil
}
func GetMultiBranchPipelineSonar(projectId, pipelineId, branchId string) ([]*SonarStatus, error) {
devops, err := cs.ClientSets().Devops()
if err != nil {
return nil, restful.NewError(http.StatusServiceUnavailable, err.Error())
}
jenkinsClient := devops.Jenkins()
job, err := jenkinsClient.GetJob(branchId, projectId, pipelineId)
if err != nil {
klog.Errorf("%+v", err)
return nil, restful.NewError(utils.GetJenkinsStatusCode(err), err.Error())
}
build, err := job.GetLastBuild()
if err != nil && utils.GetJenkinsStatusCode(err) != http.StatusNotFound {
klog.Errorf("%+v", err)
return nil, restful.NewError(utils.GetJenkinsStatusCode(err), err.Error())
} else if err != nil {
klog.Errorf("%+v", err)
return nil, nil
}
sonarStatus, err := getBuildSonarResults(build)
if err != nil {
klog.Errorf("%+v", err)
return nil, restful.NewError(http.StatusBadRequest, err.Error())
}
if len(sonarStatus) == 0 {
build, err := job.GetLastCompletedBuild()
if err != nil && utils.GetJenkinsStatusCode(err) != http.StatusNotFound {
klog.Errorf("%+v", err)
return nil, restful.NewError(utils.GetJenkinsStatusCode(err), err.Error())
} else if err != nil {
klog.Errorf("%+v", err)
return nil, nil
}
sonarStatus, err = getBuildSonarResults(build)
if err != nil {
klog.Errorf("%+v", err)
return nil, restful.NewError(http.StatusBadRequest, err.Error())
}
}
return sonarStatus, nil
func (o *projectPipelineOperator) GetProjectPipelineConfig(projectId, pipelineId string) (*devops.ProjectPipeline, error) {
return o.pipelineOperator.GetProjectPipelineConfig(projectId, pipelineId)
}

View File

@@ -0,0 +1,122 @@
package devops
import (
"github.com/emicklei/go-restful"
"k8s.io/klog"
"kubesphere.io/kubesphere/pkg/server/errors"
"kubesphere.io/kubesphere/pkg/simple/client/devops"
"kubesphere.io/kubesphere/pkg/simple/client/sonarqube"
"net/http"
)
type PipelineSonarGetter interface {
GetPipelineSonar(projectId, pipelineId string) ([]*sonarqube.SonarStatus, error)
GetMultiBranchPipelineSonar(projectId, pipelineId, branchId string) ([]*sonarqube.SonarStatus, error)
}
type pipelineSonarGetter struct {
devops.BuildGetter
sonarqube.SonarInterface
}
func NewPipelineSonarGetter(devopClient devops.BuildGetter, sonarClient sonarqube.SonarInterface) PipelineSonarGetter {
return &pipelineSonarGetter{
BuildGetter: devopClient,
SonarInterface: sonarClient,
}
}
func (g *pipelineSonarGetter) GetPipelineSonar(projectId, pipelineId string) ([]*sonarqube.SonarStatus, error) {
build, err := g.GetProjectPipelineBuildByType(projectId, pipelineId, devops.LastBuild)
if err != nil && errors.GetServiceErrorCode(err) != http.StatusNotFound {
klog.Errorf("%+v", err)
return nil, err
} else if err != nil {
klog.Errorf("%+v", err)
return nil, nil
}
var taskIds []string
for _, action := range build.Actions {
if action.ClassName == sonarqube.SonarAnalysisActionClass {
taskIds = append(taskIds, action.SonarTaskId)
}
}
var sonarStatus []*sonarqube.SonarStatus
if len(taskIds) != 0 {
sonarStatus, err = g.GetSonarResultsByTaskIds(taskIds...)
if err != nil {
klog.Errorf("%+v", err)
return nil, restful.NewError(http.StatusBadRequest, err.Error())
}
} else if len(taskIds) == 0 {
build, err := g.GetProjectPipelineBuildByType(projectId, pipelineId, devops.LastCompletedBuild)
if err != nil && errors.GetServiceErrorCode(err) != http.StatusNotFound {
klog.Errorf("%+v", err)
return nil, restful.NewError(errors.GetServiceErrorCode(err), err.Error())
} else if err != nil {
klog.Errorf("%+v", err)
return nil, nil
}
for _, action := range build.Actions {
if action.ClassName == sonarqube.SonarAnalysisActionClass {
taskIds = append(taskIds, action.SonarTaskId)
}
}
sonarStatus, err = g.GetSonarResultsByTaskIds(taskIds...)
if err != nil {
klog.Errorf("%+v", err)
return nil, restful.NewError(http.StatusBadRequest, err.Error())
}
}
return sonarStatus, nil
}
func (g *pipelineSonarGetter) GetMultiBranchPipelineSonar(projectId, pipelineId, branchId string) ([]*sonarqube.SonarStatus, error) {
build, err := g.GetMultiBranchPipelineBuildByType(projectId, pipelineId, branchId, devops.LastBuild)
if err != nil && errors.GetServiceErrorCode(err) != http.StatusNotFound {
klog.Errorf("%+v", err)
return nil, restful.NewError(errors.GetServiceErrorCode(err), err.Error())
} else if err != nil {
klog.Errorf("%+v", err)
return nil, nil
}
var taskIds []string
for _, action := range build.Actions {
if action.ClassName == sonarqube.SonarAnalysisActionClass {
taskIds = append(taskIds, action.SonarTaskId)
}
}
var sonarStatus []*sonarqube.SonarStatus
if len(taskIds) != 0 {
sonarStatus, err = g.GetSonarResultsByTaskIds(taskIds...)
if err != nil {
klog.Errorf("%+v", err)
return nil, restful.NewError(http.StatusBadRequest, err.Error())
}
} else if len(taskIds) == 0 {
build, err := g.GetMultiBranchPipelineBuildByType(projectId, pipelineId, branchId, devops.LastCompletedBuild)
if err != nil && errors.GetServiceErrorCode(err) != http.StatusNotFound {
klog.Errorf("%+v", err)
return nil, restful.NewError(errors.GetServiceErrorCode(err), err.Error())
} else if err != nil {
klog.Errorf("%+v", err)
return nil, nil
}
for _, action := range build.Actions {
if action.ClassName == sonarqube.SonarAnalysisActionClass {
taskIds = append(taskIds, action.SonarTaskId)
}
}
sonarStatus, err = g.GetSonarResultsByTaskIds(taskIds...)
if err != nil {
klog.Errorf("%+v", err)
return nil, restful.NewError(http.StatusBadRequest, err.Error())
}
}
return sonarStatus, nil
}

View File

@@ -1,633 +0,0 @@
/*
Copyright 2019 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 devops
import (
"reflect"
"testing"
)
func Test_NoScmPipelineConfig(t *testing.T) {
inputs := []*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 := []*NoScmPipeline{
{
Name: "",
Description: "for test",
Jenkinsfile: "node{echo 'hello'}",
Discarder: &DiscarderProperty{
"3", "5",
},
},
{
Name: "",
Description: "for test",
Jenkinsfile: "node{echo 'hello'}",
Discarder: &DiscarderProperty{
"3", "",
},
},
{
Name: "",
Description: "for test",
Jenkinsfile: "node{echo 'hello'}",
Discarder: &DiscarderProperty{
"", "21321",
},
},
{
Name: "",
Description: "for test",
Jenkinsfile: "node{echo 'hello'}",
Discarder: &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 := []*NoScmPipeline{
{
Name: "",
Description: "for test",
Jenkinsfile: "node{echo 'hello'}",
Parameters: &Parameters{
&Parameter{
Name: "d",
DefaultValue: "a\nb",
Type: "choice",
Description: "fortest",
},
},
},
{
Name: "",
Description: "for test",
Jenkinsfile: "node{echo 'hello'}",
Parameters: &Parameters{
&Parameter{
Name: "a",
DefaultValue: "abc",
Type: "string",
Description: "fortest",
},
&Parameter{
Name: "b",
DefaultValue: "false",
Type: "boolean",
Description: "fortest",
},
&Parameter{
Name: "c",
DefaultValue: "password \n aaa",
Type: "text",
Description: "fortest",
},
&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 := []*NoScmPipeline{
{
Name: "",
Description: "for test",
Jenkinsfile: "node{echo 'hello'}",
TimerTrigger: &TimerTrigger{
Cron: "1 1 1 * * *",
},
},
{
Name: "",
Description: "for test",
Jenkinsfile: "node{echo 'hello'}",
RemoteTrigger: &RemoteTrigger{
Token: "abc",
},
},
{
Name: "",
Description: "for test",
Jenkinsfile: "node{echo 'hello'}",
TimerTrigger: &TimerTrigger{
Cron: "1 1 1 * * *",
},
RemoteTrigger: &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 := []*MultiBranchPipeline{
{
Name: "",
Description: "for test",
ScriptPath: "Jenkinsfile",
SourceType: "git",
GitSource: &GitSource{},
},
{
Name: "",
Description: "for test",
ScriptPath: "Jenkinsfile",
SourceType: "github",
GitHubSource: &GithubSource{},
},
{
Name: "",
Description: "for test",
ScriptPath: "Jenkinsfile",
SourceType: "single_svn",
SingleSvnSource: &SingleSvnSource{},
},
{
Name: "",
Description: "for test",
ScriptPath: "Jenkinsfile",
SourceType: "svn",
SvnSource: &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 := []*MultiBranchPipeline{
{
Name: "",
Description: "for test",
ScriptPath: "Jenkinsfile",
SourceType: "git",
Discarder: &DiscarderProperty{
DaysToKeep: "1",
NumToKeep: "2",
},
GitSource: &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 := []*MultiBranchPipeline{
{
Name: "",
Description: "for test",
ScriptPath: "Jenkinsfile",
SourceType: "git",
TimerTrigger: &TimerTrigger{
Interval: "12345566",
},
GitSource: &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 := []*MultiBranchPipeline{
{
Name: "",
Description: "for test",
ScriptPath: "Jenkinsfile",
SourceType: "git",
TimerTrigger: &TimerTrigger{
Interval: "12345566",
},
GitSource: &GitSource{
Url: "https://github.com/kubesphere/devops",
CredentialId: "git",
DiscoverBranches: true,
},
},
{
Name: "",
Description: "for test",
ScriptPath: "Jenkinsfile",
SourceType: "github",
TimerTrigger: &TimerTrigger{
Interval: "12345566",
},
GitHubSource: &GithubSource{
Owner: "kubesphere",
Repo: "devops",
CredentialId: "github",
ApiUri: "https://api.github.com",
DiscoverBranches: 1,
DiscoverPRFromOrigin: 2,
DiscoverPRFromForks: &DiscoverPRFromForks{
Strategy: 1,
Trust: 1,
},
},
},
{
Name: "",
Description: "for test",
ScriptPath: "Jenkinsfile",
SourceType: "bitbucket_server",
TimerTrigger: &TimerTrigger{
Interval: "12345566",
},
BitbucketServerSource: &BitbucketServerSource{
Owner: "kubesphere",
Repo: "devops",
CredentialId: "github",
ApiUri: "https://api.github.com",
DiscoverBranches: 1,
DiscoverPRFromOrigin: 2,
DiscoverPRFromForks: &DiscoverPRFromForks{
Strategy: 1,
Trust: 1,
},
},
},
{
Name: "",
Description: "for test",
ScriptPath: "Jenkinsfile",
SourceType: "svn",
TimerTrigger: &TimerTrigger{
Interval: "12345566",
},
SvnSource: &SvnSource{
Remote: "https://api.svn.com/bcd",
CredentialId: "svn",
Excludes: "truck",
Includes: "tag/*",
},
},
{
Name: "",
Description: "for test",
ScriptPath: "Jenkinsfile",
SourceType: "single_svn",
TimerTrigger: &TimerTrigger{
Interval: "12345566",
},
SingleSvnSource: &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 := []*MultiBranchPipeline{
{
Name: "",
Description: "for test",
ScriptPath: "Jenkinsfile",
SourceType: "git",
GitSource: &GitSource{
Url: "https://github.com/kubesphere/devops",
CredentialId: "git",
DiscoverBranches: true,
CloneOption: &GitCloneOption{
Shallow: false,
Depth: 3,
Timeout: 20,
},
},
},
{
Name: "",
Description: "for test",
ScriptPath: "Jenkinsfile",
SourceType: "github",
GitHubSource: &GithubSource{
Owner: "kubesphere",
Repo: "devops",
CredentialId: "github",
ApiUri: "https://api.github.com",
DiscoverBranches: 1,
DiscoverPRFromOrigin: 2,
DiscoverPRFromForks: &DiscoverPRFromForks{
Strategy: 1,
Trust: 1,
},
CloneOption: &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 := []*MultiBranchPipeline{
{
Name: "",
Description: "for test",
ScriptPath: "Jenkinsfile",
SourceType: "git",
GitSource: &GitSource{
Url: "https://github.com/kubesphere/devops",
CredentialId: "git",
DiscoverBranches: true,
RegexFilter: ".*",
},
},
{
Name: "",
Description: "for test",
ScriptPath: "Jenkinsfile",
SourceType: "github",
GitHubSource: &GithubSource{
Owner: "kubesphere",
Repo: "devops",
CredentialId: "github",
ApiUri: "https://api.github.com",
DiscoverBranches: 1,
DiscoverPRFromOrigin: 2,
DiscoverPRFromForks: &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 := []*MultiBranchPipeline{
{
Name: "",
Description: "for test",
ScriptPath: "Jenkinsfile",
SourceType: "github",
GitHubSource: &GithubSource{
Owner: "kubesphere",
Repo: "devops",
CredentialId: "github",
ApiUri: "https://api.github.com",
DiscoverBranches: 1,
DiscoverPRFromOrigin: 2,
DiscoverPRFromForks: &DiscoverPRFromForks{
Strategy: 1,
Trust: 1,
},
RegexFilter: ".*",
},
MultiBranchJobTrigger: &MultiBranchJobTrigger{
CreateActionJobsToTrigger: "abc",
DeleteActionJobsToTrigger: "ddd",
},
},
{
Name: "",
Description: "for test",
ScriptPath: "Jenkinsfile",
SourceType: "github",
GitHubSource: &GithubSource{
Owner: "kubesphere",
Repo: "devops",
CredentialId: "github",
ApiUri: "https://api.github.com",
DiscoverBranches: 1,
DiscoverPRFromOrigin: 2,
DiscoverPRFromForks: &DiscoverPRFromForks{
Strategy: 1,
Trust: 1,
},
RegexFilter: ".*",
},
MultiBranchJobTrigger: &MultiBranchJobTrigger{
CreateActionJobsToTrigger: "abc",
},
},
{
Name: "",
Description: "for test",
ScriptPath: "Jenkinsfile",
SourceType: "github",
GitHubSource: &GithubSource{
Owner: "kubesphere",
Repo: "devops",
CredentialId: "github",
ApiUri: "https://api.github.com",
DiscoverBranches: 1,
DiscoverPRFromOrigin: 2,
DiscoverPRFromForks: &DiscoverPRFromForks{
Strategy: 1,
Trust: 1,
},
RegexFilter: ".*",
},
MultiBranchJobTrigger: &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

@@ -3,6 +3,8 @@ package devops
import (
"code.cloudfoundry.org/bytefmt"
"fmt"
"github.com/aws/aws-sdk-go/aws/awserr"
awsS3 "github.com/aws/aws-sdk-go/service/s3"
"github.com/emicklei/go-restful"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/client-go/util/retry"
@@ -10,7 +12,6 @@ import (
"kubesphere.io/kubesphere/pkg/apis/devops/v1alpha1"
"kubesphere.io/kubesphere/pkg/client/clientset/versioned"
"kubesphere.io/kubesphere/pkg/client/informers/externalversions"
"kubesphere.io/kubesphere/pkg/simple/client"
"kubesphere.io/kubesphere/pkg/simple/client/s3"
"mime/multipart"
"net/http"
@@ -22,9 +23,9 @@ const (
)
type S2iBinaryUploader interface {
UploadBinary(name, namespace, md5 string, header *multipart.FileHeader) (*v1alpha1.S2iBinary, error)
UploadS2iBinary(namespace, name, md5 string, header *multipart.FileHeader) (*v1alpha1.S2iBinary, error)
DownloadBinary(name, namespace, fileName string) (string, error)
DownloadS2iBinary(namespace, name, fileName string) (string, error)
}
type s2iBinaryUploader struct {
@@ -33,6 +34,14 @@ type s2iBinaryUploader struct {
s3Client s3.Interface
}
func NewS2iBinaryUploader(client versioned.Interface, informers externalversions.SharedInformerFactory, s3Client s3.Interface) S2iBinaryUploader {
return &s2iBinaryUploader{
client: client,
informers: informers,
s3Client: s3Client,
}
}
func (s *s2iBinaryUploader) UploadS2iBinary(namespace, name, md5 string, fileHeader *multipart.FileHeader) (*v1alpha1.S2iBinary, error) {
binFile, err := fileHeader.Open()
if err != nil {
@@ -75,58 +84,35 @@ func (s *s2iBinaryUploader) UploadS2iBinary(namespace, name, md5 string, fileHea
copy.Spec.FileName = fileHeader.Filename
copy.Spec.DownloadURL = fmt.Sprintf(GetS2iBinaryURL, namespace, name, copy.Spec.FileName)
/*
TODO: upload binary file use s3.Interface
s3session := s3Client.Session()
if s3session == nil {
err := fmt.Errorf("could not connect to s2i s3")
klog.Error(err)
_, serr := SetS2iBinaryStatusWithRetry(copy, origin.Status.Phase)
if serr != nil {
klog.Error(serr)
err = s.s3Client.Upload(fmt.Sprintf("%s-%s", namespace, name), copy.Spec.FileName, binFile)
if err != nil {
if aerr, ok := err.(awserr.Error); ok {
switch aerr.Code() {
case awsS3.ErrCodeNoSuchBucket:
klog.Error(err)
_, serr := s.SetS2iBinaryStatusWithRetry(copy, origin.Status.Phase)
if serr != nil {
klog.Error(serr)
}
return nil, err
default:
klog.Error(err)
_, serr := s.SetS2iBinaryStatusWithRetry(copy, v1alpha1.StatusUploadFailed)
if serr != nil {
klog.Error(serr)
}
return nil, err
}
return nil, err
}
uploader := s3manager.NewUploader(s3session, func(uploader *s3manager.Uploader) {
uploader.PartSize = 5 * bytefmt.MEGABYTE
uploader.LeavePartsOnError = true
})
_, err = uploader.Upload(&s3manager.UploadInput{
Bucket: s3Client.Bucket(),
Key: aws.String(fmt.Sprintf("%s-%s", namespace, name)),
Body: binFile,
ContentDisposition: aws.String(fmt.Sprintf("attachment; filename=\"%s\"", copy.Spec.FileName)),
})
klog.Error(err)
return nil, err
}
if err != nil {
if aerr, ok := err.(awserr.Error); ok {
switch aerr.Code() {
case s3.ErrCodeNoSuchBucket:
klog.Error(err)
_, serr := SetS2iBinaryStatusWithRetry(copy, origin.Status.Phase)
if serr != nil {
klog.Error(serr)
}
return nil, err
default:
klog.Error(err)
_, serr := SetS2iBinaryStatusWithRetry(copy, v1alpha1.StatusUploadFailed)
if serr != nil {
klog.Error(serr)
}
return nil, err
}
}
klog.Error(err)
return nil, err
}
*/
if copy.Spec.UploadTimeStamp == nil {
copy.Spec.UploadTimeStamp = new(metav1.Time)
}
*copy.Spec.UploadTimeStamp = metav1.Now()
copy, err = client.ClientSets().K8s().KubeSphere().DevopsV1alpha1().S2iBinaries(namespace).Update(copy)
copy, err = s.client.DevopsV1alpha1().S2iBinaries(namespace).Update(copy)
if err != nil {
klog.Error(err)
return nil, err
@@ -159,22 +145,7 @@ func (s *s2iBinaryUploader) DownloadS2iBinary(namespace, name, fileName string)
klog.Error(err)
return "", err
}
/*
TODO: get download url of requested binary
req, _ := s.s3Client.Client().GetObjectRequest(&s3.GetObjectInput{
Bucket: s3Client.Bucket(),
Key: aws.String(fmt.Sprintf("%s-%s", namespace, name)),
ResponseContentDisposition: aws.String(fmt.Sprintf("attachment; filename=\"%s\"", origin.Spec.FileName)),
})
url, err := req.Presign(5 * time.Minute)
if err != nil {
klog.Error(err)
return "", err
}
*/
return "", nil
return s.s3Client.GetDownloadURL(fmt.Sprintf("%s-%s", namespace, name), fileName)
}
func (s *s2iBinaryUploader) SetS2iBinaryStatus(s2ibin *v1alpha1.S2iBinary, status string) (*v1alpha1.S2iBinary, error) {
@@ -193,7 +164,7 @@ func (s *s2iBinaryUploader) SetS2iBinaryStatusWithRetry(s2ibin *v1alpha1.S2iBina
var bin *v1alpha1.S2iBinary
var err error
err = retry.RetryOnConflict(retry.DefaultRetry, func() error {
bin, err = s.informers.Devops().V1alpha1().S2iBinaries().Lister().S2iBinaries(s2ibin.Namespace).Get(s2ibin.Name)
bin, err = s.client.DevopsV1alpha1().S2iBinaries(s2ibin.Namespace).Get(s2ibin.Name, metav1.GetOptions{})
if err != nil {
klog.Error(err)
return err

View File

@@ -0,0 +1,114 @@
package devops
import (
"code.cloudfoundry.org/bytefmt"
"fmt"
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/watch"
clientgotesting "k8s.io/client-go/testing"
"kubesphere.io/kubesphere/pkg/apis/devops/v1alpha1"
"kubesphere.io/kubesphere/pkg/client/clientset/versioned/fake"
ksinformers "kubesphere.io/kubesphere/pkg/client/informers/externalversions"
fakeS3 "kubesphere.io/kubesphere/pkg/simple/client/s3/fake"
"kubesphere.io/kubesphere/pkg/utils/hashutil"
"mime/multipart"
"reflect"
"strings"
"testing"
"time"
)
const (
fileaContents = "This is a test file."
fileaKey = "binary"
fileaName = "filea.txt"
boundary = `MyBoundary`
ns = "testns"
s2ibname = "test"
)
const message = `
--MyBoundary
Content-Disposition: form-data; name="binary"; filename="filea.txt"
Content-Type: text/plain
` + fileaContents + `
--MyBoundary--
`
func TestS2iBinaryUploader(t *testing.T) {
s2ib := s2ibinary(ns, s2ibname)
fakeKubeClient := fake.NewSimpleClientset(s2ib)
fakeWatch := watch.NewFake()
fakeKubeClient.AddWatchReactor("*", clientgotesting.DefaultWatchReactor(fakeWatch, nil))
informerFactory := ksinformers.NewSharedInformerFactory(fakeKubeClient, 0)
stopCh := make(chan struct{})
s2iInformer := informerFactory.Devops().V1alpha1().S2iBinaries()
err := s2iInformer.Informer().GetIndexer().Add(s2ib)
defer close(stopCh)
informerFactory.Start(stopCh)
informerFactory.WaitForCacheSync(stopCh)
s3 := fakeS3.NewFakeS3()
uploader := NewS2iBinaryUploader(fakeKubeClient, informerFactory, s3)
header := prepareFileHeader()
file, err := header.Open()
if err != nil {
t.Fatal(err)
}
md5, err := hashutil.GetMD5(file)
if err != nil {
t.Fatal(err)
}
wantSpec := v1alpha1.S2iBinarySpec{
FileName: fileaName,
MD5: md5,
Size: bytefmt.ByteSize(uint64(header.Size)),
}
binary, err := uploader.UploadS2iBinary(ns, s2ibname, md5, header)
if err != nil {
t.Fatal(err)
}
wantSpec.UploadTimeStamp = binary.Spec.UploadTimeStamp
wantSpec.DownloadURL = binary.Spec.DownloadURL
if !reflect.DeepEqual(binary.Spec, wantSpec) {
t.Fatalf("s2ibinary spec is not same with expected, get: %+v, expected: %+v", binary, wantSpec)
}
_, ok := s3.Storage[fmt.Sprintf("%s-%s", ns, s2ibname)]
if !ok {
t.Fatalf("should get file in s3")
}
time.Sleep(3 * time.Second)
url, err := uploader.DownloadS2iBinary(ns, s2ibname, fileaName)
if err != nil {
t.Fatal(err)
}
if url != fmt.Sprintf("http://%s-%s/%s", ns, s2ibname, fileaName) {
t.Fatalf("download url is not equal with expected, get: %+v, expected: %+v", url, fmt.Sprintf("http://%s-%s/%s", ns, s2ibname, fileaName))
}
}
func prepareFileHeader() *multipart.FileHeader {
reader := strings.NewReader(message)
multipartReader := multipart.NewReader(reader, boundary)
form, err := multipartReader.ReadForm(25)
if err != nil {
panic(err)
}
return form.File["binary"][0]
}
func s2ibinary(namespace, name string) *v1alpha1.S2iBinary {
return &v1alpha1.S2iBinary{
ObjectMeta: v1.ObjectMeta{
Name: name,
Namespace: namespace,
},
Spec: v1alpha1.S2iBinarySpec{},
Status: v1alpha1.S2iBinaryStatus{},
}
}

View File

@@ -1,62 +0,0 @@
/*
Copyright 2019 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 devops
// Some apis for Jenkins.
const (
GetPipeBranchUrl = "/blue/rest/organizations/jenkins/pipelines/%s/pipelines/%s/branches/?"
GetBranchPipeUrl = "/blue/rest/organizations/jenkins/pipelines/%s/pipelines/%s/branches/%s/"
GetPipelineUrl = "/blue/rest/organizations/jenkins/pipelines/%s/pipelines/%s/"
SearchPipelineUrl = "/blue/rest/search/?"
RunBranchPipelineUrl = "/blue/rest/organizations/jenkins/pipelines/%s/pipelines/%s/branches/%s/runs/"
RunPipelineUrl = "/blue/rest/organizations/jenkins/pipelines/%s/pipelines/%s/runs/"
GetPipelineRunUrl = "/blue/rest/organizations/jenkins/pipelines/%s/pipelines/%s/runs/%s/"
GetPipeBranchRunUrl = "/blue/rest/organizations/jenkins/pipelines/%s/pipelines/%s/branches/%s/runs/%s/"
SearchPipelineRunUrl = "/blue/rest/organizations/jenkins/pipelines/%s/pipelines/%s/runs/?"
GetBranchPipeRunNodesUrl = "/blue/rest/organizations/jenkins/pipelines/%s/pipelines/%s/branches/%s/runs/%s/nodes/?"
GetPipeRunNodesUrl = "/blue/rest/organizations/jenkins/pipelines/%s/pipelines/%s/runs/%s/nodes/?"
GetBranchRunLogUrl = "/blue/rest/organizations/jenkins/pipelines/%s/pipelines/%s/branches/%s/runs/%s/log/?"
GetRunLogUrl = "/blue/rest/organizations/jenkins/pipelines/%s/pipelines/%s/runs/%s/log/?"
GetBranchStepLogUrl = "/blue/rest/organizations/jenkins/pipelines/%s/pipelines/%s/branches/%s/runs/%s/nodes/%s/steps/%s/log/?"
GetStepLogUrl = "/blue/rest/organizations/jenkins/pipelines/%s/pipelines/%s/runs/%s/nodes/%s/steps/%s/log/?"
StopBranchPipelineUrl = "/blue/rest/organizations/jenkins/pipelines/%s/pipelines/%s/branches/%s/runs/%s/stop/?"
StopPipelineUrl = "/blue/rest/organizations/jenkins/pipelines/%s/pipelines/%s/runs/%s/stop/?"
ReplayBranchPipelineUrl = "/blue/rest/organizations/jenkins/pipelines/%s/pipelines/%s/branches/%s/runs/%s/replay/"
ReplayPipelineUrl = "/blue/rest/organizations/jenkins/pipelines/%s/pipelines/%s/runs/%s/replay/"
GetBranchArtifactsUrl = "/blue/rest/organizations/jenkins/pipelines/%s/pipelines/%s/branches/%s/runs/%s/artifacts/?"
GetArtifactsUrl = "/blue/rest/organizations/jenkins/pipelines/%s/pipelines/%s/runs/%s/artifacts/?"
CheckBranchPipelineUrl = "/blue/rest/organizations/jenkins/pipelines/%s/pipelines/%s/branches/%s/runs/%s/nodes/%s/steps/%s/"
CheckPipelineUrl = "/blue/rest/organizations/jenkins/pipelines/%s/pipelines/%s/runs/%s/nodes/%s/steps/%s/"
GetBranchNodeStepsUrl = "/blue/rest/organizations/jenkins/pipelines/%s/pipelines/%s/branches/%s/runs/%s/nodes/%s/steps/?"
GetNodeStepsUrl = "/blue/rest/organizations/jenkins/pipelines/%s/pipelines/%s/runs/%s/nodes/%s/steps/?"
GetSCMServersUrl = "/blue/rest/organizations/jenkins/scm/%s/servers/"
CreateSCMServersUrl = "/blue/rest/organizations/jenkins/scm/%s/servers/"
ValidateUrl = "/blue/rest/organizations/jenkins/scm/%s/validate"
GetSCMOrgUrl = "/blue/rest/organizations/jenkins/scm/%s/organizations/?"
GetOrgRepoUrl = "/blue/rest/organizations/jenkins/scm/%s/organizations/%s/repositories/?"
GetConsoleLogUrl = "/job/%s/job/%s/indexing/consoleText"
ScanBranchUrl = "/job/%s/job/%s/build?"
GetCrumbUrl = "/crumbIssuer/api/json/"
ToJenkinsfileUrl = "/pipeline-model-converter/toJenkinsfile"
ToJsonUrl = "/pipeline-model-converter/toJson"
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"
)