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

@@ -7,7 +7,7 @@ import (
cliflag "k8s.io/component-base/cli/flag" cliflag "k8s.io/component-base/cli/flag"
"k8s.io/klog" "k8s.io/klog"
kubesphereconfig "kubesphere.io/kubesphere/pkg/server/config" kubesphereconfig "kubesphere.io/kubesphere/pkg/server/config"
"kubesphere.io/kubesphere/pkg/simple/client/devops" "kubesphere.io/kubesphere/pkg/simple/client/devops/jenkins"
"kubesphere.io/kubesphere/pkg/simple/client/k8s" "kubesphere.io/kubesphere/pkg/simple/client/k8s"
"kubesphere.io/kubesphere/pkg/simple/client/openpitrix" "kubesphere.io/kubesphere/pkg/simple/client/openpitrix"
"kubesphere.io/kubesphere/pkg/simple/client/s3" "kubesphere.io/kubesphere/pkg/simple/client/s3"
@@ -17,7 +17,7 @@ import (
type KubeSphereControllerManagerOptions struct { type KubeSphereControllerManagerOptions struct {
KubernetesOptions *k8s.KubernetesOptions KubernetesOptions *k8s.KubernetesOptions
DevopsOptions *devops.Options DevopsOptions *jenkins.Options
S3Options *s3.Options S3Options *s3.Options
OpenPitrixOptions *openpitrix.Options OpenPitrixOptions *openpitrix.Options
@@ -27,7 +27,7 @@ type KubeSphereControllerManagerOptions struct {
func NewKubeSphereControllerManagerOptions() *KubeSphereControllerManagerOptions { func NewKubeSphereControllerManagerOptions() *KubeSphereControllerManagerOptions {
s := &KubeSphereControllerManagerOptions{ s := &KubeSphereControllerManagerOptions{
KubernetesOptions: k8s.NewKubernetesOptions(), KubernetesOptions: k8s.NewKubernetesOptions(),
DevopsOptions: devops.NewDevopsOptions(), DevopsOptions: jenkins.NewDevopsOptions(),
S3Options: s3.NewS3Options(), S3Options: s3.NewS3Options(),
OpenPitrixOptions: openpitrix.NewOpenPitrixOptions(), OpenPitrixOptions: openpitrix.NewOpenPitrixOptions(),
LeaderElection: &leaderelection.LeaderElectionConfig{ LeaderElection: &leaderelection.LeaderElectionConfig{

View File

@@ -5,7 +5,7 @@ import (
cliflag "k8s.io/component-base/cli/flag" cliflag "k8s.io/component-base/cli/flag"
"k8s.io/klog" "k8s.io/klog"
genericoptions "kubesphere.io/kubesphere/pkg/server/options" genericoptions "kubesphere.io/kubesphere/pkg/server/options"
"kubesphere.io/kubesphere/pkg/simple/client/devops" "kubesphere.io/kubesphere/pkg/simple/client/devops/jenkins"
esclient "kubesphere.io/kubesphere/pkg/simple/client/elasticsearch" esclient "kubesphere.io/kubesphere/pkg/simple/client/elasticsearch"
"kubesphere.io/kubesphere/pkg/simple/client/k8s" "kubesphere.io/kubesphere/pkg/simple/client/k8s"
"kubesphere.io/kubesphere/pkg/simple/client/mysql" "kubesphere.io/kubesphere/pkg/simple/client/mysql"
@@ -21,7 +21,7 @@ type ServerRunOptions struct {
ConfigFile string ConfigFile string
GenericServerRunOptions *genericoptions.ServerRunOptions GenericServerRunOptions *genericoptions.ServerRunOptions
KubernetesOptions *k8s.KubernetesOptions KubernetesOptions *k8s.KubernetesOptions
DevopsOptions *devops.Options DevopsOptions *jenkins.Options
SonarQubeOptions *sonarqube.Options SonarQubeOptions *sonarqube.Options
ServiceMeshOptions *servicemesh.Options ServiceMeshOptions *servicemesh.Options
MySQLOptions *mysql.Options MySQLOptions *mysql.Options
@@ -36,7 +36,7 @@ func NewServerRunOptions() *ServerRunOptions {
s := ServerRunOptions{ s := ServerRunOptions{
GenericServerRunOptions: genericoptions.NewServerRunOptions(), GenericServerRunOptions: genericoptions.NewServerRunOptions(),
KubernetesOptions: k8s.NewKubernetesOptions(), KubernetesOptions: k8s.NewKubernetesOptions(),
DevopsOptions: devops.NewDevopsOptions(), DevopsOptions: jenkins.NewDevopsOptions(),
SonarQubeOptions: sonarqube.NewSonarQubeOptions(), SonarQubeOptions: sonarqube.NewSonarQubeOptions(),
ServiceMeshOptions: servicemesh.NewServiceMeshOptions(), ServiceMeshOptions: servicemesh.NewServiceMeshOptions(),
MySQLOptions: mysql.NewMySQLOptions(), MySQLOptions: mysql.NewMySQLOptions(),

2
go.mod
View File

@@ -99,7 +99,7 @@ require (
k8s.io/component-base v0.17.0 k8s.io/component-base v0.17.0
k8s.io/klog v1.0.0 k8s.io/klog v1.0.0
k8s.io/kube-openapi v0.0.0-20191107075043-30be4d16710a k8s.io/kube-openapi v0.0.0-20191107075043-30be4d16710a
k8s.io/kubectl v0.17.0 k8s.io/utils v0.0.0-20191114184206-e782cd3c129f // indirect
kubesphere.io/im v0.1.0 // indirect kubesphere.io/im v0.1.0 // indirect
openpitrix.io/iam v0.1.0 // indirect openpitrix.io/iam v0.1.0 // indirect
openpitrix.io/openpitrix v0.4.1-0.20190920134345-4d2be6e4965c openpitrix.io/openpitrix v0.4.1-0.20190920134345-4d2be6e4965c

25
go.sum
View File

@@ -18,8 +18,6 @@ github.com/Azure/go-autorest/tracing v0.5.0 h1:TRn4WjSnkcSy5AEG3pnbtFSwNtwzjr4VY
github.com/Azure/go-autorest/tracing v0.5.0/go.mod h1:r/s2XiOKccPW3HrqB+W0TQzfbtp2fGCgRFtBroKn4Dk= github.com/Azure/go-autorest/tracing v0.5.0/go.mod h1:r/s2XiOKccPW3HrqB+W0TQzfbtp2fGCgRFtBroKn4Dk=
github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ= github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/MakeNowJust/heredoc v0.0.0-20170808103936-bb23615498cd h1:sjQovDkwrZp8u+gxLtPgKGjk5hCxuy2hrRejBTA9xFU=
github.com/MakeNowJust/heredoc v0.0.0-20170808103936-bb23615498cd/go.mod h1:64YHyfSL2R96J44Nlwm39UHepQbyR5q10x7iYa1ks2E=
github.com/Masterminds/semver v1.5.0 h1:H65muMkzWKEuNDnfl9d70GUjFniHKHRbFPGBuZ3QEww= github.com/Masterminds/semver v1.5.0 h1:H65muMkzWKEuNDnfl9d70GUjFniHKHRbFPGBuZ3QEww=
github.com/Masterminds/semver v1.5.0/go.mod h1:MB6lktGJrhw8PrUyiEoblNEGEQ+RzHPF078ddwwvV3Y= github.com/Masterminds/semver v1.5.0/go.mod h1:MB6lktGJrhw8PrUyiEoblNEGEQ+RzHPF078ddwwvV3Y=
github.com/Microsoft/go-winio v0.4.12 h1:xAfWHN1IrQ0NJ9TBC0KBZoqLjzDTr1ML+4MywiUOryc= github.com/Microsoft/go-winio v0.4.12 h1:xAfWHN1IrQ0NJ9TBC0KBZoqLjzDTr1ML+4MywiUOryc=
@@ -62,8 +60,6 @@ github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869/go.mod h1:Ekp36dR
github.com/cenkalti/backoff v2.2.1+incompatible h1:tNowT99t7UNflLxfYYSlKYsBpXdEet03Pg2g16Swow4= github.com/cenkalti/backoff v2.2.1+incompatible h1:tNowT99t7UNflLxfYYSlKYsBpXdEet03Pg2g16Swow4=
github.com/cenkalti/backoff v2.2.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM= github.com/cenkalti/backoff v2.2.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM=
github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
github.com/chai2010/gettext-go v0.0.0-20160711120539-c6fed771bfd5 h1:7aWHqerlJ41y6FOsEUvknqgXnGmJyJSbjhAWq5pO4F8=
github.com/chai2010/gettext-go v0.0.0-20160711120539-c6fed771bfd5/go.mod h1:/iP1qXHoty45bqomnu2LM+VVyAEdWN+vtSHGlQgyxbw=
github.com/cheekybits/genny v1.0.0 h1:uGGa4nei+j20rOSeDeP5Of12XVm7TGUd4dJA9RDitfE= github.com/cheekybits/genny v1.0.0 h1:uGGa4nei+j20rOSeDeP5Of12XVm7TGUd4dJA9RDitfE=
github.com/cheekybits/genny v1.0.0/go.mod h1:+tQajlRqAUrPI7DOSpB0XAqZYtQakVtB7wXkRAgjxjQ= github.com/cheekybits/genny v1.0.0/go.mod h1:+tQajlRqAUrPI7DOSpB0XAqZYtQakVtB7wXkRAgjxjQ=
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
@@ -81,7 +77,6 @@ github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfc
github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE= github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/daviddengcn/go-colortext v0.0.0-20160507010035-511bcaf42ccd/go.mod h1:dv4zxwHi5C/8AeI+4gX4dCWOIvNi7I6JCSX0HvlKPgE=
github.com/deckarep/golang-set v1.7.1 h1:SCQV0S6gTtp6itiFrTqI+pfmJ4LN85S1YzhDf9rTHJQ= github.com/deckarep/golang-set v1.7.1 h1:SCQV0S6gTtp6itiFrTqI+pfmJ4LN85S1YzhDf9rTHJQ=
github.com/deckarep/golang-set v1.7.1/go.mod h1:93vsz/8Wt4joVM7c2AVqh+YRMiUSc14yDtF28KmMOgQ= github.com/deckarep/golang-set v1.7.1/go.mod h1:93vsz/8Wt4joVM7c2AVqh+YRMiUSc14yDtF28KmMOgQ=
github.com/denisenkom/go-mssqldb v0.0.0-20190204142019-df6d76eb9289/go.mod h1:xN/JuLBIz4bjkxNmByTiV1IbhfnYb6oo99phBn4Eqhc= github.com/denisenkom/go-mssqldb v0.0.0-20190204142019-df6d76eb9289/go.mod h1:xN/JuLBIz4bjkxNmByTiV1IbhfnYb6oo99phBn4Eqhc=
@@ -120,8 +115,6 @@ github.com/emirpasic/gods v1.12.0/go.mod h1:YfzfFFoVP/catgzJb4IKIqXjX78Ha8FMSDh3
github.com/erikstmartin/go-testdb v0.0.0-20160219214506-8d10e4a1bae5/go.mod h1:a2zkGnVExMxdzMo3M0Hi/3sEU+cWnZpSni0O6/Yb/P0= github.com/erikstmartin/go-testdb v0.0.0-20160219214506-8d10e4a1bae5/go.mod h1:a2zkGnVExMxdzMo3M0Hi/3sEU+cWnZpSni0O6/Yb/P0=
github.com/evanphx/json-patch v4.5.0+incompatible h1:ouOWdg56aJriqS0huScTkVXPC5IcNrDCXZ6OoTAWu7M= github.com/evanphx/json-patch v4.5.0+incompatible h1:ouOWdg56aJriqS0huScTkVXPC5IcNrDCXZ6OoTAWu7M=
github.com/evanphx/json-patch v4.5.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= github.com/evanphx/json-patch v4.5.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk=
github.com/exponent-io/jsonpath v0.0.0-20151013193312-d6023ce2651d h1:105gxyaGwCFad8crR9dcMQWvV9Hvulu6hwUh4tWPJnM=
github.com/exponent-io/jsonpath v0.0.0-20151013193312-d6023ce2651d/go.mod h1:ZZMPRZwes7CROmyNKgQzC3XPs6L/G2EJLHddWejkmf4=
github.com/fatih/camelcase v1.0.0 h1:hxNvNX/xYBp0ovncs8WyWZrOrpBNub/JfaMvbURyft8= github.com/fatih/camelcase v1.0.0 h1:hxNvNX/xYBp0ovncs8WyWZrOrpBNub/JfaMvbURyft8=
github.com/fatih/camelcase v1.0.0/go.mod h1:yN2Sb0lFhZJUdVvtELVWefmrXpuZESvPmqwoZc+/fpc= github.com/fatih/camelcase v1.0.0/go.mod h1:yN2Sb0lFhZJUdVvtELVWefmrXpuZESvPmqwoZc+/fpc=
github.com/fatih/color v1.7.0 h1:DkWD4oS2D8LGGgTQ6IvwJJXSL5Vp2ffcQg58nFV38Ys= github.com/fatih/color v1.7.0 h1:DkWD4oS2D8LGGgTQ6IvwJJXSL5Vp2ffcQg58nFV38Ys=
@@ -198,9 +191,6 @@ github.com/golang/mock v1.2.0 h1:28o5sBqPkBsMGnC6b4MvE2TzSr5/AT4c/1fLqVGIwlk=
github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
github.com/golang/protobuf v1.3.2 h1:6nsPYzhq5kReh6QImI3k5qWzO4PEbvbIW2cwSfR/6xs= github.com/golang/protobuf v1.3.2 h1:6nsPYzhq5kReh6QImI3k5qWzO4PEbvbIW2cwSfR/6xs=
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golangplus/bytes v0.0.0-20160111154220-45c989fe5450/go.mod h1:Bk6SMAONeMXrxql8uvOKuAZSu8aM5RUGv+1C6IJaEho=
github.com/golangplus/fmt v0.0.0-20150411045040-2a5d6d7d2995/go.mod h1:lJgMEyOkYFkPcDKwRXegd+iM6E7matEszMG5HhwytU8=
github.com/golangplus/testing v0.0.0-20180327235837-af21d9c3145e/go.mod h1:0AA//k/eakGydO4jKRoRL2j92ZKSzTgj9tclaCrvXHk=
github.com/google/btree v1.0.0 h1:0udJVsspx3VBr5FwtLhQQtuAsVc79tTq0ocGIPAU6qo= github.com/google/btree v1.0.0 h1:0udJVsspx3VBr5FwtLhQQtuAsVc79tTq0ocGIPAU6qo=
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
github.com/google/go-cmp v0.3.0 h1:crn/baboCvb5fXaQ0IJ1SGTsTVrWpDsCWC8EGETZijY= github.com/google/go-cmp v0.3.0 h1:crn/baboCvb5fXaQ0IJ1SGTsTVrWpDsCWC8EGETZijY=
@@ -296,9 +286,6 @@ github.com/leodido/go-urn v1.1.0 h1:Sm1gr51B1kKyfD2BlRcLSiEkffoG96g6TPv6eRoEiB8=
github.com/leodido/go-urn v1.1.0/go.mod h1:+cyI34gQWZcE1eQU7NVgKkkzdXDQHr1dBMtdAPozLkw= github.com/leodido/go-urn v1.1.0/go.mod h1:+cyI34gQWZcE1eQU7NVgKkkzdXDQHr1dBMtdAPozLkw=
github.com/lib/pq v1.2.0 h1:LXpIM/LZ5xGFhOpXAQUIMM1HdyqzVYM13zNdjCEEcA0= github.com/lib/pq v1.2.0 h1:LXpIM/LZ5xGFhOpXAQUIMM1HdyqzVYM13zNdjCEEcA0=
github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de h1:9TO3cAIGXtEhnIaL+V+BEER86oLrvS+kWobKpbJuye0=
github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de/go.mod h1:zAbeS9B/r2mtpb6U+EI2rYA5OAXxsYw6wTamcNW+zcE=
github.com/lithammer/dedent v1.1.0/go.mod h1:jrXYCQtgg0nJiN+StA2KgR7w6CiQNv9Fd/Z9BP0jIOc=
github.com/lucas-clemente/quic-go v0.11.1 h1:zasajC848Dqq/+WqfqBCkmPw+YHNe1MBts/z7y7nXf4= github.com/lucas-clemente/quic-go v0.11.1 h1:zasajC848Dqq/+WqfqBCkmPw+YHNe1MBts/z7y7nXf4=
github.com/lucas-clemente/quic-go v0.11.1/go.mod h1:PpMmPfPKO9nKJ/psF49ESTAGQSdfXxlg1otPbEB2nOw= github.com/lucas-clemente/quic-go v0.11.1/go.mod h1:PpMmPfPKO9nKJ/psF49ESTAGQSdfXxlg1otPbEB2nOw=
github.com/magiconair/properties v1.8.0 h1:LLgXmsheXeRoUOBOjtwPQCWIYqM/LU1ayDtDePerRcY= github.com/magiconair/properties v1.8.0 h1:LLgXmsheXeRoUOBOjtwPQCWIYqM/LU1ayDtDePerRcY=
@@ -323,8 +310,6 @@ github.com/miekg/dns v1.1.9 h1:OIdC9wT96RzuZMf2PfKRhFgsStHUUBZLM/lo1LqiM9E=
github.com/miekg/dns v1.1.9/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= github.com/miekg/dns v1.1.9/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=
github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y=
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
github.com/mitchellh/go-wordwrap v1.0.0 h1:6GlHJ/LTGMrIJbwgdqdl2eEH8o+Exx/0m8ir9Gns0u4=
github.com/mitchellh/go-wordwrap v1.0.0/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUbP2oI0UX1GXzOo=
github.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE= github.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE=
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
@@ -433,7 +418,6 @@ github.com/xanzy/ssh-agent v0.2.1 h1:TCbipTQL2JiiCprBWx9frJ2eJlCYT00NmctrHxVAr70
github.com/xanzy/ssh-agent v0.2.1/go.mod h1:mLlQY/MoOhWBj+gOGMQkOeiEvkx+8pJSI+0Bx9h2kr4= github.com/xanzy/ssh-agent v0.2.1/go.mod h1:mLlQY/MoOhWBj+gOGMQkOeiEvkx+8pJSI+0Bx9h2kr4=
github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2 h1:eY9dn8+vbi4tKz5Qo6v2eYzo7kUS51QINcR5jNpbZS8= github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2 h1:eY9dn8+vbi4tKz5Qo6v2eYzo7kUS51QINcR5jNpbZS8=
github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
github.com/xlab/handysort v0.0.0-20150421192137-fb3537ed64a1/go.mod h1:QcJo0QPSfTONNIgpN5RA8prR7fF8nkF6cTWTcNerRO8=
github.com/xlab/treeprint v0.0.0-20180616005107-d6fb6747feb6/go.mod h1:ce1O1j6UtZfjr22oyGxGLbauSBp2YVXpARAosm7dHBg= github.com/xlab/treeprint v0.0.0-20180616005107-d6fb6747feb6/go.mod h1:ce1O1j6UtZfjr22oyGxGLbauSBp2YVXpARAosm7dHBg=
github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q=
go.etcd.io/bbolt v1.3.3 h1:MUGmc65QhB3pIlaQ5bB4LwqSj6GIonVJXpZiaKNyaKk= go.etcd.io/bbolt v1.3.3 h1:MUGmc65QhB3pIlaQ5bB4LwqSj6GIonVJXpZiaKNyaKk=
@@ -535,8 +519,6 @@ k8s.io/apimachinery v0.0.0-20191028221656-72ed19daf4bb h1:ZUNsbuPdXWrj0rZziRfCWc
k8s.io/apimachinery v0.0.0-20191028221656-72ed19daf4bb/go.mod h1:llRdnznGEAqC3DcNm6yEj472xaFVfLM7hnYofMb12tQ= k8s.io/apimachinery v0.0.0-20191028221656-72ed19daf4bb/go.mod h1:llRdnznGEAqC3DcNm6yEj472xaFVfLM7hnYofMb12tQ=
k8s.io/apiserver v0.0.0-20191114103151-9ca1dc586682 h1:+FvAOv/4JyYgZanQI8h+UW9FCmLzyEz7EZunuET6p5g= k8s.io/apiserver v0.0.0-20191114103151-9ca1dc586682 h1:+FvAOv/4JyYgZanQI8h+UW9FCmLzyEz7EZunuET6p5g=
k8s.io/apiserver v0.0.0-20191114103151-9ca1dc586682/go.mod h1:Idob8Va6/sMX5SmwPLsU0pdvFlkwxuJ5x+fXMG8NbKE= k8s.io/apiserver v0.0.0-20191114103151-9ca1dc586682/go.mod h1:Idob8Va6/sMX5SmwPLsU0pdvFlkwxuJ5x+fXMG8NbKE=
k8s.io/cli-runtime v0.17.0 h1:XEuStbJBHCQlEKFyTQmceDKEWOSYHZkcYWKp3SsQ9Hk=
k8s.io/cli-runtime v0.17.0/go.mod h1:1E5iQpMODZq2lMWLUJELwRu2MLWIzwvMgDBpn3Y81Qo=
k8s.io/client-go v0.0.0-20191114101535-6c5935290e33 h1:07mhG/2oEoo3N+sHVOo0L9PJ/qvbk3N5n2dj8IWefnQ= k8s.io/client-go v0.0.0-20191114101535-6c5935290e33 h1:07mhG/2oEoo3N+sHVOo0L9PJ/qvbk3N5n2dj8IWefnQ=
k8s.io/client-go v0.0.0-20191114101535-6c5935290e33/go.mod h1:4L/zQOBkEf4pArQJ+CMk1/5xjA30B5oyWv+Bzb44DOw= k8s.io/client-go v0.0.0-20191114101535-6c5935290e33/go.mod h1:4L/zQOBkEf4pArQJ+CMk1/5xjA30B5oyWv+Bzb44DOw=
k8s.io/code-generator v0.0.0-20191004115455-8e001e5d1894 h1:NMYlxaF7rYQJk2E2IyrUhaX81zX24+dmoZdkPw0gJqI= k8s.io/code-generator v0.0.0-20191004115455-8e001e5d1894 h1:NMYlxaF7rYQJk2E2IyrUhaX81zX24+dmoZdkPw0gJqI=
@@ -549,9 +531,6 @@ k8s.io/klog v1.0.0 h1:Pt+yjF5aB1xDSVbau4VsWe+dQNzA0qv1LlXdC2dF6Q8=
k8s.io/klog v1.0.0/go.mod h1:4Bi6QPql/J/LkTDqv7R/cd3hPo4k2DG6Ptcz060Ez5I= k8s.io/klog v1.0.0/go.mod h1:4Bi6QPql/J/LkTDqv7R/cd3hPo4k2DG6Ptcz060Ez5I=
k8s.io/kube-openapi v0.0.0-20191107075043-30be4d16710a h1:UcxjrRMyNx/i/y8G7kPvLyy7rfbeuf1PYyBf973pgyU= k8s.io/kube-openapi v0.0.0-20191107075043-30be4d16710a h1:UcxjrRMyNx/i/y8G7kPvLyy7rfbeuf1PYyBf973pgyU=
k8s.io/kube-openapi v0.0.0-20191107075043-30be4d16710a/go.mod h1:1TqjTSzOxsLGIKfj0lK8EeCP7K1iUG65v09OM0/WG5E= k8s.io/kube-openapi v0.0.0-20191107075043-30be4d16710a/go.mod h1:1TqjTSzOxsLGIKfj0lK8EeCP7K1iUG65v09OM0/WG5E=
k8s.io/kubectl v0.17.0 h1:xD4EWlL+epc/JTO1gvSjmV9yiYF0Z2wiHK2DIek6URY=
k8s.io/kubectl v0.17.0/go.mod h1:jIPrUAW656Vzn9wZCCe0PC+oTcu56u2HgFD21Xbfk1s=
k8s.io/metrics v0.17.0/go.mod h1:EH1D3YAwN6d7bMelrElnLhLg72l/ERStyv2SIQVt6Do=
k8s.io/utils v0.0.0-20191114184206-e782cd3c129f h1:GiPwtSzdP43eI1hpPCbROQCCIgCuiMMNF8YUVLF3vJo= k8s.io/utils v0.0.0-20191114184206-e782cd3c129f h1:GiPwtSzdP43eI1hpPCbROQCCIgCuiMMNF8YUVLF3vJo=
k8s.io/utils v0.0.0-20191114184206-e782cd3c129f/go.mod h1:sZAwmy6armz5eXlNoLmJcl4F1QuKu7sr+mFQ0byX7Ew= k8s.io/utils v0.0.0-20191114184206-e782cd3c129f/go.mod h1:sZAwmy6armz5eXlNoLmJcl4F1QuKu7sr+mFQ0byX7Ew=
kubesphere.io/application v0.0.0-20190404151855-67ae7f915d4e/go.mod h1:NhUQ0ZUdFz8NTQ+SvQG0JUKAn+q71v3TPExjsjRPIZI= kubesphere.io/application v0.0.0-20190404151855-67ae7f915d4e/go.mod h1:NhUQ0ZUdFz8NTQ+SvQG0JUKAn+q71v3TPExjsjRPIZI=
@@ -572,13 +551,9 @@ sigs.k8s.io/controller-runtime v0.4.0 h1:wATM6/m+3w8lj8FXNaO6Fs/rq/vqoOjO1Q116Z9
sigs.k8s.io/controller-runtime v0.4.0/go.mod h1:ApC79lpY3PHW9xj/w9pj+lYkLgwAAUZwfXkME1Lajns= sigs.k8s.io/controller-runtime v0.4.0/go.mod h1:ApC79lpY3PHW9xj/w9pj+lYkLgwAAUZwfXkME1Lajns=
sigs.k8s.io/controller-tools v0.2.4 h1:la1h46EzElvWefWLqfsXrnsO3lZjpkI0asTpX6h8PLA= sigs.k8s.io/controller-tools v0.2.4 h1:la1h46EzElvWefWLqfsXrnsO3lZjpkI0asTpX6h8PLA=
sigs.k8s.io/controller-tools v0.2.4/go.mod h1:m/ztfQNocGYBgTTCmFdnK94uVvgxeZeE3LtJvd/jIzA= sigs.k8s.io/controller-tools v0.2.4/go.mod h1:m/ztfQNocGYBgTTCmFdnK94uVvgxeZeE3LtJvd/jIzA=
sigs.k8s.io/kustomize v2.0.3+incompatible h1:JUufWFNlI44MdtnjUqVnvh29rR37PQFzPbLXqhyOyX0=
sigs.k8s.io/kustomize v2.0.3+incompatible/go.mod h1:MkjgH3RdOWrievjo6c9T245dYlB5QeXV4WCbnt/PEpU=
sigs.k8s.io/structured-merge-diff v0.0.0-20190817042607-6149e4549fca h1:6dsH6AYQWbyZmtttJNe8Gq1cXOeS1BdV3eW37zHilAQ= sigs.k8s.io/structured-merge-diff v0.0.0-20190817042607-6149e4549fca h1:6dsH6AYQWbyZmtttJNe8Gq1cXOeS1BdV3eW37zHilAQ=
sigs.k8s.io/structured-merge-diff v0.0.0-20190817042607-6149e4549fca/go.mod h1:IIgPezJWb76P0hotTxzDbWsMYB8APh18qZnxkomBpxA= sigs.k8s.io/structured-merge-diff v0.0.0-20190817042607-6149e4549fca/go.mod h1:IIgPezJWb76P0hotTxzDbWsMYB8APh18qZnxkomBpxA=
sigs.k8s.io/testing_frameworks v0.1.2 h1:vK0+tvjF0BZ/RYFeZ1E6BYBwHJJXhjuZ3TdsEKH+UQM= sigs.k8s.io/testing_frameworks v0.1.2 h1:vK0+tvjF0BZ/RYFeZ1E6BYBwHJJXhjuZ3TdsEKH+UQM=
sigs.k8s.io/testing_frameworks v0.1.2/go.mod h1:ToQrwSC3s8Xf/lADdZp3Mktcql9CG0UAmdJG9th5i0w= sigs.k8s.io/testing_frameworks v0.1.2/go.mod h1:ToQrwSC3s8Xf/lADdZp3Mktcql9CG0UAmdJG9th5i0w=
sigs.k8s.io/yaml v1.1.0 h1:4A07+ZFc2wgJwo8YNlQpr1rVlgUDlxXHhPJciaPY5gs= sigs.k8s.io/yaml v1.1.0 h1:4A07+ZFc2wgJwo8YNlQpr1rVlgUDlxXHhPJciaPY5gs=
sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o= sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o=
vbom.ml/util v0.0.0-20160121211510-db5cfe13f5cc h1:MksmcCZQWAQJCTA5T0jgI/0sJ51AVm4Z41MrmfczEoc=
vbom.ml/util v0.0.0-20160121211510-db5cfe13f5cc/go.mod h1:so/NYdZXCz+E3ZpW0uAoCj6uzU2+8OWDFv/HxUSs7kI=

View File

@@ -70,9 +70,8 @@ type FluentBitList struct {
Items []FluentBit `json:"items"` Items []FluentBit `json:"items"`
} }
type FluentbitOutputsResult struct { type FluentbitOutputsResult struct {
Status int `json:"status" description:"response status"` Status int `json:"status" description:"response status"`
Error string `json:"error,omitempty" description:"debug information"` Error string `json:"error,omitempty" description:"debug information"`
Outputs []OutputPlugin `json:"outputs,omitempty" description:"array of fluent bit output plugins"` Outputs []OutputPlugin `json:"outputs,omitempty" description:"array of fluent bit output plugins"`
} }

View File

@@ -1,165 +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 (
"github.com/emicklei/go-restful"
"k8s.io/klog"
"kubesphere.io/kubesphere/pkg/constants"
"kubesphere.io/kubesphere/pkg/models/devops"
"kubesphere.io/kubesphere/pkg/server/errors"
"net/http"
)
func CreateDevOpsProjectCredentialHandler(request *restful.Request, resp *restful.Response) {
projectId := request.PathParameter("devops")
username := request.HeaderParameter(constants.UserNameHeader)
var credential *devops.JenkinsCredential
err := request.ReadEntity(&credential)
if err != nil {
klog.Errorf("%+v", err)
errors.ParseSvcErr(restful.NewError(http.StatusBadRequest, err.Error()), resp)
return
}
err = devops.CheckProjectUserInRole(username, projectId, []string{devops.ProjectOwner, devops.ProjectMaintainer})
if err != nil {
klog.Errorf("%+v", err)
errors.ParseSvcErr(restful.NewError(http.StatusForbidden, err.Error()), resp)
return
}
credentialId, err := devops.CreateProjectCredential(projectId, username, credential)
if err != nil {
klog.Errorf("%+v", err)
errors.ParseSvcErr(err, resp)
return
}
resp.WriteAsJson(struct {
Name string `json:"name"`
}{Name: credentialId})
return
}
func UpdateDevOpsProjectCredentialHandler(request *restful.Request, resp *restful.Response) {
projectId := request.PathParameter("devops")
username := request.HeaderParameter(constants.UserNameHeader)
credentialId := request.PathParameter("credential")
var credential *devops.JenkinsCredential
err := request.ReadEntity(&credential)
if err != nil {
klog.Errorf("%+v", err)
errors.ParseSvcErr(restful.NewError(http.StatusBadRequest, err.Error()), resp)
return
}
err = devops.CheckProjectUserInRole(username, projectId, []string{devops.ProjectOwner, devops.ProjectMaintainer})
if err != nil {
klog.Errorf("%+v", err)
errors.ParseSvcErr(restful.NewError(http.StatusForbidden, err.Error()), resp)
return
}
credentialId, err = devops.UpdateProjectCredential(projectId, credentialId, credential)
if err != nil {
klog.Errorf("%+v", err)
errors.ParseSvcErr(err, resp)
return
}
resp.WriteAsJson(struct {
Name string `json:"name"`
}{Name: credentialId})
return
}
func DeleteDevOpsProjectCredentialHandler(request *restful.Request, resp *restful.Response) {
projectId := request.PathParameter("devops")
username := request.HeaderParameter(constants.UserNameHeader)
credentialId := request.PathParameter("credential")
var credential *devops.JenkinsCredential
err := request.ReadEntity(&credential)
if err != nil {
klog.Errorf("%+v", err)
errors.ParseSvcErr(restful.NewError(http.StatusBadRequest, err.Error()), resp)
return
}
err = devops.CheckProjectUserInRole(username, projectId, []string{devops.ProjectOwner, devops.ProjectMaintainer})
if err != nil {
klog.Errorf("%+v", err)
errors.ParseSvcErr(restful.NewError(http.StatusForbidden, err.Error()), resp)
return
}
credentialId, err = devops.DeleteProjectCredential(projectId, credentialId, credential)
if err != nil {
klog.Errorf("%+v", err)
errors.ParseSvcErr(err, resp)
return
}
resp.WriteAsJson(struct {
Name string `json:"name"`
}{Name: credentialId})
return
}
func GetDevOpsProjectCredentialHandler(request *restful.Request, resp *restful.Response) {
projectId := request.PathParameter("devops")
username := request.HeaderParameter(constants.UserNameHeader)
credentialId := request.PathParameter("credential")
getContent := request.QueryParameter("content")
domain := request.QueryParameter("domain")
err := devops.CheckProjectUserInRole(username, projectId, []string{devops.ProjectOwner, devops.ProjectMaintainer})
if err != nil {
klog.Errorf("%+v", err)
errors.ParseSvcErr(restful.NewError(http.StatusForbidden, err.Error()), resp)
return
}
response, err := devops.GetProjectCredential(projectId, credentialId, domain, getContent)
if err != nil {
klog.Errorf("%+v", err)
errors.ParseSvcErr(err, resp)
return
}
resp.WriteAsJson(response)
return
}
func GetDevOpsProjectCredentialsHandler(request *restful.Request, resp *restful.Response) {
projectId := request.PathParameter("devops")
username := request.HeaderParameter(constants.UserNameHeader)
domain := request.QueryParameter("domain")
err := devops.CheckProjectUserInRole(username, projectId, []string{devops.ProjectOwner, devops.ProjectMaintainer})
if err != nil {
klog.Errorf("%+v", err)
errors.ParseSvcErr(restful.NewError(http.StatusForbidden, err.Error()), resp)
return
}
jenkinsCredentials, err := devops.GetProjectCredentials(projectId, domain)
if err != nil {
klog.Errorf("%+v", err)
errors.ParseSvcErr(err, resp)
return
}
resp.WriteAsJson(jenkinsCredentials)
return
}

View File

@@ -0,0 +1,54 @@
/*
*
* 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 openpitrix
import (
"github.com/emicklei/go-restful"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
"kubesphere.io/kubesphere/pkg/models/openpitrix/attachment"
"kubesphere.io/kubesphere/pkg/server/errors"
"kubesphere.io/kubesphere/pkg/simple/client"
"net/http"
)
func DescribeAttachment(req *restful.Request, resp *restful.Response) {
attachmentId := req.PathParameter("attachment")
fileName := req.QueryParameter("filename")
result, err := attachment.DescribeAttachment(attachmentId)
// file raw
if fileName != "" {
data := result.AttachmentContent[fileName]
resp.Write(data)
resp.Header().Set("Content-Type", "text/plain")
return
}
if _, notEnabled := err.(client.ClientSetNotEnabledError); notEnabled {
resp.WriteHeaderAndEntity(http.StatusNotImplemented, errors.Wrap(err))
return
}
if status.Code(err) == codes.NotFound {
resp.WriteHeaderAndEntity(http.StatusNotFound, errors.Wrap(err))
return
}
resp.WriteEntity(result)
}

View File

@@ -0,0 +1,328 @@
/*
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 tenant
import (
"github.com/emicklei/go-restful"
"k8s.io/api/core/v1"
rbacv1 "k8s.io/api/rbac/v1"
k8serr "k8s.io/apimachinery/pkg/api/errors"
"k8s.io/apimachinery/pkg/util/net"
"k8s.io/klog"
devopsv1alpha2 "kubesphere.io/kubesphere/pkg/api/devops/v1alpha2"
loggingv1alpha2 "kubesphere.io/kubesphere/pkg/api/logging/v1alpha2"
"kubesphere.io/kubesphere/pkg/apis/tenant/v1alpha1"
"kubesphere.io/kubesphere/pkg/apiserver/logging"
"kubesphere.io/kubesphere/pkg/constants"
"kubesphere.io/kubesphere/pkg/models/iam"
"kubesphere.io/kubesphere/pkg/models/metrics"
"kubesphere.io/kubesphere/pkg/models/resources/v1alpha2"
"kubesphere.io/kubesphere/pkg/models/tenant"
"kubesphere.io/kubesphere/pkg/models/workspaces"
"kubesphere.io/kubesphere/pkg/server/errors"
"kubesphere.io/kubesphere/pkg/server/params"
"kubesphere.io/kubesphere/pkg/utils/sliceutil"
"net/http"
"strings"
)
func ListWorkspaceRules(req *restful.Request, resp *restful.Response) {
workspace := req.PathParameter("workspace")
username := req.HeaderParameter(constants.UserNameHeader)
rules, err := iam.GetUserWorkspaceSimpleRules(workspace, username)
if err != nil {
resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err))
return
}
resp.WriteAsJson(rules)
}
func ListWorkspaces(req *restful.Request, resp *restful.Response) {
username := req.HeaderParameter(constants.UserNameHeader)
conditions, err := params.ParseConditions(req.QueryParameter(params.ConditionsParam))
orderBy := req.QueryParameter(params.OrderByParam)
limit, offset := params.ParsePaging(req.QueryParameter(params.PagingParam))
reverse := params.ParseReverse(req)
if orderBy == "" {
orderBy = v1alpha2.CreateTime
reverse = true
}
if err != nil {
resp.WriteHeaderAndEntity(http.StatusBadRequest, errors.Wrap(err))
return
}
result, err := tenant.ListWorkspaces(username, conditions, orderBy, reverse, limit, offset)
if err != nil {
resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err))
return
}
resp.WriteAsJson(result)
}
func DescribeWorkspace(req *restful.Request, resp *restful.Response) {
username := req.HeaderParameter(constants.UserNameHeader)
workspaceName := req.PathParameter("workspace")
result, err := tenant.DescribeWorkspace(username, workspaceName)
if err != nil {
klog.Errorf("describe workspace failed: %+v", err)
if k8serr.IsNotFound(err) {
resp.WriteHeaderAndEntity(http.StatusNotFound, errors.Wrap(err))
} else {
resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err))
}
return
}
resp.WriteAsJson(result)
}
func ListNamespacesByUsername(req *restful.Request, resp *restful.Response) {
ListNamespaces(req, resp)
}
func ListNamespaces(req *restful.Request, resp *restful.Response) {
workspace := req.PathParameter("workspace")
username := req.PathParameter("member")
// /workspaces/{workspace}/members/{username}/namespaces
if username == "" {
// /workspaces/{workspace}/namespaces
username = req.HeaderParameter(constants.UserNameHeader)
}
conditions, err := params.ParseConditions(req.QueryParameter(params.ConditionsParam))
orderBy := req.QueryParameter(params.OrderByParam)
limit, offset := params.ParsePaging(req.QueryParameter(params.PagingParam))
reverse := params.ParseReverse(req)
if err != nil {
resp.WriteHeaderAndEntity(http.StatusBadRequest, errors.Wrap(err))
return
}
conditions.Match[constants.WorkspaceLabelKey] = workspace
result, err := tenant.ListNamespaces(username, conditions, orderBy, reverse, limit, offset)
if err != nil {
resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err))
return
}
namespaces := make([]*v1.Namespace, 0)
for _, item := range result.Items {
namespaces = append(namespaces, item.(*v1.Namespace).DeepCopy())
}
namespaces = metrics.GetNamespacesWithMetrics(namespaces)
items := make([]interface{}, 0)
for _, item := range namespaces {
items = append(items, item)
}
result.Items = items
resp.WriteAsJson(result)
}
func CreateNamespace(req *restful.Request, resp *restful.Response) {
workspaceName := req.PathParameter("workspace")
username := req.HeaderParameter(constants.UserNameHeader)
var namespace v1.Namespace
err := req.ReadEntity(&namespace)
if err != nil {
resp.WriteHeaderAndEntity(http.StatusBadRequest, errors.Wrap(err))
return
}
workspace, err := tenant.GetWorkspace(workspaceName)
err = checkResourceQuotas(workspace)
if err != nil {
resp.WriteHeaderAndEntity(http.StatusForbidden, errors.Wrap(err))
return
}
if err != nil {
if k8serr.IsNotFound(err) {
resp.WriteHeaderAndEntity(http.StatusForbidden, errors.Wrap(err))
} else {
resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err))
}
return
}
created, err := tenant.CreateNamespace(workspaceName, &namespace, username)
if err != nil {
if k8serr.IsAlreadyExists(err) {
resp.WriteHeaderAndEntity(http.StatusConflict, err)
} else {
resp.WriteHeaderAndEntity(http.StatusInternalServerError, err)
}
return
}
resp.WriteAsJson(created)
}
func DeleteNamespace(req *restful.Request, resp *restful.Response) {
workspaceName := req.PathParameter("workspace")
namespaceName := req.PathParameter("namespace")
err := workspaces.DeleteNamespace(workspaceName, namespaceName)
if err != nil {
resp.WriteHeaderAndEntity(http.StatusBadRequest, errors.Wrap(err))
return
}
resp.WriteAsJson(errors.None)
}
func checkResourceQuotas(wokrspace *v1alpha1.Workspace) error {
return nil
}
func ListNamespaceRules(req *restful.Request, resp *restful.Response) {
namespace := req.PathParameter("namespace")
username := req.HeaderParameter(constants.UserNameHeader)
rules, err := iam.GetUserNamespaceSimpleRules(namespace, username)
if err != nil {
resp.WriteError(http.StatusInternalServerError, err)
return
}
resp.WriteAsJson(rules)
}
func LogQuery(req *restful.Request, resp *restful.Response) {
operation := req.QueryParameter("operation")
req, err := regenerateLoggingRequest(req)
switch {
case err != nil:
resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err))
case req != nil:
logging.LoggingQueryCluster(req, resp)
default:
if operation == "export" {
resp.Header().Set(restful.HEADER_ContentType, "text/plain")
resp.Header().Set("Content-Disposition", "attachment")
resp.Write(nil)
} else {
resp.WriteAsJson(loggingv1alpha2.QueryResult{Read: new(loggingv1alpha2.ReadResult)})
}
}
}
// override namespace query conditions
func regenerateLoggingRequest(req *restful.Request) (*restful.Request, error) {
username := req.HeaderParameter(constants.UserNameHeader)
// regenerate the request for log query
newUrl := net.FormatURL("http", "127.0.0.1", 80, "/kapis/logging.kubesphere.io/v1alpha2/cluster")
values := req.Request.URL.Query()
clusterRules, err := iam.GetUserClusterRules(username)
if err != nil {
klog.Errorln(err)
return nil, err
}
hasClusterLogAccess := iam.RulesMatchesRequired(clusterRules, rbacv1.PolicyRule{Verbs: []string{"get"}, Resources: []string{"*"}, APIGroups: []string{"logging.kubesphere.io"}})
// if the user is not a cluster admin
if !hasClusterLogAccess {
queryNamespaces := strings.Split(req.QueryParameter("namespaces"), ",")
// then the user can only view logs of namespaces he belongs to
namespaces := make([]string, 0)
roles, err := iam.GetUserRoles("", username)
if err != nil {
klog.Errorln(err)
return nil, err
}
for _, role := range roles {
if !sliceutil.HasString(namespaces, role.Namespace) && iam.RulesMatchesRequired(role.Rules, rbacv1.PolicyRule{Verbs: []string{"get"}, Resources: []string{"*"}, APIGroups: []string{"logging.kubesphere.io"}}) {
namespaces = append(namespaces, role.Namespace)
}
}
// if the user belongs to no namespace
// then no log visible
if len(namespaces) == 0 {
return nil, nil
} else if len(queryNamespaces) == 1 && queryNamespaces[0] == "" {
values.Set("namespaces", strings.Join(namespaces, ","))
} else {
inter := intersection(queryNamespaces, namespaces)
if len(inter) == 0 {
return nil, nil
}
values.Set("namespaces", strings.Join(inter, ","))
}
}
newUrl.RawQuery = values.Encode()
// forward the request to logging model
newHttpRequest, _ := http.NewRequest(http.MethodGet, newUrl.String(), nil)
return restful.NewRequest(newHttpRequest), nil
}
func intersection(s1, s2 []string) (inter []string) {
hash := make(map[string]bool)
for _, e := range s1 {
hash[e] = true
}
for _, e := range s2 {
// If elements present in the hashmap then append intersection list.
if hash[e] {
inter = append(inter, e)
}
}
//Remove dups from slice.
inter = removeDups(inter)
return
}
//Remove dups from slice.
func removeDups(elements []string) (nodups []string) {
encountered := make(map[string]bool)
for _, element := range elements {
if !encountered[element] {
nodups = append(nodups, element)
encountered[element] = true
}
}
return
}

View File

@@ -1,4 +0,0 @@
<table page-has-up="false" page-has-down="false" page-entry-newest="-9223372036854775805" page-entry-oldest="-9223372036854775807" class="pane hasPageData"><tr page-entry-id="-9223372036854775805" class="build-row single-line"><td class="build-row-cell"><div class="pane build-name"><div class="build-icon"><a href="/jenkins/job/j1/3/console" class="build-status-link"><img src="/jenkins/images/16x16/blue.png" alt="Success &gt; Console Output" tooltip="Success &gt; Console Output" style="width: 16px; height: 16px; " class="icon-blue icon-sm" /></a></div><a update-parent-class=".build-row" href="/jenkins/job/j1/3/" class="tip model-link inside build-link display-name">#3</a></div><div time="1484327939346" class="pane build-details"><a update-parent-class=".build-row" href="/jenkins/job/j1/3/" class="tip model-link inside build-link">Jan 13, 2017 9:18 AM</a></div><div class="pane build-controls"><div class="middle-align build-badge"></div></div><div class="left-bar"></div></td></tr><tr page-entry-id="-9223372036854775806" class="build-row single-line"><td class="build-row-cell"><div class="pane build-name"><div class="build-icon"><a href="/jenkins/job/j1/2/console" class="build-status-link"><img src="/jenkins/images/16x16/blue.png" alt="Success &gt; Console Output" tooltip="Success &gt; Console Output" style="width: 16px; height: 16px; " class="icon-blue icon-sm" /></a></div><a update-parent-class=".build-row" href="/jenkins/job/j1/2/" class="tip model-link inside build-link display-name">#2</a></div><div time="1484327935341" class="pane build-details"><a update-parent-class=".build-row" href="/jenkins/job/j1/2/" class="tip model-link inside build-link">Jan 13, 2017 9:18 AM</a></div><div class="pane build-controls"><div class="middle-align build-badge"></div></div><div class="left-bar"></div></td></tr><tr page-entry-id="-9223372036854775807" class="build-row single-line"><td class="build-row-cell"><div class="pane build-name"><div class="build-icon"><a href="/jenkins/job/j1/1/console" class="build-status-link"><img src="/jenkins/images/16x16/blue.png" alt="Success &gt; Console Output" tooltip="Success &gt; Console Output" style="width: 16px; height: 16px; " class="icon-blue icon-sm" /></a></div><a update-parent-class=".build-row" href="/jenkins/job/j1/1/" class="tip model-link inside build-link display-name">#1</a></div><div time="1484327924442" class="pane build-details"><a update-parent-class=".build-row" href="/jenkins/job/j1/1/" class="tip model-link inside build-link">Jan 13, 2017 9:18 AM</a></div><div class="pane build-controls"><div class="middle-align build-badge"></div></div><div class="left-bar"></div></td></tr></table>

View File

@@ -1,27 +0,0 @@
<?xml version='1.0' encoding='UTF-8'?>
<project>
<actions/>
<description>Some Job Description</description>
<keepDependencies>false</keepDependencies>
<properties>
<hudson.model.ParametersDefinitionProperty>
<parameterDefinitions>
<hudson.model.StringParameterDefinition>
<name>params1</name>
<description>description</description>
<defaultValue>defaultVal</defaultValue>
</hudson.model.StringParameterDefinition>
</parameterDefinitions>
</hudson.model.ParametersDefinitionProperty>
</properties>
<scm class="hudson.scm.NullSCM"/>
<canRoam>true</canRoam>
<disabled>false</disabled>
<blockBuildWhenDownstreamBuilding>false</blockBuildWhenDownstreamBuilding>
<blockBuildWhenUpstreamBuilding>false</blockBuildWhenUpstreamBuilding>
<triggers class="vector"/>
<concurrentBuild>false</concurrentBuild>
<builders/>
<publishers/>
<buildWrappers/>
</project>

View File

@@ -1,118 +0,0 @@
// Copyright 2015 Vadim Kravcenko
//
// Licensed under the Apache License, Version 2.0 (the "License"): you may
// not use this file except in compliance with the License. You may obtain
// a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
// License for the specific language governing permissions and limitations
// under the License.
package gojenkins
import (
"crypto/md5"
"errors"
"fmt"
"io"
"io/ioutil"
"os"
"path"
)
// Represents an Artifact
type Artifact struct {
Jenkins *Jenkins
Build *Build
FileName string
Path string
}
// Get raw byte data of Artifact
func (a Artifact) GetData() ([]byte, error) {
var data string
response, err := a.Jenkins.Requester.Get(a.Path, &data, nil)
if err != nil {
return nil, err
}
code := response.StatusCode
if code != 200 {
Error.Printf("Jenkins responded with StatusCode: %d", code)
return nil, errors.New("Could not get File Contents")
}
return []byte(data), nil
}
// Save artifact to a specific path, using your own filename.
func (a Artifact) Save(path string) (bool, error) {
data, err := a.GetData()
if err != nil {
return false, errors.New("No Data received, not saving file.")
}
if _, err = os.Stat(path); err == nil {
Warning.Println("Local Copy already exists, Overwriting...")
}
err = ioutil.WriteFile(path, data, 0644)
a.validateDownload(path)
if err != nil {
return false, err
}
return true, nil
}
// Save Artifact to directory using Artifact filename.
func (a Artifact) SaveToDir(dir string) (bool, error) {
if _, err := os.Stat(dir); err != nil {
Error.Printf("can't save artifact: directory %s does not exist", dir)
return false, fmt.Errorf("can't save artifact: directory %s does not exist", dir)
}
saved, err := a.Save(path.Join(dir, a.FileName))
if err != nil {
return saved, nil
}
return saved, nil
}
// Compare Remote and local MD5
func (a Artifact) validateDownload(path string) (bool, error) {
localHash := a.getMD5local(path)
fp := FingerPrint{Jenkins: a.Jenkins, Base: "/fingerprint/", Id: localHash, Raw: new(FingerPrintResponse)}
valid, err := fp.ValidateForBuild(a.FileName, a.Build)
if err != nil {
return false, err
}
if !valid {
return false, errors.New("FingerPrint of the downloaded artifact could not be verified")
}
return true, nil
}
// Get Local MD5
func (a Artifact) getMD5local(path string) string {
h := md5.New()
localFile, err := os.Open(path)
if err != nil {
return ""
}
buffer := make([]byte, 2^20)
n, err := localFile.Read(buffer)
defer localFile.Close()
for err == nil {
io.WriteString(h, string(buffer[0:n]))
n, err = localFile.Read(buffer)
}
return fmt.Sprintf("%x", h.Sum(nil))
}

View File

@@ -1,109 +0,0 @@
package gojenkins
import (
"io"
"strconv"
"strings"
"golang.org/x/net/html"
)
// Parse jenkins ajax response in order find the current jenkins build history
func parseBuildHistory(d io.Reader) []*History {
z := html.NewTokenizer(d)
depth := 0
buildRowCellDepth := -1
builds := make([]*History, 0)
var curBuild *History
for {
tt := z.Next()
switch tt {
case html.ErrorToken:
if z.Err() == io.EOF {
return builds
}
case html.SelfClosingTagToken:
tn, hasAttr := z.TagName()
// fmt.Println("START__", string(tn), hasAttr)
if hasAttr {
a := attr(z)
// <img src="/static/f2881562/images/16x16/red.png" alt="Failed &gt; Console Output" tooltip="Failed &gt; Console Output" style="width: 16px; height: 16px; " class="icon-red icon-sm" />
if string(tn) == "img" {
if hasCSSClass(a, "icon-sm") && buildRowCellDepth > -1 {
if alt, found := a["alt"]; found {
curBuild.BuildStatus = strings.Fields(alt)[0]
}
}
}
}
case html.StartTagToken:
depth++
tn, hasAttr := z.TagName()
// fmt.Println("START__", string(tn), hasAttr)
if hasAttr {
a := attr(z)
// <td class="build-row-cell">
if string(tn) == "td" {
if hasCSSClass(a, "build-row-cell") {
buildRowCellDepth = depth
curBuild = &History{}
builds = append(builds, curBuild)
}
}
// <a update-parent-class=".build-row" href="/job/appscode/job/43/job/build-binary/227/" class="tip model-link inside build-link display-name">#227</a>
if string(tn) == "a" {
if hasCSSClass(a, "build-link") && buildRowCellDepth > -1 {
if href, found := a["href"]; found {
parts := strings.Split(href, "/")
if num, err := strconv.Atoi(parts[len(parts)-2]); err == nil {
curBuild.BuildNumber = num
}
}
}
}
// <div time="1469024602546" class="pane build-details"> ... </div>
if string(tn) == "div" {
if hasCSSClass(a, "build-details") && buildRowCellDepth > -1 {
if t, found := a["time"]; found {
if msec, err := strconv.ParseInt(t, 10, 0); err == nil {
curBuild.BuildTimestamp = msec / 1000
}
}
}
}
}
case html.EndTagToken:
tn, _ := z.TagName()
if string(tn) == "td" && depth == buildRowCellDepth {
buildRowCellDepth = -1
curBuild = nil
}
depth--
}
}
}
func attr(z *html.Tokenizer) map[string]string {
a := make(map[string]string)
for {
k, v, more := z.TagAttr()
if k != nil && v != nil {
a[string(k)] = string(v)
}
if !more {
break
}
}
return a
}
func hasCSSClass(a map[string]string, className string) bool {
if classes, found := a["class"]; found {
for _, class := range strings.Fields(classes) {
if class == className {
return true
}
}
}
return false
}

View File

@@ -1,225 +0,0 @@
/*
Copyright 2018 The KubeSphere Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package gojenkins
const SSHCrenditalStaplerClass = "com.cloudbees.jenkins.plugins.sshcredentials.impl.BasicSSHUserPrivateKey"
const DirectSSHCrenditalStaplerClass = "com.cloudbees.jenkins.plugins.sshcredentials.impl.BasicSSHUserPrivateKey$DirectEntryPrivateKeySource"
const UsernamePassswordCredentialStaplerClass = "com.cloudbees.plugins.credentials.impl.UsernamePasswordCredentialsImpl"
const SecretTextCredentialStaplerClass = "org.jenkinsci.plugins.plaincredentials.impl.StringCredentialsImpl"
const KubeconfigCredentialStaplerClass = "com.microsoft.jenkins.kubernetes.credentials.KubeconfigCredentials"
const DirectKubeconfigCredentialStaperClass = "com.microsoft.jenkins.kubernetes.credentials.KubeconfigCredentials$DirectEntryKubeconfigSource"
const GLOBALScope = "GLOBAL"
type CreateSshCredentialRequest struct {
Credentials SshCredential `json:"credentials"`
}
type CreateUsernamePasswordCredentialRequest struct {
Credentials UsernamePasswordCredential `json:"credentials"`
}
type CreateSecretTextCredentialRequest struct {
Credentials SecretTextCredential `json:"credentials"`
}
type CreateKubeconfigCredentialRequest struct {
Credentials KubeconfigCredential `json:"credentials"`
}
type UsernamePasswordCredential struct {
Scope string `json:"scope"`
Id string `json:"id"`
Username string `json:"username"`
Password string `json:"password"`
Description string `json:"description"`
StaplerClass string `json:"stapler-class"`
}
type SshCredential struct {
Scope string `json:"scope"`
Id string `json:"id"`
Username string `json:"username"`
Passphrase string `json:"passphrase"`
KeySource PrivateKeySource `json:"privateKeySource"`
Description string `json:"description"`
StaplerClass string `json:"stapler-class"`
}
type SecretTextCredential struct {
Scope string `json:"scope"`
Id string `json:"id"`
Secret string `json:"secret"`
Description string `json:"description"`
StaplerClass string `json:"stapler-class"`
}
type KubeconfigCredential struct {
Scope string `json:"scope"`
Id string `json:"id"`
Description string `json:"description"`
KubeconfigSource KubeconfigSource `json:"kubeconfigSource"`
StaplerClass string `json:"stapler-class"`
}
type PrivateKeySource struct {
StaplerClass string `json:"stapler-class"`
PrivateKey string `json:"privateKey"`
}
type KubeconfigSource struct {
StaplerClass string `json:"stapler-class"`
Content string `json:"content"`
}
type CredentialResponse struct {
Id string `json:"id"`
TypeName string `json:"typeName"`
DisplayName string `json:"displayName"`
Fingerprint *struct {
FileName string `json:"file_name,omitempty" description:"Credential's display name and description"`
Hash string `json:"hash,omitempty" description:"Credential's hash"`
Usage []*struct {
Name string `json:"name,omitempty" description:"Jenkins pipeline full name"`
Ranges struct {
Ranges []*struct {
Start int `json:"start,omitempty" description:"Start build number"`
End int `json:"end,omitempty" description:"End build number"`
} `json:"ranges,omitempty"`
} `json:"ranges,omitempty" description:"The build number of all pipelines that use this credential"`
} `json:"usage,omitempty" description:"all usage of Credential"`
} `json:"fingerprint,omitempty" description:"usage of the Credential"`
Description string `json:"description,omitempty"`
Domain string `json:"domain"`
}
func NewCreateSshCredentialRequest(id, username, passphrase, privateKey, description string) *CreateSshCredentialRequest {
keySource := PrivateKeySource{
StaplerClass: DirectSSHCrenditalStaplerClass,
PrivateKey: privateKey,
}
sshCredential := SshCredential{
Scope: GLOBALScope,
Id: id,
Username: username,
Passphrase: passphrase,
KeySource: keySource,
Description: description,
StaplerClass: SSHCrenditalStaplerClass,
}
return &CreateSshCredentialRequest{
Credentials: sshCredential,
}
}
func NewCreateUsernamePasswordRequest(id, username, password, description string) *CreateUsernamePasswordCredentialRequest {
credential := UsernamePasswordCredential{
Scope: GLOBALScope,
Id: id,
Username: username,
Password: password,
Description: description,
StaplerClass: UsernamePassswordCredentialStaplerClass,
}
return &CreateUsernamePasswordCredentialRequest{
Credentials: credential,
}
}
func NewCreateSecretTextCredentialRequest(id, secret, description string) *CreateSecretTextCredentialRequest {
credential := SecretTextCredential{
Scope: GLOBALScope,
Id: id,
Secret: secret,
Description: description,
StaplerClass: SecretTextCredentialStaplerClass,
}
return &CreateSecretTextCredentialRequest{
Credentials: credential,
}
}
func NewCreateKubeconfigCredentialRequest(id, content, description string) *CreateKubeconfigCredentialRequest {
credentialSource := KubeconfigSource{
StaplerClass: DirectKubeconfigCredentialStaperClass,
Content: content,
}
credential := KubeconfigCredential{
Scope: GLOBALScope,
Id: id,
Description: description,
KubeconfigSource: credentialSource,
StaplerClass: KubeconfigCredentialStaplerClass,
}
return &CreateKubeconfigCredentialRequest{
credential,
}
}
func NewSshCredential(id, username, passphrase, privateKey, description string) *SshCredential {
keySource := PrivateKeySource{
StaplerClass: DirectSSHCrenditalStaplerClass,
PrivateKey: privateKey,
}
return &SshCredential{
Scope: GLOBALScope,
Id: id,
Username: username,
Passphrase: passphrase,
KeySource: keySource,
Description: description,
StaplerClass: SSHCrenditalStaplerClass,
}
}
func NewUsernamePasswordCredential(id, username, password, description string) *UsernamePasswordCredential {
return &UsernamePasswordCredential{
Scope: GLOBALScope,
Id: id,
Username: username,
Password: password,
Description: description,
StaplerClass: UsernamePassswordCredentialStaplerClass,
}
}
func NewSecretTextCredential(id, secret, description string) *SecretTextCredential {
return &SecretTextCredential{
Scope: GLOBALScope,
Id: id,
Secret: secret,
Description: description,
StaplerClass: SecretTextCredentialStaplerClass,
}
}
func NewKubeconfigCredential(id, content, description string) *KubeconfigCredential {
credentialSource := KubeconfigSource{
StaplerClass: DirectKubeconfigCredentialStaperClass,
Content: content,
}
return &KubeconfigCredential{
Scope: GLOBALScope,
Id: id,
Description: description,
KubeconfigSource: credentialSource,
StaplerClass: KubeconfigCredentialStaplerClass,
}
}

View File

@@ -1,44 +0,0 @@
// Copyright 2015 Vadim Kravcenko
//
// Licensed under the Apache License, Version 2.0 (the "License"): you may
// not use this file except in compliance with the License. You may obtain
// a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
// License for the specific language governing permissions and limitations
// under the License.
package gojenkins
type Executor struct {
Raw *ExecutorResponse
Jenkins *Jenkins
}
type ViewData struct {
Name string `json:"name"`
URL string `json:"url"`
}
type ExecutorResponse struct {
AssignedLabels []struct{} `json:"assignedLabels"`
Description interface{} `json:"description"`
Jobs []InnerJob `json:"jobs"`
Mode string `json:"mode"`
NodeDescription string `json:"nodeDescription"`
NodeName string `json:"nodeName"`
NumExecutors int64 `json:"numExecutors"`
OverallLoad struct{} `json:"overallLoad"`
PrimaryView struct {
Name string `json:"name"`
URL string `json:"url"`
} `json:"primaryView"`
QuietingDown bool `json:"quietingDown"`
SlaveAgentPort int64 `json:"slaveAgentPort"`
UnlabeledLoad struct{} `json:"unlabeledLoad"`
UseCrumbs bool `json:"useCrumbs"`
UseSecurity bool `json:"useSecurity"`
Views []ViewData `json:"views"`
}

View File

@@ -1,95 +0,0 @@
// Copyright 2015 Vadim Kravcenko
//
// Licensed under the Apache License, Version 2.0 (the "License"): you may
// not use this file except in compliance with the License. You may obtain
// a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
// License for the specific language governing permissions and limitations
// under the License.
package gojenkins
import (
"errors"
"fmt"
)
type FingerPrint struct {
Jenkins *Jenkins
Base string
Id string
Raw *FingerPrintResponse
}
type FingerPrintResponse struct {
FileName string `json:"fileName"`
Hash string `json:"hash"`
Original struct {
Name string
Number int64
} `json:"original"`
Timestamp int64 `json:"timestamp"`
Usage []struct {
Name string `json:"name"`
Ranges struct {
Ranges []struct {
End int64 `json:"end"`
Start int64 `json:"start"`
} `json:"ranges"`
} `json:"ranges"`
} `json:"usage"`
}
func (f FingerPrint) Valid() (bool, error) {
status, err := f.Poll()
if err != nil {
return false, err
}
if status != 200 || f.Raw.Hash != f.Id {
return false, fmt.Errorf("Jenkins says %s is Invalid or the Status is unknown", f.Id)
}
return true, nil
}
func (f FingerPrint) ValidateForBuild(filename string, build *Build) (bool, error) {
valid, err := f.Valid()
if err != nil {
return false, err
}
if valid {
return true, nil
}
if f.Raw.FileName != filename {
return false, errors.New("Filename does not Match")
}
if build != nil && f.Raw.Original.Name == build.Job.GetName() &&
f.Raw.Original.Number == build.GetBuildNumber() {
return true, nil
}
return false, nil
}
func (f FingerPrint) GetInfo() (*FingerPrintResponse, error) {
_, err := f.Poll()
if err != nil {
return nil, err
}
return f.Raw, nil
}
func (f FingerPrint) Poll() (int, error) {
response, err := f.Jenkins.Requester.GetJSON(f.Base+f.Id, f.Raw, nil)
if err != nil {
return 0, err
}
return response.StatusCode, nil
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,62 +0,0 @@
// Copyright 2015 Vadim Kravcenko
//
// Licensed under the Apache License, Version 2.0 (the "License"): you may
// not use this file except in compliance with the License. You may obtain
// a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
// License for the specific language governing permissions and limitations
// under the License.
package gojenkins
type Label struct {
Raw *LabelResponse
Jenkins *Jenkins
Base string
}
type MODE string
const (
NORMAL MODE = "NORMAL"
EXCLUSIVE = "EXCLUSIVE"
)
type LabelNode struct {
NodeName string `json:"nodeName"`
NodeDescription string `json:"nodeDescription"`
NumExecutors int64 `json:"numExecutors"`
Mode string `json:"mode"`
Class string `json:"_class"`
}
type LabelResponse struct {
Name string `json:"name"`
Description string `json:"description"`
Nodes []LabelNode `json:"nodes"`
Offline bool `json:"offline"`
IdleExecutors int64 `json:"idleExecutors"`
BusyExecutors int64 `json:"busyExecutors"`
TotalExecutors int64 `json:"totalExecutors"`
}
func (l *Label) GetName() string {
return l.Raw.Name
}
func (l *Label) GetNodes() []LabelNode {
return l.Raw.Nodes
}
func (l *Label) Poll() (int, error) {
response, err := l.Jenkins.Requester.GetJSON(l.Base, l.Raw, nil)
if err != nil {
return 0, err
}
return response.StatusCode, nil
}

View File

@@ -1,230 +0,0 @@
// Copyright 2015 Vadim Kravcenko
//
// Licensed under the Apache License, Version 2.0 (the "License"): you may
// not use this file except in compliance with the License. You may obtain
// a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
// License for the specific language governing permissions and limitations
// under the License.
package gojenkins
import "errors"
// Nodes
type Computers struct {
BusyExecutors int `json:"busyExecutors"`
Computers []*NodeResponse `json:"computer"`
DisplayName string `json:"displayName"`
TotalExecutors int `json:"totalExecutors"`
}
type Node struct {
Raw *NodeResponse
Jenkins *Jenkins
Base string
}
type NodeResponse struct {
Actions []interface{} `json:"actions"`
DisplayName string `json:"displayName"`
Executors []struct {
CurrentExecutable struct {
Number int `json:"number"`
URL string `json:"url"`
SubBuilds []struct {
Abort bool `json:"abort"`
Build interface{} `json:"build"`
BuildNumber int `json:"buildNumber"`
Duration string `json:"duration"`
Icon string `json:"icon"`
JobName string `json:"jobName"`
ParentBuildNumber int `json:"parentBuildNumber"`
ParentJobName string `json:"parentJobName"`
PhaseName string `json:"phaseName"`
Result string `json:"result"`
Retry bool `json:"retry"`
URL string `json:"url"`
} `json:"subBuilds"`
} `json:"currentExecutable"`
} `json:"executors"`
Icon string `json:"icon"`
IconClassName string `json:"iconClassName"`
Idle bool `json:"idle"`
JnlpAgent bool `json:"jnlpAgent"`
LaunchSupported bool `json:"launchSupported"`
LoadStatistics struct{} `json:"loadStatistics"`
ManualLaunchAllowed bool `json:"manualLaunchAllowed"`
MonitorData struct {
Hudson_NodeMonitors_ArchitectureMonitor interface{} `json:"hudson.node_monitors.ArchitectureMonitor"`
Hudson_NodeMonitors_ClockMonitor interface{} `json:"hudson.node_monitors.ClockMonitor"`
Hudson_NodeMonitors_DiskSpaceMonitor interface{} `json:"hudson.node_monitors.DiskSpaceMonitor"`
Hudson_NodeMonitors_ResponseTimeMonitor struct {
Average int64 `json:"average"`
} `json:"hudson.node_monitors.ResponseTimeMonitor"`
Hudson_NodeMonitors_SwapSpaceMonitor interface{} `json:"hudson.node_monitors.SwapSpaceMonitor"`
Hudson_NodeMonitors_TemporarySpaceMonitor interface{} `json:"hudson.node_monitors.TemporarySpaceMonitor"`
} `json:"monitorData"`
NumExecutors int64 `json:"numExecutors"`
Offline bool `json:"offline"`
OfflineCause struct{} `json:"offlineCause"`
OfflineCauseReason string `json:"offlineCauseReason"`
OneOffExecutors []interface{} `json:"oneOffExecutors"`
TemporarilyOffline bool `json:"temporarilyOffline"`
}
func (n *Node) Info() (*NodeResponse, error) {
_, err := n.Poll()
if err != nil {
return nil, err
}
return n.Raw, nil
}
func (n *Node) GetName() string {
return n.Raw.DisplayName
}
func (n *Node) Delete() (bool, error) {
resp, err := n.Jenkins.Requester.Post(n.Base+"/doDelete", nil, nil, nil)
if err != nil {
return false, err
}
return resp.StatusCode == 200, nil
}
func (n *Node) IsOnline() (bool, error) {
_, err := n.Poll()
if err != nil {
return false, err
}
return !n.Raw.Offline, nil
}
func (n *Node) IsTemporarilyOffline() (bool, error) {
_, err := n.Poll()
if err != nil {
return false, err
}
return n.Raw.TemporarilyOffline, nil
}
func (n *Node) IsIdle() (bool, error) {
_, err := n.Poll()
if err != nil {
return false, err
}
return n.Raw.Idle, nil
}
func (n *Node) IsJnlpAgent() (bool, error) {
_, err := n.Poll()
if err != nil {
return false, err
}
return n.Raw.JnlpAgent, nil
}
func (n *Node) SetOnline() (bool, error) {
_, err := n.Poll()
if err != nil {
return false, err
}
if n.Raw.Offline && !n.Raw.TemporarilyOffline {
return false, errors.New("Node is Permanently offline, can't bring it up")
}
if n.Raw.Offline && n.Raw.TemporarilyOffline {
return n.ToggleTemporarilyOffline()
}
return true, nil
}
func (n *Node) SetOffline(options ...interface{}) (bool, error) {
if !n.Raw.Offline {
return n.ToggleTemporarilyOffline(options...)
}
return false, errors.New("Node already Offline")
}
func (n *Node) ToggleTemporarilyOffline(options ...interface{}) (bool, error) {
state_before, err := n.IsTemporarilyOffline()
if err != nil {
return false, err
}
qr := map[string]string{"offlineMessage": "requested from gojenkins"}
if len(options) > 0 {
qr["offlineMessage"] = options[0].(string)
}
_, err = n.Jenkins.Requester.Post(n.Base+"/toggleOffline", nil, nil, qr)
if err != nil {
return false, err
}
new_state, err := n.IsTemporarilyOffline()
if err != nil {
return false, err
}
if state_before == new_state {
return false, errors.New("Node state not changed")
}
return true, nil
}
func (n *Node) Poll() (int, error) {
response, err := n.Jenkins.Requester.GetJSON(n.Base, n.Raw, nil)
if err != nil {
return 0, err
}
return response.StatusCode, nil
}
func (n *Node) LaunchNodeBySSH() (int, error) {
qr := map[string]string{
"json": "",
"Submit": "Launch slave agent",
}
response, err := n.Jenkins.Requester.Post(n.Base+"/launchSlaveAgent", nil, nil, qr)
if err != nil {
return 0, err
}
return response.StatusCode, nil
}
func (n *Node) Disconnect() (int, error) {
qr := map[string]string{
"offlineMessage": "",
"json": makeJson(map[string]string{"offlineMessage": ""}),
"Submit": "Yes",
}
response, err := n.Jenkins.Requester.Post(n.Base+"/doDisconnect", nil, nil, qr)
if err != nil {
return 0, err
}
return response.StatusCode, nil
}
func (n *Node) GetLogText() (string, error) {
var log string
_, err := n.Jenkins.Requester.Post(n.Base+"/log", nil, nil, nil)
if err != nil {
return "", err
}
qr := map[string]string{"start": "0"}
_, err = n.Jenkins.Requester.GetJSON(n.Base+"/logText/progressiveHtml/", &log, qr)
if err != nil {
return "", nil
}
return log, nil
}

View File

@@ -1,75 +0,0 @@
// Copyright 2015 Vadim Kravcenko
//
// Licensed under the Apache License, Version 2.0 (the "License"): you may
// not use this file except in compliance with the License. You may obtain
// a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
// License for the specific language governing permissions and limitations
// under the License.
package gojenkins
import (
"strconv"
)
type Plugins struct {
Jenkins *Jenkins
Raw *PluginResponse
Base string
Depth int
}
type PluginResponse struct {
Plugins []Plugin `json:"plugins"`
}
type Plugin struct {
Active bool `json:"active"`
BackupVersion interface{} `json:"backupVersion"`
Bundled bool `json:"bundled"`
Deleted bool `json:"deleted"`
Dependencies []struct {
Optional string `json:"optional"`
ShortName string `json:"shortname"`
Version string `json:"version"`
} `json:"dependencies"`
Downgradable bool `json:"downgradable"`
Enabled bool `json:"enabled"`
HasUpdate bool `json:"hasUpdate"`
LongName string `json:"longName"`
Pinned bool `json:"pinned"`
ShortName string `json:"shortName"`
SupportsDynamicLoad string `json:"supportsDynamicLoad"`
URL string `json:"url"`
Version string `json:"version"`
}
func (p *Plugins) Count() int {
return len(p.Raw.Plugins)
}
func (p *Plugins) Contains(name string) *Plugin {
for _, p := range p.Raw.Plugins {
if p.LongName == name || p.ShortName == name {
return &p
}
}
return nil
}
func (p *Plugins) Poll() (int, error) {
qr := map[string]string{
"depth": strconv.Itoa(p.Depth),
}
response, err := p.Jenkins.Requester.GetJSON(p.Base, p.Raw, qr)
if err != nil {
return 0, err
}
return response.StatusCode, nil
}

View File

@@ -1,158 +0,0 @@
// Copyright 2015 Vadim Kravcenko
//
// Licensed under the Apache License, Version 2.0 (the "License"): you may
// not use this file except in compliance with the License. You may obtain
// a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
// License for the specific language governing permissions and limitations
// under the License.
package gojenkins
import (
"strconv"
)
type Queue struct {
Jenkins *Jenkins
Raw *queueResponse
Base string
}
type queueResponse struct {
Items []taskResponse
}
type Task struct {
Raw *taskResponse
Jenkins *Jenkins
Queue *Queue
}
type taskResponse struct {
Actions []generalAction `json:"actions"`
Blocked bool `json:"blocked"`
Buildable bool `json:"buildable"`
BuildableStartMilliseconds int64 `json:"buildableStartMilliseconds"`
ID int64 `json:"id"`
InQueueSince int64 `json:"inQueueSince"`
Params string `json:"params"`
Pending bool `json:"pending"`
Stuck bool `json:"stuck"`
Task struct {
Color string `json:"color"`
Name string `json:"name"`
URL string `json:"url"`
} `json:"task"`
URL string `json:"url"`
Why string `json:"why"`
}
type generalAction struct {
Causes []map[string]interface{}
Parameters []parameter
}
type QueueItemResponse struct {
Actions []generalAction `json:"actions"`
Blocked bool `json:"blocked"`
Buildable bool `json:"buildable"`
ID int64 `json:"id"`
InQueueSince int64 `json:"inQueueSince"`
Params string `json:"params"`
Stuck bool `json:"stuck"`
Task struct {
Color string `json:"color"`
Name string `json:"name"`
URL string `json:"url"`
} `json:"task"`
URL string `json:"url"`
Cancelled bool `json:"cancelled"`
Why string `json:"why"`
Executable struct {
Number int64 `json:"number"`
Url string `json:"url"`
} `json:"executable"`
}
func (q *Queue) Tasks() []*Task {
tasks := make([]*Task, len(q.Raw.Items))
for i, t := range q.Raw.Items {
tasks[i] = &Task{Jenkins: q.Jenkins, Queue: q, Raw: &t}
}
return tasks
}
func (q *Queue) GetTaskById(id int64) *Task {
for _, t := range q.Raw.Items {
if t.ID == id {
return &Task{Jenkins: q.Jenkins, Queue: q, Raw: &t}
}
}
return nil
}
func (q *Queue) GetTasksForJob(name string) []*Task {
tasks := make([]*Task, 0)
for _, t := range q.Raw.Items {
if t.Task.Name == name {
tasks = append(tasks, &Task{Jenkins: q.Jenkins, Queue: q, Raw: &t})
}
}
return tasks
}
func (q *Queue) CancelTask(id int64) (bool, error) {
task := q.GetTaskById(id)
return task.Cancel()
}
func (t *Task) Cancel() (bool, error) {
qr := map[string]string{
"id": strconv.FormatInt(t.Raw.ID, 10),
}
response, err := t.Jenkins.Requester.Post(t.Jenkins.GetQueueUrl()+"/cancelItem", nil, t.Raw, qr)
if err != nil {
return false, err
}
return response.StatusCode == 200, nil
}
func (t *Task) GetJob() (*Job, error) {
return t.Jenkins.GetJob(t.Raw.Task.Name)
}
func (t *Task) GetWhy() string {
return t.Raw.Why
}
func (t *Task) GetParameters() []parameter {
for _, a := range t.Raw.Actions {
if a.Parameters != nil {
return a.Parameters
}
}
return nil
}
func (t *Task) GetCauses() []map[string]interface{} {
for _, a := range t.Raw.Actions {
if a.Causes != nil {
return a.Causes
}
}
return nil
}
func (q *Queue) Poll() (int, error) {
response, err := q.Jenkins.Requester.GetJSON(q.Base, q.Raw, nil)
if err != nil {
return 0, err
}
return response.StatusCode, nil
}

View File

@@ -1,61 +0,0 @@
// Copyright 2015 Vadim Kravcenko
//
// Licensed under the Apache License, Version 2.0 (the "License"): you may
// not use this file except in compliance with the License. You may obtain
// a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
// License for the specific language governing permissions and limitations
// under the License.
package gojenkins
import (
"encoding/json"
"strings"
"time"
"unicode/utf8"
)
func makeJson(data interface{}) string {
str, err := json.Marshal(data)
if err != nil {
return ""
}
return string(json.RawMessage(str))
}
func Reverse(s string) string {
size := len(s)
buf := make([]byte, size)
for start := 0; start < size; {
r, n := utf8.DecodeRuneInString(s[start:])
start += n
utf8.EncodeRune(buf[size-start:], r)
}
return string(buf)
}
type JenkinsBlueTime time.Time
func (t *JenkinsBlueTime) UnmarshalJSON(b []byte) error {
if b == nil || strings.Trim(string(b), "\"") == "null" {
*t = JenkinsBlueTime(time.Time{})
return nil
}
j, err := time.Parse("2006-01-02T15:04:05.000-0700", strings.Trim(string(b), "\""))
if err != nil {
return err
}
*t = JenkinsBlueTime(j)
return nil
}
func (t JenkinsBlueTime) MarshalJSON() ([]byte, error) {
return json.Marshal(time.Time(t))
}

View File

@@ -1,21 +0,0 @@
package utils
import (
"github.com/asaskevich/govalidator"
"kubesphere.io/kubesphere/pkg/gojenkins"
"net/http"
"strconv"
)
func GetJenkinsStatusCode(jenkinsErr error) int {
if code, err := strconv.Atoi(jenkinsErr.Error()); err == nil {
message := http.StatusText(code)
if !govalidator.IsNull(message) {
return code
}
}
if jErr, ok := jenkinsErr.(*gojenkins.ErrorResponse); ok {
return jErr.Response.StatusCode
}
return http.StatusInternalServerError
}

View File

@@ -1,94 +0,0 @@
// Copyright 2015 Vadim Kravcenko
//
// Licensed under the Apache License, Version 2.0 (the "License"): you may
// not use this file except in compliance with the License. You may obtain
// a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
// License for the specific language governing permissions and limitations
// under the License.
package gojenkins
import (
"errors"
"strconv"
)
type View struct {
Raw *ViewResponse
Jenkins *Jenkins
Base string
}
type ViewResponse struct {
Description string `json:"description"`
Jobs []InnerJob `json:"jobs"`
Name string `json:"name"`
Property []interface{} `json:"property"`
URL string `json:"url"`
}
var (
LIST_VIEW = "hudson.model.ListView"
NESTED_VIEW = "hudson.plugins.nested_view.NestedView"
MY_VIEW = "hudson.model.MyView"
DASHBOARD_VIEW = "hudson.plugins.view.dashboard.Dashboard"
PIPELINE_VIEW = "au.com.centrumsystems.hudson.plugin.buildpipeline.BuildPipelineView"
)
// Returns True if successfully added Job, otherwise false
func (v *View) AddJob(name string) (bool, error) {
url := "/addJobToView"
qr := map[string]string{"name": name}
resp, err := v.Jenkins.Requester.Post(v.Base+url, nil, nil, qr)
if err != nil {
return false, err
}
if resp.StatusCode == 200 {
return true, nil
}
return false, errors.New(strconv.Itoa(resp.StatusCode))
}
// Returns True if successfully deleted Job, otherwise false
func (v *View) DeleteJob(name string) (bool, error) {
url := "/removeJobFromView"
qr := map[string]string{"name": name}
resp, err := v.Jenkins.Requester.Post(v.Base+url, nil, nil, qr)
if err != nil {
return false, err
}
if resp.StatusCode == 200 {
return true, nil
}
return false, errors.New(strconv.Itoa(resp.StatusCode))
}
func (v *View) GetDescription() string {
return v.Raw.Description
}
func (v *View) GetJobs() []InnerJob {
return v.Raw.Jobs
}
func (v *View) GetName() string {
return v.Raw.Name
}
func (v *View) GetUrl() string {
return v.Raw.URL
}
func (v *View) Poll() (int, error) {
response, err := v.Jenkins.Requester.GetJSON(v.Base, v.Raw, nil)
if err != nil {
return 0, err
}
return response.StatusCode, nil
}

View File

@@ -1 +1,43 @@
package v1alpha2 package v1alpha2
import (
"kubesphere.io/kubesphere/pkg/client/clientset/versioned"
"kubesphere.io/kubesphere/pkg/client/informers/externalversions"
"kubesphere.io/kubesphere/pkg/models/devops"
devopsClient "kubesphere.io/kubesphere/pkg/simple/client/devops"
"kubesphere.io/kubesphere/pkg/simple/client/mysql"
"kubesphere.io/kubesphere/pkg/simple/client/s3"
"kubesphere.io/kubesphere/pkg/simple/client/sonarqube"
)
type ProjectPipelineHandler struct {
projectCredentialOperator devops.ProjectCredentialOperator
projectMemberOperator devops.ProjectMemberOperator
projectPipelineOperator devops.ProjectPipelineOperator
devopsOperator devops.DevopsOperator
projectOperator devops.ProjectOperator
}
type PipelineSonarHandler struct {
pipelineSonarGetter devops.PipelineSonarGetter
}
func NewProjectPipelineHandler(devopsClient devopsClient.Interface, dbClient *mysql.Database) ProjectPipelineHandler {
return ProjectPipelineHandler{
projectCredentialOperator: devops.NewProjectCredentialOperator(devopsClient, dbClient),
projectMemberOperator: devops.NewProjectMemberOperator(devopsClient, dbClient),
projectPipelineOperator: devops.NewProjectPipelineOperator(devopsClient),
devopsOperator: devops.NewDevopsOperator(devopsClient),
projectOperator: devops.NewProjectOperator(dbClient),
}
}
func NewPipelineSonarHandler(devopsClient devopsClient.Interface, sonarClient sonarqube.SonarInterface) PipelineSonarHandler {
return PipelineSonarHandler{
pipelineSonarGetter: devops.NewPipelineSonarGetter(devopsClient, sonarClient),
}
}
func NewS2iBinaryHandler(client versioned.Interface, informers externalversions.SharedInformerFactory, s3Client s3.Interface) S2iBinaryHandler {
return S2iBinaryHandler{devops.NewS2iBinaryUploader(client, informers, s3Client)}
}

View File

@@ -11,7 +11,7 @@ See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
*/ */
package devops package v1alpha2
import ( import (
"fmt" "fmt"
@@ -19,19 +19,19 @@ import (
"github.com/emicklei/go-restful" "github.com/emicklei/go-restful"
"k8s.io/klog" "k8s.io/klog"
"kubesphere.io/kubesphere/pkg/constants" "kubesphere.io/kubesphere/pkg/constants"
"kubesphere.io/kubesphere/pkg/models/devops"
"kubesphere.io/kubesphere/pkg/server/errors" "kubesphere.io/kubesphere/pkg/server/errors"
"kubesphere.io/kubesphere/pkg/server/params" "kubesphere.io/kubesphere/pkg/server/params"
"kubesphere.io/kubesphere/pkg/simple/client/devops"
"kubesphere.io/kubesphere/pkg/utils/reflectutils" "kubesphere.io/kubesphere/pkg/utils/reflectutils"
"net/http" "net/http"
) )
func GetDevOpsProjectMembersHandler(request *restful.Request, resp *restful.Response) { func (h ProjectPipelineHandler) GetDevOpsProjectMembersHandler(request *restful.Request, resp *restful.Response) {
projectId := request.PathParameter("devops") projectId := request.PathParameter("devops")
username := request.HeaderParameter(constants.UserNameHeader) username := request.HeaderParameter(constants.UserNameHeader)
err := devops.CheckProjectUserInRole(username, projectId, devops.AllRoleSlice) err := h.projectOperator.CheckProjectUserInRole(username, projectId, devops.AllRoleSlice)
if err != nil { if err != nil {
klog.Errorf("%+v", err) klog.Errorf("%+v", err)
errors.ParseSvcErr(restful.NewError(http.StatusForbidden, err.Error()), resp) errors.ParseSvcErr(restful.NewError(http.StatusForbidden, err.Error()), resp)
@@ -42,7 +42,7 @@ func GetDevOpsProjectMembersHandler(request *restful.Request, resp *restful.Resp
limit, offset := params.ParsePaging(request.QueryParameter(params.PagingParam)) limit, offset := params.ParsePaging(request.QueryParameter(params.PagingParam))
conditions, err := params.ParseConditions(request.QueryParameter(params.ConditionsParam)) conditions, err := params.ParseConditions(request.QueryParameter(params.ConditionsParam))
project, err := devops.GetProjectMembers(projectId, conditions, orderBy, reverse, limit, offset) project, err := h.projectMemberOperator.GetProjectMembers(projectId, conditions, orderBy, reverse, limit, offset)
if err != nil { if err != nil {
klog.Errorf("%+v", err) klog.Errorf("%+v", err)
@@ -54,19 +54,19 @@ func GetDevOpsProjectMembersHandler(request *restful.Request, resp *restful.Resp
return return
} }
func GetDevOpsProjectMemberHandler(request *restful.Request, resp *restful.Response) { func (h ProjectPipelineHandler) GetDevOpsProjectMemberHandler(request *restful.Request, resp *restful.Response) {
projectId := request.PathParameter("devops") projectId := request.PathParameter("devops")
username := request.HeaderParameter(constants.UserNameHeader) username := request.HeaderParameter(constants.UserNameHeader)
member := request.PathParameter("member") member := request.PathParameter("member")
err := devops.CheckProjectUserInRole(username, projectId, devops.AllRoleSlice) err := h.projectOperator.CheckProjectUserInRole(username, projectId, devops.AllRoleSlice)
if err != nil { if err != nil {
klog.Errorf("%+v", err) klog.Errorf("%+v", err)
errors.ParseSvcErr(restful.NewError(http.StatusForbidden, err.Error()), resp) errors.ParseSvcErr(restful.NewError(http.StatusForbidden, err.Error()), resp)
return return
} }
project, err := devops.GetProjectMember(projectId, member) project, err := h.projectMemberOperator.GetProjectMember(projectId, member)
if err != nil { if err != nil {
klog.Errorf("%+v", err) klog.Errorf("%+v", err)
@@ -78,11 +78,11 @@ func GetDevOpsProjectMemberHandler(request *restful.Request, resp *restful.Respo
return return
} }
func AddDevOpsProjectMemberHandler(request *restful.Request, resp *restful.Response) { func (h ProjectPipelineHandler) AddDevOpsProjectMemberHandler(request *restful.Request, resp *restful.Response) {
projectId := request.PathParameter("devops") projectId := request.PathParameter("devops")
username := request.HeaderParameter(constants.UserNameHeader) username := request.HeaderParameter(constants.UserNameHeader)
member := &devops.DevOpsProjectMembership{} member := &devops.ProjectMembership{}
err := request.ReadEntity(&member) err := request.ReadEntity(&member)
if err != nil { if err != nil {
klog.Errorf("%+v", err) klog.Errorf("%+v", err)
@@ -102,14 +102,15 @@ func AddDevOpsProjectMemberHandler(request *restful.Request, resp *restful.Respo
errors.ParseSvcErr(restful.NewError(http.StatusBadRequest, err.Error()), resp) errors.ParseSvcErr(restful.NewError(http.StatusBadRequest, err.Error()), resp)
return return
} }
err = h.projectOperator.CheckProjectUserInRole(username, projectId, []string{devops.ProjectOwner})
err = devops.CheckProjectUserInRole(username, projectId, []string{devops.ProjectOwner})
if err != nil { if err != nil {
klog.Errorf("%+v", err) klog.Errorf("%+v", err)
errors.ParseSvcErr(restful.NewError(http.StatusForbidden, err.Error()), resp) errors.ParseSvcErr(restful.NewError(http.StatusForbidden, err.Error()), resp)
return return
} }
project, err := devops.AddProjectMember(projectId, username, member)
member.GrantBy = username
project, err := h.projectMemberOperator.AddProjectMember(projectId, member)
if err != nil { if err != nil {
klog.Errorf("%+v", err) klog.Errorf("%+v", err)
@@ -121,11 +122,11 @@ func AddDevOpsProjectMemberHandler(request *restful.Request, resp *restful.Respo
return return
} }
func UpdateDevOpsProjectMemberHandler(request *restful.Request, resp *restful.Response) { func (h ProjectPipelineHandler) UpdateDevOpsProjectMemberHandler(request *restful.Request, resp *restful.Response) {
projectId := request.PathParameter("devops") projectId := request.PathParameter("devops")
username := request.HeaderParameter(constants.UserNameHeader) username := request.HeaderParameter(constants.UserNameHeader)
member := &devops.DevOpsProjectMembership{} member := &devops.ProjectMembership{}
err := request.ReadEntity(&member) err := request.ReadEntity(&member)
if err != nil { if err != nil {
klog.Errorf("%+v", err) klog.Errorf("%+v", err)
@@ -153,13 +154,13 @@ func UpdateDevOpsProjectMemberHandler(request *restful.Request, resp *restful.Re
return return
} }
err = devops.CheckProjectUserInRole(username, projectId, []string{devops.ProjectOwner}) err = h.projectOperator.CheckProjectUserInRole(username, projectId, []string{devops.ProjectOwner})
if err != nil { if err != nil {
klog.Errorf("%+v", err) klog.Errorf("%+v", err)
errors.ParseSvcErr(restful.NewError(http.StatusForbidden, err.Error()), resp) errors.ParseSvcErr(restful.NewError(http.StatusForbidden, err.Error()), resp)
return return
} }
project, err := devops.UpdateProjectMember(projectId, username, member) project, err := h.projectMemberOperator.UpdateProjectMember(projectId, member)
if err != nil { if err != nil {
klog.Errorf("%+v", err) klog.Errorf("%+v", err)
@@ -171,19 +172,19 @@ func UpdateDevOpsProjectMemberHandler(request *restful.Request, resp *restful.Re
return return
} }
func DeleteDevOpsProjectMemberHandler(request *restful.Request, resp *restful.Response) { func (h ProjectPipelineHandler) DeleteDevOpsProjectMemberHandler(request *restful.Request, resp *restful.Response) {
projectId := request.PathParameter("devops") projectId := request.PathParameter("devops")
username := request.HeaderParameter(constants.UserNameHeader) username := request.HeaderParameter(constants.UserNameHeader)
member := request.PathParameter("member") member := request.PathParameter("member")
err := devops.CheckProjectUserInRole(username, projectId, []string{devops.ProjectOwner}) err := h.projectOperator.CheckProjectUserInRole(username, projectId, []string{devops.ProjectOwner})
if err != nil { if err != nil {
klog.Errorf("%+v", err) klog.Errorf("%+v", err)
errors.ParseSvcErr(restful.NewError(http.StatusForbidden, err.Error()), resp) errors.ParseSvcErr(restful.NewError(http.StatusForbidden, err.Error()), resp)
return return
} }
username, err = devops.DeleteProjectMember(projectId, member) username, err = h.projectMemberOperator.DeleteProjectMember(projectId, member)
if err != nil { if err != nil {
klog.Errorf("%+v", err) klog.Errorf("%+v", err)
errors.ParseSvcErr(err, resp) errors.ParseSvcErr(err, resp)

View File

@@ -0,0 +1,49 @@
package v1alpha2
import (
"github.com/emicklei/go-restful"
"k8s.io/klog"
"kubesphere.io/kubesphere/pkg/constants"
"kubesphere.io/kubesphere/pkg/models/devops"
"kubesphere.io/kubesphere/pkg/server/errors"
"net/http"
)
func (h PipelineSonarHandler) GetPipelineSonarStatusHandler(request *restful.Request, resp *restful.Response) {
projectId := request.PathParameter("devops")
username := request.HeaderParameter(constants.UserNameHeader)
pipelineId := request.PathParameter("pipeline")
err := devops.CheckProjectUserInRole(username, projectId, devops.AllRoleSlice)
if err != nil {
klog.Errorf("%+v", err)
errors.ParseSvcErr(restful.NewError(http.StatusForbidden, err.Error()), resp)
return
}
sonarStatus, err := h.pipelineSonarGetter.GetPipelineSonar(projectId, pipelineId)
if err != nil {
klog.Errorf("%+v", err)
errors.ParseSvcErr(err, resp)
return
}
resp.WriteAsJson(sonarStatus)
}
func (h PipelineSonarHandler) GetMultiBranchesPipelineSonarStatusHandler(request *restful.Request, resp *restful.Response) {
projectId := request.PathParameter("devops")
username := request.HeaderParameter(constants.UserNameHeader)
pipelineId := request.PathParameter("pipeline")
branchId := request.PathParameter("branch")
err := devops.CheckProjectUserInRole(username, projectId, devops.AllRoleSlice)
if err != nil {
klog.Errorf("%+v", err)
errors.ParseSvcErr(restful.NewError(http.StatusForbidden, err.Error()), resp)
return
}
sonarStatus, err := h.pipelineSonarGetter.GetMultiBranchPipelineSonar(projectId, pipelineId, branchId)
if err != nil {
klog.Errorf("%+v", err)
errors.ParseSvcErr(err, resp)
return
}
resp.WriteAsJson(sonarStatus)
}

View File

@@ -11,30 +11,30 @@ See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
*/ */
package devops package v1alpha2
import ( import (
"github.com/emicklei/go-restful" "github.com/emicklei/go-restful"
"k8s.io/klog" "k8s.io/klog"
"kubesphere.io/kubesphere/pkg/api/devops/v1alpha2" "kubesphere.io/kubesphere/pkg/api/devops/v1alpha2"
"kubesphere.io/kubesphere/pkg/constants" "kubesphere.io/kubesphere/pkg/constants"
"kubesphere.io/kubesphere/pkg/models/devops"
"kubesphere.io/kubesphere/pkg/server/errors" "kubesphere.io/kubesphere/pkg/server/errors"
"kubesphere.io/kubesphere/pkg/simple/client/devops"
"net/http" "net/http"
) )
func GetDevOpsProjectHandler(request *restful.Request, resp *restful.Response) { func (h ProjectPipelineHandler) GetDevOpsProjectHandler(request *restful.Request, resp *restful.Response) {
projectId := request.PathParameter("devops") projectId := request.PathParameter("devops")
username := request.HeaderParameter(constants.UserNameHeader) username := request.HeaderParameter(constants.UserNameHeader)
err := devops.CheckProjectUserInRole(username, projectId, devops.AllRoleSlice) err := h.projectOperator.CheckProjectUserInRole(username, projectId, devops.AllRoleSlice)
if err != nil { if err != nil {
klog.Errorf("%+v", err) klog.Errorf("%+v", err)
errors.ParseSvcErr(restful.NewError(http.StatusForbidden, err.Error()), resp) errors.ParseSvcErr(restful.NewError(http.StatusForbidden, err.Error()), resp)
return return
} }
project, err := devops.GetProject(projectId) project, err := h.projectOperator.GetProject(projectId)
if err != nil { if err != nil {
klog.Errorf("%+v", err) klog.Errorf("%+v", err)
@@ -46,7 +46,7 @@ func GetDevOpsProjectHandler(request *restful.Request, resp *restful.Response) {
return return
} }
func UpdateProjectHandler(request *restful.Request, resp *restful.Response) { func (h ProjectPipelineHandler) UpdateProjectHandler(request *restful.Request, resp *restful.Response) {
projectId := request.PathParameter("devops") projectId := request.PathParameter("devops")
username := request.HeaderParameter(constants.UserNameHeader) username := request.HeaderParameter(constants.UserNameHeader)
@@ -58,13 +58,13 @@ func UpdateProjectHandler(request *restful.Request, resp *restful.Response) {
return return
} }
project.ProjectId = projectId project.ProjectId = projectId
err = devops.CheckProjectUserInRole(username, projectId, []string{devops.ProjectOwner}) err = h.projectOperator.CheckProjectUserInRole(username, projectId, []string{devops.ProjectOwner})
if err != nil { if err != nil {
klog.Errorf("%+v", err) klog.Errorf("%+v", err)
errors.ParseSvcErr(restful.NewError(http.StatusForbidden, err.Error()), resp) errors.ParseSvcErr(restful.NewError(http.StatusForbidden, err.Error()), resp)
return return
} }
project, err = devops.UpdateProject(project) project, err = h.projectOperator.UpdateProject(project)
if err != nil { if err != nil {
klog.Errorf("%+v", err) klog.Errorf("%+v", err)

View File

@@ -0,0 +1,122 @@
/*
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 v1alpha2
import (
"github.com/emicklei/go-restful"
"k8s.io/klog"
"kubesphere.io/kubesphere/pkg/constants"
"kubesphere.io/kubesphere/pkg/server/errors"
"kubesphere.io/kubesphere/pkg/simple/client/devops"
"net/http"
)
func (h ProjectPipelineHandler) CreateDevOpsProjectCredentialHandler(request *restful.Request, resp *restful.Response) {
projectId := request.PathParameter("devops")
username := request.HeaderParameter(constants.UserNameHeader)
var credential *devops.Credential
err := request.ReadEntity(&credential)
if err != nil {
klog.Errorf("%+v", err)
errors.ParseSvcErr(restful.NewError(http.StatusBadRequest, err.Error()), resp)
return
}
credentialId, err := h.projectCredentialOperator.CreateProjectCredential(projectId, username, credential)
if err != nil {
klog.Errorf("%+v", err)
errors.ParseSvcErr(err, resp)
return
}
resp.WriteAsJson(struct {
Name string `json:"name"`
}{Name: credentialId})
return
}
func (h ProjectPipelineHandler) UpdateDevOpsProjectCredentialHandler(request *restful.Request, resp *restful.Response) {
projectId := request.PathParameter("devops")
credentialId := request.PathParameter("credential")
var credential *devops.Credential
err := request.ReadEntity(&credential)
if err != nil {
klog.Errorf("%+v", err)
errors.ParseSvcErr(restful.NewError(http.StatusBadRequest, err.Error()), resp)
return
}
credentialId, err = h.projectCredentialOperator.UpdateProjectCredential(projectId, credentialId, credential)
if err != nil {
klog.Errorf("%+v", err)
errors.ParseSvcErr(err, resp)
return
}
resp.WriteAsJson(struct {
Name string `json:"name"`
}{Name: credentialId})
return
}
func (h ProjectPipelineHandler) DeleteDevOpsProjectCredentialHandler(request *restful.Request, resp *restful.Response) {
projectId := request.PathParameter("devops")
credentialId := request.PathParameter("credential")
credentialId, err := h.projectCredentialOperator.DeleteProjectCredential(projectId, credentialId)
if err != nil {
klog.Errorf("%+v", err)
errors.ParseSvcErr(err, resp)
return
}
resp.WriteAsJson(struct {
Name string `json:"name"`
}{Name: credentialId})
return
}
func (h ProjectPipelineHandler) GetDevOpsProjectCredentialHandler(request *restful.Request, resp *restful.Response) {
projectId := request.PathParameter("devops")
credentialId := request.PathParameter("credential")
getContent := request.QueryParameter("content")
response, err := h.projectCredentialOperator.GetProjectCredential(projectId, credentialId, getContent)
if err != nil {
klog.Errorf("%+v", err)
errors.ParseSvcErr(err, resp)
return
}
resp.WriteAsJson(response)
return
}
func (h ProjectPipelineHandler) GetDevOpsProjectCredentialsHandler(request *restful.Request, resp *restful.Response) {
projectId := request.PathParameter("devops")
jenkinsCredentials, err := h.projectCredentialOperator.GetProjectCredentials(projectId)
if err != nil {
klog.Errorf("%+v", err)
errors.ParseSvcErr(err, resp)
return
}
resp.WriteAsJson(jenkinsCredentials)
return
}

View File

@@ -11,18 +11,18 @@ See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
*/ */
package devops package v1alpha2
import ( import (
"github.com/emicklei/go-restful" "github.com/emicklei/go-restful"
"k8s.io/klog" "k8s.io/klog"
"kubesphere.io/kubesphere/pkg/constants" "kubesphere.io/kubesphere/pkg/constants"
"kubesphere.io/kubesphere/pkg/models/devops"
"kubesphere.io/kubesphere/pkg/server/errors" "kubesphere.io/kubesphere/pkg/server/errors"
"kubesphere.io/kubesphere/pkg/simple/client/devops"
"net/http" "net/http"
) )
func CreateDevOpsProjectPipelineHandler(request *restful.Request, resp *restful.Response) { func (h ProjectPipelineHandler) CreateDevOpsProjectPipelineHandler(request *restful.Request, resp *restful.Response) {
projectId := request.PathParameter("devops") projectId := request.PathParameter("devops")
username := request.HeaderParameter(constants.UserNameHeader) username := request.HeaderParameter(constants.UserNameHeader)
@@ -33,13 +33,13 @@ func CreateDevOpsProjectPipelineHandler(request *restful.Request, resp *restful.
errors.ParseSvcErr(restful.NewError(http.StatusBadRequest, err.Error()), resp) errors.ParseSvcErr(restful.NewError(http.StatusBadRequest, err.Error()), resp)
return return
} }
err = devops.CheckProjectUserInRole(username, projectId, []string{devops.ProjectOwner, devops.ProjectMaintainer}) err = h.projectOperator.CheckProjectUserInRole(username, projectId, []string{devops.ProjectOwner, devops.ProjectMaintainer})
if err != nil { if err != nil {
klog.Errorf("%+v", err) klog.Errorf("%+v", err)
errors.ParseSvcErr(restful.NewError(http.StatusForbidden, err.Error()), resp) errors.ParseSvcErr(restful.NewError(http.StatusForbidden, err.Error()), resp)
return return
} }
pipelineName, err := devops.CreateProjectPipeline(projectId, pipeline) pipelineName, err := h.projectPipelineOperator.CreateProjectPipeline(projectId, pipeline)
if err != nil { if err != nil {
klog.Errorf("%+v", err) klog.Errorf("%+v", err)
@@ -53,18 +53,18 @@ func CreateDevOpsProjectPipelineHandler(request *restful.Request, resp *restful.
return return
} }
func DeleteDevOpsProjectPipelineHandler(request *restful.Request, resp *restful.Response) { func (h ProjectPipelineHandler) DeleteDevOpsProjectPipelineHandler(request *restful.Request, resp *restful.Response) {
projectId := request.PathParameter("devops") projectId := request.PathParameter("devops")
username := request.HeaderParameter(constants.UserNameHeader) username := request.HeaderParameter(constants.UserNameHeader)
pipelineId := request.PathParameter("pipeline") pipelineId := request.PathParameter("pipeline")
err := devops.CheckProjectUserInRole(username, projectId, []string{devops.ProjectOwner, devops.ProjectMaintainer}) err := h.projectOperator.CheckProjectUserInRole(username, projectId, []string{devops.ProjectOwner, devops.ProjectMaintainer})
if err != nil { if err != nil {
klog.Errorf("%+v", err) klog.Errorf("%+v", err)
errors.ParseSvcErr(restful.NewError(http.StatusForbidden, err.Error()), resp) errors.ParseSvcErr(restful.NewError(http.StatusForbidden, err.Error()), resp)
return return
} }
pipelineName, err := devops.DeleteProjectPipeline(projectId, pipelineId) pipelineName, err := h.projectPipelineOperator.DeleteProjectPipeline(projectId, pipelineId)
if err != nil { if err != nil {
klog.Errorf("%+v", err) klog.Errorf("%+v", err)
@@ -78,7 +78,7 @@ func DeleteDevOpsProjectPipelineHandler(request *restful.Request, resp *restful.
return return
} }
func UpdateDevOpsProjectPipelineHandler(request *restful.Request, resp *restful.Response) { func (h ProjectPipelineHandler) UpdateDevOpsProjectPipelineHandler(request *restful.Request, resp *restful.Response) {
projectId := request.PathParameter("devops") projectId := request.PathParameter("devops")
username := request.HeaderParameter(constants.UserNameHeader) username := request.HeaderParameter(constants.UserNameHeader)
@@ -90,13 +90,13 @@ func UpdateDevOpsProjectPipelineHandler(request *restful.Request, resp *restful.
errors.ParseSvcErr(restful.NewError(http.StatusBadRequest, err.Error()), resp) errors.ParseSvcErr(restful.NewError(http.StatusBadRequest, err.Error()), resp)
return return
} }
err = devops.CheckProjectUserInRole(username, projectId, []string{devops.ProjectOwner, devops.ProjectMaintainer}) err = h.projectOperator.CheckProjectUserInRole(username, projectId, []string{devops.ProjectOwner, devops.ProjectMaintainer})
if err != nil { if err != nil {
klog.Errorf("%+v", err) klog.Errorf("%+v", err)
errors.ParseSvcErr(restful.NewError(http.StatusForbidden, err.Error()), resp) errors.ParseSvcErr(restful.NewError(http.StatusForbidden, err.Error()), resp)
return return
} }
pipelineName, err := devops.UpdateProjectPipeline(projectId, pipelineId, pipeline) pipelineName, err := h.projectPipelineOperator.UpdateProjectPipeline(projectId, pipelineId, pipeline)
if err != nil { if err != nil {
klog.Errorf("%+v", err) klog.Errorf("%+v", err)
@@ -110,19 +110,19 @@ func UpdateDevOpsProjectPipelineHandler(request *restful.Request, resp *restful.
return return
} }
func GetDevOpsProjectPipelineHandler(request *restful.Request, resp *restful.Response) { func (h ProjectPipelineHandler) GetDevOpsProjectPipelineConfigHandler(request *restful.Request, resp *restful.Response) {
projectId := request.PathParameter("devops") projectId := request.PathParameter("devops")
username := request.HeaderParameter(constants.UserNameHeader) username := request.HeaderParameter(constants.UserNameHeader)
pipelineId := request.PathParameter("pipeline") pipelineId := request.PathParameter("pipeline")
err := devops.CheckProjectUserInRole(username, projectId, []string{devops.ProjectOwner, devops.ProjectMaintainer}) err := h.projectOperator.CheckProjectUserInRole(username, projectId, []string{devops.ProjectOwner, devops.ProjectMaintainer})
if err != nil { if err != nil {
klog.Errorf("%+v", err) klog.Errorf("%+v", err)
errors.ParseSvcErr(restful.NewError(http.StatusForbidden, err.Error()), resp) errors.ParseSvcErr(restful.NewError(http.StatusForbidden, err.Error()), resp)
return return
} }
pipeline, err := devops.GetProjectPipeline(projectId, pipelineId) pipeline, err := h.projectPipelineOperator.GetProjectPipelineConfig(projectId, pipelineId)
if err != nil { if err != nil {
klog.Errorf("%+v", err) klog.Errorf("%+v", err)
@@ -133,42 +133,3 @@ func GetDevOpsProjectPipelineHandler(request *restful.Request, resp *restful.Res
resp.WriteAsJson(pipeline) resp.WriteAsJson(pipeline)
return return
} }
func GetPipelineSonarStatusHandler(request *restful.Request, resp *restful.Response) {
projectId := request.PathParameter("devops")
username := request.HeaderParameter(constants.UserNameHeader)
pipelineId := request.PathParameter("pipeline")
err := devops.CheckProjectUserInRole(username, projectId, devops.AllRoleSlice)
if err != nil {
klog.Errorf("%+v", err)
errors.ParseSvcErr(restful.NewError(http.StatusForbidden, err.Error()), resp)
return
}
sonarStatus, err := devops.GetPipelineSonar(projectId, pipelineId)
if err != nil {
klog.Errorf("%+v", err)
errors.ParseSvcErr(err, resp)
return
}
resp.WriteAsJson(sonarStatus)
}
func GetMultiBranchesPipelineSonarStatusHandler(request *restful.Request, resp *restful.Response) {
projectId := request.PathParameter("devops")
username := request.HeaderParameter(constants.UserNameHeader)
pipelineId := request.PathParameter("pipeline")
branchId := request.PathParameter("branch")
err := devops.CheckProjectUserInRole(username, projectId, devops.AllRoleSlice)
if err != nil {
klog.Errorf("%+v", err)
errors.ParseSvcErr(restful.NewError(http.StatusForbidden, err.Error()), resp)
return
}
sonarStatus, err := devops.GetMultiBranchPipelineSonar(projectId, pipelineId, branchId)
if err != nil {
klog.Errorf("%+v", err)
errors.ParseSvcErr(err, resp)
return
}
resp.WriteAsJson(sonarStatus)
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,4 +1,4 @@
package devops package v1alpha2
import ( import (
"code.cloudfoundry.org/bytefmt" "code.cloudfoundry.org/bytefmt"
@@ -11,7 +11,11 @@ import (
"net/http" "net/http"
) )
func UploadS2iBinary(req *restful.Request, resp *restful.Response) { type S2iBinaryHandler struct {
s2iUploader devops.S2iBinaryUploader
}
func (h S2iBinaryHandler) UploadS2iBinaryHandler(req *restful.Request, resp *restful.Response) {
ns := req.PathParameter("namespace") ns := req.PathParameter("namespace")
name := req.PathParameter("s2ibinary") name := req.PathParameter("s2ibinary")
@@ -62,7 +66,7 @@ func UploadS2iBinary(req *restful.Request, resp *restful.Response) {
} }
} }
s2ibin, err := devops.UploadS2iBinary(ns, name, filemd5, req.Request.MultipartForm.File["s2ibinary"][0]) s2ibin, err := h.s2iUploader.UploadS2iBinary(ns, name, filemd5, req.Request.MultipartForm.File["s2ibinary"][0])
if err != nil { if err != nil {
klog.Errorf("%+v", err) klog.Errorf("%+v", err)
errors.ParseSvcErr(err, resp) errors.ParseSvcErr(err, resp)
@@ -72,11 +76,11 @@ func UploadS2iBinary(req *restful.Request, resp *restful.Response) {
} }
func DownloadS2iBinary(req *restful.Request, resp *restful.Response) { func (h S2iBinaryHandler) DownloadS2iBinaryHandler(req *restful.Request, resp *restful.Response) {
ns := req.PathParameter("namespace") ns := req.PathParameter("namespace")
name := req.PathParameter("s2ibinary") name := req.PathParameter("s2ibinary")
fileName := req.PathParameter("file") fileName := req.PathParameter("file")
url, err := devops.DownloadS2iBinary(ns, name, fileName) url, err := h.s2iUploader.DownloadS2iBinary(ns, name, fileName)
if err != nil { if err != nil {
klog.Errorf("%+v", err) klog.Errorf("%+v", err)
errors.ParseSvcErr(err, resp) errors.ParseSvcErr(err, resp)

View File

@@ -0,0 +1,154 @@
/*
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 v1alpha2
import (
"github.com/emicklei/go-restful"
"k8s.io/klog"
"kubesphere.io/kubesphere/pkg/api"
devopsv1alpha2 "kubesphere.io/kubesphere/pkg/api/devops/v1alpha2"
"kubesphere.io/kubesphere/pkg/constants"
"kubesphere.io/kubesphere/pkg/server/errors"
"kubesphere.io/kubesphere/pkg/server/params"
"net/http"
)
func (h *tenantHandler) DeleteDevOpsProjectHandler(req *restful.Request, resp *restful.Response) {
projectId := req.PathParameter("devops")
workspaceName := req.PathParameter("workspace")
username := req.HeaderParameter(constants.UserNameHeader)
_, err := h.tenant.GetWorkspace(workspaceName)
if err != nil {
klog.Errorf("%+v", err)
errors.ParseSvcErr(restful.NewError(http.StatusBadRequest, err.Error()), resp)
return
}
err = h.tenant.DeleteDevOpsProject(projectId, username)
if err != nil {
klog.Errorf("%+v", err)
errors.ParseSvcErr(err, resp)
return
}
resp.WriteAsJson(errors.None)
}
func (h *tenantHandler) CreateDevOpsProjectHandler(req *restful.Request, resp *restful.Response) {
workspaceName := req.PathParameter("workspace")
username := req.HeaderParameter(constants.UserNameHeader)
var devops devopsv1alpha2.DevOpsProject
err := req.ReadEntity(&devops)
if err != nil {
klog.Infof("%+v", err)
errors.ParseSvcErr(restful.NewError(http.StatusBadRequest, err.Error()), resp)
return
}
klog.Infoln("create workspace", username, workspaceName, devops)
project, err := h.tenant.CreateDevOpsProject(username, workspaceName, &devops)
if err != nil {
klog.Errorf("%+v", err)
errors.ParseSvcErr(err, resp)
return
}
resp.WriteAsJson(project)
}
func (h *tenantHandler) GetDevOpsProjectsCountHandler(req *restful.Request, resp *restful.Response) {
username := req.HeaderParameter(constants.UserNameHeader)
result, err := h.tenant.GetDevOpsProjectsCount(username)
if err != nil {
klog.Errorf("%+v", err)
errors.ParseSvcErr(err, resp)
return
}
resp.WriteAsJson(struct {
Count uint32 `json:"count"`
}{Count: result})
}
func (h *tenantHandler) ListDevOpsProjectsHandler(req *restful.Request, resp *restful.Response) {
workspace := req.PathParameter("workspace")
username := req.PathParameter("member")
if username == "" {
username = req.HeaderParameter(constants.UserNameHeader)
}
orderBy := req.QueryParameter(params.OrderByParam)
reverse := params.GetBoolValueWithDefault(req, params.ReverseParam, false)
limit, offset := params.ParsePaging(req)
conditions, err := params.ParseConditions(req)
if err != nil {
klog.Errorf("%+v", err)
errors.ParseSvcErr(restful.NewError(http.StatusBadRequest, err.Error()), resp)
return
}
result, err := h.tenant.ListDevOpsProjects(workspace, username, conditions, orderBy, reverse, limit, offset)
if err != nil {
klog.Errorf("%+v", err)
errors.ParseSvcErr(err, resp)
return
}
resp.WriteAsJson(result)
}
func (h *tenantHandler) ListDevOpsRules(req *restful.Request, resp *restful.Response) {
devops := req.PathParameter("devops")
username := req.HeaderParameter(constants.UserNameHeader)
rules, err := h.tenant.GetUserDevOpsSimpleRules(username, devops)
if err != nil {
klog.Errorf("%+v", err)
errors.ParseSvcErr(err, resp)
return
}
resp.WriteAsJson(rules)
}
func (h *tenantHandler) ListDevopsRules(req *restful.Request, resp *restful.Response) {
devops := req.PathParameter("devops")
username := req.HeaderParameter(constants.UserNameHeader)
rules, err := h.tenant.GetUserDevOpsSimpleRules(username, devops)
if err != nil {
api.HandleInternalError(resp, err)
return
}
resp.WriteAsJson(rules)
}

View File

@@ -8,7 +8,6 @@ import (
"k8s.io/apimachinery/pkg/util/net" "k8s.io/apimachinery/pkg/util/net"
"k8s.io/klog" "k8s.io/klog"
"kubesphere.io/kubesphere/pkg/api" "kubesphere.io/kubesphere/pkg/api"
devopsv1alpha2 "kubesphere.io/kubesphere/pkg/api/devops/v1alpha2"
loggingv1alpha2 "kubesphere.io/kubesphere/pkg/api/logging/v1alpha2" loggingv1alpha2 "kubesphere.io/kubesphere/pkg/api/logging/v1alpha2"
"kubesphere.io/kubesphere/pkg/apiserver/logging" "kubesphere.io/kubesphere/pkg/apiserver/logging"
"kubesphere.io/kubesphere/pkg/constants" "kubesphere.io/kubesphere/pkg/constants"
@@ -184,45 +183,6 @@ func (h *tenantHandler) DeleteNamespace(req *restful.Request, resp *restful.Resp
resp.WriteAsJson(errors.None) resp.WriteAsJson(errors.None)
} }
func (h *tenantHandler) ListDevopsProjects(req *restful.Request, resp *restful.Response) {
workspace := req.PathParameter("workspace")
username := req.PathParameter("member")
if username == "" {
username = req.HeaderParameter(constants.UserNameHeader)
}
orderBy := params.GetStringValueWithDefault(req, params.OrderByParam, v1alpha2.CreateTime)
limit, offset := params.ParsePaging(req)
reverse := params.GetBoolValueWithDefault(req, params.ReverseParam, true)
conditions, err := params.ParseConditions(req)
if err != nil {
api.HandleBadRequest(resp, err)
return
}
result, err := tenant.ListDevopsProjects(workspace, username, conditions, orderBy, reverse, limit, offset)
if err != nil {
api.HandleInternalError(resp, err)
return
}
resp.WriteAsJson(result)
}
func (h *tenantHandler) GetDevOpsProjectsCount(req *restful.Request, resp *restful.Response) {
username := req.HeaderParameter(constants.UserNameHeader)
result, err := tenant.GetDevOpsProjectsCount(username)
if err != nil {
api.HandleInternalError(resp, err)
return
}
resp.WriteAsJson(struct {
Count uint32 `json:"count"`
}{Count: result})
}
func (h *tenantHandler) DeleteDevopsProject(req *restful.Request, resp *restful.Response) { func (h *tenantHandler) DeleteDevopsProject(req *restful.Request, resp *restful.Response) {
projectId := req.PathParameter("devops") projectId := req.PathParameter("devops")
workspace := req.PathParameter("workspace") workspace := req.PathParameter("workspace")
@@ -235,7 +195,7 @@ func (h *tenantHandler) DeleteDevopsProject(req *restful.Request, resp *restful.
return return
} }
err = tenant.DeleteDevOpsProject(projectId, username) err = h.tenant.DeleteDevOpsProject(projectId, username)
if err != nil { if err != nil {
api.HandleInternalError(resp, err) api.HandleInternalError(resp, err)
@@ -245,30 +205,6 @@ func (h *tenantHandler) DeleteDevopsProject(req *restful.Request, resp *restful.
resp.WriteAsJson(errors.None) resp.WriteAsJson(errors.None)
} }
func (h *tenantHandler) CreateDevopsProject(req *restful.Request, resp *restful.Response) {
workspaceName := req.PathParameter("workspace")
username := req.HeaderParameter(constants.UserNameHeader)
var devops devopsv1alpha2.DevOpsProject
err := req.ReadEntity(&devops)
if err != nil {
api.HandleInternalError(resp, err)
return
}
project, err := tenant.CreateDevopsProject(username, workspaceName, &devops)
if err != nil {
api.HandleInternalError(resp, err)
return
}
resp.WriteAsJson(project)
}
func (h *tenantHandler) ListNamespaceRules(req *restful.Request, resp *restful.Response) { func (h *tenantHandler) ListNamespaceRules(req *restful.Request, resp *restful.Response) {
namespace := req.PathParameter("namespace") namespace := req.PathParameter("namespace")
username := req.HeaderParameter(constants.UserNameHeader) username := req.HeaderParameter(constants.UserNameHeader)
@@ -283,21 +219,6 @@ func (h *tenantHandler) ListNamespaceRules(req *restful.Request, resp *restful.R
resp.WriteAsJson(rules) resp.WriteAsJson(rules)
} }
func (h *tenantHandler) ListDevopsRules(req *restful.Request, resp *restful.Response) {
devops := req.PathParameter("devops")
username := req.HeaderParameter(constants.UserNameHeader)
rules, err := tenant.GetUserDevopsSimpleRules(username, devops)
if err != nil {
api.HandleInternalError(resp, err)
return
}
resp.WriteAsJson(rules)
}
func (h *tenantHandler) LogQuery(req *restful.Request, resp *restful.Response) { func (h *tenantHandler) LogQuery(req *restful.Request, resp *restful.Response) {
operation := req.QueryParameter("operation") operation := req.QueryParameter("operation")
req, err := h.regenerateLoggingRequest(req) req, err := h.regenerateLoggingRequest(req)

View File

@@ -103,7 +103,7 @@ func AddToContainer(c *restful.Container) error {
Metadata(restfulspec.KeyOpenAPITags, []string{constants.TenantResourcesTag})) Metadata(restfulspec.KeyOpenAPITags, []string{constants.TenantResourcesTag}))
ws.Route(ws.GET("/workspaces/{workspace}/devops"). ws.Route(ws.GET("/workspaces/{workspace}/devops").
To(handler.ListDevopsProjects). To(handler.ListDevOpsProjectsHandler).
Param(ws.PathParameter("workspace", "workspace name")). Param(ws.PathParameter("workspace", "workspace name")).
Param(ws.QueryParameter(params.PagingParam, "page"). Param(ws.QueryParameter(params.PagingParam, "page").
Required(false). Required(false).
@@ -115,7 +115,7 @@ func AddToContainer(c *restful.Container) error {
Doc("List devops projects for the current user"). Doc("List devops projects for the current user").
Metadata(restfulspec.KeyOpenAPITags, []string{constants.TenantResourcesTag})) Metadata(restfulspec.KeyOpenAPITags, []string{constants.TenantResourcesTag}))
ws.Route(ws.GET("/workspaces/{workspace}/members/{member}/devops"). ws.Route(ws.GET("/workspaces/{workspace}/members/{member}/devops").
To(handler.ListDevopsProjects). To(handler.ListDevOpsProjectsHandler).
Param(ws.PathParameter("workspace", "workspace name")). Param(ws.PathParameter("workspace", "workspace name")).
Param(ws.PathParameter("member", "workspace member's username")). Param(ws.PathParameter("member", "workspace member's username")).
Param(ws.QueryParameter(params.PagingParam, "page"). Param(ws.QueryParameter(params.PagingParam, "page").
@@ -129,14 +129,14 @@ func AddToContainer(c *restful.Container) error {
Doc("List the devops projects for the workspace member"). Doc("List the devops projects for the workspace member").
Metadata(restfulspec.KeyOpenAPITags, []string{constants.TenantResourcesTag})) Metadata(restfulspec.KeyOpenAPITags, []string{constants.TenantResourcesTag}))
ws.Route(ws.GET("/devopscount"). ws.Route(ws.GET("/devopscount").
To(handler.GetDevOpsProjectsCount). To(handler.GetDevOpsProjectsCountHandler).
Returns(http.StatusOK, api.StatusOK, struct { Returns(http.StatusOK, api.StatusOK, struct {
Count uint32 `json:"count"` Count uint32 `json:"count"`
}{}). }{}).
Doc("Get the devops projects count for the member"). Doc("Get the devops projects count for the member").
Metadata(restfulspec.KeyOpenAPITags, []string{constants.TenantResourcesTag})) Metadata(restfulspec.KeyOpenAPITags, []string{constants.TenantResourcesTag}))
ws.Route(ws.POST("/workspaces/{workspace}/devops"). ws.Route(ws.POST("/workspaces/{workspace}/devops").
To(handler.CreateDevopsProject). To(handler.CreateDevOpsProjectHandler).
Param(ws.PathParameter("workspace", "workspace name")). Param(ws.PathParameter("workspace", "workspace name")).
Doc("Create a devops project in the specified workspace"). Doc("Create a devops project in the specified workspace").
Reads(devopsv1alpha2.DevOpsProject{}). Reads(devopsv1alpha2.DevOpsProject{}).

View File

@@ -1,43 +1,43 @@
package v1alpha2 package v1alpha2
import ( import (
"github.com/emicklei/go-restful" "github.com/emicklei/go-restful"
"github.com/gorilla/websocket" "github.com/gorilla/websocket"
"k8s.io/client-go/kubernetes" "k8s.io/client-go/kubernetes"
"k8s.io/client-go/rest" "k8s.io/client-go/rest"
"k8s.io/klog" "k8s.io/klog"
"kubesphere.io/kubesphere/pkg/models/terminal" "kubesphere.io/kubesphere/pkg/models/terminal"
"net/http" "net/http"
) )
var upgrader = websocket.Upgrader{ var upgrader = websocket.Upgrader{
ReadBufferSize: 1024, ReadBufferSize: 1024,
WriteBufferSize: 1024, WriteBufferSize: 1024,
// Allow connections from any Origin // Allow connections from any Origin
CheckOrigin: func(r *http.Request) bool { return true }, CheckOrigin: func(r *http.Request) bool { return true },
} }
type terminalHandler struct { type terminalHandler struct {
terminaler terminal.Interface terminaler terminal.Interface
} }
func newTerminalHandler(client kubernetes.Interface, config *rest.Config) *terminalHandler { func newTerminalHandler(client kubernetes.Interface, config *rest.Config) *terminalHandler {
return &terminalHandler{ return &terminalHandler{
terminaler: terminal.NewTerminaler(client, config), terminaler: terminal.NewTerminaler(client, config),
} }
} }
func (t *terminalHandler) handleTerminalSession(request *restful.Request, response *restful.Response) { func (t *terminalHandler) handleTerminalSession(request *restful.Request, response *restful.Response) {
namespace := request.PathParameter("namespace") namespace := request.PathParameter("namespace")
podName := request.PathParameter("pod") podName := request.PathParameter("pod")
containerName := request.QueryParameter("container") containerName := request.QueryParameter("container")
shell := request.QueryParameter("shell") shell := request.QueryParameter("shell")
conn, err := upgrader.Upgrade(response.ResponseWriter, request.Request, nil) conn, err := upgrader.Upgrade(response.ResponseWriter, request.Request, nil)
if err != nil { if err != nil {
klog.Warning(err) klog.Warning(err)
return return
} }
t.terminaler.HandleSession(shell, namespace, podName, containerName, conn) t.terminaler.HandleSession(shell, namespace, podName, containerName, conn)
} }

View File

@@ -14,13 +14,7 @@ limitations under the License.
package devops package devops
import ( import (
"fmt"
"github.com/fatih/structs" "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" "kubesphere.io/kubesphere/pkg/utils/stringutils"
) )
@@ -67,298 +61,3 @@ const (
const ( const (
KS_ADMIN = "admin" 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 package devops
import ( import (
"kubesphere.io/kubesphere/pkg/simple/client/devops"
"kubesphere.io/kubesphere/pkg/simple/client/devops/fake"
"net/http"
"testing" "testing"
) )
func Test_parseCronJobTime(t *testing.T) { const baseUrl = "http://127.0.0.1/kapis/devops.kubesphere.io/v1alpha2/"
type Except struct {
Last string func TestGetNodesDetail(t *testing.T) {
Next string 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 { NodeSteps := []devops.NodeSteps{
Input string {
Expected Except DisplayName: "Deploy to Kubernetes",
}{ ID: "1",
{"上次运行的时间 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"}}, Result: "SUCCESS",
{"上次运行的时间 Thursday, January 3, 2019 11:56:30 PM UTC; 下次运行的时间 Friday, January 3, 2020 12:11:30 AM UTC.", Except{Last: "2019-01-03T23:56:30Z", Next: "2020-01-03T00:11:30Z"}}, },
{"上次运行的时间 Tuesday, September 10, 2019 8:41:34 AM UTC; 下次运行的时间 Tuesday, September 10, 2019 9:41:34 AM UTC.", Except{Last: "2019-09-10T08:41:34Z", Next: "2019-09-10T09:41:34Z"}},
{"上次运行的时间 Tuesday, September 10, 2019 9:15:26 AM UTC; 下次运行的时间 Tuesday, September 10, 2019 10:03:26 AM UTC.", Except{Last: "2019-09-10T09:15:26Z", Next: "2019-09-10T10:03:26Z"}},
{"Would last have run at Tuesday, September 10, 2019 9:15:26 AM UTC; would next run at Tuesday, September 10, 2019 10:03:26 AM UTC.", Except{Last: "2019-09-10T09:15:26Z", Next: "2019-09-10T10:03:26Z"}},
{"Would last have run at Tuesday, September 10, 2019 8:41:34 AM UTC; would next run at Tuesday, September 10, 2019 9:41:34 AM UTC.", Except{Last: "2019-09-10T08:41:34Z", Next: "2019-09-10T09:41:34Z"}},
} }
for _, item := range Items { fakeData["project1-pipeline1-run1"] = PipelineRunNodes
last, next, err := parseCronJobTime(item.Input) fakeData["project1-pipeline1-run1-1"] = NodeSteps
if err != nil { fakeData["project1-pipeline1-run1-2"] = NodeSteps
t.Fatalf("should not get error %+v", err) fakeData["project1-pipeline1-run1-3"] = NodeSteps
}
if last != item.Expected.Last { devopsClient := fake.NewFakeDevops(fakeData)
t.Errorf("got %#v, expected %#v", last, item.Expected.Last)
}
if next != item.Expected.Next { devopsOperator := NewDevopsOperator(devopsClient)
t.Errorf("got %#v, expected %#v", next, item.Expected.Next)
}
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.")
}
} }
} }

View File

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

View File

@@ -18,59 +18,6 @@ import (
"time" "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 ( const (
ProjectCredentialTableName = "project_credential" ProjectCredentialTableName = "project_credential"
ProjectCredentialIdColumn = "credential_id" ProjectCredentialIdColumn = "credential_id"
@@ -78,13 +25,6 @@ const (
ProjectCredentialProjectIdColumn = "project_id" 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 { type ProjectCredential struct {
ProjectId string `json:"project_id"` ProjectId string `json:"project_id"`
CredentialId string `json:"credential_id"` CredentialId string `json:"credential_id"`

View File

@@ -15,389 +15,209 @@ package devops
import ( import (
"fmt" "fmt"
"github.com/PuerkitoBio/goquery"
"github.com/asaskevich/govalidator"
"github.com/emicklei/go-restful" "github.com/emicklei/go-restful"
"github.com/gocraft/dbr" "github.com/gocraft/dbr"
"k8s.io/klog" "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/db"
"kubesphere.io/kubesphere/pkg/gojenkins"
"kubesphere.io/kubesphere/pkg/gojenkins/utils"
cs "kubesphere.io/kubesphere/pkg/simple/client"
"net/http" "net/http"
"strings"
) )
func CreateProjectCredential(projectId, username string, credentialRequest *JenkinsCredential) (string, error) { type ProjectCredentialOperator interface {
devops, err := cs.ClientSets().Devops() CreateProjectCredential(projectId, username string, credentialRequest *devops.Credential) (string, error)
if err != nil { UpdateProjectCredential(projectId, credentialId string, credentialRequest *devops.Credential) (string, error)
return "", restful.NewError(http.StatusServiceUnavailable, err.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 { if err != nil {
klog.Errorf("%+v", err) klog.Errorf("%+v", err)
return "", err return "", err
} }
switch credentialRequest.Type { id, err := o.devopsClient.DeleteCredentialInProject(projectId, credentialId)
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)
if err != nil { if err != nil {
klog.Errorf("%+v", err) klog.Errorf("%+v", err)
return "", restful.NewError(utils.GetJenkinsStatusCode(err), err.Error()) return "", err
}
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())
} }
deleteConditions := append(make([]dbr.Builder, 0), db.Eq(ProjectCredentialProjectIdColumn, projectId)) deleteConditions := append(make([]dbr.Builder, 0), db.Eq(ProjectCredentialProjectIdColumn, projectId))
deleteConditions = append(deleteConditions, db.Eq(ProjectCredentialIdColumn, credentialId)) deleteConditions = append(deleteConditions, db.Eq(ProjectCredentialIdColumn, credentialId))
if !govalidator.IsNull(credentialRequest.Domain) { deleteConditions = append(deleteConditions, db.Eq(ProjectCredentialDomainColumn, "_"))
deleteConditions = append(deleteConditions, db.Eq(ProjectCredentialDomainColumn, credentialRequest.Domain))
} else {
deleteConditions = append(deleteConditions, db.Eq(ProjectCredentialDomainColumn, "_"))
}
_, err = dbClient.DeleteFrom(ProjectCredentialTableName). _, err = o.db.DeleteFrom(ProjectCredentialTableName).
Where(db.And(deleteConditions...)).Exec() Where(db.And(deleteConditions...)).Exec()
if err != nil && err != db.ErrNotFound { if err != nil && err != db.ErrNotFound {
klog.Errorf("%+v", err) klog.Errorf("%+v", err)
return "", restful.NewError(http.StatusInternalServerError, err.Error()) return "", err
} }
return *id, nil return *id, nil
} }
func GetProjectCredential(projectId, credentialId, domain, getContent string) (*JenkinsCredential, error) { func (o *projectCredentialOperator) GetProjectCredential(projectId, credentialId, getContent string) (*devops.Credential, error) {
devops, err := cs.ClientSets().Devops()
if err != nil {
return nil, restful.NewError(http.StatusServiceUnavailable, err.Error())
}
jenkinsClient := devops.Jenkins()
dbClient, err := cs.ClientSets().MySQL() content := false
if err != nil { if getContent != "" {
return nil, restful.NewError(http.StatusServiceUnavailable, err.Error()) content = true
} }
jenkinsResponse, err := jenkinsClient.GetCredentialInFolder(domain, credential, err := o.devopsClient.GetCredentialInProject(projectId,
credentialId, credentialId,
projectId) content)
if err != nil { if err != nil {
klog.Errorf("%+v", err) klog.Errorf("%+v", err)
return nil, restful.NewError(utils.GetJenkinsStatusCode(err), err.Error()) return nil, err
} }
projectCredential := &ProjectCredential{} projectCredential := &ProjectCredential{}
err = dbClient.Select(ProjectCredentialColumns...). err = o.db.Select(ProjectCredentialColumns...).
From(ProjectCredentialTableName).Where( From(ProjectCredentialTableName).Where(
db.And(db.Eq(ProjectCredentialProjectIdColumn, projectId), db.And(db.Eq(ProjectCredentialProjectIdColumn, projectId),
db.Eq(ProjectCredentialIdColumn, credentialId), db.Eq(ProjectCredentialIdColumn, credentialId),
db.Eq(ProjectCredentialDomainColumn, jenkinsResponse.Domain))).LoadOne(projectCredential) db.Eq(ProjectCredentialDomainColumn, credential.Domain))).LoadOne(projectCredential)
if err != nil && err != db.ErrNotFound { if err != nil && err != db.ErrNotFound {
klog.Errorf("%+v", err) klog.Errorf("%+v", err)
return nil, restful.NewError(http.StatusInternalServerError, err.Error()) return nil, restful.NewError(http.StatusInternalServerError, err.Error())
} }
response := formatCredentialResponse(jenkinsResponse, projectCredential) response := formatCredentialResponse(credential, 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
}
}
return response, nil return response, nil
} }
func GetProjectCredentials(projectId, domain string) ([]*JenkinsCredential, error) { func (o *projectCredentialOperator) GetProjectCredentials(projectId string) ([]*devops.Credential, error) {
devops, err := cs.ClientSets().Devops()
if err != nil {
return nil, restful.NewError(http.StatusServiceUnavailable, err.Error())
}
jenkinsClient := devops.Jenkins()
dbClient, err := cs.ClientSets().MySQL() credentialResponses, err := o.devopsClient.GetCredentialsInProject(projectId)
if err != nil {
return nil, restful.NewError(http.StatusServiceUnavailable, err.Error())
}
jenkinsCredentialResponses, err := jenkinsClient.GetCredentialsInFolder(domain, projectId)
if err != nil { if err != nil {
klog.Errorf("%+v", err) klog.Errorf("%+v", err)
return nil, restful.NewError(utils.GetJenkinsStatusCode(err), err.Error()) return nil, err
} }
selectCondition := db.Eq(ProjectCredentialProjectIdColumn, projectId) selectCondition := db.Eq(ProjectCredentialProjectIdColumn, projectId)
if !govalidator.IsNull(domain) {
selectCondition = db.And(selectCondition, db.Eq(ProjectCredentialDomainColumn, domain))
}
projectCredentials := make([]*ProjectCredential, 0) projectCredentials := make([]*ProjectCredential, 0)
_, err = dbClient.Select(ProjectCredentialColumns...). _, err = o.db.Select(ProjectCredentialColumns...).
From(ProjectCredentialTableName).Where(selectCondition).Load(&projectCredentials) From(ProjectCredentialTableName).Where(selectCondition).Load(&projectCredentials)
if err != nil { if err != nil {
klog.Errorf("%+v", err) klog.Errorf("%+v", err)
return nil, restful.NewError(http.StatusInternalServerError, err.Error()) return nil, restful.NewError(http.StatusInternalServerError, err.Error())
} }
response := formatCredentialsResponse(jenkinsCredentialResponses, projectCredentials) response := formatCredentialsResponse(credentialResponses, projectCredentials)
return response, nil return response, nil
} }
func insertCredentialToDb(projectId, credentialId, domain, username string) error { func (o *projectCredentialOperator) insertCredentialToDb(projectId, credentialId, domain, username string) error {
dbClient, err := cs.ClientSets().MySQL()
if err != nil {
return err
}
projectCredential := NewProjectCredential(projectId, credentialId, domain, username) projectCredential := NewProjectCredential(projectId, credentialId, domain, username)
_, err = dbClient.InsertInto(ProjectCredentialTableName).Columns(ProjectCredentialColumns...). _, err := o.db.InsertInto(ProjectCredentialTableName).Columns(ProjectCredentialColumns...).
Record(projectCredential).Exec() Record(projectCredential).Exec()
if err != nil { if err != nil {
klog.Errorf("%+v", err) klog.Errorf("%+v", err)
@@ -406,41 +226,19 @@ func insertCredentialToDb(projectId, credentialId, domain, username string) erro
return nil 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( func formatCredentialResponse(
jenkinsCredentialResponse *gojenkins.CredentialResponse, credentialResponse *devops.Credential,
dbCredentialResponse *ProjectCredential) *JenkinsCredential { dbCredentialResponse *ProjectCredential) *devops.Credential {
response := &JenkinsCredential{} response := &devops.Credential{}
response.Id = jenkinsCredentialResponse.Id response.Id = credentialResponse.Id
response.Description = jenkinsCredentialResponse.Description response.Description = credentialResponse.Description
response.DisplayName = jenkinsCredentialResponse.DisplayName response.DisplayName = credentialResponse.DisplayName
if jenkinsCredentialResponse.Fingerprint != nil && jenkinsCredentialResponse.Fingerprint.Hash != "" { if credentialResponse.Fingerprint != nil && credentialResponse.Fingerprint.Hash != "" {
response.Fingerprint = &struct { response.Fingerprint = &struct {
FileName string `json:"file_name,omitempty" description:"Credential's display name and description"` FileName string `json:"file_name,omitempty" description:"Credential's display name and description"`
Hash string `json:"hash,omitempty" description:"Credential's hash"` Hash string `json:"hash,omitempty" description:"Credential's hash"`
Usage []*struct { 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 {
Ranges []*struct { Ranges []*struct {
Start int `json:"start,omitempty" description:"Start build number"` 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:"ranges,omitempty" description:"The build number of all pipelines that use this credential"`
} `json:"usage,omitempty" description:"all usage of Credential"` } `json:"usage,omitempty" description:"all usage of Credential"`
}{} }{}
response.Fingerprint.FileName = jenkinsCredentialResponse.Fingerprint.FileName response.Fingerprint.FileName = credentialResponse.Fingerprint.FileName
response.Fingerprint.Hash = jenkinsCredentialResponse.Fingerprint.Hash response.Fingerprint.Hash = credentialResponse.Fingerprint.Hash
for _, usage := range jenkinsCredentialResponse.Fingerprint.Usage { for _, usage := range credentialResponse.Fingerprint.Usage {
response.Fingerprint.Usage = append(response.Fingerprint.Usage, usage) response.Fingerprint.Usage = append(response.Fingerprint.Usage, usage)
} }
} }
response.Domain = jenkinsCredentialResponse.Domain response.Domain = credentialResponse.Domain
if dbCredentialResponse != nil { if dbCredentialResponse != nil {
response.CreateTime = &dbCredentialResponse.CreateTime response.CreateTime = &dbCredentialResponse.CreateTime
response.Creator = dbCredentialResponse.Creator response.Creator = dbCredentialResponse.Creator
} }
credentialType, ok := CredentialTypeMap[jenkinsCredentialResponse.TypeName] credentialType, ok := devops.CredentialTypeMap[credentialResponse.Type]
if ok { if ok {
response.Type = credentialType response.Type = credentialType
return response return response
} }
response.Type = jenkinsCredentialResponse.TypeName response.Type = credentialResponse.Type
return response return response
} }
func formatCredentialsResponse(jenkinsCredentialsResponse []*gojenkins.CredentialResponse, func formatCredentialsResponse(credentialsResponse []*devops.Credential,
projectCredentials []*ProjectCredential) []*JenkinsCredential { projectCredentials []*ProjectCredential) []*devops.Credential {
responseSlice := make([]*JenkinsCredential, 0) responseSlice := make([]*devops.Credential, 0)
for _, jenkinsCredential := range jenkinsCredentialsResponse { for _, credential := range credentialsResponse {
var dbCredential *ProjectCredential = nil var dbCredential *ProjectCredential = nil
for _, projectCredential := range projectCredentials { for _, projectCredential := range projectCredentials {
if projectCredential.CredentialId == jenkinsCredential.Id && if projectCredential.CredentialId == credential.Id &&
projectCredential.Domain == jenkinsCredential.Domain { projectCredential.Domain == credential.Domain {
dbCredential = projectCredential dbCredential = projectCredential
} }
} }
responseSlice = append(responseSlice, formatCredentialResponse(jenkinsCredential, dbCredential)) responseSlice = append(responseSlice, formatCredentialResponse(credential, dbCredential))
} }
return responseSlice return responseSlice
} }

View File

@@ -14,23 +14,37 @@ limitations under the License.
package devops package devops
import ( import (
"fmt"
"github.com/asaskevich/govalidator" "github.com/asaskevich/govalidator"
"github.com/emicklei/go-restful" "github.com/emicklei/go-restful"
"github.com/gocraft/dbr" "github.com/gocraft/dbr"
"k8s.io/klog" "k8s.io/klog"
"kubesphere.io/kubesphere/pkg/api/devops/v1alpha2" "kubesphere.io/kubesphere/pkg/api/devops/v1alpha2"
"kubesphere.io/kubesphere/pkg/db" "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" "net/http"
) )
func GetProject(projectId string) (*v1alpha2.DevOpsProject, error) { type ProjectOperator interface {
dbconn, err := cs.ClientSets().MySQL() GetProject(projectId string) (*v1alpha2.DevOpsProject, error)
if err != nil { UpdateProject(project *v1alpha2.DevOpsProject) (*v1alpha2.DevOpsProject, error)
return nil, err 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{} project := &v1alpha2.DevOpsProject{}
err = dbconn.Select(DevOpsProjectColumns...). err := o.db.Select(DevOpsProjectColumns...).
From(DevOpsProjectTableName). From(DevOpsProjectTableName).
Where(db.Eq(DevOpsProjectIdColumn, projectId)). Where(db.Eq(DevOpsProjectIdColumn, projectId)).
LoadOne(project) LoadOne(project)
@@ -46,13 +60,9 @@ func GetProject(projectId string) (*v1alpha2.DevOpsProject, error) {
return project, nil return project, nil
} }
func UpdateProject(project *v1alpha2.DevOpsProject) (*v1alpha2.DevOpsProject, error) { func (o *projectOperator) UpdateProject(project *v1alpha2.DevOpsProject) (*v1alpha2.DevOpsProject, error) {
dbconn, err := cs.ClientSets().MySQL()
if err != nil {
return nil, err
}
query := dbconn.Update(DevOpsProjectTableName) query := o.db.Update(DevOpsProjectTableName)
if !govalidator.IsNull(project.Description) { if !govalidator.IsNull(project.Description) {
query.Set(DevOpsProjectDescriptionColumn, project.Description) query.Set(DevOpsProjectDescriptionColumn, project.Description)
} }
@@ -73,7 +83,7 @@ func UpdateProject(project *v1alpha2.DevOpsProject) (*v1alpha2.DevOpsProject, er
} }
} }
newProject := &v1alpha2.DevOpsProject{} newProject := &v1alpha2.DevOpsProject{}
err = dbconn.Select(DevOpsProjectColumns...). err := o.db.Select(DevOpsProjectColumns...).
From(DevOpsProjectTableName). From(DevOpsProjectTableName).
Where(db.Eq(DevOpsProjectIdColumn, project.ProjectId)). Where(db.Eq(DevOpsProjectIdColumn, project.ProjectId)).
LoadOne(newProject) LoadOne(newProject)
@@ -83,3 +93,22 @@ func UpdateProject(project *v1alpha2.DevOpsProject) (*v1alpha2.DevOpsProject, er
} }
return newProject, nil 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 ( import (
"fmt" "fmt"
"k8s.io/klog" "k8s.io/klog"
"kubesphere.io/kubesphere/pkg/simple/client/devops"
"kubesphere.io/kubesphere/pkg/simple/client/mysql"
"net/http" "net/http"
"github.com/emicklei/go-restful" "github.com/emicklei/go-restful"
"github.com/gocraft/dbr" "github.com/gocraft/dbr"
"kubesphere.io/kubesphere/pkg/db" "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/models"
"kubesphere.io/kubesphere/pkg/server/params" "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) { type ProjectMemberOperator interface {
dbconn, err := cs.ClientSets().MySQL() GetProjectMembers(projectId string, conditions *params.Conditions, orderBy string, reverse bool, limit int, offset int) (*models.PageableResponse, error)
if err != nil { GetProjectMember(projectId, username string) (*devops.ProjectMembership, error)
return nil, err 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 var sqconditions []dbr.Builder
sqconditions = append(sqconditions, db.Eq(DevOpsProjectMembershipProjectIdColumn, projectId)) sqconditions = append(sqconditions, db.Eq(ProjectMembershipProjectIdColumn, projectId))
if keyword := conditions.Match["keyword"]; keyword != "" { if keyword := conditions.Match["keyword"]; keyword != "" {
sqconditions = append(sqconditions, db.Like(DevOpsProjectMembershipUsernameColumn, keyword)) sqconditions = append(sqconditions, db.Like(ProjectMembershipUsernameColumn, keyword))
} }
query := dbconn.Select(DevOpsProjectMembershipColumns...). query := *o.db.Select(ProjectMembershipColumns...).
From(DevOpsProjectMembershipTableName) From(ProjectMembershipTableName)
switch orderBy { switch orderBy {
case "name": case "name":
if reverse { if reverse {
query.OrderDesc(DevOpsProjectMembershipUsernameColumn) query.OrderDesc(ProjectMembershipUsernameColumn)
} else { } else {
query.OrderAsc(DevOpsProjectMembershipUsernameColumn) query.OrderAsc(ProjectMembershipUsernameColumn)
} }
default: default:
if reverse { if reverse {
query.OrderDesc(DevOpsProjectMembershipRoleColumn) query.OrderDesc(ProjectMembershipRoleColumn)
} else { } else {
query.OrderAsc(DevOpsProjectMembershipRoleColumn) query.OrderAsc(ProjectMembershipRoleColumn)
} }
} }
query.Limit(uint64(limit)) query.Limit(uint64(limit))
@@ -61,7 +76,7 @@ func GetProjectMembers(projectId string, conditions *params.Conditions, orderBy
} else { } else {
query.Where(sqconditions[0]) query.Where(sqconditions[0])
} }
_, err = query.Load(&memberships) _, err := query.Load(&memberships)
if err != nil && err != dbr.ErrNotFound { if err != nil && err != dbr.ErrNotFound {
klog.Errorf("%+v", err) klog.Errorf("%+v", err)
return nil, restful.NewError(http.StatusInternalServerError, err.Error()) 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 return &models.PageableResponse{Items: result, TotalCount: int(count)}, nil
} }
func GetProjectMember(projectId, username string) (*DevOpsProjectMembership, error) { func (o *projectMemberOperator) GetProjectMember(projectId, username string) (*devops.ProjectMembership, error) {
dbconn, err := cs.ClientSets().MySQL()
if err != nil {
return nil, err
}
member := &DevOpsProjectMembership{} member := &devops.ProjectMembership{}
err = dbconn.Select(DevOpsProjectMembershipColumns...). err := o.db.Select(ProjectMembershipColumns...).
From(DevOpsProjectMembershipTableName). From(ProjectMembershipTableName).
Where(db.And(db.Eq(DevOpsProjectMembershipProjectIdColumn, projectId), Where(db.And(db.Eq(ProjectMembershipProjectIdColumn, projectId),
db.Eq(DevOpsProjectMembershipUsernameColumn, username))). db.Eq(ProjectMembershipUsernameColumn, username))).
LoadOne(&member) LoadOne(&member)
if err != nil && err != dbr.ErrNotFound { if err != nil && err != dbr.ErrNotFound {
klog.Errorf("%+v", err) klog.Errorf("%+v", err)
@@ -102,31 +113,17 @@ func GetProjectMember(projectId, username string) (*DevOpsProjectMembership, err
return member, nil return member, nil
} }
func AddProjectMember(projectId, operator string, member *DevOpsProjectMembership) (*DevOpsProjectMembership, error) { func (o *projectMemberOperator) AddProjectMember(projectId string, membership *devops.ProjectMembership) (*devops.ProjectMembership, 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())
}
membership := &DevOpsProjectMembership{} dbmembership := &devops.ProjectMembership{}
err = dbconn.Select(DevOpsProjectMembershipColumns...). err := o.db.Select(ProjectMembershipColumns...).
From(DevOpsProjectMembershipTableName). From(ProjectMembershipTableName).
Where(db.And( Where(db.And(
db.Eq(DevOpsProjectMembershipUsernameColumn, member.Username), db.Eq(ProjectMembershipUsernameColumn, membership.Username),
db.Eq(DevOpsProjectMembershipProjectIdColumn, projectId))).LoadOne(membership) db.Eq(ProjectMembershipProjectIdColumn, projectId))).LoadOne(dbmembership)
// if user could be founded in db, user have been added to project // if user could be founded in db, user have been added to project
if err == nil { 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) klog.Errorf("%+v", err)
return nil, restful.NewError(http.StatusBadRequest, err.Error()) 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()) return nil, restful.NewError(http.StatusInternalServerError, err.Error())
} }
globalRole, err := jenkinsClient.GetGlobalRole(JenkinsAllUserRoleName) _, err = o.projectMemberOperator.AddProjectMember(membership)
if err != nil { if err != nil {
klog.Errorf("%+v", err) klog.Errorf("%+v", err)
return nil, restful.NewError(utils.GetJenkinsStatusCode(err), err.Error()) return nil, err
} }
if globalRole == nil { projectMembership := NewDevOpsProjectMemberShip(membership.Username, projectId, membership.Role, membership.GrantBy)
_, err := jenkinsClient.AddGlobalRole(JenkinsAllUserRoleName, gojenkins.GlobalPermissionIds{ _, err = o.db.
GlobalRead: true, InsertInto(ProjectMembershipTableName).
}, true) Columns(ProjectMembershipColumns...).
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...).
Record(projectMembership).Exec() Record(projectMembership).Exec()
if err != nil { if err != nil {
klog.Errorf("%+v", err) klog.Errorf("%+v", err)
err = projectRole.UnAssignRole(member.Username) _, err = o.projectMemberOperator.DeleteProjectMember(membership)
if err != nil { if err != nil {
klog.Errorf("%+v", err) klog.Errorf("%+v", err)
return nil, restful.NewError(utils.GetJenkinsStatusCode(err), err.Error()) return nil, err
}
err = pipelineRole.UnAssignRole(member.Username)
if err != nil {
klog.Errorf("%+v", err)
return nil, restful.NewError(utils.GetJenkinsStatusCode(err), err.Error())
} }
return nil, restful.NewError(http.StatusInternalServerError, err.Error()) return nil, restful.NewError(http.StatusInternalServerError, err.Error())
} }
return projectMembership, nil return projectMembership, nil
} }
func UpdateProjectMember(projectId, operator string, member *DevOpsProjectMembership) (*DevOpsProjectMembership, error) { func (o *projectMemberOperator) UpdateProjectMember(projectId string, membership *devops.ProjectMembership) (*devops.ProjectMembership, 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
}
jenkinsClient := devops.Jenkins() oldMembership := &devops.ProjectMembership{}
if jenkinsClient == nil { err := o.db.Select(ProjectMembershipColumns...).
err := fmt.Errorf("could not connect to jenkins") From(ProjectMembershipTableName).
klog.Error(err)
return nil, restful.NewError(http.StatusServiceUnavailable, err.Error())
}
oldMembership := &DevOpsProjectMembership{}
err = dbconn.Select(DevOpsProjectMembershipColumns...).
From(DevOpsProjectMembershipTableName).
Where(db.And( Where(db.And(
db.Eq(DevOpsProjectMembershipUsernameColumn, member.Username), db.Eq(ProjectMembershipUsernameColumn, membership.Username),
db.Eq(DevOpsProjectMembershipProjectIdColumn, projectId), db.Eq(ProjectMembershipProjectIdColumn, projectId),
)).LoadOne(oldMembership) )).LoadOne(oldMembership)
if err != nil { if err != nil {
klog.Errorf("%+v", err) klog.Errorf("%+v", err)
return nil, restful.NewError(http.StatusBadRequest, err.Error()) 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 { if err != nil {
klog.Errorf("%+v", err) klog.Errorf("%+v", err)
return nil, restful.NewError(utils.GetJenkinsStatusCode(err), err.Error()) return nil, err
} }
_, err = o.db.Update(ProjectMembershipTableName).
err = oldProjectRole.UnAssignRole(member.Username) Set(ProjectMembershipRoleColumn, membership.Role).
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).
Where(db.And( Where(db.And(
db.Eq(DevOpsProjectMembershipProjectIdColumn, projectId), db.Eq(ProjectMembershipProjectIdColumn, projectId),
db.Eq(DevOpsProjectMembershipUsernameColumn, member.Username), db.Eq(ProjectMembershipUsernameColumn, membership.Username),
)).Exec() )).Exec()
if err != nil { if err != nil {
klog.Errorf("%+v", err) klog.Errorf("%+v", err)
return nil, restful.NewError(http.StatusInternalServerError, err.Error()) return nil, restful.NewError(http.StatusInternalServerError, err.Error())
} }
responseMembership := &DevOpsProjectMembership{} responseMembership := &devops.ProjectMembership{}
err = dbconn.Select(DevOpsProjectMembershipColumns...). err = o.db.Select(ProjectMembershipColumns...).
From(DevOpsProjectMembershipTableName). From(ProjectMembershipTableName).
Where(db.And( Where(db.And(
db.Eq(DevOpsProjectMembershipUsernameColumn, member.Username), db.Eq(ProjectMembershipUsernameColumn, membership.Username),
db.Eq(DevOpsProjectMembershipProjectIdColumn, projectId), db.Eq(ProjectMembershipProjectIdColumn, projectId),
)).LoadOne(responseMembership) )).LoadOne(responseMembership)
if err != nil { if err != nil {
klog.Errorf("%+v", err) klog.Errorf("%+v", err)
@@ -299,25 +200,14 @@ func UpdateProjectMember(projectId, operator string, member *DevOpsProjectMember
return responseMembership, nil return responseMembership, nil
} }
func DeleteProjectMember(projectId, username string) (string, error) { func (o *projectMemberOperator) DeleteProjectMember(projectId, username string) (string, error) {
dbconn, err := cs.ClientSets().MySQL()
if err != nil {
return "", restful.NewError(http.StatusServiceUnavailable, err.Error())
}
devops, err := cs.ClientSets().Devops() oldMembership := &devops.ProjectMembership{}
if err != nil { err := o.db.Select(ProjectMembershipColumns...).
return "", restful.NewError(http.StatusServiceUnavailable, err.Error()) From(ProjectMembershipTableName).
}
jenkinsClient := devops.Jenkins()
oldMembership := &DevOpsProjectMembership{}
err = dbconn.Select(DevOpsProjectMembershipColumns...).
From(DevOpsProjectMembershipTableName).
Where(db.And( Where(db.And(
db.Eq(DevOpsProjectMembershipUsernameColumn, username), db.Eq(ProjectMembershipUsernameColumn, username),
db.Eq(DevOpsProjectMembershipProjectIdColumn, projectId), db.Eq(ProjectMembershipProjectIdColumn, projectId),
)).LoadOne(oldMembership) )).LoadOne(oldMembership)
if err != nil { if err != nil {
if err != db.ErrNotFound { if err != db.ErrNotFound {
@@ -329,12 +219,12 @@ func DeleteProjectMember(projectId, username string) (string, error) {
} }
} }
if oldMembership.Role == ProjectOwner { if oldMembership.Role == devops.ProjectOwner {
count, err := dbconn.Select(DevOpsProjectMembershipProjectIdColumn). count, err := o.db.Select(ProjectMembershipProjectIdColumn).
From(DevOpsProjectMembershipTableName). From(ProjectMembershipTableName).
Where(db.And( Where(db.And(
db.Eq(DevOpsProjectMembershipProjectIdColumn, projectId), db.Eq(ProjectMembershipProjectIdColumn, projectId),
db.Eq(DevOpsProjectMembershipRoleColumn, ProjectOwner))).Count() db.Eq(ProjectMembershipRoleColumn, devops.ProjectOwner))).Count()
if err != nil { if err != nil {
klog.Errorf("%+v", err) klog.Errorf("%+v", err)
return "", restful.NewError(http.StatusInternalServerError, err.Error()) 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 { if err != nil {
klog.Errorf("%+v", err) klog.Error(err)
return "", restful.NewError(utils.GetJenkinsStatusCode(err), err.Error()) return "", err
}
err = oldProjectRole.UnAssignRole(username)
if err != nil {
klog.Errorf("%+v", err)
return "", restful.NewError(utils.GetJenkinsStatusCode(err), err.Error())
} }
oldPipelineRole, err := jenkinsClient.GetProjectRole(GetPipelineRoleName(projectId, oldMembership.Role)) _, err = o.db.DeleteFrom(ProjectMembershipTableName).
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).
Where(db.And( Where(db.And(
db.Eq(DevOpsProjectMembershipProjectIdColumn, projectId), db.Eq(ProjectMembershipProjectIdColumn, projectId),
db.Eq(DevOpsProjectMembershipUsernameColumn, username), db.Eq(ProjectMembershipUsernameColumn, username),
)).Exec() )).Exec()
if err != nil { if err != nil {
klog.Errorf("%+v", err) klog.Errorf("%+v", err)

View File

@@ -17,295 +17,49 @@ import (
"fmt" "fmt"
"github.com/emicklei/go-restful" "github.com/emicklei/go-restful"
"k8s.io/klog" "k8s.io/klog"
"kubesphere.io/kubesphere/pkg/gojenkins/utils" "kubesphere.io/kubesphere/pkg/simple/client/devops"
cs "kubesphere.io/kubesphere/pkg/simple/client"
"net/http" "net/http"
) )
func CreateProjectPipeline(projectId string, pipeline *ProjectPipeline) (string, error) { type ProjectPipelineOperator interface {
devops, err := cs.ClientSets().Devops() CreateProjectPipeline(projectId string, pipeline *devops.ProjectPipeline) (string, error)
if err != nil { DeleteProjectPipeline(projectId string, pipelineId string) (string, error)
return "", restful.NewError(http.StatusServiceUnavailable, err.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 { switch pipeline.Type {
case NoScmPipelineType: case devops.NoScmPipelineType:
pipeline.Pipeline.Name = pipelineId
config, err := createPipelineConfigXml(pipeline.Pipeline) case devops.MultiBranchPipelineType:
if err != nil { pipeline.MultiBranchPipeline.Name = pipelineId
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
default: default:
err := fmt.Errorf("error unsupport job type") err := fmt.Errorf("error unsupport pipeline type")
klog.Errorf("%+v", err) klog.Errorf("%+v", err)
return "", restful.NewError(http.StatusBadRequest, err.Error()) return "", restful.NewError(http.StatusBadRequest, err.Error())
} }
return o.pipelineOperator.UpdateProjectPipeline(projectId, pipeline)
} }
func DeleteProjectPipeline(projectId string, pipelineId string) (string, error) { func (o *projectPipelineOperator) GetProjectPipelineConfig(projectId, pipelineId string) (*devops.ProjectPipeline, error) {
devops, err := cs.ClientSets().Devops() return o.pipelineOperator.GetProjectPipelineConfig(projectId, pipelineId)
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
} }

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

@@ -3,6 +3,8 @@ package devops
import ( import (
"code.cloudfoundry.org/bytefmt" "code.cloudfoundry.org/bytefmt"
"fmt" "fmt"
"github.com/aws/aws-sdk-go/aws/awserr"
awsS3 "github.com/aws/aws-sdk-go/service/s3"
"github.com/emicklei/go-restful" "github.com/emicklei/go-restful"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/client-go/util/retry" "k8s.io/client-go/util/retry"
@@ -10,7 +12,6 @@ import (
"kubesphere.io/kubesphere/pkg/apis/devops/v1alpha1" "kubesphere.io/kubesphere/pkg/apis/devops/v1alpha1"
"kubesphere.io/kubesphere/pkg/client/clientset/versioned" "kubesphere.io/kubesphere/pkg/client/clientset/versioned"
"kubesphere.io/kubesphere/pkg/client/informers/externalversions" "kubesphere.io/kubesphere/pkg/client/informers/externalversions"
"kubesphere.io/kubesphere/pkg/simple/client"
"kubesphere.io/kubesphere/pkg/simple/client/s3" "kubesphere.io/kubesphere/pkg/simple/client/s3"
"mime/multipart" "mime/multipart"
"net/http" "net/http"
@@ -22,9 +23,9 @@ const (
) )
type S2iBinaryUploader interface { 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 { type s2iBinaryUploader struct {
@@ -33,6 +34,14 @@ type s2iBinaryUploader struct {
s3Client s3.Interface 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) { func (s *s2iBinaryUploader) UploadS2iBinary(namespace, name, md5 string, fileHeader *multipart.FileHeader) (*v1alpha1.S2iBinary, error) {
binFile, err := fileHeader.Open() binFile, err := fileHeader.Open()
if err != nil { if err != nil {
@@ -75,58 +84,35 @@ func (s *s2iBinaryUploader) UploadS2iBinary(namespace, name, md5 string, fileHea
copy.Spec.FileName = fileHeader.Filename copy.Spec.FileName = fileHeader.Filename
copy.Spec.DownloadURL = fmt.Sprintf(GetS2iBinaryURL, namespace, name, copy.Spec.FileName) copy.Spec.DownloadURL = fmt.Sprintf(GetS2iBinaryURL, namespace, name, copy.Spec.FileName)
/* err = s.s3Client.Upload(fmt.Sprintf("%s-%s", namespace, name), copy.Spec.FileName, binFile)
TODO: upload binary file use s3.Interface if err != nil {
s3session := s3Client.Session() if aerr, ok := err.(awserr.Error); ok {
if s3session == nil { switch aerr.Code() {
err := fmt.Errorf("could not connect to s2i s3") case awsS3.ErrCodeNoSuchBucket:
klog.Error(err) klog.Error(err)
_, serr := SetS2iBinaryStatusWithRetry(copy, origin.Status.Phase) _, serr := s.SetS2iBinaryStatusWithRetry(copy, origin.Status.Phase)
if serr != nil { if serr != nil {
klog.Error(serr) 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
} }
return nil, err
} }
uploader := s3manager.NewUploader(s3session, func(uploader *s3manager.Uploader) { klog.Error(err)
uploader.PartSize = 5 * bytefmt.MEGABYTE return nil, err
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)),
})
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 { if copy.Spec.UploadTimeStamp == nil {
copy.Spec.UploadTimeStamp = new(metav1.Time) copy.Spec.UploadTimeStamp = new(metav1.Time)
} }
*copy.Spec.UploadTimeStamp = metav1.Now() *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 { if err != nil {
klog.Error(err) klog.Error(err)
return nil, err return nil, err
@@ -159,22 +145,7 @@ func (s *s2iBinaryUploader) DownloadS2iBinary(namespace, name, fileName string)
klog.Error(err) klog.Error(err)
return "", err return "", err
} }
return s.s3Client.GetDownloadURL(fmt.Sprintf("%s-%s", namespace, name), fileName)
/*
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
} }
func (s *s2iBinaryUploader) SetS2iBinaryStatus(s2ibin *v1alpha1.S2iBinary, status string) (*v1alpha1.S2iBinary, error) { 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 bin *v1alpha1.S2iBinary
var err error var err error
err = retry.RetryOnConflict(retry.DefaultRetry, func() 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 { if err != nil {
klog.Error(err) klog.Error(err)
return 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"
)

View File

@@ -566,9 +566,9 @@ func (im *imOperator) deleteUserInDevOps(username string) error {
jenkinsClient := dp.Jenkins() jenkinsClient := dp.Jenkins()
_, err = devopsDb.DeleteFrom(devops.DevOpsProjectMembershipTableName). _, err = devopsDb.DeleteFrom(devops.ProjectMembershipTableName).
Where(db.And( Where(db.And(
db.Eq(devops.DevOpsProjectMembershipUsernameColumn, username), db.Eq(devops.ProjectMembershipUsernameColumn, username),
)).Exec() )).Exec()
if err != nil { if err != nil {
klog.Errorf("%+v", err) klog.Errorf("%+v", err)

View File

@@ -64,4 +64,4 @@ func objectsToInterfaces(objs []runtime.Object) []interface{} {
res = append(res, obj) res = append(res, obj)
} }
return res return res
} }

View File

@@ -1,75 +1,75 @@
package namespace package namespace
import ( import (
v1 "k8s.io/api/core/v1" v1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/labels" "k8s.io/apimachinery/pkg/labels"
"k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime"
"k8s.io/client-go/informers" "k8s.io/client-go/informers"
"kubesphere.io/kubesphere/pkg/api" "kubesphere.io/kubesphere/pkg/api"
"kubesphere.io/kubesphere/pkg/apiserver/query" "kubesphere.io/kubesphere/pkg/apiserver/query"
"kubesphere.io/kubesphere/pkg/models/resources/v1alpha3" "kubesphere.io/kubesphere/pkg/models/resources/v1alpha3"
"strings" "strings"
) )
type namespaceGetter struct { type namespaceGetter struct {
informers informers.SharedInformerFactory informers informers.SharedInformerFactory
} }
func NewNamespaceGetter(informers informers.SharedInformerFactory) v1alpha3.Interface { func NewNamespaceGetter(informers informers.SharedInformerFactory) v1alpha3.Interface {
return &namespaceGetter{informers:informers} return &namespaceGetter{informers: informers}
} }
func (n namespaceGetter) Get(_, name string) (runtime.Object, error) { func (n namespaceGetter) Get(_, name string) (runtime.Object, error) {
return n.informers.Core().V1().Namespaces().Lister().Get(name) return n.informers.Core().V1().Namespaces().Lister().Get(name)
} }
func (n namespaceGetter) List(_ string, query *query.Query) (*api.ListResult, error) { func (n namespaceGetter) List(_ string, query *query.Query) (*api.ListResult, error) {
ns, err := n.informers.Core().V1().Namespaces().Lister().List(labels.Everything()) ns, err := n.informers.Core().V1().Namespaces().Lister().List(labels.Everything())
if err != nil { if err != nil {
return nil, err return nil, err
} }
var result []runtime.Object var result []runtime.Object
for _, item := range ns { for _, item := range ns {
result = append(result, item) result = append(result, item)
} }
return v1alpha3.DefaultList(result, query, n.compare, n.filter), nil return v1alpha3.DefaultList(result, query, n.compare, n.filter), nil
} }
func (n namespaceGetter) filter(item runtime.Object, filter query.Filter) bool { func (n namespaceGetter) filter(item runtime.Object, filter query.Filter) bool {
namespace, ok := item.(*v1.Namespace) namespace, ok := item.(*v1.Namespace)
if !ok { if !ok {
return false return false
} }
switch filter.Field { switch filter.Field {
case query.FieldName: case query.FieldName:
return query.ComparableString(namespace.Name).Contains(filter.Value) return query.ComparableString(namespace.Name).Contains(filter.Value)
case query.FieldStatus: case query.FieldStatus:
return query.ComparableString(namespace.Status.Phase).Compare(filter.Value) == 0 return query.ComparableString(namespace.Status.Phase).Compare(filter.Value) == 0
default: default:
return false return false
} }
} }
func (n namespaceGetter) compare(left runtime.Object, right runtime.Object, field query.Field) bool { func (n namespaceGetter) compare(left runtime.Object, right runtime.Object, field query.Field) bool {
leftNs, ok := left.(*v1.Namespace) leftNs, ok := left.(*v1.Namespace)
if !ok { if !ok {
return false return false
} }
rightNs, ok := right.(*v1.Namespace) rightNs, ok := right.(*v1.Namespace)
if !ok { if !ok {
return true return true
} }
switch field { switch field {
case query.FieldName: case query.FieldName:
return strings.Compare(leftNs.Name, rightNs.Name) > 0 return strings.Compare(leftNs.Name, rightNs.Name) > 0
case query.FieldCreationTimeStamp: case query.FieldCreationTimeStamp:
return leftNs.CreationTimestamp.After(rightNs.CreationTimestamp.Time) return leftNs.CreationTimestamp.After(rightNs.CreationTimestamp.Time)
default: default:
return false return false
} }
} }

View File

@@ -24,35 +24,40 @@ import (
"k8s.io/klog" "k8s.io/klog"
"kubesphere.io/kubesphere/pkg/api/devops/v1alpha2" "kubesphere.io/kubesphere/pkg/api/devops/v1alpha2"
"kubesphere.io/kubesphere/pkg/db" "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/models"
"kubesphere.io/kubesphere/pkg/models/devops" "kubesphere.io/kubesphere/pkg/models/devops"
"kubesphere.io/kubesphere/pkg/models/iam" "kubesphere.io/kubesphere/pkg/models/iam"
"kubesphere.io/kubesphere/pkg/server/params" "kubesphere.io/kubesphere/pkg/server/params"
cs "kubesphere.io/kubesphere/pkg/simple/client" dsClient "kubesphere.io/kubesphere/pkg/simple/client/devops"
"kubesphere.io/kubesphere/pkg/simple/client/mysql"
"net/http" "net/http"
"sync"
) )
type DevOpsProjectRoleResponse struct { type DevOpsProjectOperator interface {
ProjectRole *gojenkins.ProjectRole ListDevOpsProjects(workspace, username string, conditions *params.Conditions, orderBy string, reverse bool, limit int, offset int) (*models.PageableResponse, error)
Err error CreateDevOpsProject(username string, workspace string, req *v1alpha2.DevOpsProject) (*v1alpha2.DevOpsProject, error)
GetDevOpsProjectsCount(username string) (uint32, error)
DeleteDevOpsProject(projectId, username string) error
GetUserDevOpsSimpleRules(username, projectId string) ([]iam.SimpleRule, error)
} }
func ListDevopsProjects(workspace, username string, conditions *params.Conditions, orderBy string, reverse bool, limit int, offset int) (*models.PageableResponse, error) { type devopsProjectOperator struct {
ksProjectOperator devops.ProjectOperator
db *mysql.Database
dsProject dsClient.ProjectOperator
}
dbconn, err := cs.ClientSets().MySQL() func newProjectOperator(operator devops.ProjectOperator, db *mysql.Database, client dsClient.ProjectOperator) DevOpsProjectOperator {
if err != nil { return &devopsProjectOperator{
if _, ok := err.(cs.ClientSetNotEnabledError); ok { ksProjectOperator: operator,
klog.V(4).Info("devops client is not enable") db: db,
return nil, err dsProject: client,
}
klog.Error(err)
return nil, err
} }
}
query := dbconn.Select(devops.GetColumnsFromStructWithPrefix(devops.DevOpsProjectTableName, v1alpha2.DevOpsProject{})...). func (o *devopsProjectOperator) ListDevOpsProjects(workspace, username string, conditions *params.Conditions, orderBy string, reverse bool, limit int, offset int) (*models.PageableResponse, error) {
query := o.db.Select(devops.GetColumnsFromStructWithPrefix(devops.DevOpsProjectTableName, v1alpha2.DevOpsProject{})...).
From(devops.DevOpsProjectTableName) From(devops.DevOpsProjectTableName)
var sqconditions []dbr.Builder var sqconditions []dbr.Builder
@@ -61,11 +66,11 @@ func ListDevopsProjects(workspace, username string, conditions *params.Condition
switch username { switch username {
case devops.KS_ADMIN: case devops.KS_ADMIN:
default: default:
onCondition := fmt.Sprintf("%s = %s", devops.DevOpsProjectMembershipProjectIdColumn, devops.DevOpsProjectIdColumn) onCondition := fmt.Sprintf("%s = %s", devops.ProjectMembershipProjectIdColumn, devops.DevOpsProjectIdColumn)
query.Join(devops.DevOpsProjectMembershipTableName, onCondition) query.Join(devops.ProjectMembershipTableName, onCondition)
sqconditions = append(sqconditions, db.Eq(devops.DevOpsProjectMembershipUsernameColumn, username)) sqconditions = append(sqconditions, db.Eq(devops.ProjectMembershipUsernameColumn, username))
sqconditions = append(sqconditions, db.Eq( sqconditions = append(sqconditions, db.Eq(
devops.DevOpsProjectMembershipTableName+"."+devops.StatusColumn, devops.StatusActive)) devops.ProjectMembershipTableName+"."+devops.StatusColumn, devops.StatusActive))
} }
sqconditions = append(sqconditions, db.Eq( sqconditions = append(sqconditions, db.Eq(
@@ -95,7 +100,7 @@ func ListDevopsProjects(workspace, username string, conditions *params.Condition
} }
query.Limit(uint64(limit)) query.Limit(uint64(limit))
query.Offset(uint64(offset)) query.Offset(uint64(offset))
_, err = query.Load(&projects) _, err := query.Load(&projects)
if err != nil { if err != nil {
klog.Errorf("%+v", err) klog.Errorf("%+v", err)
return nil, restful.NewError(http.StatusInternalServerError, err.Error()) return nil, restful.NewError(http.StatusInternalServerError, err.Error())
@@ -114,23 +119,18 @@ func ListDevopsProjects(workspace, username string, conditions *params.Condition
return &models.PageableResponse{Items: result, TotalCount: int(count)}, nil return &models.PageableResponse{Items: result, TotalCount: int(count)}, nil
} }
func GetDevOpsProjectsCount(username string) (uint32, error) { func (o *devopsProjectOperator) GetDevOpsProjectsCount(username string) (uint32, error) {
dbconn, err := cs.ClientSets().MySQL()
if err != nil {
klog.Error(err)
return 0, err
}
query := dbconn.Select(devops.GetColumnsFromStructWithPrefix(devops.DevOpsProjectTableName, v1alpha2.DevOpsProject{})...). query := o.db.Select(devops.GetColumnsFromStructWithPrefix(devops.DevOpsProjectTableName, v1alpha2.DevOpsProject{})...).
From(devops.DevOpsProjectTableName) From(devops.DevOpsProjectTableName)
var sqconditions []dbr.Builder var sqconditions []dbr.Builder
if username != devops.KS_ADMIN { if username != devops.KS_ADMIN {
onCondition := fmt.Sprintf("%s = %s", devops.DevOpsProjectMembershipProjectIdColumn, devops.DevOpsProjectIdColumn) onCondition := fmt.Sprintf("%s = %s", devops.ProjectMembershipProjectIdColumn, devops.DevOpsProjectIdColumn)
query.Join(devops.DevOpsProjectMembershipTableName, onCondition) query.Join(devops.ProjectMembershipTableName, onCondition)
sqconditions = append(sqconditions, db.Eq(devops.DevOpsProjectMembershipUsernameColumn, username)) sqconditions = append(sqconditions, db.Eq(devops.ProjectMembershipUsernameColumn, username))
sqconditions = append(sqconditions, db.Eq( sqconditions = append(sqconditions, db.Eq(
devops.DevOpsProjectMembershipTableName+"."+devops.StatusColumn, devops.StatusActive)) devops.ProjectMembershipTableName+"."+devops.StatusColumn, devops.StatusActive))
} }
sqconditions = append(sqconditions, db.Eq( sqconditions = append(sqconditions, db.Eq(
@@ -146,171 +146,61 @@ func GetDevOpsProjectsCount(username string) (uint32, error) {
return count, nil return count, nil
} }
func DeleteDevOpsProject(projectId, username string) error { func (o *devopsProjectOperator) DeleteDevOpsProject(projectId, username string) error {
err := devops.CheckProjectUserInRole(username, projectId, []string{devops.ProjectOwner}) err := o.ksProjectOperator.CheckProjectUserInRole(username, projectId, []string{dsClient.ProjectOwner})
if err != nil { if err != nil {
klog.Errorf("%+v", err) klog.Errorf("%+v", err)
return restful.NewError(http.StatusForbidden, err.Error()) return restful.NewError(http.StatusForbidden, err.Error())
} }
dp, err := cs.ClientSets().Devops() err = o.dsProject.DeleteDevOpsProject(projectId)
if err != nil {
klog.Error(err)
return restful.NewError(http.StatusServiceUnavailable, err.Error())
}
jenkins := dp.Jenkins()
devopsdb, err := cs.ClientSets().MySQL()
if err != nil {
klog.Error(err)
return restful.NewError(http.StatusServiceUnavailable, err.Error())
}
_, err = jenkins.DeleteJob(projectId)
if err != nil && utils.GetJenkinsStatusCode(err) != http.StatusNotFound {
klog.Errorf("%+v", err)
return restful.NewError(utils.GetJenkinsStatusCode(err), err.Error())
}
roleNames := make([]string, 0)
for role := range devops.JenkinsProjectPermissionMap {
roleNames = append(roleNames, devops.GetProjectRoleName(projectId, role))
roleNames = append(roleNames, devops.GetPipelineRoleName(projectId, role))
}
err = jenkins.DeleteProjectRoles(roleNames...)
if err != nil { if err != nil {
klog.Errorf("%+v", err) klog.Errorf("%+v", err)
return restful.NewError(utils.GetJenkinsStatusCode(err), err.Error()) return err
} }
_, err = devopsdb.DeleteFrom(devops.DevOpsProjectMembershipTableName). _, err = o.db.DeleteFrom(devops.ProjectMembershipTableName).
Where(db.Eq(devops.DevOpsProjectMembershipProjectIdColumn, projectId)).Exec() Where(db.Eq(devops.ProjectMembershipProjectIdColumn, projectId)).Exec()
if err != nil { if err != nil {
klog.Errorf("%+v", err) klog.Errorf("%+v", err)
return restful.NewError(utils.GetJenkinsStatusCode(err), err.Error()) return err
} }
_, err = devopsdb.Update(devops.DevOpsProjectTableName). _, err = o.db.Update(devops.DevOpsProjectTableName).
Set(devops.StatusColumn, devops.StatusDeleted). Set(devops.StatusColumn, devops.StatusDeleted).
Where(db.Eq(devops.DevOpsProjectIdColumn, projectId)).Exec() Where(db.Eq(devops.DevOpsProjectIdColumn, projectId)).Exec()
if err != nil { if err != nil {
klog.Errorf("%+v", err) klog.Errorf("%+v", err)
return restful.NewError(utils.GetJenkinsStatusCode(err), err.Error()) return err
} }
project := &v1alpha2.DevOpsProject{} project := &v1alpha2.DevOpsProject{}
err = devopsdb.Select(devops.DevOpsProjectColumns...). err = o.db.Select(devops.DevOpsProjectColumns...).
From(devops.DevOpsProjectTableName). From(devops.DevOpsProjectTableName).
Where(db.Eq(devops.DevOpsProjectIdColumn, projectId)). Where(db.Eq(devops.DevOpsProjectIdColumn, projectId)).
LoadOne(project) LoadOne(project)
if err != nil { if err != nil {
klog.Errorf("%+v", err) klog.Errorf("%+v", err)
return restful.NewError(utils.GetJenkinsStatusCode(err), err.Error()) return err
} }
return nil return nil
} }
func CreateDevopsProject(username string, workspace string, req *v1alpha2.DevOpsProject) (*v1alpha2.DevOpsProject, error) { func (o *devopsProjectOperator) CreateDevOpsProject(username string, workspace string, req *v1alpha2.DevOpsProject) (*v1alpha2.DevOpsProject, error) {
dp, err := cs.ClientSets().Devops()
if err != nil {
klog.Error(err)
return nil, restful.NewError(http.StatusServiceUnavailable, err.Error())
}
jenkinsClient := dp.Jenkins()
devopsdb, err := cs.ClientSets().MySQL()
if err != nil {
klog.Error(err)
return nil, restful.NewError(http.StatusServiceUnavailable, err.Error())
}
project := devops.NewDevOpsProject(req.Name, req.Description, username, req.Extra, workspace) project := devops.NewDevOpsProject(req.Name, req.Description, username, req.Extra, workspace)
_, err = jenkinsClient.CreateFolder(project.ProjectId, project.Description) _, err := o.dsProject.CreateDevOpsProject(username, project)
if err != nil { if err != nil {
klog.Errorf("%+v", err) klog.Error(err)
return nil, restful.NewError(utils.GetJenkinsStatusCode(err), err.Error()) return nil, err
} }
_, err = o.db.InsertInto(devops.DevOpsProjectTableName).
var addRoleCh = make(chan *DevOpsProjectRoleResponse, 8)
var addRoleWg sync.WaitGroup
for role, permission := range devops.JenkinsProjectPermissionMap {
addRoleWg.Add(1)
go func(role string, permission gojenkins.ProjectPermissionIds) {
_, err := jenkinsClient.AddProjectRole(devops.GetProjectRoleName(project.ProjectId, role),
devops.GetProjectRolePattern(project.ProjectId), permission, true)
addRoleCh <- &DevOpsProjectRoleResponse{nil, err}
addRoleWg.Done()
}(role, permission)
}
for role, permission := range devops.JenkinsPipelinePermissionMap {
addRoleWg.Add(1)
go func(role string, permission gojenkins.ProjectPermissionIds) {
_, err := jenkinsClient.AddProjectRole(devops.GetPipelineRoleName(project.ProjectId, role),
devops.GetPipelineRolePattern(project.ProjectId), permission, true)
addRoleCh <- &DevOpsProjectRoleResponse{nil, err}
addRoleWg.Done()
}(role, permission)
}
addRoleWg.Wait()
close(addRoleCh)
for addRoleResponse := range addRoleCh {
if addRoleResponse.Err != nil {
klog.Errorf("%+v", addRoleResponse.Err)
return nil, restful.NewError(utils.GetJenkinsStatusCode(addRoleResponse.Err), addRoleResponse.Err.Error())
}
}
globalRole, err := jenkinsClient.GetGlobalRole(devops.JenkinsAllUserRoleName)
if err != nil {
klog.Errorf("%+v", err)
return nil, restful.NewError(utils.GetJenkinsStatusCode(err), err.Error())
}
if globalRole == nil {
_, err := jenkinsClient.AddGlobalRole(devops.JenkinsAllUserRoleName, gojenkins.GlobalPermissionIds{
GlobalRead: true,
}, true)
if err != nil {
klog.Error("failed to create jenkins global role")
return nil, restful.NewError(utils.GetJenkinsStatusCode(err), err.Error())
}
}
err = globalRole.AssignRole(username)
if err != nil {
klog.Errorf("%+v", err)
return nil, restful.NewError(utils.GetJenkinsStatusCode(err), err.Error())
}
projectRole, err := jenkinsClient.GetProjectRole(devops.GetProjectRoleName(project.ProjectId, devops.ProjectOwner))
if err != nil {
klog.Errorf("%+v", err)
return nil, restful.NewError(utils.GetJenkinsStatusCode(err), err.Error())
}
err = projectRole.AssignRole(username)
if err != nil {
klog.Errorf("%+v", err)
return nil, restful.NewError(utils.GetJenkinsStatusCode(err), err.Error())
}
pipelineRole, err := jenkinsClient.GetProjectRole(devops.GetPipelineRoleName(project.ProjectId, devops.ProjectOwner))
if err != nil {
klog.Errorf("%+v", err)
return nil, restful.NewError(utils.GetJenkinsStatusCode(err), err.Error())
}
err = pipelineRole.AssignRole(username)
if err != nil {
klog.Errorf("%+v", err)
return nil, restful.NewError(utils.GetJenkinsStatusCode(err), err.Error())
}
_, err = devopsdb.InsertInto(devops.DevOpsProjectTableName).
Columns(devops.DevOpsProjectColumns...).Record(project).Exec() Columns(devops.DevOpsProjectColumns...).Record(project).Exec()
if err != nil { if err != nil {
klog.Errorf("%+v", err) klog.Errorf("%+v", err)
return nil, restful.NewError(http.StatusInternalServerError, err.Error()) return nil, restful.NewError(http.StatusInternalServerError, err.Error())
} }
projectMembership := devops.NewDevOpsProjectMemberShip(username, project.ProjectId, devops.ProjectOwner, username) projectMembership := devops.NewDevOpsProjectMemberShip(username, project.ProjectId, dsClient.ProjectOwner, username)
_, err = devopsdb.InsertInto(devops.DevOpsProjectMembershipTableName). _, err = o.db.InsertInto(devops.ProjectMembershipTableName).
Columns(devops.DevOpsProjectMembershipColumns...).Record(projectMembership).Exec() Columns(devops.ProjectMembershipColumns...).Record(projectMembership).Exec()
if err != nil { if err != nil {
klog.Errorf("%+v", err) klog.Errorf("%+v", err)
return nil, restful.NewError(http.StatusInternalServerError, err.Error()) return nil, restful.NewError(http.StatusInternalServerError, err.Error())
@@ -318,8 +208,9 @@ func CreateDevopsProject(username string, workspace string, req *v1alpha2.DevOps
return project, nil return project, nil
} }
func GetUserDevopsSimpleRules(username, projectId string) ([]iam.SimpleRule, error) { func (o *devopsProjectOperator) GetUserDevOpsSimpleRules(username, projectId string) ([]iam.SimpleRule, error) {
role, err := devops.GetProjectUserRole(username, projectId)
role, err := o.getProjectUserRole(username, projectId)
if err != nil { if err != nil {
klog.Errorf("%+v", err) klog.Errorf("%+v", err)
return nil, restful.NewError(http.StatusForbidden, err.Error()) return nil, restful.NewError(http.StatusForbidden, err.Error())
@@ -327,6 +218,24 @@ func GetUserDevopsSimpleRules(username, projectId string) ([]iam.SimpleRule, err
return GetDevopsRoleSimpleRules(role), nil return GetDevopsRoleSimpleRules(role), nil
} }
func (o *devopsProjectOperator) getProjectUserRole(username, projectId string) (string, error) {
if username == devops.KS_ADMIN {
return dsClient.ProjectOwner, nil
}
membership := &dsClient.ProjectMembership{}
err := o.db.Select(devops.ProjectMembershipColumns...).
From(devops.ProjectMembershipTableName).
Where(db.And(
db.Eq(devops.ProjectMembershipUsernameColumn, username),
db.Eq(devops.ProjectMembershipProjectIdColumn, projectId))).LoadOne(membership)
if err != nil {
return "", err
}
return membership.Role, nil
}
func GetDevopsRoleSimpleRules(role string) []iam.SimpleRule { func GetDevopsRoleSimpleRules(role string) []iam.SimpleRule {
var rules []iam.SimpleRule var rules []iam.SimpleRule

View File

@@ -36,11 +36,14 @@ type Interface interface {
DescribeWorkspace(username, workspace string) (*v1alpha1.Workspace, error) DescribeWorkspace(username, workspace string) (*v1alpha1.Workspace, error)
ListWorkspaces(username string, conditions *params.Conditions, orderBy string, reverse bool, limit, offset int) (*models.PageableResponse, error) ListWorkspaces(username string, conditions *params.Conditions, orderBy string, reverse bool, limit, offset int) (*models.PageableResponse, error)
ListNamespaces(username string, conditions *params.Conditions, orderBy string, reverse bool, limit, offset int) (*models.PageableResponse, error) ListNamespaces(username string, conditions *params.Conditions, orderBy string, reverse bool, limit, offset int) (*models.PageableResponse, error)
GetWorkspace(workspace string) (*v1alpha1.Workspace, error)
DevOpsProjectOperator
} }
type tenantOperator struct { type tenantOperator struct {
workspaces WorkspaceInterface workspaces WorkspaceInterface
namespaces NamespaceInterface namespaces NamespaceInterface
DevOpsProjectOperator
} }
func (t *tenantOperator) DeleteNamespace(workspace, namespace string) error { func (t *tenantOperator) DeleteNamespace(workspace, namespace string) error {
@@ -101,7 +104,7 @@ func (t *tenantOperator) appendAnnotations(username string, workspace *v1alpha1.
if err == nil { if err == nil {
workspace.Annotations["kubesphere.io/namespace-count"] = strconv.Itoa(ns.TotalCount) workspace.Annotations["kubesphere.io/namespace-count"] = strconv.Itoa(ns.TotalCount)
} }
devops, err := ListDevopsProjects(workspace.Name, username, &params.Conditions{}, "", false, 1, 0) devops, err := t.ListDevOpsProjects(workspace.Name, username, &params.Conditions{}, "", false, 1, 0)
if err == nil { if err == nil {
workspace.Annotations["kubesphere.io/devops-count"] = strconv.Itoa(devops.TotalCount) workspace.Annotations["kubesphere.io/devops-count"] = strconv.Itoa(devops.TotalCount)
} }
@@ -132,3 +135,7 @@ func (t *tenantOperator) ListNamespaces(username string, conditions *params.Cond
return &models.PageableResponse{Items: result, TotalCount: len(namespaces)}, nil return &models.PageableResponse{Items: result, TotalCount: len(namespaces)}, nil
} }
func (t *tenantOperator) GetWorkspace(workspace string) (*v1alpha1.Workspace, error) {
return t.workspaces.GetWorkspace(workspace)
}

View File

@@ -145,12 +145,12 @@ type terminaler struct {
} }
func NewTerminaler(client kubernetes.Interface, config *rest.Config) Interface { func NewTerminaler(client kubernetes.Interface, config *rest.Config) Interface {
return &terminaler{client:client, config:config} return &terminaler{client: client, config: config}
} }
// startProcess is called by handleAttach // startProcess is called by handleAttach
// Executed cmd in the container specified in request and connects it up with the ptyHandler (a session) // Executed cmd in the container specified in request and connects it up with the ptyHandler (a session)
func (t *terminaler)startProcess(namespace, podName, containerName string, cmd []string, ptyHandler PtyHandler) error { func (t *terminaler) startProcess(namespace, podName, containerName string, cmd []string, ptyHandler PtyHandler) error {
req := t.client.CoreV1().RESTClient().Post(). req := t.client.CoreV1().RESTClient().Post().
Resource("pods"). Resource("pods").
Name(podName). Name(podName).
@@ -194,7 +194,7 @@ func isValidShell(validShells []string, shell string) bool {
return false return false
} }
func (t *terminaler)HandleSession(shell, namespace, podName, containerName string, conn *websocket.Conn) { func (t *terminaler) HandleSession(shell, namespace, podName, containerName string, conn *websocket.Conn) {
var err error var err error
validShells := []string{"sh", "bash"} validShells := []string{"sh", "bash"}

View File

@@ -9,7 +9,7 @@ import (
"kubesphere.io/kubesphere/pkg/apiserver/runtime" "kubesphere.io/kubesphere/pkg/apiserver/runtime"
"kubesphere.io/kubesphere/pkg/simple/client/alerting" "kubesphere.io/kubesphere/pkg/simple/client/alerting"
"kubesphere.io/kubesphere/pkg/simple/client/cache" "kubesphere.io/kubesphere/pkg/simple/client/cache"
"kubesphere.io/kubesphere/pkg/simple/client/devops" "kubesphere.io/kubesphere/pkg/simple/client/devops/jenkins"
"kubesphere.io/kubesphere/pkg/simple/client/elasticsearch" "kubesphere.io/kubesphere/pkg/simple/client/elasticsearch"
"kubesphere.io/kubesphere/pkg/simple/client/k8s" "kubesphere.io/kubesphere/pkg/simple/client/k8s"
"kubesphere.io/kubesphere/pkg/simple/client/kubesphere" "kubesphere.io/kubesphere/pkg/simple/client/kubesphere"
@@ -159,7 +159,7 @@ var (
type Config struct { type Config struct {
MySQLOptions *mysql.Options `json:"mysql,omitempty" yaml:"mysql,omitempty" mapstructure:"mysql"` MySQLOptions *mysql.Options `json:"mysql,omitempty" yaml:"mysql,omitempty" mapstructure:"mysql"`
DevopsOptions *devops.Options `json:"devops,omitempty" yaml:"devops,omitempty" mapstructure:"devops"` DevopsOptions *jenkins.Options `json:"devops,omitempty" yaml:"devops,omitempty" mapstructure:"devops"`
SonarQubeOptions *sonarqube.Options `json:"sonarqube,omitempty" yaml:"sonarQube,omitempty" mapstructure:"sonarqube"` SonarQubeOptions *sonarqube.Options `json:"sonarqube,omitempty" yaml:"sonarQube,omitempty" mapstructure:"sonarqube"`
KubernetesOptions *k8s.KubernetesOptions `json:"kubernetes,omitempty" yaml:"kubernetes,omitempty" mapstructure:"kubernetes"` KubernetesOptions *k8s.KubernetesOptions `json:"kubernetes,omitempty" yaml:"kubernetes,omitempty" mapstructure:"kubernetes"`
ServiceMeshOptions *servicemesh.Options `json:"servicemesh,omitempty" yaml:"servicemesh,omitempty" mapstructure:"servicemesh"` ServiceMeshOptions *servicemesh.Options `json:"servicemesh,omitempty" yaml:"servicemesh,omitempty" mapstructure:"servicemesh"`
@@ -182,7 +182,7 @@ type Config struct {
func newConfig() *Config { func newConfig() *Config {
return &Config{ return &Config{
MySQLOptions: mysql.NewMySQLOptions(), MySQLOptions: mysql.NewMySQLOptions(),
DevopsOptions: devops.NewDevopsOptions(), DevopsOptions: jenkins.NewDevopsOptions(),
SonarQubeOptions: sonarqube.NewSonarQubeOptions(), SonarQubeOptions: sonarqube.NewSonarQubeOptions(),
KubernetesOptions: k8s.NewKubernetesOptions(), KubernetesOptions: k8s.NewKubernetesOptions(),
ServiceMeshOptions: servicemesh.NewServiceMeshOptions(), ServiceMeshOptions: servicemesh.NewServiceMeshOptions(),

View File

@@ -6,7 +6,7 @@ import (
"io/ioutil" "io/ioutil"
"kubesphere.io/kubesphere/pkg/simple/client/alerting" "kubesphere.io/kubesphere/pkg/simple/client/alerting"
"kubesphere.io/kubesphere/pkg/simple/client/cache" "kubesphere.io/kubesphere/pkg/simple/client/cache"
"kubesphere.io/kubesphere/pkg/simple/client/devops" "kubesphere.io/kubesphere/pkg/simple/client/devops/jenkins"
"kubesphere.io/kubesphere/pkg/simple/client/elasticsearch" "kubesphere.io/kubesphere/pkg/simple/client/elasticsearch"
"kubesphere.io/kubesphere/pkg/simple/client/k8s" "kubesphere.io/kubesphere/pkg/simple/client/k8s"
"kubesphere.io/kubesphere/pkg/simple/client/kubesphere" "kubesphere.io/kubesphere/pkg/simple/client/kubesphere"
@@ -34,7 +34,7 @@ func newTestConfig() *Config {
MaxOpenConnections: 20, MaxOpenConnections: 20,
MaxConnectionLifeTime: time.Duration(10) * time.Second, MaxConnectionLifeTime: time.Duration(10) * time.Second,
}, },
DevopsOptions: &devops.Options{ DevopsOptions: &jenkins.Options{
Host: "http://ks-devops.kubesphere-devops-system.svc", Host: "http://ks-devops.kubesphere-devops-system.svc",
Username: "jenkins", Username: "jenkins",
Password: "kubesphere", Password: "kubesphere",

View File

@@ -47,3 +47,11 @@ func ParseSvcErr(err error, resp *restful.Response) {
resp.WriteHeaderAndEntity(http.StatusInternalServerError, Wrap(err)) resp.WriteHeaderAndEntity(http.StatusInternalServerError, Wrap(err))
} }
} }
func GetServiceErrorCode(err error) int {
if svcErr, ok := err.(restful.ServiceError); ok {
return svcErr.Code
} else {
return http.StatusInternalServerError
}
}

View File

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

View File

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

View File

@@ -0,0 +1,115 @@
package devops
const (
LastBuild = "lastBuild"
LastCompletedBuild = "lastCompletedBuild"
LastFailedBuild = "lastFailedBuild"
LastStableBuild = "lastStableBuild"
LastSuccessfulBuild = "lastSuccessfulBuild"
LastUnstableBuild = "lastUnstableBuild"
LastUnsuccessfulBuild = "lastUnsuccessfulBuild"
FirstBuild = "firstBuild"
)
type GeneralParameter struct {
Name string
Value string
}
type Branch struct {
SHA1 string `json:",omitempty"`
Name string `json:",omitempty"`
}
type BuildRevision struct {
SHA1 string `json:"SHA1,omitempty"`
Branch []Branch `json:"Branch,omitempty"`
}
type Builds struct {
BuildNumber int64 `json:"buildNumber"`
BuildResult interface{} `json:"buildResult"`
Marked BuildRevision `json:"marked"`
Revision BuildRevision `json:"revision"`
}
type Culprit struct {
AbsoluteUrl string
FullName string
}
type GeneralAction struct {
Parameters []GeneralParameter `json:"parameters,omitempty"`
Causes []map[string]interface{} `json:"causes,omitempty"`
BuildsByBranchName map[string]Builds `json:"buildsByBranchName,omitempty"`
LastBuiltRevision *BuildRevision `json:"lastBuiltRevision,omitempty"`
RemoteUrls []string `json:"remoteUrls,omitempty"`
ScmName string `json:"scmName,omitempty"`
Subdir interface{} `json:"subdir,omitempty"`
ClassName string `json:"_class,omitempty"`
SonarTaskId string `json:"ceTaskId,omitempty"`
SonarServerUrl string `json:"serverUrl,omitempty"`
SonarDashboardUrl string `json:"sonarqubeDashboardUrl,omitempty"`
TotalCount int64 `json:",omitempty"`
UrlName string `json:",omitempty"`
}
type Build struct {
Actions []GeneralAction
Artifacts []struct {
DisplayPath string `json:"displayPath"`
FileName string `json:"fileName"`
RelativePath string `json:"relativePath"`
} `json:"artifacts"`
Building bool `json:"building"`
BuiltOn string `json:"builtOn"`
ChangeSet struct {
Items []struct {
AffectedPaths []string `json:"affectedPaths"`
Author struct {
AbsoluteUrl string `json:"absoluteUrl"`
FullName string `json:"fullName"`
} `json:"author"`
Comment string `json:"comment"`
CommitID string `json:"commitId"`
Date string `json:"date"`
ID string `json:"id"`
Msg string `json:"msg"`
Paths []struct {
EditType string `json:"editType"`
File string `json:"file"`
} `json:"paths"`
Timestamp int64 `json:"timestamp"`
} `json:"items"`
Kind string `json:"kind"`
Revisions []struct {
Module string
Revision int
} `json:"revision"`
} `json:"changeSet"`
Culprits []Culprit `json:"culprits"`
Description interface{} `json:"description"`
Duration int64 `json:"duration"`
EstimatedDuration int64 `json:"estimatedDuration"`
Executor interface{} `json:"executor"`
FullDisplayName string `json:"fullDisplayName"`
ID string `json:"id"`
KeepLog bool `json:"keepLog"`
Number int64 `json:"number"`
QueueID int64 `json:"queueId"`
Result string `json:"result"`
Timestamp int64 `json:"timestamp"`
URL string `json:"url"`
Runs []struct {
Number int64
URL string
} `json:"runs"`
}
type BuildGetter interface {
// GetProjectPipelineBuildByType get the last build of the pipeline, status can specify the status of the last build.
GetProjectPipelineBuildByType(projectId, pipelineId string, status string) (*Build, error)
// GetMultiBranchPipelineBuildByType get the last build of the pipeline, status can specify the status of the last build.
GetMultiBranchPipelineBuildByType(projectId, pipelineId, branch string, status string) (*Build, error)
}

View File

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

View File

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

View File

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

View File

@@ -12,14 +12,15 @@
// License for the specific language governing permissions and limitations // License for the specific language governing permissions and limitations
// under the License. // under the License.
package gojenkins package jenkins
import ( import (
"bytes" "bytes"
"errors" "errors"
"github.com/emicklei/go-restful"
"kubesphere.io/kubesphere/pkg/simple/client/devops"
"net/http" "net/http"
"net/url" "net/url"
"regexp"
"strconv" "strconv"
"time" "time"
) )
@@ -31,26 +32,26 @@ const (
) )
type Build struct { type Build struct {
Raw *BuildResponse Raw *devops.Build
Job *Job Job *Job
Jenkins *Jenkins Jenkins *Jenkins
Base string Base string
Depth int Depth int
} }
type parameter struct { type Parameter struct {
Name string Name string
Value string Value string
} }
type branch struct { type Branch struct {
SHA1 string `json:",omitempty"` SHA1 string `json:",omitempty"`
Name string `json:",omitempty"` Name string `json:",omitempty"`
} }
type BuildRevision struct { type BuildRevision struct {
SHA1 string `json:"SHA1,omitempty"` SHA1 string `json:"SHA1,omitempty"`
Branch []branch `json:"branch,omitempty"` Branch []Branch `json:"Branch,omitempty"`
} }
type Builds struct { type Builds struct {
@@ -66,7 +67,7 @@ type Culprit struct {
} }
type GeneralObj struct { type GeneralObj struct {
Parameters []parameter `json:"parameters,omitempty"` Parameters []Parameter `json:"parameters,omitempty"`
Causes []map[string]interface{} `json:"causes,omitempty"` Causes []map[string]interface{} `json:"causes,omitempty"`
BuildsByBranchName map[string]Builds `json:"buildsByBranchName,omitempty"` BuildsByBranchName map[string]Builds `json:"buildsByBranchName,omitempty"`
LastBuiltRevision *BuildRevision `json:"lastBuiltRevision,omitempty"` LastBuiltRevision *BuildRevision `json:"lastBuiltRevision,omitempty"`
@@ -114,7 +115,7 @@ type TestResult struct {
} }
type BuildResponse struct { type BuildResponse struct {
Actions []GeneralObj Actions []devops.GeneralAction
Artifacts []struct { Artifacts []struct {
DisplayPath string `json:"displayPath"` DisplayPath string `json:"displayPath"`
FileName string `json:"fileName"` FileName string `json:"fileName"`
@@ -146,22 +147,21 @@ type BuildResponse struct {
Revision int Revision int
} `json:"revision"` } `json:"revision"`
} `json:"changeSet"` } `json:"changeSet"`
Culprits []Culprit `json:"culprits"` Culprits []devops.Culprit `json:"culprits"`
Description interface{} `json:"description"` Description interface{} `json:"description"`
Duration int64 `json:"duration"` Duration int64 `json:"duration"`
EstimatedDuration int64 `json:"estimatedDuration"` EstimatedDuration int64 `json:"estimatedDuration"`
Executor interface{} `json:"executor"` Executor interface{} `json:"executor"`
FullDisplayName string `json:"fullDisplayName"` FullDisplayName string `json:"fullDisplayName"`
ID string `json:"id"` ID string `json:"id"`
KeepLog bool `json:"keepLog"` KeepLog bool `json:"keepLog"`
Number int64 `json:"number"` Number int64 `json:"number"`
QueueID int64 `json:"queueId"` QueueID int64 `json:"queueId"`
Result string `json:"result"` Result string `json:"result"`
Timestamp int64 `json:"timestamp"` Timestamp int64 `json:"timestamp"`
URL string `json:"url"` URL string `json:"url"`
MavenArtifacts interface{} `json:"mavenArtifacts"` MavenArtifacts interface{} `json:"mavenArtifacts"`
MavenVersionUsed string `json:"mavenVersionUsed"` MavenVersionUsed string `json:"mavenVersionUsed"`
FingerPrint []FingerPrintResponse
Runs []struct { Runs []struct {
Number int64 Number int64
URL string URL string
@@ -169,14 +169,10 @@ type BuildResponse struct {
} }
// Builds // Builds
func (b *Build) Info() *BuildResponse { func (b *Build) Info() *devops.Build {
return b.Raw return b.Raw
} }
func (b *Build) GetActions() []GeneralObj {
return b.Raw.Actions
}
func (b *Build) GetUrl() string { func (b *Build) GetUrl() string {
return b.Raw.URL return b.Raw.URL
} }
@@ -188,23 +184,6 @@ func (b *Build) GetResult() string {
return b.Raw.Result return b.Raw.Result
} }
func (b *Build) GetArtifacts() []Artifact {
artifacts := make([]Artifact, len(b.Raw.Artifacts))
for i, artifact := range b.Raw.Artifacts {
artifacts[i] = Artifact{
Jenkins: b.Jenkins,
Build: b,
FileName: artifact.FileName,
Path: b.Base + "/artifact/" + artifact.RelativePath,
}
}
return artifacts
}
func (b *Build) GetCulprits() []Culprit {
return b.Raw.Culprits
}
func (b *Build) Stop() (bool, error) { func (b *Build) Stop() (bool, error) {
if b.IsRunning() { if b.IsRunning() {
response, err := b.Jenkins.Requester.Post(b.Base+"/stop", nil, nil, nil) response, err := b.Jenkins.Requester.Post(b.Base+"/stop", nil, nil, nil)
@@ -238,15 +217,6 @@ func (b *Build) GetCauses() ([]map[string]interface{}, error) {
return nil, errors.New("No Causes") return nil, errors.New("No Causes")
} }
func (b *Build) GetParameters() []parameter {
for _, a := range b.Raw.Actions {
if a.Parameters != nil {
return a.Parameters
}
}
return nil
}
func (b *Build) GetInjectedEnvVars() (map[string]string, error) { func (b *Build) GetInjectedEnvVars() (map[string]string, error) {
var envVars struct { var envVars struct {
EnvMap map[string]string `json:"envMap"` EnvMap map[string]string `json:"envMap"`
@@ -286,31 +256,6 @@ func (b *Build) GetDownstreamBuilds() ([]*Build, error) {
return result, nil return result, nil
} }
func (b *Build) GetDownstreamJobNames() []string {
result := make([]string, 0)
downstreamJobs := b.Job.GetDownstreamJobsMetadata()
fingerprints := b.GetAllFingerPrints()
for _, fingerprint := range fingerprints {
for _, usage := range fingerprint.Raw.Usage {
for _, job := range downstreamJobs {
if job.Name == usage.Name {
result = append(result, job.Name)
}
}
}
}
return result
}
func (b *Build) GetAllFingerPrints() []*FingerPrint {
b.Poll(3)
result := make([]*FingerPrint, len(b.Raw.FingerPrint))
for i, f := range b.Raw.FingerPrint {
result[i] = &FingerPrint{Jenkins: b.Jenkins, Base: "/fingerprint/", Id: f.Hash, Raw: &f}
}
return result
}
func (b *Build) GetUpstreamJob() (*Job, error) { func (b *Build) GetUpstreamJob() (*Job, error) {
causes, err := b.GetCauses() causes, err := b.GetCauses()
if err != nil { if err != nil {
@@ -356,22 +301,6 @@ func (b *Build) GetUpstreamBuild() (*Build, error) {
return nil, errors.New("Build not found") return nil, errors.New("Build not found")
} }
func (b *Build) GetMatrixRuns() ([]*Build, error) {
_, err := b.Poll(0)
if err != nil {
return nil, err
}
runs := b.Raw.Runs
result := make([]*Build, len(b.Raw.Runs))
r, _ := regexp.Compile(`job/(.*?)/(.*?)/(\d+)/`)
for i, run := range runs {
result[i] = &Build{Jenkins: b.Jenkins, Job: b.Job, Raw: new(BuildResponse), Depth: 1, Base: "/" + r.FindString(run.URL)}
result[i].Poll()
}
return result, nil
}
func (b *Build) GetResultSet() (*TestResult, error) { func (b *Build) GetResultSet() (*TestResult, error) {
url := b.Base + "/testReport" url := b.Base + "/testReport"
@@ -395,24 +324,6 @@ func (b *Build) GetDuration() int64 {
return b.Raw.Duration return b.Raw.Duration
} }
func (b *Build) GetRevision() string {
vcs := b.Raw.ChangeSet.Kind
if vcs == Git || vcs == Hg {
for _, a := range b.Raw.Actions {
if a.LastBuiltRevision.SHA1 != "" {
return a.LastBuiltRevision.SHA1
}
if a.MercurialRevisionNumber != "" {
return a.MercurialRevisionNumber
}
}
} else if vcs == Svn {
return strconv.Itoa(b.Raw.ChangeSet.Revisions[0].Revision)
}
return ""
}
func (b *Build) GetRevisionBranch() string { func (b *Build) GetRevisionBranch() string {
vcs := b.Raw.ChangeSet.Kind vcs := b.Raw.ChangeSet.Kind
if vcs == Git { if vcs == Git {
@@ -456,7 +367,7 @@ func (b *Build) PauseToggle() error {
return nil return nil
} }
// Poll for current data. Optional parameter - depth. // Poll for current data. Optional Parameter - depth.
// More about depth here: https://wiki.jenkins-ci.org/display/JENKINS/Remote+access+API // More about depth here: https://wiki.jenkins-ci.org/display/JENKINS/Remote+access+API
func (b *Build) Poll(options ...interface{}) (int, error) { func (b *Build) Poll(options ...interface{}) (int, error) {
depth := "-1" depth := "-1"
@@ -484,3 +395,20 @@ func (b *Build) Poll(options ...interface{}) (int, error) {
} }
return response.StatusCode, nil return response.StatusCode, nil
} }
func (j *Jenkins) GetProjectPipelineBuildByType(projectId, pipelineId string, status string) (*devops.Build, error) {
job, err := j.GetJob(pipelineId, projectId)
if err != nil {
return nil, restful.NewError(GetJenkinsStatusCode(err), err.Error())
}
build, err := job.getBuildByType(status)
return build.Raw, nil
}
func (j *Jenkins) GetMultiBranchPipelineBuildByType(projectId, pipelineId, branch string, status string) (*devops.Build, error) {
job, err := j.GetJob(pipelineId, projectId, branch)
if err != nil {
return nil, restful.NewError(GetJenkinsStatusCode(err), err.Error())
}
build, err := job.getBuildByType(status)
return build.Raw, nil
}

View File

@@ -1,4 +1,4 @@
package gojenkins package jenkins
const ( const (
STATUS_FAIL = "FAIL" STATUS_FAIL = "FAIL"

View File

@@ -0,0 +1,341 @@
/*
Copyright 2018 The KubeSphere Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package jenkins
import (
"errors"
"fmt"
"github.com/PuerkitoBio/goquery"
"github.com/emicklei/go-restful"
"k8s.io/klog"
"kubesphere.io/kubesphere/pkg/simple/client/devops"
"net/http"
"strconv"
"strings"
)
const SSHCrenditalStaplerClass = "com.cloudbees.jenkins.plugins.sshcredentials.impl.BasicSSHUserPrivateKey"
const DirectSSHCrenditalStaplerClass = "com.cloudbees.jenkins.plugins.sshcredentials.impl.BasicSSHUserPrivateKey$DirectEntryPrivateKeySource"
const UsernamePassswordCredentialStaplerClass = "com.cloudbees.plugins.credentials.impl.UsernamePasswordCredentialsImpl"
const SecretTextCredentialStaplerClass = "org.jenkinsci.plugins.plaincredentials.impl.StringCredentialsImpl"
const KubeconfigCredentialStaplerClass = "com.microsoft.jenkins.kubernetes.credentials.KubeconfigCredentials"
const DirectKubeconfigCredentialStaperClass = "com.microsoft.jenkins.kubernetes.credentials.KubeconfigCredentials$DirectEntryKubeconfigSource"
const GLOBALScope = "GLOBAL"
type UsernamePasswordCredential struct {
Scope string `json:"scope"`
Id string `json:"id"`
Username string `json:"username"`
Password string `json:"password"`
Description string `json:"description"`
StaplerClass string `json:"stapler-class"`
}
type SshCredential struct {
Scope string `json:"scope"`
Id string `json:"id"`
Username string `json:"username"`
Passphrase string `json:"passphrase"`
KeySource PrivateKeySource `json:"privateKeySource"`
Description string `json:"description"`
StaplerClass string `json:"stapler-class"`
}
type SecretTextCredential struct {
Scope string `json:"scope"`
Id string `json:"id"`
Secret string `json:"secret"`
Description string `json:"description"`
StaplerClass string `json:"stapler-class"`
}
type KubeconfigCredential struct {
Scope string `json:"scope"`
Id string `json:"id"`
Description string `json:"description"`
KubeconfigSource KubeconfigSource `json:"kubeconfigSource"`
StaplerClass string `json:"stapler-class"`
}
type PrivateKeySource struct {
StaplerClass string `json:"stapler-class"`
PrivateKey string `json:"privateKey"`
}
type KubeconfigSource struct {
StaplerClass string `json:"stapler-class"`
Content string `json:"content"`
}
type CredentialResponse struct {
Id string `json:"id"`
TypeName string `json:"typeName"`
DisplayName string `json:"displayName"`
Fingerprint *struct {
FileName string `json:"file_name,omitempty" description:"Credential's display name and description"`
Hash string `json:"hash,omitempty" description:"Credential's hash"`
Usage []*struct {
Name string `json:"name,omitempty" description:"Jenkins pipeline full name"`
Ranges struct {
Ranges []*struct {
Start int `json:"start,omitempty" description:"Start build number"`
End int `json:"end,omitempty" description:"End build number"`
} `json:"ranges,omitempty"`
} `json:"ranges,omitempty" description:"The build number of all pipelines that use this credential"`
} `json:"usage,omitempty" description:"all usage of Credential"`
} `json:"fingerprint,omitempty" description:"usage of the Credential"`
Description string `json:"description,omitempty"`
Domain string `json:"domain"`
}
func NewSshCredential(id, username, passphrase, privateKey, description string) *SshCredential {
keySource := PrivateKeySource{
StaplerClass: DirectSSHCrenditalStaplerClass,
PrivateKey: privateKey,
}
return &SshCredential{
Scope: GLOBALScope,
Id: id,
Username: username,
Passphrase: passphrase,
KeySource: keySource,
Description: description,
StaplerClass: SSHCrenditalStaplerClass,
}
}
func NewUsernamePasswordCredential(id, username, password, description string) *UsernamePasswordCredential {
return &UsernamePasswordCredential{
Scope: GLOBALScope,
Id: id,
Username: username,
Password: password,
Description: description,
StaplerClass: UsernamePassswordCredentialStaplerClass,
}
}
func NewSecretTextCredential(id, secret, description string) *SecretTextCredential {
return &SecretTextCredential{
Scope: GLOBALScope,
Id: id,
Secret: secret,
Description: description,
StaplerClass: SecretTextCredentialStaplerClass,
}
}
func NewKubeconfigCredential(id, content, description string) *KubeconfigCredential {
credentialSource := KubeconfigSource{
StaplerClass: DirectKubeconfigCredentialStaperClass,
Content: content,
}
return &KubeconfigCredential{
Scope: GLOBALScope,
Id: id,
Description: description,
KubeconfigSource: credentialSource,
StaplerClass: KubeconfigCredentialStaplerClass,
}
}
func (j *Jenkins) GetCredentialInProject(projectId, id string, content bool) (*devops.Credential, error) {
responseStruct := &devops.Credential{}
domain := "_"
response, err := j.Requester.GetJSON(
fmt.Sprintf("/job/%s/credentials/store/folder/domain/_/credential/%s", projectId, id),
responseStruct, map[string]string{
"depth": "2",
})
if err != nil {
return nil, err
}
if response.StatusCode != http.StatusOK {
return nil, errors.New(strconv.Itoa(response.StatusCode))
}
responseStruct.Domain = domain
if content {
}
contentString := ""
response, err = j.Requester.GetHtml(
fmt.Sprintf("/job/%s/credentials/store/folder/domain/%s/credential/%s/update", projectId, domain, id),
&contentString, nil)
if err != nil {
return nil, err
}
if response.StatusCode != http.StatusOK {
return nil, errors.New(strconv.Itoa(response.StatusCode))
}
stringReader := strings.NewReader(contentString)
doc, err := goquery.NewDocumentFromReader(stringReader)
if err != nil {
klog.Errorf("%+v", err)
return nil, restful.NewError(http.StatusInternalServerError, err.Error())
}
switch responseStruct.Type {
case devops.CredentialTypeKubeConfig:
content := &devops.KubeconfigCredential{}
doc.Find("textarea[name*=content]").Each(func(i int, selection *goquery.Selection) {
value := selection.Text()
content.Content = value
})
responseStruct.KubeconfigCredential = content
case devops.CredentialTypeUsernamePassword:
content := &devops.UsernamePasswordCredential{}
doc.Find("input[name*=username]").Each(func(i int, selection *goquery.Selection) {
value, _ := selection.Attr("value")
content.Username = value
})
responseStruct.UsernamePasswordCredential = content
case devops.CredentialTypeSsh:
content := &devops.SshCredential{}
doc.Find("input[name*=username]").Each(func(i int, selection *goquery.Selection) {
value, _ := selection.Attr("value")
content.Username = value
})
doc.Find("textarea[name*=privateKey]").Each(func(i int, selection *goquery.Selection) {
value := selection.Text()
content.PrivateKey = value
})
responseStruct.SshCredential = content
}
return responseStruct, nil
}
func (j *Jenkins) GetCredentialsInProject(projectId string) ([]*devops.Credential, error) {
domain := "_"
var responseStruct = &struct {
Credentials []*devops.Credential `json:"credentials"`
}{}
response, err := j.Requester.GetJSON(
fmt.Sprintf("/job/%s/credentials/store/folder/domain/_", projectId),
responseStruct, map[string]string{
"depth": "2",
})
if err != nil {
return nil, err
}
if response.StatusCode != http.StatusOK {
return nil, errors.New(strconv.Itoa(response.StatusCode))
}
for _, credential := range responseStruct.Credentials {
credential.Domain = domain
}
return responseStruct.Credentials, nil
}
func (j *Jenkins) CreateCredentialInProject(projectId string, credential *devops.Credential) (*string, error) {
var request interface{}
responseString := ""
switch credential.Type {
case devops.CredentialTypeUsernamePassword:
request = NewUsernamePasswordCredential(credential.Id,
credential.UsernamePasswordCredential.Username, credential.UsernamePasswordCredential.Password,
credential.Description)
case devops.CredentialTypeSsh:
request = NewSshCredential(credential.Id,
credential.SshCredential.Username, credential.SshCredential.Passphrase,
credential.SshCredential.PrivateKey, credential.Description)
case devops.CredentialTypeSecretText:
request = NewSecretTextCredential(credential.Id,
credential.SecretTextCredential.Secret, credential.Description)
case devops.CredentialTypeKubeConfig:
request = NewKubeconfigCredential(credential.Id,
credential.KubeconfigCredential.Content, credential.Description)
default:
err := fmt.Errorf("error unsupport credential type")
klog.Errorf("%+v", err)
return nil, restful.NewError(http.StatusBadRequest, err.Error())
}
response, err := j.Requester.Post(
fmt.Sprintf("/job/%s/credentials/store/folder/domain/_/createCredentials", projectId),
nil, &responseString, map[string]string{
"json": makeJson(map[string]interface{}{
"credentials": request,
}),
})
if err != nil {
return nil, err
}
if response.StatusCode != http.StatusOK {
return nil, errors.New(strconv.Itoa(response.StatusCode))
}
return &credential.Id, nil
}
func (j *Jenkins) UpdateCredentialInProject(projectId string, credential *devops.Credential) (*string, error) {
requestContent := ""
switch credential.Type {
case devops.CredentialTypeUsernamePassword:
requestStruct := NewUsernamePasswordCredential(credential.Id,
credential.UsernamePasswordCredential.Username, credential.UsernamePasswordCredential.Password,
credential.Description)
requestContent = makeJson(requestStruct)
case devops.CredentialTypeSsh:
requestStruct := NewSshCredential(credential.Id,
credential.SshCredential.Username, credential.SshCredential.Passphrase,
credential.SshCredential.PrivateKey, credential.Description)
requestContent = makeJson(requestStruct)
case devops.CredentialTypeSecretText:
requestStruct := NewSecretTextCredential(credential.Id,
credential.SecretTextCredential.Secret, credential.Description)
requestContent = makeJson(requestStruct)
case devops.CredentialTypeKubeConfig:
requestStruct := NewKubeconfigCredential(credential.Id,
credential.KubeconfigCredential.Content, credential.Description)
requestContent = makeJson(requestStruct)
default:
err := fmt.Errorf("error unsupport credential type")
klog.Errorf("%+v", err)
return nil, restful.NewError(http.StatusBadRequest, err.Error())
}
response, err := j.Requester.Post(
fmt.Sprintf("/job/%s/credentials/store/folder/domain/_/credential/%s/updateSubmit", projectId, credential.Id),
nil, nil, map[string]string{
"json": requestContent,
})
if err != nil {
return nil, err
}
if response.StatusCode != http.StatusOK {
return nil, errors.New(strconv.Itoa(response.StatusCode))
}
return &credential.Id, nil
}
func (j *Jenkins) DeleteCredentialInProject(projectId, id string) (*string, error) {
response, err := j.Requester.Post(
fmt.Sprintf("/job/%s/credentials/store/folder/domain/_/credential/%s/doDelete", projectId, id),
nil, nil, nil)
if err != nil {
return nil, err
}
if response.StatusCode != http.StatusOK {
return nil, errors.New(strconv.Itoa(response.StatusCode))
}
return &id, nil
}

View File

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

View File

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

View File

@@ -12,7 +12,7 @@
// License for the specific language governing permissions and limitations // License for the specific language governing permissions and limitations
// under the License. // under the License.
package gojenkins package jenkins
import ( import (
"errors" "errors"
@@ -33,8 +33,6 @@ type FolderResponse struct {
Name string `json:"name"` Name string `json:"name"`
URL string `json:"url"` URL string `json:"url"`
Jobs []InnerJob `json:"jobs"` Jobs []InnerJob `json:"jobs"`
PrimaryView *ViewData `json:"primaryView"`
Views []ViewData `json:"views"`
} }
func (f *Folder) parentBase() string { func (f *Folder) parentBase() string {

View File

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

View File

@@ -12,13 +12,14 @@
// License for the specific language governing permissions and limitations // License for the specific language governing permissions and limitations
// under the License. // under the License.
package gojenkins package jenkins
import ( import (
"bytes" "bytes"
"encoding/json" "encoding/json"
"errors" "errors"
"fmt" "fmt"
"kubesphere.io/kubesphere/pkg/simple/client/devops"
"net/url" "net/url"
"path" "path"
"strconv" "strconv"
@@ -60,7 +61,7 @@ type ParameterDefinition struct {
type JobResponse struct { type JobResponse struct {
Class string `json:"_class"` Class string `json:"_class"`
Actions []GeneralObj Actions []devops.GeneralAction
Buildable bool `json:"buildable"` Buildable bool `json:"buildable"`
Builds []JobBuild Builds []JobBuild
Color string `json:"color"` Color string `json:"color"`
@@ -96,8 +97,6 @@ type JobResponse struct {
UpstreamProjects []InnerJob `json:"upstreamProjects"` UpstreamProjects []InnerJob `json:"upstreamProjects"`
URL string `json:"url"` URL string `json:"url"`
Jobs []InnerJob `json:"jobs"` Jobs []InnerJob `json:"jobs"`
PrimaryView *ViewData `json:"primaryView"`
Views []ViewData `json:"views"`
} }
func (j *Job) parentBase() string { func (j *Job) parentBase() string {
@@ -123,7 +122,7 @@ func (j *Job) GetDetails() *JobResponse {
} }
func (j *Job) GetBuild(id int64) (*Build, error) { func (j *Job) GetBuild(id int64) (*Build, error) {
build := Build{Jenkins: j.Jenkins, Job: j, Raw: new(BuildResponse), Depth: 1, Base: "/job/" + j.GetName() + "/" + strconv.FormatInt(id, 10)} build := Build{Jenkins: j.Jenkins, Job: j, Raw: new(devops.Build), Depth: 1, Base: "/job/" + j.GetName() + "/" + strconv.FormatInt(id, 10)}
status, err := build.Poll() status, err := build.Poll()
if err != nil { if err != nil {
return nil, err return nil, err
@@ -153,7 +152,7 @@ func (j *Job) getBuildByType(buildType string) (*Build, error) {
Jenkins: j.Jenkins, Jenkins: j.Jenkins,
Depth: 1, Depth: 1,
Job: j, Job: j,
Raw: new(BuildResponse), Raw: new(devops.Build),
Base: j.Base + "/" + number} Base: j.Base + "/" + number}
status, err := build.Poll() status, err := build.Poll()
if err != nil { if err != nil {
@@ -212,10 +211,6 @@ func (j *Job) GetAllBuildStatus() ([]JobBuildStatus, error) {
return buildsResp.Builds, nil return buildsResp.Builds, nil
} }
func (j *Job) GetSubJobsMetadata() []InnerJob {
return j.Raw.SubJobs
}
func (j *Job) GetUpstreamJobsMetadata() []InnerJob { func (j *Job) GetUpstreamJobsMetadata() []InnerJob {
return j.Raw.UpstreamProjects return j.Raw.UpstreamProjects
} }
@@ -224,18 +219,6 @@ func (j *Job) GetDownstreamJobsMetadata() []InnerJob {
return j.Raw.DownstreamProjects return j.Raw.DownstreamProjects
} }
func (j *Job) GetSubJobs() ([]*Job, error) {
jobs := make([]*Job, len(j.Raw.SubJobs))
for i, job := range j.Raw.SubJobs {
ji, err := j.Jenkins.GetSubJob(j.GetName(), job.Name)
if err != nil {
return nil, err
}
jobs[i] = ji
}
return jobs, nil
}
func (j *Job) GetInnerJobsMetadata() []InnerJob { func (j *Job) GetInnerJobsMetadata() []InnerJob {
return j.Raw.Jobs return j.Raw.Jobs
} }
@@ -519,11 +502,3 @@ func (j *Job) Poll() (int, error) {
} }
return response.StatusCode, nil return response.StatusCode, nil
} }
func (j *Job) History() ([]*History, error) {
resp, err := j.Jenkins.Requester.Get(j.Base+"/buildHistory/ajax", nil, nil)
if err != nil {
return nil, err
}
return parseBuildHistory(resp.Body), nil
}

View File

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

View File

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

View File

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

View File

@@ -1,179 +1,14 @@
/* package jenkins
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 ( import (
"fmt" "fmt"
"github.com/beevik/etree" "github.com/beevik/etree"
"github.com/kubesphere/sonargo/sonar" "kubesphere.io/kubesphere/pkg/simple/client/devops"
"k8s.io/klog"
"kubesphere.io/kubesphere/pkg/gojenkins"
"kubesphere.io/kubesphere/pkg/simple/client"
"strconv" "strconv"
"strings" "strings"
"time" "time"
) )
const (
NoScmPipelineType = "pipeline"
MultiBranchPipelineType = "multi-branch-pipeline"
)
type Parameters []*Parameter
var ParameterTypeMap = map[string]string{
"hudson.model.StringParameterDefinition": "string",
"hudson.model.ChoiceParameterDefinition": "choice",
"hudson.model.TextParameterDefinition": "text",
"hudson.model.BooleanParameterDefinition": "boolean",
"hudson.model.FileParameterDefinition": "file",
"hudson.model.PasswordParameterDefinition": "password",
}
const (
SonarAnalysisActionClass = "hudson.plugins.sonar.action.SonarAnalysisAction"
SonarMetricKeys = "alert_status,quality_gate_details,bugs,new_bugs,reliability_rating,new_reliability_rating,vulnerabilities,new_vulnerabilities,security_rating,new_security_rating,code_smells,new_code_smells,sqale_rating,new_maintainability_rating,sqale_index,new_technical_debt,coverage,new_coverage,new_lines_to_cover,tests,duplicated_lines_density,new_duplicated_lines_density,duplicated_blocks,ncloc,ncloc_language_distribution,projects,new_lines"
SonarAdditionalFields = "metrics,periods"
)
type SonarStatus struct {
Measures *sonargo.MeasuresComponentObject `json:"measures,omitempty"`
Issues *sonargo.IssuesSearchObject `json:"issues,omitempty"`
JenkinsAction *gojenkins.GeneralObj `json:"jenkinsAction,omitempty"`
Task *sonargo.CeTaskObject `json:"task,omitempty"`
}
type ProjectPipeline struct {
Type string `json:"type" description:"type of devops pipeline, in scm or no scm"`
Pipeline *NoScmPipeline `json:"pipeline,omitempty" description:"no scm pipeline structs"`
MultiBranchPipeline *MultiBranchPipeline `json:"multi_branch_pipeline,omitempty" description:"in scm pipeline structs"`
}
type NoScmPipeline struct {
Name string `json:"name" description:"name of pipeline"`
Description string `json:"descriptio,omitempty" description:"description of pipeline"`
Discarder *DiscarderProperty `json:"discarder,omitempty" description:"Discarder of pipeline, managing when to drop a pipeline"`
Parameters *Parameters `json:"parameters,omitempty" description:"Parameters define of pipeline,user could pass param when run pipeline"`
DisableConcurrent bool `json:"disable_concurrent,omitempty" mapstructure:"disable_concurrent" description:"Whether to prohibit the pipeline from running in parallel"`
TimerTrigger *TimerTrigger `json:"timer_trigger,omitempty" mapstructure:"timer_trigger" description:"Timer to trigger pipeline run"`
RemoteTrigger *RemoteTrigger `json:"remote_trigger,omitempty" mapstructure:"remote_trigger" description:"Remote api define to trigger pipeline run"`
Jenkinsfile string `json:"jenkinsfile,omitempty" description:"Jenkinsfile's content'"`
}
type MultiBranchPipeline struct {
Name string `json:"name" description:"name of pipeline"`
Description string `json:"descriptio,omitempty" description:"description of pipeline"`
Discarder *DiscarderProperty `json:"discarder,omitempty" description:"Discarder of pipeline, managing when to drop a pipeline"`
TimerTrigger *TimerTrigger `json:"timer_trigger,omitempty" mapstructure:"timer_trigger" description:"Timer to trigger pipeline run"`
SourceType string `json:"source_type" description:"type of scm, such as github/git/svn"`
GitSource *GitSource `json:"git_source,omitempty" description:"git scm define"`
GitHubSource *GithubSource `json:"github_source,omitempty" description:"github scm define"`
SvnSource *SvnSource `json:"svn_source,omitempty" description:"multi branch svn scm define"`
SingleSvnSource *SingleSvnSource `json:"single_svn_source,omitempty" description:"single branch svn scm define"`
BitbucketServerSource *BitbucketServerSource `json:"bitbucket_server_source,omitempty" description:"bitbucket server scm defile"`
ScriptPath string `json:"script_path" mapstructure:"script_path" description:"script path in scm"`
MultiBranchJobTrigger *MultiBranchJobTrigger `json:"multibranch_job_trigger,omitempty" mapstructure:"multibranch_job_trigger" description:"Pipeline tasks that need to be triggered when branch creation/deletion"`
}
type GitSource struct {
ScmId string `json:"scm_id,omitempty" description:"uid of scm"`
Url string `json:"url,omitempty" mapstructure:"url" description:"url of git source"`
CredentialId string `json:"credential_id,omitempty" mapstructure:"credential_id" description:"credential id to access git source"`
DiscoverBranches bool `json:"discover_branches,omitempty" mapstructure:"discover_branches" description:"Whether to discover a branch"`
CloneOption *GitCloneOption `json:"git_clone_option,omitempty" mapstructure:"git_clone_option" description:"advavced git clone options"`
RegexFilter string `json:"regex_filter,omitempty" mapstructure:"regex_filter" description:"Regex used to match the name of the branch that needs to be run"`
}
type GithubSource struct {
ScmId string `json:"scm_id,omitempty" description:"uid of scm"`
Owner string `json:"owner,omitempty" mapstructure:"owner" description:"owner of github repo"`
Repo string `json:"repo,omitempty" mapstructure:"repo" description:"repo name of github repo"`
CredentialId string `json:"credential_id,omitempty" mapstructure:"credential_id" description:"credential id to access github source"`
ApiUri string `json:"api_uri,omitempty" mapstructure:"api_uri" description:"The api url can specify the location of the github apiserver.For private cloud configuration"`
DiscoverBranches int `json:"discover_branches,omitempty" mapstructure:"discover_branches" description:"Discover branch configuration"`
DiscoverPRFromOrigin int `json:"discover_pr_from_origin,omitempty" mapstructure:"discover_pr_from_origin" description:"Discover origin PR configuration"`
DiscoverPRFromForks *DiscoverPRFromForks `json:"discover_pr_from_forks,omitempty" mapstructure:"discover_pr_from_forks" description:"Discover fork PR configuration"`
CloneOption *GitCloneOption `json:"git_clone_option,omitempty" mapstructure:"git_clone_option" description:"advavced git clone options"`
RegexFilter string `json:"regex_filter,omitempty" mapstructure:"regex_filter" description:"Regex used to match the name of the branch that needs to be run"`
}
type MultiBranchJobTrigger struct {
CreateActionJobsToTrigger string `json:"create_action_job_to_trigger,omitempty" description:"pipeline name to trigger"`
DeleteActionJobsToTrigger string `json:"delete_action_job_to_trigger,omitempty" description:"pipeline name to trigger"`
}
type BitbucketServerSource struct {
ScmId string `json:"scm_id,omitempty" description:"uid of scm"`
Owner string `json:"owner,omitempty" mapstructure:"owner" description:"owner of github repo"`
Repo string `json:"repo,omitempty" mapstructure:"repo" description:"repo name of github repo"`
CredentialId string `json:"credential_id,omitempty" mapstructure:"credential_id" description:"credential id to access github source"`
ApiUri string `json:"api_uri,omitempty" mapstructure:"api_uri" description:"The api url can specify the location of the github apiserver.For private cloud configuration"`
DiscoverBranches int `json:"discover_branches,omitempty" mapstructure:"discover_branches" description:"Discover branch configuration"`
DiscoverPRFromOrigin int `json:"discover_pr_from_origin,omitempty" mapstructure:"discover_pr_from_origin" description:"Discover origin PR configuration"`
DiscoverPRFromForks *DiscoverPRFromForks `json:"discover_pr_from_forks,omitempty" mapstructure:"discover_pr_from_forks" description:"Discover fork PR configuration"`
CloneOption *GitCloneOption `json:"git_clone_option,omitempty" mapstructure:"git_clone_option" description:"advavced git clone options"`
RegexFilter string `json:"regex_filter,omitempty" mapstructure:"regex_filter" description:"Regex used to match the name of the branch that needs to be run"`
}
type GitCloneOption struct {
Shallow bool `json:"shallow,omitempty" mapstructure:"shallow" description:"Whether to use git shallow clone"`
Timeout int `json:"timeout,omitempty" mapstructure:"timeout" description:"git clone timeout mins"`
Depth int `json:"depth,omitempty" mapstructure:"depth" description:"git clone depth"`
}
type SvnSource struct {
ScmId string `json:"scm_id,omitempty" description:"uid of scm"`
Remote string `json:"remote,omitempty" description:"remote address url"`
CredentialId string `json:"credential_id,omitempty" mapstructure:"credential_id" description:"credential id to access svn source"`
Includes string `json:"includes,omitempty" description:"branches to run pipeline"`
Excludes string `json:"excludes,omitempty" description:"branches do not run pipeline"`
}
type SingleSvnSource struct {
ScmId string `json:"scm_id,omitempty" description:"uid of scm"`
Remote string `json:"remote,omitempty" description:"remote address url"`
CredentialId string `json:"credential_id,omitempty" mapstructure:"credential_id" description:"credential id to access svn source"`
}
type DiscoverPRFromForks struct {
Strategy int `json:"strategy,omitempty" mapstructure:"strategy" description:"github discover strategy"`
Trust int `json:"trust,omitempty" mapstructure:"trust" description:"trust user type"`
}
type DiscarderProperty struct {
DaysToKeep string `json:"days_to_keep,omitempty" mapstructure:"days_to_keep" description:"days to keep pipeline"`
NumToKeep string `json:"num_to_keep,omitempty" mapstructure:"num_to_keep" description:"nums to keep pipeline"`
}
type Parameter struct {
Name string `json:"name" description:"name of param"`
DefaultValue string `json:"default_value,omitempty" mapstructure:"default_value" description:"default value of param"`
Type string `json:"type" description:"type of param"`
Description string `json:"description,omitempty" description:"description of pipeline"`
}
type TimerTrigger struct {
// user in no scm job
Cron string `json:"cron,omitempty" description:"jenkins cron script"`
// use in multi-branch job
Interval string `json:"interval,omitempty" description:"interval ms"`
}
type RemoteTrigger struct {
Token string `json:"token,omitempty" description:"remote trigger token"`
}
func replaceXmlVersion(config, oldVersion, targetVersion string) string { func replaceXmlVersion(config, oldVersion, targetVersion string) string {
lines := strings.Split(config, "\n") lines := strings.Split(config, "\n")
lines[0] = strings.Replace(lines[0], oldVersion, targetVersion, -1) lines[0] = strings.Replace(lines[0], oldVersion, targetVersion, -1)
@@ -181,7 +16,7 @@ func replaceXmlVersion(config, oldVersion, targetVersion string) string {
return output return output
} }
func createPipelineConfigXml(pipeline *NoScmPipeline) (string, error) { func createPipelineConfigXml(pipeline *devops.NoScmPipeline) (string, error) {
doc := etree.NewDocument() doc := etree.NewDocument()
xmlString := `<?xml version='1.0' encoding='UTF-8'?> xmlString := `<?xml version='1.0' encoding='UTF-8'?>
<flow-definition plugin="workflow-job"> <flow-definition plugin="workflow-job">
@@ -215,7 +50,7 @@ func createPipelineConfigXml(pipeline *NoScmPipeline) (string, error) {
strategy.CreateElement("artifactNumToKeep").SetText("-1") strategy.CreateElement("artifactNumToKeep").SetText("-1")
} }
if pipeline.Parameters != nil { if pipeline.Parameters != nil {
pipeline.Parameters.appendToEtree(properties) appendParametersToEtree(properties, pipeline.Parameters)
} }
if pipeline.TimerTrigger != nil { if pipeline.TimerTrigger != nil {
@@ -247,8 +82,8 @@ func createPipelineConfigXml(pipeline *NoScmPipeline) (string, error) {
return replaceXmlVersion(stringXml, "1.0", "1.1"), err return replaceXmlVersion(stringXml, "1.0", "1.1"), err
} }
func parsePipelineConfigXml(config string) (*NoScmPipeline, error) { func parsePipelineConfigXml(config string) (*devops.NoScmPipeline, error) {
pipeline := &NoScmPipeline{} pipeline := &devops.NoScmPipeline{}
config = replaceXmlVersion(config, "1.1", "1.0") config = replaceXmlVersion(config, "1.1", "1.0")
doc := etree.NewDocument() doc := etree.NewDocument()
err := doc.ReadFromString(config) err := doc.ReadFromString(config)
@@ -271,13 +106,13 @@ func parsePipelineConfigXml(config string) (*NoScmPipeline, error) {
strategy := properties. strategy := properties.
SelectElement("jenkins.model.BuildDiscarderProperty"). SelectElement("jenkins.model.BuildDiscarderProperty").
SelectElement("strategy") SelectElement("strategy")
pipeline.Discarder = &DiscarderProperty{ pipeline.Discarder = &devops.DiscarderProperty{
DaysToKeep: strategy.SelectElement("daysToKeep").Text(), DaysToKeep: strategy.SelectElement("daysToKeep").Text(),
NumToKeep: strategy.SelectElement("numToKeep").Text(), NumToKeep: strategy.SelectElement("numToKeep").Text(),
} }
} }
pipeline.Parameters = &Parameters{} pipeline.Parameters = &devops.Parameters{}
pipeline.Parameters = pipeline.Parameters.fromEtree(properties) pipeline.Parameters = getParametersfromEtree(properties)
if len(*pipeline.Parameters) == 0 { if len(*pipeline.Parameters) == 0 {
pipeline.Parameters = nil pipeline.Parameters = nil
} }
@@ -287,13 +122,13 @@ func parsePipelineConfigXml(config string) (*NoScmPipeline, error) {
"org.jenkinsci.plugins.workflow.job.properties.PipelineTriggersJobProperty"); triggerProperty != nil { "org.jenkinsci.plugins.workflow.job.properties.PipelineTriggersJobProperty"); triggerProperty != nil {
triggers := triggerProperty.SelectElement("triggers") triggers := triggerProperty.SelectElement("triggers")
if timerTrigger := triggers.SelectElement("hudson.triggers.TimerTrigger"); timerTrigger != nil { if timerTrigger := triggers.SelectElement("hudson.triggers.TimerTrigger"); timerTrigger != nil {
pipeline.TimerTrigger = &TimerTrigger{ pipeline.TimerTrigger = &devops.TimerTrigger{
Cron: timerTrigger.SelectElement("spec").Text(), Cron: timerTrigger.SelectElement("spec").Text(),
} }
} }
} }
if authToken := flow.SelectElement("authToken"); authToken != nil { if authToken := flow.SelectElement("authToken"); authToken != nil {
pipeline.RemoteTrigger = &RemoteTrigger{ pipeline.RemoteTrigger = &devops.RemoteTrigger{
Token: authToken.Text(), Token: authToken.Text(),
} }
} }
@@ -305,11 +140,11 @@ func parsePipelineConfigXml(config string) (*NoScmPipeline, error) {
return pipeline, nil return pipeline, nil
} }
func (s *Parameters) appendToEtree(properties *etree.Element) *Parameters { func appendParametersToEtree(properties *etree.Element, parameters *devops.Parameters) {
parameterDefinitions := properties.CreateElement("hudson.model.ParametersDefinitionProperty"). parameterDefinitions := properties.CreateElement("hudson.model.ParametersDefinitionProperty").
CreateElement("parameterDefinitions") CreateElement("parameterDefinitions")
for _, parameter := range *s { for _, parameter := range *parameters {
for className, typeName := range ParameterTypeMap { for className, typeName := range devops.ParameterTypeMap {
if typeName == parameter.Type { if typeName == parameter.Type {
paramDefine := parameterDefinitions.CreateElement(className) paramDefine := parameterDefinitions.CreateElement(className)
paramDefine.CreateElement("name").SetText(parameter.Name) paramDefine.CreateElement("name").SetText(parameter.Name)
@@ -332,66 +167,62 @@ func (s *Parameters) appendToEtree(properties *etree.Element) *Parameters {
} }
} }
} }
return s
} }
func (s *Parameters) fromEtree(properties *etree.Element) *Parameters { func getParametersfromEtree(properties *etree.Element) *devops.Parameters {
var parameters devops.Parameters
if parametersProperty := properties.SelectElement("hudson.model.ParametersDefinitionProperty"); parametersProperty != nil { if parametersProperty := properties.SelectElement("hudson.model.ParametersDefinitionProperty"); parametersProperty != nil {
params := parametersProperty.SelectElement("parameterDefinitions").ChildElements() params := parametersProperty.SelectElement("parameterDefinitions").ChildElements()
if *s == nil {
*s = make([]*Parameter, 0)
}
for _, param := range params { for _, param := range params {
switch param.Tag { switch param.Tag {
case "hudson.model.StringParameterDefinition": case "hudson.model.StringParameterDefinition":
*s = append(*s, &Parameter{ parameters = append(parameters, &devops.Parameter{
Name: param.SelectElement("name").Text(), Name: param.SelectElement("name").Text(),
Description: param.SelectElement("description").Text(), Description: param.SelectElement("description").Text(),
DefaultValue: param.SelectElement("defaultValue").Text(), DefaultValue: param.SelectElement("defaultValue").Text(),
Type: ParameterTypeMap["hudson.model.StringParameterDefinition"], Type: devops.ParameterTypeMap["hudson.model.StringParameterDefinition"],
}) })
case "hudson.model.BooleanParameterDefinition": case "hudson.model.BooleanParameterDefinition":
*s = append(*s, &Parameter{ parameters = append(parameters, &devops.Parameter{
Name: param.SelectElement("name").Text(), Name: param.SelectElement("name").Text(),
Description: param.SelectElement("description").Text(), Description: param.SelectElement("description").Text(),
DefaultValue: param.SelectElement("defaultValue").Text(), DefaultValue: param.SelectElement("defaultValue").Text(),
Type: ParameterTypeMap["hudson.model.BooleanParameterDefinition"], Type: devops.ParameterTypeMap["hudson.model.BooleanParameterDefinition"],
}) })
case "hudson.model.TextParameterDefinition": case "hudson.model.TextParameterDefinition":
*s = append(*s, &Parameter{ parameters = append(parameters, &devops.Parameter{
Name: param.SelectElement("name").Text(), Name: param.SelectElement("name").Text(),
Description: param.SelectElement("description").Text(), Description: param.SelectElement("description").Text(),
DefaultValue: param.SelectElement("defaultValue").Text(), DefaultValue: param.SelectElement("defaultValue").Text(),
Type: ParameterTypeMap["hudson.model.TextParameterDefinition"], Type: devops.ParameterTypeMap["hudson.model.TextParameterDefinition"],
}) })
case "hudson.model.FileParameterDefinition": case "hudson.model.FileParameterDefinition":
*s = append(*s, &Parameter{ parameters = append(parameters, &devops.Parameter{
Name: param.SelectElement("name").Text(), Name: param.SelectElement("name").Text(),
Description: param.SelectElement("description").Text(), Description: param.SelectElement("description").Text(),
Type: ParameterTypeMap["hudson.model.FileParameterDefinition"], Type: devops.ParameterTypeMap["hudson.model.FileParameterDefinition"],
}) })
case "hudson.model.PasswordParameterDefinition": case "hudson.model.PasswordParameterDefinition":
*s = append(*s, &Parameter{ parameters = append(parameters, &devops.Parameter{
Name: param.SelectElement("name").Text(), Name: param.SelectElement("name").Text(),
Description: param.SelectElement("description").Text(), Description: param.SelectElement("description").Text(),
DefaultValue: param.SelectElement("name").Text(), DefaultValue: param.SelectElement("name").Text(),
Type: ParameterTypeMap["hudson.model.PasswordParameterDefinition"], Type: devops.ParameterTypeMap["hudson.model.PasswordParameterDefinition"],
}) })
case "hudson.model.ChoiceParameterDefinition": case "hudson.model.ChoiceParameterDefinition":
choiceParameter := &Parameter{ choiceParameter := &devops.Parameter{
Name: param.SelectElement("name").Text(), Name: param.SelectElement("name").Text(),
Description: param.SelectElement("description").Text(), Description: param.SelectElement("description").Text(),
Type: ParameterTypeMap["hudson.model.ChoiceParameterDefinition"], Type: devops.ParameterTypeMap["hudson.model.ChoiceParameterDefinition"],
} }
choices := param.SelectElement("choices").SelectElement("a").SelectElements("string") choices := param.SelectElement("choices").SelectElement("a").SelectElements("string")
for _, choice := range choices { for _, choice := range choices {
choiceParameter.DefaultValue += fmt.Sprintf("%s\n", choice.Text()) choiceParameter.DefaultValue += fmt.Sprintf("%s\n", choice.Text())
} }
choiceParameter.DefaultValue = strings.TrimSpace(choiceParameter.DefaultValue) choiceParameter.DefaultValue = strings.TrimSpace(choiceParameter.DefaultValue)
*s = append(*s, choiceParameter) parameters = append(parameters, choiceParameter)
default: default:
*s = append(*s, &Parameter{ parameters = append(parameters, &devops.Parameter{
Name: param.SelectElement("name").Text(), Name: param.SelectElement("name").Text(),
Description: param.SelectElement("description").Text(), Description: param.SelectElement("description").Text(),
DefaultValue: "unknown", DefaultValue: "unknown",
@@ -400,110 +231,112 @@ func (s *Parameters) fromEtree(properties *etree.Element) *Parameters {
} }
} }
} }
return s return &parameters
} }
func (s *GitSource) appendToEtree(source *etree.Element) *GitSource { func appendGitSourceToEtree(source *etree.Element, gitSource *devops.GitSource) {
source.CreateAttr("class", "jenkins.plugins.git.GitSCMSource") source.CreateAttr("class", "jenkins.plugins.git.GitSCMSource")
source.CreateAttr("plugin", "git") source.CreateAttr("plugin", "git")
source.CreateElement("id").SetText(s.ScmId) source.CreateElement("id").SetText(gitSource.ScmId)
source.CreateElement("remote").SetText(s.Url) source.CreateElement("remote").SetText(gitSource.Url)
if s.CredentialId != "" { if gitSource.CredentialId != "" {
source.CreateElement("credentialsId").SetText(s.CredentialId) source.CreateElement("credentialsId").SetText(gitSource.CredentialId)
} }
traits := source.CreateElement("traits") traits := source.CreateElement("traits")
if s.DiscoverBranches { if gitSource.DiscoverBranches {
traits.CreateElement("jenkins.plugins.git.traits.BranchDiscoveryTrait") traits.CreateElement("jenkins.plugins.git.traits.BranchDiscoveryTrait")
} }
if s.CloneOption != nil { if gitSource.CloneOption != nil {
cloneExtension := traits.CreateElement("jenkins.plugins.git.traits.CloneOptionTrait").CreateElement("extension") cloneExtension := traits.CreateElement("jenkins.plugins.git.traits.CloneOptionTrait").CreateElement("extension")
cloneExtension.CreateAttr("class", "hudson.plugins.git.extensions.impl.CloneOption") cloneExtension.CreateAttr("class", "hudson.plugins.git.extensions.impl.CloneOption")
cloneExtension.CreateElement("shallow").SetText(strconv.FormatBool(s.CloneOption.Shallow)) cloneExtension.CreateElement("shallow").SetText(strconv.FormatBool(gitSource.CloneOption.Shallow))
cloneExtension.CreateElement("noTags").SetText(strconv.FormatBool(false)) cloneExtension.CreateElement("noTags").SetText(strconv.FormatBool(false))
cloneExtension.CreateElement("honorRefspec").SetText(strconv.FormatBool(true)) cloneExtension.CreateElement("honorRefspec").SetText(strconv.FormatBool(true))
cloneExtension.CreateElement("reference") cloneExtension.CreateElement("reference")
if s.CloneOption.Timeout >= 0 { if gitSource.CloneOption.Timeout >= 0 {
cloneExtension.CreateElement("timeout").SetText(strconv.Itoa(s.CloneOption.Timeout)) cloneExtension.CreateElement("timeout").SetText(strconv.Itoa(gitSource.CloneOption.Timeout))
} else { } else {
cloneExtension.CreateElement("timeout").SetText(strconv.Itoa(10)) cloneExtension.CreateElement("timeout").SetText(strconv.Itoa(10))
} }
if s.CloneOption.Depth >= 0 { if gitSource.CloneOption.Depth >= 0 {
cloneExtension.CreateElement("depth").SetText(strconv.Itoa(s.CloneOption.Depth)) cloneExtension.CreateElement("depth").SetText(strconv.Itoa(gitSource.CloneOption.Depth))
} else { } else {
cloneExtension.CreateElement("depth").SetText(strconv.Itoa(1)) cloneExtension.CreateElement("depth").SetText(strconv.Itoa(1))
} }
} }
if s.RegexFilter != "" { if gitSource.RegexFilter != "" {
regexTraits := traits.CreateElement("jenkins.scm.impl.trait.RegexSCMHeadFilterTrait") regexTraits := traits.CreateElement("jenkins.scm.impl.trait.RegexSCMHeadFilterTrait")
regexTraits.CreateAttr("plugin", "scm-api@2.4.0") regexTraits.CreateAttr("plugin", "scm-api@2.4.0")
regexTraits.CreateElement("regex").SetText(s.RegexFilter) regexTraits.CreateElement("regex").SetText(gitSource.RegexFilter)
} }
return s return
} }
func (s *GitSource) fromEtree(source *etree.Element) *GitSource { func getGitSourcefromEtree(source *etree.Element) *devops.GitSource {
var gitSource devops.GitSource
if credential := source.SelectElement("credentialsId"); credential != nil { if credential := source.SelectElement("credentialsId"); credential != nil {
s.CredentialId = credential.Text() gitSource.CredentialId = credential.Text()
} }
if remote := source.SelectElement("remote"); remote != nil { if remote := source.SelectElement("remote"); remote != nil {
s.Url = remote.Text() gitSource.Url = remote.Text()
} }
traits := source.SelectElement("traits") traits := source.SelectElement("traits")
if branchDiscoverTrait := traits.SelectElement( if branchDiscoverTrait := traits.SelectElement(
"jenkins.plugins.git.traits.BranchDiscoveryTrait"); branchDiscoverTrait != nil { "jenkins.plugins.git.traits.BranchDiscoveryTrait"); branchDiscoverTrait != nil {
s.DiscoverBranches = true gitSource.DiscoverBranches = true
} }
if cloneTrait := traits.SelectElement( if cloneTrait := traits.SelectElement(
"jenkins.plugins.git.traits.CloneOptionTrait"); cloneTrait != nil { "jenkins.plugins.git.traits.CloneOptionTrait"); cloneTrait != nil {
if cloneExtension := cloneTrait.SelectElement( if cloneExtension := cloneTrait.SelectElement(
"extension"); cloneExtension != nil { "extension"); cloneExtension != nil {
s.CloneOption = &GitCloneOption{} gitSource.CloneOption = &devops.GitCloneOption{}
if value, err := strconv.ParseBool(cloneExtension.SelectElement("shallow").Text()); err == nil { if value, err := strconv.ParseBool(cloneExtension.SelectElement("shallow").Text()); err == nil {
s.CloneOption.Shallow = value gitSource.CloneOption.Shallow = value
} }
if value, err := strconv.ParseInt(cloneExtension.SelectElement("timeout").Text(), 10, 32); err == nil { if value, err := strconv.ParseInt(cloneExtension.SelectElement("timeout").Text(), 10, 32); err == nil {
s.CloneOption.Timeout = int(value) gitSource.CloneOption.Timeout = int(value)
} }
if value, err := strconv.ParseInt(cloneExtension.SelectElement("depth").Text(), 10, 32); err == nil { if value, err := strconv.ParseInt(cloneExtension.SelectElement("depth").Text(), 10, 32); err == nil {
s.CloneOption.Depth = int(value) gitSource.CloneOption.Depth = int(value)
} }
} }
} }
if regexTrait := traits.SelectElement( if regexTrait := traits.SelectElement(
"jenkins.scm.impl.trait.RegexSCMHeadFilterTrait"); regexTrait != nil { "jenkins.scm.impl.trait.RegexSCMHeadFilterTrait"); regexTrait != nil {
if regex := regexTrait.SelectElement("regex"); regex != nil { if regex := regexTrait.SelectElement("regex"); regex != nil {
s.RegexFilter = regex.Text() gitSource.RegexFilter = regex.Text()
} }
} }
return s return &gitSource
} }
func (s *GithubSource) fromEtree(source *etree.Element) *GithubSource { func getGithubSourcefromEtree(source *etree.Element) *devops.GithubSource {
var githubSource devops.GithubSource
if credential := source.SelectElement("credentialsId"); credential != nil { if credential := source.SelectElement("credentialsId"); credential != nil {
s.CredentialId = credential.Text() githubSource.CredentialId = credential.Text()
} }
if repoOwner := source.SelectElement("repoOwner"); repoOwner != nil { if repoOwner := source.SelectElement("repoOwner"); repoOwner != nil {
s.Owner = repoOwner.Text() githubSource.Owner = repoOwner.Text()
} }
if repository := source.SelectElement("repository"); repository != nil { if repository := source.SelectElement("repository"); repository != nil {
s.Repo = repository.Text() githubSource.Repo = repository.Text()
} }
if apiUri := source.SelectElement("apiUri"); apiUri != nil { if apiUri := source.SelectElement("apiUri"); apiUri != nil {
s.ApiUri = apiUri.Text() githubSource.ApiUri = apiUri.Text()
} }
traits := source.SelectElement("traits") traits := source.SelectElement("traits")
if branchDiscoverTrait := traits.SelectElement( if branchDiscoverTrait := traits.SelectElement(
"org.jenkinsci.plugins.github__branch__source.BranchDiscoveryTrait"); branchDiscoverTrait != nil { "org.jenkinsci.plugins.github__branch__source.BranchDiscoveryTrait"); branchDiscoverTrait != nil {
strategyId, _ := strconv.Atoi(branchDiscoverTrait.SelectElement("strategyId").Text()) strategyId, _ := strconv.Atoi(branchDiscoverTrait.SelectElement("strategyId").Text())
s.DiscoverBranches = strategyId githubSource.DiscoverBranches = strategyId
} }
if originPRDiscoverTrait := traits.SelectElement( if originPRDiscoverTrait := traits.SelectElement(
"org.jenkinsci.plugins.github__branch__source.OriginPullRequestDiscoveryTrait"); originPRDiscoverTrait != nil { "org.jenkinsci.plugins.github__branch__source.OriginPullRequestDiscoveryTrait"); originPRDiscoverTrait != nil {
strategyId, _ := strconv.Atoi(originPRDiscoverTrait.SelectElement("strategyId").Text()) strategyId, _ := strconv.Atoi(originPRDiscoverTrait.SelectElement("strategyId").Text())
s.DiscoverPRFromOrigin = strategyId githubSource.DiscoverPRFromOrigin = strategyId
} }
if forkPRDiscoverTrait := traits.SelectElement( if forkPRDiscoverTrait := traits.SelectElement(
"org.jenkinsci.plugins.github__branch__source.ForkPullRequestDiscoveryTrait"); forkPRDiscoverTrait != nil { "org.jenkinsci.plugins.github__branch__source.ForkPullRequestDiscoveryTrait"); forkPRDiscoverTrait != nil {
@@ -512,22 +345,22 @@ func (s *GithubSource) fromEtree(source *etree.Element) *GithubSource {
trust := strings.Split(trustClass, "$") trust := strings.Split(trustClass, "$")
switch trust[1] { switch trust[1] {
case "TrustContributors": case "TrustContributors":
s.DiscoverPRFromForks = &DiscoverPRFromForks{ githubSource.DiscoverPRFromForks = &devops.DiscoverPRFromForks{
Strategy: strategyId, Strategy: strategyId,
Trust: 1, Trust: 1,
} }
case "TrustEveryone": case "TrustEveryone":
s.DiscoverPRFromForks = &DiscoverPRFromForks{ githubSource.DiscoverPRFromForks = &devops.DiscoverPRFromForks{
Strategy: strategyId, Strategy: strategyId,
Trust: 2, Trust: 2,
} }
case "TrustPermission": case "TrustPermission":
s.DiscoverPRFromForks = &DiscoverPRFromForks{ githubSource.DiscoverPRFromForks = &devops.DiscoverPRFromForks{
Strategy: strategyId, Strategy: strategyId,
Trust: 3, Trust: 3,
} }
case "TrustNobody": case "TrustNobody":
s.DiscoverPRFromForks = &DiscoverPRFromForks{ githubSource.DiscoverPRFromForks = &devops.DiscoverPRFromForks{
Strategy: strategyId, Strategy: strategyId,
Trust: 4, Trust: 4,
} }
@@ -536,15 +369,15 @@ func (s *GithubSource) fromEtree(source *etree.Element) *GithubSource {
"jenkins.plugins.git.traits.CloneOptionTrait"); cloneTrait != nil { "jenkins.plugins.git.traits.CloneOptionTrait"); cloneTrait != nil {
if cloneExtension := cloneTrait.SelectElement( if cloneExtension := cloneTrait.SelectElement(
"extension"); cloneExtension != nil { "extension"); cloneExtension != nil {
s.CloneOption = &GitCloneOption{} githubSource.CloneOption = &devops.GitCloneOption{}
if value, err := strconv.ParseBool(cloneExtension.SelectElement("shallow").Text()); err == nil { if value, err := strconv.ParseBool(cloneExtension.SelectElement("shallow").Text()); err == nil {
s.CloneOption.Shallow = value githubSource.CloneOption.Shallow = value
} }
if value, err := strconv.ParseInt(cloneExtension.SelectElement("timeout").Text(), 10, 32); err == nil { if value, err := strconv.ParseInt(cloneExtension.SelectElement("timeout").Text(), 10, 32); err == nil {
s.CloneOption.Timeout = int(value) githubSource.CloneOption.Timeout = int(value)
} }
if value, err := strconv.ParseInt(cloneExtension.SelectElement("depth").Text(), 10, 32); err == nil { if value, err := strconv.ParseInt(cloneExtension.SelectElement("depth").Text(), 10, 32); err == nil {
s.CloneOption.Depth = int(value) githubSource.CloneOption.Depth = int(value)
} }
} }
} }
@@ -552,37 +385,37 @@ func (s *GithubSource) fromEtree(source *etree.Element) *GithubSource {
if regexTrait := traits.SelectElement( if regexTrait := traits.SelectElement(
"jenkins.scm.impl.trait.RegexSCMHeadFilterTrait"); regexTrait != nil { "jenkins.scm.impl.trait.RegexSCMHeadFilterTrait"); regexTrait != nil {
if regex := regexTrait.SelectElement("regex"); regex != nil { if regex := regexTrait.SelectElement("regex"); regex != nil {
s.RegexFilter = regex.Text() githubSource.RegexFilter = regex.Text()
} }
} }
} }
return s return &githubSource
} }
func (s *GithubSource) appendToEtree(source *etree.Element) *GithubSource { func appendGithubSourceToEtree(source *etree.Element, githubSource *devops.GithubSource) {
source.CreateAttr("class", "org.jenkinsci.plugins.github_branch_source.GitHubSCMSource") source.CreateAttr("class", "org.jenkinsci.plugins.github_branch_source.GitHubSCMSource")
source.CreateAttr("plugin", "github-branch-source") source.CreateAttr("plugin", "github-branch-source")
source.CreateElement("id").SetText(s.ScmId) source.CreateElement("id").SetText(githubSource.ScmId)
source.CreateElement("credentialsId").SetText(s.CredentialId) source.CreateElement("credentialsId").SetText(githubSource.CredentialId)
source.CreateElement("repoOwner").SetText(s.Owner) source.CreateElement("repoOwner").SetText(githubSource.Owner)
source.CreateElement("repository").SetText(s.Repo) source.CreateElement("repository").SetText(githubSource.Repo)
if s.ApiUri != "" { if githubSource.ApiUri != "" {
source.CreateElement("apiUri").SetText(s.ApiUri) source.CreateElement("apiUri").SetText(githubSource.ApiUri)
} }
traits := source.CreateElement("traits") traits := source.CreateElement("traits")
if s.DiscoverBranches != 0 { if githubSource.DiscoverBranches != 0 {
traits.CreateElement("org.jenkinsci.plugins.github__branch__source.BranchDiscoveryTrait"). traits.CreateElement("org.jenkinsci.plugins.github__branch__source.BranchDiscoveryTrait").
CreateElement("strategyId").SetText(strconv.Itoa(s.DiscoverBranches)) CreateElement("strategyId").SetText(strconv.Itoa(githubSource.DiscoverBranches))
} }
if s.DiscoverPRFromOrigin != 0 { if githubSource.DiscoverPRFromOrigin != 0 {
traits.CreateElement("org.jenkinsci.plugins.github__branch__source.OriginPullRequestDiscoveryTrait"). traits.CreateElement("org.jenkinsci.plugins.github__branch__source.OriginPullRequestDiscoveryTrait").
CreateElement("strategyId").SetText(strconv.Itoa(s.DiscoverPRFromOrigin)) CreateElement("strategyId").SetText(strconv.Itoa(githubSource.DiscoverPRFromOrigin))
} }
if s.DiscoverPRFromForks != nil { if githubSource.DiscoverPRFromForks != nil {
forkTrait := traits.CreateElement("org.jenkinsci.plugins.github__branch__source.ForkPullRequestDiscoveryTrait") forkTrait := traits.CreateElement("org.jenkinsci.plugins.github__branch__source.ForkPullRequestDiscoveryTrait")
forkTrait.CreateElement("strategyId").SetText(strconv.Itoa(s.DiscoverPRFromForks.Strategy)) forkTrait.CreateElement("strategyId").SetText(strconv.Itoa(githubSource.DiscoverPRFromForks.Strategy))
trustClass := "org.jenkinsci.plugins.github_branch_source.ForkPullRequestDiscoveryTrait$" trustClass := "org.jenkinsci.plugins.github_branch_source.ForkPullRequestDiscoveryTrait$"
switch s.DiscoverPRFromForks.Trust { switch githubSource.DiscoverPRFromForks.Trust {
case 1: case 1:
trustClass += "TrustContributors" trustClass += "TrustContributors"
case 2: case 2:
@@ -595,34 +428,35 @@ func (s *GithubSource) appendToEtree(source *etree.Element) *GithubSource {
} }
forkTrait.CreateElement("trust").CreateAttr("class", trustClass) forkTrait.CreateElement("trust").CreateAttr("class", trustClass)
} }
if s.CloneOption != nil { if githubSource.CloneOption != nil {
cloneExtension := traits.CreateElement("jenkins.plugins.git.traits.CloneOptionTrait").CreateElement("extension") cloneExtension := traits.CreateElement("jenkins.plugins.git.traits.CloneOptionTrait").CreateElement("extension")
cloneExtension.CreateAttr("class", "hudson.plugins.git.extensions.impl.CloneOption") cloneExtension.CreateAttr("class", "hudson.plugins.git.extensions.impl.CloneOption")
cloneExtension.CreateElement("shallow").SetText(strconv.FormatBool(s.CloneOption.Shallow)) cloneExtension.CreateElement("shallow").SetText(strconv.FormatBool(githubSource.CloneOption.Shallow))
cloneExtension.CreateElement("noTags").SetText(strconv.FormatBool(false)) cloneExtension.CreateElement("noTags").SetText(strconv.FormatBool(false))
cloneExtension.CreateElement("honorRefspec").SetText(strconv.FormatBool(true)) cloneExtension.CreateElement("honorRefspec").SetText(strconv.FormatBool(true))
cloneExtension.CreateElement("reference") cloneExtension.CreateElement("reference")
if s.CloneOption.Timeout >= 0 { if githubSource.CloneOption.Timeout >= 0 {
cloneExtension.CreateElement("timeout").SetText(strconv.Itoa(s.CloneOption.Timeout)) cloneExtension.CreateElement("timeout").SetText(strconv.Itoa(githubSource.CloneOption.Timeout))
} else { } else {
cloneExtension.CreateElement("timeout").SetText(strconv.Itoa(10)) cloneExtension.CreateElement("timeout").SetText(strconv.Itoa(10))
} }
if s.CloneOption.Depth >= 0 { if githubSource.CloneOption.Depth >= 0 {
cloneExtension.CreateElement("depth").SetText(strconv.Itoa(s.CloneOption.Depth)) cloneExtension.CreateElement("depth").SetText(strconv.Itoa(githubSource.CloneOption.Depth))
} else { } else {
cloneExtension.CreateElement("depth").SetText(strconv.Itoa(1)) cloneExtension.CreateElement("depth").SetText(strconv.Itoa(1))
} }
} }
if s.RegexFilter != "" { if githubSource.RegexFilter != "" {
regexTraits := traits.CreateElement("jenkins.scm.impl.trait.RegexSCMHeadFilterTrait") regexTraits := traits.CreateElement("jenkins.scm.impl.trait.RegexSCMHeadFilterTrait")
regexTraits.CreateAttr("plugin", "scm-api@2.4.0") regexTraits.CreateAttr("plugin", "scm-api@2.4.0")
regexTraits.CreateElement("regex").SetText(s.RegexFilter) regexTraits.CreateElement("regex").SetText(githubSource.RegexFilter)
} }
return s return
} }
func (s *BitbucketServerSource) fromEtree(source *etree.Element) *BitbucketServerSource { func getBitbucketServerSourceFromEtree(source *etree.Element) *devops.BitbucketServerSource {
var s devops.BitbucketServerSource
if credential := source.SelectElement("credentialsId"); credential != nil { if credential := source.SelectElement("credentialsId"); credential != nil {
s.CredentialId = credential.Text() s.CredentialId = credential.Text()
} }
@@ -653,17 +487,17 @@ func (s *BitbucketServerSource) fromEtree(source *etree.Element) *BitbucketServe
trust := strings.Split(trustClass, "$") trust := strings.Split(trustClass, "$")
switch trust[1] { switch trust[1] {
case "TrustEveryone": case "TrustEveryone":
s.DiscoverPRFromForks = &DiscoverPRFromForks{ s.DiscoverPRFromForks = &devops.DiscoverPRFromForks{
Strategy: strategyId, Strategy: strategyId,
Trust: 1, Trust: 1,
} }
case "TrustTeamForks": case "TrustTeamForks":
s.DiscoverPRFromForks = &DiscoverPRFromForks{ s.DiscoverPRFromForks = &devops.DiscoverPRFromForks{
Strategy: strategyId, Strategy: strategyId,
Trust: 2, Trust: 2,
} }
case "TrustNobody": case "TrustNobody":
s.DiscoverPRFromForks = &DiscoverPRFromForks{ s.DiscoverPRFromForks = &devops.DiscoverPRFromForks{
Strategy: strategyId, Strategy: strategyId,
Trust: 3, Trust: 3,
} }
@@ -672,7 +506,7 @@ func (s *BitbucketServerSource) fromEtree(source *etree.Element) *BitbucketServe
"jenkins.plugins.git.traits.CloneOptionTrait"); cloneTrait != nil { "jenkins.plugins.git.traits.CloneOptionTrait"); cloneTrait != nil {
if cloneExtension := cloneTrait.SelectElement( if cloneExtension := cloneTrait.SelectElement(
"extension"); cloneExtension != nil { "extension"); cloneExtension != nil {
s.CloneOption = &GitCloneOption{} s.CloneOption = &devops.GitCloneOption{}
if value, err := strconv.ParseBool(cloneExtension.SelectElement("shallow").Text()); err == nil { if value, err := strconv.ParseBool(cloneExtension.SelectElement("shallow").Text()); err == nil {
s.CloneOption.Shallow = value s.CloneOption.Shallow = value
} }
@@ -692,10 +526,10 @@ func (s *BitbucketServerSource) fromEtree(source *etree.Element) *BitbucketServe
} }
} }
} }
return s return &s
} }
func (s *BitbucketServerSource) appendToEtree(source *etree.Element) *BitbucketServerSource { func appendBitbucketServerSourceToEtree(source *etree.Element, s *devops.BitbucketServerSource) {
source.CreateAttr("class", "com.cloudbees.jenkins.plugins.bitbucket.BitbucketSCMSource") source.CreateAttr("class", "com.cloudbees.jenkins.plugins.bitbucket.BitbucketSCMSource")
source.CreateAttr("plugin", "cloudbees-bitbucket-branch-source") source.CreateAttr("plugin", "cloudbees-bitbucket-branch-source")
source.CreateElement("id").SetText(s.ScmId) source.CreateElement("id").SetText(s.ScmId)
@@ -753,10 +587,11 @@ func (s *BitbucketServerSource) appendToEtree(source *etree.Element) *BitbucketS
regexTraits.CreateAttr("plugin", "scm-api@2.4.0") regexTraits.CreateAttr("plugin", "scm-api@2.4.0")
regexTraits.CreateElement("regex").SetText(s.RegexFilter) regexTraits.CreateElement("regex").SetText(s.RegexFilter)
} }
return s return
} }
func (s *SvnSource) fromEtree(source *etree.Element) *SvnSource { func getSvnSourcefromEtree(source *etree.Element) *devops.SvnSource {
var s devops.SvnSource
if remote := source.SelectElement("remoteBase"); remote != nil { if remote := source.SelectElement("remoteBase"); remote != nil {
s.Remote = remote.Text() s.Remote = remote.Text()
} }
@@ -772,10 +607,10 @@ func (s *SvnSource) fromEtree(source *etree.Element) *SvnSource {
if excludes := source.SelectElement("excludes"); excludes != nil { if excludes := source.SelectElement("excludes"); excludes != nil {
s.Excludes = excludes.Text() s.Excludes = excludes.Text()
} }
return s return &s
} }
func (s *SvnSource) appendToEtree(source *etree.Element) *SvnSource { func appendSvnSourceToEtree(source *etree.Element, s *devops.SvnSource) {
source.CreateAttr("class", "jenkins.scm.impl.subversion.SubversionSCMSource") source.CreateAttr("class", "jenkins.scm.impl.subversion.SubversionSCMSource")
source.CreateAttr("plugin", "subversion") source.CreateAttr("plugin", "subversion")
source.CreateElement("id").SetText(s.ScmId) source.CreateElement("id").SetText(s.ScmId)
@@ -791,10 +626,11 @@ func (s *SvnSource) appendToEtree(source *etree.Element) *SvnSource {
if s.Excludes != "" { if s.Excludes != "" {
source.CreateElement("excludes").SetText(s.Excludes) source.CreateElement("excludes").SetText(s.Excludes)
} }
return nil return
} }
func (s *SingleSvnSource) fromEtree(source *etree.Element) *SingleSvnSource { func getSingleSvnSourceFromEtree(source *etree.Element) *devops.SingleSvnSource {
var s devops.SingleSvnSource
if scm := source.SelectElement("scm"); scm != nil { if scm := source.SelectElement("scm"); scm != nil {
if locations := scm.SelectElement("locations"); locations != nil { if locations := scm.SelectElement("locations"); locations != nil {
if moduleLocations := locations.SelectElement("hudson.scm.SubversionSCM_-ModuleLocation"); moduleLocations != nil { if moduleLocations := locations.SelectElement("hudson.scm.SubversionSCM_-ModuleLocation"); moduleLocations != nil {
@@ -807,10 +643,10 @@ func (s *SingleSvnSource) fromEtree(source *etree.Element) *SingleSvnSource {
} }
} }
} }
return s return &s
} }
func (s *SingleSvnSource) appendToEtree(source *etree.Element) *SingleSvnSource { func appendSingleSvnSourceToEtree(source *etree.Element, s *devops.SingleSvnSource) {
source.CreateAttr("class", "jenkins.scm.impl.SingleSCMSource") source.CreateAttr("class", "jenkins.scm.impl.SingleSCMSource")
source.CreateAttr("plugin", "scm-api") source.CreateAttr("plugin", "scm-api")
@@ -843,26 +679,27 @@ func (s *SingleSvnSource) appendToEtree(source *etree.Element) *SingleSvnSource
source.CreateElement("filterChangelog").SetText("false") source.CreateElement("filterChangelog").SetText("false")
source.CreateElement("quietOperation").SetText("true") source.CreateElement("quietOperation").SetText("true")
return s return
} }
func (s *MultiBranchJobTrigger) appendToEtree(properties *etree.Element) *MultiBranchJobTrigger { func appendMultiBranchJobTriggerToEtree(properties *etree.Element, s *devops.MultiBranchJobTrigger) {
triggerProperty := properties.CreateElement("org.jenkinsci.plugins.workflow.multibranch.PipelineTriggerProperty") triggerProperty := properties.CreateElement("org.jenkinsci.plugins.workflow.multibranch.PipelineTriggerProperty")
triggerProperty.CreateAttr("plugin", "multibranch-action-triggers") triggerProperty.CreateAttr("plugin", "multibranch-action-triggers")
triggerProperty.CreateElement("createActionJobsToTrigger").SetText(s.CreateActionJobsToTrigger) triggerProperty.CreateElement("createActionJobsToTrigger").SetText(s.CreateActionJobsToTrigger)
triggerProperty.CreateElement("deleteActionJobsToTrigger").SetText(s.DeleteActionJobsToTrigger) triggerProperty.CreateElement("deleteActionJobsToTrigger").SetText(s.DeleteActionJobsToTrigger)
return s return
} }
func (s *MultiBranchJobTrigger) fromEtree(properties *etree.Element) *MultiBranchJobTrigger { func getMultiBranchJobTriggerfromEtree(properties *etree.Element) *devops.MultiBranchJobTrigger {
var s devops.MultiBranchJobTrigger
triggerProperty := properties.SelectElement("org.jenkinsci.plugins.workflow.multibranch.PipelineTriggerProperty") triggerProperty := properties.SelectElement("org.jenkinsci.plugins.workflow.multibranch.PipelineTriggerProperty")
if triggerProperty != nil { if triggerProperty != nil {
s.CreateActionJobsToTrigger = triggerProperty.SelectElement("createActionJobsToTrigger").Text() s.CreateActionJobsToTrigger = triggerProperty.SelectElement("createActionJobsToTrigger").Text()
s.DeleteActionJobsToTrigger = triggerProperty.SelectElement("deleteActionJobsToTrigger").Text() s.DeleteActionJobsToTrigger = triggerProperty.SelectElement("deleteActionJobsToTrigger").Text()
} }
return s return &s
} }
func createMultiBranchPipelineConfigXml(projectName string, pipeline *MultiBranchPipeline) (string, error) { func createMultiBranchPipelineConfigXml(projectName string, pipeline *devops.MultiBranchPipeline) (string, error) {
doc := etree.NewDocument() doc := etree.NewDocument()
xmlString := ` xmlString := `
<?xml version='1.0' encoding='UTF-8'?> <?xml version='1.0' encoding='UTF-8'?>
@@ -896,7 +733,7 @@ func createMultiBranchPipelineConfigXml(projectName string, pipeline *MultiBranc
if pipeline.MultiBranchJobTrigger != nil { if pipeline.MultiBranchJobTrigger != nil {
properties := project.SelectElement("properties") properties := project.SelectElement("properties")
pipeline.MultiBranchJobTrigger.appendToEtree(properties) appendMultiBranchJobTriggerToEtree(properties, pipeline.MultiBranchJobTrigger)
} }
if pipeline.Discarder != nil { if pipeline.Discarder != nil {
@@ -939,27 +776,15 @@ func createMultiBranchPipelineConfigXml(projectName string, pipeline *MultiBranc
switch pipeline.SourceType { switch pipeline.SourceType {
case "git": case "git":
gitDefine := pipeline.GitSource appendGitSourceToEtree(source, pipeline.GitSource)
gitDefine.ScmId = projectName + pipeline.Name
gitDefine.appendToEtree(source)
case "github": case "github":
githubDefine := pipeline.GitHubSource appendGithubSourceToEtree(source, pipeline.GitHubSource)
githubDefine.ScmId = projectName + pipeline.Name
githubDefine.appendToEtree(source)
case "svn": case "svn":
svnDefine := pipeline.SvnSource appendSvnSourceToEtree(source, pipeline.SvnSource)
svnDefine.ScmId = projectName + pipeline.Name
svnDefine.appendToEtree(source)
case "single_svn": case "single_svn":
singSvnDefine := pipeline.SingleSvnSource appendSingleSvnSourceToEtree(source, pipeline.SingleSvnSource)
singSvnDefine.ScmId = projectName + pipeline.Name
singSvnDefine.appendToEtree(source)
case "bitbucket_server": case "bitbucket_server":
bitbucketServerDefine := pipeline.BitbucketServerSource appendBitbucketServerSourceToEtree(source, pipeline.BitbucketServerSource)
bitbucketServerDefine.ScmId = projectName + pipeline.Name
bitbucketServerDefine.appendToEtree(source)
default: default:
return "", fmt.Errorf("unsupport source type") return "", fmt.Errorf("unsupport source type")
@@ -977,8 +802,8 @@ func createMultiBranchPipelineConfigXml(projectName string, pipeline *MultiBranc
return replaceXmlVersion(stringXml, "1.0", "1.1"), err return replaceXmlVersion(stringXml, "1.0", "1.1"), err
} }
func parseMultiBranchPipelineConfigXml(config string) (*MultiBranchPipeline, error) { func parseMultiBranchPipelineConfigXml(config string) (*devops.MultiBranchPipeline, error) {
pipeline := &MultiBranchPipeline{} pipeline := &devops.MultiBranchPipeline{}
config = replaceXmlVersion(config, "1.1", "1.0") config = replaceXmlVersion(config, "1.1", "1.0")
doc := etree.NewDocument() doc := etree.NewDocument()
err := doc.ReadFromString(config) err := doc.ReadFromString(config)
@@ -992,15 +817,13 @@ func parseMultiBranchPipelineConfigXml(config string) (*MultiBranchPipeline, err
if properties := project.SelectElement("properties"); properties != nil { if properties := project.SelectElement("properties"); properties != nil {
if multibranchTrigger := properties.SelectElement( if multibranchTrigger := properties.SelectElement(
"org.jenkinsci.plugins.workflow.multibranch.PipelineTriggerProperty"); multibranchTrigger != nil { "org.jenkinsci.plugins.workflow.multibranch.PipelineTriggerProperty"); multibranchTrigger != nil {
trigger := &MultiBranchJobTrigger{} pipeline.MultiBranchJobTrigger = getMultiBranchJobTriggerfromEtree(properties)
trigger.fromEtree(properties)
pipeline.MultiBranchJobTrigger = trigger
} }
} }
pipeline.Description = project.SelectElement("description").Text() pipeline.Description = project.SelectElement("description").Text()
if discarder := project.SelectElement("orphanedItemStrategy"); discarder != nil { if discarder := project.SelectElement("orphanedItemStrategy"); discarder != nil {
pipeline.Discarder = &DiscarderProperty{ pipeline.Discarder = &devops.DiscarderProperty{
DaysToKeep: discarder.SelectElement("daysToKeep").Text(), DaysToKeep: discarder.SelectElement("daysToKeep").Text(),
NumToKeep: discarder.SelectElement("numToKeep").Text(), NumToKeep: discarder.SelectElement("numToKeep").Text(),
} }
@@ -1008,7 +831,7 @@ func parseMultiBranchPipelineConfigXml(config string) (*MultiBranchPipeline, err
if triggers := project.SelectElement("triggers"); triggers != nil { if triggers := project.SelectElement("triggers"); triggers != nil {
if timerTrigger := triggers.SelectElement( if timerTrigger := triggers.SelectElement(
"com.cloudbees.hudson.plugins.folder.computed.PeriodicFolderTrigger"); timerTrigger != nil { "com.cloudbees.hudson.plugins.folder.computed.PeriodicFolderTrigger"); timerTrigger != nil {
pipeline.TimerTrigger = &TimerTrigger{ pipeline.TimerTrigger = &devops.TimerTrigger{
Interval: timerTrigger.SelectElement("interval").Text(), Interval: timerTrigger.SelectElement("interval").Text(),
} }
} }
@@ -1020,33 +843,23 @@ func parseMultiBranchPipelineConfigXml(config string) (*MultiBranchPipeline, err
source := branchSource.SelectElement("source") source := branchSource.SelectElement("source")
switch source.SelectAttr("class").Value { switch source.SelectAttr("class").Value {
case "org.jenkinsci.plugins.github_branch_source.GitHubSCMSource": case "org.jenkinsci.plugins.github_branch_source.GitHubSCMSource":
githubSource := &GithubSource{} pipeline.GitHubSource = getGithubSourcefromEtree(source)
githubSource.fromEtree(source)
pipeline.GitHubSource = githubSource
pipeline.SourceType = "github" pipeline.SourceType = "github"
case "com.cloudbees.jenkins.plugins.bitbucket.BitbucketSCMSource": case "com.cloudbees.jenkins.plugins.bitbucket.BitbucketSCMSource":
bitbucketServerSource := &BitbucketServerSource{} pipeline.BitbucketServerSource = getBitbucketServerSourceFromEtree(source)
bitbucketServerSource.fromEtree(source)
pipeline.BitbucketServerSource = bitbucketServerSource
pipeline.SourceType = "bitbucket_server" pipeline.SourceType = "bitbucket_server"
case "jenkins.plugins.git.GitSCMSource": case "jenkins.plugins.git.GitSCMSource":
gitSource := &GitSource{}
gitSource.fromEtree(source)
pipeline.SourceType = "git" pipeline.SourceType = "git"
pipeline.GitSource = gitSource pipeline.GitSource = getGitSourcefromEtree(source)
case "jenkins.scm.impl.SingleSCMSource": case "jenkins.scm.impl.SingleSCMSource":
singleSvnSource := &SingleSvnSource{}
singleSvnSource.fromEtree(source)
pipeline.SourceType = "single_svn" pipeline.SourceType = "single_svn"
pipeline.SingleSvnSource = singleSvnSource pipeline.SingleSvnSource = getSingleSvnSourceFromEtree(source)
case "jenkins.scm.impl.subversion.SubversionSCMSource": case "jenkins.scm.impl.subversion.SubversionSCMSource":
svnSource := &SvnSource{}
svnSource.fromEtree(source)
pipeline.SourceType = "svn" pipeline.SourceType = "svn"
pipeline.SvnSource = svnSource pipeline.SvnSource = getSvnSourcefromEtree(source)
} }
} }
} }
@@ -1078,55 +891,3 @@ func toCrontab(millis int64) string {
return "H H * * *" return "H H * * *"
} }
func getBuildSonarResults(build *gojenkins.Build) ([]*SonarStatus, error) {
sonarClient, err := client.ClientSets().SonarQube()
if err != nil {
return nil, err
}
actions := build.GetActions()
sonarStatuses := make([]*SonarStatus, 0)
for _, action := range actions {
if action.ClassName == SonarAnalysisActionClass {
sonarStatus := &SonarStatus{}
taskOptions := &sonargo.CeTaskOption{
Id: action.SonarTaskId,
}
ceTask, _, err := sonarClient.SonarQube().Ce.Task(taskOptions)
if err != nil {
klog.Errorf("get sonar task error [%+v]", err)
continue
}
sonarStatus.Task = ceTask
measuresComponentOption := &sonargo.MeasuresComponentOption{
Component: ceTask.Task.ComponentKey,
AdditionalFields: SonarAdditionalFields,
MetricKeys: SonarMetricKeys,
}
measures, _, err := sonarClient.SonarQube().Measures.Component(measuresComponentOption)
if err != nil {
klog.Errorf("get sonar task error [%+v]", err)
continue
}
sonarStatus.Measures = measures
issuesSearchOption := &sonargo.IssuesSearchOption{
AdditionalFields: "_all",
ComponentKeys: ceTask.Task.ComponentKey,
Resolved: "false",
Ps: "10",
S: "FILE_LINE",
Facets: "severities,types",
}
issuesSearch, _, err := sonarClient.SonarQube().Issues.Search(issuesSearchOption)
sonarStatus.Issues = issuesSearch
jenkinsAction := action
sonarStatus.JenkinsAction = &jenkinsAction
sonarStatuses = append(sonarStatuses, sonarStatus)
}
}
return sonarStatuses, nil
}

View File

@@ -1,25 +1,13 @@
/* package jenkins
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 ( import (
"kubesphere.io/kubesphere/pkg/simple/client/devops"
"reflect" "reflect"
"testing" "testing"
) )
func Test_NoScmPipelineConfig(t *testing.T) { func Test_NoScmPipelineConfig(t *testing.T) {
inputs := []*NoScmPipeline{ inputs := []*devops.NoScmPipeline{
{ {
Name: "", Name: "",
Description: "for test", Description: "for test",
@@ -54,12 +42,12 @@ func Test_NoScmPipelineConfig(t *testing.T) {
} }
func Test_NoScmPipelineConfig_Discarder(t *testing.T) { func Test_NoScmPipelineConfig_Discarder(t *testing.T) {
inputs := []*NoScmPipeline{ inputs := []*devops.NoScmPipeline{
{ {
Name: "", Name: "",
Description: "for test", Description: "for test",
Jenkinsfile: "node{echo 'hello'}", Jenkinsfile: "node{echo 'hello'}",
Discarder: &DiscarderProperty{ Discarder: &devops.DiscarderProperty{
"3", "5", "3", "5",
}, },
}, },
@@ -67,7 +55,7 @@ func Test_NoScmPipelineConfig_Discarder(t *testing.T) {
Name: "", Name: "",
Description: "for test", Description: "for test",
Jenkinsfile: "node{echo 'hello'}", Jenkinsfile: "node{echo 'hello'}",
Discarder: &DiscarderProperty{ Discarder: &devops.DiscarderProperty{
"3", "", "3", "",
}, },
}, },
@@ -75,7 +63,7 @@ func Test_NoScmPipelineConfig_Discarder(t *testing.T) {
Name: "", Name: "",
Description: "for test", Description: "for test",
Jenkinsfile: "node{echo 'hello'}", Jenkinsfile: "node{echo 'hello'}",
Discarder: &DiscarderProperty{ Discarder: &devops.DiscarderProperty{
"", "21321", "", "21321",
}, },
}, },
@@ -83,7 +71,7 @@ func Test_NoScmPipelineConfig_Discarder(t *testing.T) {
Name: "", Name: "",
Description: "for test", Description: "for test",
Jenkinsfile: "node{echo 'hello'}", Jenkinsfile: "node{echo 'hello'}",
Discarder: &DiscarderProperty{ Discarder: &devops.DiscarderProperty{
"", "", "", "",
}, },
}, },
@@ -105,13 +93,13 @@ func Test_NoScmPipelineConfig_Discarder(t *testing.T) {
} }
func Test_NoScmPipelineConfig_Param(t *testing.T) { func Test_NoScmPipelineConfig_Param(t *testing.T) {
inputs := []*NoScmPipeline{ inputs := []*devops.NoScmPipeline{
{ {
Name: "", Name: "",
Description: "for test", Description: "for test",
Jenkinsfile: "node{echo 'hello'}", Jenkinsfile: "node{echo 'hello'}",
Parameters: &Parameters{ Parameters: &devops.Parameters{
&Parameter{ &devops.Parameter{
Name: "d", Name: "d",
DefaultValue: "a\nb", DefaultValue: "a\nb",
Type: "choice", Type: "choice",
@@ -123,26 +111,26 @@ func Test_NoScmPipelineConfig_Param(t *testing.T) {
Name: "", Name: "",
Description: "for test", Description: "for test",
Jenkinsfile: "node{echo 'hello'}", Jenkinsfile: "node{echo 'hello'}",
Parameters: &Parameters{ Parameters: &devops.Parameters{
&Parameter{ &devops.Parameter{
Name: "a", Name: "a",
DefaultValue: "abc", DefaultValue: "abc",
Type: "string", Type: "string",
Description: "fortest", Description: "fortest",
}, },
&Parameter{ &devops.Parameter{
Name: "b", Name: "b",
DefaultValue: "false", DefaultValue: "false",
Type: "boolean", Type: "boolean",
Description: "fortest", Description: "fortest",
}, },
&Parameter{ &devops.Parameter{
Name: "c", Name: "c",
DefaultValue: "password \n aaa", DefaultValue: "password \n aaa",
Type: "text", Type: "text",
Description: "fortest", Description: "fortest",
}, },
&Parameter{ &devops.Parameter{
Name: "d", Name: "d",
DefaultValue: "a\nb", DefaultValue: "a\nb",
Type: "choice", Type: "choice",
@@ -168,12 +156,12 @@ func Test_NoScmPipelineConfig_Param(t *testing.T) {
} }
func Test_NoScmPipelineConfig_Trigger(t *testing.T) { func Test_NoScmPipelineConfig_Trigger(t *testing.T) {
inputs := []*NoScmPipeline{ inputs := []*devops.NoScmPipeline{
{ {
Name: "", Name: "",
Description: "for test", Description: "for test",
Jenkinsfile: "node{echo 'hello'}", Jenkinsfile: "node{echo 'hello'}",
TimerTrigger: &TimerTrigger{ TimerTrigger: &devops.TimerTrigger{
Cron: "1 1 1 * * *", Cron: "1 1 1 * * *",
}, },
}, },
@@ -182,7 +170,7 @@ func Test_NoScmPipelineConfig_Trigger(t *testing.T) {
Name: "", Name: "",
Description: "for test", Description: "for test",
Jenkinsfile: "node{echo 'hello'}", Jenkinsfile: "node{echo 'hello'}",
RemoteTrigger: &RemoteTrigger{ RemoteTrigger: &devops.RemoteTrigger{
Token: "abc", Token: "abc",
}, },
}, },
@@ -190,10 +178,10 @@ func Test_NoScmPipelineConfig_Trigger(t *testing.T) {
Name: "", Name: "",
Description: "for test", Description: "for test",
Jenkinsfile: "node{echo 'hello'}", Jenkinsfile: "node{echo 'hello'}",
TimerTrigger: &TimerTrigger{ TimerTrigger: &devops.TimerTrigger{
Cron: "1 1 1 * * *", Cron: "1 1 1 * * *",
}, },
RemoteTrigger: &RemoteTrigger{ RemoteTrigger: &devops.RemoteTrigger{
Token: "abc", Token: "abc",
}, },
}, },
@@ -217,34 +205,34 @@ func Test_NoScmPipelineConfig_Trigger(t *testing.T) {
func Test_MultiBranchPipelineConfig(t *testing.T) { func Test_MultiBranchPipelineConfig(t *testing.T) {
inputs := []*MultiBranchPipeline{ inputs := []*devops.MultiBranchPipeline{
{ {
Name: "", Name: "",
Description: "for test", Description: "for test",
ScriptPath: "Jenkinsfile", ScriptPath: "Jenkinsfile",
SourceType: "git", SourceType: "git",
GitSource: &GitSource{}, GitSource: &devops.GitSource{},
}, },
{ {
Name: "", Name: "",
Description: "for test", Description: "for test",
ScriptPath: "Jenkinsfile", ScriptPath: "Jenkinsfile",
SourceType: "github", SourceType: "github",
GitHubSource: &GithubSource{}, GitHubSource: &devops.GithubSource{},
}, },
{ {
Name: "", Name: "",
Description: "for test", Description: "for test",
ScriptPath: "Jenkinsfile", ScriptPath: "Jenkinsfile",
SourceType: "single_svn", SourceType: "single_svn",
SingleSvnSource: &SingleSvnSource{}, SingleSvnSource: &devops.SingleSvnSource{},
}, },
{ {
Name: "", Name: "",
Description: "for test", Description: "for test",
ScriptPath: "Jenkinsfile", ScriptPath: "Jenkinsfile",
SourceType: "svn", SourceType: "svn",
SvnSource: &SvnSource{}, SvnSource: &devops.SvnSource{},
}, },
} }
for _, input := range inputs { for _, input := range inputs {
@@ -265,17 +253,17 @@ func Test_MultiBranchPipelineConfig(t *testing.T) {
func Test_MultiBranchPipelineConfig_Discarder(t *testing.T) { func Test_MultiBranchPipelineConfig_Discarder(t *testing.T) {
inputs := []*MultiBranchPipeline{ inputs := []*devops.MultiBranchPipeline{
{ {
Name: "", Name: "",
Description: "for test", Description: "for test",
ScriptPath: "Jenkinsfile", ScriptPath: "Jenkinsfile",
SourceType: "git", SourceType: "git",
Discarder: &DiscarderProperty{ Discarder: &devops.DiscarderProperty{
DaysToKeep: "1", DaysToKeep: "1",
NumToKeep: "2", NumToKeep: "2",
}, },
GitSource: &GitSource{}, GitSource: &devops.GitSource{},
}, },
} }
for _, input := range inputs { for _, input := range inputs {
@@ -295,16 +283,16 @@ func Test_MultiBranchPipelineConfig_Discarder(t *testing.T) {
} }
func Test_MultiBranchPipelineConfig_TimerTrigger(t *testing.T) { func Test_MultiBranchPipelineConfig_TimerTrigger(t *testing.T) {
inputs := []*MultiBranchPipeline{ inputs := []*devops.MultiBranchPipeline{
{ {
Name: "", Name: "",
Description: "for test", Description: "for test",
ScriptPath: "Jenkinsfile", ScriptPath: "Jenkinsfile",
SourceType: "git", SourceType: "git",
TimerTrigger: &TimerTrigger{ TimerTrigger: &devops.TimerTrigger{
Interval: "12345566", Interval: "12345566",
}, },
GitSource: &GitSource{}, GitSource: &devops.GitSource{},
}, },
} }
for _, input := range inputs { for _, input := range inputs {
@@ -325,16 +313,16 @@ func Test_MultiBranchPipelineConfig_TimerTrigger(t *testing.T) {
func Test_MultiBranchPipelineConfig_Source(t *testing.T) { func Test_MultiBranchPipelineConfig_Source(t *testing.T) {
inputs := []*MultiBranchPipeline{ inputs := []*devops.MultiBranchPipeline{
{ {
Name: "", Name: "",
Description: "for test", Description: "for test",
ScriptPath: "Jenkinsfile", ScriptPath: "Jenkinsfile",
SourceType: "git", SourceType: "git",
TimerTrigger: &TimerTrigger{ TimerTrigger: &devops.TimerTrigger{
Interval: "12345566", Interval: "12345566",
}, },
GitSource: &GitSource{ GitSource: &devops.GitSource{
Url: "https://github.com/kubesphere/devops", Url: "https://github.com/kubesphere/devops",
CredentialId: "git", CredentialId: "git",
DiscoverBranches: true, DiscoverBranches: true,
@@ -345,17 +333,17 @@ func Test_MultiBranchPipelineConfig_Source(t *testing.T) {
Description: "for test", Description: "for test",
ScriptPath: "Jenkinsfile", ScriptPath: "Jenkinsfile",
SourceType: "github", SourceType: "github",
TimerTrigger: &TimerTrigger{ TimerTrigger: &devops.TimerTrigger{
Interval: "12345566", Interval: "12345566",
}, },
GitHubSource: &GithubSource{ GitHubSource: &devops.GithubSource{
Owner: "kubesphere", Owner: "kubesphere",
Repo: "devops", Repo: "devops",
CredentialId: "github", CredentialId: "github",
ApiUri: "https://api.github.com", ApiUri: "https://api.github.com",
DiscoverBranches: 1, DiscoverBranches: 1,
DiscoverPRFromOrigin: 2, DiscoverPRFromOrigin: 2,
DiscoverPRFromForks: &DiscoverPRFromForks{ DiscoverPRFromForks: &devops.DiscoverPRFromForks{
Strategy: 1, Strategy: 1,
Trust: 1, Trust: 1,
}, },
@@ -366,17 +354,17 @@ func Test_MultiBranchPipelineConfig_Source(t *testing.T) {
Description: "for test", Description: "for test",
ScriptPath: "Jenkinsfile", ScriptPath: "Jenkinsfile",
SourceType: "bitbucket_server", SourceType: "bitbucket_server",
TimerTrigger: &TimerTrigger{ TimerTrigger: &devops.TimerTrigger{
Interval: "12345566", Interval: "12345566",
}, },
BitbucketServerSource: &BitbucketServerSource{ BitbucketServerSource: &devops.BitbucketServerSource{
Owner: "kubesphere", Owner: "kubesphere",
Repo: "devops", Repo: "devops",
CredentialId: "github", CredentialId: "github",
ApiUri: "https://api.github.com", ApiUri: "https://api.github.com",
DiscoverBranches: 1, DiscoverBranches: 1,
DiscoverPRFromOrigin: 2, DiscoverPRFromOrigin: 2,
DiscoverPRFromForks: &DiscoverPRFromForks{ DiscoverPRFromForks: &devops.DiscoverPRFromForks{
Strategy: 1, Strategy: 1,
Trust: 1, Trust: 1,
}, },
@@ -388,10 +376,10 @@ func Test_MultiBranchPipelineConfig_Source(t *testing.T) {
Description: "for test", Description: "for test",
ScriptPath: "Jenkinsfile", ScriptPath: "Jenkinsfile",
SourceType: "svn", SourceType: "svn",
TimerTrigger: &TimerTrigger{ TimerTrigger: &devops.TimerTrigger{
Interval: "12345566", Interval: "12345566",
}, },
SvnSource: &SvnSource{ SvnSource: &devops.SvnSource{
Remote: "https://api.svn.com/bcd", Remote: "https://api.svn.com/bcd",
CredentialId: "svn", CredentialId: "svn",
Excludes: "truck", Excludes: "truck",
@@ -403,10 +391,10 @@ func Test_MultiBranchPipelineConfig_Source(t *testing.T) {
Description: "for test", Description: "for test",
ScriptPath: "Jenkinsfile", ScriptPath: "Jenkinsfile",
SourceType: "single_svn", SourceType: "single_svn",
TimerTrigger: &TimerTrigger{ TimerTrigger: &devops.TimerTrigger{
Interval: "12345566", Interval: "12345566",
}, },
SingleSvnSource: &SingleSvnSource{ SingleSvnSource: &devops.SingleSvnSource{
Remote: "https://api.svn.com/bcd", Remote: "https://api.svn.com/bcd",
CredentialId: "svn", CredentialId: "svn",
}, },
@@ -431,17 +419,17 @@ func Test_MultiBranchPipelineConfig_Source(t *testing.T) {
func Test_MultiBranchPipelineCloneConfig(t *testing.T) { func Test_MultiBranchPipelineCloneConfig(t *testing.T) {
inputs := []*MultiBranchPipeline{ inputs := []*devops.MultiBranchPipeline{
{ {
Name: "", Name: "",
Description: "for test", Description: "for test",
ScriptPath: "Jenkinsfile", ScriptPath: "Jenkinsfile",
SourceType: "git", SourceType: "git",
GitSource: &GitSource{ GitSource: &devops.GitSource{
Url: "https://github.com/kubesphere/devops", Url: "https://github.com/kubesphere/devops",
CredentialId: "git", CredentialId: "git",
DiscoverBranches: true, DiscoverBranches: true,
CloneOption: &GitCloneOption{ CloneOption: &devops.GitCloneOption{
Shallow: false, Shallow: false,
Depth: 3, Depth: 3,
Timeout: 20, Timeout: 20,
@@ -453,18 +441,18 @@ func Test_MultiBranchPipelineCloneConfig(t *testing.T) {
Description: "for test", Description: "for test",
ScriptPath: "Jenkinsfile", ScriptPath: "Jenkinsfile",
SourceType: "github", SourceType: "github",
GitHubSource: &GithubSource{ GitHubSource: &devops.GithubSource{
Owner: "kubesphere", Owner: "kubesphere",
Repo: "devops", Repo: "devops",
CredentialId: "github", CredentialId: "github",
ApiUri: "https://api.github.com", ApiUri: "https://api.github.com",
DiscoverBranches: 1, DiscoverBranches: 1,
DiscoverPRFromOrigin: 2, DiscoverPRFromOrigin: 2,
DiscoverPRFromForks: &DiscoverPRFromForks{ DiscoverPRFromForks: &devops.DiscoverPRFromForks{
Strategy: 1, Strategy: 1,
Trust: 1, Trust: 1,
}, },
CloneOption: &GitCloneOption{ CloneOption: &devops.GitCloneOption{
Shallow: false, Shallow: false,
Depth: 3, Depth: 3,
Timeout: 20, Timeout: 20,
@@ -492,13 +480,13 @@ func Test_MultiBranchPipelineCloneConfig(t *testing.T) {
func Test_MultiBranchPipelineRegexFilter(t *testing.T) { func Test_MultiBranchPipelineRegexFilter(t *testing.T) {
inputs := []*MultiBranchPipeline{ inputs := []*devops.MultiBranchPipeline{
{ {
Name: "", Name: "",
Description: "for test", Description: "for test",
ScriptPath: "Jenkinsfile", ScriptPath: "Jenkinsfile",
SourceType: "git", SourceType: "git",
GitSource: &GitSource{ GitSource: &devops.GitSource{
Url: "https://github.com/kubesphere/devops", Url: "https://github.com/kubesphere/devops",
CredentialId: "git", CredentialId: "git",
DiscoverBranches: true, DiscoverBranches: true,
@@ -510,14 +498,14 @@ func Test_MultiBranchPipelineRegexFilter(t *testing.T) {
Description: "for test", Description: "for test",
ScriptPath: "Jenkinsfile", ScriptPath: "Jenkinsfile",
SourceType: "github", SourceType: "github",
GitHubSource: &GithubSource{ GitHubSource: &devops.GithubSource{
Owner: "kubesphere", Owner: "kubesphere",
Repo: "devops", Repo: "devops",
CredentialId: "github", CredentialId: "github",
ApiUri: "https://api.github.com", ApiUri: "https://api.github.com",
DiscoverBranches: 1, DiscoverBranches: 1,
DiscoverPRFromOrigin: 2, DiscoverPRFromOrigin: 2,
DiscoverPRFromForks: &DiscoverPRFromForks{ DiscoverPRFromForks: &devops.DiscoverPRFromForks{
Strategy: 1, Strategy: 1,
Trust: 1, Trust: 1,
}, },
@@ -545,26 +533,26 @@ func Test_MultiBranchPipelineRegexFilter(t *testing.T) {
func Test_MultiBranchPipelineMultibranchTrigger(t *testing.T) { func Test_MultiBranchPipelineMultibranchTrigger(t *testing.T) {
inputs := []*MultiBranchPipeline{ inputs := []*devops.MultiBranchPipeline{
{ {
Name: "", Name: "",
Description: "for test", Description: "for test",
ScriptPath: "Jenkinsfile", ScriptPath: "Jenkinsfile",
SourceType: "github", SourceType: "github",
GitHubSource: &GithubSource{ GitHubSource: &devops.GithubSource{
Owner: "kubesphere", Owner: "kubesphere",
Repo: "devops", Repo: "devops",
CredentialId: "github", CredentialId: "github",
ApiUri: "https://api.github.com", ApiUri: "https://api.github.com",
DiscoverBranches: 1, DiscoverBranches: 1,
DiscoverPRFromOrigin: 2, DiscoverPRFromOrigin: 2,
DiscoverPRFromForks: &DiscoverPRFromForks{ DiscoverPRFromForks: &devops.DiscoverPRFromForks{
Strategy: 1, Strategy: 1,
Trust: 1, Trust: 1,
}, },
RegexFilter: ".*", RegexFilter: ".*",
}, },
MultiBranchJobTrigger: &MultiBranchJobTrigger{ MultiBranchJobTrigger: &devops.MultiBranchJobTrigger{
CreateActionJobsToTrigger: "abc", CreateActionJobsToTrigger: "abc",
DeleteActionJobsToTrigger: "ddd", DeleteActionJobsToTrigger: "ddd",
}, },
@@ -574,20 +562,20 @@ func Test_MultiBranchPipelineMultibranchTrigger(t *testing.T) {
Description: "for test", Description: "for test",
ScriptPath: "Jenkinsfile", ScriptPath: "Jenkinsfile",
SourceType: "github", SourceType: "github",
GitHubSource: &GithubSource{ GitHubSource: &devops.GithubSource{
Owner: "kubesphere", Owner: "kubesphere",
Repo: "devops", Repo: "devops",
CredentialId: "github", CredentialId: "github",
ApiUri: "https://api.github.com", ApiUri: "https://api.github.com",
DiscoverBranches: 1, DiscoverBranches: 1,
DiscoverPRFromOrigin: 2, DiscoverPRFromOrigin: 2,
DiscoverPRFromForks: &DiscoverPRFromForks{ DiscoverPRFromForks: &devops.DiscoverPRFromForks{
Strategy: 1, Strategy: 1,
Trust: 1, Trust: 1,
}, },
RegexFilter: ".*", RegexFilter: ".*",
}, },
MultiBranchJobTrigger: &MultiBranchJobTrigger{ MultiBranchJobTrigger: &devops.MultiBranchJobTrigger{
CreateActionJobsToTrigger: "abc", CreateActionJobsToTrigger: "abc",
}, },
}, },
@@ -596,20 +584,20 @@ func Test_MultiBranchPipelineMultibranchTrigger(t *testing.T) {
Description: "for test", Description: "for test",
ScriptPath: "Jenkinsfile", ScriptPath: "Jenkinsfile",
SourceType: "github", SourceType: "github",
GitHubSource: &GithubSource{ GitHubSource: &devops.GithubSource{
Owner: "kubesphere", Owner: "kubesphere",
Repo: "devops", Repo: "devops",
CredentialId: "github", CredentialId: "github",
ApiUri: "https://api.github.com", ApiUri: "https://api.github.com",
DiscoverBranches: 1, DiscoverBranches: 1,
DiscoverPRFromOrigin: 2, DiscoverPRFromOrigin: 2,
DiscoverPRFromForks: &DiscoverPRFromForks{ DiscoverPRFromForks: &devops.DiscoverPRFromForks{
Strategy: 1, Strategy: 1,
Trust: 1, Trust: 1,
}, },
RegexFilter: ".*", RegexFilter: ".*",
}, },
MultiBranchJobTrigger: &MultiBranchJobTrigger{ MultiBranchJobTrigger: &devops.MultiBranchJobTrigger{
DeleteActionJobsToTrigger: "ddd", DeleteActionJobsToTrigger: "ddd",
}, },
}, },

View File

@@ -11,7 +11,7 @@ See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
*/ */
package gojenkins package jenkins
import ( import (
"errors" "errors"

View File

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

View File

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

View File

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

View File

@@ -12,7 +12,7 @@
// License for the specific language governing permissions and limitations // License for the specific language governing permissions and limitations
// under the License. // under the License.
package gojenkins package jenkins
import ( import (
"bytes" "bytes"

View File

@@ -1,4 +1,4 @@
package gojenkins package jenkins
import ( import (
"errors" "errors"

View File

@@ -0,0 +1,146 @@
// Copyright 2015 Vadim Kravcenko
//
// Licensed under the Apache License, Version 2.0 (the "License"): you may
// not use this file except in compliance with the License. You may obtain
// a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
// License for the specific language governing permissions and limitations
// under the License.
package jenkins
import (
"compress/gzip"
"encoding/json"
"github.com/asaskevich/govalidator"
"io"
"io/ioutil"
"k8s.io/klog"
"net/http"
"net/url"
"strconv"
"strings"
"time"
"unicode/utf8"
)
func makeJson(data interface{}) string {
str, err := json.Marshal(data)
if err != nil {
return ""
}
return string(json.RawMessage(str))
}
func Reverse(s string) string {
size := len(s)
buf := make([]byte, size)
for start := 0; start < size; {
r, n := utf8.DecodeRuneInString(s[start:])
start += n
utf8.EncodeRune(buf[size-start:], r)
}
return string(buf)
}
type JkError struct {
Message string `json:"message"`
Code int `json:"code"`
}
func (err *JkError) Error() string {
return err.Message
}
// Decompress response.body of JenkinsAPIResponse
func getRespBody(resp *http.Response) ([]byte, error) {
var reader io.ReadCloser
if resp.Header.Get("Content-Encoding") == "gzip" {
reader, _ = gzip.NewReader(resp.Body)
} else {
reader = resp.Body
}
resBody, err := ioutil.ReadAll(reader)
if err != nil {
klog.Error(err)
return nil, err
}
return resBody, err
}
// parseJenkinsQuery Parse the special query of jenkins.
// ParseQuery in the standard library makes the query not re-encode
func parseJenkinsQuery(query string) (url.Values, error) {
m := make(url.Values)
err := error(nil)
for query != "" {
key := query
if i := strings.IndexAny(key, "&"); i >= 0 {
key, query = key[:i], key[i+1:]
} else {
query = ""
}
if key == "" {
continue
}
value := ""
if i := strings.Index(key, "="); i >= 0 {
key, value = key[:i], key[i+1:]
}
key, err1 := url.QueryUnescape(key)
if err1 != nil {
if err == nil {
err = err1
}
continue
}
value, err1 = url.QueryUnescape(value)
if err1 != nil {
if err == nil {
err = err1
}
continue
}
m[key] = append(m[key], value)
}
return m, err
}
type JenkinsBlueTime time.Time
func (t *JenkinsBlueTime) UnmarshalJSON(b []byte) error {
if b == nil || strings.Trim(string(b), "\"") == "null" {
*t = JenkinsBlueTime(time.Time{})
return nil
}
j, err := time.Parse("2006-01-02T15:04:05.000-0700", strings.Trim(string(b), "\""))
if err != nil {
return err
}
*t = JenkinsBlueTime(j)
return nil
}
func (t JenkinsBlueTime) MarshalJSON() ([]byte, error) {
return json.Marshal(time.Time(t))
}
func GetJenkinsStatusCode(jenkinsErr error) int {
if code, err := strconv.Atoi(jenkinsErr.Error()); err == nil {
message := http.StatusText(code)
if !govalidator.IsNull(message) {
return code
}
}
if jErr, ok := jenkinsErr.(*ErrorResponse); ok {
return jErr.Response.StatusCode
}
return http.StatusInternalServerError
}

View File

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

View File

@@ -1,22 +1,16 @@
/*
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 package devops
import (
"io"
"net/http"
"net/url"
)
type PipelineList struct {
Items []Pipeline `json:"items"`
Total int `json:"total_count"`
}
// GetPipeline & SearchPipelines // GetPipeline & SearchPipelines
type Pipeline struct { type Pipeline struct {
Class string `json:"_class,omitempty" description:"Its a fully qualified name and is an identifier of the producer of this resource's capability." ` Class string `json:"_class,omitempty" description:"Its a fully qualified name and is an identifier of the producer of this resource's capability." `
@@ -85,7 +79,7 @@ type Pipeline struct {
} }
// GetPipeBranchRun & SearchPipelineRuns // GetPipeBranchRun & SearchPipelineRuns
type BranchPipelineRun struct { type PipelineRunList struct {
Class string `json:"_class,omitempty" description:"Its a fully qualified name and is an identifier of the producer of this resource's capability."` Class string `json:"_class,omitempty" description:"Its a fully qualified name and is an identifier of the producer of this resource's capability."`
Links struct { Links struct {
PrevRun struct { PrevRun struct {
@@ -316,7 +310,7 @@ type OrgRepo struct {
} }
// StopPipeline // StopPipeline
type StopPipe struct { type StopPipeline struct {
Class string `json:"_class,omitempty" description:"Its a fully qualified name and is an identifier of the producer of this resource's capability."` Class string `json:"_class,omitempty" description:"Its a fully qualified name and is an identifier of the producer of this resource's capability."`
Links struct { Links struct {
Parent struct { Parent struct {
@@ -390,7 +384,7 @@ type StopPipe struct {
} }
// ReplayPipeline // ReplayPipeline
type ReplayPipe struct { type ReplayPipeline struct {
Class string `json:"_class,omitempty" description:"Its a fully qualified name and is an identifier of the producer of this resource's capability."` Class string `json:"_class,omitempty" description:"Its a fully qualified name and is an identifier of the producer of this resource's capability."`
Links struct { Links struct {
Parent struct { Parent struct {
@@ -468,7 +462,7 @@ type Artifacts struct {
} }
// GetPipeBranch // GetPipeBranch
type PipeBranch struct { type PipelineBranch struct {
Class string `json:"_class,omitempty" description:"Its a fully qualified name and is an identifier of the producer of this resource's capability."` Class string `json:"_class,omitempty" description:"Its a fully qualified name and is an identifier of the producer of this resource's capability."`
Links struct { Links struct {
Self struct { Self struct {
@@ -598,7 +592,7 @@ type RunPayload struct {
} `json:"parameters,omitempty"` } `json:"parameters,omitempty"`
} }
type QueuedBlueRun struct { type RunPipeline struct {
Class string `json:"_class,omitempty" description:"Its a fully qualified name and is an identifier of the producer of this resource's capability."` Class string `json:"_class,omitempty" description:"Its a fully qualified name and is an identifier of the producer of this resource's capability."`
Links struct { Links struct {
Parent struct { Parent struct {
@@ -1108,22 +1102,18 @@ type NodesDetail struct {
Actions []interface{} `json:"actions,omitempty" description:"the list of all actions"` Actions []interface{} `json:"actions,omitempty" description:"the list of all actions"`
DisplayDescription interface{} `json:"displayDescription,omitempty" description:"display description"` DisplayDescription interface{} `json:"displayDescription,omitempty" description:"display description"`
DisplayName string `json:"displayName,omitempty" description:"display name"` DisplayName string `json:"displayName,omitempty" description:"display name"`
DurationInMillis int `json:"durationInMillis,omitempty" description:"duration time in millis"` DurationInMillis int `json:"durationInMillis,omitempty" description:"duration time in mullis"`
ID string `json:"id,omitempty" description:"id"` ID string `json:"id,omitempty" description:"id"`
Input *Input `json:"input,omitempty" description:"the action should user input"` Input *Input `json:"input,omitempty" description:"the action should user input"`
Result string `json:"result,omitempty" description:"the result of pipeline run. e.g. SUCCESS"` Result string `json:"result,omitempty" description:"the result of pipeline run. e.g. SUCCESS"`
StartTime string `json:"startTime,omitempty" description:"the time of start"` StartTime string `json:"startTime,omitempty" description:"the time of start"`
State string `json:"state,omitempty" description:"run state. e.g. SKIPPED"` State string `json:"state,omitempty" description:"run state. e.g. FINISHED"`
Type string `json:"type,omitempty" description:"type"` Type string `json:"type,omitempty" description:"type"`
CauseOfBlockage interface{} `json:"causeOfBlockage,omitempty" description:"the cause of blockage"` CauseOfBlockage interface{} `json:"causeOfBlockage,omitempty" description:"the cause of blockage"`
Edges []struct { Edges []interface{} `json:"edges,omitempty" description:"edges"`
Class string `json:"_class,omitempty" description:"Its a fully qualified name and is an identifier of the producer of this resource's capability."` FirstParent interface{} `json:"firstParent,omitempty" description:"first parent"`
ID string `json:"id,omitempty" description:"id"` Restartable bool `json:"restartable,omitempty" description:"restartable or not"`
Type string `json:"type,omitempty" description:"type"` Steps []NodeSteps `json:"steps,omitempty" description:"steps"`
} `json:"edges,omitempty"`
FirstParent interface{} `json:"firstParent,omitempty" description:"first parent"`
Restartable bool `json:"restartable,omitempty" description:"restartable or not"`
Steps []NodeSteps `json:"steps,omitempty" description:"steps"`
} }
type NodesStepsIndex struct { type NodesStepsIndex struct {
@@ -1145,3 +1135,65 @@ type Input struct {
Parameters []interface{} `json:"parameters,omitempty" description:"the parameters of check action"` Parameters []interface{} `json:"parameters,omitempty" description:"the parameters of check action"`
Submitter interface{} `json:"submitter,omitempty" description:"check submitter"` Submitter interface{} `json:"submitter,omitempty" description:"check submitter"`
} }
type HttpParameters struct {
Method string `json:"method,omitempty"`
Header http.Header `json:"header,omitempty"`
Body io.ReadCloser `json:"body,omitempty"`
Form url.Values `json:"form,omitempty"`
PostForm url.Values `json:"postForm,omitempty"`
Url *url.URL `json:"url,omitempty"`
}
type PipelineOperator interface {
// Pipelinne operator interface
GetPipeline(projectName, pipelineName string, httpParameters *HttpParameters) (*Pipeline, error)
ListPipelines(httpParameters *HttpParameters) (*PipelineList, error)
GetPipelineRun(projectName, pipelineName, runId string, httpParameters *HttpParameters) (*PipelineRun, error)
ListPipelineRuns(projectName, pipelineName string, httpParameters *HttpParameters) (*PipelineRunList, error)
StopPipeline(projectName, pipelineName, runId string, httpParameters *HttpParameters) (*StopPipeline, error)
ReplayPipeline(projectName, pipelineName, runId string, httpParameters *HttpParameters) (*ReplayPipeline, error)
RunPipeline(projectName, pipelineName string, httpParameters *HttpParameters) (*RunPipeline, error)
GetArtifacts(projectName, pipelineName, runId string, httpParameters *HttpParameters) ([]Artifacts, error)
GetRunLog(projectName, pipelineName, runId string, httpParameters *HttpParameters) ([]byte, error)
GetStepLog(projectName, pipelineName, runId, nodeId, stepId string, httpParameters *HttpParameters) ([]byte, http.Header, error)
GetNodeSteps(projectName, pipelineName, runId, nodeId string, httpParameters *HttpParameters) ([]NodeSteps, error)
GetPipelineRunNodes(projectName, pipelineName, runId string, httpParameters *HttpParameters) ([]PipelineRunNodes, error)
SubmitInputStep(projectName, pipelineName, runId, nodeId, stepId string, httpParameters *HttpParameters) ([]byte, error)
//BranchPipelinne operator interface
GetBranchPipeline(projectName, pipelineName, branchName string, httpParameters *HttpParameters) (*BranchPipeline, error)
GetBranchPipelineRun(projectName, pipelineName, branchName, runId string, httpParameters *HttpParameters) (*PipelineRun, error)
StopBranchPipeline(projectName, pipelineName, branchName, runId string, httpParameters *HttpParameters) (*StopPipeline, error)
ReplayBranchPipeline(projectName, pipelineName, branchName, runId string, httpParameters *HttpParameters) (*ReplayPipeline, error)
RunBranchPipeline(projectName, pipelineName, branchName string, httpParameters *HttpParameters) (*RunPipeline, error)
GetBranchArtifacts(projectName, pipelineName, branchName, runId string, httpParameters *HttpParameters) ([]Artifacts, error)
GetBranchRunLog(projectName, pipelineName, branchName, runId string, httpParameters *HttpParameters) ([]byte, error)
GetBranchStepLog(projectName, pipelineName, branchName, runId, nodeId, stepId string, httpParameters *HttpParameters) ([]byte, http.Header, error)
GetBranchNodeSteps(projectName, pipelineName, branchName, runId, nodeId string, httpParameters *HttpParameters) ([]NodeSteps, error)
GetBranchPipelineRunNodes(projectName, pipelineName, branchName, runId string, httpParameters *HttpParameters) ([]BranchPipelineRunNodes, error)
SubmitBranchInputStep(projectName, pipelineName, branchName, runId, nodeId, stepId string, httpParameters *HttpParameters) ([]byte, error)
GetPipelineBranch(projectName, pipelineName string, httpParameters *HttpParameters) (*PipelineBranch, error)
ScanBranch(projectName, pipelineName string, httpParameters *HttpParameters) ([]byte, error)
// Common pipeline operator interface
GetConsoleLog(projectName, pipelineName string, httpParameters *HttpParameters) ([]byte, error)
GetCrumb(httpParameters *HttpParameters) (*Crumb, error)
// SCM operator interface
GetSCMServers(scmId string, httpParameters *HttpParameters) ([]SCMServer, error)
GetSCMOrg(scmId string, httpParameters *HttpParameters) ([]SCMOrg, error)
GetOrgRepo(scmId, organizationId string, httpParameters *HttpParameters) ([]OrgRepo, error)
CreateSCMServers(scmId string, httpParameters *HttpParameters) (*SCMServer, error)
Validate(scmId string, httpParameters *HttpParameters) (*Validates, error)
//Webhook operator interface
GetNotifyCommit(httpParameters *HttpParameters) ([]byte, error)
GithubWebhook(httpParameters *HttpParameters) ([]byte, error)
CheckScriptCompile(projectName, pipelineName string, httpParameters *HttpParameters) (*CheckScript, error)
CheckCron(projectName string, httpParameters *HttpParameters) (*CheckCronRes, error)
ToJenkinsfile(httpParameters *HttpParameters) (*ResJenkinsfile, error)
ToJson(httpParameters *HttpParameters) (*ResJson, error)
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

Some files were not shown because too many files have changed in this diff Show More