From c5a340a2b47a184dad27fa63a38a469e30374ab4 Mon Sep 17 00:00:00 2001 From: runzexia Date: Tue, 4 Feb 2020 10:40:36 +0800 Subject: [PATCH] devops refactor (#1739) * add devops client interface Signed-off-by: runzexia * direct return jenkins Signed-off-by: runzexia * add some interface Signed-off-by: runzexia * update Signed-off-by: runzexia * update interface Signed-off-by: runzexia * update Signed-off-by: runzexia * credential op structs Signed-off-by: runzexia * status Signed-off-by: zhuxiaoyang * update interface Signed-off-by: zhuxiaoyang * credential handler Signed-off-by: runzexia * update devopsoperator func Signed-off-by: zhuxiaoyang * get build sonar Signed-off-by: runzexia * sonar handler * mv code to cilent Signed-off-by: runzexia * update Signed-off-by: runzexia * project member handler Signed-off-by: runzexia * update pipeline operator interface Signed-off-by: zhuxiaoyang * add tenant devops handler Signed-off-by: runzexia * update merge Signed-off-by: runzexia * clean Signed-off-by: runzexia * fmt Signed-off-by: runzexia * update ListPipelineRuns Signed-off-by: zhuxiaoyang * complate pipelineOperator interface Signed-off-by: zhuxiaoyang * update HttpParameters Signed-off-by: zhuxiaoyang * add pipeline steps interface Signed-off-by: zhuxiaoyang * update pipeline GetNodesDetail Signed-off-by: zhuxiaoyang * add s2i api Signed-off-by: runzexia * add branch pipeline interface and update handler Signed-off-by: zhuxiaoyang * add scan branch interface and update handler Signed-off-by: zhuxiaoyang * add common interface and update handler Signed-off-by: zhuxiaoyang * add SCM interface and update handler Signed-off-by: zhuxiaoyang * add handler Signed-off-by: runzexia * add fake s3 Signed-off-by: runzexia * add webhook&check interface and update handler Signed-off-by: zhuxiaoyang * clean Signed-off-by: zhuxiaoyang * clean Signed-off-by: zhuxiaoyang * format Signed-off-by: zhuxiaoyang * add some func Signed-off-by: runzexia * clean code Signed-off-by: runzexia * implement interface Signed-off-by: zhuxiaoyang * fix interface GetBranchArtifacts Signed-off-by: zhuxiaoyang * add s2ibinary upload test Signed-off-by: runzexia * tenant devops Signed-off-by: runzexia * update tenant Signed-off-by: runzexia * fake Signed-off-by: zhuxiaoyang * add some unit test Signed-off-by: zhuxiaoyang * add devops tenant handler Signed-off-by: runzexia * status Signed-off-by: zhuxiaoyang * status Signed-off-by: zhuxiaoyang * status Signed-off-by: zhuxiaoyang * update fake test Signed-off-by: zhuxiaoyang * update unit test and fake data Signed-off-by: zhuxiaoyang * update Co-authored-by: Xiaoyang Zhu --- cmd/controller-manager/app/options/options.go | 6 +- cmd/ks-apiserver/app/options/options.go | 6 +- go.mod | 2 +- go.sum | 25 - pkg/apis/logging/v1alpha2/fluentbit_types.go | 7 +- pkg/apiserver/devops/project_credential.go | 165 --- pkg/apiserver/openpitrix/attachments.go | 54 + pkg/apiserver/tenant/tenant.go | 328 +++++ pkg/gojenkins/_tests/build_history.txt | 4 - pkg/gojenkins/_tests/job.xml | 27 - pkg/gojenkins/artifact.go | 118 -- pkg/gojenkins/build_history.go | 109 -- pkg/gojenkins/credential.go | 225 ---- pkg/gojenkins/executor.go | 44 - pkg/gojenkins/fingerprint.go | 95 -- pkg/gojenkins/jenkins.go | 1083 ----------------- pkg/gojenkins/label.go | 62 - pkg/gojenkins/node.go | 230 ---- pkg/gojenkins/plugin.go | 75 -- pkg/gojenkins/queue.go | 158 --- pkg/gojenkins/utils.go | 61 - pkg/gojenkins/utils/utils.go | 21 - pkg/gojenkins/views.go | 94 -- .../devops => kapis/devops/v1alpha2}/OWNERS | 0 .../devops/v1alpha2}/devops.go | 420 +++---- pkg/kapis/devops/v1alpha2/handler.go | 42 + .../devops/v1alpha2}/member.go | 41 +- pkg/kapis/devops/v1alpha2/pipeline_sonar.go | 49 + .../devops/v1alpha2}/project.go | 16 +- .../devops/v1alpha2/project_credential.go | 122 ++ .../devops/v1alpha2}/project_pipeline.go | 67 +- pkg/kapis/devops/v1alpha2/register.go | 714 ++++++----- .../devops/v1alpha2}/s2ibinary.go | 14 +- pkg/kapis/tenant/v1alpha2/devops.go | 154 +++ pkg/kapis/tenant/v1alpha2/handler.go | 81 +- pkg/kapis/tenant/v1alpha2/register.go | 8 +- pkg/kapis/terminal/v1alpha2/handler.go | 52 +- pkg/models/devops/common.go | 301 ----- pkg/models/devops/devops.go | 990 ++++----------- pkg/models/devops/devops_test.go | 121 +- pkg/models/devops/membership.go | 24 +- pkg/models/devops/project_credential.go | 60 - .../devops/project_credential_handler.go | 392 ++---- pkg/models/devops/project_handler.go | 57 +- pkg/models/devops/project_member_handler.go | 302 ++--- pkg/models/devops/project_pipeline_handler.go | 300 +---- .../devops/project_pipeline_sonar_handler.go | 122 ++ pkg/models/devops/s2ibinary_handler.go | 99 +- pkg/models/devops/s2ibinary_handler_test.go | 114 ++ pkg/models/devops/urlconfig.go | 62 - pkg/models/iam/im.go | 4 +- pkg/models/resources/v1alpha3/interface.go | 2 +- .../v1alpha3/namespace/namespaces.go | 98 +- pkg/models/tenant/devops.go | 237 ++-- pkg/models/tenant/tenant.go | 9 +- pkg/models/terminal/terminal.go | 6 +- pkg/server/config/config.go | 6 +- pkg/server/config/config_test.go | 4 +- pkg/server/errors/errors.go | 8 + pkg/simple/client/cache/cache.go | 2 +- pkg/simple/client/cache/simple_cache.go | 20 +- .../client/devops}/OWNERS | 0 pkg/simple/client/devops/build.go | 115 ++ pkg/simple/client/devops/credential.go | 77 ++ pkg/simple/client/devops/fake/fakedevops.go | 206 ++++ pkg/simple/client/devops/interface.go | 16 +- .../client/devops/jenkins}/README.md | 0 .../client/devops/jenkins}/build.go | 158 +-- .../client/devops/jenkins}/constants.go | 2 +- .../client/devops/jenkins/credential.go | 341 ++++++ .../client/devops/{ => jenkins}/devops.go | 13 +- .../client/devops/jenkins/devops_test.go | 40 + .../client/devops/jenkins}/folder.go | 4 +- pkg/simple/client/devops/jenkins/jenkins.go | 803 ++++++++++++ .../client/devops/jenkins}/job.go | 35 +- pkg/simple/client/devops/jenkins/member.go | 322 +++++ .../client/devops/{ => jenkins}/options.go | 2 +- pkg/simple/client/devops/jenkins/pipeline.go | 722 +++++++++++ .../devops/jenkins/pipeline_internal.go} | 517 +++----- .../devops/jenkins/pipeline_internal_test.go} | 142 +-- .../jenkins}/pipeline_model_converter.go | 2 +- pkg/simple/client/devops/jenkins/project.go | 109 ++ .../client/devops/jenkins/project_pipeline.go | 165 +++ .../client/devops/jenkins/pure_request.go | 54 + .../client/devops/jenkins}/request.go | 2 +- .../client/devops/jenkins}/role.go | 2 +- pkg/simple/client/devops/jenkins/utils.go | 146 +++ pkg/simple/client/devops/member.go | 48 + .../client/devops/pipeline.go} | 114 +- pkg/simple/client/devops/project.go | 8 + pkg/simple/client/devops/project_pipeline.go | 144 +++ pkg/simple/client/factory.go | 52 +- pkg/simple/client/monitoring/interface.go | 19 +- pkg/simple/client/monitoring/prometheus.go | 22 +- .../client/monitoring/prometheus_options.go | 40 +- pkg/simple/client/prometheus/prometheus.go | 1 - pkg/simple/client/s3/fake/fakes3.go | 43 + pkg/simple/client/s3/fake/fakes3_test.go | 52 + pkg/simple/client/s3/interface.go | 6 +- pkg/simple/client/s3/s3.go | 36 +- pkg/simple/client/sonarqube/interface.go | 68 +- 101 files changed, 6359 insertions(+), 6408 deletions(-) delete mode 100644 pkg/apiserver/devops/project_credential.go create mode 100644 pkg/apiserver/openpitrix/attachments.go create mode 100644 pkg/apiserver/tenant/tenant.go delete mode 100644 pkg/gojenkins/_tests/build_history.txt delete mode 100644 pkg/gojenkins/_tests/job.xml delete mode 100644 pkg/gojenkins/artifact.go delete mode 100644 pkg/gojenkins/build_history.go delete mode 100644 pkg/gojenkins/credential.go delete mode 100644 pkg/gojenkins/executor.go delete mode 100644 pkg/gojenkins/fingerprint.go delete mode 100644 pkg/gojenkins/jenkins.go delete mode 100644 pkg/gojenkins/label.go delete mode 100644 pkg/gojenkins/node.go delete mode 100644 pkg/gojenkins/plugin.go delete mode 100644 pkg/gojenkins/queue.go delete mode 100644 pkg/gojenkins/utils.go delete mode 100644 pkg/gojenkins/utils/utils.go delete mode 100644 pkg/gojenkins/views.go rename pkg/{apiserver/devops => kapis/devops/v1alpha2}/OWNERS (100%) rename pkg/{apiserver/devops => kapis/devops/v1alpha2}/devops.go (55%) rename pkg/{apiserver/devops => kapis/devops/v1alpha2}/member.go (73%) create mode 100644 pkg/kapis/devops/v1alpha2/pipeline_sonar.go rename pkg/{apiserver/devops => kapis/devops/v1alpha2}/project.go (76%) create mode 100644 pkg/kapis/devops/v1alpha2/project_credential.go rename pkg/{apiserver/devops => kapis/devops/v1alpha2}/project_pipeline.go (53%) rename pkg/{apiserver/devops => kapis/devops/v1alpha2}/s2ibinary.go (82%) create mode 100644 pkg/kapis/tenant/v1alpha2/devops.go create mode 100644 pkg/models/devops/project_pipeline_sonar_handler.go create mode 100644 pkg/models/devops/s2ibinary_handler_test.go delete mode 100644 pkg/models/devops/urlconfig.go rename pkg/{gojenkins => simple/client/devops}/OWNERS (100%) create mode 100644 pkg/simple/client/devops/build.go create mode 100644 pkg/simple/client/devops/credential.go create mode 100644 pkg/simple/client/devops/fake/fakedevops.go rename pkg/{gojenkins => simple/client/devops/jenkins}/README.md (100%) rename pkg/{gojenkins => simple/client/devops/jenkins}/build.go (74%) rename pkg/{gojenkins => simple/client/devops/jenkins}/constants.go (96%) create mode 100644 pkg/simple/client/devops/jenkins/credential.go rename pkg/simple/client/devops/{ => jenkins}/devops.go (81%) create mode 100644 pkg/simple/client/devops/jenkins/devops_test.go rename pkg/{gojenkins => simple/client/devops/jenkins}/folder.go (94%) create mode 100644 pkg/simple/client/devops/jenkins/jenkins.go rename pkg/{gojenkins => simple/client/devops/jenkins}/job.go (93%) create mode 100644 pkg/simple/client/devops/jenkins/member.go rename pkg/simple/client/devops/{ => jenkins}/options.go (99%) create mode 100644 pkg/simple/client/devops/jenkins/pipeline.go rename pkg/{models/devops/project_pipeline.go => simple/client/devops/jenkins/pipeline_internal.go} (59%) rename pkg/{models/devops/project_pipeline_test.go => simple/client/devops/jenkins/pipeline_internal_test.go} (82%) rename pkg/{gojenkins => simple/client/devops/jenkins}/pipeline_model_converter.go (99%) create mode 100644 pkg/simple/client/devops/jenkins/project.go create mode 100644 pkg/simple/client/devops/jenkins/project_pipeline.go create mode 100644 pkg/simple/client/devops/jenkins/pure_request.go rename pkg/{gojenkins => simple/client/devops/jenkins}/request.go (99%) rename pkg/{gojenkins => simple/client/devops/jenkins}/role.go (99%) create mode 100644 pkg/simple/client/devops/jenkins/utils.go create mode 100644 pkg/simple/client/devops/member.go rename pkg/{models/devops/json.go => simple/client/devops/pipeline.go} (92%) create mode 100644 pkg/simple/client/devops/project.go create mode 100644 pkg/simple/client/devops/project_pipeline.go create mode 100644 pkg/simple/client/s3/fake/fakes3.go create mode 100644 pkg/simple/client/s3/fake/fakes3_test.go diff --git a/cmd/controller-manager/app/options/options.go b/cmd/controller-manager/app/options/options.go index 89b3703a..7da45f10 100644 --- a/cmd/controller-manager/app/options/options.go +++ b/cmd/controller-manager/app/options/options.go @@ -7,7 +7,7 @@ import ( cliflag "k8s.io/component-base/cli/flag" "k8s.io/klog" 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/openpitrix" "kubesphere.io/kubesphere/pkg/simple/client/s3" @@ -17,7 +17,7 @@ import ( type KubeSphereControllerManagerOptions struct { KubernetesOptions *k8s.KubernetesOptions - DevopsOptions *devops.Options + DevopsOptions *jenkins.Options S3Options *s3.Options OpenPitrixOptions *openpitrix.Options @@ -27,7 +27,7 @@ type KubeSphereControllerManagerOptions struct { func NewKubeSphereControllerManagerOptions() *KubeSphereControllerManagerOptions { s := &KubeSphereControllerManagerOptions{ KubernetesOptions: k8s.NewKubernetesOptions(), - DevopsOptions: devops.NewDevopsOptions(), + DevopsOptions: jenkins.NewDevopsOptions(), S3Options: s3.NewS3Options(), OpenPitrixOptions: openpitrix.NewOpenPitrixOptions(), LeaderElection: &leaderelection.LeaderElectionConfig{ diff --git a/cmd/ks-apiserver/app/options/options.go b/cmd/ks-apiserver/app/options/options.go index 66d02c95..5cbcb660 100644 --- a/cmd/ks-apiserver/app/options/options.go +++ b/cmd/ks-apiserver/app/options/options.go @@ -5,7 +5,7 @@ import ( cliflag "k8s.io/component-base/cli/flag" "k8s.io/klog" 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" "kubesphere.io/kubesphere/pkg/simple/client/k8s" "kubesphere.io/kubesphere/pkg/simple/client/mysql" @@ -21,7 +21,7 @@ type ServerRunOptions struct { ConfigFile string GenericServerRunOptions *genericoptions.ServerRunOptions KubernetesOptions *k8s.KubernetesOptions - DevopsOptions *devops.Options + DevopsOptions *jenkins.Options SonarQubeOptions *sonarqube.Options ServiceMeshOptions *servicemesh.Options MySQLOptions *mysql.Options @@ -36,7 +36,7 @@ func NewServerRunOptions() *ServerRunOptions { s := ServerRunOptions{ GenericServerRunOptions: genericoptions.NewServerRunOptions(), KubernetesOptions: k8s.NewKubernetesOptions(), - DevopsOptions: devops.NewDevopsOptions(), + DevopsOptions: jenkins.NewDevopsOptions(), SonarQubeOptions: sonarqube.NewSonarQubeOptions(), ServiceMeshOptions: servicemesh.NewServiceMeshOptions(), MySQLOptions: mysql.NewMySQLOptions(), diff --git a/go.mod b/go.mod index e55e0cf8..9cf07cb0 100644 --- a/go.mod +++ b/go.mod @@ -99,7 +99,7 @@ require ( k8s.io/component-base v0.17.0 k8s.io/klog v1.0.0 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 openpitrix.io/iam v0.1.0 // indirect openpitrix.io/openpitrix v0.4.1-0.20190920134345-4d2be6e4965c diff --git a/go.sum b/go.sum index 3c4b61c5..40d948b7 100644 --- a/go.sum +++ b/go.sum @@ -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/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ= 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/go.mod h1:MB6lktGJrhw8PrUyiEoblNEGEQ+RzHPF078ddwwvV3Y= 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/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM= 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/go.mod h1:+tQajlRqAUrPI7DOSpB0XAqZYtQakVtB7wXkRAgjxjQ= 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/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/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/go.mod h1:93vsz/8Wt4joVM7c2AVqh+YRMiUSc14yDtF28KmMOgQ= 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/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/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/go.mod h1:yN2Sb0lFhZJUdVvtELVWefmrXpuZESvPmqwoZc+/fpc= 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/protobuf v1.3.2 h1:6nsPYzhq5kReh6QImI3k5qWzO4PEbvbIW2cwSfR/6xs= 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/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= 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/lib/pq v1.2.0 h1:LXpIM/LZ5xGFhOpXAQUIMM1HdyqzVYM13zNdjCEEcA0= 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/go.mod h1:PpMmPfPKO9nKJ/psF49ESTAGQSdfXxlg1otPbEB2nOw= 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/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-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/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= 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/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/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/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= 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/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/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/go.mod h1:4L/zQOBkEf4pArQJ+CMk1/5xjA30B5oyWv+Bzb44DOw= 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/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/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/go.mod h1:sZAwmy6armz5eXlNoLmJcl4F1QuKu7sr+mFQ0byX7Ew= 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-tools v0.2.4 h1:la1h46EzElvWefWLqfsXrnsO3lZjpkI0asTpX6h8PLA= 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/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/go.mod h1:ToQrwSC3s8Xf/lADdZp3Mktcql9CG0UAmdJG9th5i0w= sigs.k8s.io/yaml v1.1.0 h1:4A07+ZFc2wgJwo8YNlQpr1rVlgUDlxXHhPJciaPY5gs= 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= diff --git a/pkg/apis/logging/v1alpha2/fluentbit_types.go b/pkg/apis/logging/v1alpha2/fluentbit_types.go index cef27969..440d85df 100644 --- a/pkg/apis/logging/v1alpha2/fluentbit_types.go +++ b/pkg/apis/logging/v1alpha2/fluentbit_types.go @@ -70,9 +70,8 @@ type FluentBitList struct { Items []FluentBit `json:"items"` } - type FluentbitOutputsResult struct { - Status int `json:"status" description:"response status"` - Error string `json:"error,omitempty" description:"debug information"` + Status int `json:"status" description:"response status"` + Error string `json:"error,omitempty" description:"debug information"` Outputs []OutputPlugin `json:"outputs,omitempty" description:"array of fluent bit output plugins"` -} \ No newline at end of file +} diff --git a/pkg/apiserver/devops/project_credential.go b/pkg/apiserver/devops/project_credential.go deleted file mode 100644 index 1ad03267..00000000 --- a/pkg/apiserver/devops/project_credential.go +++ /dev/null @@ -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 -} diff --git a/pkg/apiserver/openpitrix/attachments.go b/pkg/apiserver/openpitrix/attachments.go new file mode 100644 index 00000000..908a95df --- /dev/null +++ b/pkg/apiserver/openpitrix/attachments.go @@ -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) +} diff --git a/pkg/apiserver/tenant/tenant.go b/pkg/apiserver/tenant/tenant.go new file mode 100644 index 00000000..8f6431a5 --- /dev/null +++ b/pkg/apiserver/tenant/tenant.go @@ -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 +} diff --git a/pkg/gojenkins/_tests/build_history.txt b/pkg/gojenkins/_tests/build_history.txt deleted file mode 100644 index b9c777d3..00000000 --- a/pkg/gojenkins/_tests/build_history.txt +++ /dev/null @@ -1,4 +0,0 @@ - - - -
\ No newline at end of file diff --git a/pkg/gojenkins/_tests/job.xml b/pkg/gojenkins/_tests/job.xml deleted file mode 100644 index 76a65ec2..00000000 --- a/pkg/gojenkins/_tests/job.xml +++ /dev/null @@ -1,27 +0,0 @@ - - - - Some Job Description - false - - - - - params1 - description - defaultVal - - - - - - true - false - false - false - - false - - - - diff --git a/pkg/gojenkins/artifact.go b/pkg/gojenkins/artifact.go deleted file mode 100644 index 36494065..00000000 --- a/pkg/gojenkins/artifact.go +++ /dev/null @@ -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)) -} diff --git a/pkg/gojenkins/build_history.go b/pkg/gojenkins/build_history.go deleted file mode 100644 index 2e7cae11..00000000 --- a/pkg/gojenkins/build_history.go +++ /dev/null @@ -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) - // Failed > Console Output - 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) - // - if string(tn) == "td" { - if hasCSSClass(a, "build-row-cell") { - buildRowCellDepth = depth - curBuild = &History{} - builds = append(builds, curBuild) - } - } - // #227 - 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 - } - } - } - } - //
...
- 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 -} diff --git a/pkg/gojenkins/credential.go b/pkg/gojenkins/credential.go deleted file mode 100644 index da41bb90..00000000 --- a/pkg/gojenkins/credential.go +++ /dev/null @@ -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, - } -} diff --git a/pkg/gojenkins/executor.go b/pkg/gojenkins/executor.go deleted file mode 100644 index e5ba6f6c..00000000 --- a/pkg/gojenkins/executor.go +++ /dev/null @@ -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"` -} diff --git a/pkg/gojenkins/fingerprint.go b/pkg/gojenkins/fingerprint.go deleted file mode 100644 index 3afaa8b6..00000000 --- a/pkg/gojenkins/fingerprint.go +++ /dev/null @@ -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 -} diff --git a/pkg/gojenkins/jenkins.go b/pkg/gojenkins/jenkins.go deleted file mode 100644 index feaf49c6..00000000 --- a/pkg/gojenkins/jenkins.go +++ /dev/null @@ -1,1083 +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. - -// Gojenkins is a Jenkins Client in Go, that exposes the jenkins REST api in a more developer friendly way. -package gojenkins - -import ( - "encoding/json" - "errors" - "fmt" - "log" - "net/http" - "os" - "reflect" - "strconv" - "strings" -) - -// Basic Authentication -type BasicAuth struct { - Username string - Password string -} - -type Jenkins struct { - Server string - Version string - Raw *ExecutorResponse - 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() - - // Check Connection - j.Raw = new(ExecutorResponse) - rsp, err := j.Requester.GetJSON("/", j.Raw, 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) -} - -// Get Basic Information About Jenkins -func (j *Jenkins) Info() (*ExecutorResponse, error) { - _, err := j.Requester.Get("/", j.Raw, nil) - - if err != nil { - return nil, err - } - return j.Raw, nil -} - -// Create a new Node -// Can be JNLPLauncher or SSHLauncher -// Example : jenkins.CreateNode("nodeName", 1, "Description", "/var/lib/jenkins", "jdk8 docker", map[string]string{"method": "JNLPLauncher"}) -// By Default JNLPLauncher is created -// Multiple labels should be separated by blanks -func (j *Jenkins) CreateNode(name string, numExecutors int, description string, remoteFS string, label string, options ...interface{}) (*Node, error) { - params := map[string]string{"method": "JNLPLauncher"} - - if len(options) > 0 { - params, _ = options[0].(map[string]string) - } - - if _, ok := params["method"]; !ok { - params["method"] = "JNLPLauncher" - } - - method := params["method"] - var launcher map[string]string - switch method { - case "": - fallthrough - case "JNLPLauncher": - launcher = map[string]string{"stapler-class": "hudson.slaves.JNLPLauncher"} - case "SSHLauncher": - launcher = map[string]string{ - "stapler-class": "hudson.plugins.sshslaves.SSHLauncher", - "$class": "hudson.plugins.sshslaves.SSHLauncher", - "host": params["host"], - "port": params["port"], - "credentialsId": params["credentialsId"], - "jvmOptions": params["jvmOptions"], - "javaPath": params["javaPath"], - "prefixStartSlaveCmd": params["prefixStartSlaveCmd"], - "suffixStartSlaveCmd": params["suffixStartSlaveCmd"], - "maxNumRetries": params["maxNumRetries"], - "retryWaitTime": params["retryWaitTime"], - "lanuchTimeoutSeconds": params["lanuchTimeoutSeconds"], - "type": "hudson.slaves.DumbSlave", - "stapler-class-bag": "true"} - default: - return nil, errors.New("launcher method not supported") - } - - node := &Node{Jenkins: j, Raw: new(NodeResponse), Base: "/computer/" + name} - NODE_TYPE := "hudson.slaves.DumbSlave$DescriptorImpl" - MODE := "NORMAL" - qr := map[string]string{ - "name": name, - "type": NODE_TYPE, - "json": makeJson(map[string]interface{}{ - "name": name, - "nodeDescription": description, - "remoteFS": remoteFS, - "numExecutors": numExecutors, - "mode": MODE, - "type": NODE_TYPE, - "labelString": label, - "retentionsStrategy": map[string]string{"stapler-class": "hudson.slaves.RetentionStrategy$Always"}, - "nodeProperties": map[string]string{"stapler-class-bag": "true"}, - "launcher": launcher, - }), - } - - resp, err := j.Requester.Post("/computer/doCreateItem", nil, nil, qr) - - if err != nil { - return nil, err - } - - if resp.StatusCode < 400 { - _, err := node.Poll() - if err != nil { - return nil, err - } - return node, nil - } - return nil, errors.New(strconv.Itoa(resp.StatusCode)) -} - -// Delete a Jenkins slave node -func (j *Jenkins) DeleteNode(name string) (bool, error) { - node := Node{Jenkins: j, Raw: new(NodeResponse), Base: "/computer/" + name} - return node.Delete() -} - -// 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("", "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("","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) GetNode(name string) (*Node, error) { - node := Node{Jenkins: j, Raw: new(NodeResponse), Base: "/computer/" + name} - status, err := node.Poll() - if err != nil { - return nil, err - } - if status == 200 { - return &node, nil - } - return nil, errors.New("No node found") -} - -func (j *Jenkins) GetLabel(name string) (*Label, error) { - label := Label{Jenkins: j, Raw: new(LabelResponse), Base: "/label/" + name} - status, err := label.Poll() - if err != nil { - return nil, err - } - if status == 200 { - return &label, nil - } - return nil, errors.New("No label found") -} - -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) GetSubJob(parentId string, childId string) (*Job, error) { - job := Job{Jenkins: j, Raw: new(JobResponse), Base: "/job/" + parentId + "/job/" + childId} - status, err := job.Poll() - if err != nil { - return nil, fmt.Errorf("trouble polling job: %v", 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)) -} - -func (j *Jenkins) GetAllNodes() ([]*Node, error) { - computers := new(Computers) - - qr := map[string]string{ - "depth": "1", - } - - _, err := j.Requester.GetJSON("/computer", computers, qr) - if err != nil { - return nil, err - } - - nodes := make([]*Node, len(computers.Computers)) - for i, node := range computers.Computers { - nodes[i] = &Node{Jenkins: j, Raw: node, Base: "/computer/" + node.DisplayName} - } - - return nodes, nil -} - -// 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) GetAllBuildIds(job string) ([]JobBuild, error) { - jobObj, err := j.GetJob(job) - if err != nil { - return nil, err - } - return jobObj.GetAllBuildIds() -} - -func (j *Jenkins) GetAllBuildStatus(jobId string) ([]JobBuildStatus, error) { - job, err := j.GetJob(jobId) - if err != nil { - return nil, err - } - return job.GetAllBuildStatus() -} - -// Get Only Array of Job Names, Color, URL -// Does not query each single Job. -func (j *Jenkins) GetAllJobNames() ([]InnerJob, error) { - exec := Executor{Raw: new(ExecutorResponse), Jenkins: j} - _, err := j.Requester.GetJSON("/", exec.Raw, nil) - - if err != nil { - return nil, err - } - - return exec.Raw.Jobs, nil -} - -// Get All Possible Job Objects. -// Each job will be queried. -func (j *Jenkins) GetAllJobs() ([]*Job, error) { - exec := Executor{Raw: new(ExecutorResponse), Jenkins: j} - _, err := j.Requester.GetJSON("/", exec.Raw, nil) - - if err != nil { - return nil, err - } - - jobs := make([]*Job, len(exec.Raw.Jobs)) - for i, job := range exec.Raw.Jobs { - ji, err := j.GetJob(job.Name) - if err != nil { - return nil, err - } - jobs[i] = ji - } - return jobs, nil -} - -// Returns a Queue -func (j *Jenkins) GetQueue() (*Queue, error) { - q := &Queue{Jenkins: j, Raw: new(queueResponse), Base: j.GetQueueUrl()} - _, err := q.Poll() - if err != nil { - return nil, err - } - return q, nil -} - -func (j *Jenkins) GetQueueUrl() string { - return "/queue" -} - -// Get Artifact data by Hash -func (j *Jenkins) GetArtifactData(id string) (*FingerPrintResponse, error) { - fp := FingerPrint{Jenkins: j, Base: "/fingerprint/", Id: id, Raw: new(FingerPrintResponse)} - return fp.GetInfo() -} - -// Returns the list of all plugins installed on the Jenkins server. -// You can supply depth parameter, to limit how much data is returned. -func (j *Jenkins) GetPlugins(depth int) (*Plugins, error) { - p := Plugins{Jenkins: j, Raw: new(PluginResponse), Base: "/pluginManager", Depth: depth} - _, err := p.Poll() - if err != nil { - return nil, err - } - return &p, nil -} - -// Check if the plugin is installed on the server. -// Depth level 1 is used. If you need to go deeper, you can use GetPlugins, and iterate through them. -func (j *Jenkins) HasPlugin(name string) (*Plugin, error) { - p, err := j.GetPlugins(1) - - if err != nil { - return nil, err - } - return p.Contains(name), nil -} - -// Verify FingerPrint -func (j *Jenkins) ValidateFingerPrint(id string) (bool, error) { - fp := FingerPrint{Jenkins: j, Base: "/fingerprint/", Id: id, Raw: new(FingerPrintResponse)} - valid, err := fp.Valid() - if err != nil { - return false, err - } - if valid { - return true, nil - } - return false, nil -} - -func (j *Jenkins) GetView(name string) (*View, error) { - url := "/view/" + name - view := View{Jenkins: j, Raw: new(ViewResponse), Base: url} - _, err := view.Poll() - if err != nil { - return nil, err - } - return &view, nil -} - -func (j *Jenkins) GetAllViews() ([]*View, error) { - _, err := j.Poll() - if err != nil { - return nil, err - } - views := make([]*View, len(j.Raw.Views)) - for i, v := range j.Raw.Views { - views[i], _ = j.GetView(v.Name) - } - return views, nil -} - -// Create View -// First Parameter - name of the View -// Second parameter - Type -// Possible Types: -// gojenkins.LIST_VIEW -// gojenkins.NESTED_VIEW -// gojenkins.MY_VIEW -// gojenkins.DASHBOARD_VIEW -// gojenkins.PIPELINE_VIEW -// Example: jenkins.CreateView("newView",gojenkins.LIST_VIEW) -func (j *Jenkins) CreateView(name string, viewType string) (*View, error) { - view := &View{Jenkins: j, Raw: new(ViewResponse), Base: "/view/" + name} - endpoint := "/createView" - data := map[string]string{ - "name": name, - "mode": viewType, - "Submit": "OK", - "json": makeJson(map[string]string{ - "name": name, - "mode": viewType, - }), - } - r, err := j.Requester.Post(endpoint, nil, view.Raw, data) - - if err != nil { - return nil, err - } - - if r.StatusCode == 200 { - return j.GetView(name) - } - return nil, errors.New(strconv.Itoa(r.StatusCode)) -} - -func (j *Jenkins) Poll() (int, error) { - resp, err := j.Requester.GetJSON("/", j.Raw, nil) - if err != nil { - return 0, err - } - return resp.StatusCode, nil -} - -// Create a ssh credentials -// return credentials id -func (j *Jenkins) CreateSshCredential(id, username, passphrase, privateKey, description string) (*string, error) { - requestStruct := NewCreateSshCredentialRequest(id, username, passphrase, privateKey, description) - param := map[string]string{"json": makeJson(requestStruct)} - responseString := "" - response, err := j.Requester.Post("/credentials/store/system/domain/_/createCredentials", - nil, &responseString, param) - if err != nil { - return nil, err - } - if response.StatusCode != http.StatusOK { - return nil, errors.New(strconv.Itoa(response.StatusCode)) - } - return &requestStruct.Credentials.Id, nil -} - -func (j *Jenkins) CreateUsernamePasswordCredential(id, username, password, description string) (*string, error) { - requestStruct := NewCreateUsernamePasswordRequest(id, username, password, description) - param := map[string]string{"json": makeJson(requestStruct)} - responseString := "" - response, err := j.Requester.Post("/credentials/store/system/domain/_/createCredentials", - nil, &responseString, param) - if err != nil { - return nil, err - } - if response.StatusCode != http.StatusOK { - return nil, errors.New(strconv.Itoa(response.StatusCode)) - } - return &requestStruct.Credentials.Id, nil -} - -func (j *Jenkins) CreateSshCredentialInFolder(domain, id, username, passphrase, privateKey, description string, folders ...string) (*string, error) { - requestStruct := NewCreateSshCredentialRequest(id, username, passphrase, privateKey, description) - param := map[string]string{"json": makeJson(requestStruct)} - responseString := "" - prePath := "" - if domain == "" { - domain = "_" - } - if len(folders) == 0 { - return nil, fmt.Errorf("folder name shoud not be nil") - } - for _, folder := range folders { - prePath = prePath + fmt.Sprintf("/job/%s", folder) - } - response, err := j.Requester.Post(prePath+ - fmt.Sprintf("/credentials/store/folder/domain/%s/createCredentials", domain), - nil, &responseString, param) - if err != nil { - return nil, err - } - if response.StatusCode != http.StatusOK { - return nil, errors.New(strconv.Itoa(response.StatusCode)) - } - return &requestStruct.Credentials.Id, nil -} - -func (j *Jenkins) CreateUsernamePasswordCredentialInFolder(domain, id, username, password, description string, folders ...string) (*string, error) { - requestStruct := NewCreateUsernamePasswordRequest(id, username, password, description) - param := map[string]string{"json": makeJson(requestStruct)} - responseString := "" - prePath := "" - if domain == "" { - domain = "_" - } - if len(folders) == 0 { - return nil, fmt.Errorf("folder name shoud not be nil") - } - for _, folder := range folders { - prePath = prePath + fmt.Sprintf("/job/%s", folder) - } - response, err := j.Requester.Post(prePath+ - fmt.Sprintf("/credentials/store/folder/domain/%s/createCredentials", domain), - nil, &responseString, param) - if err != nil { - return nil, err - } - if response.StatusCode != http.StatusOK { - return nil, errors.New(strconv.Itoa(response.StatusCode)) - } - return &requestStruct.Credentials.Id, nil -} - -func (j *Jenkins) CreateSecretTextCredentialInFolder(domain, id, secret, description string, folders ...string) (*string, error) { - requestStruct := NewCreateSecretTextCredentialRequest(id, secret, description) - param := map[string]string{"json": makeJson(requestStruct)} - responseString := "" - prePath := "" - if domain == "" { - domain = "_" - } - if len(folders) == 0 { - return nil, fmt.Errorf("folder name shoud not be nil") - } - for _, folder := range folders { - prePath = prePath + fmt.Sprintf("/job/%s", folder) - } - response, err := j.Requester.Post(prePath+ - fmt.Sprintf("/credentials/store/folder/domain/%s/createCredentials", domain), - nil, &responseString, param) - if err != nil { - return nil, err - } - if response.StatusCode != http.StatusOK { - return nil, errors.New(strconv.Itoa(response.StatusCode)) - } - return &requestStruct.Credentials.Id, nil -} - -func (j *Jenkins) CreateKubeconfigCredentialInFolder(domain, id, content, description string, folders ...string) (*string, error) { - requestStruct := NewCreateKubeconfigCredentialRequest(id, content, description) - param := map[string]string{"json": makeJson(requestStruct)} - responseString := "" - prePath := "" - if domain == "" { - domain = "_" - } - if len(folders) == 0 { - return nil, fmt.Errorf("folder name shoud not be nil") - } - for _, folder := range folders { - prePath = prePath + fmt.Sprintf("/job/%s", folder) - } - response, err := j.Requester.Post(prePath+ - fmt.Sprintf("/credentials/store/folder/domain/%s/createCredentials", domain), - nil, &responseString, param) - if err != nil { - return nil, err - } - if response.StatusCode != http.StatusOK { - return nil, errors.New(strconv.Itoa(response.StatusCode)) - } - return &requestStruct.Credentials.Id, nil -} - -func (j *Jenkins) UpdateSshCredentialInFolder(domain, id, username, passphrase, privateKey, description string, folders ...string) (*string, error) { - requestStruct := NewSshCredential(id, username, passphrase, privateKey, description) - param := map[string]string{"json": makeJson(requestStruct)} - prePath := "" - if domain == "" { - domain = "_" - } - if len(folders) == 0 { - return nil, fmt.Errorf("folder name shoud not be nil") - } - for _, folder := range folders { - prePath = prePath + fmt.Sprintf("/job/%s", folder) - } - response, err := j.Requester.Post(prePath+ - fmt.Sprintf("/credentials/store/folder/domain/%s/credential/%s/updateSubmit", domain, id), - nil, nil, param) - if err != nil { - return nil, err - } - if response.StatusCode != http.StatusOK { - return nil, errors.New(strconv.Itoa(response.StatusCode)) - } - return &id, nil -} - -func (j *Jenkins) UpdateUsernamePasswordCredentialInFolder(domain, id, username, password, description string, folders ...string) (*string, error) { - requestStruct := NewUsernamePasswordCredential(id, username, password, description) - param := map[string]string{"json": makeJson(requestStruct)} - prePath := "" - if domain == "" { - domain = "_" - } - if len(folders) == 0 { - return nil, fmt.Errorf("folder name shoud not be nil") - } - for _, folder := range folders { - prePath = prePath + fmt.Sprintf("/job/%s", folder) - } - response, err := j.Requester.Post(prePath+ - fmt.Sprintf("/credentials/store/folder/domain/%s/credential/%s/updateSubmit", domain, id), - nil, nil, param) - if err != nil { - return nil, err - } - if response.StatusCode != http.StatusOK { - return nil, errors.New(strconv.Itoa(response.StatusCode)) - } - return &id, nil -} - -func (j *Jenkins) UpdateSecretTextCredentialInFolder(domain, id, secret, description string, folders ...string) (*string, error) { - requestStruct := NewSecretTextCredential(id, secret, description) - param := map[string]string{"json": makeJson(requestStruct)} - prePath := "" - if domain == "" { - domain = "_" - } - if len(folders) == 0 { - return nil, fmt.Errorf("folder name shoud not be nil") - } - for _, folder := range folders { - prePath = prePath + fmt.Sprintf("/job/%s", folder) - } - response, err := j.Requester.Post(prePath+ - fmt.Sprintf("/credentials/store/folder/domain/%s/credential/%s/updateSubmit", domain, id), - nil, nil, param) - if err != nil { - return nil, err - } - if response.StatusCode != http.StatusOK { - return nil, errors.New(strconv.Itoa(response.StatusCode)) - } - return &id, nil -} - -func (j *Jenkins) UpdateKubeconfigCredentialInFolder(domain, id, content, description string, folders ...string) (*string, error) { - requestStruct := NewKubeconfigCredential(id, content, description) - param := map[string]string{"json": makeJson(requestStruct)} - prePath := "" - if domain == "" { - domain = "_" - } - if len(folders) == 0 { - return nil, fmt.Errorf("folder name shoud not be nil") - } - for _, folder := range folders { - prePath = prePath + fmt.Sprintf("/job/%s", folder) - } - response, err := j.Requester.Post(prePath+ - fmt.Sprintf("/credentials/store/folder/domain/%s/credential/%s/updateSubmit", domain, id), - nil, nil, param) - if err != nil { - return nil, err - } - if response.StatusCode != http.StatusOK { - return nil, errors.New(strconv.Itoa(response.StatusCode)) - } - return &id, nil -} - -func (j *Jenkins) GetCredentialInFolder(domain, id string, folders ...string) (*CredentialResponse, error) { - responseStruct := &CredentialResponse{} - prePath := "" - if domain == "" { - domain = "_" - } - if len(folders) == 0 { - return nil, fmt.Errorf("folder name shoud not be nil") - } - for _, folder := range folders { - prePath = prePath + fmt.Sprintf("/job/%s", folder) - } - response, err := j.Requester.GetJSON(prePath+ - fmt.Sprintf("/credentials/store/folder/domain/%s/credential/%s", domain, 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 - return responseStruct, nil -} - -func (j *Jenkins) GetCredentialContentInFolder(domain, id string, folders ...string) (string, error) { - responseStruct := "" - prePath := "" - if domain == "" { - domain = "_" - } - if len(folders) == 0 { - return "", fmt.Errorf("folder name shoud not be nil") - } - for _, folder := range folders { - prePath = prePath + fmt.Sprintf("/job/%s", folder) - } - response, err := j.Requester.GetHtml(prePath+ - fmt.Sprintf("/credentials/store/folder/domain/%s/credential/%s/update", domain, id), - &responseStruct, nil) - if err != nil { - return "", err - } - if response.StatusCode != http.StatusOK { - return "", errors.New(strconv.Itoa(response.StatusCode)) - } - return responseStruct, nil -} - -func (j *Jenkins) GetCredentialsInFolder(domain string, folders ...string) ([]*CredentialResponse, error) { - prePath := "" - if len(folders) == 0 { - return nil, fmt.Errorf("folder name shoud not be nil") - } - for _, folder := range folders { - prePath = prePath + fmt.Sprintf("/job/%s", folder) - } - - if domain == "" { - var responseStruct = &struct { - Domains map[string]struct { - Credentials []*CredentialResponse `json:"credentials"` - } `json:"domains"` - }{} - response, err := j.Requester.GetJSON(prePath+ - "/credentials/store/folder/", - 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)) - } - responseArray := make([]*CredentialResponse, 0) - for domainName, domain := range responseStruct.Domains { - for _, credential := range domain.Credentials { - credential.Domain = domainName - responseArray = append(responseArray, credential) - } - } - return responseArray, nil - } - - var responseStruct = &struct { - Credentials []*CredentialResponse `json:"credentials"` - }{} - response, err := j.Requester.GetJSON(prePath+ - fmt.Sprintf("/credentials/store/folder/domain/%s", domain), - 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) DeleteCredentialInFolder(domain, id string, folders ...string) (*string, error) { - prePath := "" - if domain == "" { - domain = "_" - } - if len(folders) == 0 { - return nil, fmt.Errorf("folder name shoud not be nil") - } - for _, folder := range folders { - prePath = prePath + fmt.Sprintf("/job/%s", folder) - } - response, err := j.Requester.Post(prePath+ - fmt.Sprintf("/credentials/store/folder/domain/%s/credential/%s/doDelete", domain, 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 -} - -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) GetQueueItem(number int64) (*QueueItemResponse, error) { - responseItem := &QueueItemResponse{} - response, err := j.Requester.GetJSON(fmt.Sprintf("/queue/item/%s", strconv.FormatInt(number, 10)), - responseItem, nil) - if err != nil { - return nil, err - } - if response.StatusCode != http.StatusOK { - return nil, errors.New(strconv.Itoa(response.StatusCode)) - } - return responseItem, nil -} - -// 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 -} diff --git a/pkg/gojenkins/label.go b/pkg/gojenkins/label.go deleted file mode 100644 index a757b630..00000000 --- a/pkg/gojenkins/label.go +++ /dev/null @@ -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 -} diff --git a/pkg/gojenkins/node.go b/pkg/gojenkins/node.go deleted file mode 100644 index 14a6face..00000000 --- a/pkg/gojenkins/node.go +++ /dev/null @@ -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 -} diff --git a/pkg/gojenkins/plugin.go b/pkg/gojenkins/plugin.go deleted file mode 100644 index 5e44717d..00000000 --- a/pkg/gojenkins/plugin.go +++ /dev/null @@ -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 -} diff --git a/pkg/gojenkins/queue.go b/pkg/gojenkins/queue.go deleted file mode 100644 index e73c939b..00000000 --- a/pkg/gojenkins/queue.go +++ /dev/null @@ -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 -} diff --git a/pkg/gojenkins/utils.go b/pkg/gojenkins/utils.go deleted file mode 100644 index 92b71449..00000000 --- a/pkg/gojenkins/utils.go +++ /dev/null @@ -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)) -} diff --git a/pkg/gojenkins/utils/utils.go b/pkg/gojenkins/utils/utils.go deleted file mode 100644 index 93ad30b3..00000000 --- a/pkg/gojenkins/utils/utils.go +++ /dev/null @@ -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 -} diff --git a/pkg/gojenkins/views.go b/pkg/gojenkins/views.go deleted file mode 100644 index 99b80264..00000000 --- a/pkg/gojenkins/views.go +++ /dev/null @@ -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 -} diff --git a/pkg/apiserver/devops/OWNERS b/pkg/kapis/devops/v1alpha2/OWNERS similarity index 100% rename from pkg/apiserver/devops/OWNERS rename to pkg/kapis/devops/v1alpha2/OWNERS diff --git a/pkg/apiserver/devops/devops.go b/pkg/kapis/devops/v1alpha2/devops.go similarity index 55% rename from pkg/apiserver/devops/devops.go rename to pkg/kapis/devops/v1alpha2/devops.go index a4277c5e..d7aea16c 100644 --- a/pkg/apiserver/devops/devops.go +++ b/pkg/kapis/devops/v1alpha2/devops.go @@ -15,10 +15,9 @@ limitations under the License. */ -package devops +package v1alpha2 import ( - "encoding/json" "github.com/emicklei/go-restful" log "k8s.io/klog" "kubesphere.io/kubesphere/pkg/models/devops" @@ -28,328 +27,319 @@ import ( const jenkinsHeaderPre = "X-" -func GetPipeline(req *restful.Request, resp *restful.Response) { +func (h *ProjectPipelineHandler) GetPipeline(req *restful.Request, resp *restful.Response) { projectName := req.PathParameter("devops") pipelineName := req.PathParameter("pipeline") - res, err := devops.GetPipeline(projectName, pipelineName, req.Request) + res, err := h.devopsOperator.GetPipeline(projectName, pipelineName, req.Request) if err != nil { parseErr(err, resp) return } resp.Header().Set(restful.HEADER_ContentType, restful.MIME_JSON) - resp.Write(res) + resp.WriteAsJson(res) } -func SearchPipelines(req *restful.Request, resp *restful.Response) { - res, err := devops.SearchPipelines(req.Request) +func (h *ProjectPipelineHandler) ListPipelines(req *restful.Request, resp *restful.Response) { + res, err := h.devopsOperator.ListPipelines(req.Request) if err != nil { parseErr(err, resp) return } resp.Header().Set(restful.HEADER_ContentType, restful.MIME_JSON) - resp.Write(res) + resp.WriteAsJson(res) } -func SearchPipelineRuns(req *restful.Request, resp *restful.Response) { +func (h *ProjectPipelineHandler) GetPipelineRun(req *restful.Request, resp *restful.Response) { projectName := req.PathParameter("devops") pipelineName := req.PathParameter("pipeline") + runId := req.PathParameter("run") - res, err := devops.SearchPipelineRuns(projectName, pipelineName, req.Request) + res, err := h.devopsOperator.GetPipelineRun(projectName, pipelineName, runId, req.Request) if err != nil { parseErr(err, resp) return } resp.Header().Set(restful.HEADER_ContentType, restful.MIME_JSON) - resp.Write(res) + resp.WriteAsJson(res) } -func GetBranchPipelineRun(req *restful.Request, resp *restful.Response) { +func (h *ProjectPipelineHandler) ListPipelineRuns(req *restful.Request, resp *restful.Response) { projectName := req.PathParameter("devops") pipelineName := req.PathParameter("pipeline") - branchName := req.PathParameter("branch") - runId := req.PathParameter("run") - res, err := devops.GetBranchPipelineRun(projectName, pipelineName, branchName, runId, req.Request) + res, err := h.devopsOperator.ListPipelineRuns(projectName, pipelineName, req.Request) if err != nil { parseErr(err, resp) return } resp.Header().Set(restful.HEADER_ContentType, restful.MIME_JSON) - resp.Write(res) + resp.WriteAsJson(res) } -func GetPipelineRunNodesbyBranch(req *restful.Request, resp *restful.Response) { +func (h *ProjectPipelineHandler) StopPipeline(req *restful.Request, resp *restful.Response) { projectName := req.PathParameter("devops") pipelineName := req.PathParameter("pipeline") - branchName := req.PathParameter("branch") runId := req.PathParameter("run") - res, err := devops.GetPipelineRunNodesbyBranch(projectName, pipelineName, branchName, runId, req.Request) + res, err := h.devopsOperator.StopPipeline(projectName, pipelineName, runId, req.Request) if err != nil { parseErr(err, resp) return } resp.Header().Set(restful.HEADER_ContentType, restful.MIME_JSON) - resp.Write(res) + resp.WriteAsJson(res) } -func GetBranchStepLog(req *restful.Request, resp *restful.Response) { +func (h *ProjectPipelineHandler) ReplayPipeline(req *restful.Request, resp *restful.Response) { projectName := req.PathParameter("devops") pipelineName := req.PathParameter("pipeline") - branchName := req.PathParameter("branch") runId := req.PathParameter("run") - nodeId := req.PathParameter("node") - stepId := req.PathParameter("step") - - res, header, err := devops.GetBranchStepLog(projectName, pipelineName, branchName, runId, nodeId, stepId, req.Request) + res, err := h.devopsOperator.ReplayPipeline(projectName, pipelineName, runId, req.Request) if err != nil { parseErr(err, resp) return } - for k, v := range header { - if strings.HasPrefix(k, jenkinsHeaderPre) { - resp.AddHeader(k, v[0]) - } - } - resp.Write(res) + + resp.Header().Set(restful.HEADER_ContentType, restful.MIME_JSON) + resp.WriteAsJson(res) } -func GetStepLog(req *restful.Request, resp *restful.Response) { +func (h *ProjectPipelineHandler) RunPipeline(req *restful.Request, resp *restful.Response) { projectName := req.PathParameter("devops") pipelineName := req.PathParameter("pipeline") - runId := req.PathParameter("run") - nodeId := req.PathParameter("node") - stepId := req.PathParameter("step") - res, header, err := devops.GetStepLog(projectName, pipelineName, runId, nodeId, stepId, req.Request) + res, err := h.devopsOperator.RunPipeline(projectName, pipelineName, req.Request) if err != nil { parseErr(err, resp) return } - for k, v := range header { - if strings.HasPrefix(k, jenkinsHeaderPre) { - resp.AddHeader(k, v[0]) - } - } - resp.Write(res) + + resp.Header().Set(restful.HEADER_ContentType, restful.MIME_JSON) + resp.WriteAsJson(res) } -func GetSCMServers(req *restful.Request, resp *restful.Response) { - scmId := req.PathParameter("scm") +func (h *ProjectPipelineHandler) GetArtifacts(req *restful.Request, resp *restful.Response) { + projectName := req.PathParameter("devops") + pipelineName := req.PathParameter("pipeline") + runId := req.PathParameter("run") - res, err := devops.GetSCMServers(scmId, req.Request) + res, err := h.devopsOperator.GetArtifacts(projectName, pipelineName, runId, req.Request) if err != nil { parseErr(err, resp) return } - resp.Header().Set(restful.HEADER_ContentType, restful.MIME_JSON) - resp.Write(res) + resp.WriteAsJson(res) } -func CreateSCMServers(req *restful.Request, resp *restful.Response) { - scmId := req.PathParameter("scm") +func (h *ProjectPipelineHandler) GetRunLog(req *restful.Request, resp *restful.Response) { + projectName := req.PathParameter("devops") + pipelineName := req.PathParameter("pipeline") + runId := req.PathParameter("run") - res, err := devops.CreateSCMServers(scmId, req.Request) + res, err := h.devopsOperator.GetRunLog(projectName, pipelineName, runId, req.Request) if err != nil { parseErr(err, resp) return } - resp.Header().Set(restful.HEADER_ContentType, restful.MIME_JSON) resp.Write(res) } -func Validate(req *restful.Request, resp *restful.Response) { - scmId := req.PathParameter("scm") +func (h *ProjectPipelineHandler) GetStepLog(req *restful.Request, resp *restful.Response) { + projectName := req.PathParameter("devops") + pipelineName := req.PathParameter("pipeline") + runId := req.PathParameter("run") + nodeId := req.PathParameter("node") + stepId := req.PathParameter("step") - res, err := devops.Validate(scmId, req.Request) + res, header, err := h.devopsOperator.GetStepLog(projectName, pipelineName, runId, nodeId, stepId, req.Request) if err != nil { - log.Error(err) - if jErr, ok := err.(*devops.JkError); ok { - if jErr.Code != http.StatusUnauthorized { - resp.WriteError(jErr.Code, err) - } else { - resp.WriteHeader(http.StatusPreconditionRequired) - } - } else { - resp.WriteError(http.StatusInternalServerError, err) - } + parseErr(err, resp) return } - - resp.Header().Set(restful.HEADER_ContentType, restful.MIME_JSON) + for k, v := range header { + if strings.HasPrefix(k, jenkinsHeaderPre) { + resp.AddHeader(k, v[0]) + } + } resp.Write(res) } -func GetSCMOrg(req *restful.Request, resp *restful.Response) { - scmId := req.PathParameter("scm") +func (h *ProjectPipelineHandler) GetNodeSteps(req *restful.Request, resp *restful.Response) { + projectName := req.PathParameter("devops") + pipelineName := req.PathParameter("pipeline") + runId := req.PathParameter("run") + nodeId := req.PathParameter("node") - res, err := devops.GetSCMOrg(scmId, req.Request) + res, err := h.devopsOperator.GetNodeSteps(projectName, pipelineName, runId, nodeId, req.Request) if err != nil { parseErr(err, resp) return } - resp.Header().Set(restful.HEADER_ContentType, restful.MIME_JSON) - resp.Write(res) + resp.WriteAsJson(res) } -func GetOrgRepo(req *restful.Request, resp *restful.Response) { - scmId := req.PathParameter("scm") - organizationId := req.PathParameter("organization") +func (h *ProjectPipelineHandler) GetPipelineRunNodes(req *restful.Request, resp *restful.Response) { + projectName := req.PathParameter("devops") + pipelineName := req.PathParameter("pipeline") + runId := req.PathParameter("run") - res, err := devops.GetOrgRepo(scmId, organizationId, req.Request) + res, err := h.devopsOperator.GetPipelineRunNodes(projectName, pipelineName, runId, req.Request) if err != nil { parseErr(err, resp) return } - resp.Header().Set(restful.HEADER_ContentType, restful.MIME_JSON) - resp.Write(res) + resp.WriteAsJson(res) } -func StopBranchPipeline(req *restful.Request, resp *restful.Response) { +func (h *ProjectPipelineHandler) SubmitInputStep(req *restful.Request, resp *restful.Response) { projectName := req.PathParameter("devops") pipelineName := req.PathParameter("pipeline") - branchName := req.PathParameter("branch") runId := req.PathParameter("run") + nodeId := req.PathParameter("node") + stepId := req.PathParameter("step") - res, err := devops.StopBranchPipeline(projectName, pipelineName, branchName, runId, req.Request) + res, err := h.devopsOperator.SubmitInputStep(projectName, pipelineName, runId, nodeId, stepId, req.Request) if err != nil { parseErr(err, resp) return } - resp.Header().Set(restful.HEADER_ContentType, restful.MIME_JSON) resp.Write(res) } -func StopPipeline(req *restful.Request, resp *restful.Response) { +func (h *ProjectPipelineHandler) GetNodesDetail(req *restful.Request, resp *restful.Response) { projectName := req.PathParameter("devops") pipelineName := req.PathParameter("pipeline") runId := req.PathParameter("run") - res, err := devops.StopPipeline(projectName, pipelineName, runId, req.Request) + res, err := h.devopsOperator.GetNodesDetail(projectName, pipelineName, runId, req.Request) if err != nil { parseErr(err, resp) return } - - resp.Header().Set(restful.HEADER_ContentType, restful.MIME_JSON) - resp.Write(res) + resp.WriteAsJson(res) } -func ReplayBranchPipeline(req *restful.Request, resp *restful.Response) { +func (h *ProjectPipelineHandler) GetBranchPipeline(req *restful.Request, resp *restful.Response) { projectName := req.PathParameter("devops") pipelineName := req.PathParameter("pipeline") branchName := req.PathParameter("branch") - runId := req.PathParameter("run") - res, err := devops.ReplayBranchPipeline(projectName, pipelineName, branchName, runId, req.Request) + res, err := h.devopsOperator.GetBranchPipeline(projectName, pipelineName, branchName, req.Request) if err != nil { parseErr(err, resp) return } resp.Header().Set(restful.HEADER_ContentType, restful.MIME_JSON) - resp.Write(res) + resp.WriteAsJson(res) } -func ReplayPipeline(req *restful.Request, resp *restful.Response) { +func (h *ProjectPipelineHandler) GetBranchPipelineRun(req *restful.Request, resp *restful.Response) { projectName := req.PathParameter("devops") pipelineName := req.PathParameter("pipeline") + branchName := req.PathParameter("branch") runId := req.PathParameter("run") - res, err := devops.ReplayPipeline(projectName, pipelineName, runId, req.Request) + res, err := h.devopsOperator.GetBranchPipelineRun(projectName, pipelineName, branchName, runId, req.Request) if err != nil { parseErr(err, resp) return } resp.Header().Set(restful.HEADER_ContentType, restful.MIME_JSON) - resp.Write(res) + resp.WriteAsJson(res) } -func GetBranchRunLog(req *restful.Request, resp *restful.Response) { +func (h *ProjectPipelineHandler) StopBranchPipeline(req *restful.Request, resp *restful.Response) { projectName := req.PathParameter("devops") pipelineName := req.PathParameter("pipeline") branchName := req.PathParameter("branch") runId := req.PathParameter("run") - res, err := devops.GetBranchRunLog(projectName, pipelineName, branchName, runId, req.Request) + res, err := h.devopsOperator.StopBranchPipeline(projectName, pipelineName, branchName, runId, req.Request) if err != nil { parseErr(err, resp) return } - resp.Write(res) + resp.Header().Set(restful.HEADER_ContentType, restful.MIME_JSON) + resp.WriteAsJson(res) } -func GetRunLog(req *restful.Request, resp *restful.Response) { +func (h *ProjectPipelineHandler) ReplayBranchPipeline(req *restful.Request, resp *restful.Response) { projectName := req.PathParameter("devops") pipelineName := req.PathParameter("pipeline") + branchName := req.PathParameter("branch") runId := req.PathParameter("run") - res, err := devops.GetRunLog(projectName, pipelineName, runId, req.Request) + res, err := h.devopsOperator.ReplayBranchPipeline(projectName, pipelineName, branchName, runId, req.Request) if err != nil { parseErr(err, resp) return } - resp.Write(res) + resp.Header().Set(restful.HEADER_ContentType, restful.MIME_JSON) + resp.WriteAsJson(res) } -func GetBranchArtifacts(req *restful.Request, resp *restful.Response) { +func (h *ProjectPipelineHandler) RunBranchPipeline(req *restful.Request, resp *restful.Response) { projectName := req.PathParameter("devops") pipelineName := req.PathParameter("pipeline") branchName := req.PathParameter("branch") - runId := req.PathParameter("run") - res, err := devops.GetBranchArtifacts(projectName, pipelineName, branchName, runId, req.Request) + res, err := h.devopsOperator.RunBranchPipeline(projectName, pipelineName, branchName, req.Request) if err != nil { parseErr(err, resp) return } + resp.Header().Set(restful.HEADER_ContentType, restful.MIME_JSON) - resp.Write(res) + resp.WriteAsJson(res) } -func GetArtifacts(req *restful.Request, resp *restful.Response) { +func (h *ProjectPipelineHandler) GetBranchArtifacts(req *restful.Request, resp *restful.Response) { projectName := req.PathParameter("devops") pipelineName := req.PathParameter("pipeline") + branchName := req.PathParameter("branch") runId := req.PathParameter("run") - res, err := devops.GetArtifacts(projectName, pipelineName, runId, req.Request) + res, err := h.devopsOperator.GetBranchArtifacts(projectName, pipelineName, branchName, runId, req.Request) if err != nil { parseErr(err, resp) return } resp.Header().Set(restful.HEADER_ContentType, restful.MIME_JSON) - resp.Write(res) + resp.WriteAsJson(res) } -func GetPipeBranch(req *restful.Request, resp *restful.Response) { +func (h *ProjectPipelineHandler) GetBranchRunLog(req *restful.Request, resp *restful.Response) { projectName := req.PathParameter("devops") pipelineName := req.PathParameter("pipeline") + branchName := req.PathParameter("branch") + runId := req.PathParameter("run") - res, err := devops.GetPipeBranch(projectName, pipelineName, req.Request) + res, err := h.devopsOperator.GetBranchRunLog(projectName, pipelineName, branchName, runId, req.Request) if err != nil { parseErr(err, resp) return } - resp.Header().Set(restful.HEADER_ContentType, restful.MIME_JSON) + resp.Write(res) } -func SubmitBranchInputStep(req *restful.Request, resp *restful.Response) { +func (h *ProjectPipelineHandler) GetBranchStepLog(req *restful.Request, resp *restful.Response) { projectName := req.PathParameter("devops") pipelineName := req.PathParameter("pipeline") branchName := req.PathParameter("branch") @@ -357,49 +347,61 @@ func SubmitBranchInputStep(req *restful.Request, resp *restful.Response) { nodeId := req.PathParameter("node") stepId := req.PathParameter("step") - res, err := devops.SubmitBranchInputStep(projectName, pipelineName, branchName, runId, nodeId, stepId, req.Request) + res, header, err := h.devopsOperator.GetBranchStepLog(projectName, pipelineName, branchName, runId, nodeId, stepId, req.Request) + if err != nil { parseErr(err, resp) return } - + for k, v := range header { + if strings.HasPrefix(k, jenkinsHeaderPre) { + resp.AddHeader(k, v[0]) + } + } resp.Write(res) } -func SubmitInputStep(req *restful.Request, resp *restful.Response) { +func (h *ProjectPipelineHandler) GetBranchNodeSteps(req *restful.Request, resp *restful.Response) { projectName := req.PathParameter("devops") pipelineName := req.PathParameter("pipeline") + branchName := req.PathParameter("branch") runId := req.PathParameter("run") nodeId := req.PathParameter("node") - stepId := req.PathParameter("step") - res, err := devops.SubmitInputStep(projectName, pipelineName, runId, nodeId, stepId, req.Request) + res, err := h.devopsOperator.GetBranchNodeSteps(projectName, pipelineName, branchName, runId, nodeId, req.Request) if err != nil { parseErr(err, resp) return } - - resp.Write(res) + resp.Header().Set(restful.HEADER_ContentType, restful.MIME_JSON) + resp.WriteAsJson(res) } -func GetConsoleLog(req *restful.Request, resp *restful.Response) { +func (h *ProjectPipelineHandler) GetBranchPipelineRunNodes(req *restful.Request, resp *restful.Response) { projectName := req.PathParameter("devops") pipelineName := req.PathParameter("pipeline") + branchName := req.PathParameter("branch") + runId := req.PathParameter("run") - res, err := devops.GetConsoleLog(projectName, pipelineName, req.Request) + res, err := h.devopsOperator.GetBranchPipelineRunNodes(projectName, pipelineName, branchName, runId, req.Request) if err != nil { parseErr(err, resp) return } - resp.Write(res) + resp.Header().Set(restful.HEADER_ContentType, restful.MIME_JSON) + resp.WriteAsJson(res) } -func ScanBranch(req *restful.Request, resp *restful.Response) { +func (h *ProjectPipelineHandler) SubmitBranchInputStep(req *restful.Request, resp *restful.Response) { projectName := req.PathParameter("devops") pipelineName := req.PathParameter("pipeline") + branchName := req.PathParameter("branch") + runId := req.PathParameter("run") + nodeId := req.PathParameter("node") + stepId := req.PathParameter("step") - res, err := devops.ScanBranch(projectName, pipelineName, req.Request) + res, err := h.devopsOperator.SubmitBranchInputStep(projectName, pipelineName, branchName, runId, nodeId, stepId, req.Request) if err != nil { parseErr(err, resp) return @@ -408,77 +410,61 @@ func ScanBranch(req *restful.Request, resp *restful.Response) { resp.Write(res) } -func RunBranchPipeline(req *restful.Request, resp *restful.Response) { +func (h *ProjectPipelineHandler) GetBranchNodesDetail(req *restful.Request, resp *restful.Response) { projectName := req.PathParameter("devops") pipelineName := req.PathParameter("pipeline") branchName := req.PathParameter("branch") + runId := req.PathParameter("run") - res, err := devops.RunBranchPipeline(projectName, pipelineName, branchName, req.Request) + res, err := h.devopsOperator.GetBranchNodesDetail(projectName, pipelineName, branchName, runId, req.Request) if err != nil { parseErr(err, resp) return } - - resp.Header().Set(restful.HEADER_ContentType, restful.MIME_JSON) - resp.Write(res) + resp.WriteAsJson(res) } -func RunPipeline(req *restful.Request, resp *restful.Response) { +func (h *ProjectPipelineHandler) GetPipelineBranch(req *restful.Request, resp *restful.Response) { projectName := req.PathParameter("devops") pipelineName := req.PathParameter("pipeline") - res, err := devops.RunPipeline(projectName, pipelineName, req.Request) + res, err := h.devopsOperator.GetPipelineBranch(projectName, pipelineName, req.Request) if err != nil { parseErr(err, resp) return } - resp.Header().Set(restful.HEADER_ContentType, restful.MIME_JSON) - resp.Write(res) + resp.WriteAsJson(res) } -func GetCrumb(req *restful.Request, resp *restful.Response) { - res, err := devops.GetCrumb(req.Request) +func (h *ProjectPipelineHandler) ScanBranch(req *restful.Request, resp *restful.Response) { + projectName := req.PathParameter("devops") + pipelineName := req.PathParameter("pipeline") + + res, err := h.devopsOperator.ScanBranch(projectName, pipelineName, req.Request) if err != nil { parseErr(err, resp) return } - resp.Header().Set(restful.HEADER_ContentType, restful.MIME_JSON) resp.Write(res) } -func CheckScriptCompile(req *restful.Request, resp *restful.Response) { +func (h *ProjectPipelineHandler) GetConsoleLog(req *restful.Request, resp *restful.Response) { projectName := req.PathParameter("devops") pipelineName := req.PathParameter("pipeline") - resBody, err := devops.CheckScriptCompile(projectName, pipelineName, req.Request) + res, err := h.devopsOperator.GetConsoleLog(projectName, pipelineName, req.Request) if err != nil { parseErr(err, resp) return } - // Jenkins will return different struct according to different results. - var resJson = new(devops.CheckScript) - if ok := json.Unmarshal(resBody, &resJson); ok != nil { - var resJson []interface{} - err := json.Unmarshal(resBody, &resJson) - if err != nil { - resp.WriteError(http.StatusInternalServerError, err) - return - } - resp.WriteAsJson(resJson[0]) - return - - } - - resp.WriteAsJson(resJson) + resp.Write(res) } -func CheckCron(req *restful.Request, resp *restful.Response) { - projectName := req.PathParameter("devops") - - res, err := devops.CheckCron(projectName, req.Request) +func (h *ProjectPipelineHandler) GetCrumb(req *restful.Request, resp *restful.Response) { + res, err := h.devopsOperator.GetCrumb(req.Request) if err != nil { parseErr(err, resp) return @@ -488,103 +474,101 @@ func CheckCron(req *restful.Request, resp *restful.Response) { resp.WriteAsJson(res) } -func GetPipelineRun(req *restful.Request, resp *restful.Response) { - projectName := req.PathParameter("devops") - pipelineName := req.PathParameter("pipeline") - runId := req.PathParameter("run") +func (h *ProjectPipelineHandler) GetSCMServers(req *restful.Request, resp *restful.Response) { + scmId := req.PathParameter("scm") - res, err := devops.GetPipelineRun(projectName, pipelineName, runId, req.Request) + res, err := h.devopsOperator.GetSCMServers(scmId, req.Request) if err != nil { parseErr(err, resp) return } resp.Header().Set(restful.HEADER_ContentType, restful.MIME_JSON) - resp.Write(res) + resp.WriteAsJson(res) } -func GetBranchPipeline(req *restful.Request, resp *restful.Response) { - projectName := req.PathParameter("devops") - pipelineName := req.PathParameter("pipeline") - branchName := req.PathParameter("branch") +func (h *ProjectPipelineHandler) GetSCMOrg(req *restful.Request, resp *restful.Response) { + scmId := req.PathParameter("scm") - res, err := devops.GetBranchPipeline(projectName, pipelineName, branchName, req.Request) + res, err := h.devopsOperator.GetSCMOrg(scmId, req.Request) if err != nil { parseErr(err, resp) return } resp.Header().Set(restful.HEADER_ContentType, restful.MIME_JSON) - resp.Write(res) + resp.WriteAsJson(res) } -func GetPipelineRunNodes(req *restful.Request, resp *restful.Response) { - projectName := req.PathParameter("devops") - pipelineName := req.PathParameter("pipeline") - runId := req.PathParameter("run") +func (h *ProjectPipelineHandler) GetOrgRepo(req *restful.Request, resp *restful.Response) { + scmId := req.PathParameter("scm") + organizationId := req.PathParameter("organization") - res, err := devops.GetPipelineRunNodes(projectName, pipelineName, runId, req.Request) + res, err := h.devopsOperator.GetOrgRepo(scmId, organizationId, req.Request) if err != nil { parseErr(err, resp) return } + resp.Header().Set(restful.HEADER_ContentType, restful.MIME_JSON) - resp.Write(res) + resp.WriteAsJson(res) } -func GetBranchNodeSteps(req *restful.Request, resp *restful.Response) { - projectName := req.PathParameter("devops") - pipelineName := req.PathParameter("pipeline") - branchName := req.PathParameter("branch") - runId := req.PathParameter("run") - nodeId := req.PathParameter("node") +func (h *ProjectPipelineHandler) CreateSCMServers(req *restful.Request, resp *restful.Response) { + scmId := req.PathParameter("scm") - res, err := devops.GetBranchNodeSteps(projectName, pipelineName, branchName, runId, nodeId, req.Request) + res, err := h.devopsOperator.CreateSCMServers(scmId, req.Request) if err != nil { parseErr(err, resp) return } + resp.Header().Set(restful.HEADER_ContentType, restful.MIME_JSON) - resp.Write(res) + resp.WriteAsJson(res) } -func GetNodeSteps(req *restful.Request, resp *restful.Response) { - projectName := req.PathParameter("devops") - pipelineName := req.PathParameter("pipeline") - runId := req.PathParameter("run") - nodeId := req.PathParameter("node") +func (h *ProjectPipelineHandler) Validate(req *restful.Request, resp *restful.Response) { + scmId := req.PathParameter("scm") - res, err := devops.GetNodeSteps(projectName, pipelineName, runId, nodeId, req.Request) + res, err := h.devopsOperator.Validate(scmId, req.Request) if err != nil { - parseErr(err, resp) + log.Error(err) + if jErr, ok := err.(*devops.JkError); ok { + if jErr.Code != http.StatusUnauthorized { + resp.WriteError(jErr.Code, err) + } else { + resp.WriteHeader(http.StatusPreconditionRequired) + } + } else { + resp.WriteError(http.StatusInternalServerError, err) + } return } + resp.Header().Set(restful.HEADER_ContentType, restful.MIME_JSON) - resp.Write(res) + resp.WriteAsJson(res) } -func ToJenkinsfile(req *restful.Request, resp *restful.Response) { - res, err := devops.ToJenkinsfile(req.Request) +func (h *ProjectPipelineHandler) GetNotifyCommit(req *restful.Request, resp *restful.Response) { + res, err := h.devopsOperator.GetNotifyCommit(req.Request) if err != nil { parseErr(err, resp) return } - resp.Header().Set(restful.HEADER_ContentType, restful.MIME_JSON) resp.Write(res) } -func ToJson(req *restful.Request, resp *restful.Response) { - res, err := devops.ToJson(req.Request) +func (h *ProjectPipelineHandler) PostNotifyCommit(req *restful.Request, resp *restful.Response) { + res, err := h.devopsOperator.GetNotifyCommit(req.Request) if err != nil { parseErr(err, resp) return } - resp.Header().Set(restful.HEADER_ContentType, restful.MIME_JSON) resp.Write(res) } -func GetNotifyCommit(req *restful.Request, resp *restful.Response) { - res, err := devops.GetNotifyCommit(req.Request) +func (h *ProjectPipelineHandler) GithubWebhook(req *restful.Request, resp *restful.Response) { + res, err := h.devopsOperator.GithubWebhook(req.Request) if err != nil { parseErr(err, resp) return @@ -592,47 +576,49 @@ func GetNotifyCommit(req *restful.Request, resp *restful.Response) { resp.Write(res) } -func PostNotifyCommit(req *restful.Request, resp *restful.Response) { - res, err := devops.GetNotifyCommit(req.Request) +func (h *ProjectPipelineHandler) CheckScriptCompile(req *restful.Request, resp *restful.Response) { + projectName := req.PathParameter("devops") + pipelineName := req.PathParameter("pipeline") + + resBody, err := h.devopsOperator.CheckScriptCompile(projectName, pipelineName, req.Request) if err != nil { parseErr(err, resp) return } - resp.Write(res) + + resp.WriteAsJson(resBody) } -func GithubWebhook(req *restful.Request, resp *restful.Response) { - res, err := devops.GithubWebhook(req.Request) + +func (h *ProjectPipelineHandler) CheckCron(req *restful.Request, resp *restful.Response) { + projectName := req.PathParameter("devops") + + res, err := h.devopsOperator.CheckCron(projectName, req.Request) if err != nil { parseErr(err, resp) return } - resp.Write(res) -} -func GetBranchNodesDetail(req *restful.Request, resp *restful.Response) { - projectName := req.PathParameter("devops") - pipelineName := req.PathParameter("pipeline") - branchName := req.PathParameter("branch") - runId := req.PathParameter("run") + resp.Header().Set(restful.HEADER_ContentType, restful.MIME_JSON) + resp.WriteAsJson(res) +} - res, err := devops.GetBranchNodesDetail(projectName, pipelineName, branchName, runId, req.Request) +func (h *ProjectPipelineHandler) ToJenkinsfile(req *restful.Request, resp *restful.Response) { + res, err := h.devopsOperator.ToJenkinsfile(req.Request) if err != nil { parseErr(err, resp) return } + resp.Header().Set(restful.HEADER_ContentType, restful.MIME_JSON) resp.WriteAsJson(res) } -func GetNodesDetail(req *restful.Request, resp *restful.Response) { - projectName := req.PathParameter("devops") - pipelineName := req.PathParameter("pipeline") - runId := req.PathParameter("run") - - res, err := devops.GetNodesDetail(projectName, pipelineName, runId, req.Request) +func (h *ProjectPipelineHandler) ToJson(req *restful.Request, resp *restful.Response) { + res, err := h.devopsOperator.ToJson(req.Request) if err != nil { parseErr(err, resp) return } + resp.Header().Set(restful.HEADER_ContentType, restful.MIME_JSON) resp.WriteAsJson(res) } diff --git a/pkg/kapis/devops/v1alpha2/handler.go b/pkg/kapis/devops/v1alpha2/handler.go index aaaf31cd..3ad4daae 100644 --- a/pkg/kapis/devops/v1alpha2/handler.go +++ b/pkg/kapis/devops/v1alpha2/handler.go @@ -1 +1,43 @@ 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)} +} diff --git a/pkg/apiserver/devops/member.go b/pkg/kapis/devops/v1alpha2/member.go similarity index 73% rename from pkg/apiserver/devops/member.go rename to pkg/kapis/devops/v1alpha2/member.go index d168e833..42a4b156 100644 --- a/pkg/apiserver/devops/member.go +++ b/pkg/kapis/devops/v1alpha2/member.go @@ -11,7 +11,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -package devops +package v1alpha2 import ( "fmt" @@ -19,19 +19,19 @@ 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" "kubesphere.io/kubesphere/pkg/server/params" + "kubesphere.io/kubesphere/pkg/simple/client/devops" "kubesphere.io/kubesphere/pkg/utils/reflectutils" "net/http" ) -func GetDevOpsProjectMembersHandler(request *restful.Request, resp *restful.Response) { +func (h ProjectPipelineHandler) GetDevOpsProjectMembersHandler(request *restful.Request, resp *restful.Response) { projectId := request.PathParameter("devops") username := request.HeaderParameter(constants.UserNameHeader) - err := devops.CheckProjectUserInRole(username, projectId, devops.AllRoleSlice) + err := h.projectOperator.CheckProjectUserInRole(username, projectId, devops.AllRoleSlice) if err != nil { klog.Errorf("%+v", err) 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)) 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 { klog.Errorf("%+v", err) @@ -54,19 +54,19 @@ func GetDevOpsProjectMembersHandler(request *restful.Request, resp *restful.Resp return } -func GetDevOpsProjectMemberHandler(request *restful.Request, resp *restful.Response) { +func (h ProjectPipelineHandler) GetDevOpsProjectMemberHandler(request *restful.Request, resp *restful.Response) { projectId := request.PathParameter("devops") username := request.HeaderParameter(constants.UserNameHeader) member := request.PathParameter("member") - err := devops.CheckProjectUserInRole(username, projectId, devops.AllRoleSlice) + err := h.projectOperator.CheckProjectUserInRole(username, projectId, devops.AllRoleSlice) if err != nil { klog.Errorf("%+v", err) errors.ParseSvcErr(restful.NewError(http.StatusForbidden, err.Error()), resp) return } - project, err := devops.GetProjectMember(projectId, member) + project, err := h.projectMemberOperator.GetProjectMember(projectId, member) if err != nil { klog.Errorf("%+v", err) @@ -78,11 +78,11 @@ func GetDevOpsProjectMemberHandler(request *restful.Request, resp *restful.Respo return } -func AddDevOpsProjectMemberHandler(request *restful.Request, resp *restful.Response) { +func (h ProjectPipelineHandler) AddDevOpsProjectMemberHandler(request *restful.Request, resp *restful.Response) { projectId := request.PathParameter("devops") username := request.HeaderParameter(constants.UserNameHeader) - member := &devops.DevOpsProjectMembership{} + member := &devops.ProjectMembership{} err := request.ReadEntity(&member) if err != nil { 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) return } - - err = devops.CheckProjectUserInRole(username, projectId, []string{devops.ProjectOwner}) + err = h.projectOperator.CheckProjectUserInRole(username, projectId, []string{devops.ProjectOwner}) if err != nil { klog.Errorf("%+v", err) errors.ParseSvcErr(restful.NewError(http.StatusForbidden, err.Error()), resp) return } - project, err := devops.AddProjectMember(projectId, username, member) + + member.GrantBy = username + project, err := h.projectMemberOperator.AddProjectMember(projectId, member) if err != nil { klog.Errorf("%+v", err) @@ -121,11 +122,11 @@ func AddDevOpsProjectMemberHandler(request *restful.Request, resp *restful.Respo return } -func UpdateDevOpsProjectMemberHandler(request *restful.Request, resp *restful.Response) { +func (h ProjectPipelineHandler) UpdateDevOpsProjectMemberHandler(request *restful.Request, resp *restful.Response) { projectId := request.PathParameter("devops") username := request.HeaderParameter(constants.UserNameHeader) - member := &devops.DevOpsProjectMembership{} + member := &devops.ProjectMembership{} err := request.ReadEntity(&member) if err != nil { klog.Errorf("%+v", err) @@ -153,13 +154,13 @@ func UpdateDevOpsProjectMemberHandler(request *restful.Request, resp *restful.Re return } - err = devops.CheckProjectUserInRole(username, projectId, []string{devops.ProjectOwner}) + err = h.projectOperator.CheckProjectUserInRole(username, projectId, []string{devops.ProjectOwner}) if err != nil { klog.Errorf("%+v", err) errors.ParseSvcErr(restful.NewError(http.StatusForbidden, err.Error()), resp) return } - project, err := devops.UpdateProjectMember(projectId, username, member) + project, err := h.projectMemberOperator.UpdateProjectMember(projectId, member) if err != nil { klog.Errorf("%+v", err) @@ -171,19 +172,19 @@ func UpdateDevOpsProjectMemberHandler(request *restful.Request, resp *restful.Re return } -func DeleteDevOpsProjectMemberHandler(request *restful.Request, resp *restful.Response) { +func (h ProjectPipelineHandler) DeleteDevOpsProjectMemberHandler(request *restful.Request, resp *restful.Response) { projectId := request.PathParameter("devops") username := request.HeaderParameter(constants.UserNameHeader) member := request.PathParameter("member") - err := devops.CheckProjectUserInRole(username, projectId, []string{devops.ProjectOwner}) + err := h.projectOperator.CheckProjectUserInRole(username, projectId, []string{devops.ProjectOwner}) if err != nil { klog.Errorf("%+v", err) errors.ParseSvcErr(restful.NewError(http.StatusForbidden, err.Error()), resp) return } - username, err = devops.DeleteProjectMember(projectId, member) + username, err = h.projectMemberOperator.DeleteProjectMember(projectId, member) if err != nil { klog.Errorf("%+v", err) errors.ParseSvcErr(err, resp) diff --git a/pkg/kapis/devops/v1alpha2/pipeline_sonar.go b/pkg/kapis/devops/v1alpha2/pipeline_sonar.go new file mode 100644 index 00000000..8b5ecee3 --- /dev/null +++ b/pkg/kapis/devops/v1alpha2/pipeline_sonar.go @@ -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) +} diff --git a/pkg/apiserver/devops/project.go b/pkg/kapis/devops/v1alpha2/project.go similarity index 76% rename from pkg/apiserver/devops/project.go rename to pkg/kapis/devops/v1alpha2/project.go index 5589dac6..2bd4b88a 100644 --- a/pkg/apiserver/devops/project.go +++ b/pkg/kapis/devops/v1alpha2/project.go @@ -11,30 +11,30 @@ See the License for the specific language governing permissions and limitations under the License. */ -package devops +package v1alpha2 import ( "github.com/emicklei/go-restful" "k8s.io/klog" "kubesphere.io/kubesphere/pkg/api/devops/v1alpha2" "kubesphere.io/kubesphere/pkg/constants" - "kubesphere.io/kubesphere/pkg/models/devops" "kubesphere.io/kubesphere/pkg/server/errors" + "kubesphere.io/kubesphere/pkg/simple/client/devops" "net/http" ) -func GetDevOpsProjectHandler(request *restful.Request, resp *restful.Response) { +func (h ProjectPipelineHandler) GetDevOpsProjectHandler(request *restful.Request, resp *restful.Response) { projectId := request.PathParameter("devops") username := request.HeaderParameter(constants.UserNameHeader) - err := devops.CheckProjectUserInRole(username, projectId, devops.AllRoleSlice) + err := h.projectOperator.CheckProjectUserInRole(username, projectId, devops.AllRoleSlice) if err != nil { klog.Errorf("%+v", err) errors.ParseSvcErr(restful.NewError(http.StatusForbidden, err.Error()), resp) return } - project, err := devops.GetProject(projectId) + project, err := h.projectOperator.GetProject(projectId) if err != nil { klog.Errorf("%+v", err) @@ -46,7 +46,7 @@ func GetDevOpsProjectHandler(request *restful.Request, resp *restful.Response) { return } -func UpdateProjectHandler(request *restful.Request, resp *restful.Response) { +func (h ProjectPipelineHandler) UpdateProjectHandler(request *restful.Request, resp *restful.Response) { projectId := request.PathParameter("devops") username := request.HeaderParameter(constants.UserNameHeader) @@ -58,13 +58,13 @@ func UpdateProjectHandler(request *restful.Request, resp *restful.Response) { return } project.ProjectId = projectId - err = devops.CheckProjectUserInRole(username, projectId, []string{devops.ProjectOwner}) + err = h.projectOperator.CheckProjectUserInRole(username, projectId, []string{devops.ProjectOwner}) if err != nil { klog.Errorf("%+v", err) errors.ParseSvcErr(restful.NewError(http.StatusForbidden, err.Error()), resp) return } - project, err = devops.UpdateProject(project) + project, err = h.projectOperator.UpdateProject(project) if err != nil { klog.Errorf("%+v", err) diff --git a/pkg/kapis/devops/v1alpha2/project_credential.go b/pkg/kapis/devops/v1alpha2/project_credential.go new file mode 100644 index 00000000..9d9d9289 --- /dev/null +++ b/pkg/kapis/devops/v1alpha2/project_credential.go @@ -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 +} diff --git a/pkg/apiserver/devops/project_pipeline.go b/pkg/kapis/devops/v1alpha2/project_pipeline.go similarity index 53% rename from pkg/apiserver/devops/project_pipeline.go rename to pkg/kapis/devops/v1alpha2/project_pipeline.go index fe62c69e..b752dafd 100644 --- a/pkg/apiserver/devops/project_pipeline.go +++ b/pkg/kapis/devops/v1alpha2/project_pipeline.go @@ -11,18 +11,18 @@ See the License for the specific language governing permissions and limitations under the License. */ -package devops +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" + "kubesphere.io/kubesphere/pkg/simple/client/devops" "net/http" ) -func CreateDevOpsProjectPipelineHandler(request *restful.Request, resp *restful.Response) { +func (h ProjectPipelineHandler) CreateDevOpsProjectPipelineHandler(request *restful.Request, resp *restful.Response) { projectId := request.PathParameter("devops") 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) 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 { klog.Errorf("%+v", err) errors.ParseSvcErr(restful.NewError(http.StatusForbidden, err.Error()), resp) return } - pipelineName, err := devops.CreateProjectPipeline(projectId, pipeline) + pipelineName, err := h.projectPipelineOperator.CreateProjectPipeline(projectId, pipeline) if err != nil { klog.Errorf("%+v", err) @@ -53,18 +53,18 @@ func CreateDevOpsProjectPipelineHandler(request *restful.Request, resp *restful. return } -func DeleteDevOpsProjectPipelineHandler(request *restful.Request, resp *restful.Response) { +func (h ProjectPipelineHandler) DeleteDevOpsProjectPipelineHandler(request *restful.Request, resp *restful.Response) { projectId := request.PathParameter("devops") username := request.HeaderParameter(constants.UserNameHeader) 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 { klog.Errorf("%+v", err) errors.ParseSvcErr(restful.NewError(http.StatusForbidden, err.Error()), resp) return } - pipelineName, err := devops.DeleteProjectPipeline(projectId, pipelineId) + pipelineName, err := h.projectPipelineOperator.DeleteProjectPipeline(projectId, pipelineId) if err != nil { klog.Errorf("%+v", err) @@ -78,7 +78,7 @@ func DeleteDevOpsProjectPipelineHandler(request *restful.Request, resp *restful. return } -func UpdateDevOpsProjectPipelineHandler(request *restful.Request, resp *restful.Response) { +func (h ProjectPipelineHandler) UpdateDevOpsProjectPipelineHandler(request *restful.Request, resp *restful.Response) { projectId := request.PathParameter("devops") 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) 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 { klog.Errorf("%+v", err) errors.ParseSvcErr(restful.NewError(http.StatusForbidden, err.Error()), resp) return } - pipelineName, err := devops.UpdateProjectPipeline(projectId, pipelineId, pipeline) + pipelineName, err := h.projectPipelineOperator.UpdateProjectPipeline(projectId, pipelineId, pipeline) if err != nil { klog.Errorf("%+v", err) @@ -110,19 +110,19 @@ func UpdateDevOpsProjectPipelineHandler(request *restful.Request, resp *restful. return } -func GetDevOpsProjectPipelineHandler(request *restful.Request, resp *restful.Response) { +func (h ProjectPipelineHandler) GetDevOpsProjectPipelineConfigHandler(request *restful.Request, resp *restful.Response) { projectId := request.PathParameter("devops") username := request.HeaderParameter(constants.UserNameHeader) 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 { klog.Errorf("%+v", err) errors.ParseSvcErr(restful.NewError(http.StatusForbidden, err.Error()), resp) return } - pipeline, err := devops.GetProjectPipeline(projectId, pipelineId) + pipeline, err := h.projectPipelineOperator.GetProjectPipelineConfig(projectId, pipelineId) if err != nil { klog.Errorf("%+v", err) @@ -133,42 +133,3 @@ func GetDevOpsProjectPipelineHandler(request *restful.Request, resp *restful.Res resp.WriteAsJson(pipeline) 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) -} diff --git a/pkg/kapis/devops/v1alpha2/register.go b/pkg/kapis/devops/v1alpha2/register.go index ea766819..68c088de 100644 --- a/pkg/kapis/devops/v1alpha2/register.go +++ b/pkg/kapis/devops/v1alpha2/register.go @@ -24,10 +24,10 @@ import ( "k8s.io/apimachinery/pkg/runtime/schema" "kubesphere.io/kubesphere/pkg/api/devops/v1alpha2" devopsv1alpha1 "kubesphere.io/kubesphere/pkg/apis/devops/v1alpha1" - devopsapi "kubesphere.io/kubesphere/pkg/apiserver/devops" "kubesphere.io/kubesphere/pkg/apiserver/runtime" "kubesphere.io/kubesphere/pkg/constants" - "kubesphere.io/kubesphere/pkg/models/devops" + //"kubesphere.io/kubesphere/pkg/models/devops" + "kubesphere.io/kubesphere/pkg/simple/client/devops" "kubesphere.io/kubesphere/pkg/server/params" "net/http" @@ -49,8 +49,14 @@ func addWebService(c *restful.Container) error { webservice := runtime.NewWebService(GroupVersion) + sonarHandler := NewPipelineSonarHandler() + + projectPipelineHander := NewProjectPipelineHandler() + + s2iHandler := NewS2iBinaryHandler() + webservice.Route(webservice.GET("/devops/{devops}"). - To(devopsapi.GetDevOpsProjectHandler). + To(projectPipelineHander.GetDevOpsProjectHandler). Doc("Get the specified DevOps Project"). Metadata(restfulspec.KeyOpenAPITags, []string{constants.DevOpsProjectTag}). Param(webservice.PathParameter("devops", "DevOps project's ID, e.g. project-RRRRAzLBlLEm")). @@ -58,7 +64,7 @@ func addWebService(c *restful.Container) error { Writes(v1alpha2.DevOpsProject{})) webservice.Route(webservice.PATCH("/devops/{devops}"). - To(devopsapi.UpdateProjectHandler). + To(projectPipelineHander.UpdateProjectHandler). Doc("Update the specified DevOps Project"). Metadata(restfulspec.KeyOpenAPITags, []string{constants.DevOpsProjectTag}). Param(webservice.PathParameter("devops", "DevOps project's ID, e.g. project-RRRRAzLBlLEm")). @@ -67,7 +73,7 @@ func addWebService(c *restful.Container) error { Writes(v1alpha2.DevOpsProject{})) webservice.Route(webservice.GET("/devops/{devops}/defaultroles"). - To(devopsapi.GetDevOpsProjectDefaultRoles). + To(GetDevOpsProjectDefaultRoles). Doc("Get the build-in roles info of the specified DevOps project"). Metadata(restfulspec.KeyOpenAPITags, []string{constants.DevOpsProjectMemberTag}). Param(webservice.PathParameter("devops", "DevOps project's ID, e.g. project-RRRRAzLBlLEm")). @@ -75,7 +81,7 @@ func addWebService(c *restful.Container) error { Writes([]devops.Role{})) webservice.Route(webservice.GET("/devops/{devops}/members"). - To(devopsapi.GetDevOpsProjectMembersHandler). + To(projectPipelineHander.GetDevOpsProjectMembersHandler). Doc("Get the members of the specified DevOps project"). Metadata(restfulspec.KeyOpenAPITags, []string{constants.DevOpsProjectMemberTag}). Param(webservice.PathParameter("devops", "DevOps project's ID, e.g. project-RRRRAzLBlLEm")). @@ -90,7 +96,7 @@ func addWebService(c *restful.Container) error { Writes([]devops.DevOpsProjectMembership{})) webservice.Route(webservice.GET("/devops/{devops}/members/{member}"). - To(devopsapi.GetDevOpsProjectMemberHandler). + To(projectPipelineHander.GetDevOpsProjectMemberHandler). Doc("Get the specified member of the DevOps project"). Metadata(restfulspec.KeyOpenAPITags, []string{constants.DevOpsProjectMemberTag}). Param(webservice.PathParameter("devops", "DevOps project's ID, e.g. project-RRRRAzLBlLEm")). @@ -99,7 +105,7 @@ func addWebService(c *restful.Container) error { Writes(devops.DevOpsProjectMembership{})) webservice.Route(webservice.POST("/devops/{devops}/members"). - To(devopsapi.AddDevOpsProjectMemberHandler). + To(projectPipelineHander.AddDevOpsProjectMemberHandler). Doc("Add a member to the specified DevOps project"). Metadata(restfulspec.KeyOpenAPITags, []string{constants.DevOpsProjectMemberTag}). Param(webservice.PathParameter("devops", "DevOps project's ID, e.g. project-RRRRAzLBlLEm")). @@ -108,7 +114,7 @@ func addWebService(c *restful.Container) error { Reads(devops.DevOpsProjectMembership{})) webservice.Route(webservice.PATCH("/devops/{devops}/members/{member}"). - To(devopsapi.UpdateDevOpsProjectMemberHandler). + To(projectPipelineHander.UpdateDevOpsProjectMemberHandler). Doc("Update the specified member of the DevOps project"). Metadata(restfulspec.KeyOpenAPITags, []string{constants.DevOpsProjectMemberTag}). Param(webservice.PathParameter("devops", "DevOps project's ID, e.g. project-RRRRAzLBlLEm")). @@ -118,7 +124,7 @@ func addWebService(c *restful.Container) error { Writes(devops.DevOpsProjectMembership{})) webservice.Route(webservice.DELETE("/devops/{devops}/members/{member}"). - To(devopsapi.DeleteDevOpsProjectMemberHandler). + To(projectPipelineHander.DeleteDevOpsProjectMemberHandler). Doc("Delete the specified member of the DevOps project"). Metadata(restfulspec.KeyOpenAPITags, []string{constants.DevOpsProjectMemberTag}). Param(webservice.PathParameter("devops", "DevOps project's ID, e.g. project-RRRRAzLBlLEm")). @@ -126,7 +132,7 @@ func addWebService(c *restful.Container) error { Writes(devops.DevOpsProjectMembership{})) webservice.Route(webservice.POST("/devops/{devops}/pipelines"). - To(devopsapi.CreateDevOpsProjectPipelineHandler). + To(projectPipelineHander.CreateDevOpsProjectPipelineHandler). Doc("Create a DevOps project pipeline"). Param(webservice.PathParameter("devops", "DevOps project's ID, e.g. project-RRRRAzLBlLEm")). Metadata(restfulspec.KeyOpenAPITags, []string{constants.DevOpsPipelineTag}). @@ -135,7 +141,7 @@ func addWebService(c *restful.Container) error { Reads(devops.ProjectPipeline{})) webservice.Route(webservice.PUT("/devops/{devops}/pipelines/{pipeline}"). - To(devopsapi.UpdateDevOpsProjectPipelineHandler). + To(projectPipelineHander.UpdateDevOpsProjectPipelineHandler). Doc("Update the specified pipeline of the DevOps project"). Param(webservice.PathParameter("devops", "DevOps project's ID, e.g. project-RRRRAzLBlLEm")). Param(webservice.PathParameter("pipeline", "the name of pipeline, e.g. sample-pipeline")). @@ -144,14 +150,14 @@ func addWebService(c *restful.Container) error { Reads(devops.ProjectPipeline{})) webservice.Route(webservice.DELETE("/devops/{devops}/pipelines/{pipeline}"). - To(devopsapi.DeleteDevOpsProjectPipelineHandler). + To(projectPipelineHander.DeleteDevOpsProjectPipelineHandler). Doc("Delete the specified pipeline of the DevOps project"). Metadata(restfulspec.KeyOpenAPITags, []string{constants.DevOpsPipelineTag}). Param(webservice.PathParameter("devops", "DevOps project's ID, e.g. project-RRRRAzLBlLEm")). Param(webservice.PathParameter("pipeline", "the name of pipeline, e.g. sample-pipeline"))) webservice.Route(webservice.GET("/devops/{devops}/pipelines/{pipeline}/config"). - To(devopsapi.GetDevOpsProjectPipelineHandler). + To(projectPipelineHander.GetDevOpsProjectPipelineConfigHandler). Doc("Get the configuration information of the specified pipeline of the DevOps Project"). Metadata(restfulspec.KeyOpenAPITags, []string{constants.DevOpsPipelineTag}). Param(webservice.PathParameter("devops", "DevOps project's ID, e.g. project-RRRRAzLBlLEm")). @@ -160,7 +166,7 @@ func addWebService(c *restful.Container) error { Writes(devops.ProjectPipeline{})) webservice.Route(webservice.GET("/devops/{devops}/pipelines/{pipeline}/sonarstatus"). - To(devopsapi.GetPipelineSonarStatusHandler). + To(sonarHandler.GetPipelineSonarStatusHandler). Doc("Get the sonar quality information for the specified pipeline of the DevOps project. More info: https://docs.sonarqube.org/7.4/user-guide/metric-definitions/"). Metadata(restfulspec.KeyOpenAPITags, []string{constants.DevOpsPipelineTag}). Param(webservice.PathParameter("devops", "DevOps project's ID, e.g. project-RRRRAzLBlLEm")). @@ -169,7 +175,7 @@ func addWebService(c *restful.Container) error { Writes([]devops.SonarStatus{})) webservice.Route(webservice.GET("/devops/{devops}/pipelines/{pipeline}/branches/{branch}/sonarstatus"). - To(devopsapi.GetMultiBranchesPipelineSonarStatusHandler). + To(sonarHandler.GetMultiBranchesPipelineSonarStatusHandler). Doc("Get the sonar quality check information for the specified pipeline branch of the DevOps project. More info: https://docs.sonarqube.org/7.4/user-guide/metric-definitions/"). Metadata(restfulspec.KeyOpenAPITags, []string{constants.DevOpsPipelineTag}). Param(webservice.PathParameter("devops", "DevOps project's ID, e.g. project-RRRRAzLBlLEm")). @@ -179,29 +185,29 @@ func addWebService(c *restful.Container) error { Writes([]devops.SonarStatus{})) webservice.Route(webservice.POST("/devops/{devops}/credentials"). - To(devopsapi.CreateDevOpsProjectCredentialHandler). + To(projectPipelineHander.CreateDevOpsProjectCredentialHandler). Doc("Create a credential in the specified DevOps project"). Metadata(restfulspec.KeyOpenAPITags, []string{constants.DevOpsProjectCredentialTag}). Param(webservice.PathParameter("devops", "DevOps project's ID, e.g. project-RRRRAzLBlLEm")). - Reads(devops.JenkinsCredential{})) + Reads(devops.Credential{})) webservice.Route(webservice.PUT("/devops/{devops}/credentials/{credential}"). - To(devopsapi.UpdateDevOpsProjectCredentialHandler). + To(projectPipelineHander.UpdateDevOpsProjectCredentialHandler). Doc("Update the specified credential of the DevOps project"). Metadata(restfulspec.KeyOpenAPITags, []string{constants.DevOpsProjectCredentialTag}). Param(webservice.PathParameter("devops", "DevOps project's ID, e.g. project-RRRRAzLBlLEm")). Param(webservice.PathParameter("credential", "credential's ID, e.g. dockerhub-id")). - Reads(devops.JenkinsCredential{})) + Reads(devops.Credential{})) webservice.Route(webservice.DELETE("/devops/{devops}/credentials/{credential}"). - To(devopsapi.DeleteDevOpsProjectCredentialHandler). + To(projectPipelineHander.DeleteDevOpsProjectCredentialHandler). Doc("Delete the specified credential of the DevOps project"). Metadata(restfulspec.KeyOpenAPITags, []string{constants.DevOpsProjectCredentialTag}). Param(webservice.PathParameter("devops", "DevOps project's ID, e.g. project-RRRRAzLBlLEm")). Param(webservice.PathParameter("credential", "credential's ID, e.g. dockerhub-id"))) webservice.Route(webservice.GET("/devops/{devops}/credentials/{credential}"). - To(devopsapi.GetDevOpsProjectCredentialHandler). + To(projectPipelineHander.GetDevOpsProjectCredentialHandler). Doc("Get the specified credential of the DevOps project"). Metadata(restfulspec.KeyOpenAPITags, []string{constants.DevOpsProjectCredentialTag}). Param(webservice.PathParameter("devops", "DevOps project's ID, e.g. project-RRRRAzLBlLEm")). @@ -212,18 +218,18 @@ Specifically, there are three types of info in a credential. One is the basic in The second one is non-encrypted info such as the username of the username-password type of credential, which returns when the "content" parameter is set to non-empty. The last one is encrypted info, such as the password of the username-password type of credential, which never returns. `)). - Returns(http.StatusOK, RespOK, devops.JenkinsCredential{})) + Returns(http.StatusOK, RespOK, devops.Credential{})) webservice.Route(webservice.GET("/devops/{devops}/credentials"). - To(devopsapi.GetDevOpsProjectCredentialsHandler). + To(projectPipelineHander.GetDevOpsProjectCredentialsHandler). Doc("Get all credentials of the specified DevOps project"). Metadata(restfulspec.KeyOpenAPITags, []string{constants.DevOpsProjectCredentialTag}). Param(webservice.PathParameter("devops", "DevOps project's ID, e.g. project-RRRRAzLBlLEm")). - Returns(http.StatusOK, RespOK, []devops.JenkinsCredential{})) + Returns(http.StatusOK, RespOK, []devops.Credential{})) // match Jenkisn api "/blue/rest/organizations/jenkins/pipelines/{devops}/{pipeline}" webservice.Route(webservice.GET("/devops/{devops}/pipelines/{pipeline}"). - To(devopsapi.GetPipeline). + To(projectPipelineHander.GetPipeline). Metadata(restfulspec.KeyOpenAPITags, []string{constants.DevOpsPipelineTag}). Doc("Get the specified pipeline of the DevOps project"). Param(webservice.PathParameter("devops", "DevOps project's ID, e.g. project-RRRRAzLBlLEm")). @@ -233,7 +239,7 @@ The last one is encrypted info, such as the password of the username-password ty // match Jenkisn api: "jenkins_api/blue/rest/search" webservice.Route(webservice.GET("/search"). - To(devopsapi.SearchPipelines). + To(projectPipelineHander.ListPipelines). Metadata(restfulspec.KeyOpenAPITags, []string{constants.DevOpsPipelineTag}). Doc("Search DevOps resource. More info: https://github.com/jenkinsci/blueocean-plugin/tree/master/blueocean-rest#get-pipelines-across-organization"). Param(webservice.QueryParameter("q", "query pipelines, condition for filtering."). @@ -248,18 +254,23 @@ The last one is encrypted info, such as the password of the username-password ty Param(webservice.QueryParameter("limit", "the limit item count of the search."). Required(false). DataFormat("limit=%d")). - Returns(http.StatusOK, RespOK, struct { - Items []devops.Pipeline `json:"items"` - Total int `json:"total_count"` - }{}). - Writes(struct { - Items []devops.Pipeline `json:"items"` - Total int `json:"total_count"` - }{})) + Returns(http.StatusOK, RespOK, devops.PipelineList{}). + Writes(devops.PipelineList{})) + + // match /blue/rest/organizations/jenkins/pipelines/{devops}/{pipeline}/runs/{run}/ + webservice.Route(webservice.GET("/devops/{devops}/pipelines/{pipeline}/runs/{run}"). + To(projectPipelineHander.GetPipelineRun). + Metadata(restfulspec.KeyOpenAPITags, []string{constants.DevOpsPipelineTag}). + Doc("Get details in the specified pipeline activity."). + Param(webservice.PathParameter("devops", "the name of devops project")). + Param(webservice.PathParameter("pipeline", "the name of the CI/CD pipeline")). + Param(webservice.PathParameter("run", "pipeline run ID, the unique ID for a pipeline once build.")). + Returns(http.StatusOK, RespOK, devops.PipelineRun{}). + Writes(devops.PipelineRun{})) // match Jenkisn api "/blue/rest/organizations/jenkins/pipelines/{devops}/{pipeline}/runs/" webservice.Route(webservice.GET("/devops/{devops}/pipelines/{pipeline}/runs"). - To(devopsapi.SearchPipelineRuns). + To(projectPipelineHander.ListPipelineRuns). Metadata(restfulspec.KeyOpenAPITags, []string{constants.DevOpsPipelineTag}). Doc("Get all runs of the specified pipeline"). Param(webservice.PathParameter("pipeline", "the name of the CI/CD pipeline")). @@ -273,63 +284,76 @@ The last one is encrypted info, such as the password of the username-password ty Param(webservice.QueryParameter("branch", "the name of branch, same as repository branch, will be filtered by branch."). Required(false). DataFormat("branch=%s")). - Returns(http.StatusOK, RespOK, struct { - Items []devops.BranchPipelineRun `json:"items"` - Total int `json:"total_count"` - }{}). - Writes(struct { - Items []devops.BranchPipelineRun `json:"items"` - Total int `json:"total_count"` - }{}). - Writes(struct { - Items []devops.BranchPipelineRun `json:"items"` - Total int `json:"total_count"` - }{}). - Writes(struct { - Items []devops.BranchPipelineRun `json:"items"` - Total int `json:"total_count"` - }{})) + Returns(http.StatusOK, RespOK, devops.PipelineRunList{}). + Writes(devops.PipelineRunList{})) - // match Jenkins api "/blue/rest/organizations/jenkins/pipelines/{devops}/{pipeline}/branches/{branch}/runs/{run}/" - webservice.Route(webservice.GET("/devops/{devops}/pipelines/{pipeline}/branches/{branch}/runs/{run}"). - To(devopsapi.GetBranchPipelineRun). + // match /blue/rest/organizations/jenkins/pipelines/{devops}/pipelines/{pipeline}/runs/{run}/stop/ + webservice.Route(webservice.POST("/devops/{devops}/pipelines/{pipeline}/runs/{run}/stop"). + To(projectPipelineHander.StopPipeline). Metadata(restfulspec.KeyOpenAPITags, []string{constants.DevOpsPipelineTag}). - Doc("(MultiBranchesPipeline) Get all runs in the specified branch"). + Doc("Stop pipeline"). Param(webservice.PathParameter("devops", "DevOps project's ID, e.g. project-RRRRAzLBlLEm")). Param(webservice.PathParameter("pipeline", "the name of the CI/CD pipeline")). - Param(webservice.PathParameter("branch", "the name of branch, same as repository branch.")). - Param(webservice.PathParameter("run", "pipeline run id, the unique id for a pipeline once build.")). - Returns(http.StatusOK, RespOK, devops.BranchPipelineRun{}). - Writes(devops.BranchPipelineRun{})) + Param(webservice.PathParameter("run", "pipeline run ID, the unique ID for a pipeline once build.")). + Param(webservice.QueryParameter("blocking", "stop and between each retries will sleep."). + Required(false). + DataFormat("blocking=%t"). + DefaultValue("blocking=false")). + Param(webservice.QueryParameter("timeOutInSecs", "the time of stop and between each retries sleep."). + Required(false). + DataFormat("timeOutInSecs=%d"). + DefaultValue("timeOutInSecs=10")). + Returns(http.StatusOK, RespOK, devops.StopPipeline{}). + Writes(devops.StopPipeline{})) - // match Jenkins api "/blue/rest/organizations/jenkins/pipelines/{devops}/{pipeline}/branches/{branch}/runs/{run}/nodes" - webservice.Route(webservice.GET("/devops/{devops}/pipelines/{pipeline}/branches/{branch}/runs/{run}/nodes"). - To(devopsapi.GetPipelineRunNodesbyBranch). + // match /blue/rest/organizations/jenkins/pipelines/{devops}/pipelines/{pipeline}/runs/{run}/Replay/ + webservice.Route(webservice.POST("/devops/{devops}/pipelines/{pipeline}/runs/{run}/replay"). + To(projectPipelineHander.ReplayPipeline). Metadata(restfulspec.KeyOpenAPITags, []string{constants.DevOpsPipelineTag}). - Doc("(MultiBranchesPipeline) Get run nodes."). + Doc("Replay pipeline"). Param(webservice.PathParameter("devops", "DevOps project's ID, e.g. project-RRRRAzLBlLEm")). Param(webservice.PathParameter("pipeline", "the name of the CI/CD pipeline")). - Param(webservice.PathParameter("branch", "the name of branch, same as repository branch.")). - Param(webservice.PathParameter("run", "pipeline run id, the unique id for a pipeline once build.")). + Param(webservice.PathParameter("run", "pipeline run ID, the unique ID for a pipeline once build.")). + Returns(http.StatusOK, RespOK, devops.ReplayPipeline{}). + Writes(devops.ReplayPipeline{})) + + // match /blue/rest/organizations/jenkins/pipelines/{devops}/{pipeline}/runs/ + webservice.Route(webservice.POST("/devops/{devops}/pipelines/{pipeline}/runs"). + To(projectPipelineHander.RunPipeline). + Metadata(restfulspec.KeyOpenAPITags, []string{constants.DevOpsPipelineTag}). + Doc("Run pipeline."). + Reads(devops.RunPayload{}). + Param(webservice.PathParameter("devops", "DevOps project's ID, e.g. project-RRRRAzLBlLEm")). + Param(webservice.PathParameter("pipeline", "the name of the CI/CD pipeline")). + Returns(http.StatusOK, RespOK, devops.RunPipeline{}). + Writes(devops.RunPipeline{})) + + // match /blue/rest/organizations/jenkins/pipelines/{devops}/{pipeline}/runs/{run}/artifacts + webservice.Route(webservice.GET("/devops/{devops}/pipelines/{pipeline}/runs/{run}/artifacts"). + To(projectPipelineHander.GetArtifacts). + Metadata(restfulspec.KeyOpenAPITags, []string{constants.DevOpsPipelineTag}). + Doc("Get all artifacts in the specified pipeline."). + Param(webservice.PathParameter("devops", "DevOps project's ID, e.g. project-RRRRAzLBlLEm")). + Param(webservice.PathParameter("pipeline", "the name of the CI/CD pipeline")). + Param(webservice.PathParameter("run", "pipeline run ID, the unique ID for a pipeline once build.")). + Param(webservice.QueryParameter("start", "the item number that the search starts from."). + Required(false). + DataFormat("start=%d")). Param(webservice.QueryParameter("limit", "the limit item count of the search."). Required(false). - DataFormat("limit=%d"). - DefaultValue("limit=10000")). - Returns(http.StatusOK, RespOK, []devops.BranchPipelineRunNodes{}). - Writes([]devops.BranchPipelineRunNodes{})) + DataFormat("limit=%d")). + Returns(http.StatusOK, "The filed of \"Url\" in response can download artifacts", []devops.Artifacts{}). + Writes([]devops.Artifacts{})) - // match "/blue/rest/organizations/jenkins/pipelines/{devops}/{pipeline}/branches/{branch}/runs/{run}/nodes/{node}/steps/{step}/log/?start=0" - webservice.Route(webservice.GET("/devops/{devops}/pipelines/{pipeline}/branches/{branch}/runs/{run}/nodes/{node}/steps/{step}/log"). - To(devopsapi.GetBranchStepLog). + // match /blue/rest/organizations/jenkins/pipelines/{devops}/{pipeline}/runs/{run}/log/?start=0 + webservice.Route(webservice.GET("/devops/{devops}/pipelines/{pipeline}/runs/{run}/log"). + To(projectPipelineHander.GetRunLog). Metadata(restfulspec.KeyOpenAPITags, []string{constants.DevOpsPipelineTag}). - Doc("(MultiBranchesPipeline) Get the step logs in the specified pipeline activity."). + Doc("Get run logs of the specified pipeline activity."). Produces("text/plain; charset=utf-8"). Param(webservice.PathParameter("devops", "DevOps project's ID, e.g. project-RRRRAzLBlLEm")). Param(webservice.PathParameter("pipeline", "the name of the CI/CD pipeline")). - Param(webservice.PathParameter("branch", "the name of branch, same as repository branch.")). - Param(webservice.PathParameter("run", "pipeline run id, the unique id for a pipeline once build.")). - Param(webservice.PathParameter("node", "pipeline node id, the stage in pipeline.")). - Param(webservice.PathParameter("step", "pipeline step id, the step in pipeline.")). + Param(webservice.PathParameter("run", "pipeline run ID, the unique ID for a pipeline once build.")). Param(webservice.QueryParameter("start", "the item number that the search starts from."). Required(false). DataFormat("start=%d"). @@ -337,7 +361,7 @@ The last one is encrypted info, such as the password of the username-password ty // match "/blue/rest/organizations/jenkins/pipelines/{devops}/{pipeline}/runs/{run}/nodes/{node}/steps/{step}/log/?start=0" webservice.Route(webservice.GET("/devops/{devops}/pipelines/{pipeline}/runs/{run}/nodes/{node}/steps/{step}/log"). - To(devopsapi.GetStepLog). + To(projectPipelineHander.GetStepLog). Metadata(restfulspec.KeyOpenAPITags, []string{constants.DevOpsPipelineTag}). Doc("Get pipelines step log."). Produces("text/plain; charset=utf-8"). @@ -351,92 +375,85 @@ The last one is encrypted info, such as the password of the username-password ty DataFormat("start=%d"). DefaultValue("start=0"))) - // match "/blue/rest/organizations/jenkins/scm/github/validate/" - webservice.Route(webservice.POST("/scms/{scm}/verify"). - To(devopsapi.Validate). - Metadata(restfulspec.KeyOpenAPITags, []string{constants.DevOpsScmTag}). - Doc("Validate the access token of the specified source configuration management (SCM) such as Github"). - Param(webservice.PathParameter("scm", "the ID of the source configuration management (SCM).")). - Returns(http.StatusOK, RespOK, devops.Validates{}). - Writes(devops.Validates{})) + // match /blue/rest/organizations/jenkins/pipelines/%s/%s/runs/%s/nodes/%s/steps/?limit= + webservice.Route(webservice.GET("/devops/{devops}/pipelines/{pipeline}/runs/{run}/nodes/{node}/steps"). + To(projectPipelineHander.GetNodeSteps). + Metadata(restfulspec.KeyOpenAPITags, []string{constants.DevOpsPipelineTag}). + Doc("Get all steps in the specified node."). + Param(webservice.PathParameter("devops", "the name of devops project")). + Param(webservice.PathParameter("pipeline", "the name of the CI/CD pipeline")). + Param(webservice.PathParameter("run", "pipeline run ID, the unique ID for a pipeline once build")). + Param(webservice.PathParameter("node", "pipeline node ID, the stage in pipeline.")). + Returns(http.StatusOK, RespOK, []devops.NodeSteps{}). + Writes([]devops.NodeSteps{})) - // match "/blue/rest/organizations/jenkins/scm/{scm}/organizations/?credentialId=github" - webservice.Route(webservice.GET("/scms/{scm}/organizations"). - To(devopsapi.GetSCMOrg). - Metadata(restfulspec.KeyOpenAPITags, []string{constants.DevOpsScmTag}). - Doc("List all organizations of the specified source configuration management (SCM) such as Github."). - Param(webservice.PathParameter("scm", "the ID of the source configuration management (SCM).")). - Param(webservice.QueryParameter("credentialId", "credential ID for source configuration management (SCM)."). - Required(true). - DataFormat("credentialId=%s")). - Returns(http.StatusOK, RespOK, []devops.SCMOrg{}). - Writes([]devops.SCMOrg{})) + // match /blue/rest/organizations/jenkins/pipelines/{devops}/pipelines/{pipeline}/runs/{run}/nodes/?limit=10000 + webservice.Route(webservice.GET("/devops/{devops}/pipelines/{pipeline}/runs/{run}/nodes"). + To(projectPipelineHander.GetPipelineRunNodes). + Metadata(restfulspec.KeyOpenAPITags, []string{constants.DevOpsPipelineTag}). + Doc("Get all nodes in the specified activity. node is the stage in the pipeline task"). + Param(webservice.PathParameter("devops", "the name of devops project")). + Param(webservice.PathParameter("pipeline", "the name of the CI/CD pipeline")). + Param(webservice.PathParameter("run", "pipeline run ID, the unique ID for a pipeline once build")). + Returns(http.StatusOK, RespOK, []devops.PipelineRunNodes{}). + Writes([]devops.PipelineRunNodes{})) - // match "/blue/rest/organizations/jenkins/scm/%s/servers/" - webservice.Route(webservice.GET("/scms/{scm}/servers"). - To(devopsapi.GetSCMServers). - Metadata(restfulspec.KeyOpenAPITags, []string{constants.DevOpsScmTag}). - Doc("List all servers in the jenkins."). - Param(webservice.PathParameter("scm", "The ID of the source configuration management (SCM).")). - Returns(http.StatusOK, RespOK, []devops.SCMServer{}). - Writes([]devops.SCMServer{})) + // match /blue/rest/organizations/jenkins/pipelines/{devops}/pipelines/{pipeline}/runs/{run}/nodes/{node}/steps/{step} + webservice.Route(webservice.POST("/devops/{devops}/pipelines/{pipeline}/runs/{run}/nodes/{node}/steps/{step}"). + To(projectPipelineHander.SubmitInputStep). + Metadata(restfulspec.KeyOpenAPITags, []string{constants.DevOpsPipelineTag}). + Doc("Proceed or Break the paused pipeline which is waiting for user input."). + Reads(devops.CheckPlayload{}). + Produces("text/plain; charset=utf-8"). + Param(webservice.PathParameter("devops", "DevOps project's ID, e.g. project-RRRRAzLBlLEm")). + Param(webservice.PathParameter("pipeline", "the name of the CI/CD pipeline")). + Param(webservice.PathParameter("run", "pipeline run ID, the unique ID for a pipeline once build.")). + Param(webservice.PathParameter("node", "pipeline node ID, the stage in pipeline.")). + Param(webservice.PathParameter("step", "pipeline step ID"))) - // match "/blue/rest/organizations/jenkins/scm/%s/servers/" - webservice.Route(webservice.POST("/scms/{scm}/servers"). - To(devopsapi.CreateSCMServers). - Metadata(restfulspec.KeyOpenAPITags, []string{constants.DevOpsScmTag}). - Doc("Create scm server in the jenkins."). - Param(webservice.PathParameter("scm", "The ID of the source configuration management (SCM).")). - Reads(devops.CreateScmServerReq{}). - Returns(http.StatusOK, RespOK, devops.SCMServer{}). - Writes(devops.SCMServer{})) + // out of scm get all steps in nodes. + webservice.Route(webservice.GET("/devops/{devops}/pipelines/{pipeline}/runs/{run}/nodesdetail"). + To(projectPipelineHander.GetNodesDetail). + Metadata(restfulspec.KeyOpenAPITags, []string{constants.DevOpsPipelineTag}). + Doc("Get steps details inside a activity node. For a node, the steps which defined inside the node."). + Param(webservice.PathParameter("devops", "DevOps project's ID, e.g. project-RRRRAzLBlLEm")). + Param(webservice.PathParameter("pipeline", "the name of the CI/CD pipeline")). + Param(webservice.PathParameter("branch", "the name of branch, same as repository branch.")). + Param(webservice.PathParameter("run", "pipeline run ID, the unique ID for a pipeline once build.")). + Returns(http.StatusOK, RespOK, []devops.NodesDetail{}). + Writes(devops.NodesDetail{})) - // match "/blue/rest/organizations/jenkins/scm/{scm}/organizations/{organization}/repositories/?credentialId=&pageNumber&pageSize=" - webservice.Route(webservice.GET("/scms/{scm}/organizations/{organization}/repositories"). - To(devopsapi.GetOrgRepo). - Metadata(restfulspec.KeyOpenAPITags, []string{constants.DevOpsScmTag}). - Doc("List all repositories in the specified organization."). - Param(webservice.PathParameter("scm", "The ID of the source configuration management (SCM).")). - Param(webservice.PathParameter("organization", "organization ID, such as github username.")). - Param(webservice.QueryParameter("credentialId", "credential ID for SCM."). - Required(true). - DataFormat("credentialId=%s")). - Param(webservice.QueryParameter("pageNumber", "page number."). - Required(true). - DataFormat("pageNumber=%d")). - Param(webservice.QueryParameter("pageSize", "the item count of one page."). - Required(true). - DataFormat("pageSize=%d")). - Returns(http.StatusOK, RespOK, []devops.OrgRepo{}). - Writes([]devops.OrgRepo{})) + // match /blue/rest/organizations/jenkins/pipelines/{devops}/pipelines/{pipeline}/branches/{branch} + webservice.Route(webservice.GET("/devops/{devops}/pipelines/{pipeline}/branches/{branch}"). + To(projectPipelineHander.GetBranchPipeline). + Metadata(restfulspec.KeyOpenAPITags, []string{constants.DevOpsPipelineTag}). + Doc("(MultiBranchesPipeline) Get the specified branch pipeline of the DevOps project"). + Param(webservice.PathParameter("devops", "the name of devops project")). + Param(webservice.PathParameter("pipeline", "the name of the CI/CD pipeline")). + Param(webservice.PathParameter("branch", "the name of branch, same as repository branch")). + Returns(http.StatusOK, RespOK, devops.BranchPipeline{}). + Writes(devops.BranchPipeline{})) - // match /blue/rest/organizations/jenkins/pipelines/{devops}/pipelines/{pipeline}/branches/{branch}/runs/{run}/stop/ - webservice.Route(webservice.POST("/devops/{devops}/pipelines/{pipeline}/branches/{branch}/runs/{run}/stop"). - To(devopsapi.StopBranchPipeline). + // match Jenkins api "/blue/rest/organizations/jenkins/pipelines/{devops}/{pipeline}/branches/{branch}/runs/{run}/" + webservice.Route(webservice.GET("/devops/{devops}/pipelines/{pipeline}/branches/{branch}/runs/{run}"). + To(projectPipelineHander.GetBranchPipelineRun). Metadata(restfulspec.KeyOpenAPITags, []string{constants.DevOpsPipelineTag}). - Doc("(MultiBranchesPipeline) Stop the specified pipeline of the DevOps project."). + Doc("(MultiBranchesPipeline) Get details in the specified pipeline activity."). Param(webservice.PathParameter("devops", "DevOps project's ID, e.g. project-RRRRAzLBlLEm")). Param(webservice.PathParameter("pipeline", "the name of the CI/CD pipeline")). Param(webservice.PathParameter("branch", "the name of branch, same as repository branch.")). - Param(webservice.PathParameter("run", "pipeline run ID, the unique ID for a pipeline once build.")). - Param(webservice.QueryParameter("blocking", "stop and between each retries will sleep."). - Required(false). - DataFormat("blocking=%t"). - DefaultValue("blocking=false")). - Param(webservice.QueryParameter("timeOutInSecs", "the time of stop and between each retries sleep."). - Required(false). - DataFormat("timeOutInSecs=%d"). - DefaultValue("timeOutInSecs=10")). - Returns(http.StatusOK, RespOK, devops.StopPipe{}). - Writes(devops.StopPipe{})) + Param(webservice.PathParameter("run", "pipeline run id, the unique id for a pipeline once build.")). + Returns(http.StatusOK, RespOK, devops.PipelineRun{}). + Writes(devops.PipelineRun{})) - // match /blue/rest/organizations/jenkins/pipelines/{devops}/pipelines/{pipeline}/runs/{run}/stop/ - webservice.Route(webservice.POST("/devops/{devops}/pipelines/{pipeline}/runs/{run}/stop"). - To(devopsapi.StopPipeline). + // match /blue/rest/organizations/jenkins/pipelines/{devops}/pipelines/{pipeline}/branches/{branch}/runs/{run}/stop/ + webservice.Route(webservice.POST("/devops/{devops}/pipelines/{pipeline}/branches/{branch}/runs/{run}/stop"). + To(projectPipelineHander.StopBranchPipeline). Metadata(restfulspec.KeyOpenAPITags, []string{constants.DevOpsPipelineTag}). - Doc("Stop pipeline"). + Doc("(MultiBranchesPipeline) Stop the specified pipeline of the DevOps project."). Param(webservice.PathParameter("devops", "DevOps project's ID, e.g. project-RRRRAzLBlLEm")). Param(webservice.PathParameter("pipeline", "the name of the CI/CD pipeline")). + Param(webservice.PathParameter("branch", "the name of branch, same as repository branch.")). Param(webservice.PathParameter("run", "pipeline run ID, the unique ID for a pipeline once build.")). Param(webservice.QueryParameter("blocking", "stop and between each retries will sleep."). Required(false). @@ -446,35 +463,54 @@ The last one is encrypted info, such as the password of the username-password ty Required(false). DataFormat("timeOutInSecs=%d"). DefaultValue("timeOutInSecs=10")). - Returns(http.StatusOK, RespOK, devops.StopPipe{}). - Writes(devops.StopPipe{})) + Returns(http.StatusOK, RespOK, devops.StopPipeline{}). + Writes(devops.StopPipeline{})) // match /blue/rest/organizations/jenkins/pipelines/{devops}/pipelines/{pipeline}/branches/{branch}/runs/{run}/Replay/ webservice.Route(webservice.POST("/devops/{devops}/pipelines/{pipeline}/branches/{branch}/runs/{run}/replay"). - To(devopsapi.ReplayBranchPipeline). + To(projectPipelineHander.ReplayBranchPipeline). Metadata(restfulspec.KeyOpenAPITags, []string{constants.DevOpsPipelineTag}). Doc("(MultiBranchesPipeline) Replay the specified pipeline of the DevOps project"). Param(webservice.PathParameter("devops", "DevOps project's ID, e.g. project-RRRRAzLBlLEm")). Param(webservice.PathParameter("pipeline", "the name of the CI/CD pipeline")). Param(webservice.PathParameter("branch", "the name of branch, same as repository branch.")). Param(webservice.PathParameter("run", "pipeline run ID, the unique ID for a pipeline once build.")). - Returns(http.StatusOK, RespOK, devops.ReplayPipe{}). - Writes(devops.ReplayPipe{})) + Returns(http.StatusOK, RespOK, devops.ReplayPipeline{}). + Writes(devops.ReplayPipeline{})) - // match /blue/rest/organizations/jenkins/pipelines/{devops}/pipelines/{pipeline}/runs/{run}/Replay/ - webservice.Route(webservice.POST("/devops/{devops}/pipelines/{pipeline}/runs/{run}/replay"). - To(devopsapi.ReplayPipeline). + // match /blue/rest/organizations/jenkins/pipelines/{devops}/{pipeline}/branches/{}/runs/ + webservice.Route(webservice.POST("/devops/{devops}/pipelines/{pipeline}/branches/{branch}/runs"). + To(projectPipelineHander.RunBranchPipeline). Metadata(restfulspec.KeyOpenAPITags, []string{constants.DevOpsPipelineTag}). - Doc("Replay pipeline"). + Doc("(MultiBranchesPipeline) Run the specified pipeline of the DevOps project."). + Reads(devops.RunPayload{}). + Param(webservice.PathParameter("devops", "DevOps project's ID, e.g. project-RRRRAzLBlLEm")). + Param(webservice.PathParameter("pipeline", "the name of the CI/CD pipeline")). + Param(webservice.PathParameter("branch", "the name of branch, same as repository branch.")). + Returns(http.StatusOK, RespOK, devops.RunPipeline{}). + Writes(devops.RunPipeline{})) + + // match /blue/rest/organizations/jenkins/pipelines/{devops}/{pipeline}/branches/{branch}/runs/{run}/artifacts + webservice.Route(webservice.GET("/devops/{devops}/pipelines/{pipeline}/branches/{branch}/runs/{run}/artifacts"). + To(projectPipelineHander.GetBranchArtifacts). + Metadata(restfulspec.KeyOpenAPITags, []string{constants.DevOpsPipelineTag}). + Doc("(MultiBranchesPipeline) Get all artifacts generated from the specified run of the pipeline branch."). Param(webservice.PathParameter("devops", "DevOps project's ID, e.g. project-RRRRAzLBlLEm")). Param(webservice.PathParameter("pipeline", "the name of the CI/CD pipeline")). + Param(webservice.PathParameter("branch", "the name of branch, same as repository branch.")). Param(webservice.PathParameter("run", "pipeline run ID, the unique ID for a pipeline once build.")). - Returns(http.StatusOK, RespOK, devops.ReplayPipe{}). - Writes(devops.ReplayPipe{})) + Param(webservice.QueryParameter("start", "the item number that the search starts from."). + Required(false). + DataFormat("start=%d")). + Param(webservice.QueryParameter("limit", "the limit item count of the search."). + Required(false). + DataFormat("limit=%d")). + Returns(http.StatusOK, "The filed of \"Url\" in response can download artifacts", []devops.Artifacts{}). + Writes([]devops.Artifacts{})) // match /blue/rest/organizations/jenkins/pipelines/{devops}/{pipeline}/branches/{branch}/runs/{run}/log/?start=0 webservice.Route(webservice.GET("/devops/{devops}/pipelines/{pipeline}/branches/{branch}/runs/{run}/log"). - To(devopsapi.GetBranchRunLog). + To(projectPipelineHander.GetBranchRunLog). Metadata(restfulspec.KeyOpenAPITags, []string{constants.DevOpsPipelineTag}). Doc("(MultiBranchesPipeline) Get run logs of the specified pipeline activity."). Produces("text/plain; charset=utf-8"). @@ -487,77 +523,55 @@ The last one is encrypted info, such as the password of the username-password ty DataFormat("start=%d"). DefaultValue("start=0"))) - // match /blue/rest/organizations/jenkins/pipelines/{devops}/{pipeline}/runs/{run}/log/?start=0 - webservice.Route(webservice.GET("/devops/{devops}/pipelines/{pipeline}/runs/{run}/log"). - To(devopsapi.GetRunLog). + // match "/blue/rest/organizations/jenkins/pipelines/{devops}/{pipeline}/branches/{branch}/runs/{run}/nodes/{node}/steps/{step}/log/?start=0" + webservice.Route(webservice.GET("/devops/{devops}/pipelines/{pipeline}/branches/{branch}/runs/{run}/nodes/{node}/steps/{step}/log"). + To(projectPipelineHander.GetBranchStepLog). Metadata(restfulspec.KeyOpenAPITags, []string{constants.DevOpsPipelineTag}). - Doc("Get run logs of the specified pipeline activity."). + Doc("(MultiBranchesPipeline) Get the step logs in the specified pipeline activity."). Produces("text/plain; charset=utf-8"). Param(webservice.PathParameter("devops", "DevOps project's ID, e.g. project-RRRRAzLBlLEm")). Param(webservice.PathParameter("pipeline", "the name of the CI/CD pipeline")). - Param(webservice.PathParameter("run", "pipeline run ID, the unique ID for a pipeline once build.")). + Param(webservice.PathParameter("branch", "the name of branch, same as repository branch.")). + Param(webservice.PathParameter("run", "pipeline run id, the unique id for a pipeline once build.")). + Param(webservice.PathParameter("node", "pipeline node id, the stage in pipeline.")). + Param(webservice.PathParameter("step", "pipeline step id, the step in pipeline.")). Param(webservice.QueryParameter("start", "the item number that the search starts from."). Required(false). DataFormat("start=%d"). DefaultValue("start=0"))) - // match /blue/rest/organizations/jenkins/pipelines/{devops}/{pipeline}/branches/{branch}/runs/{run}/artifacts - webservice.Route(webservice.GET("/devops/{devops}/pipelines/{pipeline}/branches/{branch}/runs/{run}/artifacts"). - To(devopsapi.GetBranchArtifacts). + // match /blue/rest/organizations/jenkins/pipelines/%s/%s/branches/%s/runs/%s/nodes/%s/steps/?limit= + webservice.Route(webservice.GET("/devops/{devops}/pipelines/{pipeline}/branches/{branch}/runs/{run}/nodes/{node}/steps"). + To(projectPipelineHander.GetBranchNodeSteps). Metadata(restfulspec.KeyOpenAPITags, []string{constants.DevOpsPipelineTag}). - Doc("(MultiBranchesPipeline) Get all artifacts generated from the specified run of the pipeline branch."). - Param(webservice.PathParameter("devops", "DevOps project's ID, e.g. project-RRRRAzLBlLEm")). + Doc("(MultiBranchesPipeline) Get all steps in the specified node."). + Param(webservice.PathParameter("devops", "the name of devops project")). Param(webservice.PathParameter("pipeline", "the name of the CI/CD pipeline")). Param(webservice.PathParameter("branch", "the name of branch, same as repository branch.")). Param(webservice.PathParameter("run", "pipeline run ID, the unique ID for a pipeline once build.")). - Param(webservice.QueryParameter("start", "the item number that the search starts from."). - Required(false). - DataFormat("start=%d")). - Param(webservice.QueryParameter("limit", "the limit item count of the search."). - Required(false). - DataFormat("limit=%d")). - Returns(http.StatusOK, "The filed of \"Url\" in response can download artifacts", []devops.Artifacts{}). - Writes([]devops.Artifacts{})) + Param(webservice.PathParameter("node", "pipeline node ID, the stage in pipeline.")). + Returns(http.StatusOK, RespOK, []devops.NodeSteps{}). + Writes([]devops.NodeSteps{})) - // match /blue/rest/organizations/jenkins/pipelines/{devops}/{pipeline}/runs/{run}/artifacts - webservice.Route(webservice.GET("/devops/{devops}/pipelines/{pipeline}/runs/{run}/artifacts"). - To(devopsapi.GetArtifacts). + // match Jenkins api "/blue/rest/organizations/jenkins/pipelines/{devops}/{pipeline}/branches/{branch}/runs/{run}/nodes" + webservice.Route(webservice.GET("/devops/{devops}/pipelines/{pipeline}/branches/{branch}/runs/{run}/nodes"). + To(projectPipelineHander.GetBranchPipelineRunNodes). Metadata(restfulspec.KeyOpenAPITags, []string{constants.DevOpsPipelineTag}). - Doc("Get all artifacts in the specified pipeline."). + Doc("(MultiBranchesPipeline) Get run nodes."). Param(webservice.PathParameter("devops", "DevOps project's ID, e.g. project-RRRRAzLBlLEm")). Param(webservice.PathParameter("pipeline", "the name of the CI/CD pipeline")). - Param(webservice.PathParameter("run", "pipeline run ID, the unique ID for a pipeline once build.")). - Param(webservice.QueryParameter("start", "the item number that the search starts from."). - Required(false). - DataFormat("start=%d")). + Param(webservice.PathParameter("branch", "the name of branch, same as repository branch.")). + Param(webservice.PathParameter("run", "pipeline run id, the unique id for a pipeline once build.")). Param(webservice.QueryParameter("limit", "the limit item count of the search."). Required(false). - DataFormat("limit=%d")). - Returns(http.StatusOK, "The filed of \"Url\" in response can download artifacts", []devops.Artifacts{}). - Writes([]devops.Artifacts{})) - - // match /blue/rest/organizations/jenkins/pipelines/{devops}/{pipeline}/branches/?filter=&start&limit= - webservice.Route(webservice.GET("/devops/{devops}/pipelines/{pipeline}/branches"). - To(devopsapi.GetPipeBranch). - Metadata(restfulspec.KeyOpenAPITags, []string{constants.DevOpsPipelineTag}). - Doc("(MultiBranchesPipeline) Get all branches in the specified pipeline."). - Param(webservice.PathParameter("devops", "DevOps project's ID, e.g. project-RRRRAzLBlLEm")). - Param(webservice.PathParameter("pipeline", "the name of the CI/CD pipeline")). - Param(webservice.QueryParameter("filter", "filter remote scm. e.g. origin"). - Required(false). - DataFormat("filter=%s")). - Param(webservice.QueryParameter("start", "the count of branches start."). - Required(false). - DataFormat("start=%d").DefaultValue("start=0")). - Param(webservice.QueryParameter("limit", "the count of branches limit."). - Required(false). - DataFormat("limit=%d").DefaultValue("limit=100")). - Returns(http.StatusOK, RespOK, []devops.PipeBranch{}). - Writes([]devops.PipeBranch{})) + DataFormat("limit=%d"). + DefaultValue("limit=10000")). + Returns(http.StatusOK, RespOK, []devops.BranchPipelineRunNodes{}). + Writes([]devops.BranchPipelineRunNodes{})) // /blue/rest/organizations/jenkins/pipelines/{devops}/pipelines/{pipeline}/branches/{branch}/runs/{run}/nodes/{node}/steps/{step} webservice.Route(webservice.POST("/devops/{devops}/pipelines/{pipeline}/branches/{branch}/runs/{run}/nodes/{node}/steps/{step}"). - To(devopsapi.SubmitBranchInputStep). + To(projectPipelineHander.SubmitBranchInputStep). Metadata(restfulspec.KeyOpenAPITags, []string{constants.DevOpsPipelineTag}). Doc("(MultiBranchesPipeline) Proceed or Break the paused pipeline which waiting for user input."). Param(webservice.PathParameter("devops", "DevOps project's ID, e.g. project-RRRRAzLBlLEm")). @@ -569,31 +583,40 @@ The last one is encrypted info, such as the password of the username-password ty Reads(devops.CheckPlayload{}). Produces("text/plain; charset=utf-8")) - // match /blue/rest/organizations/jenkins/pipelines/{devops}/pipelines/{pipeline}/runs/{run}/nodes/{node}/steps/{step} - webservice.Route(webservice.POST("/devops/{devops}/pipelines/{pipeline}/runs/{run}/nodes/{node}/steps/{step}"). - To(devopsapi.SubmitInputStep). + // in scm get all steps in nodes. + webservice.Route(webservice.GET("/devops/{devops}/pipelines/{pipeline}/branches/{branch}/runs/{run}/nodesdetail"). + To(projectPipelineHander.GetBranchNodesDetail). Metadata(restfulspec.KeyOpenAPITags, []string{constants.DevOpsPipelineTag}). - Doc("Proceed or Break the paused pipeline which is waiting for user input."). - Reads(devops.CheckPlayload{}). - Produces("text/plain; charset=utf-8"). + Doc("(MultiBranchesPipeline) Get steps details in an activity node. For a node, the steps which is defined inside the node."). Param(webservice.PathParameter("devops", "DevOps project's ID, e.g. project-RRRRAzLBlLEm")). Param(webservice.PathParameter("pipeline", "the name of the CI/CD pipeline")). + Param(webservice.PathParameter("branch", "the name of branch, same as repository branch.")). Param(webservice.PathParameter("run", "pipeline run ID, the unique ID for a pipeline once build.")). - Param(webservice.PathParameter("node", "pipeline node ID, the stage in pipeline.")). - Param(webservice.PathParameter("step", "pipeline step ID"))) + Returns(http.StatusOK, RespOK, []devops.NodesDetail{}). + Writes(devops.NodesDetail{})) - // match /job/project-8QnvykoJw4wZ/job/test-1/indexing/consoleText - webservice.Route(webservice.GET("/devops/{devops}/pipelines/{pipeline}/consolelog"). - To(devopsapi.GetConsoleLog). + // match /blue/rest/organizations/jenkins/pipelines/{devops}/{pipeline}/branches/?filter=&start&limit= + webservice.Route(webservice.GET("/devops/{devops}/pipelines/{pipeline}/branches"). + To(projectPipelineHander.GetPipelineBranch). Metadata(restfulspec.KeyOpenAPITags, []string{constants.DevOpsPipelineTag}). - Doc("Get scan reponsitory logs in the specified pipeline."). - Produces("text/plain; charset=utf-8"). + Doc("(MultiBranchesPipeline) Get all branches in the specified pipeline."). Param(webservice.PathParameter("devops", "DevOps project's ID, e.g. project-RRRRAzLBlLEm")). - Param(webservice.PathParameter("pipeline", "the name of the CI/CD pipeline"))) + Param(webservice.PathParameter("pipeline", "the name of the CI/CD pipeline")). + Param(webservice.QueryParameter("filter", "filter remote scm. e.g. origin"). + Required(false). + DataFormat("filter=%s")). + Param(webservice.QueryParameter("start", "the count of branches start."). + Required(false). + DataFormat("start=%d").DefaultValue("start=0")). + Param(webservice.QueryParameter("limit", "the count of branches limit."). + Required(false). + DataFormat("limit=%d").DefaultValue("limit=100")). + Returns(http.StatusOK, RespOK, []devops.PipelineBranch{}). + Writes([]devops.PipelineBranch{})) // match /job/{devops}/job/{pipeline}/build?delay=0 webservice.Route(webservice.POST("/devops/{devops}/pipelines/{pipeline}/scan"). - To(devopsapi.ScanBranch). + To(projectPipelineHander.ScanBranch). Metadata(restfulspec.KeyOpenAPITags, []string{constants.DevOpsPipelineTag}). Doc("Scan remote Repository, Start a build if have new branch."). Produces("text/html; charset=utf-8"). @@ -603,39 +626,111 @@ The last one is encrypted info, such as the password of the username-password ty Required(false). DataFormat("delay=%d"))) - // match /blue/rest/organizations/jenkins/pipelines/{devops}/{pipeline}/branches/{}/runs/ - webservice.Route(webservice.POST("/devops/{devops}/pipelines/{pipeline}/branches/{branch}/runs"). - To(devopsapi.RunBranchPipeline). - Metadata(restfulspec.KeyOpenAPITags, []string{constants.DevOpsPipelineTag}). - Doc("(MultiBranchesPipeline) Run the specified pipeline of the DevOps project."). - Reads(devops.RunPayload{}). - Param(webservice.PathParameter("devops", "DevOps project's ID, e.g. project-RRRRAzLBlLEm")). - Param(webservice.PathParameter("pipeline", "the name of the CI/CD pipeline")). - Param(webservice.PathParameter("branch", "the name of branch, same as repository branch.")). - Returns(http.StatusOK, RespOK, devops.QueuedBlueRun{}). - Writes(devops.QueuedBlueRun{})) - - // match /blue/rest/organizations/jenkins/pipelines/{devops}/{pipeline}/runs/ - webservice.Route(webservice.POST("/devops/{devops}/pipelines/{pipeline}/runs"). - To(devopsapi.RunPipeline). + // match /job/project-8QnvykoJw4wZ/job/test-1/indexing/consoleText + webservice.Route(webservice.GET("/devops/{devops}/pipelines/{pipeline}/consolelog"). + To(projectPipelineHander.GetConsoleLog). Metadata(restfulspec.KeyOpenAPITags, []string{constants.DevOpsPipelineTag}). - Doc("Run pipeline."). - Reads(devops.RunPayload{}). + Doc("Get scan reponsitory logs in the specified pipeline."). + Produces("text/plain; charset=utf-8"). Param(webservice.PathParameter("devops", "DevOps project's ID, e.g. project-RRRRAzLBlLEm")). - Param(webservice.PathParameter("pipeline", "the name of the CI/CD pipeline")). - Returns(http.StatusOK, RespOK, devops.QueuedBlueRun{}). - Writes(devops.QueuedBlueRun{})) + Param(webservice.PathParameter("pipeline", "the name of the CI/CD pipeline"))) // match /crumbIssuer/api/json/ webservice.Route(webservice.GET("/crumbissuer"). - To(devopsapi.GetCrumb). + To(projectPipelineHander.GetCrumb). Metadata(restfulspec.KeyOpenAPITags, []string{constants.DevOpsPipelineTag}). Doc("Get crumb issuer. A CrumbIssuer represents an algorithm to generate a nonce value, known as a crumb, to counter cross site request forgery exploits. Crumbs are typically hashes incorporating information that uniquely identifies an agent that sends a request, along with a guarded secret so that the crumb value cannot be forged by a third party."). Returns(http.StatusOK, RespOK, devops.Crumb{}). Writes(devops.Crumb{})) + // match "/blue/rest/organizations/jenkins/scm/%s/servers/" + webservice.Route(webservice.GET("/scms/{scm}/servers"). + To(projectPipelineHander.GetSCMServers). + Metadata(restfulspec.KeyOpenAPITags, []string{constants.DevOpsScmTag}). + Doc("List all servers in the jenkins."). + Param(webservice.PathParameter("scm", "The ID of the source configuration management (SCM).")). + Returns(http.StatusOK, RespOK, []devops.SCMServer{}). + Writes([]devops.SCMServer{})) + + // match "/blue/rest/organizations/jenkins/scm/{scm}/organizations/?credentialId=github" + webservice.Route(webservice.GET("/scms/{scm}/organizations"). + To(projectPipelineHander.GetSCMOrg). + Metadata(restfulspec.KeyOpenAPITags, []string{constants.DevOpsScmTag}). + Doc("List all organizations of the specified source configuration management (SCM) such as Github."). + Param(webservice.PathParameter("scm", "the ID of the source configuration management (SCM).")). + Param(webservice.QueryParameter("credentialId", "credential ID for source configuration management (SCM)."). + Required(true). + DataFormat("credentialId=%s")). + Returns(http.StatusOK, RespOK, []devops.SCMOrg{}). + Writes([]devops.SCMOrg{})) + + // match "/blue/rest/organizations/jenkins/scm/{scm}/organizations/{organization}/repositories/?credentialId=&pageNumber&pageSize=" + webservice.Route(webservice.GET("/scms/{scm}/organizations/{organization}/repositories"). + To(projectPipelineHander.GetOrgRepo). + Metadata(restfulspec.KeyOpenAPITags, []string{constants.DevOpsScmTag}). + Doc("List all repositories in the specified organization."). + Param(webservice.PathParameter("scm", "The ID of the source configuration management (SCM).")). + Param(webservice.PathParameter("organization", "organization ID, such as github username.")). + Param(webservice.QueryParameter("credentialId", "credential ID for SCM."). + Required(true). + DataFormat("credentialId=%s")). + Param(webservice.QueryParameter("pageNumber", "page number."). + Required(true). + DataFormat("pageNumber=%d")). + Param(webservice.QueryParameter("pageSize", "the item count of one page."). + Required(true). + DataFormat("pageSize=%d")). + Returns(http.StatusOK, RespOK, []devops.OrgRepo{}). + Writes([]devops.OrgRepo{})) + + // match "/blue/rest/organizations/jenkins/scm/%s/servers/" create bitbucket server + webservice.Route(webservice.POST("/scms/{scm}/servers"). + To(projectPipelineHander.CreateSCMServers). + Metadata(restfulspec.KeyOpenAPITags, []string{constants.DevOpsScmTag}). + Doc("Create scm server in the jenkins."). + Param(webservice.PathParameter("scm", "The ID of the source configuration management (SCM).")). + Reads(devops.CreateScmServerReq{}). + Returns(http.StatusOK, RespOK, devops.SCMServer{}). + Writes(devops.SCMServer{})) + + // match "/blue/rest/organizations/jenkins/scm/github/validate/" + webservice.Route(webservice.POST("/scms/{scm}/verify"). + To(projectPipelineHander.Validate). + Metadata(restfulspec.KeyOpenAPITags, []string{constants.DevOpsScmTag}). + Doc("Validate the access token of the specified source configuration management (SCM) such as Github"). + Param(webservice.PathParameter("scm", "the ID of the source configuration management (SCM).")). + Returns(http.StatusOK, RespOK, devops.Validates{}). + Writes(devops.Validates{})) + + // match /git/notifyCommit/?url= + webservice.Route(webservice.GET("/webhook/git"). + To(projectPipelineHander.GetNotifyCommit). + Metadata(restfulspec.KeyOpenAPITags, []string{constants.DevOpsWebhookTag}). + Doc("Get commit notification by HTTP GET method. Git webhook will request here."). + Produces("text/plain; charset=utf-8"). + Param(webservice.QueryParameter("url", "Git url"). + Required(true). + DataFormat("url=%s"))) + + // Gitlab or some other scm managers can only use HTTP method. match /git/notifyCommit/?url= + webservice.Route(webservice.POST("/webhook/git"). + To(projectPipelineHander.PostNotifyCommit). + Metadata(restfulspec.KeyOpenAPITags, []string{constants.DevOpsWebhookTag}). + Doc("Get commit notification by HTTP POST method. Git webhook will request here."). + Consumes("application/json"). + Produces("text/plain; charset=utf-8"). + Param(webservice.QueryParameter("url", "Git url"). + Required(true). + DataFormat("url=%s"))) + + webservice.Route(webservice.POST("/webhook/github"). + To(projectPipelineHander.GithubWebhook). + Consumes("application/x-www-form-urlencoded", "application/json"). + Metadata(restfulspec.KeyOpenAPITags, []string{constants.DevOpsWebhookTag}). + Doc("Get commit notification. Github webhook will request here.")) + webservice.Route(webservice.PUT("/namespaces/{namespace}/s2ibinaries/{s2ibinary}/file"). - To(devopsapi.UploadS2iBinary). + To(s2iHandler.UploadS2iBinaryHandler). Consumes("multipart/form-data"). Produces(restful.MIME_JSON). Doc("Upload S2iBinary file"). @@ -646,7 +741,7 @@ The last one is encrypted info, such as the password of the username-password ty Returns(http.StatusOK, RespOK, devopsv1alpha1.S2iBinary{})) webservice.Route(webservice.GET("/namespaces/{namespace}/s2ibinaries/{s2ibinary}/file/{file}"). - To(devopsapi.DownloadS2iBinary). + To(s2iHandler.DownloadS2iBinaryHandler). Produces(restful.MIME_OCTET). Doc("Download S2iBinary file"). Param(webservice.PathParameter("namespace", "the name of namespaces")). @@ -655,7 +750,7 @@ The last one is encrypted info, such as the password of the username-password ty Returns(http.StatusOK, RespOK, nil)) webservice.Route(webservice.POST("/devops/{devops}/pipelines/{pipeline}/checkScriptCompile"). - To(devopsapi.CheckScriptCompile). + To(projectPipelineHander.CheckScriptCompile). Metadata(restfulspec.KeyOpenAPITags, []string{constants.DevOpsPipelineTag}). Param(webservice.PathParameter("devops", "DevOps project's ID, e.g. project-RRRRAzLBlLEm")). Param(webservice.QueryParameter("pipeline", "the name of the CI/CD pipeline"). @@ -669,7 +764,7 @@ The last one is encrypted info, such as the password of the username-password ty Writes(devops.CheckScript{})) webservice.Route(webservice.POST("/devops/{devops}/checkCron"). - To(devopsapi.CheckCron). + To(projectPipelineHander.CheckCron). Metadata(restfulspec.KeyOpenAPITags, []string{constants.DevOpsPipelineTag}). Param(webservice.PathParameter("devops", "DevOps project's ID, e.g. project-RRRRAzLBlLEm")). Param(webservice.PathParameter("pipeline", "the name of the CI/CD pipeline")). @@ -679,67 +774,9 @@ The last one is encrypted info, such as the password of the username-password ty Returns(http.StatusOK, RespOK, devops.CheckCronRes{}). Writes(devops.CheckCronRes{})) - // match /blue/rest/organizations/jenkins/pipelines/{devops}/{pipeline}/runs/{run}/ - webservice.Route(webservice.GET("/devops/{devops}/pipelines/{pipeline}/runs/{run}"). - To(devopsapi.GetPipelineRun). - Metadata(restfulspec.KeyOpenAPITags, []string{constants.DevOpsPipelineTag}). - Doc("Get all activities in the specified pipeline."). - Param(webservice.PathParameter("devops", "the name of devops project")). - Param(webservice.PathParameter("pipeline", "the name of the CI/CD pipeline")). - Param(webservice.PathParameter("run", "pipeline run ID, the unique ID for a pipeline once build.")). - Returns(http.StatusOK, RespOK, devops.PipelineRun{}). - Writes(devops.PipelineRun{})) - - // match /blue/rest/organizations/jenkins/pipelines/{devops}/pipelines/{pipeline}/branches/{branch} - webservice.Route(webservice.GET("/devops/{devops}/pipelines/{pipeline}/branches/{branch}"). - To(devopsapi.GetBranchPipeline). - Metadata(restfulspec.KeyOpenAPITags, []string{constants.DevOpsPipelineTag}). - Doc("(MultiBranchesPipeline) Get all activities in the specified pipeline."). - Param(webservice.PathParameter("devops", "the name of devops project")). - Param(webservice.PathParameter("pipeline", "the name of the CI/CD pipeline")). - Param(webservice.PathParameter("branch", "the name of branch, same as repository branch")). - Returns(http.StatusOK, RespOK, devops.BranchPipeline{}). - Writes(devops.BranchPipeline{})) - - // match /blue/rest/organizations/jenkins/pipelines/{devops}/pipelines/{pipeline}/runs/{run}/nodes/?limit=10000 - webservice.Route(webservice.GET("/devops/{devops}/pipelines/{pipeline}/runs/{run}/nodes"). - To(devopsapi.GetPipelineRunNodes). - Metadata(restfulspec.KeyOpenAPITags, []string{constants.DevOpsPipelineTag}). - Doc("Get all nodes in the specified activity. node is the stage in the pipeline task"). - Param(webservice.PathParameter("devops", "the name of devops project")). - Param(webservice.PathParameter("pipeline", "the name of the CI/CD pipeline")). - Param(webservice.PathParameter("run", "pipeline run ID, the unique ID for a pipeline once build")). - Returns(http.StatusOK, RespOK, []devops.PipelineRunNodes{}). - Writes([]devops.PipelineRunNodes{})) - - // match /blue/rest/organizations/jenkins/pipelines/%s/%s/branches/%s/runs/%s/nodes/%s/steps/?limit= - webservice.Route(webservice.GET("/devops/{devops}/pipelines/{pipeline}/branches/{branch}/runs/{run}/nodes/{node}/steps"). - To(devopsapi.GetBranchNodeSteps). - Metadata(restfulspec.KeyOpenAPITags, []string{constants.DevOpsPipelineTag}). - Doc("(MultiBranchesPipeline) Get all steps in the specified node."). - Param(webservice.PathParameter("devops", "the name of devops project")). - Param(webservice.PathParameter("pipeline", "the name of the CI/CD pipeline")). - Param(webservice.PathParameter("branch", "the name of branch, same as repository branch.")). - Param(webservice.PathParameter("run", "pipeline run ID, the unique ID for a pipeline once build.")). - Param(webservice.PathParameter("node", "pipeline node ID, the stage in pipeline.")). - Returns(http.StatusOK, RespOK, []devops.NodeSteps{}). - Writes([]devops.NodeSteps{})) - - // match /blue/rest/organizations/jenkins/pipelines/%s/%s/runs/%s/nodes/%s/steps/?limit= - webservice.Route(webservice.GET("/devops/{devops}/pipelines/{pipeline}/runs/{run}/nodes/{node}/steps"). - To(devopsapi.GetNodeSteps). - Metadata(restfulspec.KeyOpenAPITags, []string{constants.DevOpsPipelineTag}). - Doc("Get all steps in the specified node."). - Param(webservice.PathParameter("devops", "the name of devops project")). - Param(webservice.PathParameter("pipeline", "the name of the CI/CD pipeline")). - Param(webservice.PathParameter("run", "pipeline run ID, the unique ID for a pipeline once build")). - Param(webservice.PathParameter("node", "pipeline node ID, the stage in pipeline.")). - Returns(http.StatusOK, RespOK, []devops.NodeSteps{}). - Writes([]devops.NodeSteps{})) - // match /pipeline-model-converter/toJenkinsfile webservice.Route(webservice.POST("/tojenkinsfile"). - To(devopsapi.ToJenkinsfile). + To(projectPipelineHander.ToJenkinsfile). Metadata(restfulspec.KeyOpenAPITags, []string{constants.DevOpsJenkinsfileTag}). Consumes("application/x-www-form-urlencoded"). Produces("application/json", "charset=utf-8"). @@ -750,7 +787,7 @@ The last one is encrypted info, such as the password of the username-password ty // match /pipeline-model-converter/toJson webservice.Route(webservice.POST("/tojson"). - To(devopsapi.ToJson). + To(projectPipelineHander.ToJson). Metadata(restfulspec.KeyOpenAPITags, []string{constants.DevOpsJenkinsfileTag}). Consumes("application/x-www-form-urlencoded"). Produces("application/json", "charset=utf-8"). @@ -759,57 +796,6 @@ The last one is encrypted info, such as the password of the username-password ty Returns(http.StatusOK, RespOK, devops.ResJson{}). Writes(devops.ResJson{})) - // match /git/notifyCommit/?url= - webservice.Route(webservice.GET("/webhook/git"). - To(devopsapi.GetNotifyCommit). - Metadata(restfulspec.KeyOpenAPITags, []string{constants.DevOpsWebhookTag}). - Doc("Get commit notification by HTTP GET method. Git webhook will request here."). - Produces("text/plain; charset=utf-8"). - Param(webservice.QueryParameter("url", "Git url"). - Required(true). - DataFormat("url=%s"))) - - // Gitlab or some other scm managers can only use HTTP method. match /git/notifyCommit/?url= - webservice.Route(webservice.POST("/webhook/git"). - To(devopsapi.PostNotifyCommit). - Metadata(restfulspec.KeyOpenAPITags, []string{constants.DevOpsWebhookTag}). - Doc("Get commit notification by HTTP POST method. Git webhook will request here."). - Consumes("application/json"). - Produces("text/plain; charset=utf-8"). - Param(webservice.QueryParameter("url", "Git url"). - Required(true). - DataFormat("url=%s"))) - - webservice.Route(webservice.POST("/webhook/github"). - To(devopsapi.GithubWebhook). - Consumes("application/x-www-form-urlencoded", "application/json"). - Metadata(restfulspec.KeyOpenAPITags, []string{constants.DevOpsWebhookTag}). - Doc("Get commit notification. Github webhook will request here.")) - - // in scm get all steps in nodes. - webservice.Route(webservice.GET("/devops/{devops}/pipelines/{pipeline}/branches/{branch}/runs/{run}/nodesdetail"). - To(devopsapi.GetBranchNodesDetail). - Metadata(restfulspec.KeyOpenAPITags, []string{constants.DevOpsPipelineTag}). - Doc("(MultiBranchesPipeline) Get steps details in an activity node. For a node, the steps which is defined inside the node."). - Param(webservice.PathParameter("devops", "DevOps project's ID, e.g. project-RRRRAzLBlLEm")). - Param(webservice.PathParameter("pipeline", "the name of the CI/CD pipeline")). - Param(webservice.PathParameter("branch", "the name of branch, same as repository branch.")). - Param(webservice.PathParameter("run", "pipeline run ID, the unique ID for a pipeline once build.")). - Returns(http.StatusOK, RespOK, []devops.NodesDetail{}). - Writes(devops.NodesDetail{})) - - // out of scm get all steps in nodes. - webservice.Route(webservice.GET("/devops/{devops}/pipelines/{pipeline}/runs/{run}/nodesdetail"). - To(devopsapi.GetNodesDetail). - Metadata(restfulspec.KeyOpenAPITags, []string{constants.DevOpsPipelineTag}). - Doc("Get steps details inside a activity node. For a node, the steps which defined inside the node."). - Param(webservice.PathParameter("devops", "DevOps project's ID, e.g. project-RRRRAzLBlLEm")). - Param(webservice.PathParameter("pipeline", "the name of the CI/CD pipeline")). - Param(webservice.PathParameter("branch", "the name of branch, same as repository branch.")). - Param(webservice.PathParameter("run", "pipeline run ID, the unique ID for a pipeline once build.")). - Returns(http.StatusOK, RespOK, []devops.NodesDetail{}). - Writes(devops.NodesDetail{})) - c.Add(webservice) return nil diff --git a/pkg/apiserver/devops/s2ibinary.go b/pkg/kapis/devops/v1alpha2/s2ibinary.go similarity index 82% rename from pkg/apiserver/devops/s2ibinary.go rename to pkg/kapis/devops/v1alpha2/s2ibinary.go index bf4eeee5..0ce781f5 100644 --- a/pkg/apiserver/devops/s2ibinary.go +++ b/pkg/kapis/devops/v1alpha2/s2ibinary.go @@ -1,4 +1,4 @@ -package devops +package v1alpha2 import ( "code.cloudfoundry.org/bytefmt" @@ -11,7 +11,11 @@ import ( "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") 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 { klog.Errorf("%+v", err) 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") name := req.PathParameter("s2ibinary") fileName := req.PathParameter("file") - url, err := devops.DownloadS2iBinary(ns, name, fileName) + url, err := h.s2iUploader.DownloadS2iBinary(ns, name, fileName) if err != nil { klog.Errorf("%+v", err) errors.ParseSvcErr(err, resp) diff --git a/pkg/kapis/tenant/v1alpha2/devops.go b/pkg/kapis/tenant/v1alpha2/devops.go new file mode 100644 index 00000000..1295f42b --- /dev/null +++ b/pkg/kapis/tenant/v1alpha2/devops.go @@ -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) +} diff --git a/pkg/kapis/tenant/v1alpha2/handler.go b/pkg/kapis/tenant/v1alpha2/handler.go index e068fb14..b4a70208 100644 --- a/pkg/kapis/tenant/v1alpha2/handler.go +++ b/pkg/kapis/tenant/v1alpha2/handler.go @@ -8,7 +8,6 @@ import ( "k8s.io/apimachinery/pkg/util/net" "k8s.io/klog" "kubesphere.io/kubesphere/pkg/api" - devopsv1alpha2 "kubesphere.io/kubesphere/pkg/api/devops/v1alpha2" loggingv1alpha2 "kubesphere.io/kubesphere/pkg/api/logging/v1alpha2" "kubesphere.io/kubesphere/pkg/apiserver/logging" "kubesphere.io/kubesphere/pkg/constants" @@ -184,45 +183,6 @@ func (h *tenantHandler) DeleteNamespace(req *restful.Request, resp *restful.Resp 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) { projectId := req.PathParameter("devops") workspace := req.PathParameter("workspace") @@ -235,7 +195,7 @@ func (h *tenantHandler) DeleteDevopsProject(req *restful.Request, resp *restful. return } - err = tenant.DeleteDevOpsProject(projectId, username) + err = h.tenant.DeleteDevOpsProject(projectId, username) if err != nil { api.HandleInternalError(resp, err) @@ -245,30 +205,6 @@ func (h *tenantHandler) DeleteDevopsProject(req *restful.Request, resp *restful. 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) { namespace := req.PathParameter("namespace") username := req.HeaderParameter(constants.UserNameHeader) @@ -283,21 +219,6 @@ func (h *tenantHandler) ListNamespaceRules(req *restful.Request, resp *restful.R 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) { operation := req.QueryParameter("operation") req, err := h.regenerateLoggingRequest(req) diff --git a/pkg/kapis/tenant/v1alpha2/register.go b/pkg/kapis/tenant/v1alpha2/register.go index bca9d087..cc1d61c7 100644 --- a/pkg/kapis/tenant/v1alpha2/register.go +++ b/pkg/kapis/tenant/v1alpha2/register.go @@ -103,7 +103,7 @@ func AddToContainer(c *restful.Container) error { Metadata(restfulspec.KeyOpenAPITags, []string{constants.TenantResourcesTag})) ws.Route(ws.GET("/workspaces/{workspace}/devops"). - To(handler.ListDevopsProjects). + To(handler.ListDevOpsProjectsHandler). Param(ws.PathParameter("workspace", "workspace name")). Param(ws.QueryParameter(params.PagingParam, "page"). Required(false). @@ -115,7 +115,7 @@ func AddToContainer(c *restful.Container) error { Doc("List devops projects for the current user"). Metadata(restfulspec.KeyOpenAPITags, []string{constants.TenantResourcesTag})) ws.Route(ws.GET("/workspaces/{workspace}/members/{member}/devops"). - To(handler.ListDevopsProjects). + To(handler.ListDevOpsProjectsHandler). Param(ws.PathParameter("workspace", "workspace name")). Param(ws.PathParameter("member", "workspace member's username")). 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"). Metadata(restfulspec.KeyOpenAPITags, []string{constants.TenantResourcesTag})) ws.Route(ws.GET("/devopscount"). - To(handler.GetDevOpsProjectsCount). + To(handler.GetDevOpsProjectsCountHandler). Returns(http.StatusOK, api.StatusOK, struct { Count uint32 `json:"count"` }{}). Doc("Get the devops projects count for the member"). Metadata(restfulspec.KeyOpenAPITags, []string{constants.TenantResourcesTag})) ws.Route(ws.POST("/workspaces/{workspace}/devops"). - To(handler.CreateDevopsProject). + To(handler.CreateDevOpsProjectHandler). Param(ws.PathParameter("workspace", "workspace name")). Doc("Create a devops project in the specified workspace"). Reads(devopsv1alpha2.DevOpsProject{}). diff --git a/pkg/kapis/terminal/v1alpha2/handler.go b/pkg/kapis/terminal/v1alpha2/handler.go index af39e58c..c8e6ea55 100644 --- a/pkg/kapis/terminal/v1alpha2/handler.go +++ b/pkg/kapis/terminal/v1alpha2/handler.go @@ -1,43 +1,43 @@ package v1alpha2 import ( - "github.com/emicklei/go-restful" - "github.com/gorilla/websocket" - "k8s.io/client-go/kubernetes" - "k8s.io/client-go/rest" - "k8s.io/klog" - "kubesphere.io/kubesphere/pkg/models/terminal" - "net/http" + "github.com/emicklei/go-restful" + "github.com/gorilla/websocket" + "k8s.io/client-go/kubernetes" + "k8s.io/client-go/rest" + "k8s.io/klog" + "kubesphere.io/kubesphere/pkg/models/terminal" + "net/http" ) var upgrader = websocket.Upgrader{ - ReadBufferSize: 1024, - WriteBufferSize: 1024, - // Allow connections from any Origin - CheckOrigin: func(r *http.Request) bool { return true }, + ReadBufferSize: 1024, + WriteBufferSize: 1024, + // Allow connections from any Origin + CheckOrigin: func(r *http.Request) bool { return true }, } type terminalHandler struct { - terminaler terminal.Interface + terminaler terminal.Interface } func newTerminalHandler(client kubernetes.Interface, config *rest.Config) *terminalHandler { - return &terminalHandler{ - terminaler: terminal.NewTerminaler(client, config), - } + return &terminalHandler{ + terminaler: terminal.NewTerminaler(client, config), + } } func (t *terminalHandler) handleTerminalSession(request *restful.Request, response *restful.Response) { - namespace := request.PathParameter("namespace") - podName := request.PathParameter("pod") - containerName := request.QueryParameter("container") - shell := request.QueryParameter("shell") + namespace := request.PathParameter("namespace") + podName := request.PathParameter("pod") + containerName := request.QueryParameter("container") + shell := request.QueryParameter("shell") - conn, err := upgrader.Upgrade(response.ResponseWriter, request.Request, nil) - if err != nil { - klog.Warning(err) - return - } + conn, err := upgrader.Upgrade(response.ResponseWriter, request.Request, nil) + if err != nil { + klog.Warning(err) + return + } - t.terminaler.HandleSession(shell, namespace, podName, containerName, conn) -} \ No newline at end of file + t.terminaler.HandleSession(shell, namespace, podName, containerName, conn) +} diff --git a/pkg/models/devops/common.go b/pkg/models/devops/common.go index 7d0af838..c23288c4 100644 --- a/pkg/models/devops/common.go +++ b/pkg/models/devops/common.go @@ -14,13 +14,7 @@ limitations under the License. package devops import ( - "fmt" "github.com/fatih/structs" - "k8s.io/klog" - "kubesphere.io/kubesphere/pkg/db" - "kubesphere.io/kubesphere/pkg/gojenkins" - "kubesphere.io/kubesphere/pkg/simple/client" - "kubesphere.io/kubesphere/pkg/utils/reflectutils" "kubesphere.io/kubesphere/pkg/utils/stringutils" ) @@ -67,298 +61,3 @@ const ( const ( KS_ADMIN = "admin" ) - -const ( - ProjectOwner = "owner" - ProjectMaintainer = "maintainer" - ProjectDeveloper = "developer" - ProjectReporter = "reporter" -) - -const ( - JenkinsAllUserRoleName = "kubesphere-user" -) - -type Role struct { - Name string `json:"name" description:"role's name e.g. owner'"` - Description string `json:"description" description:"role 's description'"` -} - -var DefaultRoles = []*Role{ - { - Name: ProjectOwner, - Description: "Owner have access to do all the operations of a DevOps project and own the highest permissions as well.", - }, - { - Name: ProjectMaintainer, - Description: "Maintainer have access to manage pipeline and credential configuration in a DevOps project.", - }, - { - Name: ProjectDeveloper, - Description: "Developer is able to view and trigger the pipeline.", - }, - { - Name: ProjectReporter, - Description: "Reporter is only allowed to view the status of the pipeline.", - }, -} - -var AllRoleSlice = []string{ProjectDeveloper, ProjectReporter, ProjectMaintainer, ProjectOwner} - -var JenkinsOwnerProjectPermissionIds = &gojenkins.ProjectPermissionIds{ - CredentialCreate: true, - CredentialDelete: true, - CredentialManageDomains: true, - CredentialUpdate: true, - CredentialView: true, - ItemBuild: true, - ItemCancel: true, - ItemConfigure: true, - ItemCreate: true, - ItemDelete: true, - ItemDiscover: true, - ItemMove: true, - ItemRead: true, - ItemWorkspace: true, - RunDelete: true, - RunReplay: true, - RunUpdate: true, - SCMTag: true, -} - -var JenkinsProjectPermissionMap = map[string]gojenkins.ProjectPermissionIds{ - ProjectOwner: { - CredentialCreate: true, - CredentialDelete: true, - CredentialManageDomains: true, - CredentialUpdate: true, - CredentialView: true, - ItemBuild: true, - ItemCancel: true, - ItemConfigure: true, - ItemCreate: true, - ItemDelete: true, - ItemDiscover: true, - ItemMove: true, - ItemRead: true, - ItemWorkspace: true, - RunDelete: true, - RunReplay: true, - RunUpdate: true, - SCMTag: true, - }, - ProjectMaintainer: { - CredentialCreate: true, - CredentialDelete: true, - CredentialManageDomains: true, - CredentialUpdate: true, - CredentialView: true, - ItemBuild: true, - ItemCancel: true, - ItemConfigure: false, - ItemCreate: true, - ItemDelete: false, - ItemDiscover: true, - ItemMove: false, - ItemRead: true, - ItemWorkspace: true, - RunDelete: true, - RunReplay: true, - RunUpdate: true, - SCMTag: true, - }, - ProjectDeveloper: { - CredentialCreate: false, - CredentialDelete: false, - CredentialManageDomains: false, - CredentialUpdate: false, - CredentialView: false, - ItemBuild: true, - ItemCancel: true, - ItemConfigure: false, - ItemCreate: false, - ItemDelete: false, - ItemDiscover: true, - ItemMove: false, - ItemRead: true, - ItemWorkspace: true, - RunDelete: true, - RunReplay: true, - RunUpdate: true, - SCMTag: false, - }, - ProjectReporter: { - CredentialCreate: false, - CredentialDelete: false, - CredentialManageDomains: false, - CredentialUpdate: false, - CredentialView: false, - ItemBuild: false, - ItemCancel: false, - ItemConfigure: false, - ItemCreate: false, - ItemDelete: false, - ItemDiscover: true, - ItemMove: false, - ItemRead: true, - ItemWorkspace: false, - RunDelete: false, - RunReplay: false, - RunUpdate: false, - SCMTag: false, - }, -} - -var JenkinsPipelinePermissionMap = map[string]gojenkins.ProjectPermissionIds{ - ProjectOwner: { - CredentialCreate: true, - CredentialDelete: true, - CredentialManageDomains: true, - CredentialUpdate: true, - CredentialView: true, - ItemBuild: true, - ItemCancel: true, - ItemConfigure: true, - ItemCreate: true, - ItemDelete: true, - ItemDiscover: true, - ItemMove: true, - ItemRead: true, - ItemWorkspace: true, - RunDelete: true, - RunReplay: true, - RunUpdate: true, - SCMTag: true, - }, - ProjectMaintainer: { - CredentialCreate: true, - CredentialDelete: true, - CredentialManageDomains: true, - CredentialUpdate: true, - CredentialView: true, - ItemBuild: true, - ItemCancel: true, - ItemConfigure: true, - ItemCreate: true, - ItemDelete: true, - ItemDiscover: true, - ItemMove: true, - ItemRead: true, - ItemWorkspace: true, - RunDelete: true, - RunReplay: true, - RunUpdate: true, - SCMTag: true, - }, - ProjectDeveloper: { - CredentialCreate: false, - CredentialDelete: false, - CredentialManageDomains: false, - CredentialUpdate: false, - CredentialView: false, - ItemBuild: true, - ItemCancel: true, - ItemConfigure: false, - ItemCreate: false, - ItemDelete: false, - ItemDiscover: true, - ItemMove: false, - ItemRead: true, - ItemWorkspace: true, - RunDelete: true, - RunReplay: true, - RunUpdate: true, - SCMTag: false, - }, - ProjectReporter: { - CredentialCreate: false, - CredentialDelete: false, - CredentialManageDomains: false, - CredentialUpdate: false, - CredentialView: false, - ItemBuild: false, - ItemCancel: false, - ItemConfigure: false, - ItemCreate: false, - ItemDelete: false, - ItemDiscover: true, - ItemMove: false, - ItemRead: true, - ItemWorkspace: false, - RunDelete: false, - RunReplay: false, - RunUpdate: false, - SCMTag: false, - }, -} - -func GetProjectRoleName(projectId, role string) string { - return fmt.Sprintf("%s-%s-project", projectId, role) -} - -func GetPipelineRoleName(projectId, role string) string { - return fmt.Sprintf("%s-%s-pipeline", projectId, role) -} - -func GetProjectRolePattern(projectId string) string { - return fmt.Sprintf("^%s$", projectId) -} - -func GetPipelineRolePattern(projectId string) string { - return fmt.Sprintf("^%s/.*", projectId) -} - -func CheckProjectUserInRole(username, projectId string, roles []string) error { - if username == KS_ADMIN { - return nil - } - dbconn, err := client.ClientSets().MySQL() - if err != nil { - if _, ok := err.(client.ClientSetNotEnabledError); ok { - klog.Error("mysql is not enabled") - } else { - klog.Error("error creating mysql client", err) - } - return nil - } - - membership := &DevOpsProjectMembership{} - err = dbconn.Select(DevOpsProjectMembershipColumns...). - From(DevOpsProjectMembershipTableName). - Where(db.And( - db.Eq(DevOpsProjectMembershipUsernameColumn, username), - db.Eq(DevOpsProjectMembershipProjectIdColumn, projectId))).LoadOne(membership) - if err != nil { - return err - } - if !reflectutils.In(membership.Role, roles) { - return fmt.Errorf("user [%s] in project [%s] role is not in %s", username, projectId, roles) - } - return nil -} - -func GetProjectUserRole(username, projectId string) (string, error) { - if username == KS_ADMIN { - return ProjectOwner, nil - } - dbconn, err := client.ClientSets().MySQL() - if err != nil { - if _, ok := err.(client.ClientSetNotEnabledError); ok { - klog.Error("mysql is not enabled") - } else { - klog.Error("error creating mysql client", err) - } - return "", err - } - membership := &DevOpsProjectMembership{} - err = dbconn.Select(DevOpsProjectMembershipColumns...). - From(DevOpsProjectMembershipTableName). - Where(db.And( - db.Eq(DevOpsProjectMembershipUsernameColumn, username), - db.Eq(DevOpsProjectMembershipProjectIdColumn, projectId))).LoadOne(membership) - if err != nil { - return "", err - } - - return membership.Role, nil -} diff --git a/pkg/models/devops/devops.go b/pkg/models/devops/devops.go index 6b032dda..94298915 100644 --- a/pkg/models/devops/devops.go +++ b/pkg/models/devops/devops.go @@ -19,182 +19,153 @@ package devops import ( "bytes" - "compress/gzip" "encoding/json" "fmt" - "github.com/PuerkitoBio/goquery" - "github.com/emicklei/go-restful" "io" "io/ioutil" - "kubesphere.io/kubesphere/pkg/models" - "k8s.io/klog" - cs "kubesphere.io/kubesphere/pkg/simple/client" + "kubesphere.io/kubesphere/pkg/simple/client/devops" "net/http" - "net/url" - "strings" "sync" - "time" ) const ( channelMaxCapacity = 100 - cronJobLayout = "Monday, January 2, 2006 15:04:05 PM" ) type DevopsOperator interface { + GetPipeline(projectName, pipelineName string, req *http.Request) (*devops.Pipeline, error) + ListPipelines(req *http.Request) (*devops.PipelineList, error) + GetPipelineRun(projectName, pipelineName, runId string, req *http.Request) (*devops.PipelineRun, error) + ListPipelineRuns(projectName, pipelineName string, req *http.Request) (*devops.PipelineRunList, error) + StopPipeline(projectName, pipelineName, runId string, req *http.Request) (*devops.StopPipeline, error) + ReplayPipeline(projectName, pipelineName, runId string, req *http.Request) (*devops.ReplayPipeline, error) + RunPipeline(projectName, pipelineName string, req *http.Request) (*devops.RunPipeline, error) + GetArtifacts(projectName, pipelineName, runId string, req *http.Request) ([]devops.Artifacts, error) + GetRunLog(projectName, pipelineName, runId string, req *http.Request) ([]byte, error) + GetStepLog(projectName, pipelineName, runId, nodeId, stepId string, req *http.Request) ([]byte, http.Header, error) + GetNodeSteps(projectName, pipelineName, runId, nodeId string, req *http.Request) ([]devops.NodeSteps, error) + GetPipelineRunNodes(projectName, pipelineName, runId string, req *http.Request) ([]devops.PipelineRunNodes, error) + SubmitInputStep(projectName, pipelineName, runId, nodeId, stepId string, req *http.Request) ([]byte, error) + GetNodesDetail(projectName, pipelineName, runId string, req *http.Request) ([]devops.NodesDetail, error) + + GetBranchPipeline(projectName, pipelineName, branchName string, req *http.Request) (*devops.BranchPipeline, error) + GetBranchPipelineRun(projectName, pipelineName, branchName, runId string, req *http.Request) (*devops.PipelineRun, error) + StopBranchPipeline(projectName, pipelineName, branchName, runId string, req *http.Request) (*devops.StopPipeline, error) + ReplayBranchPipeline(projectName, pipelineName, branchName, runId string, req *http.Request) (*devops.ReplayPipeline, error) + RunBranchPipeline(projectName, pipelineName, branchName string, req *http.Request) (*devops.RunPipeline, error) + GetBranchArtifacts(projectName, pipelineName, branchName, runId string, req *http.Request) ([]devops.Artifacts, error) + GetBranchRunLog(projectName, pipelineName, branchName, runId string, req *http.Request) ([]byte, error) + GetBranchStepLog(projectName, pipelineName, branchName, runId, nodeId, stepId string, req *http.Request) ([]byte, http.Header, error) + GetBranchNodeSteps(projectName, pipelineName, branchName, runId, nodeId string, req *http.Request) ([]devops.NodeSteps, error) + GetBranchPipelineRunNodes(projectName, pipelineName, branchName, runId string, req *http.Request) ([]devops.BranchPipelineRunNodes, error) + SubmitBranchInputStep(projectName, pipelineName, branchName, runId, nodeId, stepId string, req *http.Request) ([]byte, error) + GetBranchNodesDetail(projectName, pipelineName, branchName, runId string, req *http.Request) ([]devops.NodesDetail, error) + GetPipelineBranch(projectName, pipelineName string, req *http.Request) (*devops.PipelineBranch, error) + ScanBranch(projectName, pipelineName string, req *http.Request) ([]byte, error) + + GetConsoleLog(projectName, pipelineName string, req *http.Request) ([]byte, error) + GetCrumb(req *http.Request) (*devops.Crumb, error) + + GetSCMServers(scmId string, req *http.Request) ([]devops.SCMServer, error) + GetSCMOrg(scmId string, req *http.Request) ([]devops.SCMOrg, error) + GetOrgRepo(scmId, organizationId string, req *http.Request) ([]devops.OrgRepo, error) + CreateSCMServers(scmId string, req *http.Request) (*devops.SCMServer, error) + Validate(scmId string, req *http.Request) (*devops.Validates, error) + + GetNotifyCommit(req *http.Request) ([]byte, error) + GithubWebhook(req *http.Request) ([]byte, error) + + CheckScriptCompile(projectName, pipelineName string, req *http.Request) (*devops.CheckScript, error) + CheckCron(projectName string, req *http.Request) (*devops.CheckCronRes, error) + + ToJenkinsfile(req *http.Request) (*devops.ResJenkinsfile, error) + ToJson(req *http.Request) (*devops.ResJson, error) } type devopsOperator struct { + devopsClient devops.Interface } -func GetPipeline(projectName, pipelineName string, req *http.Request) ([]byte, error) { - devops, err := cs.ClientSets().Devops() - if err != nil { - return nil, restful.NewError(http.StatusServiceUnavailable, err.Error()) +func NewDevopsOperator(client devops.Interface) DevopsOperator { + return &devopsOperator{devopsClient: client} +} + +func convertToHttpParameters(req *http.Request) *devops.HttpParameters { + httpParameters := devops.HttpParameters{ + Method: req.Method, + Header: req.Header, + Body: req.Body, + Form: req.Form, + PostForm: req.PostForm, + Url: req.URL, } - baseUrl := fmt.Sprintf(devops.Jenkins().Server+GetPipelineUrl, projectName, pipelineName) + return &httpParameters +} + +func (d devopsOperator) GetPipeline(projectName, pipelineName string, req *http.Request) (*devops.Pipeline, error) { - res, err := sendJenkinsRequest(baseUrl, req) + res, err := d.devopsClient.GetPipeline(projectName, pipelineName, convertToHttpParameters(req)) if err != nil { klog.Error(err) - return nil, err } - return res, err } -func SearchPipelines(req *http.Request) ([]byte, error) { - devops, err := cs.ClientSets().Devops() - if err != nil { - return nil, restful.NewError(http.StatusServiceUnavailable, err.Error()) - } +func (d devopsOperator) ListPipelines(req *http.Request) (*devops.PipelineList, error) { - baseUrl := devops.Jenkins().Server + SearchPipelineUrl + req.URL.RawQuery - - res, err := sendJenkinsRequest(baseUrl, req) - if err != nil { - klog.Error(err) - return nil, err - } - count, err := searchPipelineCount(req) - if err != nil { - klog.Error(err) - return nil, err - } - responseStruct := models.PageableResponse{TotalCount: count} - err = json.Unmarshal(res, &responseStruct.Items) + res, err := d.devopsClient.ListPipelines(convertToHttpParameters(req)) if err != nil { klog.Error(err) - return nil, err - } - res, err = json.Marshal(responseStruct) - if err != nil { - klog.Error(err) - return nil, err } return res, err } -func searchPipelineCount(req *http.Request) (int, error) { - devops, err := cs.ClientSets().Devops() - if err != nil { - return 0, restful.NewError(http.StatusServiceUnavailable, err.Error()) - } - - query, _ := parseJenkinsQuery(req.URL.RawQuery) - query.Set("start", "0") - query.Set("limit", "1000") - query.Set("depth", "-1") - - baseUrl := devops.Jenkins().Server + SearchPipelineUrl + query.Encode() - klog.V(4).Info("Jenkins-url: " + baseUrl) +func (d devopsOperator) GetPipelineRun(projectName, pipelineName, runId string, req *http.Request) (*devops.PipelineRun, error) { - res, err := sendJenkinsRequest(baseUrl, req) - if err != nil { - klog.Error(err) - return 0, err - } - var pipelines []Pipeline - err = json.Unmarshal(res, &pipelines) + res, err := d.devopsClient.GetPipelineRun(projectName, pipelineName, runId, convertToHttpParameters(req)) if err != nil { klog.Error(err) - return 0, err } - return len(pipelines), nil + return res, err } -func searchPipelineRunsCount(projectName, pipelineName string, req *http.Request) (int, error) { - devops, err := cs.ClientSets().Devops() - if err != nil { - return 0, restful.NewError(http.StatusServiceUnavailable, err.Error()) - } - query, _ := parseJenkinsQuery(req.URL.RawQuery) - query.Set("start", "0") - query.Set("limit", "1000") - query.Set("depth", "-1") - baseUrl := fmt.Sprintf(devops.Jenkins().Server+SearchPipelineRunUrl, projectName, pipelineName) - - klog.V(4).Info("Jenkins-url: " + baseUrl) +func (d devopsOperator) ListPipelineRuns(projectName, pipelineName string, req *http.Request) (*devops.PipelineRunList, error) { - res, err := sendJenkinsRequest(baseUrl+query.Encode(), req) - if err != nil { - klog.Error(err) - return 0, err - } - var runs []PipelineRun - err = json.Unmarshal(res, &runs) + res, err := d.devopsClient.ListPipelineRuns(projectName, pipelineName, convertToHttpParameters(req)) if err != nil { klog.Error(err) - return 0, err } - return len(runs), nil + return res, err } -func SearchPipelineRuns(projectName, pipelineName string, req *http.Request) ([]byte, error) { - devops, err := cs.ClientSets().Devops() - if err != nil { - return nil, restful.NewError(http.StatusServiceUnavailable, err.Error()) - } - - baseUrl := fmt.Sprintf(devops.Jenkins().Server+SearchPipelineRunUrl, projectName, pipelineName) +func (d devopsOperator) StopPipeline(projectName, pipelineName, runId string, req *http.Request) (*devops.StopPipeline, error) { - klog.V(4).Info("Jenkins-url: " + baseUrl) - - res, err := sendJenkinsRequest(baseUrl+req.URL.RawQuery, req) - if err != nil { - klog.Error(err) - return nil, err - } - count, err := searchPipelineRunsCount(projectName, pipelineName, req) - if err != nil { - klog.Error(err) - return nil, err - } - responseStruct := models.PageableResponse{TotalCount: count} - err = json.Unmarshal(res, &responseStruct.Items) + req.Method = http.MethodPut + res, err := d.devopsClient.StopPipeline(projectName, pipelineName, runId, convertToHttpParameters(req)) if err != nil { klog.Error(err) return nil, err } - res, err = json.Marshal(responseStruct) + + return res, err +} + +func (d devopsOperator) ReplayPipeline(projectName, pipelineName, runId string, req *http.Request) (*devops.ReplayPipeline, error) { + + res, err := d.devopsClient.ReplayPipeline(projectName, pipelineName, runId, convertToHttpParameters(req)) if err != nil { klog.Error(err) return nil, err } + return res, err } -func GetBranchPipelineRun(projectName, pipelineName, branchName, runId string, req *http.Request) ([]byte, error) { - devops, err := cs.ClientSets().Devops() - if err != nil { - return nil, restful.NewError(http.StatusServiceUnavailable, err.Error()) - } +func (d devopsOperator) RunPipeline(projectName, pipelineName string, req *http.Request) (*devops.RunPipeline, error) { - baseUrl := fmt.Sprintf(devops.Jenkins().Server+GetPipeBranchRunUrl, projectName, pipelineName, branchName, runId) - - res, err := sendJenkinsRequest(baseUrl, req) + res, err := d.devopsClient.RunPipeline(projectName, pipelineName, convertToHttpParameters(req)) if err != nil { klog.Error(err) return nil, err @@ -203,16 +174,9 @@ func GetBranchPipelineRun(projectName, pipelineName, branchName, runId string, r return res, err } -func GetPipelineRunNodesbyBranch(projectName, pipelineName, branchName, runId string, req *http.Request) ([]byte, error) { - devops, err := cs.ClientSets().Devops() - if err != nil { - return nil, restful.NewError(http.StatusServiceUnavailable, err.Error()) - } - - baseUrl := fmt.Sprintf(devops.Jenkins().Server+GetBranchPipeRunNodesUrl+req.URL.RawQuery, projectName, pipelineName, branchName, runId) - klog.V(4).Info("Jenkins-url: " + baseUrl) +func (d devopsOperator) GetArtifacts(projectName, pipelineName, runId string, req *http.Request) ([]devops.Artifacts, error) { - res, err := sendJenkinsRequest(baseUrl, req) + res, err := d.devopsClient.GetArtifacts(projectName, pipelineName, runId, convertToHttpParameters(req)) if err != nil { klog.Error(err) return nil, err @@ -221,32 +185,20 @@ func GetPipelineRunNodesbyBranch(projectName, pipelineName, branchName, runId st return res, err } -func GetBranchStepLog(projectName, pipelineName, branchName, runId, nodeId, stepId string, req *http.Request) ([]byte, http.Header, error) { - devops, err := cs.ClientSets().Devops() - if err != nil { - return nil, nil, restful.NewError(http.StatusServiceUnavailable, err.Error()) - } - - baseUrl := fmt.Sprintf(devops.Jenkins().Server+GetBranchStepLogUrl+req.URL.RawQuery, projectName, pipelineName, branchName, runId, nodeId, stepId) +func (d devopsOperator) GetRunLog(projectName, pipelineName, runId string, req *http.Request) ([]byte, error) { - resBody, header, err := jenkinsClient(baseUrl, req) + res, err := d.devopsClient.GetRunLog(projectName, pipelineName, runId, convertToHttpParameters(req)) if err != nil { klog.Error(err) - return nil, nil, err + return nil, err } - return resBody, header, err + return res, err } -func GetStepLog(projectName, pipelineName, runId, nodeId, stepId string, req *http.Request) ([]byte, http.Header, error) { - devops, err := cs.ClientSets().Devops() - if err != nil { - return nil, nil, restful.NewError(http.StatusServiceUnavailable, err.Error()) - } - - baseUrl := fmt.Sprintf(devops.Jenkins().Server+GetStepLogUrl+req.URL.RawQuery, projectName, pipelineName, runId, nodeId, stepId) +func (d devopsOperator) GetStepLog(projectName, pipelineName, runId, nodeId, stepId string, req *http.Request) ([]byte, http.Header, error) { - resBody, header, err := jenkinsClient(baseUrl, req) + resBody, header, err := d.devopsClient.GetStepLog(projectName, pipelineName, runId, nodeId, stepId, convertToHttpParameters(req)) if err != nil { klog.Error(err) return nil, nil, err @@ -255,110 +207,95 @@ func GetStepLog(projectName, pipelineName, runId, nodeId, stepId string, req *ht return resBody, header, err } -func GetSCMServers(scmId string, req *http.Request) ([]byte, error) { - devops, err := cs.ClientSets().Devops() - if err != nil { - return nil, restful.NewError(http.StatusServiceUnavailable, err.Error()) - } - - baseUrl := fmt.Sprintf(devops.Jenkins().Server+GetSCMServersUrl, scmId) - req.Method = http.MethodGet - resBody, err := sendJenkinsRequest(baseUrl, req) +func (d devopsOperator) GetNodeSteps(projectName, pipelineName, runId, nodeId string, req *http.Request) ([]devops.NodeSteps, error) { + res, err := d.devopsClient.GetNodeSteps(projectName, pipelineName, runId, nodeId, convertToHttpParameters(req)) if err != nil { klog.Error(err) return nil, err } - return resBody, err + + return res, err } -func CreateSCMServers(scmId string, req *http.Request) ([]byte, error) { - devops, err := cs.ClientSets().Devops() - if err != nil { - return nil, restful.NewError(http.StatusServiceUnavailable, err.Error()) - } +func (d devopsOperator) GetPipelineRunNodes(projectName, pipelineName, runId string, req *http.Request) ([]devops.PipelineRunNodes, error) { - requestBody, err := ioutil.ReadAll(req.Body) + res, err := d.devopsClient.GetPipelineRunNodes(projectName, pipelineName, runId, convertToHttpParameters(req)) if err != nil { klog.Error(err) return nil, err } - createReq := &CreateScmServerReq{} - err = json.Unmarshal(requestBody, createReq) + + fmt.Println() + + return res, err +} + +func (d devopsOperator) SubmitInputStep(projectName, pipelineName, runId, nodeId, stepId string, req *http.Request) ([]byte, error) { + + newBody, err := getInputReqBody(req.Body) if err != nil { klog.Error(err) return nil, err } - req.Body = nil - byteServers, err := GetSCMServers(scmId, req) + req.Body = newBody + + resBody, err := d.devopsClient.SubmitInputStep(projectName, pipelineName, runId, nodeId, stepId, convertToHttpParameters(req)) if err != nil { klog.Error(err) return nil, err } - var servers []*SCMServer - _ = json.Unmarshal(byteServers, &servers) - for _, server := range servers { - if server.ApiURL == createReq.ApiURL { - return json.Marshal(server) - } - } - req.Body = ioutil.NopCloser(bytes.NewReader(requestBody)) + return resBody, err +} - baseUrl := fmt.Sprintf(devops.Jenkins().Server+CreateSCMServersUrl, scmId) +func (d devopsOperator) GetNodesDetail(projectName, pipelineName, runId string, req *http.Request) ([]devops.NodesDetail, error) { + var wg sync.WaitGroup + var nodesDetails []devops.NodesDetail + stepChan := make(chan *devops.NodesStepsIndex, channelMaxCapacity) - req.Method = http.MethodPost - resBody, err := sendJenkinsRequest(baseUrl, req) + respNodes, err := d.GetPipelineRunNodes(projectName, pipelineName, runId, req) if err != nil { klog.Error(err) return nil, err } - return resBody, err -} -func Validate(scmId string, req *http.Request) ([]byte, error) { - devops, err := cs.ClientSets().Devops() - if err != nil { - return nil, restful.NewError(http.StatusServiceUnavailable, err.Error()) - } - - baseUrl := fmt.Sprintf(devops.Jenkins().Server+ValidateUrl, scmId) - - req.Method = http.MethodPut - resBody, err := sendJenkinsRequest(baseUrl, req) + Nodes, err := json.Marshal(respNodes) + err = json.Unmarshal(Nodes, &nodesDetails) if err != nil { klog.Error(err) return nil, err } - return resBody, err -} + // get all steps in nodes. + for i, v := range respNodes { + wg.Add(1) + go func(nodeId string, index int) { + Steps, err := d.GetNodeSteps(projectName, pipelineName, runId, nodeId, req) + if err != nil { + klog.Error(err) + return + } -func GetSCMOrg(scmId string, req *http.Request) ([]byte, error) { - devops, err := cs.ClientSets().Devops() - if err != nil { - return nil, restful.NewError(http.StatusServiceUnavailable, err.Error()) + stepChan <- &devops.NodesStepsIndex{Id: index, Steps: Steps} + wg.Done() + }(v.ID, i) } - baseUrl := fmt.Sprintf(devops.Jenkins().Server+GetSCMOrgUrl+req.URL.RawQuery, scmId) + wg.Wait() + close(stepChan) - res, err := sendJenkinsRequest(baseUrl, req) - if err != nil { - klog.Error(err) - return nil, err + for oneNodeSteps := range stepChan { + if oneNodeSteps != nil { + nodesDetails[oneNodeSteps.Id].Steps = append(nodesDetails[oneNodeSteps.Id].Steps, oneNodeSteps.Steps...) + } } - return res, err + return nodesDetails, err } -func GetOrgRepo(scmId, organizationId string, req *http.Request) ([]byte, error) { - devops, err := cs.ClientSets().Devops() - if err != nil { - return nil, restful.NewError(http.StatusServiceUnavailable, err.Error()) - } - - baseUrl := fmt.Sprintf(devops.Jenkins().Server+GetOrgRepoUrl+req.URL.RawQuery, scmId, organizationId) +func (d devopsOperator) GetBranchPipeline(projectName, pipelineName, branchName string, req *http.Request) (*devops.BranchPipeline, error) { - res, err := sendJenkinsRequest(baseUrl, req) + res, err := d.devopsClient.GetBranchPipeline(projectName, pipelineName, branchName, convertToHttpParameters(req)) if err != nil { klog.Error(err) return nil, err @@ -367,16 +304,9 @@ func GetOrgRepo(scmId, organizationId string, req *http.Request) ([]byte, error) return res, err } -func StopBranchPipeline(projectName, pipelineName, branchName, runId string, req *http.Request) ([]byte, error) { - devops, err := cs.ClientSets().Devops() - if err != nil { - return nil, restful.NewError(http.StatusServiceUnavailable, err.Error()) - } - - baseUrl := fmt.Sprintf(devops.Jenkins().Server+StopBranchPipelineUrl+req.URL.RawQuery, projectName, pipelineName, branchName, runId) +func (d devopsOperator) GetBranchPipelineRun(projectName, pipelineName, branchName, runId string, req *http.Request) (*devops.PipelineRun, error) { - req.Method = http.MethodPut - res, err := sendJenkinsRequest(baseUrl, req) + res, err := d.devopsClient.GetBranchPipelineRun(projectName, pipelineName, branchName, runId, convertToHttpParameters(req)) if err != nil { klog.Error(err) return nil, err @@ -385,16 +315,10 @@ func StopBranchPipeline(projectName, pipelineName, branchName, runId string, req return res, err } -func StopPipeline(projectName, pipelineName, runId string, req *http.Request) ([]byte, error) { - devops, err := cs.ClientSets().Devops() - if err != nil { - return nil, restful.NewError(http.StatusServiceUnavailable, err.Error()) - } - - baseUrl := fmt.Sprintf(devops.Jenkins().Server+StopPipelineUrl+req.URL.RawQuery, projectName, pipelineName, runId) +func (d devopsOperator) StopBranchPipeline(projectName, pipelineName, branchName, runId string, req *http.Request) (*devops.StopPipeline, error) { req.Method = http.MethodPut - res, err := sendJenkinsRequest(baseUrl, req) + res, err := d.devopsClient.StopBranchPipeline(projectName, pipelineName, branchName, runId, convertToHttpParameters(req)) if err != nil { klog.Error(err) return nil, err @@ -403,15 +327,9 @@ func StopPipeline(projectName, pipelineName, runId string, req *http.Request) ([ return res, err } -func ReplayBranchPipeline(projectName, pipelineName, branchName, runId string, req *http.Request) ([]byte, error) { - devops, err := cs.ClientSets().Devops() - if err != nil { - return nil, restful.NewError(http.StatusServiceUnavailable, err.Error()) - } - - baseUrl := fmt.Sprintf(devops.Jenkins().Server+ReplayBranchPipelineUrl+req.URL.RawQuery, projectName, pipelineName, branchName, runId) +func (d devopsOperator) ReplayBranchPipeline(projectName, pipelineName, branchName, runId string, req *http.Request) (*devops.ReplayPipeline, error) { - res, err := sendJenkinsRequest(baseUrl, req) + res, err := d.devopsClient.ReplayBranchPipeline(projectName, pipelineName, branchName, runId, convertToHttpParameters(req)) if err != nil { klog.Error(err) return nil, err @@ -420,15 +338,9 @@ func ReplayBranchPipeline(projectName, pipelineName, branchName, runId string, r return res, err } -func ReplayPipeline(projectName, pipelineName, runId string, req *http.Request) ([]byte, error) { - devops, err := cs.ClientSets().Devops() - if err != nil { - return nil, restful.NewError(http.StatusServiceUnavailable, err.Error()) - } +func (d devopsOperator) RunBranchPipeline(projectName, pipelineName, branchName string, req *http.Request) (*devops.RunPipeline, error) { - baseUrl := fmt.Sprintf(devops.Jenkins().Server+ReplayPipelineUrl+req.URL.RawQuery, projectName, pipelineName, runId) - - res, err := sendJenkinsRequest(baseUrl, req) + res, err := d.devopsClient.RunBranchPipeline(projectName, pipelineName, branchName, convertToHttpParameters(req)) if err != nil { klog.Error(err) return nil, err @@ -437,15 +349,9 @@ func ReplayPipeline(projectName, pipelineName, runId string, req *http.Request) return res, err } -func GetBranchRunLog(projectName, pipelineName, branchName, runId string, req *http.Request) ([]byte, error) { - devops, err := cs.ClientSets().Devops() - if err != nil { - return nil, restful.NewError(http.StatusServiceUnavailable, err.Error()) - } - - baseUrl := fmt.Sprintf(devops.Jenkins().Server+GetBranchRunLogUrl+req.URL.RawQuery, projectName, pipelineName, branchName, runId) +func (d devopsOperator) GetBranchArtifacts(projectName, pipelineName, branchName, runId string, req *http.Request) ([]devops.Artifacts, error) { - res, err := sendJenkinsRequest(baseUrl, req) + res, err := d.devopsClient.GetBranchArtifacts(projectName, pipelineName, branchName, runId, convertToHttpParameters(req)) if err != nil { klog.Error(err) return nil, err @@ -454,15 +360,9 @@ func GetBranchRunLog(projectName, pipelineName, branchName, runId string, req *h return res, err } -func GetRunLog(projectName, pipelineName, runId string, req *http.Request) ([]byte, error) { - devops, err := cs.ClientSets().Devops() - if err != nil { - return nil, restful.NewError(http.StatusServiceUnavailable, err.Error()) - } - - baseUrl := fmt.Sprintf(devops.Jenkins().Server+GetRunLogUrl+req.URL.RawQuery, projectName, pipelineName, runId) +func (d devopsOperator) GetBranchRunLog(projectName, pipelineName, branchName, runId string, req *http.Request) ([]byte, error) { - res, err := sendJenkinsRequest(baseUrl, req) + res, err := d.devopsClient.GetBranchRunLog(projectName, pipelineName, branchName, runId, convertToHttpParameters(req)) if err != nil { klog.Error(err) return nil, err @@ -471,32 +371,20 @@ func GetRunLog(projectName, pipelineName, runId string, req *http.Request) ([]by return res, err } -func GetBranchArtifacts(projectName, pipelineName, branchName, runId string, req *http.Request) ([]byte, error) { - devops, err := cs.ClientSets().Devops() - if err != nil { - return nil, restful.NewError(http.StatusServiceUnavailable, err.Error()) - } - - baseUrl := fmt.Sprintf(devops.Jenkins().Server+GetBranchArtifactsUrl+req.URL.RawQuery, projectName, pipelineName, branchName, runId) +func (d devopsOperator) GetBranchStepLog(projectName, pipelineName, branchName, runId, nodeId, stepId string, req *http.Request) ([]byte, http.Header, error) { - res, err := sendJenkinsRequest(baseUrl, req) + resBody, header, err := d.devopsClient.GetBranchStepLog(projectName, pipelineName, branchName, runId, nodeId, stepId, convertToHttpParameters(req)) if err != nil { klog.Error(err) - return nil, err + return nil, nil, err } - return res, err + return resBody, header, err } -func GetArtifacts(projectName, pipelineName, runId string, req *http.Request) ([]byte, error) { - devops, err := cs.ClientSets().Devops() - if err != nil { - return nil, restful.NewError(http.StatusServiceUnavailable, err.Error()) - } - - baseUrl := fmt.Sprintf(devops.Jenkins().Server+GetArtifactsUrl+req.URL.RawQuery, projectName, pipelineName, runId) +func (d devopsOperator) GetBranchNodeSteps(projectName, pipelineName, branchName, runId, nodeId string, req *http.Request) ([]devops.NodeSteps, error) { - res, err := sendJenkinsRequest(baseUrl, req) + res, err := d.devopsClient.GetBranchNodeSteps(projectName, pipelineName, branchName, runId, nodeId, convertToHttpParameters(req)) if err != nil { klog.Error(err) return nil, err @@ -505,15 +393,9 @@ func GetArtifacts(projectName, pipelineName, runId string, req *http.Request) ([ return res, err } -func GetPipeBranch(projectName, pipelineName string, req *http.Request) ([]byte, error) { - devops, err := cs.ClientSets().Devops() - if err != nil { - return nil, restful.NewError(http.StatusServiceUnavailable, err.Error()) - } +func (d devopsOperator) GetBranchPipelineRunNodes(projectName, pipelineName, branchName, runId string, req *http.Request) ([]devops.BranchPipelineRunNodes, error) { - baseUrl := fmt.Sprintf(devops.Jenkins().Server+GetPipeBranchUrl, projectName, pipelineName) - - res, err := sendJenkinsRequest(baseUrl+req.URL.RawQuery, req) + res, err := d.devopsClient.GetBranchPipelineRunNodes(projectName, pipelineName, branchName, runId, convertToHttpParameters(req)) if err != nil { klog.Error(err) return nil, err @@ -522,13 +404,7 @@ func GetPipeBranch(projectName, pipelineName string, req *http.Request) ([]byte, return res, err } -func SubmitBranchInputStep(projectName, pipelineName, branchName, runId, nodeId, stepId string, req *http.Request) ([]byte, error) { - devops, err := cs.ClientSets().Devops() - if err != nil { - return nil, restful.NewError(http.StatusServiceUnavailable, err.Error()) - } - - baseUrl := fmt.Sprintf(devops.Jenkins().Server+CheckBranchPipelineUrl+req.URL.RawQuery, projectName, pipelineName, branchName, runId, nodeId, stepId) +func (d devopsOperator) SubmitBranchInputStep(projectName, pipelineName, branchName, runId, nodeId, stepId string, req *http.Request) ([]byte, error) { newBody, err := getInputReqBody(req.Body) if err != nil { @@ -536,7 +412,7 @@ func SubmitBranchInputStep(projectName, pipelineName, branchName, runId, nodeId, return nil, err } req.Body = newBody - resBody, err := sendJenkinsRequest(baseUrl, req) + resBody, err := d.devopsClient.SubmitBranchInputStep(projectName, pipelineName, branchName, runId, nodeId, stepId, convertToHttpParameters(req)) if err != nil { klog.Error(err) return nil, err @@ -545,94 +421,65 @@ func SubmitBranchInputStep(projectName, pipelineName, branchName, runId, nodeId, return resBody, err } -func SubmitInputStep(projectName, pipelineName, runId, nodeId, stepId string, req *http.Request) ([]byte, error) { - devops, err := cs.ClientSets().Devops() - if err != nil { - return nil, restful.NewError(http.StatusServiceUnavailable, err.Error()) - } - - baseUrl := fmt.Sprintf(devops.Jenkins().Server+CheckPipelineUrl+req.URL.RawQuery, projectName, pipelineName, runId, nodeId, stepId) +func (d devopsOperator) GetBranchNodesDetail(projectName, pipelineName, branchName, runId string, req *http.Request) ([]devops.NodesDetail, error) { + var wg sync.WaitGroup + var nodesDetails []devops.NodesDetail + stepChan := make(chan *devops.NodesStepsIndex, channelMaxCapacity) - newBody, err := getInputReqBody(req.Body) + respNodes, err := d.GetBranchPipelineRunNodes(projectName, pipelineName, branchName, runId, req) if err != nil { klog.Error(err) return nil, err } - req.Body = newBody - resBody, err := sendJenkinsRequest(baseUrl, req) + Nodes, err := json.Marshal(respNodes) + err = json.Unmarshal(Nodes, &nodesDetails) if err != nil { klog.Error(err) return nil, err } - return resBody, err -} - -func getInputReqBody(reqBody io.ReadCloser) (newReqBody io.ReadCloser, err error) { - var checkBody CheckPlayload - var jsonBody []byte - var workRound struct { - ID string `json:"id,omitempty" description:"id"` - Parameters []CheckPlayloadParameters `json:"parameters"` - Abort bool `json:"abort,omitempty" description:"abort or not"` - } + // get all steps in nodes. + for i, v := range nodesDetails { + wg.Add(1) + go func(nodeId string, index int) { + Steps, err := d.GetBranchNodeSteps(projectName, pipelineName, branchName, runId, nodeId, req) + if err != nil { + klog.Error(err) + return + } - Body, err := ioutil.ReadAll(reqBody) - if err != nil { - klog.Error(err) - return nil, err + stepChan <- &devops.NodesStepsIndex{Id: index, Steps: Steps} + wg.Done() + }(v.ID, i) } - err = json.Unmarshal(Body, &checkBody) + wg.Wait() + close(stepChan) - if checkBody.Abort != true && checkBody.Parameters == nil { - workRound.Parameters = []CheckPlayloadParameters{} - workRound.ID = checkBody.ID - jsonBody, _ = json.Marshal(workRound) - } else { - jsonBody, _ = json.Marshal(checkBody) + for oneNodeSteps := range stepChan { + if oneNodeSteps != nil { + nodesDetails[oneNodeSteps.Id].Steps = append(nodesDetails[oneNodeSteps.Id].Steps, oneNodeSteps.Steps...) + } } - newReqBody = parseBody(bytes.NewBuffer(jsonBody)) - - return newReqBody, nil - -} - -func parseBody(body io.Reader) (newReqBody io.ReadCloser) { - rc, ok := body.(io.ReadCloser) - if !ok && body != nil { - rc = ioutil.NopCloser(body) - } - return rc + return nodesDetails, err } -func GetConsoleLog(projectName, pipelineName string, req *http.Request) ([]byte, error) { - devops, err := cs.ClientSets().Devops() - if err != nil { - return nil, restful.NewError(http.StatusServiceUnavailable, err.Error()) - } - - baseUrl := fmt.Sprintf(devops.Jenkins().Server+GetConsoleLogUrl+req.URL.RawQuery, projectName, pipelineName) +func (d devopsOperator) GetPipelineBranch(projectName, pipelineName string, req *http.Request) (*devops.PipelineBranch, error) { - resBody, err := sendJenkinsRequest(baseUrl, req) + res, err := d.devopsClient.GetPipelineBranch(projectName, pipelineName, convertToHttpParameters(req)) + //baseUrl+req.URL.RawQuery, req) if err != nil { klog.Error(err) return nil, err } - return resBody, err + return res, err } -func ScanBranch(projectName, pipelineName string, req *http.Request) ([]byte, error) { - devops, err := cs.ClientSets().Devops() - if err != nil { - return nil, restful.NewError(http.StatusServiceUnavailable, err.Error()) - } - - baseUrl := fmt.Sprintf(devops.Jenkins().Server+ScanBranchUrl+req.URL.RawQuery, projectName, pipelineName) +func (d devopsOperator) ScanBranch(projectName, pipelineName string, req *http.Request) ([]byte, error) { - resBody, err := sendJenkinsRequest(baseUrl, req) + resBody, err := d.devopsClient.ScanBranch(projectName, pipelineName, convertToHttpParameters(req)) if err != nil { klog.Error(err) return nil, err @@ -641,31 +488,20 @@ func ScanBranch(projectName, pipelineName string, req *http.Request) ([]byte, er return resBody, err } -func RunBranchPipeline(projectName, pipelineName, branchName string, req *http.Request) ([]byte, error) { - devops, err := cs.ClientSets().Devops() - if err != nil { - return nil, restful.NewError(http.StatusServiceUnavailable, err.Error()) - } +func (d devopsOperator) GetConsoleLog(projectName, pipelineName string, req *http.Request) ([]byte, error) { - baseUrl := fmt.Sprintf(devops.Jenkins().Server+RunBranchPipelineUrl+req.URL.RawQuery, projectName, pipelineName, branchName) - - res, err := sendJenkinsRequest(baseUrl, req) + resBody, err := d.devopsClient.GetConsoleLog(projectName, pipelineName, convertToHttpParameters(req)) if err != nil { klog.Error(err) return nil, err } - return res, err + return resBody, err } -func RunPipeline(projectName, pipelineName string, req *http.Request) ([]byte, error) { - devops, err := cs.ClientSets().Devops() - if err != nil { - return nil, restful.NewError(http.StatusServiceUnavailable, err.Error()) - } - baseUrl := fmt.Sprintf(devops.Jenkins().Server+RunPipelineUrl+req.URL.RawQuery, projectName, pipelineName) +func (d devopsOperator) GetCrumb(req *http.Request) (*devops.Crumb, error) { - res, err := sendJenkinsRequest(baseUrl, req) + res, err := d.devopsClient.GetCrumb(convertToHttpParameters(req)) if err != nil { klog.Error(err) return nil, err @@ -674,202 +510,91 @@ func RunPipeline(projectName, pipelineName string, req *http.Request) ([]byte, e return res, err } -func GetCrumb(req *http.Request) ([]byte, error) { - devops, err := cs.ClientSets().Devops() - if err != nil { - return nil, restful.NewError(http.StatusServiceUnavailable, err.Error()) - } - - baseUrl := fmt.Sprintf(devops.Jenkins().Server + GetCrumbUrl) - - res, err := sendJenkinsRequest(baseUrl, req) - if err != nil { - klog.Error(err) - return nil, err - } - - return res, err -} +func (d devopsOperator) GetSCMServers(scmId string, req *http.Request) ([]devops.SCMServer, error) { -func CheckScriptCompile(projectName, pipelineName string, req *http.Request) ([]byte, error) { - devops, err := cs.ClientSets().Devops() - if err != nil { - return nil, restful.NewError(http.StatusServiceUnavailable, err.Error()) - } - - baseUrl := fmt.Sprintf(devops.Jenkins().Server+CheckScriptCompileUrl, projectName, pipelineName) - - resBody, err := sendJenkinsRequest(baseUrl, req) + req.Method = http.MethodGet + resBody, err := d.devopsClient.GetSCMServers(scmId, convertToHttpParameters(req)) if err != nil { klog.Error(err) - return nil, err } - return resBody, err } -func CheckCron(projectName string, req *http.Request) (*CheckCronRes, error) { - devops, err := cs.ClientSets().Devops() - if err != nil { - return nil, restful.NewError(http.StatusServiceUnavailable, err.Error()) - } - - jenkins := devops.Jenkins() - - var res = new(CheckCronRes) - var cron = new(CronData) - var reader io.ReadCloser - var baseUrl string - - reader = req.Body - cronData, err := ioutil.ReadAll(reader) - json.Unmarshal(cronData, cron) - - if cron.PipelineName != "" { - baseUrl = fmt.Sprintf(jenkins.Server+CheckPipelienCronUrl, projectName, cron.PipelineName, cron.Cron) - } else { - baseUrl = fmt.Sprintf(jenkins.Server+CheckCronUrl, projectName, cron.Cron) - } +func (d devopsOperator) GetSCMOrg(scmId string, req *http.Request) ([]devops.SCMOrg, error) { - newUrl, err := url.Parse(baseUrl) + res, err := d.devopsClient.GetSCMOrg(scmId, convertToHttpParameters(req)) if err != nil { klog.Error(err) return nil, err } - newUrl.RawQuery = newUrl.Query().Encode() - reqJenkins := &http.Request{ - Method: http.MethodGet, - URL: newUrl, - Header: req.Header, - } - - client := &http.Client{Timeout: 30 * time.Second} - - resp, err := client.Do(reqJenkins) + return res, err +} - if resp != nil && resp.StatusCode != http.StatusOK { - resBody, _ := getRespBody(resp) - return &CheckCronRes{ - Result: "error", - Message: string(resBody), - }, err - } - if err != nil { - klog.Error(err) - return nil, err - } - defer resp.Body.Close() +func (d devopsOperator) GetOrgRepo(scmId, organizationId string, req *http.Request) ([]devops.OrgRepo, error) { - doc, err := goquery.NewDocumentFromReader(resp.Body) + res, err := d.devopsClient.GetOrgRepo(scmId, organizationId, convertToHttpParameters(req)) 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, ";") +func (d devopsOperator) CreateSCMServers(scmId string, req *http.Request) (*devops.SCMServer, error) { - lastTmp := strings.Split(times[0], " ") - lastCount := len(lastTmp) - lastTmp = lastTmp[lastCount-7 : lastCount-1] - lastTime := strings.Join(lastTmp, " ") - lastUinx, err := time.Parse(cronJobLayout, lastTime) + requestBody, err := ioutil.ReadAll(req.Body) if err != nil { klog.Error(err) - return "", "", err + return nil, 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) + createReq := &devops.CreateScmServerReq{} + err = json.Unmarshal(requestBody, createReq) if err != nil { klog.Error(err) - return "", "", err - } - next := nextUinx.Format(time.RFC3339) - - return last, next, nil -} - -func GetPipelineRun(projectName, pipelineName, runId string, req *http.Request) ([]byte, error) { - devops, err := cs.ClientSets().Devops() - if err != nil { - return nil, restful.NewError(http.StatusServiceUnavailable, err.Error()) + return nil, err } - - baseUrl := fmt.Sprintf(devops.Jenkins().Server+GetPipelineRunUrl, projectName, pipelineName, runId) - - res, err := sendJenkinsRequest(baseUrl, req) + req.Body = nil + servers, err := d.GetSCMServers(scmId, req) if err != nil { klog.Error(err) return nil, err } - return res, err -} - -func GetBranchPipeline(projectName, pipelineName, branchName string, req *http.Request) ([]byte, error) { - devops, err := cs.ClientSets().Devops() - if err != nil { - return nil, restful.NewError(http.StatusServiceUnavailable, err.Error()) + for _, server := range servers { + if server.ApiURL == createReq.ApiURL { + return &server, nil + } } + req.Body = ioutil.NopCloser(bytes.NewReader(requestBody)) - baseUrl := fmt.Sprintf(devops.Jenkins().Server+GetBranchPipeUrl, projectName, pipelineName, branchName) - - res, err := sendJenkinsRequest(baseUrl, req) + req.Method = http.MethodPost + resBody, err := d.devopsClient.CreateSCMServers(scmId, convertToHttpParameters(req)) if err != nil { klog.Error(err) return nil, err } - - return res, err + return resBody, err } -func GetPipelineRunNodes(projectName, pipelineName, runId string, req *http.Request) ([]byte, error) { - devops, err := cs.ClientSets().Devops() - if err != nil { - return nil, restful.NewError(http.StatusServiceUnavailable, err.Error()) - } - - baseUrl := fmt.Sprintf(devops.Jenkins().Server+GetPipeRunNodesUrl+req.URL.RawQuery, projectName, pipelineName, runId) +func (d devopsOperator) Validate(scmId string, req *http.Request) (*devops.Validates, error) { - res, err := sendJenkinsRequest(baseUrl, req) + req.Method = http.MethodPut + resBody, err := d.devopsClient.Validate(scmId, convertToHttpParameters(req)) if err != nil { klog.Error(err) return nil, err } - return res, err + return resBody, err } -func GetBranchNodeSteps(projectName, pipelineName, branchName, runId, nodeId string, req *http.Request) ([]byte, error) { - devops, err := cs.ClientSets().Devops() - if err != nil { - return nil, restful.NewError(http.StatusServiceUnavailable, err.Error()) - } +func (d devopsOperator) GetNotifyCommit(req *http.Request) ([]byte, error) { - baseUrl := fmt.Sprintf(devops.Jenkins().Server+GetBranchNodeStepsUrl+req.URL.RawQuery, projectName, pipelineName, branchName, runId, nodeId) + req.Method = http.MethodGet - res, err := sendJenkinsRequest(baseUrl, req) + res, err := d.devopsClient.GetNotifyCommit(convertToHttpParameters(req)) if err != nil { klog.Error(err) return nil, err @@ -878,15 +603,9 @@ func GetBranchNodeSteps(projectName, pipelineName, branchName, runId, nodeId str return res, err } -func GetNodeSteps(projectName, pipelineName, runId, nodeId string, req *http.Request) ([]byte, error) { - devops, err := cs.ClientSets().Devops() - if err != nil { - return nil, restful.NewError(http.StatusServiceUnavailable, err.Error()) - } - - baseUrl := fmt.Sprintf(devops.Jenkins().Server+GetNodeStepsUrl+req.URL.RawQuery, projectName, pipelineName, runId, nodeId) +func (d devopsOperator) GithubWebhook(req *http.Request) ([]byte, error) { - res, err := sendJenkinsRequest(baseUrl, req) + res, err := d.devopsClient.GithubWebhook(convertToHttpParameters(req)) if err != nil { klog.Error(err) return nil, err @@ -895,32 +614,21 @@ func GetNodeSteps(projectName, pipelineName, runId, nodeId string, req *http.Req return res, err } -func ToJenkinsfile(req *http.Request) ([]byte, error) { - devops, err := cs.ClientSets().Devops() - if err != nil { - return nil, restful.NewError(http.StatusServiceUnavailable, err.Error()) - } +func (d devopsOperator) CheckScriptCompile(projectName, pipelineName string, req *http.Request) (*devops.CheckScript, error) { - baseUrl := fmt.Sprintf(devops.Jenkins().Server + ToJenkinsfileUrl) - - res, err := sendJenkinsRequest(baseUrl, req) + resBody, err := d.devopsClient.CheckScriptCompile(projectName, pipelineName, convertToHttpParameters(req)) if err != nil { klog.Error(err) return nil, err } - return res, err + return resBody, err } -func ToJson(req *http.Request) ([]byte, error) { - devops, err := cs.ClientSets().Devops() - if err != nil { - return nil, restful.NewError(http.StatusServiceUnavailable, err.Error()) - } +func (d devopsOperator) CheckCron(projectName string, req *http.Request) (*devops.CheckCronRes, error) { - baseUrl := fmt.Sprintf(devops.Jenkins().Server + ToJsonUrl) + res, err := d.devopsClient.CheckCron(projectName, convertToHttpParameters(req)) - res, err := sendJenkinsRequest(baseUrl, req) if err != nil { klog.Error(err) return nil, err @@ -929,16 +637,9 @@ func ToJson(req *http.Request) ([]byte, error) { return res, err } -func GetNotifyCommit(req *http.Request) ([]byte, error) { - devops, err := cs.ClientSets().Devops() - if err != nil { - return nil, restful.NewError(http.StatusServiceUnavailable, err.Error()) - } +func (d devopsOperator) ToJenkinsfile(req *http.Request) (*devops.ResJenkinsfile, error) { - baseUrl := fmt.Sprint(devops.Jenkins().Server, GetNotifyCommitUrl, req.URL.RawQuery) - req.Method = "GET" - - res, err := sendJenkinsRequest(baseUrl, req) + res, err := d.devopsClient.ToJenkinsfile(convertToHttpParameters(req)) if err != nil { klog.Error(err) return nil, err @@ -947,15 +648,9 @@ func GetNotifyCommit(req *http.Request) ([]byte, error) { return res, err } -func GithubWebhook(req *http.Request) ([]byte, error) { - devops, err := cs.ClientSets().Devops() - if err != nil { - return nil, restful.NewError(http.StatusServiceUnavailable, err.Error()) - } - - baseUrl := fmt.Sprint(devops.Jenkins().Server, GithubWebhookUrl, req.URL.RawQuery) +func (d devopsOperator) ToJson(req *http.Request) (*devops.ResJson, error) { - res, err := sendJenkinsRequest(baseUrl, req) + res, err := d.devopsClient.ToJson(convertToHttpParameters(req)) if err != nil { klog.Error(err) return nil, err @@ -964,192 +659,41 @@ func GithubWebhook(req *http.Request) ([]byte, error) { return res, err } -func GetBranchNodesDetail(projectName, pipelineName, branchName, runId string, req *http.Request) ([]NodesDetail, error) { - var wg sync.WaitGroup - var nodesDetails []NodesDetail - stepChan := make(chan *NodesStepsIndex, channelMaxCapacity) - - respNodes, err := GetPipelineRunNodesbyBranch(projectName, pipelineName, branchName, runId, req) - if err != nil { - klog.Error(err) - return nil, err - } - err = json.Unmarshal(respNodes, &nodesDetails) - if err != nil { - klog.Error(err) - return nil, err - } - - // get all steps in nodes. - for i, v := range nodesDetails { - wg.Add(1) - go func(nodeId string, index int) { - var steps []NodeSteps - respSteps, err := GetBranchNodeSteps(projectName, pipelineName, branchName, runId, nodeId, req) - if err != nil { - klog.Error(err) - return - } - err = json.Unmarshal(respSteps, &steps) - - stepChan <- &NodesStepsIndex{index, steps} - wg.Done() - }(v.ID, i) - } - - wg.Wait() - close(stepChan) - - for oneNodeSteps := range stepChan { - if oneNodeSteps != nil { - nodesDetails[oneNodeSteps.Id].Steps = append(nodesDetails[oneNodeSteps.Id].Steps, oneNodeSteps.Steps...) - } +func getInputReqBody(reqBody io.ReadCloser) (newReqBody io.ReadCloser, err error) { + var checkBody devops.CheckPlayload + var jsonBody []byte + var workRound struct { + ID string `json:"id,omitempty" description:"id"` + Parameters []devops.CheckPlayloadParameters `json:"parameters"` + Abort bool `json:"abort,omitempty" description:"abort or not"` } - return nodesDetails, err -} - -func GetNodesDetail(projectName, pipelineName, runId string, req *http.Request) ([]NodesDetail, error) { - var wg sync.WaitGroup - var nodesDetails []NodesDetail - stepChan := make(chan *NodesStepsIndex, channelMaxCapacity) - - respNodes, err := GetPipelineRunNodes(projectName, pipelineName, runId, req) - if err != nil { - klog.Error(err) - return nil, err - } - err = json.Unmarshal(respNodes, &nodesDetails) + Body, err := ioutil.ReadAll(reqBody) if err != nil { klog.Error(err) return nil, err } - // get all steps in nodes. - for i, v := range nodesDetails { - wg.Add(1) - go func(nodeId string, index int) { - var steps []NodeSteps - respSteps, err := GetNodeSteps(projectName, pipelineName, runId, nodeId, req) - if err != nil { - klog.Error(err) - return - } - err = json.Unmarshal(respSteps, &steps) - - stepChan <- &NodesStepsIndex{index, steps} - wg.Done() - }(v.ID, i) - } - - wg.Wait() - close(stepChan) - - for oneNodeSteps := range stepChan { - if oneNodeSteps != nil { - nodesDetails[oneNodeSteps.Id].Steps = append(nodesDetails[oneNodeSteps.Id].Steps, oneNodeSteps.Steps...) - } - } - - return nodesDetails, err -} - -// create jenkins request -func sendJenkinsRequest(baseUrl string, req *http.Request) ([]byte, error) { - resBody, _, err := jenkinsClient(baseUrl, req) - return resBody, err -} - -func jenkinsClient(baseUrl string, req *http.Request) ([]byte, http.Header, error) { - newReqUrl, err := url.Parse(baseUrl) - if err != nil { - klog.Error(err) - return nil, nil, err - } - - client := &http.Client{Timeout: 30 * time.Second} - - newRequest := &http.Request{ - Method: req.Method, - URL: newReqUrl, - Header: req.Header, - Body: req.Body, - Form: req.Form, - PostForm: req.PostForm, - } - - resp, err := client.Do(newRequest) - if err != nil { - klog.Error(err) - return nil, nil, err - } - - resBody, _ := getRespBody(resp) - defer resp.Body.Close() + err = json.Unmarshal(Body, &checkBody) - 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 + if checkBody.Abort != true && checkBody.Parameters == nil { + workRound.Parameters = []devops.CheckPlayloadParameters{} + workRound.ID = checkBody.ID + jsonBody, _ = json.Marshal(workRound) + } else { + jsonBody, _ = json.Marshal(checkBody) } - return resBody, resp.Header, nil - -} + newReqBody = parseBody(bytes.NewBuffer(jsonBody)) -// 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 + return newReqBody, nil } -// 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) +func parseBody(body io.Reader) (newReqBody io.ReadCloser) { + rc, ok := body.(io.ReadCloser) + if !ok && body != nil { + rc = ioutil.NopCloser(body) } - return m, err + return rc } diff --git a/pkg/models/devops/devops_test.go b/pkg/models/devops/devops_test.go index 3749d604..4d82e5b4 100644 --- a/pkg/models/devops/devops_test.go +++ b/pkg/models/devops/devops_test.go @@ -1,40 +1,113 @@ package devops import ( + "kubesphere.io/kubesphere/pkg/simple/client/devops" + "kubesphere.io/kubesphere/pkg/simple/client/devops/fake" + "net/http" "testing" ) -func Test_parseCronJobTime(t *testing.T) { - type Except struct { - Last string - Next string +const baseUrl = "http://127.0.0.1/kapis/devops.kubesphere.io/v1alpha2/" + +func TestGetNodesDetail(t *testing.T) { + fakeData := make(map[string]interface{}) + PipelineRunNodes := []devops.PipelineRunNodes{ + { + DisplayName: "Deploy to Kubernetes", + ID: "1", + Result: "SUCCESS", + }, + { + DisplayName: "Deploy to Kubernetes", + ID: "2", + Result: "SUCCESS", + }, + { + DisplayName: "Deploy to Kubernetes", + ID: "3", + Result: "SUCCESS", + }, } - Items := []struct { - Input string - Expected Except - }{ - {"上次运行的时间 Tuesday, September 10, 2019 8:59:09 AM UTC; 下次运行的时间 Tuesday, September 10, 2019 9:14:09 AM UTC.", Except{Last: "2019-09-10T08:59:09Z", Next: "2019-09-10T09:14:09Z"}}, - {"上次运行的时间 Thursday, January 3, 2019 11:56:30 PM UTC; 下次运行的时间 Friday, January 3, 2020 12:11:30 AM UTC.", Except{Last: "2019-01-03T23:56:30Z", Next: "2020-01-03T00:11:30Z"}}, - {"上次运行的时间 Tuesday, September 10, 2019 8:41:34 AM UTC; 下次运行的时间 Tuesday, September 10, 2019 9:41:34 AM UTC.", Except{Last: "2019-09-10T08:41:34Z", Next: "2019-09-10T09:41:34Z"}}, - {"上次运行的时间 Tuesday, September 10, 2019 9:15:26 AM UTC; 下次运行的时间 Tuesday, September 10, 2019 10:03:26 AM UTC.", Except{Last: "2019-09-10T09:15:26Z", Next: "2019-09-10T10:03:26Z"}}, - {"Would last have run at Tuesday, September 10, 2019 9:15:26 AM UTC; would next run at Tuesday, September 10, 2019 10:03:26 AM UTC.", Except{Last: "2019-09-10T09:15:26Z", Next: "2019-09-10T10:03:26Z"}}, - {"Would last have run at Tuesday, September 10, 2019 8:41:34 AM UTC; would next run at Tuesday, September 10, 2019 9:41:34 AM UTC.", Except{Last: "2019-09-10T08:41:34Z", Next: "2019-09-10T09:41:34Z"}}, + NodeSteps := []devops.NodeSteps{ + { + DisplayName: "Deploy to Kubernetes", + ID: "1", + Result: "SUCCESS", + }, } - for _, item := range Items { - last, next, err := parseCronJobTime(item.Input) - if err != nil { - t.Fatalf("should not get error %+v", err) - } + fakeData["project1-pipeline1-run1"] = PipelineRunNodes + fakeData["project1-pipeline1-run1-1"] = NodeSteps + fakeData["project1-pipeline1-run1-2"] = NodeSteps + fakeData["project1-pipeline1-run1-3"] = NodeSteps - if last != item.Expected.Last { - t.Errorf("got %#v, expected %#v", last, item.Expected.Last) - } + devopsClient := fake.NewFakeDevops(fakeData) - if next != item.Expected.Next { - t.Errorf("got %#v, expected %#v", next, item.Expected.Next) + devopsOperator := NewDevopsOperator(devopsClient) + + httpReq, _ := http.NewRequest(http.MethodGet, baseUrl+"devops/project1/pipelines/pipeline1/runs/run1/nodesdetail/?limit=10000", nil) + + nodesDetails, err := devopsOperator.GetNodesDetail("project1", "pipeline1", "run1", httpReq) + if err != nil || nodesDetails == nil { + t.Fatalf("should not get error %+v", err) + } + + for _, v := range nodesDetails { + if v.Steps[0].ID == "" { + t.Fatalf("Can not get any step.") } + } +} +func TestGetBranchNodesDetail(t *testing.T) { + fakeData := make(map[string]interface{}) + + BranchPipelineRunNodes := []devops.BranchPipelineRunNodes{ + { + DisplayName: "Deploy to Kubernetes", + ID: "1", + Result: "SUCCESS", + }, + { + DisplayName: "Deploy to Kubernetes", + ID: "2", + Result: "SUCCESS", + }, + { + DisplayName: "Deploy to Kubernetes", + ID: "3", + Result: "SUCCESS", + }, + } + + BranchNodeSteps := []devops.NodeSteps{ + { + DisplayName: "Deploy to Kubernetes", + ID: "1", + Result: "SUCCESS", + }, + } + + fakeData["project1-pipeline1-branch1-run1"] = BranchPipelineRunNodes + fakeData["project1-pipeline1-branch1-run1-1"] = BranchNodeSteps + fakeData["project1-pipeline1-branch1-run1-2"] = BranchNodeSteps + fakeData["project1-pipeline1-branch1-run1-3"] = BranchNodeSteps + + devopsClient := fake.NewFakeDevops(fakeData) + + devopsOperator := NewDevopsOperator(devopsClient) + + httpReq, _ := http.NewRequest(http.MethodGet, baseUrl+"devops/project1/pipelines/pipeline1/branchs/branch1/runs/run1/nodesdetail/?limit=10000", nil) + + nodesDetails, err := devopsOperator.GetBranchNodesDetail("project1", "pipeline1", "branch1", "run1", httpReq) + if err != nil || nodesDetails == nil { + t.Fatalf("should not get error %+v", err) + } + + for _, v := range nodesDetails { + if v.Steps[0].ID == "" { + t.Fatalf("Can not get any step.") + } } } diff --git a/pkg/models/devops/membership.go b/pkg/models/devops/membership.go index 57cf4628..0d4336e2 100644 --- a/pkg/models/devops/membership.go +++ b/pkg/models/devops/membership.go @@ -13,25 +13,19 @@ limitations under the License. package devops +import "kubesphere.io/kubesphere/pkg/simple/client/devops" + const ( - DevOpsProjectMembershipTableName = "project_membership" - DevOpsProjectMembershipUsernameColumn = "project_membership.username" - DevOpsProjectMembershipProjectIdColumn = "project_membership.project_id" - DevOpsProjectMembershipRoleColumn = "project_membership.role" + ProjectMembershipTableName = "project_membership" + ProjectMembershipUsernameColumn = "project_membership.username" + ProjectMembershipProjectIdColumn = "project_membership.project_id" + ProjectMembershipRoleColumn = "project_membership.role" ) -type DevOpsProjectMembership struct { - Username string `json:"username" description:"Member's username,username 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{}) +var ProjectMembershipColumns = GetColumnsFromStruct(&devops.ProjectMembership{}) -func NewDevOpsProjectMemberShip(username, projectId, role, grantBy string) *DevOpsProjectMembership { - return &DevOpsProjectMembership{ +func NewDevOpsProjectMemberShip(username, projectId, role, grantBy string) *devops.ProjectMembership { + return &devops.ProjectMembership{ Username: username, ProjectId: projectId, Role: role, diff --git a/pkg/models/devops/project_credential.go b/pkg/models/devops/project_credential.go index 7f64a13d..9fc22870 100644 --- a/pkg/models/devops/project_credential.go +++ b/pkg/models/devops/project_credential.go @@ -18,59 +18,6 @@ import ( "time" ) -const ( - CredentialTypeUsernamePassword = "username_password" - CredentialTypeSsh = "ssh" - CredentialTypeSecretText = "secret_text" - CredentialTypeKubeConfig = "kubeconfig" -) - -type JenkinsCredential struct { - Id string `json:"id" description:"Id of Credential, e.g. dockerhub-id"` - Type string `json:"type" description:"Type of Credential, e.g. ssh/kubeconfig"` - DisplayName string `json:"display_name,omitempty" description:"Credential's display name"` - Fingerprint *struct { - FileName string `json:"file_name,omitempty" description:"Credential's display name and description"` - Hash string `json:"hash,omitempty" description:"Credential's hash"` - Usage []*struct { - Name string `json:"name,omitempty" description:"Jenkins pipeline full name"` - Ranges struct { - Ranges []*struct { - Start int `json:"start,omitempty" description:"Start build number"` - End int `json:"end,omitempty" description:"End build number"` - } `json:"ranges,omitempty"` - } `json:"ranges,omitempty" description:"The build number of all pipelines that use this credential"` - } `json:"usage,omitempty" description:"all usage of Credential"` - } `json:"fingerprint,omitempty" description:"usage of the Credential"` - Description string `json:"description,omitempty" description:"Credential's description'"` - Domain string `json:"domain,omitempty" description:"Credential's domain,In ks we only use the default domain, default '_''"` - CreateTime *time.Time `json:"create_time,omitempty" description:"Credential's create_time'"` - Creator string `json:"creator,omitempty" description:"Creator's username"` - UsernamePasswordCredential *UsernamePasswordCredential `json:"username_password,omitempty" description:"username password Credential struct"` - SshCredential *SshCredential `json:"ssh,omitempty" description:"ssh Credential struct"` - SecretTextCredential *SecretTextCredential `json:"secret_text,omitempty" description:"secret_text Credential struct"` - KubeconfigCredential *KubeconfigCredential `json:"kubeconfig,omitempty" description:"kubeconfig Credential struct"` -} - -type UsernamePasswordCredential struct { - Username string `json:"username,omitempty" description:"username of username_password credential"` - Password string `json:"password,omitempty" description:"password of username_password credential"` -} - -type SshCredential struct { - Username string `json:"username,omitempty" description:"username of ssh credential"` - Passphrase string `json:"passphrase,omitempty" description:"passphrase of ssh credential, password of ssh credential"` - PrivateKey string `json:"private_key,omitempty" mapstructure:"private_key" description:"private key of ssh credential"` -} - -type SecretTextCredential struct { - Secret string `json:"secret,omitempty" description:"secret content of credential"` -} - -type KubeconfigCredential struct { - Content string `json:"content,omitempty" description:"content of kubeconfig"` -} - const ( ProjectCredentialTableName = "project_credential" ProjectCredentialIdColumn = "credential_id" @@ -78,13 +25,6 @@ const ( ProjectCredentialProjectIdColumn = "project_id" ) -var CredentialTypeMap = map[string]string{ - "SSH Username with private key": CredentialTypeSsh, - "Username with password": CredentialTypeUsernamePassword, - "Secret text": CredentialTypeSecretText, - "Kubernetes configuration (kubeconfig)": CredentialTypeKubeConfig, -} - type ProjectCredential struct { ProjectId string `json:"project_id"` CredentialId string `json:"credential_id"` diff --git a/pkg/models/devops/project_credential_handler.go b/pkg/models/devops/project_credential_handler.go index de0e0f8e..5640b3fe 100644 --- a/pkg/models/devops/project_credential_handler.go +++ b/pkg/models/devops/project_credential_handler.go @@ -15,389 +15,209 @@ package devops import ( "fmt" - "github.com/PuerkitoBio/goquery" - "github.com/asaskevich/govalidator" "github.com/emicklei/go-restful" "github.com/gocraft/dbr" "k8s.io/klog" + "kubesphere.io/kubesphere/pkg/simple/client/devops" + "kubesphere.io/kubesphere/pkg/simple/client/mysql" "kubesphere.io/kubesphere/pkg/db" - "kubesphere.io/kubesphere/pkg/gojenkins" - "kubesphere.io/kubesphere/pkg/gojenkins/utils" - cs "kubesphere.io/kubesphere/pkg/simple/client" "net/http" - "strings" ) -func CreateProjectCredential(projectId, username string, credentialRequest *JenkinsCredential) (string, error) { - devops, err := cs.ClientSets().Devops() - if err != nil { - return "", restful.NewError(http.StatusServiceUnavailable, err.Error()) - } +type ProjectCredentialOperator interface { + CreateProjectCredential(projectId, username string, credentialRequest *devops.Credential) (string, error) + UpdateProjectCredential(projectId, credentialId string, credentialRequest *devops.Credential) (string, error) + DeleteProjectCredential(projectId, credentialId string) (string, error) + GetProjectCredential(projectId, credentialId, getContent string) (*devops.Credential, error) + GetProjectCredentials(projectId string) ([]*devops.Credential, error) +} - jenkinsClient := devops.Jenkins() +type projectCredentialOperator struct { + devopsClient devops.Interface + db *mysql.Database +} - err = checkJenkinsCredentialExists(projectId, credentialRequest.Domain, credentialRequest.Id) - if err != nil { - klog.Errorf("%+v", err) - return "", err - } +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 CredentialTypeUsernamePassword: + 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()) } - 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: + 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()) } - 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: + 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()) } - - 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: + 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()) } - 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()) } + 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 } -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() +func (o *projectCredentialOperator) UpdateProjectCredential(projectId, credentialId string, credentialRequest *devops.Credential) (string, error) { - jenkinsCredential, err := jenkinsClient.GetCredentialInFolder(credentialRequest.Domain, - credentialId, - projectId) + credential, err := o.devopsClient.GetCredentialInProject(projectId, + credentialId, false) if err != nil { klog.Errorf("%+v", err) - return "", restful.NewError(utils.GetJenkinsStatusCode(err), err.Error()) + return "", err } - credentialType := CredentialTypeMap[jenkinsCredential.TypeName] - switch credentialType { - case CredentialTypeUsernamePassword: + 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()) } - 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: + 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()) } - 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: + 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()) } - 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: + 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()) } - 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() + credentialRequest.Id = credentialId + _, err = o.devopsClient.UpdateCredentialInProject(projectId, credentialRequest) if err != nil { - return "", restful.NewError(http.StatusServiceUnavailable, err.Error()) + klog.Errorf("%+v", err) + return "", restful.NewError(http.StatusBadRequest, err.Error()) } - jenkinsClient := devops.Jenkins() + return credentialId, nil - dbClient, err := cs.ClientSets().MySQL() - if err != nil { - return "", restful.NewError(http.StatusServiceUnavailable, err.Error()) - } +} - _, err = jenkinsClient.GetCredentialInFolder(credentialRequest.Domain, - credentialId, - projectId) +func (o *projectCredentialOperator) DeleteProjectCredential(projectId, credentialId string) (string, error) { + + _, err := o.devopsClient.GetCredentialInProject(projectId, + credentialId, false) if err != nil { klog.Errorf("%+v", err) - return "", restful.NewError(utils.GetJenkinsStatusCode(err), err.Error()) + return "", err } - id, err := jenkinsClient.DeleteCredentialInFolder(credentialRequest.Domain, credentialId, projectId) + id, err := o.devopsClient.DeleteCredentialInProject(projectId, credentialId) if err != nil { klog.Errorf("%+v", err) - return "", restful.NewError(utils.GetJenkinsStatusCode(err), err.Error()) + return "", err } deleteConditions := append(make([]dbr.Builder, 0), db.Eq(ProjectCredentialProjectIdColumn, projectId)) deleteConditions = append(deleteConditions, db.Eq(ProjectCredentialIdColumn, credentialId)) - if !govalidator.IsNull(credentialRequest.Domain) { - deleteConditions = append(deleteConditions, db.Eq(ProjectCredentialDomainColumn, credentialRequest.Domain)) - } else { - deleteConditions = append(deleteConditions, db.Eq(ProjectCredentialDomainColumn, "_")) - } + deleteConditions = append(deleteConditions, db.Eq(ProjectCredentialDomainColumn, "_")) - _, err = dbClient.DeleteFrom(ProjectCredentialTableName). + _, err = o.db.DeleteFrom(ProjectCredentialTableName). Where(db.And(deleteConditions...)).Exec() if err != nil && err != db.ErrNotFound { klog.Errorf("%+v", err) - return "", restful.NewError(http.StatusInternalServerError, err.Error()) + return "", err } return *id, nil } -func GetProjectCredential(projectId, credentialId, domain, getContent string) (*JenkinsCredential, error) { - devops, err := cs.ClientSets().Devops() - if err != nil { - return nil, restful.NewError(http.StatusServiceUnavailable, err.Error()) - } - jenkinsClient := devops.Jenkins() +func (o *projectCredentialOperator) GetProjectCredential(projectId, credentialId, getContent string) (*devops.Credential, error) { - dbClient, err := cs.ClientSets().MySQL() - if err != nil { - return nil, restful.NewError(http.StatusServiceUnavailable, err.Error()) + content := false + if getContent != "" { + content = true } - jenkinsResponse, err := jenkinsClient.GetCredentialInFolder(domain, + credential, err := o.devopsClient.GetCredentialInProject(projectId, credentialId, - projectId) + content) if err != nil { klog.Errorf("%+v", err) - return nil, restful.NewError(utils.GetJenkinsStatusCode(err), err.Error()) + return nil, err } - projectCredential := &ProjectCredential{} - err = dbClient.Select(ProjectCredentialColumns...). + err = o.db.Select(ProjectCredentialColumns...). From(ProjectCredentialTableName).Where( db.And(db.Eq(ProjectCredentialProjectIdColumn, projectId), db.Eq(ProjectCredentialIdColumn, credentialId), - db.Eq(ProjectCredentialDomainColumn, jenkinsResponse.Domain))).LoadOne(projectCredential) + db.Eq(ProjectCredentialDomainColumn, credential.Domain))).LoadOne(projectCredential) if err != nil && err != db.ErrNotFound { klog.Errorf("%+v", err) return nil, restful.NewError(http.StatusInternalServerError, err.Error()) } - response := formatCredentialResponse(jenkinsResponse, projectCredential) - if getContent != "" { - stringBody, err := jenkinsClient.GetCredentialContentInFolder(jenkinsResponse.Domain, credentialId, projectId) - if err != nil { - klog.Errorf("%+v", err) - return nil, restful.NewError(utils.GetJenkinsStatusCode(err), err.Error()) - } - stringReader := strings.NewReader(stringBody) - doc, err := goquery.NewDocumentFromReader(stringReader) - if err != nil { - klog.Errorf("%+v", err) - return nil, restful.NewError(http.StatusInternalServerError, err.Error()) - } - switch response.Type { - case CredentialTypeKubeConfig: - content := &KubeconfigCredential{} - doc.Find("textarea[name*=content]").Each(func(i int, selection *goquery.Selection) { - value := selection.Text() - content.Content = value - }) - response.KubeconfigCredential = content - case CredentialTypeUsernamePassword: - content := &UsernamePasswordCredential{} - doc.Find("input[name*=username]").Each(func(i int, selection *goquery.Selection) { - value, _ := selection.Attr("value") - content.Username = value - }) - - response.UsernamePasswordCredential = content - case CredentialTypeSsh: - content := &SshCredential{} - doc.Find("input[name*=username]").Each(func(i int, selection *goquery.Selection) { - value, _ := selection.Attr("value") - content.Username = value - }) - - doc.Find("textarea[name*=privateKey]").Each(func(i int, selection *goquery.Selection) { - value := selection.Text() - content.PrivateKey = value - }) - response.SshCredential = content - } - } + response := formatCredentialResponse(credential, projectCredential) return response, nil } -func GetProjectCredentials(projectId, domain string) ([]*JenkinsCredential, error) { - devops, err := cs.ClientSets().Devops() - if err != nil { - return nil, restful.NewError(http.StatusServiceUnavailable, err.Error()) - } - jenkinsClient := devops.Jenkins() +func (o *projectCredentialOperator) GetProjectCredentials(projectId string) ([]*devops.Credential, error) { - dbClient, err := cs.ClientSets().MySQL() - if err != nil { - return nil, restful.NewError(http.StatusServiceUnavailable, err.Error()) - } - jenkinsCredentialResponses, err := jenkinsClient.GetCredentialsInFolder(domain, projectId) + credentialResponses, err := o.devopsClient.GetCredentialsInProject(projectId) if err != nil { klog.Errorf("%+v", err) - return nil, restful.NewError(utils.GetJenkinsStatusCode(err), err.Error()) + return nil, err } selectCondition := db.Eq(ProjectCredentialProjectIdColumn, projectId) - if !govalidator.IsNull(domain) { - selectCondition = db.And(selectCondition, db.Eq(ProjectCredentialDomainColumn, domain)) - } projectCredentials := make([]*ProjectCredential, 0) - _, err = dbClient.Select(ProjectCredentialColumns...). + _, err = o.db.Select(ProjectCredentialColumns...). From(ProjectCredentialTableName).Where(selectCondition).Load(&projectCredentials) if err != nil { klog.Errorf("%+v", err) return nil, restful.NewError(http.StatusInternalServerError, err.Error()) } - response := formatCredentialsResponse(jenkinsCredentialResponses, projectCredentials) + response := formatCredentialsResponse(credentialResponses, projectCredentials) return response, nil } -func insertCredentialToDb(projectId, credentialId, domain, username string) error { - dbClient, err := cs.ClientSets().MySQL() - if err != nil { - return err - } +func (o *projectCredentialOperator) insertCredentialToDb(projectId, credentialId, domain, username string) error { projectCredential := NewProjectCredential(projectId, credentialId, domain, username) - _, err = dbClient.InsertInto(ProjectCredentialTableName).Columns(ProjectCredentialColumns...). + _, err := o.db.InsertInto(ProjectCredentialTableName).Columns(ProjectCredentialColumns...). Record(projectCredential).Exec() if err != nil { klog.Errorf("%+v", err) @@ -406,41 +226,19 @@ func insertCredentialToDb(projectId, credentialId, domain, username string) erro return nil } -func checkJenkinsCredentialExists(projectId, domain, credentialId string) error { - devops, err := cs.ClientSets().Devops() - if err != nil { - return restful.NewError(http.StatusServiceUnavailable, err.Error()) - } - - jenkinsClient := devops.Jenkins() - - credential, err := jenkinsClient.GetCredentialInFolder(domain, credentialId, projectId) - if credential != nil { - err := fmt.Errorf("credential id [%s] has been used", credential.Id) - klog.Warning(err.Error()) - return restful.NewError(http.StatusConflict, err.Error()) - } - if err != nil && utils.GetJenkinsStatusCode(err) != http.StatusNotFound { - klog.Errorf("%+v", err) - - return restful.NewError(http.StatusBadRequest, err.Error()) - } - return nil -} - func formatCredentialResponse( - jenkinsCredentialResponse *gojenkins.CredentialResponse, - dbCredentialResponse *ProjectCredential) *JenkinsCredential { - response := &JenkinsCredential{} - response.Id = jenkinsCredentialResponse.Id - response.Description = jenkinsCredentialResponse.Description - response.DisplayName = jenkinsCredentialResponse.DisplayName - if jenkinsCredentialResponse.Fingerprint != nil && jenkinsCredentialResponse.Fingerprint.Hash != "" { + credentialResponse *devops.Credential, + dbCredentialResponse *ProjectCredential) *devops.Credential { + response := &devops.Credential{} + response.Id = credentialResponse.Id + response.Description = credentialResponse.Description + response.DisplayName = credentialResponse.DisplayName + if credentialResponse.Fingerprint != nil && credentialResponse.Fingerprint.Hash != "" { response.Fingerprint = &struct { FileName string `json:"file_name,omitempty" description:"Credential's display name and description"` Hash string `json:"hash,omitempty" description:"Credential's hash"` Usage []*struct { - Name string `json:"name,omitempty" description:"Jenkins pipeline full name"` + Name string `json:"name,omitempty" description:"pipeline full name"` Ranges struct { Ranges []*struct { Start int `json:"start,omitempty" description:"Start build number"` @@ -449,40 +247,40 @@ func formatCredentialResponse( } `json:"ranges,omitempty" description:"The build number of all pipelines that use this credential"` } `json:"usage,omitempty" description:"all usage of Credential"` }{} - response.Fingerprint.FileName = jenkinsCredentialResponse.Fingerprint.FileName - response.Fingerprint.Hash = jenkinsCredentialResponse.Fingerprint.Hash - for _, usage := range jenkinsCredentialResponse.Fingerprint.Usage { + response.Fingerprint.FileName = credentialResponse.Fingerprint.FileName + response.Fingerprint.Hash = credentialResponse.Fingerprint.Hash + for _, usage := range credentialResponse.Fingerprint.Usage { response.Fingerprint.Usage = append(response.Fingerprint.Usage, usage) } } - response.Domain = jenkinsCredentialResponse.Domain + response.Domain = credentialResponse.Domain if dbCredentialResponse != nil { response.CreateTime = &dbCredentialResponse.CreateTime response.Creator = dbCredentialResponse.Creator } - credentialType, ok := CredentialTypeMap[jenkinsCredentialResponse.TypeName] + credentialType, ok := devops.CredentialTypeMap[credentialResponse.Type] if ok { response.Type = credentialType return response } - response.Type = jenkinsCredentialResponse.TypeName + response.Type = credentialResponse.Type return response } -func formatCredentialsResponse(jenkinsCredentialsResponse []*gojenkins.CredentialResponse, - projectCredentials []*ProjectCredential) []*JenkinsCredential { - responseSlice := make([]*JenkinsCredential, 0) - for _, jenkinsCredential := range jenkinsCredentialsResponse { +func formatCredentialsResponse(credentialsResponse []*devops.Credential, + projectCredentials []*ProjectCredential) []*devops.Credential { + responseSlice := make([]*devops.Credential, 0) + for _, credential := range credentialsResponse { var dbCredential *ProjectCredential = nil for _, projectCredential := range projectCredentials { - if projectCredential.CredentialId == jenkinsCredential.Id && - projectCredential.Domain == jenkinsCredential.Domain { + if projectCredential.CredentialId == credential.Id && + projectCredential.Domain == credential.Domain { dbCredential = projectCredential } } - responseSlice = append(responseSlice, formatCredentialResponse(jenkinsCredential, dbCredential)) + responseSlice = append(responseSlice, formatCredentialResponse(credential, dbCredential)) } return responseSlice } diff --git a/pkg/models/devops/project_handler.go b/pkg/models/devops/project_handler.go index 49537baf..76a25460 100644 --- a/pkg/models/devops/project_handler.go +++ b/pkg/models/devops/project_handler.go @@ -14,23 +14,37 @@ limitations under the License. package devops import ( + "fmt" "github.com/asaskevich/govalidator" "github.com/emicklei/go-restful" "github.com/gocraft/dbr" "k8s.io/klog" "kubesphere.io/kubesphere/pkg/api/devops/v1alpha2" "kubesphere.io/kubesphere/pkg/db" - cs "kubesphere.io/kubesphere/pkg/simple/client" + "kubesphere.io/kubesphere/pkg/simple/client/devops" + "kubesphere.io/kubesphere/pkg/simple/client/mysql" + "kubesphere.io/kubesphere/pkg/utils/reflectutils" "net/http" ) -func GetProject(projectId string) (*v1alpha2.DevOpsProject, error) { - dbconn, err := cs.ClientSets().MySQL() - if err != nil { - return nil, err - } +type ProjectOperator interface { + GetProject(projectId string) (*v1alpha2.DevOpsProject, error) + UpdateProject(project *v1alpha2.DevOpsProject) (*v1alpha2.DevOpsProject, error) + CheckProjectUserInRole(username, projectId string, roles []string) error +} + +type projectOperator struct { + db *mysql.Database +} + +func NewProjectOperator(dbClient *mysql.Database) ProjectOperator { + return &projectOperator{db: dbClient} +} + +func (o *projectOperator) GetProject(projectId string) (*v1alpha2.DevOpsProject, error) { + project := &v1alpha2.DevOpsProject{} - err = dbconn.Select(DevOpsProjectColumns...). + err := o.db.Select(DevOpsProjectColumns...). From(DevOpsProjectTableName). Where(db.Eq(DevOpsProjectIdColumn, projectId)). LoadOne(project) @@ -46,13 +60,9 @@ func GetProject(projectId string) (*v1alpha2.DevOpsProject, error) { return project, nil } -func UpdateProject(project *v1alpha2.DevOpsProject) (*v1alpha2.DevOpsProject, error) { - dbconn, err := cs.ClientSets().MySQL() - if err != nil { - return nil, err - } +func (o *projectOperator) UpdateProject(project *v1alpha2.DevOpsProject) (*v1alpha2.DevOpsProject, error) { - query := dbconn.Update(DevOpsProjectTableName) + query := o.db.Update(DevOpsProjectTableName) if !govalidator.IsNull(project.Description) { query.Set(DevOpsProjectDescriptionColumn, project.Description) } @@ -73,7 +83,7 @@ func UpdateProject(project *v1alpha2.DevOpsProject) (*v1alpha2.DevOpsProject, er } } newProject := &v1alpha2.DevOpsProject{} - err = dbconn.Select(DevOpsProjectColumns...). + err := o.db.Select(DevOpsProjectColumns...). From(DevOpsProjectTableName). Where(db.Eq(DevOpsProjectIdColumn, project.ProjectId)). LoadOne(newProject) @@ -83,3 +93,22 @@ func UpdateProject(project *v1alpha2.DevOpsProject) (*v1alpha2.DevOpsProject, er } return newProject, nil } + +func (o *projectOperator) CheckProjectUserInRole(username, projectId string, roles []string) error { + if username == KS_ADMIN { + return nil + } + membership := &devops.ProjectMembership{} + err := o.db.Select(ProjectMembershipColumns...). + From(ProjectMembershipTableName). + Where(db.And( + db.Eq(ProjectMembershipUsernameColumn, username), + db.Eq(ProjectMembershipProjectIdColumn, projectId))).LoadOne(membership) + if err != nil { + return err + } + if !reflectutils.In(membership.Role, roles) { + return fmt.Errorf("user [%s] in project [%s] role is not in %s", username, projectId, roles) + } + return nil +} diff --git a/pkg/models/devops/project_member_handler.go b/pkg/models/devops/project_member_handler.go index 856f1d0a..3702c034 100644 --- a/pkg/models/devops/project_member_handler.go +++ b/pkg/models/devops/project_member_handler.go @@ -15,43 +15,58 @@ package devops import ( "fmt" "k8s.io/klog" + "kubesphere.io/kubesphere/pkg/simple/client/devops" + "kubesphere.io/kubesphere/pkg/simple/client/mysql" "net/http" "github.com/emicklei/go-restful" "github.com/gocraft/dbr" "kubesphere.io/kubesphere/pkg/db" - "kubesphere.io/kubesphere/pkg/gojenkins" - "kubesphere.io/kubesphere/pkg/gojenkins/utils" "kubesphere.io/kubesphere/pkg/models" "kubesphere.io/kubesphere/pkg/server/params" - cs "kubesphere.io/kubesphere/pkg/simple/client" ) -func GetProjectMembers(projectId string, conditions *params.Conditions, orderBy string, reverse bool, limit int, offset int) (*models.PageableResponse, error) { - dbconn, err := cs.ClientSets().MySQL() - if err != nil { - return nil, err +type ProjectMemberOperator interface { + GetProjectMembers(projectId string, conditions *params.Conditions, orderBy string, reverse bool, limit int, offset int) (*models.PageableResponse, error) + GetProjectMember(projectId, username string) (*devops.ProjectMembership, error) + AddProjectMember(projectId string, membership *devops.ProjectMembership) (*devops.ProjectMembership, error) + UpdateProjectMember(projectId string, membership *devops.ProjectMembership) (*devops.ProjectMembership, error) + DeleteProjectMember(projectId, username string) (string, error) +} +type projectMemberOperator struct { + db *mysql.Database + projectMemberOperator devops.ProjectMemberOperator +} + +func NewProjectMemberOperator(devopsClient devops.ProjectMemberOperator, dbClient *mysql.Database) ProjectMemberOperator { + return &projectMemberOperator{ + db: dbClient, + projectMemberOperator: devopsClient, } - memberships := make([]*DevOpsProjectMembership, 0) +} + +func (o *projectMemberOperator) GetProjectMembers(projectId string, conditions *params.Conditions, orderBy string, reverse bool, limit int, offset int) (*models.PageableResponse, error) { + + memberships := make([]*devops.ProjectMembership, 0) var sqconditions []dbr.Builder - sqconditions = append(sqconditions, db.Eq(DevOpsProjectMembershipProjectIdColumn, projectId)) + sqconditions = append(sqconditions, db.Eq(ProjectMembershipProjectIdColumn, projectId)) if keyword := conditions.Match["keyword"]; keyword != "" { - sqconditions = append(sqconditions, db.Like(DevOpsProjectMembershipUsernameColumn, keyword)) + sqconditions = append(sqconditions, db.Like(ProjectMembershipUsernameColumn, keyword)) } - query := dbconn.Select(DevOpsProjectMembershipColumns...). - From(DevOpsProjectMembershipTableName) + query := *o.db.Select(ProjectMembershipColumns...). + From(ProjectMembershipTableName) switch orderBy { case "name": if reverse { - query.OrderDesc(DevOpsProjectMembershipUsernameColumn) + query.OrderDesc(ProjectMembershipUsernameColumn) } else { - query.OrderAsc(DevOpsProjectMembershipUsernameColumn) + query.OrderAsc(ProjectMembershipUsernameColumn) } default: if reverse { - query.OrderDesc(DevOpsProjectMembershipRoleColumn) + query.OrderDesc(ProjectMembershipRoleColumn) } else { - query.OrderAsc(DevOpsProjectMembershipRoleColumn) + query.OrderAsc(ProjectMembershipRoleColumn) } } query.Limit(uint64(limit)) @@ -61,7 +76,7 @@ func GetProjectMembers(projectId string, conditions *params.Conditions, orderBy } else { query.Where(sqconditions[0]) } - _, err = query.Load(&memberships) + _, err := query.Load(&memberships) if err != nil && err != dbr.ErrNotFound { klog.Errorf("%+v", err) return nil, restful.NewError(http.StatusInternalServerError, err.Error()) @@ -79,17 +94,13 @@ func GetProjectMembers(projectId string, conditions *params.Conditions, orderBy return &models.PageableResponse{Items: result, TotalCount: int(count)}, nil } -func GetProjectMember(projectId, username string) (*DevOpsProjectMembership, error) { - dbconn, err := cs.ClientSets().MySQL() - if err != nil { - return nil, err - } +func (o *projectMemberOperator) GetProjectMember(projectId, username string) (*devops.ProjectMembership, error) { - member := &DevOpsProjectMembership{} - err = dbconn.Select(DevOpsProjectMembershipColumns...). - From(DevOpsProjectMembershipTableName). - Where(db.And(db.Eq(DevOpsProjectMembershipProjectIdColumn, projectId), - db.Eq(DevOpsProjectMembershipUsernameColumn, username))). + member := &devops.ProjectMembership{} + err := o.db.Select(ProjectMembershipColumns...). + From(ProjectMembershipTableName). + Where(db.And(db.Eq(ProjectMembershipProjectIdColumn, projectId), + db.Eq(ProjectMembershipUsernameColumn, username))). LoadOne(&member) if err != nil && err != dbr.ErrNotFound { klog.Errorf("%+v", err) @@ -102,31 +113,17 @@ func GetProjectMember(projectId, username string) (*DevOpsProjectMembership, err return member, nil } -func AddProjectMember(projectId, operator string, member *DevOpsProjectMembership) (*DevOpsProjectMembership, error) { - dbconn, err := cs.ClientSets().MySQL() - if err != nil { - return nil, err - } - devops, err := cs.ClientSets().Devops() - if err != nil { - return nil, err - } - jenkinsClient := devops.Jenkins() - if jenkinsClient == nil { - err := fmt.Errorf("could not connect to jenkins") - klog.Error(err) - return nil, restful.NewError(http.StatusServiceUnavailable, err.Error()) - } +func (o *projectMemberOperator) AddProjectMember(projectId string, membership *devops.ProjectMembership) (*devops.ProjectMembership, error) { - membership := &DevOpsProjectMembership{} - err = dbconn.Select(DevOpsProjectMembershipColumns...). - From(DevOpsProjectMembershipTableName). + dbmembership := &devops.ProjectMembership{} + err := o.db.Select(ProjectMembershipColumns...). + From(ProjectMembershipTableName). Where(db.And( - db.Eq(DevOpsProjectMembershipUsernameColumn, member.Username), - db.Eq(DevOpsProjectMembershipProjectIdColumn, projectId))).LoadOne(membership) + db.Eq(ProjectMembershipUsernameColumn, membership.Username), + db.Eq(ProjectMembershipProjectIdColumn, projectId))).LoadOne(dbmembership) // if user could be founded in db, user have been added to project if err == nil { - err = fmt.Errorf("user [%s] have been added to project", member.Username) + err = fmt.Errorf("user [%s] have been added to project", membership.Username) klog.Errorf("%+v", err) return nil, restful.NewError(http.StatusBadRequest, err.Error()) } @@ -136,161 +133,65 @@ func AddProjectMember(projectId, operator string, member *DevOpsProjectMembershi return nil, restful.NewError(http.StatusInternalServerError, err.Error()) } - globalRole, err := jenkinsClient.GetGlobalRole(JenkinsAllUserRoleName) - if err != nil { - klog.Errorf("%+v", err) - return nil, restful.NewError(utils.GetJenkinsStatusCode(err), err.Error()) - } - if globalRole == nil { - _, err := jenkinsClient.AddGlobalRole(JenkinsAllUserRoleName, gojenkins.GlobalPermissionIds{ - GlobalRead: true, - }, true) - if err != nil { - klog.Errorf("failed to create jenkins global role %+v", err) - return nil, restful.NewError(utils.GetJenkinsStatusCode(err), err.Error()) - } - } - err = globalRole.AssignRole(member.Username) + _, err = o.projectMemberOperator.AddProjectMember(membership) 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()) + return nil, err } - projectMembership := NewDevOpsProjectMemberShip(member.Username, projectId, member.Role, operator) - _, err = dbconn. - InsertInto(DevOpsProjectMembershipTableName). - Columns(DevOpsProjectMembershipColumns...). + projectMembership := NewDevOpsProjectMemberShip(membership.Username, projectId, membership.Role, membership.GrantBy) + _, err = o.db. + InsertInto(ProjectMembershipTableName). + Columns(ProjectMembershipColumns...). Record(projectMembership).Exec() if err != nil { klog.Errorf("%+v", err) - err = projectRole.UnAssignRole(member.Username) - if err != nil { - klog.Errorf("%+v", err) - return nil, restful.NewError(utils.GetJenkinsStatusCode(err), err.Error()) - } - err = pipelineRole.UnAssignRole(member.Username) + _, err = o.projectMemberOperator.DeleteProjectMember(membership) if err != nil { klog.Errorf("%+v", err) - return nil, restful.NewError(utils.GetJenkinsStatusCode(err), err.Error()) + return nil, err } return nil, restful.NewError(http.StatusInternalServerError, err.Error()) } return projectMembership, nil } -func UpdateProjectMember(projectId, operator string, member *DevOpsProjectMembership) (*DevOpsProjectMembership, error) { - dbconn, err := cs.ClientSets().MySQL() - if err != nil { - return nil, restful.NewError(http.StatusServiceUnavailable, err.Error()) - } - devops, err := cs.ClientSets().Devops() - if err != nil { - return nil, err - } - - jenkinsClient := devops.Jenkins() - if jenkinsClient == nil { - err := fmt.Errorf("could not connect to jenkins") - klog.Error(err) - return nil, restful.NewError(http.StatusServiceUnavailable, err.Error()) - } +func (o *projectMemberOperator) UpdateProjectMember(projectId string, membership *devops.ProjectMembership) (*devops.ProjectMembership, error) { - oldMembership := &DevOpsProjectMembership{} - err = dbconn.Select(DevOpsProjectMembershipColumns...). - From(DevOpsProjectMembershipTableName). + oldMembership := &devops.ProjectMembership{} + err := o.db.Select(ProjectMembershipColumns...). + From(ProjectMembershipTableName). Where(db.And( - db.Eq(DevOpsProjectMembershipUsernameColumn, member.Username), - db.Eq(DevOpsProjectMembershipProjectIdColumn, projectId), + db.Eq(ProjectMembershipUsernameColumn, membership.Username), + db.Eq(ProjectMembershipProjectIdColumn, projectId), )).LoadOne(oldMembership) if err != nil { klog.Errorf("%+v", err) return nil, restful.NewError(http.StatusBadRequest, err.Error()) } - oldProjectRole, err := jenkinsClient.GetProjectRole(GetProjectRoleName(projectId, oldMembership.Role)) + _, err = o.projectMemberOperator.UpdateProjectMember(oldMembership, membership) if err != nil { klog.Errorf("%+v", err) - return nil, restful.NewError(utils.GetJenkinsStatusCode(err), err.Error()) - } - - err = oldProjectRole.UnAssignRole(member.Username) - if err != nil { - klog.Errorf("%+v", err) - return nil, restful.NewError(utils.GetJenkinsStatusCode(err), err.Error()) - } - - oldPipelineRole, err := jenkinsClient.GetProjectRole(GetPipelineRoleName(projectId, oldMembership.Role)) - if err != nil { - klog.Errorf("%+v", err) - return nil, restful.NewError(utils.GetJenkinsStatusCode(err), err.Error()) - } - - err = oldPipelineRole.UnAssignRole(member.Username) - if err != nil { - klog.Errorf("%+v", err) - return nil, restful.NewError(utils.GetJenkinsStatusCode(err), err.Error()) - } - - projectRole, err := jenkinsClient.GetProjectRole(GetProjectRoleName(projectId, member.Role)) - if err != nil { - klog.Errorf("%+v", err) - return nil, restful.NewError(utils.GetJenkinsStatusCode(err), err.Error()) - } - - err = projectRole.AssignRole(member.Username) - if err != nil { - klog.Errorf("%+v", err) - return nil, restful.NewError(utils.GetJenkinsStatusCode(err), err.Error()) - } - - pipelineRole, err := jenkinsClient.GetProjectRole(GetPipelineRoleName(projectId, member.Role)) - if err != nil { - klog.Errorf("%+v", err) - return nil, restful.NewError(utils.GetJenkinsStatusCode(err), err.Error()) - } - - err = pipelineRole.AssignRole(member.Username) - if err != nil { - klog.Errorf("%+v", err) - return nil, restful.NewError(utils.GetJenkinsStatusCode(err), err.Error()) + return nil, err } - - _, err = dbconn.Update(DevOpsProjectMembershipTableName). - Set(DevOpsProjectMembershipRoleColumn, member.Role). + _, err = o.db.Update(ProjectMembershipTableName). + Set(ProjectMembershipRoleColumn, membership.Role). Where(db.And( - db.Eq(DevOpsProjectMembershipProjectIdColumn, projectId), - db.Eq(DevOpsProjectMembershipUsernameColumn, member.Username), + db.Eq(ProjectMembershipProjectIdColumn, projectId), + db.Eq(ProjectMembershipUsernameColumn, membership.Username), )).Exec() + if err != nil { klog.Errorf("%+v", err) return nil, restful.NewError(http.StatusInternalServerError, err.Error()) } - responseMembership := &DevOpsProjectMembership{} - err = dbconn.Select(DevOpsProjectMembershipColumns...). - From(DevOpsProjectMembershipTableName). + responseMembership := &devops.ProjectMembership{} + err = o.db.Select(ProjectMembershipColumns...). + From(ProjectMembershipTableName). Where(db.And( - db.Eq(DevOpsProjectMembershipUsernameColumn, member.Username), - db.Eq(DevOpsProjectMembershipProjectIdColumn, projectId), + db.Eq(ProjectMembershipUsernameColumn, membership.Username), + db.Eq(ProjectMembershipProjectIdColumn, projectId), )).LoadOne(responseMembership) if err != nil { klog.Errorf("%+v", err) @@ -299,25 +200,14 @@ func UpdateProjectMember(projectId, operator string, member *DevOpsProjectMember return responseMembership, nil } -func DeleteProjectMember(projectId, username string) (string, error) { - dbconn, err := cs.ClientSets().MySQL() - if err != nil { - return "", restful.NewError(http.StatusServiceUnavailable, err.Error()) - } - - devops, err := cs.ClientSets().Devops() - if err != nil { - return "", restful.NewError(http.StatusServiceUnavailable, err.Error()) - } - - jenkinsClient := devops.Jenkins() +func (o *projectMemberOperator) DeleteProjectMember(projectId, username string) (string, error) { - oldMembership := &DevOpsProjectMembership{} - err = dbconn.Select(DevOpsProjectMembershipColumns...). - From(DevOpsProjectMembershipTableName). + oldMembership := &devops.ProjectMembership{} + err := o.db.Select(ProjectMembershipColumns...). + From(ProjectMembershipTableName). Where(db.And( - db.Eq(DevOpsProjectMembershipUsernameColumn, username), - db.Eq(DevOpsProjectMembershipProjectIdColumn, projectId), + db.Eq(ProjectMembershipUsernameColumn, username), + db.Eq(ProjectMembershipProjectIdColumn, projectId), )).LoadOne(oldMembership) if err != nil { if err != db.ErrNotFound { @@ -329,12 +219,12 @@ func DeleteProjectMember(projectId, username string) (string, error) { } } - if oldMembership.Role == ProjectOwner { - count, err := dbconn.Select(DevOpsProjectMembershipProjectIdColumn). - From(DevOpsProjectMembershipTableName). + if oldMembership.Role == devops.ProjectOwner { + count, err := o.db.Select(ProjectMembershipProjectIdColumn). + From(ProjectMembershipTableName). Where(db.And( - db.Eq(DevOpsProjectMembershipProjectIdColumn, projectId), - db.Eq(DevOpsProjectMembershipRoleColumn, ProjectOwner))).Count() + db.Eq(ProjectMembershipProjectIdColumn, projectId), + db.Eq(ProjectMembershipRoleColumn, devops.ProjectOwner))).Count() if err != nil { klog.Errorf("%+v", err) return "", restful.NewError(http.StatusInternalServerError, err.Error()) @@ -346,32 +236,16 @@ func DeleteProjectMember(projectId, username string) (string, error) { } } - oldProjectRole, err := jenkinsClient.GetProjectRole(GetProjectRoleName(projectId, oldMembership.Role)) + _, err = o.projectMemberOperator.DeleteProjectMember(oldMembership) if err != nil { - klog.Errorf("%+v", err) - return "", restful.NewError(utils.GetJenkinsStatusCode(err), err.Error()) - } - err = oldProjectRole.UnAssignRole(username) - if err != nil { - klog.Errorf("%+v", err) - return "", restful.NewError(utils.GetJenkinsStatusCode(err), err.Error()) - } - - oldPipelineRole, err := jenkinsClient.GetProjectRole(GetPipelineRoleName(projectId, oldMembership.Role)) - if err != nil { - klog.Errorf("%+v", err) - return "", restful.NewError(utils.GetJenkinsStatusCode(err), err.Error()) - } - err = oldPipelineRole.UnAssignRole(username) - if err != nil { - klog.Errorf("%+v", err) - return "", restful.NewError(utils.GetJenkinsStatusCode(err), err.Error()) + klog.Error(err) + return "", err } - _, err = dbconn.DeleteFrom(DevOpsProjectMembershipTableName). + _, err = o.db.DeleteFrom(ProjectMembershipTableName). Where(db.And( - db.Eq(DevOpsProjectMembershipProjectIdColumn, projectId), - db.Eq(DevOpsProjectMembershipUsernameColumn, username), + db.Eq(ProjectMembershipProjectIdColumn, projectId), + db.Eq(ProjectMembershipUsernameColumn, username), )).Exec() if err != nil { klog.Errorf("%+v", err) diff --git a/pkg/models/devops/project_pipeline_handler.go b/pkg/models/devops/project_pipeline_handler.go index 96f6986d..c263846e 100644 --- a/pkg/models/devops/project_pipeline_handler.go +++ b/pkg/models/devops/project_pipeline_handler.go @@ -17,295 +17,49 @@ import ( "fmt" "github.com/emicklei/go-restful" "k8s.io/klog" - "kubesphere.io/kubesphere/pkg/gojenkins/utils" - cs "kubesphere.io/kubesphere/pkg/simple/client" + "kubesphere.io/kubesphere/pkg/simple/client/devops" "net/http" ) -func CreateProjectPipeline(projectId string, pipeline *ProjectPipeline) (string, error) { - devops, err := cs.ClientSets().Devops() - if err != nil { - return "", restful.NewError(http.StatusServiceUnavailable, err.Error()) - } - 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(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 +type ProjectPipelineOperator interface { + CreateProjectPipeline(projectId string, pipeline *devops.ProjectPipeline) (string, error) + DeleteProjectPipeline(projectId string, pipelineId string) (string, error) + UpdateProjectPipeline(projectId, pipelineId string, pipeline *devops.ProjectPipeline) (string, error) + GetProjectPipelineConfig(projectId, pipelineId string) (*devops.ProjectPipeline, error) +} +type projectPipelineOperator struct { + pipelineOperator devops.ProjectPipelineOperator +} - default: - err := fmt.Errorf("error unsupport job type") - klog.Errorf("%+v", err) - return "", restful.NewError(http.StatusBadRequest, err.Error()) +func NewProjectPipelineOperator(devopsClient devops.ProjectPipelineOperator) ProjectPipelineOperator { + return &projectPipelineOperator{ + pipelineOperator: devopsClient, } } -func DeleteProjectPipeline(projectId string, pipelineId string) (string, error) { - devops, err := cs.ClientSets().Devops() - if err != nil { - return "", restful.NewError(http.StatusServiceUnavailable, err.Error()) - } - jenkinsClient := devops.Jenkins() +func (o *projectPipelineOperator) CreateProjectPipeline(projectId string, pipeline *devops.ProjectPipeline) (string, error) { + return o.pipelineOperator.CreateProjectPipeline(projectId, pipeline) +} - _, err = jenkinsClient.DeleteJob(pipelineId, projectId) - if err != nil { - klog.Errorf("%+v", err) - return "", restful.NewError(utils.GetJenkinsStatusCode(err), err.Error()) - } - return pipelineId, nil +func (o *projectPipelineOperator) DeleteProjectPipeline(projectId string, pipelineId string) (string, error) { + return o.pipelineOperator.DeleteProjectPipeline(projectId, pipelineId) } -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() +func (o *projectPipelineOperator) UpdateProjectPipeline(projectId, pipelineId string, pipeline *devops.ProjectPipeline) (string, error) { switch pipeline.Type { - case NoScmPipelineType: - - config, err := createPipelineConfigXml(pipeline.Pipeline) - if err != nil { - klog.Errorf("%+v", err) - return "", restful.NewError(http.StatusInternalServerError, err.Error()) - } - - job, err := jenkinsClient.GetJob(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 - + case devops.NoScmPipelineType: + pipeline.Pipeline.Name = pipelineId + case devops.MultiBranchPipelineType: + pipeline.MultiBranchPipeline.Name = pipelineId default: - err := fmt.Errorf("error unsupport job type") + err := fmt.Errorf("error unsupport pipeline type") klog.Errorf("%+v", err) return "", restful.NewError(http.StatusBadRequest, err.Error()) } + return o.pipelineOperator.UpdateProjectPipeline(projectId, pipeline) } -func GetProjectPipeline(projectId, pipelineId string) (*ProjectPipeline, error) { - devops, err := cs.ClientSets().Devops() - if err != nil { - return nil, restful.NewError(http.StatusServiceUnavailable, err.Error()) - } - jenkinsClient := devops.Jenkins() - - job, err := jenkinsClient.GetJob(pipelineId, projectId) - if err != nil { - klog.Errorf("%+v", err) - return nil, restful.NewError(utils.GetJenkinsStatusCode(err), err.Error()) - } - switch job.Raw.Class { - case "org.jenkinsci.plugins.workflow.job.WorkflowJob": - config, err := job.GetConfig() - if err != nil { - klog.Errorf("%+v", err) - return nil, restful.NewError(utils.GetJenkinsStatusCode(err), err.Error()) - } - pipeline, err := parsePipelineConfigXml(config) - if err != nil { - klog.Errorf("%+v", err) - return nil, restful.NewError(utils.GetJenkinsStatusCode(err), err.Error()) - } - pipeline.Name = pipelineId - return &ProjectPipeline{ - Type: NoScmPipelineType, - Pipeline: pipeline, - }, nil - - case "org.jenkinsci.plugins.workflow.multibranch.WorkflowMultiBranchProject": - config, err := job.GetConfig() - if err != nil { - klog.Errorf("%+v", err) - return nil, restful.NewError(utils.GetJenkinsStatusCode(err), err.Error()) - } - pipeline, err := parseMultiBranchPipelineConfigXml(config) - if err != nil { - klog.Errorf("%+v", err) - return nil, restful.NewError(utils.GetJenkinsStatusCode(err), err.Error()) - } - pipeline.Name = pipelineId - return &ProjectPipeline{ - Type: MultiBranchPipelineType, - MultiBranchPipeline: pipeline, - }, nil - default: - err := fmt.Errorf("error unsupport job type") - klog.Errorf("%+v", err) - return nil, restful.NewError(http.StatusBadRequest, err.Error()) - - } -} - -func GetPipelineSonar(projectId, pipelineId string) ([]*SonarStatus, error) { - devops, err := cs.ClientSets().Devops() - if err != nil { - return nil, restful.NewError(http.StatusServiceUnavailable, err.Error()) - } - jenkinsClient := devops.Jenkins() - - job, err := jenkinsClient.GetJob(pipelineId, projectId) - if err != nil { - klog.Errorf("%+v", err) - return nil, restful.NewError(utils.GetJenkinsStatusCode(err), err.Error()) - } - build, err := job.GetLastBuild() - if err != nil && utils.GetJenkinsStatusCode(err) != http.StatusNotFound { - klog.Errorf("%+v", err) - return nil, restful.NewError(utils.GetJenkinsStatusCode(err), err.Error()) - } else if err != nil { - klog.Errorf("%+v", err) - return nil, nil - } - - sonarStatus, err := getBuildSonarResults(build) - if err != nil { - klog.Errorf("%+v", err) - return nil, restful.NewError(http.StatusBadRequest, err.Error()) - } - if len(sonarStatus) == 0 { - build, err := job.GetLastCompletedBuild() - if err != nil && utils.GetJenkinsStatusCode(err) != http.StatusNotFound { - klog.Errorf("%+v", err) - return nil, restful.NewError(utils.GetJenkinsStatusCode(err), err.Error()) - } else if err != nil { - klog.Errorf("%+v", err) - return nil, nil - } - sonarStatus, err = getBuildSonarResults(build) - if err != nil { - klog.Errorf("%+v", err) - return nil, restful.NewError(http.StatusBadRequest, err.Error()) - } - } - return sonarStatus, nil -} - -func GetMultiBranchPipelineSonar(projectId, pipelineId, branchId string) ([]*SonarStatus, error) { - devops, err := cs.ClientSets().Devops() - if err != nil { - return nil, restful.NewError(http.StatusServiceUnavailable, err.Error()) - } - jenkinsClient := devops.Jenkins() - - job, err := jenkinsClient.GetJob(branchId, projectId, pipelineId) - if err != nil { - klog.Errorf("%+v", err) - return nil, restful.NewError(utils.GetJenkinsStatusCode(err), err.Error()) - } - build, err := job.GetLastBuild() - if err != nil && utils.GetJenkinsStatusCode(err) != http.StatusNotFound { - klog.Errorf("%+v", err) - return nil, restful.NewError(utils.GetJenkinsStatusCode(err), err.Error()) - } else if err != nil { - klog.Errorf("%+v", err) - return nil, nil - } - - sonarStatus, err := getBuildSonarResults(build) - if err != nil { - klog.Errorf("%+v", err) - return nil, restful.NewError(http.StatusBadRequest, err.Error()) - } - if len(sonarStatus) == 0 { - build, err := job.GetLastCompletedBuild() - if err != nil && utils.GetJenkinsStatusCode(err) != http.StatusNotFound { - klog.Errorf("%+v", err) - return nil, restful.NewError(utils.GetJenkinsStatusCode(err), err.Error()) - } else if err != nil { - klog.Errorf("%+v", err) - return nil, nil - } - sonarStatus, err = getBuildSonarResults(build) - if err != nil { - klog.Errorf("%+v", err) - return nil, restful.NewError(http.StatusBadRequest, err.Error()) - } - } - return sonarStatus, nil +func (o *projectPipelineOperator) GetProjectPipelineConfig(projectId, pipelineId string) (*devops.ProjectPipeline, error) { + return o.pipelineOperator.GetProjectPipelineConfig(projectId, pipelineId) } diff --git a/pkg/models/devops/project_pipeline_sonar_handler.go b/pkg/models/devops/project_pipeline_sonar_handler.go new file mode 100644 index 00000000..474729f9 --- /dev/null +++ b/pkg/models/devops/project_pipeline_sonar_handler.go @@ -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 +} diff --git a/pkg/models/devops/s2ibinary_handler.go b/pkg/models/devops/s2ibinary_handler.go index 71c3c060..782ec380 100644 --- a/pkg/models/devops/s2ibinary_handler.go +++ b/pkg/models/devops/s2ibinary_handler.go @@ -3,6 +3,8 @@ package devops import ( "code.cloudfoundry.org/bytefmt" "fmt" + "github.com/aws/aws-sdk-go/aws/awserr" + awsS3 "github.com/aws/aws-sdk-go/service/s3" "github.com/emicklei/go-restful" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/client-go/util/retry" @@ -10,7 +12,6 @@ import ( "kubesphere.io/kubesphere/pkg/apis/devops/v1alpha1" "kubesphere.io/kubesphere/pkg/client/clientset/versioned" "kubesphere.io/kubesphere/pkg/client/informers/externalversions" - "kubesphere.io/kubesphere/pkg/simple/client" "kubesphere.io/kubesphere/pkg/simple/client/s3" "mime/multipart" "net/http" @@ -22,9 +23,9 @@ const ( ) type S2iBinaryUploader interface { - UploadBinary(name, namespace, md5 string, header *multipart.FileHeader) (*v1alpha1.S2iBinary, error) + UploadS2iBinary(namespace, name, md5 string, header *multipart.FileHeader) (*v1alpha1.S2iBinary, error) - DownloadBinary(name, namespace, fileName string) (string, error) + DownloadS2iBinary(namespace, name, fileName string) (string, error) } type s2iBinaryUploader struct { @@ -33,6 +34,14 @@ type s2iBinaryUploader struct { s3Client s3.Interface } +func NewS2iBinaryUploader(client versioned.Interface, informers externalversions.SharedInformerFactory, s3Client s3.Interface) S2iBinaryUploader { + return &s2iBinaryUploader{ + client: client, + informers: informers, + s3Client: s3Client, + } +} + func (s *s2iBinaryUploader) UploadS2iBinary(namespace, name, md5 string, fileHeader *multipart.FileHeader) (*v1alpha1.S2iBinary, error) { binFile, err := fileHeader.Open() if err != nil { @@ -75,58 +84,35 @@ func (s *s2iBinaryUploader) UploadS2iBinary(namespace, name, md5 string, fileHea copy.Spec.FileName = fileHeader.Filename copy.Spec.DownloadURL = fmt.Sprintf(GetS2iBinaryURL, namespace, name, copy.Spec.FileName) - /* - TODO: upload binary file use s3.Interface - s3session := s3Client.Session() - if s3session == nil { - err := fmt.Errorf("could not connect to s2i s3") - klog.Error(err) - _, serr := SetS2iBinaryStatusWithRetry(copy, origin.Status.Phase) - if serr != nil { - klog.Error(serr) + err = s.s3Client.Upload(fmt.Sprintf("%s-%s", namespace, name), copy.Spec.FileName, binFile) + if err != nil { + if aerr, ok := err.(awserr.Error); ok { + switch aerr.Code() { + case awsS3.ErrCodeNoSuchBucket: + klog.Error(err) + _, serr := s.SetS2iBinaryStatusWithRetry(copy, origin.Status.Phase) + if serr != nil { + klog.Error(serr) + } return nil, err - } - return nil, err - } - uploader := s3manager.NewUploader(s3session, func(uploader *s3manager.Uploader) { - uploader.PartSize = 5 * bytefmt.MEGABYTE - uploader.LeavePartsOnError = true - }) - _, err = uploader.Upload(&s3manager.UploadInput{ - Bucket: s3Client.Bucket(), - Key: aws.String(fmt.Sprintf("%s-%s", namespace, name)), - Body: binFile, - ContentDisposition: aws.String(fmt.Sprintf("attachment; filename=\"%s\"", copy.Spec.FileName)), - }) - - 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 + default: + klog.Error(err) + _, serr := s.SetS2iBinaryStatusWithRetry(copy, v1alpha1.StatusUploadFailed) + if serr != nil { + klog.Error(serr) } + return nil, err } - klog.Error(err) - return nil, err } - */ + klog.Error(err) + return nil, err + } + if copy.Spec.UploadTimeStamp == nil { copy.Spec.UploadTimeStamp = new(metav1.Time) } *copy.Spec.UploadTimeStamp = metav1.Now() - copy, err = client.ClientSets().K8s().KubeSphere().DevopsV1alpha1().S2iBinaries(namespace).Update(copy) + copy, err = s.client.DevopsV1alpha1().S2iBinaries(namespace).Update(copy) if err != nil { klog.Error(err) return nil, err @@ -159,22 +145,7 @@ func (s *s2iBinaryUploader) DownloadS2iBinary(namespace, name, fileName string) klog.Error(err) return "", err } - - /* - TODO: get download url of requested binary - req, _ := s.s3Client.Client().GetObjectRequest(&s3.GetObjectInput{ - Bucket: s3Client.Bucket(), - Key: aws.String(fmt.Sprintf("%s-%s", namespace, name)), - ResponseContentDisposition: aws.String(fmt.Sprintf("attachment; filename=\"%s\"", origin.Spec.FileName)), - }) - url, err := req.Presign(5 * time.Minute) - if err != nil { - klog.Error(err) - return "", err - } - */ - return "", nil - + return s.s3Client.GetDownloadURL(fmt.Sprintf("%s-%s", namespace, name), fileName) } func (s *s2iBinaryUploader) SetS2iBinaryStatus(s2ibin *v1alpha1.S2iBinary, status string) (*v1alpha1.S2iBinary, error) { @@ -193,7 +164,7 @@ func (s *s2iBinaryUploader) SetS2iBinaryStatusWithRetry(s2ibin *v1alpha1.S2iBina var bin *v1alpha1.S2iBinary var err error err = retry.RetryOnConflict(retry.DefaultRetry, func() error { - bin, err = s.informers.Devops().V1alpha1().S2iBinaries().Lister().S2iBinaries(s2ibin.Namespace).Get(s2ibin.Name) + bin, err = s.client.DevopsV1alpha1().S2iBinaries(s2ibin.Namespace).Get(s2ibin.Name, metav1.GetOptions{}) if err != nil { klog.Error(err) return err diff --git a/pkg/models/devops/s2ibinary_handler_test.go b/pkg/models/devops/s2ibinary_handler_test.go new file mode 100644 index 00000000..f056cc93 --- /dev/null +++ b/pkg/models/devops/s2ibinary_handler_test.go @@ -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{}, + } +} diff --git a/pkg/models/devops/urlconfig.go b/pkg/models/devops/urlconfig.go deleted file mode 100644 index 222cc07a..00000000 --- a/pkg/models/devops/urlconfig.go +++ /dev/null @@ -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" -) diff --git a/pkg/models/iam/im.go b/pkg/models/iam/im.go index 5aa35a6c..6706d9b1 100644 --- a/pkg/models/iam/im.go +++ b/pkg/models/iam/im.go @@ -566,9 +566,9 @@ func (im *imOperator) deleteUserInDevOps(username string) error { jenkinsClient := dp.Jenkins() - _, err = devopsDb.DeleteFrom(devops.DevOpsProjectMembershipTableName). + _, err = devopsDb.DeleteFrom(devops.ProjectMembershipTableName). Where(db.And( - db.Eq(devops.DevOpsProjectMembershipUsernameColumn, username), + db.Eq(devops.ProjectMembershipUsernameColumn, username), )).Exec() if err != nil { klog.Errorf("%+v", err) diff --git a/pkg/models/resources/v1alpha3/interface.go b/pkg/models/resources/v1alpha3/interface.go index 93cef31a..871a50cd 100644 --- a/pkg/models/resources/v1alpha3/interface.go +++ b/pkg/models/resources/v1alpha3/interface.go @@ -64,4 +64,4 @@ func objectsToInterfaces(objs []runtime.Object) []interface{} { res = append(res, obj) } return res -} \ No newline at end of file +} diff --git a/pkg/models/resources/v1alpha3/namespace/namespaces.go b/pkg/models/resources/v1alpha3/namespace/namespaces.go index f41f2a75..d3b4821e 100644 --- a/pkg/models/resources/v1alpha3/namespace/namespaces.go +++ b/pkg/models/resources/v1alpha3/namespace/namespaces.go @@ -1,75 +1,75 @@ package namespace import ( - v1 "k8s.io/api/core/v1" - "k8s.io/apimachinery/pkg/labels" - "k8s.io/apimachinery/pkg/runtime" - "k8s.io/client-go/informers" - "kubesphere.io/kubesphere/pkg/api" - "kubesphere.io/kubesphere/pkg/apiserver/query" - "kubesphere.io/kubesphere/pkg/models/resources/v1alpha3" - "strings" + v1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/labels" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/client-go/informers" + "kubesphere.io/kubesphere/pkg/api" + "kubesphere.io/kubesphere/pkg/apiserver/query" + "kubesphere.io/kubesphere/pkg/models/resources/v1alpha3" + "strings" ) type namespaceGetter struct { - informers informers.SharedInformerFactory + informers informers.SharedInformerFactory } func NewNamespaceGetter(informers informers.SharedInformerFactory) v1alpha3.Interface { - return &namespaceGetter{informers:informers} + return &namespaceGetter{informers: informers} } 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) { - ns, err := n.informers.Core().V1().Namespaces().Lister().List(labels.Everything()) - if err != nil { - return nil, err - } + ns, err := n.informers.Core().V1().Namespaces().Lister().List(labels.Everything()) + if err != nil { + return nil, err + } - var result []runtime.Object - for _, item := range ns { - result = append(result, item) - } + var result []runtime.Object + for _, item := range ns { + 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 { - namespace, ok := item.(*v1.Namespace) - if !ok { - return false - } + namespace, ok := item.(*v1.Namespace) + if !ok { + return false + } - switch filter.Field { - case query.FieldName: - return query.ComparableString(namespace.Name).Contains(filter.Value) - case query.FieldStatus: - return query.ComparableString(namespace.Status.Phase).Compare(filter.Value) == 0 - default: - return false - } + switch filter.Field { + case query.FieldName: + return query.ComparableString(namespace.Name).Contains(filter.Value) + case query.FieldStatus: + return query.ComparableString(namespace.Status.Phase).Compare(filter.Value) == 0 + default: + return false + } } func (n namespaceGetter) compare(left runtime.Object, right runtime.Object, field query.Field) bool { - leftNs, ok := left.(*v1.Namespace) - if !ok { - return false - } + leftNs, ok := left.(*v1.Namespace) + if !ok { + return false + } - rightNs, ok := right.(*v1.Namespace) - if !ok { - return true - } + rightNs, ok := right.(*v1.Namespace) + if !ok { + return true + } - switch field { - case query.FieldName: - return strings.Compare(leftNs.Name, rightNs.Name) > 0 - case query.FieldCreationTimeStamp: - return leftNs.CreationTimestamp.After(rightNs.CreationTimestamp.Time) - default: - return false - } -} \ No newline at end of file + switch field { + case query.FieldName: + return strings.Compare(leftNs.Name, rightNs.Name) > 0 + case query.FieldCreationTimeStamp: + return leftNs.CreationTimestamp.After(rightNs.CreationTimestamp.Time) + default: + return false + } +} diff --git a/pkg/models/tenant/devops.go b/pkg/models/tenant/devops.go index f9e36c01..e9068f60 100644 --- a/pkg/models/tenant/devops.go +++ b/pkg/models/tenant/devops.go @@ -24,35 +24,40 @@ import ( "k8s.io/klog" "kubesphere.io/kubesphere/pkg/api/devops/v1alpha2" "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/devops" "kubesphere.io/kubesphere/pkg/models/iam" "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" - "sync" ) -type DevOpsProjectRoleResponse struct { - ProjectRole *gojenkins.ProjectRole - Err error +type DevOpsProjectOperator interface { + ListDevOpsProjects(workspace, username string, conditions *params.Conditions, orderBy string, reverse bool, limit int, offset int) (*models.PageableResponse, 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() - if err != nil { - if _, ok := err.(cs.ClientSetNotEnabledError); ok { - klog.V(4).Info("devops client is not enable") - return nil, err - } - klog.Error(err) - return nil, err +func newProjectOperator(operator devops.ProjectOperator, db *mysql.Database, client dsClient.ProjectOperator) DevOpsProjectOperator { + return &devopsProjectOperator{ + ksProjectOperator: operator, + db: db, + dsProject: client, } +} + +func (o *devopsProjectOperator) ListDevOpsProjects(workspace, username string, conditions *params.Conditions, orderBy string, reverse bool, limit int, offset int) (*models.PageableResponse, error) { - query := dbconn.Select(devops.GetColumnsFromStructWithPrefix(devops.DevOpsProjectTableName, v1alpha2.DevOpsProject{})...). + query := o.db.Select(devops.GetColumnsFromStructWithPrefix(devops.DevOpsProjectTableName, v1alpha2.DevOpsProject{})...). From(devops.DevOpsProjectTableName) var sqconditions []dbr.Builder @@ -61,11 +66,11 @@ func ListDevopsProjects(workspace, username string, conditions *params.Condition switch username { case devops.KS_ADMIN: default: - onCondition := fmt.Sprintf("%s = %s", devops.DevOpsProjectMembershipProjectIdColumn, devops.DevOpsProjectIdColumn) - query.Join(devops.DevOpsProjectMembershipTableName, onCondition) - sqconditions = append(sqconditions, db.Eq(devops.DevOpsProjectMembershipUsernameColumn, username)) + onCondition := fmt.Sprintf("%s = %s", devops.ProjectMembershipProjectIdColumn, devops.DevOpsProjectIdColumn) + query.Join(devops.ProjectMembershipTableName, onCondition) + sqconditions = append(sqconditions, db.Eq(devops.ProjectMembershipUsernameColumn, username)) sqconditions = append(sqconditions, db.Eq( - devops.DevOpsProjectMembershipTableName+"."+devops.StatusColumn, devops.StatusActive)) + devops.ProjectMembershipTableName+"."+devops.StatusColumn, devops.StatusActive)) } sqconditions = append(sqconditions, db.Eq( @@ -95,7 +100,7 @@ func ListDevopsProjects(workspace, username string, conditions *params.Condition } query.Limit(uint64(limit)) query.Offset(uint64(offset)) - _, err = query.Load(&projects) + _, err := query.Load(&projects) if err != nil { klog.Errorf("%+v", err) 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 } -func GetDevOpsProjectsCount(username string) (uint32, error) { - dbconn, err := cs.ClientSets().MySQL() - if err != nil { - klog.Error(err) - return 0, err - } +func (o *devopsProjectOperator) GetDevOpsProjectsCount(username string) (uint32, error) { - query := dbconn.Select(devops.GetColumnsFromStructWithPrefix(devops.DevOpsProjectTableName, v1alpha2.DevOpsProject{})...). + query := o.db.Select(devops.GetColumnsFromStructWithPrefix(devops.DevOpsProjectTableName, v1alpha2.DevOpsProject{})...). From(devops.DevOpsProjectTableName) var sqconditions []dbr.Builder if username != devops.KS_ADMIN { - onCondition := fmt.Sprintf("%s = %s", devops.DevOpsProjectMembershipProjectIdColumn, devops.DevOpsProjectIdColumn) - query.Join(devops.DevOpsProjectMembershipTableName, onCondition) - sqconditions = append(sqconditions, db.Eq(devops.DevOpsProjectMembershipUsernameColumn, username)) + onCondition := fmt.Sprintf("%s = %s", devops.ProjectMembershipProjectIdColumn, devops.DevOpsProjectIdColumn) + query.Join(devops.ProjectMembershipTableName, onCondition) + sqconditions = append(sqconditions, db.Eq(devops.ProjectMembershipUsernameColumn, username)) sqconditions = append(sqconditions, db.Eq( - devops.DevOpsProjectMembershipTableName+"."+devops.StatusColumn, devops.StatusActive)) + devops.ProjectMembershipTableName+"."+devops.StatusColumn, devops.StatusActive)) } sqconditions = append(sqconditions, db.Eq( @@ -146,171 +146,61 @@ func GetDevOpsProjectsCount(username string) (uint32, error) { return count, nil } -func DeleteDevOpsProject(projectId, username string) error { - err := devops.CheckProjectUserInRole(username, projectId, []string{devops.ProjectOwner}) +func (o *devopsProjectOperator) DeleteDevOpsProject(projectId, username string) error { + err := o.ksProjectOperator.CheckProjectUserInRole(username, projectId, []string{dsClient.ProjectOwner}) if err != nil { klog.Errorf("%+v", err) return restful.NewError(http.StatusForbidden, err.Error()) } - dp, err := cs.ClientSets().Devops() - 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...) + err = o.dsProject.DeleteDevOpsProject(projectId) if err != nil { klog.Errorf("%+v", err) - return restful.NewError(utils.GetJenkinsStatusCode(err), err.Error()) + return err } - _, err = devopsdb.DeleteFrom(devops.DevOpsProjectMembershipTableName). - Where(db.Eq(devops.DevOpsProjectMembershipProjectIdColumn, projectId)).Exec() + _, err = o.db.DeleteFrom(devops.ProjectMembershipTableName). + Where(db.Eq(devops.ProjectMembershipProjectIdColumn, projectId)).Exec() if err != nil { 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). Where(db.Eq(devops.DevOpsProjectIdColumn, projectId)).Exec() if err != nil { klog.Errorf("%+v", err) - return restful.NewError(utils.GetJenkinsStatusCode(err), err.Error()) + return err } project := &v1alpha2.DevOpsProject{} - err = devopsdb.Select(devops.DevOpsProjectColumns...). + err = o.db.Select(devops.DevOpsProjectColumns...). From(devops.DevOpsProjectTableName). Where(db.Eq(devops.DevOpsProjectIdColumn, projectId)). LoadOne(project) if err != nil { klog.Errorf("%+v", err) - return restful.NewError(utils.GetJenkinsStatusCode(err), err.Error()) + return err } return nil } -func 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()) - } +func (o *devopsProjectOperator) CreateDevOpsProject(username string, workspace string, req *v1alpha2.DevOpsProject) (*v1alpha2.DevOpsProject, error) { 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 { - klog.Errorf("%+v", err) - return nil, restful.NewError(utils.GetJenkinsStatusCode(err), err.Error()) - } - - 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()) + klog.Error(err) + return nil, err } - _, err = devopsdb.InsertInto(devops.DevOpsProjectTableName). + _, err = o.db.InsertInto(devops.DevOpsProjectTableName). Columns(devops.DevOpsProjectColumns...).Record(project).Exec() if err != nil { klog.Errorf("%+v", err) return nil, restful.NewError(http.StatusInternalServerError, err.Error()) } - projectMembership := devops.NewDevOpsProjectMemberShip(username, project.ProjectId, devops.ProjectOwner, username) - _, err = devopsdb.InsertInto(devops.DevOpsProjectMembershipTableName). - Columns(devops.DevOpsProjectMembershipColumns...).Record(projectMembership).Exec() + projectMembership := devops.NewDevOpsProjectMemberShip(username, project.ProjectId, dsClient.ProjectOwner, username) + _, err = o.db.InsertInto(devops.ProjectMembershipTableName). + Columns(devops.ProjectMembershipColumns...).Record(projectMembership).Exec() if err != nil { klog.Errorf("%+v", err) return nil, restful.NewError(http.StatusInternalServerError, err.Error()) @@ -318,8 +208,9 @@ func CreateDevopsProject(username string, workspace string, req *v1alpha2.DevOps return project, nil } -func GetUserDevopsSimpleRules(username, projectId string) ([]iam.SimpleRule, error) { - role, err := devops.GetProjectUserRole(username, projectId) +func (o *devopsProjectOperator) GetUserDevOpsSimpleRules(username, projectId string) ([]iam.SimpleRule, error) { + + role, err := o.getProjectUserRole(username, projectId) if err != nil { klog.Errorf("%+v", err) return nil, restful.NewError(http.StatusForbidden, err.Error()) @@ -327,6 +218,24 @@ func GetUserDevopsSimpleRules(username, projectId string) ([]iam.SimpleRule, err 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 { var rules []iam.SimpleRule diff --git a/pkg/models/tenant/tenant.go b/pkg/models/tenant/tenant.go index a68b72c3..14e13bf9 100644 --- a/pkg/models/tenant/tenant.go +++ b/pkg/models/tenant/tenant.go @@ -36,11 +36,14 @@ type Interface interface { DescribeWorkspace(username, workspace string) (*v1alpha1.Workspace, 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) + GetWorkspace(workspace string) (*v1alpha1.Workspace, error) + DevOpsProjectOperator } type tenantOperator struct { workspaces WorkspaceInterface namespaces NamespaceInterface + DevOpsProjectOperator } func (t *tenantOperator) DeleteNamespace(workspace, namespace string) error { @@ -101,7 +104,7 @@ func (t *tenantOperator) appendAnnotations(username string, workspace *v1alpha1. if err == nil { workspace.Annotations["kubesphere.io/namespace-count"] = strconv.Itoa(ns.TotalCount) } - devops, err := ListDevopsProjects(workspace.Name, username, ¶ms.Conditions{}, "", false, 1, 0) + devops, err := t.ListDevOpsProjects(workspace.Name, username, ¶ms.Conditions{}, "", false, 1, 0) if err == nil { 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 } + +func (t *tenantOperator) GetWorkspace(workspace string) (*v1alpha1.Workspace, error) { + return t.workspaces.GetWorkspace(workspace) +} diff --git a/pkg/models/terminal/terminal.go b/pkg/models/terminal/terminal.go index c4f88ecf..45943252 100644 --- a/pkg/models/terminal/terminal.go +++ b/pkg/models/terminal/terminal.go @@ -145,12 +145,12 @@ type terminaler struct { } 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 // 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(). Resource("pods"). Name(podName). @@ -194,7 +194,7 @@ func isValidShell(validShells []string, shell string) bool { 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 validShells := []string{"sh", "bash"} diff --git a/pkg/server/config/config.go b/pkg/server/config/config.go index 4dc1f6a1..0db2c2c3 100644 --- a/pkg/server/config/config.go +++ b/pkg/server/config/config.go @@ -9,7 +9,7 @@ import ( "kubesphere.io/kubesphere/pkg/apiserver/runtime" "kubesphere.io/kubesphere/pkg/simple/client/alerting" "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/k8s" "kubesphere.io/kubesphere/pkg/simple/client/kubesphere" @@ -159,7 +159,7 @@ var ( type Config struct { 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"` KubernetesOptions *k8s.KubernetesOptions `json:"kubernetes,omitempty" yaml:"kubernetes,omitempty" mapstructure:"kubernetes"` ServiceMeshOptions *servicemesh.Options `json:"servicemesh,omitempty" yaml:"servicemesh,omitempty" mapstructure:"servicemesh"` @@ -182,7 +182,7 @@ type Config struct { func newConfig() *Config { return &Config{ MySQLOptions: mysql.NewMySQLOptions(), - DevopsOptions: devops.NewDevopsOptions(), + DevopsOptions: jenkins.NewDevopsOptions(), SonarQubeOptions: sonarqube.NewSonarQubeOptions(), KubernetesOptions: k8s.NewKubernetesOptions(), ServiceMeshOptions: servicemesh.NewServiceMeshOptions(), diff --git a/pkg/server/config/config_test.go b/pkg/server/config/config_test.go index af73477b..8f06b65a 100644 --- a/pkg/server/config/config_test.go +++ b/pkg/server/config/config_test.go @@ -6,7 +6,7 @@ import ( "io/ioutil" "kubesphere.io/kubesphere/pkg/simple/client/alerting" "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/k8s" "kubesphere.io/kubesphere/pkg/simple/client/kubesphere" @@ -34,7 +34,7 @@ func newTestConfig() *Config { MaxOpenConnections: 20, MaxConnectionLifeTime: time.Duration(10) * time.Second, }, - DevopsOptions: &devops.Options{ + DevopsOptions: &jenkins.Options{ Host: "http://ks-devops.kubesphere-devops-system.svc", Username: "jenkins", Password: "kubesphere", diff --git a/pkg/server/errors/errors.go b/pkg/server/errors/errors.go index 80daea63..b89f315b 100644 --- a/pkg/server/errors/errors.go +++ b/pkg/server/errors/errors.go @@ -47,3 +47,11 @@ func ParseSvcErr(err error, resp *restful.Response) { 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 + } +} diff --git a/pkg/simple/client/cache/cache.go b/pkg/simple/client/cache/cache.go index d71f15b9..658b17d3 100644 --- a/pkg/simple/client/cache/cache.go +++ b/pkg/simple/client/cache/cache.go @@ -20,4 +20,4 @@ type Interface interface { // Expires updates object's expiration time, return err if key doesn't exist Expire(key string, duration time.Duration) error -} \ No newline at end of file +} diff --git a/pkg/simple/client/cache/simple_cache.go b/pkg/simple/client/cache/simple_cache.go index 67049a60..349d416b 100644 --- a/pkg/simple/client/cache/simple_cache.go +++ b/pkg/simple/client/cache/simple_cache.go @@ -3,38 +3,38 @@ package cache import "time" type simpleObject struct { - value string - expire time.Time + value string + expire time.Time } type SimpleCache struct { - store map[string]simpleObject + store map[string]simpleObject } func NewSimpleCache() Interface { - return &SimpleCache{store: make(map[string]simpleObject)} + return &SimpleCache{store: make(map[string]simpleObject)} } func (s *SimpleCache) Keys(pattern string) ([]string, error) { - panic("implement me") + panic("implement me") } func (s *SimpleCache) Set(key string, value string, duration time.Duration) error { - panic("implement me") + panic("implement me") } func (s *SimpleCache) Del(key string) error { - panic("implement me") + panic("implement me") } func (s *SimpleCache) Get(key string) (string, error) { - return "", nil + return "", nil } func (s *SimpleCache) Exists(key string) (bool, error) { - panic("implement me") + panic("implement me") } func (s *SimpleCache) Expire(key string, duration time.Duration) error { - panic("implement me") + panic("implement me") } diff --git a/pkg/gojenkins/OWNERS b/pkg/simple/client/devops/OWNERS similarity index 100% rename from pkg/gojenkins/OWNERS rename to pkg/simple/client/devops/OWNERS diff --git a/pkg/simple/client/devops/build.go b/pkg/simple/client/devops/build.go new file mode 100644 index 00000000..d1e3f0ad --- /dev/null +++ b/pkg/simple/client/devops/build.go @@ -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) +} diff --git a/pkg/simple/client/devops/credential.go b/pkg/simple/client/devops/credential.go new file mode 100644 index 00000000..6a932b70 --- /dev/null +++ b/pkg/simple/client/devops/credential.go @@ -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) +} diff --git a/pkg/simple/client/devops/fake/fakedevops.go b/pkg/simple/client/devops/fake/fakedevops.go new file mode 100644 index 00000000..f35b6622 --- /dev/null +++ b/pkg/simple/client/devops/fake/fakedevops.go @@ -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 +} diff --git a/pkg/simple/client/devops/interface.go b/pkg/simple/client/devops/interface.go index 07ec338e..80b5ec6c 100644 --- a/pkg/simple/client/devops/interface.go +++ b/pkg/simple/client/devops/interface.go @@ -1,19 +1,13 @@ package devops -type Job struct { - -} - type Interface interface { - GetJob(projectId, pipelineName string) (*Job, error) - - DeleteJob(projectId, pipelineId string) (bool, error) + CredentialOperator - CreateJobInFolder() + BuildGetter - GetGlobalRole(roleName string) + PipelineOperator - AddGlobalRole(roleName string, permission string) + ProjectMemberOperator - GetProjectRole(roleName string) + ProjectPipelineOperator } diff --git a/pkg/gojenkins/README.md b/pkg/simple/client/devops/jenkins/README.md similarity index 100% rename from pkg/gojenkins/README.md rename to pkg/simple/client/devops/jenkins/README.md diff --git a/pkg/gojenkins/build.go b/pkg/simple/client/devops/jenkins/build.go similarity index 74% rename from pkg/gojenkins/build.go rename to pkg/simple/client/devops/jenkins/build.go index 0d8af0a5..43f81a19 100644 --- a/pkg/gojenkins/build.go +++ b/pkg/simple/client/devops/jenkins/build.go @@ -12,14 +12,15 @@ // License for the specific language governing permissions and limitations // under the License. -package gojenkins +package jenkins import ( "bytes" "errors" + "github.com/emicklei/go-restful" + "kubesphere.io/kubesphere/pkg/simple/client/devops" "net/http" "net/url" - "regexp" "strconv" "time" ) @@ -31,26 +32,26 @@ const ( ) type Build struct { - Raw *BuildResponse + Raw *devops.Build Job *Job Jenkins *Jenkins Base string Depth int } -type parameter struct { +type Parameter struct { Name string Value string } -type branch struct { +type Branch struct { SHA1 string `json:",omitempty"` Name string `json:",omitempty"` } type BuildRevision struct { SHA1 string `json:"SHA1,omitempty"` - Branch []branch `json:"branch,omitempty"` + Branch []Branch `json:"Branch,omitempty"` } type Builds struct { @@ -66,7 +67,7 @@ type Culprit struct { } type GeneralObj struct { - Parameters []parameter `json:"parameters,omitempty"` + Parameters []Parameter `json:"parameters,omitempty"` Causes []map[string]interface{} `json:"causes,omitempty"` BuildsByBranchName map[string]Builds `json:"buildsByBranchName,omitempty"` LastBuiltRevision *BuildRevision `json:"lastBuiltRevision,omitempty"` @@ -114,7 +115,7 @@ type TestResult struct { } type BuildResponse struct { - Actions []GeneralObj + Actions []devops.GeneralAction Artifacts []struct { DisplayPath string `json:"displayPath"` FileName string `json:"fileName"` @@ -146,22 +147,21 @@ type BuildResponse struct { 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"` - MavenArtifacts interface{} `json:"mavenArtifacts"` - MavenVersionUsed string `json:"mavenVersionUsed"` - FingerPrint []FingerPrintResponse + Culprits []devops.Culprit `json:"culprits"` + Description interface{} `json:"description"` + Duration int64 `json:"duration"` + EstimatedDuration int64 `json:"estimatedDuration"` + Executor interface{} `json:"executor"` + FullDisplayName string `json:"fullDisplayName"` + ID string `json:"id"` + KeepLog bool `json:"keepLog"` + Number int64 `json:"number"` + QueueID int64 `json:"queueId"` + Result string `json:"result"` + Timestamp int64 `json:"timestamp"` + URL string `json:"url"` + MavenArtifacts interface{} `json:"mavenArtifacts"` + MavenVersionUsed string `json:"mavenVersionUsed"` Runs []struct { Number int64 URL string @@ -169,14 +169,10 @@ type BuildResponse struct { } // Builds -func (b *Build) Info() *BuildResponse { +func (b *Build) Info() *devops.Build { return b.Raw } -func (b *Build) GetActions() []GeneralObj { - return b.Raw.Actions -} - func (b *Build) GetUrl() string { return b.Raw.URL } @@ -188,23 +184,6 @@ func (b *Build) GetResult() string { 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) { if b.IsRunning() { 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") } -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) { var envVars struct { EnvMap map[string]string `json:"envMap"` @@ -286,31 +256,6 @@ func (b *Build) GetDownstreamBuilds() ([]*Build, error) { 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) { causes, err := b.GetCauses() if err != nil { @@ -356,22 +301,6 @@ func (b *Build) GetUpstreamBuild() (*Build, error) { 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) { url := b.Base + "/testReport" @@ -395,24 +324,6 @@ func (b *Build) GetDuration() int64 { 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 { vcs := b.Raw.ChangeSet.Kind if vcs == Git { @@ -456,7 +367,7 @@ func (b *Build) PauseToggle() error { 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 func (b *Build) Poll(options ...interface{}) (int, error) { depth := "-1" @@ -484,3 +395,20 @@ func (b *Build) Poll(options ...interface{}) (int, error) { } 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 +} diff --git a/pkg/gojenkins/constants.go b/pkg/simple/client/devops/jenkins/constants.go similarity index 96% rename from pkg/gojenkins/constants.go rename to pkg/simple/client/devops/jenkins/constants.go index 1876fb95..eee426b0 100644 --- a/pkg/gojenkins/constants.go +++ b/pkg/simple/client/devops/jenkins/constants.go @@ -1,4 +1,4 @@ -package gojenkins +package jenkins const ( STATUS_FAIL = "FAIL" diff --git a/pkg/simple/client/devops/jenkins/credential.go b/pkg/simple/client/devops/jenkins/credential.go new file mode 100644 index 00000000..e5dff570 --- /dev/null +++ b/pkg/simple/client/devops/jenkins/credential.go @@ -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 +} diff --git a/pkg/simple/client/devops/devops.go b/pkg/simple/client/devops/jenkins/devops.go similarity index 81% rename from pkg/simple/client/devops/devops.go rename to pkg/simple/client/devops/jenkins/devops.go index 90c03ae6..6c439110 100644 --- a/pkg/simple/client/devops/devops.go +++ b/pkg/simple/client/devops/jenkins/devops.go @@ -11,12 +11,11 @@ See the License for the specific language governing permissions and limitations under the License. */ -package devops +package jenkins import ( "fmt" "k8s.io/klog" - "kubesphere.io/kubesphere/pkg/gojenkins" "sync" ) @@ -25,13 +24,13 @@ const ( ) type Client struct { - jenkinsClient *gojenkins.Jenkins + jenkinsClient *Jenkins } func NewDevopsClient(options *Options) (*Client, error) { var d Client - jenkins := gojenkins.CreateJenkins(nil, options.Host, options.MaxConnections, options.Username, options.Password) + jenkins := CreateJenkins(nil, options.Host, options.MaxConnections, options.Username, options.Password) jenkins, err := jenkins.Init() if err != nil { klog.Errorf("failed to connecto to jenkins role, %+v", err) @@ -49,7 +48,7 @@ func NewDevopsClient(options *Options) (*Client, error) { return &d, nil } -func (c *Client) Jenkins() *gojenkins.Jenkins { +func (c *Client) Jenkins() *Jenkins { return c.jenkinsClient } @@ -71,14 +70,14 @@ func (c *Client) initializeJenkins() error { // Jenkins uninitialized, create global role if globalRole == nil { - _, err := c.jenkinsClient.AddGlobalRole(jenkinsAllUserRoleName, gojenkins.GlobalPermissionIds{GlobalRead: true}, true) + _, err := c.jenkinsClient.AddGlobalRole(jenkinsAllUserRoleName, GlobalPermissionIds{GlobalRead: true}, true) if err != nil { klog.Error(err) return err } } - _, err = c.jenkinsClient.AddProjectRole(jenkinsAllUserRoleName, "\\n\\s*\\r", gojenkins.ProjectPermissionIds{SCMTag: true}, true) + _, err = c.jenkinsClient.AddProjectRole(jenkinsAllUserRoleName, "\\n\\s*\\r", ProjectPermissionIds{SCMTag: true}, true) if err != nil { klog.Error(err) return err diff --git a/pkg/simple/client/devops/jenkins/devops_test.go b/pkg/simple/client/devops/jenkins/devops_test.go new file mode 100644 index 00000000..bd11e5d6 --- /dev/null +++ b/pkg/simple/client/devops/jenkins/devops_test.go @@ -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) + } + + } +} diff --git a/pkg/gojenkins/folder.go b/pkg/simple/client/devops/jenkins/folder.go similarity index 94% rename from pkg/gojenkins/folder.go rename to pkg/simple/client/devops/jenkins/folder.go index 053a8ef3..1646f6c8 100644 --- a/pkg/gojenkins/folder.go +++ b/pkg/simple/client/devops/jenkins/folder.go @@ -12,7 +12,7 @@ // License for the specific language governing permissions and limitations // under the License. -package gojenkins +package jenkins import ( "errors" @@ -33,8 +33,6 @@ type FolderResponse struct { Name string `json:"name"` URL string `json:"url"` Jobs []InnerJob `json:"jobs"` - PrimaryView *ViewData `json:"primaryView"` - Views []ViewData `json:"views"` } func (f *Folder) parentBase() string { diff --git a/pkg/simple/client/devops/jenkins/jenkins.go b/pkg/simple/client/devops/jenkins/jenkins.go new file mode 100644 index 00000000..c8ca746f --- /dev/null +++ b/pkg/simple/client/devops/jenkins/jenkins.go @@ -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("", "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("","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 +} diff --git a/pkg/gojenkins/job.go b/pkg/simple/client/devops/jenkins/job.go similarity index 93% rename from pkg/gojenkins/job.go rename to pkg/simple/client/devops/jenkins/job.go index c075a42e..413cd396 100644 --- a/pkg/gojenkins/job.go +++ b/pkg/simple/client/devops/jenkins/job.go @@ -12,13 +12,14 @@ // License for the specific language governing permissions and limitations // under the License. -package gojenkins +package jenkins import ( "bytes" "encoding/json" "errors" "fmt" + "kubesphere.io/kubesphere/pkg/simple/client/devops" "net/url" "path" "strconv" @@ -60,7 +61,7 @@ type ParameterDefinition struct { type JobResponse struct { Class string `json:"_class"` - Actions []GeneralObj + Actions []devops.GeneralAction Buildable bool `json:"buildable"` Builds []JobBuild Color string `json:"color"` @@ -96,8 +97,6 @@ type JobResponse struct { UpstreamProjects []InnerJob `json:"upstreamProjects"` URL string `json:"url"` Jobs []InnerJob `json:"jobs"` - PrimaryView *ViewData `json:"primaryView"` - Views []ViewData `json:"views"` } func (j *Job) parentBase() string { @@ -123,7 +122,7 @@ func (j *Job) GetDetails() *JobResponse { } 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() if err != nil { return nil, err @@ -153,7 +152,7 @@ func (j *Job) getBuildByType(buildType string) (*Build, error) { Jenkins: j.Jenkins, Depth: 1, Job: j, - Raw: new(BuildResponse), + Raw: new(devops.Build), Base: j.Base + "/" + number} status, err := build.Poll() if err != nil { @@ -212,10 +211,6 @@ func (j *Job) GetAllBuildStatus() ([]JobBuildStatus, error) { return buildsResp.Builds, nil } -func (j *Job) GetSubJobsMetadata() []InnerJob { - return j.Raw.SubJobs -} - func (j *Job) GetUpstreamJobsMetadata() []InnerJob { return j.Raw.UpstreamProjects } @@ -224,18 +219,6 @@ func (j *Job) GetDownstreamJobsMetadata() []InnerJob { 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 { return j.Raw.Jobs } @@ -519,11 +502,3 @@ func (j *Job) Poll() (int, error) { } 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 -} diff --git a/pkg/simple/client/devops/jenkins/member.go b/pkg/simple/client/devops/jenkins/member.go new file mode 100644 index 00000000..2d9cb47a --- /dev/null +++ b/pkg/simple/client/devops/jenkins/member.go @@ -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 +} diff --git a/pkg/simple/client/devops/options.go b/pkg/simple/client/devops/jenkins/options.go similarity index 99% rename from pkg/simple/client/devops/options.go rename to pkg/simple/client/devops/jenkins/options.go index e3b1bd09..70280a27 100644 --- a/pkg/simple/client/devops/options.go +++ b/pkg/simple/client/devops/jenkins/options.go @@ -1,4 +1,4 @@ -package devops +package jenkins import ( "fmt" diff --git a/pkg/simple/client/devops/jenkins/pipeline.go b/pkg/simple/client/devops/jenkins/pipeline.go new file mode 100644 index 00000000..08122f46 --- /dev/null +++ b/pkg/simple/client/devops/jenkins/pipeline.go @@ -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 +} diff --git a/pkg/models/devops/project_pipeline.go b/pkg/simple/client/devops/jenkins/pipeline_internal.go similarity index 59% rename from pkg/models/devops/project_pipeline.go rename to pkg/simple/client/devops/jenkins/pipeline_internal.go index 1cd5d2ee..cb84f4fc 100644 --- a/pkg/models/devops/project_pipeline.go +++ b/pkg/simple/client/devops/jenkins/pipeline_internal.go @@ -1,179 +1,14 @@ -/* -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 jenkins import ( "fmt" "github.com/beevik/etree" - "github.com/kubesphere/sonargo/sonar" - "k8s.io/klog" - "kubesphere.io/kubesphere/pkg/gojenkins" - "kubesphere.io/kubesphere/pkg/simple/client" + "kubesphere.io/kubesphere/pkg/simple/client/devops" "strconv" "strings" "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 { lines := strings.Split(config, "\n") lines[0] = strings.Replace(lines[0], oldVersion, targetVersion, -1) @@ -181,7 +16,7 @@ func replaceXmlVersion(config, oldVersion, targetVersion string) string { return output } -func createPipelineConfigXml(pipeline *NoScmPipeline) (string, error) { +func createPipelineConfigXml(pipeline *devops.NoScmPipeline) (string, error) { doc := etree.NewDocument() xmlString := ` @@ -215,7 +50,7 @@ func createPipelineConfigXml(pipeline *NoScmPipeline) (string, error) { strategy.CreateElement("artifactNumToKeep").SetText("-1") } if pipeline.Parameters != nil { - pipeline.Parameters.appendToEtree(properties) + appendParametersToEtree(properties, pipeline.Parameters) } if pipeline.TimerTrigger != nil { @@ -247,8 +82,8 @@ func createPipelineConfigXml(pipeline *NoScmPipeline) (string, error) { return replaceXmlVersion(stringXml, "1.0", "1.1"), err } -func parsePipelineConfigXml(config string) (*NoScmPipeline, error) { - pipeline := &NoScmPipeline{} +func parsePipelineConfigXml(config string) (*devops.NoScmPipeline, error) { + pipeline := &devops.NoScmPipeline{} config = replaceXmlVersion(config, "1.1", "1.0") doc := etree.NewDocument() err := doc.ReadFromString(config) @@ -271,13 +106,13 @@ func parsePipelineConfigXml(config string) (*NoScmPipeline, error) { strategy := properties. SelectElement("jenkins.model.BuildDiscarderProperty"). SelectElement("strategy") - pipeline.Discarder = &DiscarderProperty{ + pipeline.Discarder = &devops.DiscarderProperty{ DaysToKeep: strategy.SelectElement("daysToKeep").Text(), NumToKeep: strategy.SelectElement("numToKeep").Text(), } } - pipeline.Parameters = &Parameters{} - pipeline.Parameters = pipeline.Parameters.fromEtree(properties) + pipeline.Parameters = &devops.Parameters{} + pipeline.Parameters = getParametersfromEtree(properties) if len(*pipeline.Parameters) == 0 { pipeline.Parameters = nil } @@ -287,13 +122,13 @@ func parsePipelineConfigXml(config string) (*NoScmPipeline, error) { "org.jenkinsci.plugins.workflow.job.properties.PipelineTriggersJobProperty"); triggerProperty != nil { triggers := triggerProperty.SelectElement("triggers") if timerTrigger := triggers.SelectElement("hudson.triggers.TimerTrigger"); timerTrigger != nil { - pipeline.TimerTrigger = &TimerTrigger{ + pipeline.TimerTrigger = &devops.TimerTrigger{ Cron: timerTrigger.SelectElement("spec").Text(), } } } if authToken := flow.SelectElement("authToken"); authToken != nil { - pipeline.RemoteTrigger = &RemoteTrigger{ + pipeline.RemoteTrigger = &devops.RemoteTrigger{ Token: authToken.Text(), } } @@ -305,11 +140,11 @@ func parsePipelineConfigXml(config string) (*NoScmPipeline, error) { 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"). CreateElement("parameterDefinitions") - for _, parameter := range *s { - for className, typeName := range ParameterTypeMap { + for _, parameter := range *parameters { + for className, typeName := range devops.ParameterTypeMap { if typeName == parameter.Type { paramDefine := parameterDefinitions.CreateElement(className) 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 { params := parametersProperty.SelectElement("parameterDefinitions").ChildElements() - if *s == nil { - *s = make([]*Parameter, 0) - } for _, param := range params { switch param.Tag { case "hudson.model.StringParameterDefinition": - *s = append(*s, &Parameter{ + parameters = append(parameters, &devops.Parameter{ Name: param.SelectElement("name").Text(), Description: param.SelectElement("description").Text(), DefaultValue: param.SelectElement("defaultValue").Text(), - Type: ParameterTypeMap["hudson.model.StringParameterDefinition"], + Type: devops.ParameterTypeMap["hudson.model.StringParameterDefinition"], }) case "hudson.model.BooleanParameterDefinition": - *s = append(*s, &Parameter{ + parameters = append(parameters, &devops.Parameter{ Name: param.SelectElement("name").Text(), Description: param.SelectElement("description").Text(), DefaultValue: param.SelectElement("defaultValue").Text(), - Type: ParameterTypeMap["hudson.model.BooleanParameterDefinition"], + Type: devops.ParameterTypeMap["hudson.model.BooleanParameterDefinition"], }) case "hudson.model.TextParameterDefinition": - *s = append(*s, &Parameter{ + parameters = append(parameters, &devops.Parameter{ Name: param.SelectElement("name").Text(), Description: param.SelectElement("description").Text(), DefaultValue: param.SelectElement("defaultValue").Text(), - Type: ParameterTypeMap["hudson.model.TextParameterDefinition"], + Type: devops.ParameterTypeMap["hudson.model.TextParameterDefinition"], }) case "hudson.model.FileParameterDefinition": - *s = append(*s, &Parameter{ + parameters = append(parameters, &devops.Parameter{ Name: param.SelectElement("name").Text(), Description: param.SelectElement("description").Text(), - Type: ParameterTypeMap["hudson.model.FileParameterDefinition"], + Type: devops.ParameterTypeMap["hudson.model.FileParameterDefinition"], }) case "hudson.model.PasswordParameterDefinition": - *s = append(*s, &Parameter{ + parameters = append(parameters, &devops.Parameter{ Name: param.SelectElement("name").Text(), Description: param.SelectElement("description").Text(), DefaultValue: param.SelectElement("name").Text(), - Type: ParameterTypeMap["hudson.model.PasswordParameterDefinition"], + Type: devops.ParameterTypeMap["hudson.model.PasswordParameterDefinition"], }) case "hudson.model.ChoiceParameterDefinition": - choiceParameter := &Parameter{ + choiceParameter := &devops.Parameter{ Name: param.SelectElement("name").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") for _, choice := range choices { choiceParameter.DefaultValue += fmt.Sprintf("%s\n", choice.Text()) } choiceParameter.DefaultValue = strings.TrimSpace(choiceParameter.DefaultValue) - *s = append(*s, choiceParameter) + parameters = append(parameters, choiceParameter) default: - *s = append(*s, &Parameter{ + parameters = append(parameters, &devops.Parameter{ Name: param.SelectElement("name").Text(), Description: param.SelectElement("description").Text(), DefaultValue: "unknown", @@ -400,110 +231,112 @@ func (s *Parameters) fromEtree(properties *etree.Element) *Parameters { } } } - return s + return ¶meters } -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("plugin", "git") - source.CreateElement("id").SetText(s.ScmId) - source.CreateElement("remote").SetText(s.Url) - if s.CredentialId != "" { - source.CreateElement("credentialsId").SetText(s.CredentialId) + source.CreateElement("id").SetText(gitSource.ScmId) + source.CreateElement("remote").SetText(gitSource.Url) + if gitSource.CredentialId != "" { + source.CreateElement("credentialsId").SetText(gitSource.CredentialId) } traits := source.CreateElement("traits") - if s.DiscoverBranches { + if gitSource.DiscoverBranches { 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.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("honorRefspec").SetText(strconv.FormatBool(true)) cloneExtension.CreateElement("reference") - if s.CloneOption.Timeout >= 0 { - cloneExtension.CreateElement("timeout").SetText(strconv.Itoa(s.CloneOption.Timeout)) + if gitSource.CloneOption.Timeout >= 0 { + cloneExtension.CreateElement("timeout").SetText(strconv.Itoa(gitSource.CloneOption.Timeout)) } else { cloneExtension.CreateElement("timeout").SetText(strconv.Itoa(10)) } - if s.CloneOption.Depth >= 0 { - cloneExtension.CreateElement("depth").SetText(strconv.Itoa(s.CloneOption.Depth)) + if gitSource.CloneOption.Depth >= 0 { + cloneExtension.CreateElement("depth").SetText(strconv.Itoa(gitSource.CloneOption.Depth)) } else { cloneExtension.CreateElement("depth").SetText(strconv.Itoa(1)) } } - if s.RegexFilter != "" { + if gitSource.RegexFilter != "" { regexTraits := traits.CreateElement("jenkins.scm.impl.trait.RegexSCMHeadFilterTrait") 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 { - s.CredentialId = credential.Text() + gitSource.CredentialId = credential.Text() } if remote := source.SelectElement("remote"); remote != nil { - s.Url = remote.Text() + gitSource.Url = remote.Text() } traits := source.SelectElement("traits") if branchDiscoverTrait := traits.SelectElement( "jenkins.plugins.git.traits.BranchDiscoveryTrait"); branchDiscoverTrait != nil { - s.DiscoverBranches = true + gitSource.DiscoverBranches = true } if cloneTrait := traits.SelectElement( "jenkins.plugins.git.traits.CloneOptionTrait"); cloneTrait != nil { if cloneExtension := cloneTrait.SelectElement( "extension"); cloneExtension != nil { - s.CloneOption = &GitCloneOption{} + gitSource.CloneOption = &devops.GitCloneOption{} 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 { - s.CloneOption.Timeout = int(value) + gitSource.CloneOption.Timeout = int(value) } 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( "jenkins.scm.impl.trait.RegexSCMHeadFilterTrait"); regexTrait != 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 { - s.CredentialId = credential.Text() + githubSource.CredentialId = credential.Text() } if repoOwner := source.SelectElement("repoOwner"); repoOwner != nil { - s.Owner = repoOwner.Text() + githubSource.Owner = repoOwner.Text() } if repository := source.SelectElement("repository"); repository != nil { - s.Repo = repository.Text() + githubSource.Repo = repository.Text() } if apiUri := source.SelectElement("apiUri"); apiUri != nil { - s.ApiUri = apiUri.Text() + githubSource.ApiUri = apiUri.Text() } traits := source.SelectElement("traits") if branchDiscoverTrait := traits.SelectElement( "org.jenkinsci.plugins.github__branch__source.BranchDiscoveryTrait"); branchDiscoverTrait != nil { strategyId, _ := strconv.Atoi(branchDiscoverTrait.SelectElement("strategyId").Text()) - s.DiscoverBranches = strategyId + githubSource.DiscoverBranches = strategyId } if originPRDiscoverTrait := traits.SelectElement( "org.jenkinsci.plugins.github__branch__source.OriginPullRequestDiscoveryTrait"); originPRDiscoverTrait != nil { strategyId, _ := strconv.Atoi(originPRDiscoverTrait.SelectElement("strategyId").Text()) - s.DiscoverPRFromOrigin = strategyId + githubSource.DiscoverPRFromOrigin = strategyId } if forkPRDiscoverTrait := traits.SelectElement( "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, "$") switch trust[1] { case "TrustContributors": - s.DiscoverPRFromForks = &DiscoverPRFromForks{ + githubSource.DiscoverPRFromForks = &devops.DiscoverPRFromForks{ Strategy: strategyId, Trust: 1, } case "TrustEveryone": - s.DiscoverPRFromForks = &DiscoverPRFromForks{ + githubSource.DiscoverPRFromForks = &devops.DiscoverPRFromForks{ Strategy: strategyId, Trust: 2, } case "TrustPermission": - s.DiscoverPRFromForks = &DiscoverPRFromForks{ + githubSource.DiscoverPRFromForks = &devops.DiscoverPRFromForks{ Strategy: strategyId, Trust: 3, } case "TrustNobody": - s.DiscoverPRFromForks = &DiscoverPRFromForks{ + githubSource.DiscoverPRFromForks = &devops.DiscoverPRFromForks{ Strategy: strategyId, Trust: 4, } @@ -536,15 +369,15 @@ func (s *GithubSource) fromEtree(source *etree.Element) *GithubSource { "jenkins.plugins.git.traits.CloneOptionTrait"); cloneTrait != nil { if cloneExtension := cloneTrait.SelectElement( "extension"); cloneExtension != nil { - s.CloneOption = &GitCloneOption{} + githubSource.CloneOption = &devops.GitCloneOption{} 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 { - s.CloneOption.Timeout = int(value) + githubSource.CloneOption.Timeout = int(value) } 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( "jenkins.scm.impl.trait.RegexSCMHeadFilterTrait"); regexTrait != 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("plugin", "github-branch-source") - source.CreateElement("id").SetText(s.ScmId) - source.CreateElement("credentialsId").SetText(s.CredentialId) - source.CreateElement("repoOwner").SetText(s.Owner) - source.CreateElement("repository").SetText(s.Repo) - if s.ApiUri != "" { - source.CreateElement("apiUri").SetText(s.ApiUri) + source.CreateElement("id").SetText(githubSource.ScmId) + source.CreateElement("credentialsId").SetText(githubSource.CredentialId) + source.CreateElement("repoOwner").SetText(githubSource.Owner) + source.CreateElement("repository").SetText(githubSource.Repo) + if githubSource.ApiUri != "" { + source.CreateElement("apiUri").SetText(githubSource.ApiUri) } traits := source.CreateElement("traits") - if s.DiscoverBranches != 0 { + if githubSource.DiscoverBranches != 0 { 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"). - 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.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$" - switch s.DiscoverPRFromForks.Trust { + switch githubSource.DiscoverPRFromForks.Trust { case 1: trustClass += "TrustContributors" case 2: @@ -595,34 +428,35 @@ func (s *GithubSource) appendToEtree(source *etree.Element) *GithubSource { } 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.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("honorRefspec").SetText(strconv.FormatBool(true)) cloneExtension.CreateElement("reference") - if s.CloneOption.Timeout >= 0 { - cloneExtension.CreateElement("timeout").SetText(strconv.Itoa(s.CloneOption.Timeout)) + if githubSource.CloneOption.Timeout >= 0 { + cloneExtension.CreateElement("timeout").SetText(strconv.Itoa(githubSource.CloneOption.Timeout)) } else { cloneExtension.CreateElement("timeout").SetText(strconv.Itoa(10)) } - if s.CloneOption.Depth >= 0 { - cloneExtension.CreateElement("depth").SetText(strconv.Itoa(s.CloneOption.Depth)) + if githubSource.CloneOption.Depth >= 0 { + cloneExtension.CreateElement("depth").SetText(strconv.Itoa(githubSource.CloneOption.Depth)) } else { cloneExtension.CreateElement("depth").SetText(strconv.Itoa(1)) } } - if s.RegexFilter != "" { + if githubSource.RegexFilter != "" { regexTraits := traits.CreateElement("jenkins.scm.impl.trait.RegexSCMHeadFilterTrait") 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 { s.CredentialId = credential.Text() } @@ -653,17 +487,17 @@ func (s *BitbucketServerSource) fromEtree(source *etree.Element) *BitbucketServe trust := strings.Split(trustClass, "$") switch trust[1] { case "TrustEveryone": - s.DiscoverPRFromForks = &DiscoverPRFromForks{ + s.DiscoverPRFromForks = &devops.DiscoverPRFromForks{ Strategy: strategyId, Trust: 1, } case "TrustTeamForks": - s.DiscoverPRFromForks = &DiscoverPRFromForks{ + s.DiscoverPRFromForks = &devops.DiscoverPRFromForks{ Strategy: strategyId, Trust: 2, } case "TrustNobody": - s.DiscoverPRFromForks = &DiscoverPRFromForks{ + s.DiscoverPRFromForks = &devops.DiscoverPRFromForks{ Strategy: strategyId, Trust: 3, } @@ -672,7 +506,7 @@ func (s *BitbucketServerSource) fromEtree(source *etree.Element) *BitbucketServe "jenkins.plugins.git.traits.CloneOptionTrait"); cloneTrait != nil { if cloneExtension := cloneTrait.SelectElement( "extension"); cloneExtension != nil { - s.CloneOption = &GitCloneOption{} + s.CloneOption = &devops.GitCloneOption{} if value, err := strconv.ParseBool(cloneExtension.SelectElement("shallow").Text()); err == nil { 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("plugin", "cloudbees-bitbucket-branch-source") 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.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 { s.Remote = remote.Text() } @@ -772,10 +607,10 @@ func (s *SvnSource) fromEtree(source *etree.Element) *SvnSource { if excludes := source.SelectElement("excludes"); excludes != nil { 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("plugin", "subversion") source.CreateElement("id").SetText(s.ScmId) @@ -791,10 +626,11 @@ func (s *SvnSource) appendToEtree(source *etree.Element) *SvnSource { if 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 locations := scm.SelectElement("locations"); locations != 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("plugin", "scm-api") @@ -843,26 +679,27 @@ func (s *SingleSvnSource) appendToEtree(source *etree.Element) *SingleSvnSource source.CreateElement("filterChangelog").SetText("false") 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.CreateAttr("plugin", "multibranch-action-triggers") triggerProperty.CreateElement("createActionJobsToTrigger").SetText(s.CreateActionJobsToTrigger) 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") if triggerProperty != nil { s.CreateActionJobsToTrigger = triggerProperty.SelectElement("createActionJobsToTrigger").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() xmlString := ` @@ -896,7 +733,7 @@ func createMultiBranchPipelineConfigXml(projectName string, pipeline *MultiBranc if pipeline.MultiBranchJobTrigger != nil { properties := project.SelectElement("properties") - pipeline.MultiBranchJobTrigger.appendToEtree(properties) + appendMultiBranchJobTriggerToEtree(properties, pipeline.MultiBranchJobTrigger) } if pipeline.Discarder != nil { @@ -939,27 +776,15 @@ func createMultiBranchPipelineConfigXml(projectName string, pipeline *MultiBranc switch pipeline.SourceType { case "git": - gitDefine := pipeline.GitSource - gitDefine.ScmId = projectName + pipeline.Name - gitDefine.appendToEtree(source) + appendGitSourceToEtree(source, pipeline.GitSource) case "github": - githubDefine := pipeline.GitHubSource - githubDefine.ScmId = projectName + pipeline.Name - githubDefine.appendToEtree(source) + appendGithubSourceToEtree(source, pipeline.GitHubSource) case "svn": - svnDefine := pipeline.SvnSource - svnDefine.ScmId = projectName + pipeline.Name - svnDefine.appendToEtree(source) - + appendSvnSourceToEtree(source, pipeline.SvnSource) case "single_svn": - singSvnDefine := pipeline.SingleSvnSource - singSvnDefine.ScmId = projectName + pipeline.Name - singSvnDefine.appendToEtree(source) - + appendSingleSvnSourceToEtree(source, pipeline.SingleSvnSource) case "bitbucket_server": - bitbucketServerDefine := pipeline.BitbucketServerSource - bitbucketServerDefine.ScmId = projectName + pipeline.Name - bitbucketServerDefine.appendToEtree(source) + appendBitbucketServerSourceToEtree(source, pipeline.BitbucketServerSource) default: return "", fmt.Errorf("unsupport source type") @@ -977,8 +802,8 @@ func createMultiBranchPipelineConfigXml(projectName string, pipeline *MultiBranc return replaceXmlVersion(stringXml, "1.0", "1.1"), err } -func parseMultiBranchPipelineConfigXml(config string) (*MultiBranchPipeline, error) { - pipeline := &MultiBranchPipeline{} +func parseMultiBranchPipelineConfigXml(config string) (*devops.MultiBranchPipeline, error) { + pipeline := &devops.MultiBranchPipeline{} config = replaceXmlVersion(config, "1.1", "1.0") doc := etree.NewDocument() err := doc.ReadFromString(config) @@ -992,15 +817,13 @@ func parseMultiBranchPipelineConfigXml(config string) (*MultiBranchPipeline, err if properties := project.SelectElement("properties"); properties != nil { if multibranchTrigger := properties.SelectElement( "org.jenkinsci.plugins.workflow.multibranch.PipelineTriggerProperty"); multibranchTrigger != nil { - trigger := &MultiBranchJobTrigger{} - trigger.fromEtree(properties) - pipeline.MultiBranchJobTrigger = trigger + pipeline.MultiBranchJobTrigger = getMultiBranchJobTriggerfromEtree(properties) } } pipeline.Description = project.SelectElement("description").Text() if discarder := project.SelectElement("orphanedItemStrategy"); discarder != nil { - pipeline.Discarder = &DiscarderProperty{ + pipeline.Discarder = &devops.DiscarderProperty{ DaysToKeep: discarder.SelectElement("daysToKeep").Text(), NumToKeep: discarder.SelectElement("numToKeep").Text(), } @@ -1008,7 +831,7 @@ func parseMultiBranchPipelineConfigXml(config string) (*MultiBranchPipeline, err if triggers := project.SelectElement("triggers"); triggers != nil { if timerTrigger := triggers.SelectElement( "com.cloudbees.hudson.plugins.folder.computed.PeriodicFolderTrigger"); timerTrigger != nil { - pipeline.TimerTrigger = &TimerTrigger{ + pipeline.TimerTrigger = &devops.TimerTrigger{ Interval: timerTrigger.SelectElement("interval").Text(), } } @@ -1020,33 +843,23 @@ func parseMultiBranchPipelineConfigXml(config string) (*MultiBranchPipeline, err source := branchSource.SelectElement("source") switch source.SelectAttr("class").Value { case "org.jenkinsci.plugins.github_branch_source.GitHubSCMSource": - githubSource := &GithubSource{} - githubSource.fromEtree(source) - pipeline.GitHubSource = githubSource + pipeline.GitHubSource = getGithubSourcefromEtree(source) pipeline.SourceType = "github" case "com.cloudbees.jenkins.plugins.bitbucket.BitbucketSCMSource": - bitbucketServerSource := &BitbucketServerSource{} - bitbucketServerSource.fromEtree(source) - pipeline.BitbucketServerSource = bitbucketServerSource + pipeline.BitbucketServerSource = getBitbucketServerSourceFromEtree(source) pipeline.SourceType = "bitbucket_server" case "jenkins.plugins.git.GitSCMSource": - gitSource := &GitSource{} - gitSource.fromEtree(source) pipeline.SourceType = "git" - pipeline.GitSource = gitSource + pipeline.GitSource = getGitSourcefromEtree(source) case "jenkins.scm.impl.SingleSCMSource": - singleSvnSource := &SingleSvnSource{} - singleSvnSource.fromEtree(source) pipeline.SourceType = "single_svn" - pipeline.SingleSvnSource = singleSvnSource + pipeline.SingleSvnSource = getSingleSvnSourceFromEtree(source) case "jenkins.scm.impl.subversion.SubversionSCMSource": - svnSource := &SvnSource{} - svnSource.fromEtree(source) pipeline.SourceType = "svn" - pipeline.SvnSource = svnSource + pipeline.SvnSource = getSvnSourcefromEtree(source) } } } @@ -1078,55 +891,3 @@ func toCrontab(millis int64) string { 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 -} diff --git a/pkg/models/devops/project_pipeline_test.go b/pkg/simple/client/devops/jenkins/pipeline_internal_test.go similarity index 82% rename from pkg/models/devops/project_pipeline_test.go rename to pkg/simple/client/devops/jenkins/pipeline_internal_test.go index 4f572b55..478a3fa4 100644 --- a/pkg/models/devops/project_pipeline_test.go +++ b/pkg/simple/client/devops/jenkins/pipeline_internal_test.go @@ -1,25 +1,13 @@ -/* -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 jenkins import ( + "kubesphere.io/kubesphere/pkg/simple/client/devops" "reflect" "testing" ) func Test_NoScmPipelineConfig(t *testing.T) { - inputs := []*NoScmPipeline{ + inputs := []*devops.NoScmPipeline{ { Name: "", Description: "for test", @@ -54,12 +42,12 @@ func Test_NoScmPipelineConfig(t *testing.T) { } func Test_NoScmPipelineConfig_Discarder(t *testing.T) { - inputs := []*NoScmPipeline{ + inputs := []*devops.NoScmPipeline{ { Name: "", Description: "for test", Jenkinsfile: "node{echo 'hello'}", - Discarder: &DiscarderProperty{ + Discarder: &devops.DiscarderProperty{ "3", "5", }, }, @@ -67,7 +55,7 @@ func Test_NoScmPipelineConfig_Discarder(t *testing.T) { Name: "", Description: "for test", Jenkinsfile: "node{echo 'hello'}", - Discarder: &DiscarderProperty{ + Discarder: &devops.DiscarderProperty{ "3", "", }, }, @@ -75,7 +63,7 @@ func Test_NoScmPipelineConfig_Discarder(t *testing.T) { Name: "", Description: "for test", Jenkinsfile: "node{echo 'hello'}", - Discarder: &DiscarderProperty{ + Discarder: &devops.DiscarderProperty{ "", "21321", }, }, @@ -83,7 +71,7 @@ func Test_NoScmPipelineConfig_Discarder(t *testing.T) { Name: "", Description: "for test", 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) { - inputs := []*NoScmPipeline{ + inputs := []*devops.NoScmPipeline{ { Name: "", Description: "for test", Jenkinsfile: "node{echo 'hello'}", - Parameters: &Parameters{ - &Parameter{ + Parameters: &devops.Parameters{ + &devops.Parameter{ Name: "d", DefaultValue: "a\nb", Type: "choice", @@ -123,26 +111,26 @@ func Test_NoScmPipelineConfig_Param(t *testing.T) { Name: "", Description: "for test", Jenkinsfile: "node{echo 'hello'}", - Parameters: &Parameters{ - &Parameter{ + Parameters: &devops.Parameters{ + &devops.Parameter{ Name: "a", DefaultValue: "abc", Type: "string", Description: "fortest", }, - &Parameter{ + &devops.Parameter{ Name: "b", DefaultValue: "false", Type: "boolean", Description: "fortest", }, - &Parameter{ + &devops.Parameter{ Name: "c", DefaultValue: "password \n aaa", Type: "text", Description: "fortest", }, - &Parameter{ + &devops.Parameter{ Name: "d", DefaultValue: "a\nb", Type: "choice", @@ -168,12 +156,12 @@ func Test_NoScmPipelineConfig_Param(t *testing.T) { } func Test_NoScmPipelineConfig_Trigger(t *testing.T) { - inputs := []*NoScmPipeline{ + inputs := []*devops.NoScmPipeline{ { Name: "", Description: "for test", Jenkinsfile: "node{echo 'hello'}", - TimerTrigger: &TimerTrigger{ + TimerTrigger: &devops.TimerTrigger{ Cron: "1 1 1 * * *", }, }, @@ -182,7 +170,7 @@ func Test_NoScmPipelineConfig_Trigger(t *testing.T) { Name: "", Description: "for test", Jenkinsfile: "node{echo 'hello'}", - RemoteTrigger: &RemoteTrigger{ + RemoteTrigger: &devops.RemoteTrigger{ Token: "abc", }, }, @@ -190,10 +178,10 @@ func Test_NoScmPipelineConfig_Trigger(t *testing.T) { Name: "", Description: "for test", Jenkinsfile: "node{echo 'hello'}", - TimerTrigger: &TimerTrigger{ + TimerTrigger: &devops.TimerTrigger{ Cron: "1 1 1 * * *", }, - RemoteTrigger: &RemoteTrigger{ + RemoteTrigger: &devops.RemoteTrigger{ Token: "abc", }, }, @@ -217,34 +205,34 @@ func Test_NoScmPipelineConfig_Trigger(t *testing.T) { func Test_MultiBranchPipelineConfig(t *testing.T) { - inputs := []*MultiBranchPipeline{ + inputs := []*devops.MultiBranchPipeline{ { Name: "", Description: "for test", ScriptPath: "Jenkinsfile", SourceType: "git", - GitSource: &GitSource{}, + GitSource: &devops.GitSource{}, }, { Name: "", Description: "for test", ScriptPath: "Jenkinsfile", SourceType: "github", - GitHubSource: &GithubSource{}, + GitHubSource: &devops.GithubSource{}, }, { Name: "", Description: "for test", ScriptPath: "Jenkinsfile", SourceType: "single_svn", - SingleSvnSource: &SingleSvnSource{}, + SingleSvnSource: &devops.SingleSvnSource{}, }, { Name: "", Description: "for test", ScriptPath: "Jenkinsfile", SourceType: "svn", - SvnSource: &SvnSource{}, + SvnSource: &devops.SvnSource{}, }, } for _, input := range inputs { @@ -265,17 +253,17 @@ func Test_MultiBranchPipelineConfig(t *testing.T) { func Test_MultiBranchPipelineConfig_Discarder(t *testing.T) { - inputs := []*MultiBranchPipeline{ + inputs := []*devops.MultiBranchPipeline{ { Name: "", Description: "for test", ScriptPath: "Jenkinsfile", SourceType: "git", - Discarder: &DiscarderProperty{ + Discarder: &devops.DiscarderProperty{ DaysToKeep: "1", NumToKeep: "2", }, - GitSource: &GitSource{}, + GitSource: &devops.GitSource{}, }, } for _, input := range inputs { @@ -295,16 +283,16 @@ func Test_MultiBranchPipelineConfig_Discarder(t *testing.T) { } func Test_MultiBranchPipelineConfig_TimerTrigger(t *testing.T) { - inputs := []*MultiBranchPipeline{ + inputs := []*devops.MultiBranchPipeline{ { Name: "", Description: "for test", ScriptPath: "Jenkinsfile", SourceType: "git", - TimerTrigger: &TimerTrigger{ + TimerTrigger: &devops.TimerTrigger{ Interval: "12345566", }, - GitSource: &GitSource{}, + GitSource: &devops.GitSource{}, }, } for _, input := range inputs { @@ -325,16 +313,16 @@ func Test_MultiBranchPipelineConfig_TimerTrigger(t *testing.T) { func Test_MultiBranchPipelineConfig_Source(t *testing.T) { - inputs := []*MultiBranchPipeline{ + inputs := []*devops.MultiBranchPipeline{ { Name: "", Description: "for test", ScriptPath: "Jenkinsfile", SourceType: "git", - TimerTrigger: &TimerTrigger{ + TimerTrigger: &devops.TimerTrigger{ Interval: "12345566", }, - GitSource: &GitSource{ + GitSource: &devops.GitSource{ Url: "https://github.com/kubesphere/devops", CredentialId: "git", DiscoverBranches: true, @@ -345,17 +333,17 @@ func Test_MultiBranchPipelineConfig_Source(t *testing.T) { Description: "for test", ScriptPath: "Jenkinsfile", SourceType: "github", - TimerTrigger: &TimerTrigger{ + TimerTrigger: &devops.TimerTrigger{ Interval: "12345566", }, - GitHubSource: &GithubSource{ + GitHubSource: &devops.GithubSource{ Owner: "kubesphere", Repo: "devops", CredentialId: "github", ApiUri: "https://api.github.com", DiscoverBranches: 1, DiscoverPRFromOrigin: 2, - DiscoverPRFromForks: &DiscoverPRFromForks{ + DiscoverPRFromForks: &devops.DiscoverPRFromForks{ Strategy: 1, Trust: 1, }, @@ -366,17 +354,17 @@ func Test_MultiBranchPipelineConfig_Source(t *testing.T) { Description: "for test", ScriptPath: "Jenkinsfile", SourceType: "bitbucket_server", - TimerTrigger: &TimerTrigger{ + TimerTrigger: &devops.TimerTrigger{ Interval: "12345566", }, - BitbucketServerSource: &BitbucketServerSource{ + BitbucketServerSource: &devops.BitbucketServerSource{ Owner: "kubesphere", Repo: "devops", CredentialId: "github", ApiUri: "https://api.github.com", DiscoverBranches: 1, DiscoverPRFromOrigin: 2, - DiscoverPRFromForks: &DiscoverPRFromForks{ + DiscoverPRFromForks: &devops.DiscoverPRFromForks{ Strategy: 1, Trust: 1, }, @@ -388,10 +376,10 @@ func Test_MultiBranchPipelineConfig_Source(t *testing.T) { Description: "for test", ScriptPath: "Jenkinsfile", SourceType: "svn", - TimerTrigger: &TimerTrigger{ + TimerTrigger: &devops.TimerTrigger{ Interval: "12345566", }, - SvnSource: &SvnSource{ + SvnSource: &devops.SvnSource{ Remote: "https://api.svn.com/bcd", CredentialId: "svn", Excludes: "truck", @@ -403,10 +391,10 @@ func Test_MultiBranchPipelineConfig_Source(t *testing.T) { Description: "for test", ScriptPath: "Jenkinsfile", SourceType: "single_svn", - TimerTrigger: &TimerTrigger{ + TimerTrigger: &devops.TimerTrigger{ Interval: "12345566", }, - SingleSvnSource: &SingleSvnSource{ + SingleSvnSource: &devops.SingleSvnSource{ Remote: "https://api.svn.com/bcd", CredentialId: "svn", }, @@ -431,17 +419,17 @@ func Test_MultiBranchPipelineConfig_Source(t *testing.T) { func Test_MultiBranchPipelineCloneConfig(t *testing.T) { - inputs := []*MultiBranchPipeline{ + inputs := []*devops.MultiBranchPipeline{ { Name: "", Description: "for test", ScriptPath: "Jenkinsfile", SourceType: "git", - GitSource: &GitSource{ + GitSource: &devops.GitSource{ Url: "https://github.com/kubesphere/devops", CredentialId: "git", DiscoverBranches: true, - CloneOption: &GitCloneOption{ + CloneOption: &devops.GitCloneOption{ Shallow: false, Depth: 3, Timeout: 20, @@ -453,18 +441,18 @@ func Test_MultiBranchPipelineCloneConfig(t *testing.T) { Description: "for test", ScriptPath: "Jenkinsfile", SourceType: "github", - GitHubSource: &GithubSource{ + GitHubSource: &devops.GithubSource{ Owner: "kubesphere", Repo: "devops", CredentialId: "github", ApiUri: "https://api.github.com", DiscoverBranches: 1, DiscoverPRFromOrigin: 2, - DiscoverPRFromForks: &DiscoverPRFromForks{ + DiscoverPRFromForks: &devops.DiscoverPRFromForks{ Strategy: 1, Trust: 1, }, - CloneOption: &GitCloneOption{ + CloneOption: &devops.GitCloneOption{ Shallow: false, Depth: 3, Timeout: 20, @@ -492,13 +480,13 @@ func Test_MultiBranchPipelineCloneConfig(t *testing.T) { func Test_MultiBranchPipelineRegexFilter(t *testing.T) { - inputs := []*MultiBranchPipeline{ + inputs := []*devops.MultiBranchPipeline{ { Name: "", Description: "for test", ScriptPath: "Jenkinsfile", SourceType: "git", - GitSource: &GitSource{ + GitSource: &devops.GitSource{ Url: "https://github.com/kubesphere/devops", CredentialId: "git", DiscoverBranches: true, @@ -510,14 +498,14 @@ func Test_MultiBranchPipelineRegexFilter(t *testing.T) { Description: "for test", ScriptPath: "Jenkinsfile", SourceType: "github", - GitHubSource: &GithubSource{ + GitHubSource: &devops.GithubSource{ Owner: "kubesphere", Repo: "devops", CredentialId: "github", ApiUri: "https://api.github.com", DiscoverBranches: 1, DiscoverPRFromOrigin: 2, - DiscoverPRFromForks: &DiscoverPRFromForks{ + DiscoverPRFromForks: &devops.DiscoverPRFromForks{ Strategy: 1, Trust: 1, }, @@ -545,26 +533,26 @@ func Test_MultiBranchPipelineRegexFilter(t *testing.T) { func Test_MultiBranchPipelineMultibranchTrigger(t *testing.T) { - inputs := []*MultiBranchPipeline{ + inputs := []*devops.MultiBranchPipeline{ { Name: "", Description: "for test", ScriptPath: "Jenkinsfile", SourceType: "github", - GitHubSource: &GithubSource{ + GitHubSource: &devops.GithubSource{ Owner: "kubesphere", Repo: "devops", CredentialId: "github", ApiUri: "https://api.github.com", DiscoverBranches: 1, DiscoverPRFromOrigin: 2, - DiscoverPRFromForks: &DiscoverPRFromForks{ + DiscoverPRFromForks: &devops.DiscoverPRFromForks{ Strategy: 1, Trust: 1, }, RegexFilter: ".*", }, - MultiBranchJobTrigger: &MultiBranchJobTrigger{ + MultiBranchJobTrigger: &devops.MultiBranchJobTrigger{ CreateActionJobsToTrigger: "abc", DeleteActionJobsToTrigger: "ddd", }, @@ -574,20 +562,20 @@ func Test_MultiBranchPipelineMultibranchTrigger(t *testing.T) { Description: "for test", ScriptPath: "Jenkinsfile", SourceType: "github", - GitHubSource: &GithubSource{ + GitHubSource: &devops.GithubSource{ Owner: "kubesphere", Repo: "devops", CredentialId: "github", ApiUri: "https://api.github.com", DiscoverBranches: 1, DiscoverPRFromOrigin: 2, - DiscoverPRFromForks: &DiscoverPRFromForks{ + DiscoverPRFromForks: &devops.DiscoverPRFromForks{ Strategy: 1, Trust: 1, }, RegexFilter: ".*", }, - MultiBranchJobTrigger: &MultiBranchJobTrigger{ + MultiBranchJobTrigger: &devops.MultiBranchJobTrigger{ CreateActionJobsToTrigger: "abc", }, }, @@ -596,20 +584,20 @@ func Test_MultiBranchPipelineMultibranchTrigger(t *testing.T) { Description: "for test", ScriptPath: "Jenkinsfile", SourceType: "github", - GitHubSource: &GithubSource{ + GitHubSource: &devops.GithubSource{ Owner: "kubesphere", Repo: "devops", CredentialId: "github", ApiUri: "https://api.github.com", DiscoverBranches: 1, DiscoverPRFromOrigin: 2, - DiscoverPRFromForks: &DiscoverPRFromForks{ + DiscoverPRFromForks: &devops.DiscoverPRFromForks{ Strategy: 1, Trust: 1, }, RegexFilter: ".*", }, - MultiBranchJobTrigger: &MultiBranchJobTrigger{ + MultiBranchJobTrigger: &devops.MultiBranchJobTrigger{ DeleteActionJobsToTrigger: "ddd", }, }, diff --git a/pkg/gojenkins/pipeline_model_converter.go b/pkg/simple/client/devops/jenkins/pipeline_model_converter.go similarity index 99% rename from pkg/gojenkins/pipeline_model_converter.go rename to pkg/simple/client/devops/jenkins/pipeline_model_converter.go index 12b9003a..46640a6e 100644 --- a/pkg/gojenkins/pipeline_model_converter.go +++ b/pkg/simple/client/devops/jenkins/pipeline_model_converter.go @@ -11,7 +11,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -package gojenkins +package jenkins import ( "errors" diff --git a/pkg/simple/client/devops/jenkins/project.go b/pkg/simple/client/devops/jenkins/project.go new file mode 100644 index 00000000..43579e68 --- /dev/null +++ b/pkg/simple/client/devops/jenkins/project.go @@ -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 +} diff --git a/pkg/simple/client/devops/jenkins/project_pipeline.go b/pkg/simple/client/devops/jenkins/project_pipeline.go new file mode 100644 index 00000000..7768c2fb --- /dev/null +++ b/pkg/simple/client/devops/jenkins/project_pipeline.go @@ -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()) + } +} diff --git a/pkg/simple/client/devops/jenkins/pure_request.go b/pkg/simple/client/devops/jenkins/pure_request.go new file mode 100644 index 00000000..4943d06c --- /dev/null +++ b/pkg/simple/client/devops/jenkins/pure_request.go @@ -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 +} diff --git a/pkg/gojenkins/request.go b/pkg/simple/client/devops/jenkins/request.go similarity index 99% rename from pkg/gojenkins/request.go rename to pkg/simple/client/devops/jenkins/request.go index 25cc3e06..6239162e 100644 --- a/pkg/gojenkins/request.go +++ b/pkg/simple/client/devops/jenkins/request.go @@ -12,7 +12,7 @@ // License for the specific language governing permissions and limitations // under the License. -package gojenkins +package jenkins import ( "bytes" diff --git a/pkg/gojenkins/role.go b/pkg/simple/client/devops/jenkins/role.go similarity index 99% rename from pkg/gojenkins/role.go rename to pkg/simple/client/devops/jenkins/role.go index 872d3265..f8a7e643 100644 --- a/pkg/gojenkins/role.go +++ b/pkg/simple/client/devops/jenkins/role.go @@ -1,4 +1,4 @@ -package gojenkins +package jenkins import ( "errors" diff --git a/pkg/simple/client/devops/jenkins/utils.go b/pkg/simple/client/devops/jenkins/utils.go new file mode 100644 index 00000000..f080fedc --- /dev/null +++ b/pkg/simple/client/devops/jenkins/utils.go @@ -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 +} diff --git a/pkg/simple/client/devops/member.go b/pkg/simple/client/devops/member.go new file mode 100644 index 00000000..eaa5a486 --- /dev/null +++ b/pkg/simple/client/devops/member.go @@ -0,0 +1,48 @@ +package devops + +type ProjectMembership struct { + Username string `json:"username" description:"Member's username,username 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'"` +} diff --git a/pkg/models/devops/json.go b/pkg/simple/client/devops/pipeline.go similarity index 92% rename from pkg/models/devops/json.go rename to pkg/simple/client/devops/pipeline.go index 05ec9808..8043a50f 100644 --- a/pkg/models/devops/json.go +++ b/pkg/simple/client/devops/pipeline.go @@ -1,21 +1,15 @@ -/* - - 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 +package devops - 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. +import ( + "io" + "net/http" + "net/url" +) -*/ -package devops +type PipelineList struct { + Items []Pipeline `json:"items"` + Total int `json:"total_count"` +} // GetPipeline & SearchPipelines type Pipeline struct { @@ -85,7 +79,7 @@ type Pipeline struct { } // GetPipeBranchRun & SearchPipelineRuns -type BranchPipelineRun struct { +type PipelineRunList struct { Class string `json:"_class,omitempty" description:"It’s a fully qualified name and is an identifier of the producer of this resource's capability."` Links struct { PrevRun struct { @@ -316,7 +310,7 @@ type OrgRepo struct { } // StopPipeline -type StopPipe struct { +type StopPipeline struct { Class string `json:"_class,omitempty" description:"It’s a fully qualified name and is an identifier of the producer of this resource's capability."` Links struct { Parent struct { @@ -390,7 +384,7 @@ type StopPipe struct { } // ReplayPipeline -type ReplayPipe struct { +type ReplayPipeline struct { Class string `json:"_class,omitempty" description:"It’s a fully qualified name and is an identifier of the producer of this resource's capability."` Links struct { Parent struct { @@ -468,7 +462,7 @@ type Artifacts struct { } // GetPipeBranch -type PipeBranch struct { +type PipelineBranch struct { Class string `json:"_class,omitempty" description:"It’s a fully qualified name and is an identifier of the producer of this resource's capability."` Links struct { Self struct { @@ -598,7 +592,7 @@ type RunPayload struct { } `json:"parameters,omitempty"` } -type QueuedBlueRun struct { +type RunPipeline struct { Class string `json:"_class,omitempty" description:"It’s a fully qualified name and is an identifier of the producer of this resource's capability."` Links struct { Parent struct { @@ -1108,22 +1102,18 @@ type NodesDetail struct { Actions []interface{} `json:"actions,omitempty" description:"the list of all actions"` DisplayDescription interface{} `json:"displayDescription,omitempty" description:"display description"` 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"` 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"` 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"` CauseOfBlockage interface{} `json:"causeOfBlockage,omitempty" description:"the cause of blockage"` - Edges []struct { - Class string `json:"_class,omitempty" description:"It’s a fully qualified name and is an identifier of the producer of this resource's capability."` - ID string `json:"id,omitempty" description:"id"` - Type string `json:"type,omitempty" description:"type"` - } `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"` + Edges []interface{} `json:"edges,omitempty" description:"edges"` + 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 { @@ -1145,3 +1135,65 @@ type Input struct { Parameters []interface{} `json:"parameters,omitempty" description:"the parameters of check action"` 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) +} diff --git a/pkg/simple/client/devops/project.go b/pkg/simple/client/devops/project.go new file mode 100644 index 00000000..2ce7a782 --- /dev/null +++ b/pkg/simple/client/devops/project.go @@ -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 +} diff --git a/pkg/simple/client/devops/project_pipeline.go b/pkg/simple/client/devops/project_pipeline.go new file mode 100644 index 00000000..587b037c --- /dev/null +++ b/pkg/simple/client/devops/project_pipeline.go @@ -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) +} diff --git a/pkg/simple/client/factory.go b/pkg/simple/client/factory.go index 223fa6ce..d19325ea 100644 --- a/pkg/simple/client/factory.go +++ b/pkg/simple/client/factory.go @@ -4,6 +4,7 @@ import ( "errors" "kubesphere.io/kubesphere/pkg/simple/client/cache" "kubesphere.io/kubesphere/pkg/simple/client/devops" + "kubesphere.io/kubesphere/pkg/simple/client/devops/jenkins" esclient "kubesphere.io/kubesphere/pkg/simple/client/elasticsearch" "kubesphere.io/kubesphere/pkg/simple/client/k8s" "kubesphere.io/kubesphere/pkg/simple/client/kubesphere" @@ -22,7 +23,7 @@ type ClientSetOptions struct { mySQLOptions *mysql.Options redisOptions *cache.Options kubernetesOptions *k8s.KubernetesOptions - devopsOptions *devops.Options + devopsOptions *jenkins.Options sonarqubeOptions *sonarqube.Options ldapOptions *ldap.Options s3Options *s3.Options @@ -38,7 +39,7 @@ func NewClientSetOptions() *ClientSetOptions { redisOptions: cache.NewRedisOptions(), kubernetesOptions: k8s.NewKubernetesOptions(), ldapOptions: ldap.NewLdapOptions(), - devopsOptions: devops.NewDevopsOptions(), + devopsOptions: jenkins.NewDevopsOptions(), sonarqubeOptions: sonarqube.NewSonarQubeOptions(), s3Options: s3.NewS3Options(), openPitrixOptions: openpitrix.NewOpenPitrixOptions(), @@ -63,7 +64,7 @@ func (c *ClientSetOptions) SetKubernetesOptions(options *k8s.KubernetesOptions) return c } -func (c *ClientSetOptions) SetDevopsOptions(options *devops.Options) *ClientSetOptions { +func (c *ClientSetOptions) SetDevopsOptions(options *jenkins.Options) *ClientSetOptions { c.devopsOptions = options return c } @@ -114,7 +115,7 @@ type ClientSet struct { k8sClient k8s.Client ldapClient *ldap.Client - devopsClient *devops.Client + devopsClient *jenkins.Client sonarQubeClient *sonarqube.Client redisClient cache.Interface s3Client s3.Interface @@ -194,27 +195,28 @@ func (cs *ClientSet) Cache() (cache.Interface, error) { } } -func (cs *ClientSet) Devops() (*devops.Client, error) { - var err error - - if cs.csoptions.devopsOptions == nil || cs.csoptions.devopsOptions.Host == "" { - return nil, ErrClientSetNotEnabled - } - - if cs.devopsClient != nil { - return cs.devopsClient, nil - } else { - mutex.Lock() - defer mutex.Unlock() - - if cs.devopsClient == nil { - cs.devopsClient, err = devops.NewDevopsClient(cs.csoptions.devopsOptions) - if err != nil { - return nil, err - } - } - return cs.devopsClient, nil - } +func (cs *ClientSet) Devops() (devops.Interface, error) { + //var err error + // + //if cs.csoptions.devopsOptions == nil || cs.csoptions.devopsOptions.Host == "" { + // return nil, ErrClientSetNotEnabled + //} + // + //if cs.devopsClient != nil { + // return cs.devopsClient, nil + //} else { + // mutex.Lock() + // defer mutex.Unlock() + // + // if cs.devopsClient == nil { + // cs.devopsClient, err = jenkins.NewDevopsClient(cs.csoptions.devopsOptions) + // if err != nil { + // return nil, err + // } + // } + // return cs.devopsClient, nil + //} + return nil, nil } func (cs *ClientSet) SonarQube() (*sonarqube.Client, error) { diff --git a/pkg/simple/client/monitoring/interface.go b/pkg/simple/client/monitoring/interface.go index 6dfce3ca..8230be3a 100644 --- a/pkg/simple/client/monitoring/interface.go +++ b/pkg/simple/client/monitoring/interface.go @@ -1,39 +1,32 @@ package monitoring - type ClusterQuery struct { - } type ClusterMetrics struct { - } type WorkspaceQuery struct { - } type WorkspaceMetrics struct { - } type NamespaceQuery struct { - } type NamespaceMetrics struct { - } // Interface defines all the abstract behaviors of monitoring type Interface interface { - // Get - GetClusterMetrics(query ClusterQuery) ClusterMetrics + // Get + GetClusterMetrics(query ClusterQuery) ClusterMetrics - // - GetWorkspaceMetrics(query WorkspaceQuery) WorkspaceMetrics + // + GetWorkspaceMetrics(query WorkspaceQuery) WorkspaceMetrics - // - GetNamespaceMetrics(query NamespaceQuery) NamespaceMetrics + // + GetNamespaceMetrics(query NamespaceQuery) NamespaceMetrics } diff --git a/pkg/simple/client/monitoring/prometheus.go b/pkg/simple/client/monitoring/prometheus.go index d2e53895..5b82066e 100644 --- a/pkg/simple/client/monitoring/prometheus.go +++ b/pkg/simple/client/monitoring/prometheus.go @@ -1,31 +1,31 @@ package monitoring import ( - "net/http" - "time" + "net/http" + "time" ) // prometheus implements monitoring interface backed by Prometheus type prometheus struct { - options *Options - client *http.Client + options *Options + client *http.Client } func NewPrometheus(options *Options) Interface { - return &prometheus{ - options:options, - client: &http.Client{ Timeout: 10 * time.Second }, - } + return &prometheus{ + options: options, + client: &http.Client{Timeout: 10 * time.Second}, + } } func (p prometheus) GetClusterMetrics(query ClusterQuery) ClusterMetrics { - panic("implement me") + panic("implement me") } func (p prometheus) GetWorkspaceMetrics(query WorkspaceQuery) WorkspaceMetrics { - panic("implement me") + panic("implement me") } func (p prometheus) GetNamespaceMetrics(query NamespaceQuery) NamespaceMetrics { - panic("implement me") + panic("implement me") } diff --git a/pkg/simple/client/monitoring/prometheus_options.go b/pkg/simple/client/monitoring/prometheus_options.go index b29e01d1..0aed413c 100644 --- a/pkg/simple/client/monitoring/prometheus_options.go +++ b/pkg/simple/client/monitoring/prometheus_options.go @@ -1,41 +1,41 @@ package monitoring import ( - "github.com/spf13/pflag" + "github.com/spf13/pflag" ) type Options struct { - Endpoint string `json:"endpoint,omitempty" yaml:"endpoint"` - SecondaryEndpoint string `json:"secondaryEndpoint,omitempty" yaml:"secondaryEndpoint"` + Endpoint string `json:"endpoint,omitempty" yaml:"endpoint"` + SecondaryEndpoint string `json:"secondaryEndpoint,omitempty" yaml:"secondaryEndpoint"` } func NewPrometheusOptions() *Options { - return &Options{ - Endpoint: "", - SecondaryEndpoint: "", - } + return &Options{ + Endpoint: "", + SecondaryEndpoint: "", + } } func (s *Options) Validate() []error { - var errs []error - return errs + var errs []error + return errs } func (s *Options) ApplyTo(options *Options) { - if s.Endpoint != "" { - options.Endpoint = s.Endpoint - } + if s.Endpoint != "" { + options.Endpoint = s.Endpoint + } - if s.SecondaryEndpoint != "" { - options.SecondaryEndpoint = s.SecondaryEndpoint - } + if s.SecondaryEndpoint != "" { + options.SecondaryEndpoint = s.SecondaryEndpoint + } } func (s *Options) AddFlags(fs *pflag.FlagSet, c *Options) { - fs.StringVar(&s.Endpoint, "prometheus-endpoint", c.Endpoint, ""+ - "Prometheus service endpoint which stores KubeSphere monitoring data, if left "+ - "blank, will use builtin metrics-server as data source.") + fs.StringVar(&s.Endpoint, "prometheus-endpoint", c.Endpoint, ""+ + "Prometheus service endpoint which stores KubeSphere monitoring data, if left "+ + "blank, will use builtin metrics-server as data source.") - fs.StringVar(&s.SecondaryEndpoint, "prometheus-secondary-endpoint", c.SecondaryEndpoint, ""+ - "Prometheus secondary service endpoint, if left empty and endpoint is set, will use endpoint instead.") + fs.StringVar(&s.SecondaryEndpoint, "prometheus-secondary-endpoint", c.SecondaryEndpoint, ""+ + "Prometheus secondary service endpoint, if left empty and endpoint is set, will use endpoint instead.") } diff --git a/pkg/simple/client/prometheus/prometheus.go b/pkg/simple/client/prometheus/prometheus.go index e419ce20..a24cb160 100644 --- a/pkg/simple/client/prometheus/prometheus.go +++ b/pkg/simple/client/prometheus/prometheus.go @@ -27,7 +27,6 @@ import ( "time" ) - type Client struct { client *http.Client endpoint string diff --git a/pkg/simple/client/s3/fake/fakes3.go b/pkg/simple/client/s3/fake/fakes3.go new file mode 100644 index 00000000..f7980335 --- /dev/null +++ b/pkg/simple/client/s3/fake/fakes3.go @@ -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 +} diff --git a/pkg/simple/client/s3/fake/fakes3_test.go b/pkg/simple/client/s3/fake/fakes3_test.go new file mode 100644 index 00000000..6a5e80d5 --- /dev/null +++ b/pkg/simple/client/s3/fake/fakes3_test.go @@ -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) + } +} diff --git a/pkg/simple/client/s3/interface.go b/pkg/simple/client/s3/interface.go index 594b8438..96672ca0 100644 --- a/pkg/simple/client/s3/interface.go +++ b/pkg/simple/client/s3/interface.go @@ -2,15 +2,13 @@ package s3 import ( "io" - "time" ) type Interface interface { // Upload uploads a object to storage and returns object location if succeeded - Upload(key string, body io.Reader) (string, error) + Upload(key, fileName string, body io.Reader) error - // Get retrieves and object's downloadable location if succeeded - Get(key string, fileName string, expire time.Duration) (string, error) + GetDownloadURL(key string, fileName string) (string, error) // Delete deletes an object by its key Delete(key string) error diff --git a/pkg/simple/client/s3/s3.go b/pkg/simple/client/s3/s3.go index f74985f5..244f78c3 100644 --- a/pkg/simple/client/s3/s3.go +++ b/pkg/simple/client/s3/s3.go @@ -1,10 +1,13 @@ package s3 import ( + "code.cloudfoundry.org/bytefmt" + "fmt" "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/aws/credentials" "github.com/aws/aws-sdk-go/aws/session" "github.com/aws/aws-sdk-go/service/s3" + "github.com/aws/aws-sdk-go/service/s3/s3manager" "io" "k8s.io/klog" "time" @@ -16,16 +19,38 @@ type Client struct { bucket string } -func (s *Client) Upload(key string, body io.Reader) (string, error) { - panic("implement me") +func (s *Client) Upload(key, fileName string, body io.Reader) error { + uploader := s3manager.NewUploader(s.s3Session, func(uploader *s3manager.Uploader) { + uploader.PartSize = 5 * bytefmt.MEGABYTE + uploader.LeavePartsOnError = true + }) + _, err := uploader.Upload(&s3manager.UploadInput{ + Bucket: aws.String(s.bucket), + Key: aws.String(key), + Body: body, + ContentDisposition: aws.String(fmt.Sprintf("attachment; filename=\"%s\"", fileName)), + }) + return err } -func (s *Client) Get(key string, fileName string, expire time.Duration) (string, error) { - panic("implement me") +func (s *Client) GetDownloadURL(key string, fileName string) (string, error) { + req, _ := s.s3Client.GetObjectRequest(&s3.GetObjectInput{ + Bucket: aws.String(s.bucket), + Key: aws.String(key), + ResponseContentDisposition: aws.String(fmt.Sprintf("attachment; filename=\"%s\"", fileName)), + }) + return req.Presign(5 * time.Minute) } func (s *Client) Delete(key string) error { - panic("implement me") + _, err := s.s3Client.DeleteObject( + &s3.DeleteObjectInput{Bucket: aws.String(s.bucket), + Key: aws.String(key), + }) + if err != nil { + return err + } + return nil } func NewS3Client(options *Options) (Interface, error) { @@ -55,7 +80,6 @@ func NewS3Client(options *Options) (Interface, error) { } func (s *Client) Client() *s3.S3 { - return s.s3Client } func (s *Client) Session() *session.Session { diff --git a/pkg/simple/client/sonarqube/interface.go b/pkg/simple/client/sonarqube/interface.go index 9873cf9f..a7ab3782 100644 --- a/pkg/simple/client/sonarqube/interface.go +++ b/pkg/simple/client/sonarqube/interface.go @@ -1,8 +1,68 @@ package sonarqube -type Interface interface { - // - GetIssues() +import ( + sonargo "github.com/kubesphere/sonargo/sonar" + "k8s.io/klog" + "kubesphere.io/kubesphere/pkg/simple/client/devops" +) - // +type SonarInterface interface { + GetSonarResultsByTaskIds(taskId ...string) ([]*SonarStatus, error) +} + +type sonarQube struct { + client *sonargo.Client +} + +const ( + SonarAnalysisActionClass = "hudson.plugins.sonar.action.SonarAnalysisAction" + SonarMetricKeys = "alert_status,quality_gate_details,bugs,new_bugs,reliability_rating,new_reliability_rating,vulnerabilities,new_vulnerabilities,security_rating,new_security_rating,code_smells,new_code_smells,sqale_rating,new_maintainability_rating,sqale_index,new_technical_debt,coverage,new_coverage,new_lines_to_cover,tests,duplicated_lines_density,new_duplicated_lines_density,duplicated_blocks,ncloc,ncloc_language_distribution,projects,new_lines" + SonarAdditionalFields = "metrics,periods" +) + +type SonarStatus struct { + Measures *sonargo.MeasuresComponentObject `json:"measures,omitempty"` + Issues *sonargo.IssuesSearchObject `json:"issues,omitempty"` + GeneralAction *devops.GeneralAction `json:"generalAction,omitempty"` + Task *sonargo.CeTaskObject `json:"task,omitempty"` +} + +func (s *sonarQube) GetSonarResultsByTaskIds(taskIds ...string) ([]*SonarStatus, error) { + sonarStatuses := make([]*SonarStatus, 0) + for _, taskId := range taskIds { + sonarStatus := &SonarStatus{} + taskOptions := &sonargo.CeTaskOption{ + Id: taskId, + } + ceTask, _, err := s.client.Ce.Task(taskOptions) + if err != nil { + klog.Errorf("get sonar task error [%+v]", err) + continue + } + sonarStatus.Task = ceTask + measuresComponentOption := &sonargo.MeasuresComponentOption{ + Component: ceTask.Task.ComponentKey, + AdditionalFields: SonarAdditionalFields, + MetricKeys: SonarMetricKeys, + } + measures, _, err := s.client.Measures.Component(measuresComponentOption) + if err != nil { + klog.Errorf("get sonar task error [%+v]", err) + continue + } + sonarStatus.Measures = measures + + issuesSearchOption := &sonargo.IssuesSearchOption{ + AdditionalFields: "_all", + ComponentKeys: ceTask.Task.ComponentKey, + Resolved: "false", + Ps: "10", + S: "FILE_LINE", + Facets: "severities,types", + } + issuesSearch, _, err := s.client.Issues.Search(issuesSearchOption) + sonarStatus.Issues = issuesSearch + sonarStatuses = append(sonarStatuses, sonarStatus) + } + return sonarStatuses, nil } -- GitLab