From b7ed72e15be3141326babdfcdbc5e5c9b04bcd7f Mon Sep 17 00:00:00 2001 From: Bird Date: Tue, 12 Jul 2022 02:21:45 +0800 Subject: [PATCH 1/3] feat: new plugin jenkins-pipeline-kubernetes Signed-off-by: Bird --- .../jenkins-pipeline-kubernetes/main.go | 39 +++++ docs/plugins/jenkins-pipeline-kubernetes.md | 10 ++ .../plugins/jenkins-pipeline-kubernetes.zh.md | 10 ++ go.mod | 2 + go.sum | 4 + .../pkg/plugin/jenkins/markupformatter.yaml | 4 + internal/pkg/plugin/jenkins/plugin.go | 10 ++ .../jenkinspipelinekubernetes/create.go | 138 ++++++++++++++++++ .../jenkinspipelinekubernetes/delete.go | 30 ++++ .../jenkinspipelinekubernetes.go | 11 ++ .../jenkinspipelinekubernetes_suite_test.go | 13 ++ .../job-template.xml | 29 ++++ .../jenkinspipelinekubernetes/options.go | 18 +++ .../plugin/jenkinspipelinekubernetes/read.go | 29 ++++ .../jenkinspipelinekubernetes/update.go | 27 ++++ .../jenkinspipelinekubernetes/validate.go | 39 +++++ internal/pkg/show/config/embed_gen.go | 4 + .../plugins/jenkins-pipeline-kubernetes.yaml | 15 ++ internal/pkg/show/config/plugins/jenkins.yaml | 5 + 19 files changed, 437 insertions(+) create mode 100644 cmd/plugin/jenkins-pipeline-kubernetes/main.go create mode 100644 docs/plugins/jenkins-pipeline-kubernetes.md create mode 100644 docs/plugins/jenkins-pipeline-kubernetes.zh.md create mode 100644 internal/pkg/plugin/jenkins/markupformatter.yaml create mode 100644 internal/pkg/plugin/jenkins/plugin.go create mode 100644 internal/pkg/plugin/jenkinspipelinekubernetes/create.go create mode 100644 internal/pkg/plugin/jenkinspipelinekubernetes/delete.go create mode 100644 internal/pkg/plugin/jenkinspipelinekubernetes/jenkinspipelinekubernetes.go create mode 100644 internal/pkg/plugin/jenkinspipelinekubernetes/jenkinspipelinekubernetes_suite_test.go create mode 100644 internal/pkg/plugin/jenkinspipelinekubernetes/job-template.xml create mode 100644 internal/pkg/plugin/jenkinspipelinekubernetes/options.go create mode 100644 internal/pkg/plugin/jenkinspipelinekubernetes/read.go create mode 100644 internal/pkg/plugin/jenkinspipelinekubernetes/update.go create mode 100644 internal/pkg/plugin/jenkinspipelinekubernetes/validate.go create mode 100644 internal/pkg/show/config/plugins/jenkins-pipeline-kubernetes.yaml diff --git a/cmd/plugin/jenkins-pipeline-kubernetes/main.go b/cmd/plugin/jenkins-pipeline-kubernetes/main.go new file mode 100644 index 000000000..b33e0b50a --- /dev/null +++ b/cmd/plugin/jenkins-pipeline-kubernetes/main.go @@ -0,0 +1,39 @@ +package main + +import ( + "github.com/devstream-io/devstream/internal/pkg/plugin/jenkinspipelinekubernetes" + "github.com/devstream-io/devstream/pkg/util/log" +) + +// NAME is the name of this DevStream plugin. +const NAME = "jenkins-pipeline-kubernetes" + +// Plugin is the type used by DevStream core. It's a string. +type Plugin string + +// Create implements the create of jenkins-pipeline-kubernetes. +func (p Plugin) Create(options map[string]interface{}) (map[string]interface{}, error) { + return jenkinspipelinekubernetes.Create(options) +} + +// Update implements the update of jenkins-pipeline-kubernetes. +func (p Plugin) Update(options map[string]interface{}) (map[string]interface{}, error) { + return jenkinspipelinekubernetes.Update(options) +} + +// Delete implements the delete of jenkins-pipeline-kubernetes. +func (p Plugin) Delete(options map[string]interface{}) (bool, error) { + return jenkinspipelinekubernetes.Delete(options) +} + +// Read implements the read of jenkins-pipeline-kubernetes. +func (p Plugin) Read(options map[string]interface{}) (map[string]interface{}, error) { + return jenkinspipelinekubernetes.Read(options) +} + +// DevStreamPlugin is the exported variable used by the DevStream core. +var DevStreamPlugin Plugin + +func main() { + log.Infof("%T: %s is a plugin for DevStream. Use it with DevStream.\n", DevStreamPlugin, NAME) +} diff --git a/docs/plugins/jenkins-pipeline-kubernetes.md b/docs/plugins/jenkins-pipeline-kubernetes.md new file mode 100644 index 000000000..da76186e7 --- /dev/null +++ b/docs/plugins/jenkins-pipeline-kubernetes.md @@ -0,0 +1,10 @@ +# jenkins-pipeline-kubernetes plugin + +TODO(dtm): Add your document here. +## Usage + +```yaml + +--8<-- "jenkins-pipeline-kubernetes.yaml" + +``` \ No newline at end of file diff --git a/docs/plugins/jenkins-pipeline-kubernetes.zh.md b/docs/plugins/jenkins-pipeline-kubernetes.zh.md new file mode 100644 index 000000000..cf499e735 --- /dev/null +++ b/docs/plugins/jenkins-pipeline-kubernetes.zh.md @@ -0,0 +1,10 @@ +# jenkins-pipeline-kubernetes 插件 + +TODO(dtm): 在这里添加文档. +## 用例 + +```yaml + +--8<-- "jenkins-pipeline-kubernetes.yaml" + +``` \ No newline at end of file diff --git a/go.mod b/go.mod index 0fdac543f..1a7c66dbc 100644 --- a/go.mod +++ b/go.mod @@ -19,6 +19,7 @@ require ( github.com/mittwald/go-helm-client v0.8.4 github.com/onsi/ginkgo/v2 v2.1.4 github.com/onsi/gomega v1.19.0 + github.com/parnurzeal/gorequest v0.2.16 github.com/sirupsen/logrus v1.8.1 github.com/spf13/cobra v1.4.0 github.com/spf13/viper v1.8.1 @@ -207,6 +208,7 @@ require ( k8s.io/kube-openapi v0.0.0-20211115234752-e816edb12b65 // indirect k8s.io/kubectl v0.22.4 // indirect k8s.io/kubernetes v1.22.4 // indirect + moul.io/http2curl v1.0.0 // indirect oras.land/oras-go v0.4.0 // indirect sigs.k8s.io/kustomize/api v0.8.11 // indirect sigs.k8s.io/kustomize/kyaml v0.11.0 // indirect diff --git a/go.sum b/go.sum index a0e629812..d195173e8 100644 --- a/go.sum +++ b/go.sum @@ -1057,6 +1057,8 @@ github.com/openzipkin/zipkin-go v0.1.6/go.mod h1:QgAqvLzwWbR/WpD4A3cGpPtJrZXNIiJ github.com/openzipkin/zipkin-go v0.2.1/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnhQw8ySjnjRyN4= github.com/openzipkin/zipkin-go v0.2.2/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnhQw8ySjnjRyN4= github.com/pact-foundation/pact-go v1.0.4/go.mod h1:uExwJY4kCzNPcHRj+hCR/HBbOOIwwtUjcrb0b5/5kLM= +github.com/parnurzeal/gorequest v0.2.16 h1:T/5x+/4BT+nj+3eSknXmCTnEVGSzFzPGdpqmUVVZXHQ= +github.com/parnurzeal/gorequest v0.2.16/go.mod h1:3Kh2QUMJoqw3icWAecsyzkpY7UzRfDhbRdTjtNwNiUE= github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= github.com/patrickmn/go-cache v2.1.0+incompatible h1:HRMgzkcYKYpi3C8ajMPV8OFXaaRUnok+kx1WdO15EQc= github.com/patrickmn/go-cache v2.1.0+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ= @@ -1979,6 +1981,8 @@ modernc.org/golex v1.0.0/go.mod h1:b/QX9oBD/LhixY6NDh+IdGv17hgB+51fET1i2kPSmvk= modernc.org/mathutil v1.0.0/go.mod h1:wU0vUrJsVWBZ4P6e7xtFJEhFSNsfRLJ8H458uRjg03k= modernc.org/strutil v1.0.0/go.mod h1:lstksw84oURvj9y3tn8lGvRxyRC1S2+g5uuIzNfIOBs= modernc.org/xc v1.0.0/go.mod h1:mRNCo0bvLjGhHO9WsyuKVU4q0ceiDDDoEeWDJHrNx8I= +moul.io/http2curl v1.0.0 h1:6XwpyZOYsgZJrU8exnG87ncVkU1FVCcTRpwzOkTDUi8= +moul.io/http2curl v1.0.0/go.mod h1:f6cULg+e4Md/oW1cYmwW4IWQOVl2lGbmCNGOHvzX2kE= oras.land/oras-go v0.4.0 h1:u6+7D+raZDYHwlz/uOwNANiRmyYDSSMW7A9E1xXycUQ= oras.land/oras-go v0.4.0/go.mod h1:VJcU+VE4rkclUbum5C0O7deEZbBYnsnpbGSACwTjOcg= rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= diff --git a/internal/pkg/plugin/jenkins/markupformatter.yaml b/internal/pkg/plugin/jenkins/markupformatter.yaml new file mode 100644 index 000000000..346533a44 --- /dev/null +++ b/internal/pkg/plugin/jenkins/markupformatter.yaml @@ -0,0 +1,4 @@ +jenkins: + markupFormatter: + rawHtml: + disableSyntaxHighlighting: false diff --git a/internal/pkg/plugin/jenkins/plugin.go b/internal/pkg/plugin/jenkins/plugin.go new file mode 100644 index 000000000..dea747248 --- /dev/null +++ b/internal/pkg/plugin/jenkins/plugin.go @@ -0,0 +1,10 @@ +package jenkins + +import _ "embed" + +//go:embed markupformatter.yaml +var markupformatterYaml string + +func init() { + _ = markupformatterYaml +} diff --git a/internal/pkg/plugin/jenkinspipelinekubernetes/create.go b/internal/pkg/plugin/jenkinspipelinekubernetes/create.go new file mode 100644 index 000000000..82add503b --- /dev/null +++ b/internal/pkg/plugin/jenkinspipelinekubernetes/create.go @@ -0,0 +1,138 @@ +package jenkinspipelinekubernetes + +import ( + _ "embed" + "fmt" + "os/exec" + "strings" + + "github.com/mitchellh/mapstructure" + "github.com/parnurzeal/gorequest" + + "github.com/devstream-io/devstream/pkg/util/log" +) + +const ( + jenkinsCredentialID = "credential-devstream-io-jenkins-pipeline-kubernetes" + jenkinsCredentialDesc = "Jenkins Pipeline secret, created by devstream-io/jenkins-pipeline-kubernetes" +) + +func Create(options map[string]interface{}) (map[string]interface{}, error) { + var opts Options + if err := mapstructure.Decode(options, &opts); err != nil { + return nil, err + } + + if errs := validate(&opts); len(errs) != 0 { + for _, e := range errs { + log.Errorf("Options error: %s.", e) + } + return nil, fmt.Errorf("opts are illegal") + } + + // always try to create credential, there will be no error if it already exists + if err := CreateCredentialUsernamePassword(&opts); err != nil { + return nil, err + } + + // todo: check if the job already exists + if err := CreateItem(&opts); err != nil { + return nil, fmt.Errorf("failed to create job: %s", err) + } + + return (&resource{}).toMap(), nil +} + +//func ifCredentialExists(options *Options) bool { +// // todo +// return false +//} + +func CreateCredentialSecretText(opts *Options) error { + + accessURL := opts.GetJenkinsAccessURL() + + // todo: use gorequest to do the request + cmdGetCrumb := fmt.Sprintf(`CRUMB=$(curl -s '%s/crumbIssuer/api/xml?xpath=concat(//crumbRequestField,":",//crumb)')`, accessURL) + cmdCreateCredential := fmt.Sprintf(` +curl -H $CRUMB -X POST '%s/credentials/store/system/domain/_/createCredentials' \ +--data-urlencode 'json={ + "": "0", + "credentials": { + "scope": "GLOBAL", + "id": "%s", + "secret": "%s", + "description": "%s", + "$class": "org.jenkinsci.plugins.plaincredentials.impl.StringCredentialsImpl" + } +}'`, accessURL, jenkinsCredentialID, opts.GitHubToken, jenkinsCredentialDesc) + + cmd := exec.Command("sh", "-c", cmdGetCrumb+" && "+cmdCreateCredential) + if err := cmd.Run(); err != nil { + return fmt.Errorf("failed to create credential secret: %s", err) + } + + return nil +} + +func CreateCredentialUsernamePassword(opts *Options) error { + + accessURL := opts.GetJenkinsAccessURL() + + // todo: use gorequest to do the request + cmdGetCrumb := fmt.Sprintf(`CRUMB=$(curl -s '%s/crumbIssuer/api/xml?xpath=concat(//crumbRequestField,":",//crumb)')`, accessURL) + cmdCreateCredential := fmt.Sprintf(` +curl -H $CRUMB -X POST '%s/credentials/store/system/domain/_/createCredentials' \ +--data-urlencode 'json={ + "": "0", + "credentials": { + "scope": "GLOBAL", + "id": "%s", + "username": "foo-useless-username", + "password": "%s", + "description": "%s", + "$class": "com.cloudbees.plugins.credentials.impl.UsernamePasswordCredentialsImpl" + } +}'`, accessURL, jenkinsCredentialID, opts.GitHubToken, jenkinsCredentialDesc) + + cmd := exec.Command("sh", "-c", cmdGetCrumb+" && "+cmdCreateCredential) + if err := cmd.Run(); err != nil { + return fmt.Errorf("failed to create credential secret: %s", err) + } + + return nil +} + +//go:embed job-template.xml +var jobTemplate string + +func CreateItem(opts *Options) error { + + jobXml := renderJobXml(jobTemplate, opts) + + // create job + request := gorequest.New() + resp, body, errs := request.Post(opts.GetJenkinsAccessURL()+"/createItem"). + Set("Content-Type", "application/xml"). + Query("name=" + opts.JenkinsJobName).Send(jobXml).End() + + if len(errs) != 0 { + return fmt.Errorf("failed to create job: %s", errs) + } else if resp.StatusCode != 200 { + return fmt.Errorf("failed to create job, here is response: %s", body) + } + + return nil +} + +// todo: unit test +func renderJobXml(jobTemplate string, opts *Options) string { + // note: maybe it is better to use html/template to generate the job template, + // but it is complex and this is the simplest way to do it + jobXml := strings.Replace(jobTemplate, "{{.GitHubRepoURL}}", opts.GitHubRepoURL, 1) + jobXml = strings.Replace(jobXml, "{{.CredentialsID}}", jenkinsCredentialID, 1) + + fmt.Println(jobXml) + + return jobXml +} diff --git a/internal/pkg/plugin/jenkinspipelinekubernetes/delete.go b/internal/pkg/plugin/jenkinspipelinekubernetes/delete.go new file mode 100644 index 000000000..13120ba9a --- /dev/null +++ b/internal/pkg/plugin/jenkinspipelinekubernetes/delete.go @@ -0,0 +1,30 @@ +package jenkinspipelinekubernetes + +import ( + "fmt" + + "github.com/mitchellh/mapstructure" + + "github.com/devstream-io/devstream/pkg/util/log" +) + +func Delete(options map[string]interface{}) (bool, error) { + var opts Options + if err := mapstructure.Decode(options, &opts); err != nil { + return false, err + } + + if errs := validate(&opts); len(errs) != 0 { + for _, e := range errs { + log.Errorf("Options error: %s.", e) + } + return false, fmt.Errorf("opts are illegal") + } + + // TODO(dtm): Add your logic here. + + //todo: delete credentials + //todo: delete pipeline + + return true, nil +} diff --git a/internal/pkg/plugin/jenkinspipelinekubernetes/jenkinspipelinekubernetes.go b/internal/pkg/plugin/jenkinspipelinekubernetes/jenkinspipelinekubernetes.go new file mode 100644 index 000000000..3baa07159 --- /dev/null +++ b/internal/pkg/plugin/jenkinspipelinekubernetes/jenkinspipelinekubernetes.go @@ -0,0 +1,11 @@ +package jenkinspipelinekubernetes + +// TODO(dtm): Add your logic here. + +// todo: specify the resource fields here. +type resource struct { +} + +func (res *resource) toMap() map[string]interface{} { + return map[string]interface{}{} +} diff --git a/internal/pkg/plugin/jenkinspipelinekubernetes/jenkinspipelinekubernetes_suite_test.go b/internal/pkg/plugin/jenkinspipelinekubernetes/jenkinspipelinekubernetes_suite_test.go new file mode 100644 index 000000000..0b0c314a0 --- /dev/null +++ b/internal/pkg/plugin/jenkinspipelinekubernetes/jenkinspipelinekubernetes_suite_test.go @@ -0,0 +1,13 @@ +package jenkinspipelinekubernetes_test + +import ( + "testing" + + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" +) + +func TestJenkinspipelinekubernetes(t *testing.T) { + RegisterFailHandler(Fail) + RunSpecs(t, "Jenkinspipelinekubernetes Suite") +} diff --git a/internal/pkg/plugin/jenkinspipelinekubernetes/job-template.xml b/internal/pkg/plugin/jenkinspipelinekubernetes/job-template.xml new file mode 100644 index 000000000..3fcf24ee2 --- /dev/null +++ b/internal/pkg/plugin/jenkinspipelinekubernetes/job-template.xml @@ -0,0 +1,29 @@ + + + + false + + + + 2 + + + {{.GitHubRepoURL}} + {{.CredentialsID}} + + + + + */main + + + false + + + + Jenkinsfile + false + + + false + diff --git a/internal/pkg/plugin/jenkinspipelinekubernetes/options.go b/internal/pkg/plugin/jenkinspipelinekubernetes/options.go new file mode 100644 index 000000000..b73095dc8 --- /dev/null +++ b/internal/pkg/plugin/jenkinspipelinekubernetes/options.go @@ -0,0 +1,18 @@ +package jenkinspipelinekubernetes + +import "fmt" + +// Options is the struct for configurations of the jenkins-pipeline-kubernetes plugin. +type Options struct { + // TODO(dtm): Add your params here. + JenkinsURL string `mapstructure:"jenkinsUrl" validate:"required,hostname_port"` + JenkinsUser string `mapstructure:"jenkinsUser" validate:"required"` + JenkinsToken string `mapstructure:"jenkinsToken"` + GitHubToken string `mapstructure:"githubToken"` + GitHubRepoURL string `mapstructure:"githubRepoUrl" validate:"required"` + JenkinsJobName string `mapstructure:"jenkinsJobName" validate:"required"` +} + +func (options *Options) GetJenkinsAccessURL() string { + return fmt.Sprintf("http://%s:%s@%s", options.JenkinsUser, options.JenkinsToken, options.JenkinsURL) +} diff --git a/internal/pkg/plugin/jenkinspipelinekubernetes/read.go b/internal/pkg/plugin/jenkinspipelinekubernetes/read.go new file mode 100644 index 000000000..cd34ce230 --- /dev/null +++ b/internal/pkg/plugin/jenkinspipelinekubernetes/read.go @@ -0,0 +1,29 @@ +package jenkinspipelinekubernetes + +import ( + "fmt" + + "github.com/mitchellh/mapstructure" + + "github.com/devstream-io/devstream/pkg/util/log" +) + +func Read(options map[string]interface{}) (map[string]interface{}, error) { + var opts Options + if err := mapstructure.Decode(options, &opts); err != nil { + return nil, err + } + + if errs := validate(&opts); len(errs) != 0 { + for _, e := range errs { + log.Errorf("Options error: %s.", e) + } + return nil, fmt.Errorf("opts are illegal") + } + + // TODO(dtm): Add your logic here. + + // todo: specify the resource to be read and the way to read it. + + return nil, nil +} diff --git a/internal/pkg/plugin/jenkinspipelinekubernetes/update.go b/internal/pkg/plugin/jenkinspipelinekubernetes/update.go new file mode 100644 index 000000000..02b8e1b9d --- /dev/null +++ b/internal/pkg/plugin/jenkinspipelinekubernetes/update.go @@ -0,0 +1,27 @@ +package jenkinspipelinekubernetes + +import ( + "fmt" + + "github.com/mitchellh/mapstructure" + + "github.com/devstream-io/devstream/pkg/util/log" +) + +func Update(options map[string]interface{}) (map[string]interface{}, error) { + var opts Options + if err := mapstructure.Decode(options, &opts); err != nil { + return nil, err + } + + if errs := validate(&opts); len(errs) != 0 { + for _, e := range errs { + log.Errorf("Options error: %s.", e) + } + return nil, fmt.Errorf("opts are illegal") + } + + // TODO(dtm): Add your logic here. + + return (&resource{}).toMap(), nil +} diff --git a/internal/pkg/plugin/jenkinspipelinekubernetes/validate.go b/internal/pkg/plugin/jenkinspipelinekubernetes/validate.go new file mode 100644 index 000000000..ca80d2d82 --- /dev/null +++ b/internal/pkg/plugin/jenkinspipelinekubernetes/validate.go @@ -0,0 +1,39 @@ +package jenkinspipelinekubernetes + +import ( + "fmt" + "os" + "strings" + + "github.com/devstream-io/devstream/pkg/util/validator" +) + +// validate validates the options provided by the core. +func validate(options *Options) []error { + + // pre-handle options to remove "http://" from JenkinsURL + preHandleOptions(options) + + var retErrs []error + + if errs := validator.Struct(options); len(errs) != 0 { + retErrs = append(retErrs, errs...) + } + + options.GitHubToken = os.Getenv("GITHUB_TOKEN") + options.JenkinsToken = os.Getenv("JENKINS_TOKEN") + if options.GitHubToken == "" { + retErrs = append(retErrs, fmt.Errorf("GITHUB_TOKEN is required")) + } + if options.JenkinsToken == "" { + retErrs = append(retErrs, fmt.Errorf("JENKINS_TOKEN is required")) + } + + // todo: check if the jenkins url is valid (try to connect to jenkins) + + return retErrs +} + +func preHandleOptions(options *Options) { + options.JenkinsURL = strings.Replace(options.JenkinsURL, "http://", "", 1) +} diff --git a/internal/pkg/show/config/embed_gen.go b/internal/pkg/show/config/embed_gen.go index 5fb2e87c6..e80e1e214 100644 --- a/internal/pkg/show/config/embed_gen.go +++ b/internal/pkg/show/config/embed_gen.go @@ -48,6 +48,9 @@ var ( //go:embed plugins/helm-generic.yaml HelmGenericDefaultConfig string + //go:embed plugins/jenkins-pipeline-kubernetes.yaml + JenkinsPipelineKubernetesDefaultConfig string + //go:embed plugins/jenkins.yaml JenkinsDefaultConfig string @@ -87,6 +90,7 @@ var pluginDefaultConfigs = map[string]string{ "gitlabci-golang": GitlabciGolangDefaultConfig, "hashicorp-vault": HashicorpVaultDefaultConfig, "helm-generic": HelmGenericDefaultConfig, + "jenkins-pipeline-kubernetes": JenkinsPipelineKubernetesDefaultConfig, "jenkins": JenkinsDefaultConfig, "jira-github-integ": JiraGithubIntegDefaultConfig, "kube-prometheus": KubePrometheusDefaultConfig, diff --git a/internal/pkg/show/config/plugins/jenkins-pipeline-kubernetes.yaml b/internal/pkg/show/config/plugins/jenkins-pipeline-kubernetes.yaml new file mode 100644 index 000000000..cd5749e73 --- /dev/null +++ b/internal/pkg/show/config/plugins/jenkins-pipeline-kubernetes.yaml @@ -0,0 +1,15 @@ +tools: +# name of the tool +- name: jenkins-pipeline-kubernetes + # id of the tool instance + instanceID: default + # format: name.instanceID; If specified, dtm will make sure the dependency is applied first before handling this tool. + dependsOn: [ ] + # options for the plugin + options: + # TODO(dtm): Add your default config here. + # jenkinsUrl, format: hostname:port + jenkinsUrl: http://localhost:8080 + jenkinsUser: admin + githubRepoUrl: https://github.com/xxx/xxx.git + jenkinsJobName: diff --git a/internal/pkg/show/config/plugins/jenkins.yaml b/internal/pkg/show/config/plugins/jenkins.yaml index d84f41f45..2102cdf9d 100644 --- a/internal/pkg/show/config/plugins/jenkins.yaml +++ b/internal/pkg/show/config/plugins/jenkins.yaml @@ -44,3 +44,8 @@ tools: controller: serviceType: NodePort nodePort: 32000 + additionalPlugins: + # install "GitHub Pull Request Builder" plugin, see https://plugins.jenkins.io/ghprb/ for more details + - ghprb + # install "OWASP Markup Formatter" plugin, see https://plugins.jenkins.io/antisamy-markup-formatter/ for more details + - antisamy-markup-formatter From be60c1ab9742dc85ff93b06985ddfcf3fae52ce4 Mon Sep 17 00:00:00 2001 From: Bird Date: Thu, 14 Jul 2022 20:11:24 +0800 Subject: [PATCH 2/3] feat: update jenkins and jenkins-pipeline-kubernetes plugin Signed-off-by: Bird --- .../plugins/jenkins-pipeline-kubernetes.zh.md | 15 +- docs/plugins/jenkins.md | 2 + docs/plugins/jenkins.zh.md | 2 + .../pkg/plugin/jenkins/markupformatter.yaml | 4 - internal/pkg/plugin/jenkins/plugin.go | 10 -- .../jenkinspipelinekubernetes/client.go | 153 ++++++++++++++++++ .../jenkinspipelinekubernetes/create.go | 143 ++++++---------- .../jenkinspipelinekubernetes/delete.go | 7 +- .../jenkinspipelinekubernetes.go | 4 +- .../jenkinspipelinekubernetes/options.go | 1 - .../plugin/jenkinspipelinekubernetes/read.go | 9 +- .../jenkinspipelinekubernetes/update.go | 5 +- .../jenkinspipelinekubernetes/validate.go | 8 +- .../plugins/jenkins-pipeline-kubernetes.yaml | 3 +- internal/pkg/show/config/plugins/jenkins.yaml | 13 ++ 15 files changed, 256 insertions(+), 123 deletions(-) delete mode 100644 internal/pkg/plugin/jenkins/markupformatter.yaml delete mode 100644 internal/pkg/plugin/jenkins/plugin.go create mode 100644 internal/pkg/plugin/jenkinspipelinekubernetes/client.go diff --git a/docs/plugins/jenkins-pipeline-kubernetes.zh.md b/docs/plugins/jenkins-pipeline-kubernetes.zh.md index cf499e735..1b9d4a3d2 100644 --- a/docs/plugins/jenkins-pipeline-kubernetes.zh.md +++ b/docs/plugins/jenkins-pipeline-kubernetes.zh.md @@ -1,10 +1,21 @@ # jenkins-pipeline-kubernetes 插件 -TODO(dtm): 在这里添加文档. +这个插件在已有的 Jenkins 上建立 Jenkins job, 将 GitHub 作为 SCM。 + +步骤: + +1. 访问 Jenkins web UI,创建 token。步骤:People -> admin ->Configure -> API Token -> Add new Token。 +2. 按需修改配置项,其中 `githubRepoUrl` 为 GitHub 仓库地址,应预先建立一个 GitHub 仓库,并创建一个名为 "Jenkinsfile" 的文件放至仓库根目录。 +3. 设置环境变量 + - `GITHUB_TOKEN` + - `JENKINS_TOKEN` + ## 用例 ```yaml --8<-- "jenkins-pipeline-kubernetes.yaml" -``` \ No newline at end of file +``` + +目前,所有选项均为必填项。 diff --git a/docs/plugins/jenkins.md b/docs/plugins/jenkins.md index 8efb4837e..1de434ddf 100644 --- a/docs/plugins/jenkins.md +++ b/docs/plugins/jenkins.md @@ -2,6 +2,8 @@ This plugin installs [Jenkins](https://jenkins.io) in an existing Kubernetes cluster using the Helm chart. +It also installs [GitHub Pull Request Builder(ghprb)](https://plugins.jenkins.io/ghprb/) and [OWASP Markup Formatter](https://plugins.jenkins.io/antisamy-markup-formatter/) plugins. Then enable HTML parsing using OWASP Markup Formatter Plugin , useful with ghprb plugin. + ## Usage ### Production Environment diff --git a/docs/plugins/jenkins.zh.md b/docs/plugins/jenkins.zh.md index 3d8b60344..6c685b514 100644 --- a/docs/plugins/jenkins.zh.md +++ b/docs/plugins/jenkins.zh.md @@ -2,6 +2,8 @@ 本插件使用 helm 在已有的 k8s 集群上安装 [Jenkins](https://jenkins.io)。 +并且安装 [GitHub Pull Request Builder](https://plugins.jenkins.io/ghprb/) 插件和 [OWASP Markup Formatter](https://plugins.jenkins.io/antisamy-markup-formatter/) 插件;同时利用 OWASP Markup Formatter 插件激活 HTML 渲染模式。 + ## 使用方法 ### 生产环境 diff --git a/internal/pkg/plugin/jenkins/markupformatter.yaml b/internal/pkg/plugin/jenkins/markupformatter.yaml deleted file mode 100644 index 346533a44..000000000 --- a/internal/pkg/plugin/jenkins/markupformatter.yaml +++ /dev/null @@ -1,4 +0,0 @@ -jenkins: - markupFormatter: - rawHtml: - disableSyntaxHighlighting: false diff --git a/internal/pkg/plugin/jenkins/plugin.go b/internal/pkg/plugin/jenkins/plugin.go deleted file mode 100644 index dea747248..000000000 --- a/internal/pkg/plugin/jenkins/plugin.go +++ /dev/null @@ -1,10 +0,0 @@ -package jenkins - -import _ "embed" - -//go:embed markupformatter.yaml -var markupformatterYaml string - -func init() { - _ = markupformatterYaml -} diff --git a/internal/pkg/plugin/jenkinspipelinekubernetes/client.go b/internal/pkg/plugin/jenkinspipelinekubernetes/client.go new file mode 100644 index 000000000..2a537517e --- /dev/null +++ b/internal/pkg/plugin/jenkinspipelinekubernetes/client.go @@ -0,0 +1,153 @@ +package jenkinspipelinekubernetes + +import ( + _ "embed" + "fmt" + "os/exec" + "strings" + + "github.com/parnurzeal/gorequest" + + "github.com/devstream-io/devstream/pkg/util/log" +) + +// ClientInf represents the client abstraction for jenkins +type ClientInf interface { + CreateCredentialSecretText() error + CreateCredentialUsernamePassword() error + GetCrumb() (string, error) + GetCrumbHeader() (headerKey, headerValue string, err error) + CreateItem(jobXmlContent string) error +} + +// Client is the client for jenkins +type Client struct { + Opts *Options +} + +func NewClient(options *Options) *Client { + return &Client{ + Opts: options, + } +} + +//func (c *Client) ifCredentialExists() bool { +// // todo +// return false +//} + +// CreateCredentialSecretText creates a credential in the type of "Secret text" +func (c *Client) CreateCredentialSecretText() error { + + accessURL := c.Opts.GetJenkinsAccessURL() + crumb, err := c.GetCrumb() + if err != nil { + return fmt.Errorf("failed to create credential secret: %s", err) + } + + // TODO(aFlyBird0): use gorequest to do the request + cmdCreateCredential := fmt.Sprintf(` +curl -H %s -X POST '%s/credentials/store/system/domain/_/createCredentials' \ +--data-urlencode 'json={ + "": "0", + "credentials": { + "scope": "GLOBAL", + "id": "%s", + "secret": "%s", + "description": "%s", + "$class": "org.jenkinsci.plugins.plaincredentials.impl.StringCredentialsImpl" + } +}'`, crumb, accessURL, jenkinsCredentialID, c.Opts.GitHubToken, jenkinsCredentialDesc) + + cmd := exec.Command("sh", "-c", cmdCreateCredential) + if err := cmd.Run(); err != nil { + return fmt.Errorf("failed to create credential secret: %s", err) + } + + return nil +} + +// CreateCredentialUsernamePassword creates a credential in the type of "Username with password" +func (c *Client) CreateCredentialUsernamePassword() error { + + accessURL := c.Opts.GetJenkinsAccessURL() + crumb, err := c.GetCrumb() + if err != nil { + return fmt.Errorf("failed to create credential secret: %s", err) + } + + // TODO(aFlyBird0): use gorequest to do the request + cmdCreateCredential := fmt.Sprintf(` +curl -H %s -X POST '%s/credentials/store/system/domain/_/createCredentials' \ +--data-urlencode 'json={ + "": "0", + "credentials": { + "scope": "GLOBAL", + "id": "%s", + "username": "foo-useless-username", + "password": "%s", + "description": "%s", + "$class": "com.cloudbees.plugins.credentials.impl.UsernamePasswordCredentialsImpl" + } +}'`, crumb, accessURL, jenkinsCredentialID, c.Opts.GitHubToken, jenkinsCredentialDesc) + + cmd := exec.Command("sh", "-c", cmdCreateCredential) + if err := cmd.Run(); err != nil { + return fmt.Errorf("failed to create credential secret: %s", err) + } + + return nil +} + +// GetCrumb returns the crumb for jenkins, +// jenkins uses crumb to prevent CSRF(cross-site request forgery), +// format: "Jenkins-Crumb:a70290b6423777f0a4c771d4805637ac36d5fd78336a20d48d72167ef5f13b9a" +// ref: https://www.jenkins.io/doc/upgrade-guide/2.176/#upgrading-to-jenkins-lts-2-176-3 +// ref: https://stackoverflow.com/questions/44711696/jenkins-403-no-valid-crumb-was-included-in-the-request +func (c *Client) GetCrumb() (string, error) { + request := gorequest.New() + getCrumbURL := c.Opts.GetJenkinsAccessURL() + `/crumbIssuer/api/xml?xpath=concat(//crumbRequestField,":",//crumb)` + resp, body, errs := request.Get(getCrumbURL).End() + log.Debugf("GetCrumb url: %s", getCrumbURL) + if len(errs) != 0 { + return "", fmt.Errorf("failed to get crumb: %s", errs) + } + if resp.StatusCode != 200 { + return "", fmt.Errorf("failed to get crumb, here is response: %s", body) + } + + return strings.TrimSpace(body), nil +} + +// GetCrumbHeader behaves like GetCrumb, but it returns the header key and value +func (c *Client) GetCrumbHeader() (headerKey, headerValue string, err error) { + // crumb format: "Jenkins-Crumb:a70290b6423777f0a4c771d4805637ac36d5fd78336a20d48d72167ef5f13b9a" + crumb, err := c.GetCrumb() + if err != nil { + return "", "", err + } + crumbMap := strings.Split(crumb, ":") + if len(crumbMap) != 2 { + return "", "", fmt.Errorf("failed to get crumb, here is response: %s", crumb) + } + return crumbMap[0], crumbMap[1], nil +} + +//go:embed job-template.xml +var jobTemplate string + +// CreateItem creates a job in jenkins with the given job xml +func (c *Client) CreateItem(jobXmlContent string) error { + request := gorequest.New() + resp, body, errs := request.Post(c.Opts.GetJenkinsAccessURL()+"/createItem"). + Set("Content-Type", "application/xml"). + Query("name=" + c.Opts.JenkinsJobName).Send(jobXmlContent).End() + + if len(errs) != 0 { + return fmt.Errorf("failed to create job: %s", errs) + } else if resp.StatusCode != 200 { + return fmt.Errorf("failed to create job, here is response: %s", body) + } + + return nil +} diff --git a/internal/pkg/plugin/jenkinspipelinekubernetes/create.go b/internal/pkg/plugin/jenkinspipelinekubernetes/create.go index 82add503b..ccf332792 100644 --- a/internal/pkg/plugin/jenkinspipelinekubernetes/create.go +++ b/internal/pkg/plugin/jenkinspipelinekubernetes/create.go @@ -3,18 +3,16 @@ package jenkinspipelinekubernetes import ( _ "embed" "fmt" - "os/exec" "strings" "github.com/mitchellh/mapstructure" - "github.com/parnurzeal/gorequest" "github.com/devstream-io/devstream/pkg/util/log" ) const ( - jenkinsCredentialID = "credential-devstream-io-jenkins-pipeline-kubernetes" - jenkinsCredentialDesc = "Jenkins Pipeline secret, created by devstream-io/jenkins-pipeline-kubernetes" + jenkinsCredentialID = "credential-jenkins-pipeline-kubernetes-by-devstream" + jenkinsCredentialDesc = "Jenkins Pipeline secret, created by devstream/jenkins-pipeline-kubernetes" ) func Create(options map[string]interface{}) (map[string]interface{}, error) { @@ -30,109 +28,70 @@ func Create(options map[string]interface{}) (map[string]interface{}, error) { return nil, fmt.Errorf("opts are illegal") } + client := NewClient(&opts) + // always try to create credential, there will be no error if it already exists - if err := CreateCredentialUsernamePassword(&opts); err != nil { + if err := client.CreateCredentialUsernamePassword(); err != nil { return nil, err } - // todo: check if the job already exists - if err := CreateItem(&opts); err != nil { - return nil, fmt.Errorf("failed to create job: %s", err) - } + jobXmlContent := renderJobXml(jobTemplate, &opts) - return (&resource{}).toMap(), nil -} + // TODO(aFlyBird0): check if the job already exists -//func ifCredentialExists(options *Options) bool { -// // todo -// return false -//} - -func CreateCredentialSecretText(opts *Options) error { - - accessURL := opts.GetJenkinsAccessURL() - - // todo: use gorequest to do the request - cmdGetCrumb := fmt.Sprintf(`CRUMB=$(curl -s '%s/crumbIssuer/api/xml?xpath=concat(//crumbRequestField,":",//crumb)')`, accessURL) - cmdCreateCredential := fmt.Sprintf(` -curl -H $CRUMB -X POST '%s/credentials/store/system/domain/_/createCredentials' \ ---data-urlencode 'json={ - "": "0", - "credentials": { - "scope": "GLOBAL", - "id": "%s", - "secret": "%s", - "description": "%s", - "$class": "org.jenkinsci.plugins.plaincredentials.impl.StringCredentialsImpl" - } -}'`, accessURL, jenkinsCredentialID, opts.GitHubToken, jenkinsCredentialDesc) - - cmd := exec.Command("sh", "-c", cmdGetCrumb+" && "+cmdCreateCredential) - if err := cmd.Run(); err != nil { - return fmt.Errorf("failed to create credential secret: %s", err) + if err := client.CreateItem(jobXmlContent); err != nil { + return nil, fmt.Errorf("failed to create job: %s", err) } - return nil -} + // TODO(aFlyBird0): use JCasC to configure job creation + // configuration as code: update jenkins config + // + configure system -> GitHub Pull Request Builder -> Jenkins URL override + // + configure system -> GitHub Pull Request Builder -> git personal access token + // - job creation: + // + pr builder + // + main builder + // + GitHub project Project url + // + select GitHub Pull Request Builder under Build Triggers + // + Trigger phrase: optional + // + Pipeline Definition: pipeline script from SCM + // + Branch Specifier & Refspec: PR/main + // + unselect Lightweight checkout + // + Jenkinsfile template -> github repo (https://github.com/IronCore864/jenkins-test/blob/main/Jenkinsfile, https://plugins.jenkins.io/kubernetes/) + + // TODO(aFlyBird0): about how to create an new config yaml in JCasC: + // refer: https://plugins.jenkins.io/configuration-as-code/ + // refer: https://github.com/jenkinsci/helm-charts/blob/e4242561e5ea205bfa3405064cf5fe39b5a22d93/charts/jenkins/values.yaml#L341 + // refer: https://github.com/jenkinsci/helm-charts/blob/e4242561e5ea205bfa3405064cf5fe39b5a22d93/charts/jenkins/templates/jcasc-config.yaml#L1-L25 + // example: If we want to create a new config yaml in JCasC, we can use the following code: + // 1. render this ConfigMap by yourself: https://github.com/jenkinsci/helm-charts/blob/e4242561e5ea205bfa3405064cf5fe39b5a22d93/charts/jenkins/templates/jcasc-config.yaml#L6-L26 + // 2. $key should be your config file name(without .yaml extension), value should be the real config file content + // 3. once you want to put a new config yaml into $JenkinsHome/config-as-code/ folder, just use k8s sdk to create a new ConfigMap + // 4. don't forget to add a label to the ConfigMap, such as "author: devstream". So that we could easy to filter the ConfigMap created by devstream to delete them. + // 5. if you want to update an existing config yaml, just use k8s sdk to update the ConfigMap + // 6. it seems that once you create/update a ConfigMap, Jenkins(installed by helm) will automatically reload the config yaml, + // if not, you can use the following api to reload the config yaml: "POST JENKINS_URL/configuration-as-code/reload"( remember to add jenkins crumb in http header) + // see here for info:https://github.com/jenkinsci/configuration-as-code-plugin/blob/master/docs/features/configurationReload.md + // there are many things to do: + // 1. figure out the JCasC content we want to create + // 2. create a new ConfigMap according to the explanation above + // 3. handle update of ConfigMap + // 4. add "read" functions to the ConfigMap to get the content of the ConfigMap and check if the resource is drifted + // 5. maybe we also should consider to expose some config key in ConfigMap to the user + + // TODO(aFlyBird0): build dtm resource + + // TODO(aFlyBird0): what if user doesn't use helm to install jenkins? then the JCasC may not be automatically reloaded. -func CreateCredentialUsernamePassword(opts *Options) error { - - accessURL := opts.GetJenkinsAccessURL() - - // todo: use gorequest to do the request - cmdGetCrumb := fmt.Sprintf(`CRUMB=$(curl -s '%s/crumbIssuer/api/xml?xpath=concat(//crumbRequestField,":",//crumb)')`, accessURL) - cmdCreateCredential := fmt.Sprintf(` -curl -H $CRUMB -X POST '%s/credentials/store/system/domain/_/createCredentials' \ ---data-urlencode 'json={ - "": "0", - "credentials": { - "scope": "GLOBAL", - "id": "%s", - "username": "foo-useless-username", - "password": "%s", - "description": "%s", - "$class": "com.cloudbees.plugins.credentials.impl.UsernamePasswordCredentialsImpl" - } -}'`, accessURL, jenkinsCredentialID, opts.GitHubToken, jenkinsCredentialDesc) - - cmd := exec.Command("sh", "-c", cmdGetCrumb+" && "+cmdCreateCredential) - if err := cmd.Run(); err != nil { - return fmt.Errorf("failed to create credential secret: %s", err) - } - - return nil -} - -//go:embed job-template.xml -var jobTemplate string - -func CreateItem(opts *Options) error { - - jobXml := renderJobXml(jobTemplate, opts) - - // create job - request := gorequest.New() - resp, body, errs := request.Post(opts.GetJenkinsAccessURL()+"/createItem"). - Set("Content-Type", "application/xml"). - Query("name=" + opts.JenkinsJobName).Send(jobXml).End() - - if len(errs) != 0 { - return fmt.Errorf("failed to create job: %s", errs) - } else if resp.StatusCode != 200 { - return fmt.Errorf("failed to create job, here is response: %s", body) - } - - return nil + return (&resource{}).toMap(), nil } -// todo: unit test +// TODO(aFlyBird0): unit test func renderJobXml(jobTemplate string, opts *Options) string { // note: maybe it is better to use html/template to generate the job template, - // but it is complex and this is the simplest way to do it + // but that way is complex and this is the simplest way to do it jobXml := strings.Replace(jobTemplate, "{{.GitHubRepoURL}}", opts.GitHubRepoURL, 1) jobXml = strings.Replace(jobXml, "{{.CredentialsID}}", jenkinsCredentialID, 1) - fmt.Println(jobXml) - + log.Debugf("job xml rendered: %s", jobXml) return jobXml } diff --git a/internal/pkg/plugin/jenkinspipelinekubernetes/delete.go b/internal/pkg/plugin/jenkinspipelinekubernetes/delete.go index 13120ba9a..49d3a1910 100644 --- a/internal/pkg/plugin/jenkinspipelinekubernetes/delete.go +++ b/internal/pkg/plugin/jenkinspipelinekubernetes/delete.go @@ -21,10 +21,9 @@ func Delete(options map[string]interface{}) (bool, error) { return false, fmt.Errorf("opts are illegal") } - // TODO(dtm): Add your logic here. - - //todo: delete credentials - //todo: delete pipeline + // TODO(aFlyBird0): use helm uninstall to delete the resource created by devstream + // TODO(aFlyBird0): filter ConfigMaps Created by devstream in the "jenkins" namespace and delete them(filter by label) + // TODO(aFlyBird0): delete other resources created by devstream(if exists) return true, nil } diff --git a/internal/pkg/plugin/jenkinspipelinekubernetes/jenkinspipelinekubernetes.go b/internal/pkg/plugin/jenkinspipelinekubernetes/jenkinspipelinekubernetes.go index 3baa07159..7dafaac88 100644 --- a/internal/pkg/plugin/jenkinspipelinekubernetes/jenkinspipelinekubernetes.go +++ b/internal/pkg/plugin/jenkinspipelinekubernetes/jenkinspipelinekubernetes.go @@ -1,8 +1,6 @@ package jenkinspipelinekubernetes -// TODO(dtm): Add your logic here. - -// todo: specify the resource fields here. +// TODO(aFlyBird0): specify the resource fields here. type resource struct { } diff --git a/internal/pkg/plugin/jenkinspipelinekubernetes/options.go b/internal/pkg/plugin/jenkinspipelinekubernetes/options.go index b73095dc8..e66b1d1d4 100644 --- a/internal/pkg/plugin/jenkinspipelinekubernetes/options.go +++ b/internal/pkg/plugin/jenkinspipelinekubernetes/options.go @@ -4,7 +4,6 @@ import "fmt" // Options is the struct for configurations of the jenkins-pipeline-kubernetes plugin. type Options struct { - // TODO(dtm): Add your params here. JenkinsURL string `mapstructure:"jenkinsUrl" validate:"required,hostname_port"` JenkinsUser string `mapstructure:"jenkinsUser" validate:"required"` JenkinsToken string `mapstructure:"jenkinsToken"` diff --git a/internal/pkg/plugin/jenkinspipelinekubernetes/read.go b/internal/pkg/plugin/jenkinspipelinekubernetes/read.go index cd34ce230..18ea8f638 100644 --- a/internal/pkg/plugin/jenkinspipelinekubernetes/read.go +++ b/internal/pkg/plugin/jenkinspipelinekubernetes/read.go @@ -21,9 +21,12 @@ func Read(options map[string]interface{}) (map[string]interface{}, error) { return nil, fmt.Errorf("opts are illegal") } - // TODO(dtm): Add your logic here. - - // todo: specify the resource to be read and the way to read it. + // TODO(aFlyBird0): specify the resource to be read and the way to read it, such as: + // plugins install info(GitHub Pull Request Builder Plugin and OWASP Markup Formatter must be installed) + // job list(filter the jobs which are created by devstream) + // credential list(filter the credentials which are created by devstream)(if credential created by devstream exists) + // JCasC configuration + // job configuration && status return nil, nil } diff --git a/internal/pkg/plugin/jenkinspipelinekubernetes/update.go b/internal/pkg/plugin/jenkinspipelinekubernetes/update.go index 02b8e1b9d..dfc7f07fd 100644 --- a/internal/pkg/plugin/jenkinspipelinekubernetes/update.go +++ b/internal/pkg/plugin/jenkinspipelinekubernetes/update.go @@ -21,7 +21,10 @@ func Update(options map[string]interface{}) (map[string]interface{}, error) { return nil, fmt.Errorf("opts are illegal") } - // TODO(dtm): Add your logic here. + // TODO(aFlyBird0): determine how to update the resource, such as: + // if some config/resource are changed, we should restart the Jenkins + // some, we should only call some update function + // others, we just ignore them return (&resource{}).toMap(), nil } diff --git a/internal/pkg/plugin/jenkinspipelinekubernetes/validate.go b/internal/pkg/plugin/jenkinspipelinekubernetes/validate.go index ca80d2d82..e0c291737 100644 --- a/internal/pkg/plugin/jenkinspipelinekubernetes/validate.go +++ b/internal/pkg/plugin/jenkinspipelinekubernetes/validate.go @@ -21,6 +21,12 @@ func validate(options *Options) []error { } options.GitHubToken = os.Getenv("GITHUB_TOKEN") + + // TODO(aFlyBird0): now jenkins token should be provided by the user, + // so, user should install jenkins first and stop to set the token in env, then install this pipeline plugin. + // could we generate a token automatically in "jenkins" plugin? + // and put it into .outputs of "jenkins" plugin, + // so that user could run "jenkins" and "jenkins-pipeline-kubernetes" in the same tool file.(using depends on). options.JenkinsToken = os.Getenv("JENKINS_TOKEN") if options.GitHubToken == "" { retErrs = append(retErrs, fmt.Errorf("GITHUB_TOKEN is required")) @@ -29,7 +35,7 @@ func validate(options *Options) []error { retErrs = append(retErrs, fmt.Errorf("JENKINS_TOKEN is required")) } - // todo: check if the jenkins url is valid (try to connect to jenkins) + // TODO(aFlyBird0): check if the jenkins url is valid (try to connect to jenkins) return retErrs } diff --git a/internal/pkg/show/config/plugins/jenkins-pipeline-kubernetes.yaml b/internal/pkg/show/config/plugins/jenkins-pipeline-kubernetes.yaml index cd5749e73..df02d82bb 100644 --- a/internal/pkg/show/config/plugins/jenkins-pipeline-kubernetes.yaml +++ b/internal/pkg/show/config/plugins/jenkins-pipeline-kubernetes.yaml @@ -7,9 +7,8 @@ tools: dependsOn: [ ] # options for the plugin options: - # TODO(dtm): Add your default config here. # jenkinsUrl, format: hostname:port - jenkinsUrl: http://localhost:8080 + jenkinsUrl: localhost:8080 jenkinsUser: admin githubRepoUrl: https://github.com/xxx/xxx.git jenkinsJobName: diff --git a/internal/pkg/show/config/plugins/jenkins.yaml b/internal/pkg/show/config/plugins/jenkins.yaml index 2102cdf9d..6987969b4 100644 --- a/internal/pkg/show/config/plugins/jenkins.yaml +++ b/internal/pkg/show/config/plugins/jenkins.yaml @@ -49,3 +49,16 @@ tools: - ghprb # install "OWASP Markup Formatter" plugin, see https://plugins.jenkins.io/antisamy-markup-formatter/ for more details - antisamy-markup-formatter + # Enable HTML parsing using OWASP Markup Formatter Plugin (antisamy-markup-formatter), useful with ghprb plugin. + enableRawHtmlMarkupFormatter: true + # Jenkins Configuraction as Code, refer to https://plugins.jenkins.io/configuration-as-code/ for more details + # notice: All configuration files that are discovered MUST be supplementary. They cannot overwrite each other's configuration values. This creates a conflict and raises a ConfiguratorException. + JCasC: + defaultConfig: true + # each key-value in configScripts will be added to the ${JENKINS_HOME}/casc_configs/ directory as a file. + configScripts: + # this will create a file named "safe_html.yaml" in the ${JENKINS_HOME}/casc_configs/ directory. + # it is used to configure the "Safe HTML" plugin. + # filename must meet RFC 1123, see https://tools.ietf.org/html/rfc1123 for more details + + From 76e1ebbcbd9a1b040f5d9ea22c0a0d36d60b1d0b Mon Sep 17 00:00:00 2001 From: Bird Date: Mon, 18 Jul 2022 21:01:21 +0800 Subject: [PATCH 3/3] chore: expose a todo in jenkins plugin Signed-off-by: Bird --- docs/plugins/jenkins-pipeline-kubernetes.md | 4 ++-- internal/pkg/plugin/jenkinspipelinekubernetes/create.go | 1 + internal/pkg/plugin/jenkinspipelinekubernetes/options.go | 1 + internal/pkg/show/config/plugins/jenkins.yaml | 2 -- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/plugins/jenkins-pipeline-kubernetes.md b/docs/plugins/jenkins-pipeline-kubernetes.md index da76186e7..58776403d 100644 --- a/docs/plugins/jenkins-pipeline-kubernetes.md +++ b/docs/plugins/jenkins-pipeline-kubernetes.md @@ -1,10 +1,10 @@ # jenkins-pipeline-kubernetes plugin -TODO(dtm): Add your document here. +TODO(aFlyBird0): Add your document here. ## Usage ```yaml --8<-- "jenkins-pipeline-kubernetes.yaml" -``` \ No newline at end of file +``` diff --git a/internal/pkg/plugin/jenkinspipelinekubernetes/create.go b/internal/pkg/plugin/jenkinspipelinekubernetes/create.go index ccf332792..505a0346e 100644 --- a/internal/pkg/plugin/jenkinspipelinekubernetes/create.go +++ b/internal/pkg/plugin/jenkinspipelinekubernetes/create.go @@ -86,6 +86,7 @@ func Create(options map[string]interface{}) (map[string]interface{}, error) { } // TODO(aFlyBird0): unit test +// TODO(aFlyBird0): now jenkins script path is hardcoded to "Jenkinsfile", it should be configurable func renderJobXml(jobTemplate string, opts *Options) string { // note: maybe it is better to use html/template to generate the job template, // but that way is complex and this is the simplest way to do it diff --git a/internal/pkg/plugin/jenkinspipelinekubernetes/options.go b/internal/pkg/plugin/jenkinspipelinekubernetes/options.go index e66b1d1d4..e61878fa0 100644 --- a/internal/pkg/plugin/jenkinspipelinekubernetes/options.go +++ b/internal/pkg/plugin/jenkinspipelinekubernetes/options.go @@ -10,6 +10,7 @@ type Options struct { GitHubToken string `mapstructure:"githubToken"` GitHubRepoURL string `mapstructure:"githubRepoUrl" validate:"required"` JenkinsJobName string `mapstructure:"jenkinsJobName" validate:"required"` + // TODO(aFlyBird0): add options to configure the script path in GitHub repo, now it is hardcoded to "Jenkinsfile" } func (options *Options) GetJenkinsAccessURL() string { diff --git a/internal/pkg/show/config/plugins/jenkins.yaml b/internal/pkg/show/config/plugins/jenkins.yaml index 6987969b4..e7740e66f 100644 --- a/internal/pkg/show/config/plugins/jenkins.yaml +++ b/internal/pkg/show/config/plugins/jenkins.yaml @@ -60,5 +60,3 @@ tools: # this will create a file named "safe_html.yaml" in the ${JENKINS_HOME}/casc_configs/ directory. # it is used to configure the "Safe HTML" plugin. # filename must meet RFC 1123, see https://tools.ietf.org/html/rfc1123 for more details - -