devops refactor (#1739)

* add devops client interface

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

* direct return jenkins

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

* add some interface

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

* update

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

* update interface

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

* update

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

* credential op structs

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

* status

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

* update interface

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

* credential handler

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

* update devopsoperator func

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

* get build sonar

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

* sonar handler

* mv code to cilent

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

* update

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

* project member handler

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

* update pipeline operator interface

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

* add tenant devops handler

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

* update merge

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

* clean

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

* fmt

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

* update ListPipelineRuns

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

* complate pipelineOperator interface

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

* update HttpParameters

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

* add pipeline steps interface

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

* update pipeline GetNodesDetail

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

* add s2i api

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

* add branch pipeline interface and update handler

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

* add scan branch interface and update handler

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

* add common interface and update handler

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

* add SCM interface and update handler

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

* add handler

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

* add fake s3

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

* add webhook&check interface and update handler

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

* clean

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

* clean

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

* format

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

* add some func

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

* clean code

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

* implement interface

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

* fix interface GetBranchArtifacts

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

* add s2ibinary upload test

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

* tenant devops

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

* update tenant

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

* fake

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

* add some unit test

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

* add devops tenant handler

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

* status

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

* status

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

* status

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

* update fake test

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

* update unit test and fake data

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

* update

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

View File

@@ -7,7 +7,7 @@ import (
cliflag "k8s.io/component-base/cli/flag"
"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{

View File

@@ -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(),

2
go.mod
View File

@@ -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

25
go.sum
View File

@@ -18,8 +18,6 @@ github.com/Azure/go-autorest/tracing v0.5.0 h1:TRn4WjSnkcSy5AEG3pnbtFSwNtwzjr4VY
github.com/Azure/go-autorest/tracing v0.5.0/go.mod h1:r/s2XiOKccPW3HrqB+W0TQzfbtp2fGCgRFtBroKn4Dk=
github.com/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=

View File

@@ -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"`
}
}

View File

@@ -1,165 +0,0 @@
/*
Copyright 2019 The KubeSphere Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package devops
import (
"github.com/emicklei/go-restful"
"k8s.io/klog"
"kubesphere.io/kubesphere/pkg/constants"
"kubesphere.io/kubesphere/pkg/models/devops"
"kubesphere.io/kubesphere/pkg/server/errors"
"net/http"
)
func CreateDevOpsProjectCredentialHandler(request *restful.Request, resp *restful.Response) {
projectId := request.PathParameter("devops")
username := request.HeaderParameter(constants.UserNameHeader)
var credential *devops.JenkinsCredential
err := request.ReadEntity(&credential)
if err != nil {
klog.Errorf("%+v", err)
errors.ParseSvcErr(restful.NewError(http.StatusBadRequest, err.Error()), resp)
return
}
err = devops.CheckProjectUserInRole(username, projectId, []string{devops.ProjectOwner, devops.ProjectMaintainer})
if err != nil {
klog.Errorf("%+v", err)
errors.ParseSvcErr(restful.NewError(http.StatusForbidden, err.Error()), resp)
return
}
credentialId, err := devops.CreateProjectCredential(projectId, username, credential)
if err != nil {
klog.Errorf("%+v", err)
errors.ParseSvcErr(err, resp)
return
}
resp.WriteAsJson(struct {
Name string `json:"name"`
}{Name: credentialId})
return
}
func UpdateDevOpsProjectCredentialHandler(request *restful.Request, resp *restful.Response) {
projectId := request.PathParameter("devops")
username := request.HeaderParameter(constants.UserNameHeader)
credentialId := request.PathParameter("credential")
var credential *devops.JenkinsCredential
err := request.ReadEntity(&credential)
if err != nil {
klog.Errorf("%+v", err)
errors.ParseSvcErr(restful.NewError(http.StatusBadRequest, err.Error()), resp)
return
}
err = devops.CheckProjectUserInRole(username, projectId, []string{devops.ProjectOwner, devops.ProjectMaintainer})
if err != nil {
klog.Errorf("%+v", err)
errors.ParseSvcErr(restful.NewError(http.StatusForbidden, err.Error()), resp)
return
}
credentialId, err = devops.UpdateProjectCredential(projectId, credentialId, credential)
if err != nil {
klog.Errorf("%+v", err)
errors.ParseSvcErr(err, resp)
return
}
resp.WriteAsJson(struct {
Name string `json:"name"`
}{Name: credentialId})
return
}
func DeleteDevOpsProjectCredentialHandler(request *restful.Request, resp *restful.Response) {
projectId := request.PathParameter("devops")
username := request.HeaderParameter(constants.UserNameHeader)
credentialId := request.PathParameter("credential")
var credential *devops.JenkinsCredential
err := request.ReadEntity(&credential)
if err != nil {
klog.Errorf("%+v", err)
errors.ParseSvcErr(restful.NewError(http.StatusBadRequest, err.Error()), resp)
return
}
err = devops.CheckProjectUserInRole(username, projectId, []string{devops.ProjectOwner, devops.ProjectMaintainer})
if err != nil {
klog.Errorf("%+v", err)
errors.ParseSvcErr(restful.NewError(http.StatusForbidden, err.Error()), resp)
return
}
credentialId, err = devops.DeleteProjectCredential(projectId, credentialId, credential)
if err != nil {
klog.Errorf("%+v", err)
errors.ParseSvcErr(err, resp)
return
}
resp.WriteAsJson(struct {
Name string `json:"name"`
}{Name: credentialId})
return
}
func GetDevOpsProjectCredentialHandler(request *restful.Request, resp *restful.Response) {
projectId := request.PathParameter("devops")
username := request.HeaderParameter(constants.UserNameHeader)
credentialId := request.PathParameter("credential")
getContent := request.QueryParameter("content")
domain := request.QueryParameter("domain")
err := devops.CheckProjectUserInRole(username, projectId, []string{devops.ProjectOwner, devops.ProjectMaintainer})
if err != nil {
klog.Errorf("%+v", err)
errors.ParseSvcErr(restful.NewError(http.StatusForbidden, err.Error()), resp)
return
}
response, err := devops.GetProjectCredential(projectId, credentialId, domain, getContent)
if err != nil {
klog.Errorf("%+v", err)
errors.ParseSvcErr(err, resp)
return
}
resp.WriteAsJson(response)
return
}
func GetDevOpsProjectCredentialsHandler(request *restful.Request, resp *restful.Response) {
projectId := request.PathParameter("devops")
username := request.HeaderParameter(constants.UserNameHeader)
domain := request.QueryParameter("domain")
err := devops.CheckProjectUserInRole(username, projectId, []string{devops.ProjectOwner, devops.ProjectMaintainer})
if err != nil {
klog.Errorf("%+v", err)
errors.ParseSvcErr(restful.NewError(http.StatusForbidden, err.Error()), resp)
return
}
jenkinsCredentials, err := devops.GetProjectCredentials(projectId, domain)
if err != nil {
klog.Errorf("%+v", err)
errors.ParseSvcErr(err, resp)
return
}
resp.WriteAsJson(jenkinsCredentials)
return
}

View File

@@ -0,0 +1,54 @@
/*
*
* Copyright 2019 The KubeSphere Authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* /
*/
package openpitrix
import (
"github.com/emicklei/go-restful"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
"kubesphere.io/kubesphere/pkg/models/openpitrix/attachment"
"kubesphere.io/kubesphere/pkg/server/errors"
"kubesphere.io/kubesphere/pkg/simple/client"
"net/http"
)
func DescribeAttachment(req *restful.Request, resp *restful.Response) {
attachmentId := req.PathParameter("attachment")
fileName := req.QueryParameter("filename")
result, err := attachment.DescribeAttachment(attachmentId)
// file raw
if fileName != "" {
data := result.AttachmentContent[fileName]
resp.Write(data)
resp.Header().Set("Content-Type", "text/plain")
return
}
if _, notEnabled := err.(client.ClientSetNotEnabledError); notEnabled {
resp.WriteHeaderAndEntity(http.StatusNotImplemented, errors.Wrap(err))
return
}
if status.Code(err) == codes.NotFound {
resp.WriteHeaderAndEntity(http.StatusNotFound, errors.Wrap(err))
return
}
resp.WriteEntity(result)
}

View File

@@ -0,0 +1,328 @@
/*
Copyright 2019 The KubeSphere Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package tenant
import (
"github.com/emicklei/go-restful"
"k8s.io/api/core/v1"
rbacv1 "k8s.io/api/rbac/v1"
k8serr "k8s.io/apimachinery/pkg/api/errors"
"k8s.io/apimachinery/pkg/util/net"
"k8s.io/klog"
devopsv1alpha2 "kubesphere.io/kubesphere/pkg/api/devops/v1alpha2"
loggingv1alpha2 "kubesphere.io/kubesphere/pkg/api/logging/v1alpha2"
"kubesphere.io/kubesphere/pkg/apis/tenant/v1alpha1"
"kubesphere.io/kubesphere/pkg/apiserver/logging"
"kubesphere.io/kubesphere/pkg/constants"
"kubesphere.io/kubesphere/pkg/models/iam"
"kubesphere.io/kubesphere/pkg/models/metrics"
"kubesphere.io/kubesphere/pkg/models/resources/v1alpha2"
"kubesphere.io/kubesphere/pkg/models/tenant"
"kubesphere.io/kubesphere/pkg/models/workspaces"
"kubesphere.io/kubesphere/pkg/server/errors"
"kubesphere.io/kubesphere/pkg/server/params"
"kubesphere.io/kubesphere/pkg/utils/sliceutil"
"net/http"
"strings"
)
func ListWorkspaceRules(req *restful.Request, resp *restful.Response) {
workspace := req.PathParameter("workspace")
username := req.HeaderParameter(constants.UserNameHeader)
rules, err := iam.GetUserWorkspaceSimpleRules(workspace, username)
if err != nil {
resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err))
return
}
resp.WriteAsJson(rules)
}
func ListWorkspaces(req *restful.Request, resp *restful.Response) {
username := req.HeaderParameter(constants.UserNameHeader)
conditions, err := params.ParseConditions(req.QueryParameter(params.ConditionsParam))
orderBy := req.QueryParameter(params.OrderByParam)
limit, offset := params.ParsePaging(req.QueryParameter(params.PagingParam))
reverse := params.ParseReverse(req)
if orderBy == "" {
orderBy = v1alpha2.CreateTime
reverse = true
}
if err != nil {
resp.WriteHeaderAndEntity(http.StatusBadRequest, errors.Wrap(err))
return
}
result, err := tenant.ListWorkspaces(username, conditions, orderBy, reverse, limit, offset)
if err != nil {
resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err))
return
}
resp.WriteAsJson(result)
}
func DescribeWorkspace(req *restful.Request, resp *restful.Response) {
username := req.HeaderParameter(constants.UserNameHeader)
workspaceName := req.PathParameter("workspace")
result, err := tenant.DescribeWorkspace(username, workspaceName)
if err != nil {
klog.Errorf("describe workspace failed: %+v", err)
if k8serr.IsNotFound(err) {
resp.WriteHeaderAndEntity(http.StatusNotFound, errors.Wrap(err))
} else {
resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err))
}
return
}
resp.WriteAsJson(result)
}
func ListNamespacesByUsername(req *restful.Request, resp *restful.Response) {
ListNamespaces(req, resp)
}
func ListNamespaces(req *restful.Request, resp *restful.Response) {
workspace := req.PathParameter("workspace")
username := req.PathParameter("member")
// /workspaces/{workspace}/members/{username}/namespaces
if username == "" {
// /workspaces/{workspace}/namespaces
username = req.HeaderParameter(constants.UserNameHeader)
}
conditions, err := params.ParseConditions(req.QueryParameter(params.ConditionsParam))
orderBy := req.QueryParameter(params.OrderByParam)
limit, offset := params.ParsePaging(req.QueryParameter(params.PagingParam))
reverse := params.ParseReverse(req)
if err != nil {
resp.WriteHeaderAndEntity(http.StatusBadRequest, errors.Wrap(err))
return
}
conditions.Match[constants.WorkspaceLabelKey] = workspace
result, err := tenant.ListNamespaces(username, conditions, orderBy, reverse, limit, offset)
if err != nil {
resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err))
return
}
namespaces := make([]*v1.Namespace, 0)
for _, item := range result.Items {
namespaces = append(namespaces, item.(*v1.Namespace).DeepCopy())
}
namespaces = metrics.GetNamespacesWithMetrics(namespaces)
items := make([]interface{}, 0)
for _, item := range namespaces {
items = append(items, item)
}
result.Items = items
resp.WriteAsJson(result)
}
func CreateNamespace(req *restful.Request, resp *restful.Response) {
workspaceName := req.PathParameter("workspace")
username := req.HeaderParameter(constants.UserNameHeader)
var namespace v1.Namespace
err := req.ReadEntity(&namespace)
if err != nil {
resp.WriteHeaderAndEntity(http.StatusBadRequest, errors.Wrap(err))
return
}
workspace, err := tenant.GetWorkspace(workspaceName)
err = checkResourceQuotas(workspace)
if err != nil {
resp.WriteHeaderAndEntity(http.StatusForbidden, errors.Wrap(err))
return
}
if err != nil {
if k8serr.IsNotFound(err) {
resp.WriteHeaderAndEntity(http.StatusForbidden, errors.Wrap(err))
} else {
resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err))
}
return
}
created, err := tenant.CreateNamespace(workspaceName, &namespace, username)
if err != nil {
if k8serr.IsAlreadyExists(err) {
resp.WriteHeaderAndEntity(http.StatusConflict, err)
} else {
resp.WriteHeaderAndEntity(http.StatusInternalServerError, err)
}
return
}
resp.WriteAsJson(created)
}
func DeleteNamespace(req *restful.Request, resp *restful.Response) {
workspaceName := req.PathParameter("workspace")
namespaceName := req.PathParameter("namespace")
err := workspaces.DeleteNamespace(workspaceName, namespaceName)
if err != nil {
resp.WriteHeaderAndEntity(http.StatusBadRequest, errors.Wrap(err))
return
}
resp.WriteAsJson(errors.None)
}
func checkResourceQuotas(wokrspace *v1alpha1.Workspace) error {
return nil
}
func ListNamespaceRules(req *restful.Request, resp *restful.Response) {
namespace := req.PathParameter("namespace")
username := req.HeaderParameter(constants.UserNameHeader)
rules, err := iam.GetUserNamespaceSimpleRules(namespace, username)
if err != nil {
resp.WriteError(http.StatusInternalServerError, err)
return
}
resp.WriteAsJson(rules)
}
func LogQuery(req *restful.Request, resp *restful.Response) {
operation := req.QueryParameter("operation")
req, err := regenerateLoggingRequest(req)
switch {
case err != nil:
resp.WriteHeaderAndEntity(http.StatusInternalServerError, errors.Wrap(err))
case req != nil:
logging.LoggingQueryCluster(req, resp)
default:
if operation == "export" {
resp.Header().Set(restful.HEADER_ContentType, "text/plain")
resp.Header().Set("Content-Disposition", "attachment")
resp.Write(nil)
} else {
resp.WriteAsJson(loggingv1alpha2.QueryResult{Read: new(loggingv1alpha2.ReadResult)})
}
}
}
// override namespace query conditions
func regenerateLoggingRequest(req *restful.Request) (*restful.Request, error) {
username := req.HeaderParameter(constants.UserNameHeader)
// regenerate the request for log query
newUrl := net.FormatURL("http", "127.0.0.1", 80, "/kapis/logging.kubesphere.io/v1alpha2/cluster")
values := req.Request.URL.Query()
clusterRules, err := iam.GetUserClusterRules(username)
if err != nil {
klog.Errorln(err)
return nil, err
}
hasClusterLogAccess := iam.RulesMatchesRequired(clusterRules, rbacv1.PolicyRule{Verbs: []string{"get"}, Resources: []string{"*"}, APIGroups: []string{"logging.kubesphere.io"}})
// if the user is not a cluster admin
if !hasClusterLogAccess {
queryNamespaces := strings.Split(req.QueryParameter("namespaces"), ",")
// then the user can only view logs of namespaces he belongs to
namespaces := make([]string, 0)
roles, err := iam.GetUserRoles("", username)
if err != nil {
klog.Errorln(err)
return nil, err
}
for _, role := range roles {
if !sliceutil.HasString(namespaces, role.Namespace) && iam.RulesMatchesRequired(role.Rules, rbacv1.PolicyRule{Verbs: []string{"get"}, Resources: []string{"*"}, APIGroups: []string{"logging.kubesphere.io"}}) {
namespaces = append(namespaces, role.Namespace)
}
}
// if the user belongs to no namespace
// then no log visible
if len(namespaces) == 0 {
return nil, nil
} else if len(queryNamespaces) == 1 && queryNamespaces[0] == "" {
values.Set("namespaces", strings.Join(namespaces, ","))
} else {
inter := intersection(queryNamespaces, namespaces)
if len(inter) == 0 {
return nil, nil
}
values.Set("namespaces", strings.Join(inter, ","))
}
}
newUrl.RawQuery = values.Encode()
// forward the request to logging model
newHttpRequest, _ := http.NewRequest(http.MethodGet, newUrl.String(), nil)
return restful.NewRequest(newHttpRequest), nil
}
func intersection(s1, s2 []string) (inter []string) {
hash := make(map[string]bool)
for _, e := range s1 {
hash[e] = true
}
for _, e := range s2 {
// If elements present in the hashmap then append intersection list.
if hash[e] {
inter = append(inter, e)
}
}
//Remove dups from slice.
inter = removeDups(inter)
return
}
//Remove dups from slice.
func removeDups(elements []string) (nodups []string) {
encountered := make(map[string]bool)
for _, element := range elements {
if !encountered[element] {
nodups = append(nodups, element)
encountered[element] = true
}
}
return
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

File diff suppressed because it is too large Load Diff

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -11,7 +11,7 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
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)

View File

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

View File

@@ -11,30 +11,30 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
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)

View File

@@ -0,0 +1,122 @@
/*
Copyright 2019 The KubeSphere Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package v1alpha2
import (
"github.com/emicklei/go-restful"
"k8s.io/klog"
"kubesphere.io/kubesphere/pkg/constants"
"kubesphere.io/kubesphere/pkg/server/errors"
"kubesphere.io/kubesphere/pkg/simple/client/devops"
"net/http"
)
func (h ProjectPipelineHandler) CreateDevOpsProjectCredentialHandler(request *restful.Request, resp *restful.Response) {
projectId := request.PathParameter("devops")
username := request.HeaderParameter(constants.UserNameHeader)
var credential *devops.Credential
err := request.ReadEntity(&credential)
if err != nil {
klog.Errorf("%+v", err)
errors.ParseSvcErr(restful.NewError(http.StatusBadRequest, err.Error()), resp)
return
}
credentialId, err := h.projectCredentialOperator.CreateProjectCredential(projectId, username, credential)
if err != nil {
klog.Errorf("%+v", err)
errors.ParseSvcErr(err, resp)
return
}
resp.WriteAsJson(struct {
Name string `json:"name"`
}{Name: credentialId})
return
}
func (h ProjectPipelineHandler) UpdateDevOpsProjectCredentialHandler(request *restful.Request, resp *restful.Response) {
projectId := request.PathParameter("devops")
credentialId := request.PathParameter("credential")
var credential *devops.Credential
err := request.ReadEntity(&credential)
if err != nil {
klog.Errorf("%+v", err)
errors.ParseSvcErr(restful.NewError(http.StatusBadRequest, err.Error()), resp)
return
}
credentialId, err = h.projectCredentialOperator.UpdateProjectCredential(projectId, credentialId, credential)
if err != nil {
klog.Errorf("%+v", err)
errors.ParseSvcErr(err, resp)
return
}
resp.WriteAsJson(struct {
Name string `json:"name"`
}{Name: credentialId})
return
}
func (h ProjectPipelineHandler) DeleteDevOpsProjectCredentialHandler(request *restful.Request, resp *restful.Response) {
projectId := request.PathParameter("devops")
credentialId := request.PathParameter("credential")
credentialId, err := h.projectCredentialOperator.DeleteProjectCredential(projectId, credentialId)
if err != nil {
klog.Errorf("%+v", err)
errors.ParseSvcErr(err, resp)
return
}
resp.WriteAsJson(struct {
Name string `json:"name"`
}{Name: credentialId})
return
}
func (h ProjectPipelineHandler) GetDevOpsProjectCredentialHandler(request *restful.Request, resp *restful.Response) {
projectId := request.PathParameter("devops")
credentialId := request.PathParameter("credential")
getContent := request.QueryParameter("content")
response, err := h.projectCredentialOperator.GetProjectCredential(projectId, credentialId, getContent)
if err != nil {
klog.Errorf("%+v", err)
errors.ParseSvcErr(err, resp)
return
}
resp.WriteAsJson(response)
return
}
func (h ProjectPipelineHandler) GetDevOpsProjectCredentialsHandler(request *restful.Request, resp *restful.Response) {
projectId := request.PathParameter("devops")
jenkinsCredentials, err := h.projectCredentialOperator.GetProjectCredentials(projectId)
if err != nil {
klog.Errorf("%+v", err)
errors.ParseSvcErr(err, resp)
return
}
resp.WriteAsJson(jenkinsCredentials)
return
}

View File

@@ -11,18 +11,18 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
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)
}

File diff suppressed because it is too large Load Diff

View File

@@ -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)

View File

@@ -0,0 +1,154 @@
/*
Copyright 2019 The KubeSphere Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package v1alpha2
import (
"github.com/emicklei/go-restful"
"k8s.io/klog"
"kubesphere.io/kubesphere/pkg/api"
devopsv1alpha2 "kubesphere.io/kubesphere/pkg/api/devops/v1alpha2"
"kubesphere.io/kubesphere/pkg/constants"
"kubesphere.io/kubesphere/pkg/server/errors"
"kubesphere.io/kubesphere/pkg/server/params"
"net/http"
)
func (h *tenantHandler) DeleteDevOpsProjectHandler(req *restful.Request, resp *restful.Response) {
projectId := req.PathParameter("devops")
workspaceName := req.PathParameter("workspace")
username := req.HeaderParameter(constants.UserNameHeader)
_, err := h.tenant.GetWorkspace(workspaceName)
if err != nil {
klog.Errorf("%+v", err)
errors.ParseSvcErr(restful.NewError(http.StatusBadRequest, err.Error()), resp)
return
}
err = h.tenant.DeleteDevOpsProject(projectId, username)
if err != nil {
klog.Errorf("%+v", err)
errors.ParseSvcErr(err, resp)
return
}
resp.WriteAsJson(errors.None)
}
func (h *tenantHandler) CreateDevOpsProjectHandler(req *restful.Request, resp *restful.Response) {
workspaceName := req.PathParameter("workspace")
username := req.HeaderParameter(constants.UserNameHeader)
var devops devopsv1alpha2.DevOpsProject
err := req.ReadEntity(&devops)
if err != nil {
klog.Infof("%+v", err)
errors.ParseSvcErr(restful.NewError(http.StatusBadRequest, err.Error()), resp)
return
}
klog.Infoln("create workspace", username, workspaceName, devops)
project, err := h.tenant.CreateDevOpsProject(username, workspaceName, &devops)
if err != nil {
klog.Errorf("%+v", err)
errors.ParseSvcErr(err, resp)
return
}
resp.WriteAsJson(project)
}
func (h *tenantHandler) GetDevOpsProjectsCountHandler(req *restful.Request, resp *restful.Response) {
username := req.HeaderParameter(constants.UserNameHeader)
result, err := h.tenant.GetDevOpsProjectsCount(username)
if err != nil {
klog.Errorf("%+v", err)
errors.ParseSvcErr(err, resp)
return
}
resp.WriteAsJson(struct {
Count uint32 `json:"count"`
}{Count: result})
}
func (h *tenantHandler) ListDevOpsProjectsHandler(req *restful.Request, resp *restful.Response) {
workspace := req.PathParameter("workspace")
username := req.PathParameter("member")
if username == "" {
username = req.HeaderParameter(constants.UserNameHeader)
}
orderBy := req.QueryParameter(params.OrderByParam)
reverse := params.GetBoolValueWithDefault(req, params.ReverseParam, false)
limit, offset := params.ParsePaging(req)
conditions, err := params.ParseConditions(req)
if err != nil {
klog.Errorf("%+v", err)
errors.ParseSvcErr(restful.NewError(http.StatusBadRequest, err.Error()), resp)
return
}
result, err := h.tenant.ListDevOpsProjects(workspace, username, conditions, orderBy, reverse, limit, offset)
if err != nil {
klog.Errorf("%+v", err)
errors.ParseSvcErr(err, resp)
return
}
resp.WriteAsJson(result)
}
func (h *tenantHandler) ListDevOpsRules(req *restful.Request, resp *restful.Response) {
devops := req.PathParameter("devops")
username := req.HeaderParameter(constants.UserNameHeader)
rules, err := h.tenant.GetUserDevOpsSimpleRules(username, devops)
if err != nil {
klog.Errorf("%+v", err)
errors.ParseSvcErr(err, resp)
return
}
resp.WriteAsJson(rules)
}
func (h *tenantHandler) ListDevopsRules(req *restful.Request, resp *restful.Response) {
devops := req.PathParameter("devops")
username := req.HeaderParameter(constants.UserNameHeader)
rules, err := h.tenant.GetUserDevOpsSimpleRules(username, devops)
if err != nil {
api.HandleInternalError(resp, err)
return
}
resp.WriteAsJson(rules)
}

View File

@@ -8,7 +8,6 @@ import (
"k8s.io/apimachinery/pkg/util/net"
"k8s.io/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)

View File

@@ -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{}).

View File

@@ -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)
}
t.terminaler.HandleSession(shell, namespace, podName, containerName, conn)
}

View File

@@ -14,13 +14,7 @@ limitations under the License.
package devops
import (
"fmt"
"github.com/fatih/structs"
"k8s.io/klog"
"kubesphere.io/kubesphere/pkg/db"
"kubesphere.io/kubesphere/pkg/gojenkins"
"kubesphere.io/kubesphere/pkg/simple/client"
"kubesphere.io/kubesphere/pkg/utils/reflectutils"
"kubesphere.io/kubesphere/pkg/utils/stringutils"
)
@@ -67,298 +61,3 @@ const (
const (
KS_ADMIN = "admin"
)
const (
ProjectOwner = "owner"
ProjectMaintainer = "maintainer"
ProjectDeveloper = "developer"
ProjectReporter = "reporter"
)
const (
JenkinsAllUserRoleName = "kubesphere-user"
)
type Role struct {
Name string `json:"name" description:"role's name e.g. owner'"`
Description string `json:"description" description:"role 's description'"`
}
var DefaultRoles = []*Role{
{
Name: ProjectOwner,
Description: "Owner have access to do all the operations of a DevOps project and own the highest permissions as well.",
},
{
Name: ProjectMaintainer,
Description: "Maintainer have access to manage pipeline and credential configuration in a DevOps project.",
},
{
Name: ProjectDeveloper,
Description: "Developer is able to view and trigger the pipeline.",
},
{
Name: ProjectReporter,
Description: "Reporter is only allowed to view the status of the pipeline.",
},
}
var AllRoleSlice = []string{ProjectDeveloper, ProjectReporter, ProjectMaintainer, ProjectOwner}
var JenkinsOwnerProjectPermissionIds = &gojenkins.ProjectPermissionIds{
CredentialCreate: true,
CredentialDelete: true,
CredentialManageDomains: true,
CredentialUpdate: true,
CredentialView: true,
ItemBuild: true,
ItemCancel: true,
ItemConfigure: true,
ItemCreate: true,
ItemDelete: true,
ItemDiscover: true,
ItemMove: true,
ItemRead: true,
ItemWorkspace: true,
RunDelete: true,
RunReplay: true,
RunUpdate: true,
SCMTag: true,
}
var JenkinsProjectPermissionMap = map[string]gojenkins.ProjectPermissionIds{
ProjectOwner: {
CredentialCreate: true,
CredentialDelete: true,
CredentialManageDomains: true,
CredentialUpdate: true,
CredentialView: true,
ItemBuild: true,
ItemCancel: true,
ItemConfigure: true,
ItemCreate: true,
ItemDelete: true,
ItemDiscover: true,
ItemMove: true,
ItemRead: true,
ItemWorkspace: true,
RunDelete: true,
RunReplay: true,
RunUpdate: true,
SCMTag: true,
},
ProjectMaintainer: {
CredentialCreate: true,
CredentialDelete: true,
CredentialManageDomains: true,
CredentialUpdate: true,
CredentialView: true,
ItemBuild: true,
ItemCancel: true,
ItemConfigure: false,
ItemCreate: true,
ItemDelete: false,
ItemDiscover: true,
ItemMove: false,
ItemRead: true,
ItemWorkspace: true,
RunDelete: true,
RunReplay: true,
RunUpdate: true,
SCMTag: true,
},
ProjectDeveloper: {
CredentialCreate: false,
CredentialDelete: false,
CredentialManageDomains: false,
CredentialUpdate: false,
CredentialView: false,
ItemBuild: true,
ItemCancel: true,
ItemConfigure: false,
ItemCreate: false,
ItemDelete: false,
ItemDiscover: true,
ItemMove: false,
ItemRead: true,
ItemWorkspace: true,
RunDelete: true,
RunReplay: true,
RunUpdate: true,
SCMTag: false,
},
ProjectReporter: {
CredentialCreate: false,
CredentialDelete: false,
CredentialManageDomains: false,
CredentialUpdate: false,
CredentialView: false,
ItemBuild: false,
ItemCancel: false,
ItemConfigure: false,
ItemCreate: false,
ItemDelete: false,
ItemDiscover: true,
ItemMove: false,
ItemRead: true,
ItemWorkspace: false,
RunDelete: false,
RunReplay: false,
RunUpdate: false,
SCMTag: false,
},
}
var JenkinsPipelinePermissionMap = map[string]gojenkins.ProjectPermissionIds{
ProjectOwner: {
CredentialCreate: true,
CredentialDelete: true,
CredentialManageDomains: true,
CredentialUpdate: true,
CredentialView: true,
ItemBuild: true,
ItemCancel: true,
ItemConfigure: true,
ItemCreate: true,
ItemDelete: true,
ItemDiscover: true,
ItemMove: true,
ItemRead: true,
ItemWorkspace: true,
RunDelete: true,
RunReplay: true,
RunUpdate: true,
SCMTag: true,
},
ProjectMaintainer: {
CredentialCreate: true,
CredentialDelete: true,
CredentialManageDomains: true,
CredentialUpdate: true,
CredentialView: true,
ItemBuild: true,
ItemCancel: true,
ItemConfigure: true,
ItemCreate: true,
ItemDelete: true,
ItemDiscover: true,
ItemMove: true,
ItemRead: true,
ItemWorkspace: true,
RunDelete: true,
RunReplay: true,
RunUpdate: true,
SCMTag: true,
},
ProjectDeveloper: {
CredentialCreate: false,
CredentialDelete: false,
CredentialManageDomains: false,
CredentialUpdate: false,
CredentialView: false,
ItemBuild: true,
ItemCancel: true,
ItemConfigure: false,
ItemCreate: false,
ItemDelete: false,
ItemDiscover: true,
ItemMove: false,
ItemRead: true,
ItemWorkspace: true,
RunDelete: true,
RunReplay: true,
RunUpdate: true,
SCMTag: false,
},
ProjectReporter: {
CredentialCreate: false,
CredentialDelete: false,
CredentialManageDomains: false,
CredentialUpdate: false,
CredentialView: false,
ItemBuild: false,
ItemCancel: false,
ItemConfigure: false,
ItemCreate: false,
ItemDelete: false,
ItemDiscover: true,
ItemMove: false,
ItemRead: true,
ItemWorkspace: false,
RunDelete: false,
RunReplay: false,
RunUpdate: false,
SCMTag: false,
},
}
func GetProjectRoleName(projectId, role string) string {
return fmt.Sprintf("%s-%s-project", projectId, role)
}
func GetPipelineRoleName(projectId, role string) string {
return fmt.Sprintf("%s-%s-pipeline", projectId, role)
}
func GetProjectRolePattern(projectId string) string {
return fmt.Sprintf("^%s$", projectId)
}
func GetPipelineRolePattern(projectId string) string {
return fmt.Sprintf("^%s/.*", projectId)
}
func CheckProjectUserInRole(username, projectId string, roles []string) error {
if username == KS_ADMIN {
return nil
}
dbconn, err := client.ClientSets().MySQL()
if err != nil {
if _, ok := err.(client.ClientSetNotEnabledError); ok {
klog.Error("mysql is not enabled")
} else {
klog.Error("error creating mysql client", err)
}
return nil
}
membership := &DevOpsProjectMembership{}
err = dbconn.Select(DevOpsProjectMembershipColumns...).
From(DevOpsProjectMembershipTableName).
Where(db.And(
db.Eq(DevOpsProjectMembershipUsernameColumn, username),
db.Eq(DevOpsProjectMembershipProjectIdColumn, projectId))).LoadOne(membership)
if err != nil {
return err
}
if !reflectutils.In(membership.Role, roles) {
return fmt.Errorf("user [%s] in project [%s] role is not in %s", username, projectId, roles)
}
return nil
}
func GetProjectUserRole(username, projectId string) (string, error) {
if username == KS_ADMIN {
return ProjectOwner, nil
}
dbconn, err := client.ClientSets().MySQL()
if err != nil {
if _, ok := err.(client.ClientSetNotEnabledError); ok {
klog.Error("mysql is not enabled")
} else {
klog.Error("error creating mysql client", err)
}
return "", err
}
membership := &DevOpsProjectMembership{}
err = dbconn.Select(DevOpsProjectMembershipColumns...).
From(DevOpsProjectMembershipTableName).
Where(db.And(
db.Eq(DevOpsProjectMembershipUsernameColumn, username),
db.Eq(DevOpsProjectMembershipProjectIdColumn, projectId))).LoadOne(membership)
if err != nil {
return "", err
}
return membership.Role, nil
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,40 +1,113 @@
package devops
import (
"kubesphere.io/kubesphere/pkg/simple/client/devops"
"kubesphere.io/kubesphere/pkg/simple/client/devops/fake"
"net/http"
"testing"
)
func Test_parseCronJobTime(t *testing.T) {
type Except struct {
Last string
Next string
const baseUrl = "http://127.0.0.1/kapis/devops.kubesphere.io/v1alpha2/"
func TestGetNodesDetail(t *testing.T) {
fakeData := make(map[string]interface{})
PipelineRunNodes := []devops.PipelineRunNodes{
{
DisplayName: "Deploy to Kubernetes",
ID: "1",
Result: "SUCCESS",
},
{
DisplayName: "Deploy to Kubernetes",
ID: "2",
Result: "SUCCESS",
},
{
DisplayName: "Deploy to Kubernetes",
ID: "3",
Result: "SUCCESS",
},
}
Items := []struct {
Input string
Expected Except
}{
{"上次运行的时间 Tuesday, September 10, 2019 8:59:09 AM UTC; 下次运行的时间 Tuesday, September 10, 2019 9:14:09 AM UTC.", Except{Last: "2019-09-10T08:59:09Z", Next: "2019-09-10T09:14:09Z"}},
{"上次运行的时间 Thursday, January 3, 2019 11:56:30 PM UTC; 下次运行的时间 Friday, January 3, 2020 12:11:30 AM UTC.", Except{Last: "2019-01-03T23:56:30Z", Next: "2020-01-03T00:11:30Z"}},
{"上次运行的时间 Tuesday, September 10, 2019 8:41:34 AM UTC; 下次运行的时间 Tuesday, September 10, 2019 9:41:34 AM UTC.", Except{Last: "2019-09-10T08:41:34Z", Next: "2019-09-10T09:41:34Z"}},
{"上次运行的时间 Tuesday, September 10, 2019 9:15:26 AM UTC; 下次运行的时间 Tuesday, September 10, 2019 10:03:26 AM UTC.", Except{Last: "2019-09-10T09:15:26Z", Next: "2019-09-10T10:03:26Z"}},
{"Would last have run at Tuesday, September 10, 2019 9:15:26 AM UTC; would next run at Tuesday, September 10, 2019 10:03:26 AM UTC.", Except{Last: "2019-09-10T09:15:26Z", Next: "2019-09-10T10:03:26Z"}},
{"Would last have run at Tuesday, September 10, 2019 8:41:34 AM UTC; would next run at Tuesday, September 10, 2019 9:41:34 AM UTC.", Except{Last: "2019-09-10T08:41:34Z", Next: "2019-09-10T09:41:34Z"}},
NodeSteps := []devops.NodeSteps{
{
DisplayName: "Deploy to Kubernetes",
ID: "1",
Result: "SUCCESS",
},
}
for _, item := range Items {
last, next, err := parseCronJobTime(item.Input)
if err != nil {
t.Fatalf("should not get error %+v", err)
}
fakeData["project1-pipeline1-run1"] = PipelineRunNodes
fakeData["project1-pipeline1-run1-1"] = NodeSteps
fakeData["project1-pipeline1-run1-2"] = NodeSteps
fakeData["project1-pipeline1-run1-3"] = NodeSteps
if last != item.Expected.Last {
t.Errorf("got %#v, expected %#v", last, item.Expected.Last)
}
devopsClient := fake.NewFakeDevops(fakeData)
if next != item.Expected.Next {
t.Errorf("got %#v, expected %#v", next, item.Expected.Next)
}
devopsOperator := NewDevopsOperator(devopsClient)
httpReq, _ := http.NewRequest(http.MethodGet, baseUrl+"devops/project1/pipelines/pipeline1/runs/run1/nodesdetail/?limit=10000", nil)
nodesDetails, err := devopsOperator.GetNodesDetail("project1", "pipeline1", "run1", httpReq)
if err != nil || nodesDetails == nil {
t.Fatalf("should not get error %+v", err)
}
for _, v := range nodesDetails {
if v.Steps[0].ID == "" {
t.Fatalf("Can not get any step.")
}
}
}
func TestGetBranchNodesDetail(t *testing.T) {
fakeData := make(map[string]interface{})
BranchPipelineRunNodes := []devops.BranchPipelineRunNodes{
{
DisplayName: "Deploy to Kubernetes",
ID: "1",
Result: "SUCCESS",
},
{
DisplayName: "Deploy to Kubernetes",
ID: "2",
Result: "SUCCESS",
},
{
DisplayName: "Deploy to Kubernetes",
ID: "3",
Result: "SUCCESS",
},
}
BranchNodeSteps := []devops.NodeSteps{
{
DisplayName: "Deploy to Kubernetes",
ID: "1",
Result: "SUCCESS",
},
}
fakeData["project1-pipeline1-branch1-run1"] = BranchPipelineRunNodes
fakeData["project1-pipeline1-branch1-run1-1"] = BranchNodeSteps
fakeData["project1-pipeline1-branch1-run1-2"] = BranchNodeSteps
fakeData["project1-pipeline1-branch1-run1-3"] = BranchNodeSteps
devopsClient := fake.NewFakeDevops(fakeData)
devopsOperator := NewDevopsOperator(devopsClient)
httpReq, _ := http.NewRequest(http.MethodGet, baseUrl+"devops/project1/pipelines/pipeline1/branchs/branch1/runs/run1/nodesdetail/?limit=10000", nil)
nodesDetails, err := devopsOperator.GetBranchNodesDetail("project1", "pipeline1", "branch1", "run1", httpReq)
if err != nil || nodesDetails == nil {
t.Fatalf("should not get error %+v", err)
}
for _, v := range nodesDetails {
if v.Steps[0].ID == "" {
t.Fatalf("Can not get any step.")
}
}
}

View File

@@ -13,25 +13,19 @@ limitations under the License.
package devops
import "kubesphere.io/kubesphere/pkg/simple/client/devops"
const (
DevOpsProjectMembershipTableName = "project_membership"
DevOpsProjectMembershipUsernameColumn = "project_membership.username"
DevOpsProjectMembershipProjectIdColumn = "project_membership.project_id"
DevOpsProjectMembershipRoleColumn = "project_membership.role"
ProjectMembershipTableName = "project_membership"
ProjectMembershipUsernameColumn = "project_membership.username"
ProjectMembershipProjectIdColumn = "project_membership.project_id"
ProjectMembershipRoleColumn = "project_membership.role"
)
type DevOpsProjectMembership struct {
Username string `json:"username" description:"Member's usernameusername can uniquely identify a user"`
ProjectId string `json:"project_id" db:"project_id" description:"the DevOps Projects which project membership belongs to"`
Role string `json:"role" description:"DevOps Project membership's role type. e.g. owner '"`
Status string `json:"status" description:"Deprecated, Status of project membership. e.g. active "`
GrantBy string `json:"grand_by,omitempty" description:"Username of the user who assigned the role"`
}
var ProjectMembershipColumns = GetColumnsFromStruct(&devops.ProjectMembership{})
var DevOpsProjectMembershipColumns = GetColumnsFromStruct(&DevOpsProjectMembership{})
func NewDevOpsProjectMemberShip(username, projectId, role, grantBy string) *DevOpsProjectMembership {
return &DevOpsProjectMembership{
func NewDevOpsProjectMemberShip(username, projectId, role, grantBy string) *devops.ProjectMembership {
return &devops.ProjectMembership{
Username: username,
ProjectId: projectId,
Role: role,

View File

@@ -18,59 +18,6 @@ import (
"time"
)
const (
CredentialTypeUsernamePassword = "username_password"
CredentialTypeSsh = "ssh"
CredentialTypeSecretText = "secret_text"
CredentialTypeKubeConfig = "kubeconfig"
)
type JenkinsCredential struct {
Id string `json:"id" description:"Id of Credential, e.g. dockerhub-id"`
Type string `json:"type" description:"Type of Credential, e.g. ssh/kubeconfig"`
DisplayName string `json:"display_name,omitempty" description:"Credential's display name"`
Fingerprint *struct {
FileName string `json:"file_name,omitempty" description:"Credential's display name and description"`
Hash string `json:"hash,omitempty" description:"Credential's hash"`
Usage []*struct {
Name string `json:"name,omitempty" description:"Jenkins pipeline full name"`
Ranges struct {
Ranges []*struct {
Start int `json:"start,omitempty" description:"Start build number"`
End int `json:"end,omitempty" description:"End build number"`
} `json:"ranges,omitempty"`
} `json:"ranges,omitempty" description:"The build number of all pipelines that use this credential"`
} `json:"usage,omitempty" description:"all usage of Credential"`
} `json:"fingerprint,omitempty" description:"usage of the Credential"`
Description string `json:"description,omitempty" description:"Credential's description'"`
Domain string `json:"domain,omitempty" description:"Credential's domain,In ks we only use the default domain, default '_''"`
CreateTime *time.Time `json:"create_time,omitempty" description:"Credential's create_time'"`
Creator string `json:"creator,omitempty" description:"Creator's username"`
UsernamePasswordCredential *UsernamePasswordCredential `json:"username_password,omitempty" description:"username password Credential struct"`
SshCredential *SshCredential `json:"ssh,omitempty" description:"ssh Credential struct"`
SecretTextCredential *SecretTextCredential `json:"secret_text,omitempty" description:"secret_text Credential struct"`
KubeconfigCredential *KubeconfigCredential `json:"kubeconfig,omitempty" description:"kubeconfig Credential struct"`
}
type UsernamePasswordCredential struct {
Username string `json:"username,omitempty" description:"username of username_password credential"`
Password string `json:"password,omitempty" description:"password of username_password credential"`
}
type SshCredential struct {
Username string `json:"username,omitempty" description:"username of ssh credential"`
Passphrase string `json:"passphrase,omitempty" description:"passphrase of ssh credential, password of ssh credential"`
PrivateKey string `json:"private_key,omitempty" mapstructure:"private_key" description:"private key of ssh credential"`
}
type SecretTextCredential struct {
Secret string `json:"secret,omitempty" description:"secret content of credential"`
}
type KubeconfigCredential struct {
Content string `json:"content,omitempty" description:"content of kubeconfig"`
}
const (
ProjectCredentialTableName = "project_credential"
ProjectCredentialIdColumn = "credential_id"
@@ -78,13 +25,6 @@ const (
ProjectCredentialProjectIdColumn = "project_id"
)
var CredentialTypeMap = map[string]string{
"SSH Username with private key": CredentialTypeSsh,
"Username with password": CredentialTypeUsernamePassword,
"Secret text": CredentialTypeSecretText,
"Kubernetes configuration (kubeconfig)": CredentialTypeKubeConfig,
}
type ProjectCredential struct {
ProjectId string `json:"project_id"`
CredentialId string `json:"credential_id"`

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,62 +0,0 @@
/*
Copyright 2019 The KubeSphere Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package devops
// Some apis for Jenkins.
const (
GetPipeBranchUrl = "/blue/rest/organizations/jenkins/pipelines/%s/pipelines/%s/branches/?"
GetBranchPipeUrl = "/blue/rest/organizations/jenkins/pipelines/%s/pipelines/%s/branches/%s/"
GetPipelineUrl = "/blue/rest/organizations/jenkins/pipelines/%s/pipelines/%s/"
SearchPipelineUrl = "/blue/rest/search/?"
RunBranchPipelineUrl = "/blue/rest/organizations/jenkins/pipelines/%s/pipelines/%s/branches/%s/runs/"
RunPipelineUrl = "/blue/rest/organizations/jenkins/pipelines/%s/pipelines/%s/runs/"
GetPipelineRunUrl = "/blue/rest/organizations/jenkins/pipelines/%s/pipelines/%s/runs/%s/"
GetPipeBranchRunUrl = "/blue/rest/organizations/jenkins/pipelines/%s/pipelines/%s/branches/%s/runs/%s/"
SearchPipelineRunUrl = "/blue/rest/organizations/jenkins/pipelines/%s/pipelines/%s/runs/?"
GetBranchPipeRunNodesUrl = "/blue/rest/organizations/jenkins/pipelines/%s/pipelines/%s/branches/%s/runs/%s/nodes/?"
GetPipeRunNodesUrl = "/blue/rest/organizations/jenkins/pipelines/%s/pipelines/%s/runs/%s/nodes/?"
GetBranchRunLogUrl = "/blue/rest/organizations/jenkins/pipelines/%s/pipelines/%s/branches/%s/runs/%s/log/?"
GetRunLogUrl = "/blue/rest/organizations/jenkins/pipelines/%s/pipelines/%s/runs/%s/log/?"
GetBranchStepLogUrl = "/blue/rest/organizations/jenkins/pipelines/%s/pipelines/%s/branches/%s/runs/%s/nodes/%s/steps/%s/log/?"
GetStepLogUrl = "/blue/rest/organizations/jenkins/pipelines/%s/pipelines/%s/runs/%s/nodes/%s/steps/%s/log/?"
StopBranchPipelineUrl = "/blue/rest/organizations/jenkins/pipelines/%s/pipelines/%s/branches/%s/runs/%s/stop/?"
StopPipelineUrl = "/blue/rest/organizations/jenkins/pipelines/%s/pipelines/%s/runs/%s/stop/?"
ReplayBranchPipelineUrl = "/blue/rest/organizations/jenkins/pipelines/%s/pipelines/%s/branches/%s/runs/%s/replay/"
ReplayPipelineUrl = "/blue/rest/organizations/jenkins/pipelines/%s/pipelines/%s/runs/%s/replay/"
GetBranchArtifactsUrl = "/blue/rest/organizations/jenkins/pipelines/%s/pipelines/%s/branches/%s/runs/%s/artifacts/?"
GetArtifactsUrl = "/blue/rest/organizations/jenkins/pipelines/%s/pipelines/%s/runs/%s/artifacts/?"
CheckBranchPipelineUrl = "/blue/rest/organizations/jenkins/pipelines/%s/pipelines/%s/branches/%s/runs/%s/nodes/%s/steps/%s/"
CheckPipelineUrl = "/blue/rest/organizations/jenkins/pipelines/%s/pipelines/%s/runs/%s/nodes/%s/steps/%s/"
GetBranchNodeStepsUrl = "/blue/rest/organizations/jenkins/pipelines/%s/pipelines/%s/branches/%s/runs/%s/nodes/%s/steps/?"
GetNodeStepsUrl = "/blue/rest/organizations/jenkins/pipelines/%s/pipelines/%s/runs/%s/nodes/%s/steps/?"
GetSCMServersUrl = "/blue/rest/organizations/jenkins/scm/%s/servers/"
CreateSCMServersUrl = "/blue/rest/organizations/jenkins/scm/%s/servers/"
ValidateUrl = "/blue/rest/organizations/jenkins/scm/%s/validate"
GetSCMOrgUrl = "/blue/rest/organizations/jenkins/scm/%s/organizations/?"
GetOrgRepoUrl = "/blue/rest/organizations/jenkins/scm/%s/organizations/%s/repositories/?"
GetConsoleLogUrl = "/job/%s/job/%s/indexing/consoleText"
ScanBranchUrl = "/job/%s/job/%s/build?"
GetCrumbUrl = "/crumbIssuer/api/json/"
ToJenkinsfileUrl = "/pipeline-model-converter/toJenkinsfile"
ToJsonUrl = "/pipeline-model-converter/toJson"
GetNotifyCommitUrl = "/git/notifyCommit/?"
GithubWebhookUrl = "/github-webhook/"
CheckScriptCompileUrl = "/job/%s/job/%s/descriptorByName/org.jenkinsci.plugins.workflow.cps.CpsFlowDefinition/checkScriptCompile"
CheckPipelienCronUrl = "/job/%s/job/%s/descriptorByName/hudson.triggers.TimerTrigger/checkSpec?value=%s"
CheckCronUrl = "/job/%s/descriptorByName/hudson.triggers.TimerTrigger/checkSpec?value=%s"
)

View File

@@ -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)

View File

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

View File

@@ -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
}
}
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
}
}

View File

@@ -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,
}
}
query := dbconn.Select(devops.GetColumnsFromStructWithPrefix(devops.DevOpsProjectTableName, v1alpha2.DevOpsProject{})...).
func (o *devopsProjectOperator) ListDevOpsProjects(workspace, username string, conditions *params.Conditions, orderBy string, reverse bool, limit int, offset int) (*models.PageableResponse, error) {
query := o.db.Select(devops.GetColumnsFromStructWithPrefix(devops.DevOpsProjectTableName, v1alpha2.DevOpsProject{})...).
From(devops.DevOpsProjectTableName)
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())
klog.Error(err)
return nil, err
}
var addRoleCh = make(chan *DevOpsProjectRoleResponse, 8)
var addRoleWg sync.WaitGroup
for role, permission := range devops.JenkinsProjectPermissionMap {
addRoleWg.Add(1)
go func(role string, permission gojenkins.ProjectPermissionIds) {
_, err := jenkinsClient.AddProjectRole(devops.GetProjectRoleName(project.ProjectId, role),
devops.GetProjectRolePattern(project.ProjectId), permission, true)
addRoleCh <- &DevOpsProjectRoleResponse{nil, err}
addRoleWg.Done()
}(role, permission)
}
for role, permission := range devops.JenkinsPipelinePermissionMap {
addRoleWg.Add(1)
go func(role string, permission gojenkins.ProjectPermissionIds) {
_, err := jenkinsClient.AddProjectRole(devops.GetPipelineRoleName(project.ProjectId, role),
devops.GetPipelineRolePattern(project.ProjectId), permission, true)
addRoleCh <- &DevOpsProjectRoleResponse{nil, err}
addRoleWg.Done()
}(role, permission)
}
addRoleWg.Wait()
close(addRoleCh)
for addRoleResponse := range addRoleCh {
if addRoleResponse.Err != nil {
klog.Errorf("%+v", addRoleResponse.Err)
return nil, restful.NewError(utils.GetJenkinsStatusCode(addRoleResponse.Err), addRoleResponse.Err.Error())
}
}
globalRole, err := jenkinsClient.GetGlobalRole(devops.JenkinsAllUserRoleName)
if err != nil {
klog.Errorf("%+v", err)
return nil, restful.NewError(utils.GetJenkinsStatusCode(err), err.Error())
}
if globalRole == nil {
_, err := jenkinsClient.AddGlobalRole(devops.JenkinsAllUserRoleName, gojenkins.GlobalPermissionIds{
GlobalRead: true,
}, true)
if err != nil {
klog.Error("failed to create jenkins global role")
return nil, restful.NewError(utils.GetJenkinsStatusCode(err), err.Error())
}
}
err = globalRole.AssignRole(username)
if err != nil {
klog.Errorf("%+v", err)
return nil, restful.NewError(utils.GetJenkinsStatusCode(err), err.Error())
}
projectRole, err := jenkinsClient.GetProjectRole(devops.GetProjectRoleName(project.ProjectId, devops.ProjectOwner))
if err != nil {
klog.Errorf("%+v", err)
return nil, restful.NewError(utils.GetJenkinsStatusCode(err), err.Error())
}
err = projectRole.AssignRole(username)
if err != nil {
klog.Errorf("%+v", err)
return nil, restful.NewError(utils.GetJenkinsStatusCode(err), err.Error())
}
pipelineRole, err := jenkinsClient.GetProjectRole(devops.GetPipelineRoleName(project.ProjectId, devops.ProjectOwner))
if err != nil {
klog.Errorf("%+v", err)
return nil, restful.NewError(utils.GetJenkinsStatusCode(err), err.Error())
}
err = pipelineRole.AssignRole(username)
if err != nil {
klog.Errorf("%+v", err)
return nil, restful.NewError(utils.GetJenkinsStatusCode(err), err.Error())
}
_, err = devopsdb.InsertInto(devops.DevOpsProjectTableName).
_, 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

View File

@@ -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, &params.Conditions{}, "", false, 1, 0)
devops, err := t.ListDevOpsProjects(workspace.Name, username, &params.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)
}

View File

@@ -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"}

View File

@@ -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(),

View File

@@ -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",

View File

@@ -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
}
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -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
}

View File

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

View File

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

View File

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

View File

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

View File

@@ -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 {

View File

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

View File

@@ -12,13 +12,14 @@
// License for the specific language governing permissions and limitations
// 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
}

View File

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

View File

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

View File

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

View File

@@ -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 := `<?xml version='1.0' encoding='UTF-8'?>
<flow-definition plugin="workflow-job">
@@ -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 &parameters
}
func (s *GitSource) appendToEtree(source *etree.Element) *GitSource {
func appendGitSourceToEtree(source *etree.Element, gitSource *devops.GitSource) {
source.CreateAttr("class", "jenkins.plugins.git.GitSCMSource")
source.CreateAttr("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 := `
<?xml version='1.0' encoding='UTF-8'?>
@@ -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
}

View File

@@ -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",
},
},

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,22 +1,16 @@
/*
Copyright 2019 The KubeSphere Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package devops
import (
"io"
"net/http"
"net/url"
)
type PipelineList struct {
Items []Pipeline `json:"items"`
Total int `json:"total_count"`
}
// GetPipeline & SearchPipelines
type Pipeline struct {
Class string `json:"_class,omitempty" description:"Its a fully qualified name and is an identifier of the producer of this resource's capability." `
@@ -85,7 +79,7 @@ type Pipeline struct {
}
// GetPipeBranchRun & SearchPipelineRuns
type BranchPipelineRun struct {
type PipelineRunList struct {
Class string `json:"_class,omitempty" description:"Its a fully qualified name and is an identifier of the producer of this resource's capability."`
Links struct {
PrevRun struct {
@@ -316,7 +310,7 @@ type OrgRepo struct {
}
// StopPipeline
type StopPipe struct {
type StopPipeline struct {
Class string `json:"_class,omitempty" description:"Its a fully qualified name and is an identifier of the producer of this resource's capability."`
Links struct {
Parent struct {
@@ -390,7 +384,7 @@ type StopPipe struct {
}
// ReplayPipeline
type ReplayPipe struct {
type ReplayPipeline struct {
Class string `json:"_class,omitempty" description:"Its a fully qualified name and is an identifier of the producer of this resource's capability."`
Links struct {
Parent struct {
@@ -468,7 +462,7 @@ type Artifacts struct {
}
// GetPipeBranch
type PipeBranch struct {
type PipelineBranch struct {
Class string `json:"_class,omitempty" description:"Its a fully qualified name and is an identifier of the producer of this resource's capability."`
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:"Its 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:"Its 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)
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

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