├── .github ├── dependabot.yml ├── labeler.yml ├── release-drafter.yml ├── stale.yml └── workflows │ ├── cd.yaml │ ├── e2e.disabled │ ├── javadoc.yml │ ├── pr-labeler.yaml │ └── spotbugs.yml ├── .gitignore ├── .mvn └── maven.config ├── CONTRIBUTING.md ├── DEVELOPMENT.md ├── Dockerfile ├── Jenkinsfile ├── LICENSE ├── Makefile ├── README.md ├── codecov.yml ├── deploy.sh ├── docs ├── installation.md ├── jx-javascript-release.yaml └── tutorial.md ├── plugins.txt ├── pom.xml ├── roadmap.md └── src ├── main ├── java │ └── org │ │ └── waveywaves │ │ └── jenkins │ │ └── plugins │ │ └── tekton │ │ └── client │ │ ├── LogUtils.java │ │ ├── TektonUtils.java │ │ ├── ToolUtils.java │ │ ├── build │ │ ├── BaseStep.java │ │ ├── create │ │ │ ├── CreateCustomTaskrun.java │ │ │ ├── CreateRaw.java │ │ │ ├── TektonArg.java │ │ │ ├── TektonCommandI.java │ │ │ ├── TektonEnv.java │ │ │ ├── TektonParam.java │ │ │ ├── TektonStep.java │ │ │ ├── TektonStringParamSpec.java │ │ │ ├── TektonTaskResult.java │ │ │ ├── TektonWorkspaceBind.java │ │ │ └── TektonWorkspaceDecl.java │ │ └── delete │ │ │ └── DeleteRaw.java │ │ ├── global │ │ ├── ClusterConfig.java │ │ └── TektonGlobalConfiguration.java │ │ └── logwatch │ │ ├── PipelineRunLogWatch.java │ │ └── TaskRunLogWatch.java ├── kubernetes │ └── deployment.yaml ├── resources │ ├── index.jelly │ └── org │ │ └── waveywaves │ │ └── jenkins │ │ └── plugins │ │ └── tekton │ │ └── client │ │ ├── .gitignore │ │ ├── build │ │ ├── create │ │ │ ├── CreateCustomTaskrun │ │ │ │ └── config.jelly │ │ │ ├── CreateRaw │ │ │ │ └── config.jelly │ │ │ ├── TektonArg │ │ │ │ └── config.jelly │ │ │ ├── TektonCommandI │ │ │ │ └── config.jelly │ │ │ ├── TektonEnv │ │ │ │ └── config.jelly │ │ │ ├── TektonParam │ │ │ │ └── config.jelly │ │ │ ├── TektonStep │ │ │ │ └── config.jelly │ │ │ ├── TektonStringParamSpec │ │ │ │ └── config.jelly │ │ │ ├── TektonTaskResult │ │ │ │ └── config.jelly │ │ │ ├── TektonWorkspaceBind │ │ │ │ └── config.jelly │ │ │ └── TektonWorkspaceDecl │ │ │ │ └── config.jelly │ │ └── delete │ │ │ └── DeleteRaw │ │ │ └── config.jelly │ │ └── global │ │ ├── ClusterConfig │ │ └── config.jelly │ │ └── TektonGlobalConfiguration │ │ └── config.jelly └── tekton │ └── examples │ └── v1beta1 │ └── taskruns │ └── home-is-set.yaml └── test ├── java └── org │ └── waveywaves │ └── jenkins │ └── plugins │ └── tekton │ └── client │ ├── FakeLogFilter.java │ ├── LogUtilsTest.java │ ├── ToolUtilsTest.java │ └── build │ ├── FakeChecksPublisher.java │ ├── create │ ├── CreateRawMockServerTest.java │ ├── CreateRawTest.java │ ├── JenkinsFreestyleTest.java │ ├── JenkinsPipelineTest.java │ └── mock │ │ ├── CreateRawMock.java │ │ └── FakeCreateRaw.java │ └── delete │ ├── DeleteRawMockServerTest.java │ ├── DeleteRawTest.java │ └── mock │ └── DeleteRawMock.java └── resources ├── org └── waveywaves │ └── jenkins │ └── plugins │ └── tekton │ └── client │ ├── build │ └── create │ │ ├── jx-pipeline.expanded.yaml │ │ ├── jx-pipeline.yaml │ │ └── tekton-test-project.zip │ └── jxp │ ├── linux │ └── jx-pipeline-effective │ ├── mac │ └── jx-pipeline-effective │ └── windows │ └── jx-pipeline-effective ├── pipeline-crd.yaml ├── pipelinerun-crd.yaml ├── resource-crd.yaml ├── task-crd.yaml └── taskrun-crd.yaml /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # https://help.github.com/github/administering-a-repository/configuration-options-for-dependency-updates 3 | 4 | version: 2 5 | updates: 6 | - package-ecosystem: "maven" 7 | directory: "/" 8 | schedule: 9 | interval: "weekly" 10 | - package-ecosystem: "github-actions" 11 | directory: "/" 12 | schedule: 13 | interval: "weekly" 14 | -------------------------------------------------------------------------------- /.github/labeler.yml: -------------------------------------------------------------------------------- 1 | --- 2 | version: 1 3 | labels: 4 | - label: "in progress" 5 | title: "^WIP:.*" 6 | - label: "in progress" 7 | title: "^wip:.*" 8 | 9 | - title: "^feat.*" 10 | label: "enhancement" 11 | 12 | - title: "^bug.*" 13 | label: "bug" 14 | - title: "^fix.*" 15 | label: "bug" 16 | 17 | - title: "^chore(deps).*" 18 | label: "dependencies" 19 | 20 | - branch: "^dependabot/.*" 21 | label: "dependencies" 22 | 23 | - title: "^chore.*" 24 | label: "chore" 25 | 26 | - title: "^docs.*" 27 | label: "documentation" 28 | 29 | - files: 30 | - ".github/workflows/.*" 31 | label: "github_actions" 32 | -------------------------------------------------------------------------------- /.github/release-drafter.yml: -------------------------------------------------------------------------------- 1 | _extends: .github 2 | -------------------------------------------------------------------------------- /.github/stale.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # Number of days of inactivity before an issue becomes stale 3 | daysUntilStale: 60 4 | # Number of days of inactivity before a stale issue is closed 5 | daysUntilClose: 7 6 | # Issues with these labels will never be considered stale 7 | exemptLabels: 8 | - pinned 9 | - security 10 | # Label to use when marking an issue as stale 11 | staleLabel: stale 12 | # Comment to post when marking an issue as stale. Set to `false` to disable 13 | markComment: > 14 | This issue has been automatically marked as stale because it has not had 15 | recent activity. It will be closed if no further activity occurs. Thank you 16 | for your contributions. 17 | # Comment to post when closing a stale issue. Set to `false` to disable 18 | closeComment: false 19 | -------------------------------------------------------------------------------- /.github/workflows/cd.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | name: cd 3 | on: 4 | workflow_dispatch: 5 | push: 6 | branches: 7 | - master 8 | 9 | jobs: 10 | deploy: 11 | runs-on: ubuntu-latest 12 | steps: 13 | - name: Check out 14 | uses: actions/checkout@v4.0.0 15 | with: 16 | fetch-depth: 0 17 | 18 | - name: Set up JDK 8 19 | uses: actions/setup-java@v3.11.0 20 | with: 21 | java-version: 8 22 | distribution: 'temurin' 23 | 24 | - name: next release version 25 | id: nextversion 26 | uses: jenkins-x-plugins/jx-release-version@v2.7.1 27 | 28 | - name: Set Version 29 | run: | 30 | mvn --no-transfer-progress versions:set -DnewVersion=${{ steps.nextversion.outputs.version }} 31 | 32 | - name: Wait for build to succeed 33 | uses: fountainhead/action-wait-for-check@v1.1.0 34 | id: wait-for-build 35 | with: 36 | token: ${{ secrets.GITHUB_TOKEN }} 37 | checkName: Jenkins 38 | 39 | - name: Release Drafter 40 | uses: release-drafter/release-drafter@v5.24.0 41 | if: steps.wait-for-build.outputs.conclusion == 'success' 42 | with: 43 | name: next 44 | tag: next 45 | version: next 46 | env: 47 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 48 | 49 | - name: Interesting 50 | id: interesting 51 | if: steps.wait-for-build.outputs.conclusion == 'success' 52 | run: | 53 | set -euxo pipefail 54 | echo $GITHUB_EVENT_NAME 55 | if [ "${GITHUB_EVENT_NAME}" = "push" ] 56 | then 57 | INTERESTING_CATEGORIES='[💥🚨🎉🐛⚠🚀👷]|:(boom|tada|construction_worker):' 58 | CATEGORIES=$(gh api /repos/$GITHUB_REPOSITORY/releases | jq -e -r '.[] | select(.draft == true and .name == "next") | .body') 59 | if echo "${CATEGORIES}" | egrep -q "${INTERESTING_CATEGORIES}"; then 60 | echo "Interesting release" 61 | echo "should_release=true" >> $GITHUB_OUTPUT 62 | else 63 | echo "Not interesting release" 64 | echo "should_release=false" >> $GITHUB_OUTPUT 65 | fi 66 | else 67 | echo "should_release=true" >> $GITHUB_OUTPUT 68 | fi 69 | env: 70 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 71 | 72 | - name: Release 73 | uses: jenkins-infra/jenkins-maven-cd-action@master 74 | if: steps.interesting.outputs.should_release == 'true' 75 | with: 76 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 77 | MAVEN_USERNAME: ${{ secrets.MAVEN_USERNAME }} 78 | MAVEN_TOKEN: ${{ secrets.MAVEN_TOKEN }} 79 | -------------------------------------------------------------------------------- /.github/workflows/e2e.disabled: -------------------------------------------------------------------------------- 1 | --- 2 | name: E2E Tests 3 | 4 | on: [workflow_dispatch, pull_request] 5 | 6 | jobs: 7 | test-and-build: 8 | name: Test+Build 9 | runs-on: ubuntu-latest 10 | steps: 11 | - uses: actions/checkout@v2 12 | 13 | - name: Setup Java 14 | uses: actions/setup-java@v3.0.0 15 | with: 16 | distribution: 'temurin' 17 | java-version: 8 18 | 19 | - name: Build 20 | run: mvn --no-transfer-progress install -DskipTests 21 | 22 | - name: Set up Docker Buildx 23 | uses: docker/setup-buildx-action@v1 24 | 25 | - name: Build and push 26 | id: docker_build 27 | uses: docker/build-push-action@v2 28 | with: 29 | context: . 30 | push: false 31 | load: true 32 | tags: jenkinsci/tekton-client-plugin:latest 33 | 34 | - name: Install Kubectl 35 | uses: azure/setup-kubectl@v1 36 | 37 | - name: Set up Helm 38 | uses: azure/setup-helm@v1.1 39 | with: 40 | version: v3.5.2 41 | 42 | - uses: actions/setup-python@v2 43 | with: 44 | python-version: 3.7 45 | 46 | - name: Install Kind 47 | uses: helm/kind-action@v1.1.0 48 | with: 49 | cluster_name: tekton-client-plugin 50 | 51 | - name: Configuring and testing the Installation 52 | run: | 53 | kubectl cluster-info --context kind-tekton-client-plugin 54 | kubectl get nodes 55 | 56 | - name: Load image on the nodes of the cluster 57 | run: | 58 | kind load docker-image --name=tekton-client-plugin jenkinsci/tekton-client-plugin:latest 59 | 60 | - name: Install Jenkins 61 | run: | 62 | helm repo add jenkins https://charts.jenkins.io 63 | helm install --set 'controller.image=jenkinsci/tekton-client-plugin,controller.tag=latest,controller.installPlugins=false,controller.ingress.enabled=true,controller.imagePullPolicy=Never' jenkins jenkins/jenkins --wait 64 | 65 | - name: Install Tekton 66 | run: | 67 | kubectl apply --filename https://storage.googleapis.com/tekton-releases/pipeline/latest/release.yaml 68 | 69 | - name: Debug Installation 70 | run: | 71 | kubectl get namespaces 72 | kubectl get all 73 | curl -L -o tkn.tar.gz https://github.com/tektoncd/cli/releases/download/v0.17.0/tkn_0.17.0_Linux_x86_64.tar.gz 74 | tar xvfz tkn.tar.gz 75 | ./tkn pipelinerun list 76 | curl -L -o jcli.tar.gz https://github.com/jenkins-zh/jenkins-cli/releases/latest/download/jcli-linux-amd64.tar.gz 77 | tar xvfz jcli.tar.gz 78 | ./jcli --help 79 | -------------------------------------------------------------------------------- /.github/workflows/javadoc.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: Javadoc 3 | 4 | on: 5 | pull_request: 6 | 7 | jobs: 8 | javadoc: 9 | runs-on: ubuntu-latest 10 | steps: 11 | - uses: actions/checkout@v4.0.0 12 | 13 | - name: Setup Java 14 | uses: actions/setup-java@v3.11.0 15 | with: 16 | distribution: 'temurin' 17 | java-version: 11 18 | 19 | - name: Javadoc 20 | run: | 21 | mvn --no-transfer-progress javadoc:javadoc 22 | -------------------------------------------------------------------------------- /.github/workflows/pr-labeler.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | name: Label PRs 3 | 4 | on: 5 | schedule: 6 | - cron: '*/5 * * * *' 7 | 8 | jobs: 9 | build: 10 | 11 | runs-on: ubuntu-latest 12 | 13 | steps: 14 | - uses: garethjevans/labeler@master 15 | env: 16 | GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}" 17 | -------------------------------------------------------------------------------- /.github/workflows/spotbugs.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: SpotBugs 3 | 4 | on: 5 | pull_request: 6 | 7 | jobs: 8 | spotbugs: 9 | runs-on: ubuntu-latest 10 | steps: 11 | - uses: actions/checkout@v4.0.0 12 | 13 | - name: Setup Java 14 | uses: actions/setup-java@v3.11.0 15 | with: 16 | distribution: 'temurin' 17 | java-version: 11 18 | 19 | - name: SpotBugs 20 | run: | 21 | mvn --no-transfer-progress spotbugs:check 22 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Eclipse 2 | .classpath 3 | .project 4 | .settings/ 5 | 6 | # Intellij 7 | .idea/ 8 | *.iml 9 | *.iws 10 | 11 | # Mac 12 | .DS_Store 13 | 14 | # Maven 15 | log/ 16 | target/ 17 | work/ 18 | -------------------------------------------------------------------------------- /.mvn/maven.config: -------------------------------------------------------------------------------- 1 | -Pconsume-incrementals 2 | -Pmight-produce-incrementals 3 | -Dchangelist.format=-rc%d 4 | -Pdownload-binaries 5 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | :+1::tada: First off, thanks for taking the time to contribute! :tada::+1: 4 | 5 | The following is a set of guidelines for contributing to the Tekton Client plugin. These are mostly guidelines, not rules. 6 | Use your best judgment, and feel free to propose changes to this document in a pull request. 7 | 8 | In this project we appreciate any kind of contributions: code, documentation, design, etc. 9 | Any contribution counts, and the size does not matter! 10 | 11 | #### Table Of Contents 12 | 13 | [Commit Messages](#commit-messages) 14 | 15 | [Supplementary Videos](#some-supplementary-videos) 16 | 17 | ## Commit Messages 18 | 19 | All commit messages should follow 20 | [these best practices](https://chris.beams.io/posts/git-commit/), specifically: 21 | 22 | - Start with a subject line 23 | - Contain a body that explains _why_ you're making the change you're making 24 | - Reference an issue number one exists, closing it if applicable (with text such 25 | as 26 | ["Fixes #245" or "Closes #111"](https://help.github.com/articles/closing-issues-using-keywords/)) 27 | 28 | Not sure what to put? Try to Include: 29 | 30 | - What is the problem being solved? 31 | - Why is this the best approach? 32 | - What other approaches did you consider? 33 | - What side effects will this approach have? 34 | - What future work remains to be done? 35 | 36 | ## Some Supplementary Videos 37 | 38 | [![](https://i.ytimg.com/vi/17T3-9LeXGA/hqdefault.jpg)](https://www.youtube.com/watch?v=17T3-9LeXGA&t=67s&ab_channel=ContinuousDeliveryFoundation "Bridging the Gap with Tekton-client-plugin for Jenkins - Vibhav Bobade, Red Hat") 39 | 40 | [![](https://i.ytimg.com/vi/2RT9XwIWkVQ/hqdefault.jpg)](https://www.youtube.com/watch?v=2RT9XwIWkVQ&ab_channel=Jenkins "Using the Tekton Client Plugin for Jenkins") 41 | -------------------------------------------------------------------------------- /DEVELOPMENT.md: -------------------------------------------------------------------------------- 1 | # Developing 2 | 3 | ## Getting Started 4 | 5 | ### Prerequisites 6 | - Basic understanding of [Plugin Development in Jenkins](https://www.jenkins.io/doc/developer/plugin-development/). 7 | 8 | ### Useful articles 9 | - [Tutorial: Developing Complex Plugins for Jenkins](https://medium.com/velotio-perspectives/tutorial-developing-complex-plugins-for-jenkins-a34c0f979ca4) 10 | 11 | ## Running and testing the plugin locally 12 | 13 | Currently the plugin is in development state and a lot of things such as unit tests and such have to be added. These will be added as time passes. 14 | 15 | 16 | - _The user needs to login to their Kubernetes Cluster before they start the following._ 17 | 18 | Currently the plugin uses the default Kubernetes Client available to it through it the local kubeconfig and operates at the current local kubecontext available during development. 19 | 20 | - _Use the following command to start a instance of Jenkins locally with the plugin installed_ 21 | ``` 22 | mvn hpi:run 23 | ``` 24 | 25 | Ideally Jenkins should be available at **localhost:8080/jenkins** 26 | 27 | #### Playing around 28 | Visit [the tutorial](docs/tutorial.md) for help with doing various things with the plugin. 29 | 30 | 31 | ## Releasing 32 | 33 | Before releasing you need to run the following command: 34 | 35 | ```bash 36 | mvn package -P download-binaries 37 | ``` 38 | 39 | This will then download the [jx-pipeline](https://github.com/jenkins-x/jx-pipeline/releases) binaries for each platform so they can be embedded inside the plugin. -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM jenkins/jenkins:2.277.1-lts-jdk11 2 | 3 | COPY plugins.txt /usr/share/jenkins/ref/plugins.txt 4 | RUN jenkins-plugin-cli -f /usr/share/jenkins/ref/plugins.txt --verbose 5 | 6 | COPY target/tekton-client.hpi /usr/share/jenkins/ref/plugins 7 | -------------------------------------------------------------------------------- /Jenkinsfile: -------------------------------------------------------------------------------- 1 | buildPlugin(platforms: ["linux"]) 2 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | END OF TERMS AND CONDITIONS 178 | 179 | APPENDIX: How to apply the Apache License to your work. 180 | 181 | To apply the Apache License to your work, attach the following 182 | boilerplate notice, with the fields enclosed by brackets "[]" 183 | replaced with your own identifying information. (Don't include 184 | the brackets!) The text should be enclosed in the appropriate 185 | comment syntax for the file format. We also recommend that a 186 | file or class name and description of purpose be included on the 187 | same "printed page" as the copyright notice for easier 188 | identification within third-party archives. 189 | 190 | Copyright [yyyy] [name of copyright owner] 191 | 192 | Licensed under the Apache License, Version 2.0 (the "License"); 193 | you may not use this file except in compliance with the License. 194 | You may obtain a copy of the License at 195 | 196 | http://www.apache.org/licenses/LICENSE-2.0 197 | 198 | Unless required by applicable law or agreed to in writing, software 199 | distributed under the License is distributed on an "AS IS" BASIS, 200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 201 | See the License for the specific language governing permissions and 202 | limitations under the License. 203 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | # Kubernetes Resources Path 5 | KUBE_RES_PATH := ./src/main/kubernetes 6 | 7 | # Kube Resources 8 | JENKINS_DEPLOYMENT := ${KUBE_RES_PATH}/deployment.yaml 9 | JENKINS_SERVICE := ${KUBE_RES_PATH}/service.yaml 10 | 11 | test: 12 | mvn test 13 | 14 | install-tekton: 15 | kubectl apply --filename https://storage.googleapis.com/tekton-releases/pipeline/latest/release.yaml 16 | 17 | coverage: 18 | mvn cobertura:cobertura -q 19 | 20 | build: 21 | mvn clean install -DskipTests -q 22 | 23 | e2e: 24 | kubectl create -f $(JENKINS_DEPLOYMENT) 25 | 26 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Tekton Client Plugin 2 | 3 | [![Build Status](https://ci.jenkins.io/job/Plugins/job/tekton-client-plugin/job/master/badge/icon)](https://ci.jenkins.io/job/Plugins/job/tekton-client-plugin/job/master/) 4 | [![Contributors](https://img.shields.io/github/contributors/jenkinsci/tekton-client-plugin.svg)](https://github.com/jenkinsci/tekton-client-plugin/graphs/contributors) 5 | [![Jenkins Plugin](https://img.shields.io/jenkins/plugin/v/tekton-client.svg)](https://plugins.jenkins.io/tekton-client) 6 | [![GitHub release](https://img.shields.io/github/release/jenkinsci/tekton-client-plugin.svg?label=changelog)](https://github.com/jenkinsci/tekton-client-plugin/releases/latest) 7 | [![Jenkins Plugin Installs](https://img.shields.io/jenkins/plugin/i/tekton-client.svg?color=blue)](https://plugins.jenkins.io/tekton-client) 8 | [![Codecov](https://codecov.io/gh/jenkinsci/tekton-client-plugin/branch/master/graph/badge.svg)](https://codecov.io/gh/jenkinsci/tekton-client-plugin) 9 | 10 | 11 | Jenkins plugin to interact with [Tekton Pipelines](https://github.com/tektoncd/pipeline) on a Kubernetes Cluster. 12 | 13 | ## Quick Demo 14 | [![](https://img.youtube.com/vi/hAWOlJ0CetQ/0.jpg)](https://www.youtube.com/watch?v=hAWOlJ0CetQ "Tekton Client Plugin") 15 | 16 | ## Community 17 | 18 | [![Join the chat at https://gitter.im/jenkinsci/tekton-client-plugin](https://badges.gitter.im/jenkinsci/tekton-client-plugin.svg)](https://gitter.im/jenkinsci/tekton-client-plugin?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) 19 | 20 | 21 | ## Start using the plugin 22 | 23 | ### Supported Tekton Resource Types 24 | - Task 25 | - TaskRun 26 | - Pipeline 27 | - PipelineRun 28 | 29 | ### Supported Actions on Resources 30 | - Create 31 | - Delete 32 | 33 | _Currently in the tekton-client-plugin we are able to create and delete Tekton Resources._ 34 | 35 | - Learn more with [the tutorial!](docs/tutorial.md) 36 | - Check out the [roadmap](roadmap.md) for a better idea of what's planned for the future. 37 | - [Installation Guide](docs/installation.md) 38 | 39 | ## Want to contribute 40 | 41 | Awesome ! Let's get started ! 42 | 43 | - Check out [DEVELOPMENT.md](DEVELOPMENT.md) for getting started with setup and jenkins plugin hackery. 44 | - Check out [CONTRIBUTING.md](CONTRIBUTING.md) for an overview of the process. 45 | -------------------------------------------------------------------------------- /codecov.yml: -------------------------------------------------------------------------------- 1 | comment: 2 | layout: "reach, diff, flags, files" 3 | behavior: default 4 | require_changes: false # if true: only post the comment if coverage changes 5 | require_base: no # [yes :: must have a base report to post] 6 | require_head: yes # [yes :: must have a head report to post] 7 | branches: # branch names that can post comment 8 | - "master" 9 | -------------------------------------------------------------------------------- /deploy.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # Deploys locally the plugin in a pod which has label name=jenkins 4 | # You must be correctly logged in an OpenShift or Kubernetes cluster (KUBECONFIG set or oc login) 5 | pod_name=$( kubectl get pods -l name=jenkins --output=name | cut -f2 -d/) 6 | plugin_path=/var/lib/jenkins/plugins 7 | plugin_name=$( xmllint --xpath "/*[local-name() = 'project']/*[local-name() = 'artifactId']/text()" pom.xml ) 8 | plugin_dst_extension=jpi 9 | plugin_src_extension=hpi 10 | plugin_full_path=$plugin_path/$plugin_name.$plugin_dst_extension 11 | 12 | local_plugin_path=target/$plugin_name.$plugin_src_extension 13 | remote_plugin_path=$pod_name:$plugin_full_path 14 | remote_plugin_directory=$plugin_path/$plugin_name 15 | 16 | echo "Copying $local_plugin_path into $remote_plugin_path" 17 | kubectl cp $local_plugin_path $remote_plugin_path 18 | echo "Unzipping $plugin_full_path into $remote_plugin_directory" 19 | kubectl exec $pod_name -- unzip -o $plugin_full_path -d $remote_plugin_directory 20 | echo "Restarting container" 21 | kubectl exec $pod_name -- kill 1 22 | -------------------------------------------------------------------------------- /docs/installation.md: -------------------------------------------------------------------------------- 1 | # Installation Instructions 2 | 3 | To install and configure this plugin we would recommend the following setup: 4 | 5 | * Jenkins on k8s install using the jenkinsci/helm-chart 6 | * Tekton Install in a separate namespace 7 | 8 | ## Configuring Jenkins 9 | 10 | The recommended way of running Jenkins in a k8s cluster is to create a custom Jenkins image 11 | with pre-installed plugins to avoid downloading plugins each time the pod restarts. To install 12 | the `tekton-client` plugin, you can add the following to your `plugins.txt` 13 | 14 | ``` 15 | tekton-client:1.0.0 16 | ``` 17 | 18 | This file can be kept up to date using tools like `jenkins-infra/uc` or `plugin-installation-manager`. 19 | 20 | ## Installing Tekton 21 | 22 | It's recommended to install Tekton in a separate namespace to Jenkins, e.g. 23 | 24 | ``` 25 | kubectl create ns tekton-pipelines 26 | 27 | kubectl apply --filename https://storage.googleapis.com/tekton-releases/pipeline/latest/release.yaml 28 | ``` 29 | 30 | If using tasks from the Tekton Catalog, they should also be installed into this namespace. 31 | 32 | ## Giving Jenkins Permission to access the Tekton Namespace 33 | 34 | The final step is to grant the Jenkins Service Account the permission to view the required resources in the 35 | `tekton-pipelines` namespace. This can be done by creating `Role` & `RoleBinding` resource e.g. 36 | 37 | ``` 38 | --- 39 | kind: Role 40 | apiVersion: rbac.authorization.k8s.io/v1 41 | metadata: 42 | name: tekton-role 43 | namespace: 44 | rules: 45 | - apiGroups: 46 | - "" 47 | resources: 48 | - pods 49 | - pods/log 50 | verbs: 51 | - get 52 | - list 53 | - watch 54 | - apiGroups: 55 | - tekton.dev 56 | resources: 57 | - tasks 58 | - taskruns 59 | - pipelines 60 | - pipelineruns 61 | verbs: 62 | - create 63 | - delete 64 | - deletecollection 65 | - get 66 | - list 67 | - patch 68 | - update 69 | - watch 70 | ... 71 | ``` 72 | 73 | and 74 | 75 | ``` 76 | apiVersion: rbac.authorization.k8s.io/v1 77 | kind: RoleBinding 78 | metadata: 79 | name: tekton-role-binding 80 | namespace: 81 | roleRef: 82 | apiGroup: rbac.authorization.k8s.io 83 | kind: Role 84 | name: tekton-role 85 | subjects: 86 | - kind: ServiceAccount 87 | name: jenkins 88 | namespace: 89 | ``` 90 | -------------------------------------------------------------------------------- /docs/jx-javascript-release.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: tekton.dev/v1beta1 2 | kind: PipelineRun 3 | metadata: 4 | name: release 5 | spec: 6 | pipelineSpec: 7 | tasks: 8 | - name: from-build-pack 9 | taskSpec: 10 | steps: 11 | - image: uses:jenkins-x/jx3-pipeline-catalog/tasks/git-clone/git-clone.yaml@versionStream 12 | - image: uses:jenkins-x/jx3-pipeline-catalog/tasks/javascript/release.yaml@versionStream -------------------------------------------------------------------------------- /docs/tutorial.md: -------------------------------------------------------------------------------- 1 | # Tekton Client Plugin Tutorial 2 | 3 | Once the Tekton Client plugin is installed on your Jenkins instance we can get started by creating a new Jenkins 4 | Job for testing the plugin out. 5 | 6 | - Create a new job. 7 | - During the Job Configuration, scroll down to the _Build_ Header and in the dropdown you should be able to find steps which 8 | start with _Tekton:_. 9 | 10 | ### Create Resources 11 | 12 | - After going above in the dropdown, choose "Tekton: Create Resource". 13 | - You can create resources of type Task, TaskRun, Pipeline or PipelineRun. 14 | - Choose your method of creation _(Input Type)_, `URL` or `YAML` and add your data _(Input)_ which includes the Url or YAML definition. 15 | - Add as may steps as you want for creation of resources and "Save & Apply" the config. 16 | 17 | Once you instantiate a build, you should be able to see your resources created. 18 | 19 | ### Delete Resources 20 | 21 | - After going above in the dropdown, choose "Tekton: Delete Resource". 22 | - In the Tekton Resource Type choose the kind of resource you would like to delete. Either of Task, TaskRun, Pipeline or PipelineRun. 23 | - After that put the name of the particular resource. 24 | - Add as may steps as you want for deletion of resources and "Save & Apply" the config. 25 | 26 | Once you instantiate a build, you should be able to see that the resources you have mentioned, get deleted. 27 | 28 | ## Usage inside a pipeline 29 | 30 | The `Create Raw` step can be used inside a pipeline as follows: 31 | 32 | ```groovy 33 | pipeline { 34 | agent any 35 | stages { 36 | stage('Stage') { 37 | steps { 38 | checkout scm 39 | tektonCreateRaw(inputType: 'FILE', input: '.tekton/pipeline.yaml') 40 | } 41 | } 42 | } 43 | } 44 | ``` 45 | 46 | When used in this way, the following parameters are passed to the `PipelineRun` so that the 47 | correct source code can be cloned in the tekton pipeline: 48 | 49 | * `BUILD_ID` - the build id/number of the Jenkins job 50 | * `JOB_NAME` - the name of the jenkins job that triggered this pipeline 51 | * `PULL_BASE_REF` - name of the base branch 52 | * `PULL_PULL_SHA` - the commit sha of the pull request or branch 53 | * `REPO_NAME` - name of the repository 54 | * `REPO_OWNER` - owner of the repository 55 | * `REPO_URL` - the URL of the repository 56 | 57 | ## Using the git-clone task from the tekton catalog. 58 | 59 | To use tasks from the tekton-catalog, the tasks will need to be installed in the same namespace 60 | that tekton is running, once that is done they can be used in a `PipelineRun`. An example pipeline 61 | showing this in use is shown below: 62 | 63 | ``` 64 | apiVersion: tekton.dev/v1beta1 65 | kind: PipelineRun 66 | metadata: 67 | generateName: hello-world-pipeline- 68 | spec: 69 | workspaces: 70 | - name: shared-data 71 | volumeClaimTemplate: 72 | spec: 73 | accessModes: 74 | - ReadWriteOnce 75 | resources: 76 | requests: 77 | storage: 500Mi 78 | pipelineSpec: 79 | params: 80 | - description: the unique build number 81 | name: BUILD_ID 82 | type: string 83 | - description: the git sha of the tip of the pull request 84 | name: PULL_PULL_SHA 85 | type: string 86 | - description: git url to clone 87 | name: REPO_URL 88 | type: string 89 | workspaces: 90 | - name: shared-data 91 | tasks: 92 | - name: fetch-repo 93 | taskRef: 94 | name: git-clone 95 | workspaces: 96 | - name: output 97 | workspace: shared-data 98 | params: 99 | - name: url 100 | value: $(params.REPO_URL) 101 | - name: revision 102 | value: $(params.PULL_PULL_SHA) 103 | ``` 104 | 105 | to reuse the same workspace in future tasks you need to make use of the `runAfter` command e.g.: 106 | 107 | ``` 108 | - name: do-something-with-the-source-code 109 | runAfter: 110 | - fetch-repo 111 | workspaces: 112 | - name: source 113 | workspace: shared-data 114 | ``` 115 | -------------------------------------------------------------------------------- /plugins.txt: -------------------------------------------------------------------------------- 1 | ansicolor 2 | configuration-as-code 3 | git 4 | git-client 5 | kubernetes 6 | pipeline-build-step 7 | pipeline-github 8 | pipeline-graph-analysis 9 | pipeline-input-step 10 | pipeline-milestone-step 11 | pipeline-model-api 12 | pipeline-model-definition 13 | pipeline-model-extensions 14 | pipeline-rest-api 15 | pipeline-stage-step 16 | pipeline-stage-tags-metadata 17 | pipeline-stage-view 18 | pipeline-utility-steps 19 | plain-credentials 20 | scm-api 21 | script-security 22 | workflow-aggregator 23 | workflow-api 24 | workflow-basic-steps 25 | workflow-cps 26 | workflow-cps-global-lib 27 | workflow-durable-task-step 28 | workflow-job 29 | workflow-multibranch 30 | workflow-scm-step 31 | workflow-step-api 32 | workflow-support 33 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4.0.0 4 | 5 | org.jenkins-ci.plugins 6 | plugin 7 | 4.18 8 | 9 | 10 | 11 | org.waveywaves.jenkins.plugins 12 | tekton-client 13 | ${revision}${changelist} 14 | Tekton Client Plugin 15 | Tekton is a Cloud Native CI/CD tool for Kubernetes. This plugin helps in the creation and manipulation of Tekton Resources on you Kubernetes cluster and helps drive your operations from your Jenkins Server itself without having to carry them out from the Command Line Interface or any other User Interface. 16 | hpi 17 | https://github.com/jenkinsci/tekton-client-plugin 18 | 19 | 20 | scm:git:https://github.com/jenkinsci/tekton-client-plugin.git 21 | scm:git:https://github.com/jenkinsci/tekton-client-plugin.git 22 | https://github.com/jenkinsci/tekton-client-plugin 23 | ${scmTag} 24 | 25 | 26 | 27 | 28 | 2.263.1 29 | 8 30 | jenkinsci/${project.artifactId}-plugin 31 | 1.6.0 32 | 1.0.0 33 | -SNAPSHOT 34 | 2.34 35 | 5.4.0 36 | 5.4.0 37 | 2.22.1 38 | 0.1.8 39 | 5.6.2 40 | 0.0.163 41 | 42 | 43 | 44 | 45 | Apache License 2.0 46 | https://www.apache.org/licenses/LICENSE-2.0 47 | repo 48 | 49 | 50 | 51 | 52 | 53 | waveywaves 54 | Vibhav Bobade 55 | 56 | 57 | 58 | 59 | 60 | org.slf4j 61 | slf4j-api 62 | 63 | 64 | org.jenkins-ci.plugins.workflow 65 | workflow-aggregator 66 | 2.6 67 | 68 | 69 | io.fabric8 70 | tekton-client 71 | ${tekton-client.version} 72 | 73 | 74 | org.slf4j 75 | slf4j-api 76 | 77 | 78 | 79 | 80 | io.fabric8 81 | kubernetes-server-mock 82 | ${kubernetes-server-mock.version} 83 | test 84 | 85 | 86 | 87 | org.jenkins-ci.plugins 88 | jackson2-api 89 | 90 | 91 | 92 | 93 | org.jenkins-ci.plugins 94 | git 95 | test 96 | 97 | 98 | org.jenkins-ci.plugins 99 | github-branch-source 100 | 2.9.7 101 | test 102 | 103 | 104 | org.jenkins-ci.plugins 105 | envinject 106 | 2.3.0 107 | test 108 | 109 | 110 | org.assertj 111 | assertj-core 112 | 3.19.0 113 | test 114 | 115 | 116 | org.jenkins-ci.plugins 117 | pipeline-utility-steps 118 | 2.7.1 119 | test 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | io.jenkins.tools.bom 128 | bom-2.249.x 129 | 29 130 | import 131 | pom 132 | 133 | 134 | org.slf4j 135 | slf4j-api 136 | 1.7.30 137 | 138 | 139 | org.apache.commons 140 | commons-lang3 141 | 3.12.0 142 | 143 | 144 | org.jenkins-ci.plugins 145 | jackson2-api 146 | 2.12.1 147 | 148 | 149 | com.fasterxml.jackson.module 150 | jackson-module-jaxb-annotations 151 | 2.12.1 152 | 153 | 154 | com.squareup.okio 155 | okio 156 | 3.4.0 157 | 158 | 159 | com.squareup.okhttp3 160 | okhttp 161 | 3.14.9 162 | 163 | 164 | com.squareup.okhttp3 165 | logging-interceptor 166 | 3.14.9 167 | 168 | 169 | 170 | 171 | 172 | 173 | repo.jenkins-ci.org 174 | https://repo.jenkins-ci.org/public/ 175 | 176 | 177 | tags 178 | Custom Tags 179 | https://raw.githubusercontent.com/t-wanl/Jenkins-Dynamic-Databinding-UI-Control-Library/master/jar/ 180 | 181 | 182 | 183 | 184 | 185 | repo.jenkins-ci.org 186 | https://repo.jenkins-ci.org/public/ 187 | 188 | 189 | 190 | 191 | 192 | 193 | org.apache.maven.plugins 194 | maven-surefire-plugin 195 | 3.0.0-M5 196 | 197 | 198 | org.codehaus.mojo 199 | cobertura-maven-plugin 200 | 2.7 201 | 202 | 203 | html 204 | xml 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | 213 | 214 | 215 | 216 | download-binaries 217 | 218 | 219 | 220 | org.apache.maven.plugins 221 | maven-antrun-plugin 222 | 3.0.0 223 | 224 | 225 | generate-resources 226 | 227 | 228 | 229 | 230 | 231 | 232 | 233 | 234 | 235 | 236 | 237 | 239 | 241 | 243 | 244 | 246 | 248 | 250 | 251 | 252 | 253 | 255 | 256 | 257 | 258 | 259 | 260 | run 261 | 262 | 263 | 264 | 265 | 266 | 267 | 268 | 269 | 270 | -------------------------------------------------------------------------------- /roadmap.md: -------------------------------------------------------------------------------- 1 | 2 | # Road beyond v1.0.0 3 | 4 | ## Bugs 5 | - Avoid using an extra executor 6 | - Naming conflicts in resources 7 | 8 | ## Features 9 | - Supporting creating multiple resources from a single input. 10 | - Pipeline DSL for creating resources 11 | 12 | ## Tekton Version Support 13 | - v0.24+ 14 | -------------------------------------------------------------------------------- /src/main/java/org/waveywaves/jenkins/plugins/tekton/client/LogUtils.java: -------------------------------------------------------------------------------- 1 | package org.waveywaves.jenkins.plugins.tekton.client; 2 | 3 | import com.google.common.io.LineReader; 4 | 5 | import java.io.IOException; 6 | import java.io.InputStream; 7 | import java.io.InputStreamReader; 8 | import java.nio.charset.StandardCharsets; 9 | import java.util.logging.Level; 10 | import java.util.logging.Logger; 11 | 12 | /** 13 | * Some helper methods to log errors 14 | */ 15 | public class LogUtils { 16 | 17 | public static void logStream(InputStream in, Logger logger, boolean error) throws IOException { 18 | LineReader reader = new LineReader(new InputStreamReader(in, StandardCharsets.UTF_8)); 19 | while (true) { 20 | String line = reader.readLine(); 21 | if (line == null) { 22 | break; 23 | } 24 | if (error) { 25 | logger.log(Level.WARNING, line); 26 | } else { 27 | logger.info(line); 28 | } 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/main/java/org/waveywaves/jenkins/plugins/tekton/client/TektonUtils.java: -------------------------------------------------------------------------------- 1 | package org.waveywaves.jenkins.plugins.tekton.client; 2 | 3 | import io.fabric8.kubernetes.client.Config; 4 | import io.fabric8.kubernetes.client.ConfigBuilder; 5 | import io.fabric8.kubernetes.client.DefaultKubernetesClient; 6 | import io.fabric8.kubernetes.client.KubernetesClient; 7 | import io.fabric8.tekton.client.DefaultTektonClient; 8 | import io.fabric8.tekton.client.TektonClient; 9 | import org.waveywaves.jenkins.plugins.tekton.client.global.ClusterConfig; 10 | 11 | import java.io.BufferedReader; 12 | import java.io.ByteArrayInputStream; 13 | import java.io.IOException; 14 | import java.io.InputStreamReader; 15 | import java.io.InputStream; 16 | import java.net.URL; 17 | import java.nio.charset.StandardCharsets; 18 | import java.util.ArrayList; 19 | import java.util.HashMap; 20 | import java.util.List; 21 | import java.util.Map; 22 | import java.util.logging.Logger; 23 | 24 | public class TektonUtils { 25 | private static final Logger logger = Logger.getLogger(TektonUtils.class.getName()); 26 | public static final String DEFAULT_CLIENT_KEY = "default"; 27 | private static Map tektonClientMap = new HashMap<>(); 28 | private static Map kubernetesClientMap = new HashMap<>(); 29 | 30 | public enum TektonResourceType { 31 | task, 32 | taskrun, 33 | pipeline, 34 | pipelinerun 35 | } 36 | 37 | public synchronized static void initializeKubeClients(Config config) { 38 | tektonClientMap = new HashMap<>(); 39 | kubernetesClientMap = new HashMap<>(); 40 | 41 | logger.info("Initializing Kube and Tekton Clients"); 42 | TektonClient tektonClient = new DefaultTektonClient(config); 43 | KubernetesClient kubernetesClient = new DefaultKubernetesClient(config); 44 | 45 | tektonClientMap.put(DEFAULT_CLIENT_KEY, tektonClient); 46 | kubernetesClientMap.put(DEFAULT_CLIENT_KEY, kubernetesClient); 47 | logger.info("Added Clients for " + DEFAULT_CLIENT_KEY); 48 | } 49 | 50 | public synchronized static void initializeKubeClients(List clusterConfigs) { 51 | tektonClientMap = new HashMap<>(); 52 | kubernetesClientMap = new HashMap<>(); 53 | 54 | logger.info("Initializing Kube and Tekton Clients"); 55 | if (clusterConfigs.size() > 0) { 56 | for (ClusterConfig cc: clusterConfigs) { 57 | ConfigBuilder configBuilder = new ConfigBuilder() 58 | .withMasterUrl(cc.getMasterUrl()) 59 | .withNamespace(cc.getDefaultNamespace()); 60 | 61 | TektonClient tektonClient = new DefaultTektonClient(configBuilder.build()); 62 | KubernetesClient kubernetesClient = new DefaultKubernetesClient(configBuilder.build()); 63 | 64 | tektonClientMap.put(cc.getName(), tektonClient); 65 | kubernetesClientMap.put(cc.getName(), kubernetesClient); 66 | logger.info("Added Clients for " + cc.getName()); 67 | } 68 | } 69 | 70 | if (!tektonClientMap.containsKey(DEFAULT_CLIENT_KEY)) { 71 | tektonClientMap.put(DEFAULT_CLIENT_KEY, new DefaultTektonClient()); 72 | kubernetesClientMap.put(DEFAULT_CLIENT_KEY, new DefaultKubernetesClient()); 73 | logger.info("Added Default Clients"); 74 | } 75 | } 76 | 77 | public synchronized static void shutdownKubeClients() { 78 | if (!tektonClientMap.isEmpty() && !kubernetesClientMap.isEmpty()) { 79 | for (TektonClient c : tektonClientMap.values()) { 80 | if (c != null) { 81 | c.close(); 82 | } 83 | } 84 | for (KubernetesClient c : kubernetesClientMap.values()) { 85 | if (c != null) { 86 | c.close(); 87 | } 88 | } 89 | } 90 | } 91 | 92 | public static List getKindFromInputStream(InputStream inputStream, String inputType) { 93 | List kind = new ArrayList(); 94 | try { 95 | int nBytes = inputStream.available(); 96 | byte[] bytes = new byte[nBytes]; 97 | inputStream.read(bytes, 0, nBytes); 98 | String readInput = new String(bytes, StandardCharsets.UTF_8); 99 | logger.info("Creating from "+ inputType); 100 | 101 | String[] yamlLineByLine = readInput.split(System.lineSeparator()); 102 | for (int i=0; i < yamlLineByLine.length; i++){ 103 | String yamlLine = yamlLineByLine[i]; 104 | if (yamlLine.startsWith("kind")){ 105 | String kindName = yamlLine.split(":")[1].trim().toLowerCase(); 106 | kind.add(TektonResourceType.valueOf(kindName)); 107 | } 108 | } 109 | } catch(IOException e){ 110 | logger.warning("IOException occurred "+e.toString()); 111 | } 112 | 113 | return kind; 114 | } 115 | 116 | public static InputStream urlToByteArrayStream(URL url) { 117 | InputStream inputStream = null; 118 | BufferedReader reader = null; 119 | try { 120 | inputStream = url.openStream(); 121 | reader = new BufferedReader(new InputStreamReader(inputStream, StandardCharsets.UTF_8)); 122 | String response = ""; 123 | StringBuffer sb = new StringBuffer(); 124 | for (String line; (line = reader.readLine()) != null; response = sb.append(line).append("\n").toString()); 125 | inputStream = new ByteArrayInputStream(response.getBytes(StandardCharsets.UTF_8)); 126 | } catch (IOException e){ 127 | logger.warning("IOException occurred "+ e.toString()); 128 | } finally { 129 | if (inputStream != null) { 130 | try { 131 | inputStream.close(); 132 | } catch (IOException e) { 133 | logger.warning("IOException occurred "+ e.toString()); 134 | } 135 | } 136 | if (reader != null) { 137 | try { 138 | reader.close(); 139 | } catch (IOException e) { 140 | logger.warning("IOException occurred "+ e.toString()); 141 | } 142 | } 143 | } 144 | 145 | return inputStream; 146 | } 147 | 148 | public synchronized static Map getTektonClientMap(){ 149 | return tektonClientMap; 150 | } 151 | 152 | public synchronized static Map getKubernetesClientMap() { 153 | return kubernetesClientMap; 154 | } 155 | 156 | public synchronized static TektonClient getTektonClient(String name){ 157 | return tektonClientMap.get(name); 158 | } 159 | 160 | public synchronized static KubernetesClient getKubernetesClient(String name) { 161 | return kubernetesClientMap.get(name); 162 | } 163 | } 164 | 165 | -------------------------------------------------------------------------------- /src/main/java/org/waveywaves/jenkins/plugins/tekton/client/ToolUtils.java: -------------------------------------------------------------------------------- 1 | package org.waveywaves.jenkins.plugins.tekton.client; 2 | 3 | import org.apache.commons.lang.SystemUtils; 4 | 5 | import java.io.File; 6 | import java.io.IOException; 7 | import java.io.InputStream; 8 | import java.nio.file.Files; 9 | import java.util.logging.Level; 10 | import java.util.logging.Logger; 11 | 12 | /** 13 | * A helper class for accessing the jx-pipeline-effective binary 14 | */ 15 | public class ToolUtils { 16 | private static final Logger LOGGER = Logger.getLogger(ToolUtils.class.getName()); 17 | 18 | private static String jxPipelineFile = System.getenv("JX_PIPELINE_EFFECTIVE_PATH"); 19 | 20 | /** 21 | * @return the file name location of the jx-pipeline-effective binary 22 | * @throws IOException 23 | * @param classLoader 24 | */ 25 | public static synchronized String getJXPipelineBinary(ClassLoader classLoader) throws IOException { 26 | if (jxPipelineFile == null) { 27 | File f = File.createTempFile("jx-pipeline-effective-", ""); 28 | boolean success = f.delete(); 29 | if (!success) { 30 | LOGGER.log(Level.WARNING, "unable to delete temporary file " + f); 31 | } 32 | f.deleteOnExit(); 33 | 34 | String platform = "linux"; 35 | if (SystemUtils.IS_OS_MAC || SystemUtils.IS_OS_MAC_OSX) { 36 | platform = "mac"; 37 | } else if (SystemUtils.IS_OS_WINDOWS) { 38 | platform = "windows"; 39 | } 40 | 41 | String resource = "org/waveywaves/jenkins/plugins/tekton/client/jxp/" + platform + "/jx-pipeline-effective"; 42 | InputStream in = classLoader.getResourceAsStream(resource); 43 | if (in == null) { 44 | throw new IOException("could not find resource on classpath: " + resource); 45 | } 46 | 47 | String path = f.getPath(); 48 | try { 49 | Files.copy(in, f.toPath()); 50 | } catch (IOException e) { 51 | LOGGER.log(Level.SEVERE, "failed to copy jx-pipeline-effective to " + path + " due to " + e); 52 | throw new IOException("failed to copy jx-pipeline-effective to " + path + " cause: " + e, e); 53 | } 54 | 55 | boolean chmodSuccess = f.setExecutable(true); 56 | if (!chmodSuccess) { 57 | throw new IOException("failed make the file executable: " + path); 58 | } 59 | 60 | jxPipelineFile = path; 61 | 62 | LOGGER.info("saved jx-pipeline-effective binary to " + jxPipelineFile); 63 | } 64 | return jxPipelineFile; 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /src/main/java/org/waveywaves/jenkins/plugins/tekton/client/build/BaseStep.java: -------------------------------------------------------------------------------- 1 | package org.waveywaves.jenkins.plugins.tekton.client.build; 2 | 3 | import hudson.tasks.Builder; 4 | import io.fabric8.kubernetes.client.Client; 5 | import io.fabric8.kubernetes.client.dsl.MixedOperation; 6 | import io.fabric8.kubernetes.client.dsl.Resource; 7 | import io.fabric8.tekton.pipeline.v1beta1.*; 8 | import io.fabric8.tekton.resource.v1alpha1.PipelineResource; 9 | import io.fabric8.tekton.resource.v1alpha1.PipelineResourceList; 10 | import jenkins.tasks.SimpleBuildStep; 11 | 12 | public abstract class BaseStep extends Builder implements SimpleBuildStep { 13 | protected transient Client tektonClient; 14 | protected transient Client kubernetesClient; 15 | 16 | protected MixedOperation> 17 | taskRunClient; 18 | protected MixedOperation> 19 | taskClient; 20 | protected MixedOperation> 21 | pipelineClient; 22 | protected MixedOperation> 23 | pipelineRunClient; 24 | 25 | @edu.umd.cs.findbugs.annotations.SuppressFBWarnings("URF_UNREAD_PUBLIC_OR_PROTECTED_FIELD") 26 | protected MixedOperation> 27 | pipelineResourceClient; 28 | 29 | public enum InputType { 30 | URL, 31 | YAML, 32 | FILE, 33 | Interactive 34 | } 35 | 36 | public void setKubernetesClient(Client kc) { 37 | this.kubernetesClient = kc; 38 | } 39 | 40 | public void setTektonClient(Client tc) { 41 | this.tektonClient = tc; 42 | } 43 | 44 | public void setTaskRunClient( 45 | MixedOperation> trc){ 46 | this.taskRunClient = trc; 47 | } 48 | 49 | public void setTaskClient( 50 | MixedOperation> tc){ 51 | this.taskClient = tc; 52 | } 53 | 54 | public void setPipelineClient( 55 | MixedOperation> pc){ 56 | this.pipelineClient = pc; 57 | } 58 | 59 | public void setPipelineRunClient( 60 | MixedOperation> prc){ 61 | this.pipelineRunClient = prc; 62 | } 63 | 64 | public void setPipelineResourceClient( 65 | MixedOperation> presc){ 66 | this.pipelineResourceClient = presc; 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /src/main/java/org/waveywaves/jenkins/plugins/tekton/client/build/create/CreateCustomTaskrun.java: -------------------------------------------------------------------------------- 1 | package org.waveywaves.jenkins.plugins.tekton.client.build.create; 2 | 3 | import hudson.Extension; 4 | import hudson.FilePath; 5 | import hudson.Launcher; 6 | import hudson.model.AbstractProject; 7 | import hudson.model.Run; 8 | import hudson.model.TaskListener; 9 | import hudson.tasks.BuildStepDescriptor; 10 | import hudson.tasks.Builder; 11 | import hudson.util.FormValidation; 12 | import hudson.util.ListBoxModel; 13 | import io.fabric8.kubernetes.api.model.ObjectMeta; 14 | import io.fabric8.kubernetes.api.model.PersistentVolumeClaimVolumeSource; 15 | import io.fabric8.tekton.client.TektonClient; 16 | import io.fabric8.tekton.pipeline.v1beta1.*; 17 | import org.jenkinsci.Symbol; 18 | import org.kohsuke.stapler.DataBoundConstructor; 19 | import org.kohsuke.stapler.QueryParameter; 20 | import org.waveywaves.jenkins.plugins.tekton.client.TektonUtils; 21 | import org.waveywaves.jenkins.plugins.tekton.client.build.BaseStep; 22 | 23 | import javax.annotation.Nonnull; 24 | import java.io.IOException; 25 | import java.io.PrintStream; 26 | import java.util.ArrayList; 27 | import java.util.List; 28 | import java.util.logging.Logger; 29 | 30 | @Symbol("customCreateTaskrun") 31 | public class CreateCustomTaskrun extends BaseStep { 32 | private static final Logger logger = Logger.getLogger(CreateCustomTaskrun.class.getName()); 33 | private String clusterName; 34 | private PrintStream consoleLogger; 35 | private String kind; 36 | // ObjectMeta 37 | private String name; 38 | private String generateName; 39 | private String namespace; 40 | // Spec 41 | private List params; 42 | private List workspaces; 43 | private String taskRef; 44 | 45 | @DataBoundConstructor 46 | public CreateCustomTaskrun(final String name, 47 | final String generateName, 48 | final String namespace, 49 | final String clusterName, 50 | final List workspaces, 51 | final List params, 52 | final String taskRef){ 53 | super(); 54 | this.kind = TektonUtils.TektonResourceType.taskrun.toString(); 55 | this.name = name; 56 | this.generateName = generateName; 57 | this.namespace = namespace; 58 | this.taskRef = taskRef; 59 | this.workspaces = workspaces; 60 | this.params = params; 61 | this.clusterName = clusterName; 62 | } 63 | 64 | public String getKind() { return this.kind; } 65 | public String getName() { return this.name; } 66 | public String getNamespace() { return this.namespace; } 67 | public String getTaskRef() { return this.taskRef; } 68 | public String getGenerateName() { return this.generateName; } 69 | public List getWorkspaces() { return this.workspaces; } 70 | public List getParams() { return this.params; } 71 | 72 | @Override 73 | public void perform(@Nonnull Run run, @Nonnull FilePath filePath, @Nonnull Launcher launcher, @Nonnull TaskListener taskListener) throws InterruptedException, IOException { 74 | consoleLogger = taskListener.getLogger(); 75 | logTektonTaskrun(); 76 | runCreate(); 77 | } 78 | 79 | private void runCreate(){ 80 | ObjectMeta metadata = new ObjectMeta(); 81 | metadata.setName(getName()); 82 | metadata.setNamespace(getNamespace()); 83 | metadata.setGenerateName(getGenerateName()); 84 | 85 | TaskRef taskRef = new TaskRef(); 86 | taskRef.setKind("Task"); 87 | taskRef.setApiVersion("tekton.dev/v1beta1"); 88 | taskRef.setName(getTaskRef()); 89 | 90 | TaskRunSpec spec = new TaskRunSpec(); 91 | spec.setTaskRef(taskRef); 92 | spec.setWorkspaces(workspacesToWorkspaceBindingList()); 93 | spec.setParams(paramsToParamList()); 94 | 95 | TaskRunBuilder taskRunBuilder = new TaskRunBuilder(); 96 | taskRunBuilder.withApiVersion("tekton.dev/v1beta1"); 97 | taskRunBuilder.withKind("TaskRun"); 98 | taskRunBuilder.withMetadata(metadata); 99 | taskRunBuilder.withSpec(spec); 100 | 101 | TaskRun taskRun = taskRunBuilder.build(); 102 | if (taskClient == null) { 103 | TektonClient tc = TektonUtils.getTektonClient(clusterName); 104 | setTaskRunClient(tc.v1beta1().taskRuns()); 105 | } 106 | taskRun = taskRunClient.create(taskRun); 107 | String resourceName = taskRun.getMetadata().getName(); 108 | 109 | consoleLogger.print(String.format("Created Task with Name %s", resourceName)); 110 | } 111 | 112 | private List paramsToParamList() { 113 | List paramList = new ArrayList<>(); 114 | for (TektonParam p: this.params) { 115 | Param param = new Param(); 116 | param.setName(p.getName()); 117 | 118 | ArrayOrString s = new ArrayOrString(); 119 | s.setStringVal(p.getValue()); 120 | param.setValue(s); 121 | 122 | paramList.add(param); 123 | } 124 | return paramList; 125 | } 126 | 127 | public List workspacesToWorkspaceBindingList() { 128 | List wsbList = new ArrayList<>(); 129 | for (TektonWorkspaceBind w: this.workspaces){ 130 | WorkspaceBinding wsb = new WorkspaceBinding(); 131 | wsb.setName(w.getName()); 132 | wsb.setPersistentVolumeClaim(new PersistentVolumeClaimVolumeSource(w.getClaimName(), false)); 133 | 134 | wsbList.add(wsb); 135 | } 136 | return wsbList; 137 | } 138 | 139 | private void logTektonTaskrun() { 140 | consoleLogger.println("Creating Resource from Custom Config"); 141 | String l = String.format("Kind: %s%n" + 142 | "Name: %s%n" + 143 | "Namespace: %s%n" + 144 | "\tTaskRef: %s %n", 145 | getKind(), getName(), getNamespace(), getTaskRef()); 146 | consoleLogger.print(l); 147 | } 148 | 149 | @Extension 150 | public static final class DescriptorImpl extends BuildStepDescriptor { 151 | public DescriptorImpl() { 152 | load(); 153 | } 154 | public FormValidation doCheckName(@QueryParameter(value = "name") final String name){ 155 | if (name.length() == 0){ 156 | return FormValidation.error("Name not provided"); 157 | } 158 | return FormValidation.ok(); 159 | } 160 | 161 | public FormValidation doCheckNamespace(@QueryParameter(value = "namespace") final String namespace){ 162 | if (namespace.length() == 0){ 163 | return FormValidation.error("Namespace not provided"); 164 | } 165 | return FormValidation.ok(); 166 | } 167 | 168 | public ListBoxModel doFillClusterNameItems(@QueryParameter(value = "clusterName") final String clusterName){ 169 | ListBoxModel items = new ListBoxModel(); 170 | for (String cn: TektonUtils.getTektonClientMap().keySet()){ 171 | items.add(cn); 172 | } 173 | return items; 174 | } 175 | 176 | @Override 177 | public boolean isApplicable(Class jobType) { 178 | return true; 179 | } 180 | 181 | @Override 182 | public String getDisplayName() { 183 | return "Tekton : Create TaskRun"; 184 | } 185 | } 186 | } 187 | -------------------------------------------------------------------------------- /src/main/java/org/waveywaves/jenkins/plugins/tekton/client/build/create/TektonArg.java: -------------------------------------------------------------------------------- 1 | package org.waveywaves.jenkins.plugins.tekton.client.build.create; 2 | 3 | import hudson.Extension; 4 | import hudson.model.AbstractDescribableImpl; 5 | import hudson.model.Descriptor; 6 | import org.kohsuke.stapler.DataBoundConstructor; 7 | 8 | public final class TektonArg extends AbstractDescribableImpl { 9 | private final String value; 10 | 11 | @DataBoundConstructor 12 | public TektonArg(String value) { 13 | this.value = value; 14 | } 15 | 16 | public String getValue() { 17 | return value; 18 | } 19 | 20 | @Extension 21 | public static class DescriptorImpl extends Descriptor { 22 | @Override 23 | public String getDisplayName() { 24 | return "arg"; 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/main/java/org/waveywaves/jenkins/plugins/tekton/client/build/create/TektonCommandI.java: -------------------------------------------------------------------------------- 1 | package org.waveywaves.jenkins.plugins.tekton.client.build.create; 2 | 3 | import hudson.Extension; 4 | import hudson.model.AbstractDescribableImpl; 5 | import hudson.model.Descriptor; 6 | import org.kohsuke.stapler.DataBoundConstructor; 7 | 8 | public final class TektonCommandI extends AbstractDescribableImpl { 9 | private final String value; 10 | 11 | @DataBoundConstructor 12 | public TektonCommandI(String value) { 13 | this.value = value; 14 | } 15 | 16 | public String getValue() { 17 | return value; 18 | } 19 | 20 | @Extension 21 | public static class DescriptorImpl extends Descriptor { 22 | @Override 23 | public String getDisplayName() { 24 | return ""; 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/main/java/org/waveywaves/jenkins/plugins/tekton/client/build/create/TektonEnv.java: -------------------------------------------------------------------------------- 1 | package org.waveywaves.jenkins.plugins.tekton.client.build.create; 2 | 3 | import hudson.Extension; 4 | import hudson.model.AbstractDescribableImpl; 5 | import hudson.model.Descriptor; 6 | import org.kohsuke.stapler.DataBoundConstructor; 7 | 8 | public final class TektonEnv extends AbstractDescribableImpl { 9 | private final String name; 10 | private final String value; 11 | 12 | @DataBoundConstructor 13 | public TektonEnv(String name,String value) { 14 | this.name = name; 15 | this.value = value; 16 | } 17 | 18 | public String getName() { 19 | return name; 20 | } 21 | 22 | public String getValue() { 23 | return value; 24 | } 25 | 26 | @Extension 27 | public static class DescriptorImpl extends Descriptor { 28 | @Override 29 | public String getDisplayName() { 30 | return "env"; 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/main/java/org/waveywaves/jenkins/plugins/tekton/client/build/create/TektonParam.java: -------------------------------------------------------------------------------- 1 | package org.waveywaves.jenkins.plugins.tekton.client.build.create; 2 | 3 | import hudson.Extension; 4 | import hudson.model.AbstractDescribableImpl; 5 | import hudson.model.Descriptor; 6 | import org.kohsuke.stapler.DataBoundConstructor; 7 | 8 | public class TektonParam extends AbstractDescribableImpl { 9 | private final String name; 10 | private final String value; 11 | 12 | @DataBoundConstructor 13 | public TektonParam(final String name, 14 | final String value) { 15 | this.name = name; 16 | this.value = value; 17 | } 18 | 19 | public String getName() { 20 | return this.name; 21 | } 22 | 23 | public String getValue() { 24 | return this.value; 25 | } 26 | 27 | @Extension 28 | public static class DescriptorImpl extends Descriptor { 29 | public DescriptorImpl() { 30 | load(); 31 | } 32 | 33 | @Override 34 | public String getDisplayName() { 35 | return "param"; 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/main/java/org/waveywaves/jenkins/plugins/tekton/client/build/create/TektonStep.java: -------------------------------------------------------------------------------- 1 | package org.waveywaves.jenkins.plugins.tekton.client.build.create; 2 | 3 | import hudson.Extension; 4 | import hudson.model.AbstractDescribableImpl; 5 | import hudson.model.Descriptor; 6 | import org.kohsuke.stapler.DataBoundConstructor; 7 | 8 | import java.util.List; 9 | 10 | public class TektonStep extends AbstractDescribableImpl { 11 | private final String name; 12 | private final String image; 13 | 14 | private List args; 15 | private List command; 16 | private List envs; 17 | private String script; 18 | private Boolean tty; 19 | private String workingDir; 20 | 21 | @DataBoundConstructor 22 | public TektonStep(String name, 23 | String image, 24 | List args, 25 | List command, 26 | List envs, 27 | String script, 28 | Boolean tty, 29 | String workingDir) { 30 | this.name = name; 31 | this.image = image; 32 | this.args = args; 33 | this.command = command; 34 | this.envs = envs; 35 | this.script = script; 36 | this.tty = tty; 37 | this.workingDir = workingDir; 38 | } 39 | 40 | public String getName() { 41 | return name; 42 | } 43 | 44 | public String getImage() { 45 | return image; 46 | } 47 | 48 | public List getArgs() { 49 | return args; 50 | } 51 | 52 | public List getCommand() { 53 | return command; 54 | } 55 | 56 | public String getScript() { 57 | return script; 58 | } 59 | 60 | public Boolean getTty() { 61 | return tty; 62 | } 63 | 64 | public String getWorkingDir() { 65 | return workingDir; 66 | } 67 | 68 | public List getEnvs() { 69 | return envs; 70 | } 71 | 72 | @Extension 73 | public static class DescriptorImpl extends Descriptor { 74 | @Override public String getDisplayName() { 75 | return "step"; 76 | } 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /src/main/java/org/waveywaves/jenkins/plugins/tekton/client/build/create/TektonStringParamSpec.java: -------------------------------------------------------------------------------- 1 | package org.waveywaves.jenkins.plugins.tekton.client.build.create; 2 | 3 | import hudson.Extension; 4 | import hudson.model.AbstractDescribableImpl; 5 | import hudson.model.Descriptor; 6 | import org.kohsuke.stapler.DataBoundConstructor; 7 | 8 | public class TektonStringParamSpec extends AbstractDescribableImpl { 9 | private final String name; 10 | private final String description; 11 | private final String defaultValue; 12 | 13 | @DataBoundConstructor 14 | public TektonStringParamSpec(final String name, 15 | final String description, 16 | final String defaultValue) { 17 | this.name = name; 18 | this.description = description; 19 | this.defaultValue = defaultValue; 20 | } 21 | 22 | public String getName() { 23 | return this.name; 24 | } 25 | 26 | public String getDescription() { 27 | return this.description; 28 | } 29 | 30 | public String getDefaultValue() { 31 | return this.defaultValue; 32 | } 33 | 34 | 35 | @Extension 36 | public static class DescriptorImpl extends Descriptor { 37 | public DescriptorImpl() { 38 | load(); 39 | } 40 | 41 | @Override 42 | public String getDisplayName() { 43 | return "param"; 44 | } 45 | } 46 | 47 | 48 | } 49 | -------------------------------------------------------------------------------- /src/main/java/org/waveywaves/jenkins/plugins/tekton/client/build/create/TektonTaskResult.java: -------------------------------------------------------------------------------- 1 | package org.waveywaves.jenkins.plugins.tekton.client.build.create; 2 | 3 | import hudson.Extension; 4 | import hudson.model.AbstractDescribableImpl; 5 | import hudson.model.Descriptor; 6 | import org.kohsuke.stapler.DataBoundConstructor; 7 | 8 | public class TektonTaskResult extends AbstractDescribableImpl { 9 | private final String name; 10 | private final String description; 11 | 12 | @DataBoundConstructor 13 | public TektonTaskResult(final String name, 14 | final String description) { 15 | this.name = name; 16 | this.description = description; 17 | } 18 | 19 | public String getName() { 20 | return this.name; 21 | } 22 | 23 | public String getDescription() { 24 | return this.description; 25 | } 26 | 27 | @Extension 28 | public static class DescriptorImpl extends Descriptor { 29 | public DescriptorImpl() { 30 | load(); 31 | } 32 | 33 | @Override 34 | public String getDisplayName() { 35 | return "result"; 36 | } 37 | } 38 | 39 | } 40 | -------------------------------------------------------------------------------- /src/main/java/org/waveywaves/jenkins/plugins/tekton/client/build/create/TektonWorkspaceBind.java: -------------------------------------------------------------------------------- 1 | package org.waveywaves.jenkins.plugins.tekton.client.build.create; 2 | 3 | import hudson.Extension; 4 | import hudson.model.AbstractDescribableImpl; 5 | import hudson.model.Descriptor; 6 | import org.kohsuke.stapler.DataBoundConstructor; 7 | 8 | public final class TektonWorkspaceBind extends AbstractDescribableImpl { 9 | private final String name; 10 | private final String claimName; 11 | 12 | @DataBoundConstructor 13 | public TektonWorkspaceBind(String name, String claimName) { 14 | this.name = name; 15 | this.claimName = claimName; 16 | } 17 | 18 | public String getName() { 19 | return name; 20 | } 21 | 22 | public String getClaimName() { 23 | return claimName; 24 | } 25 | 26 | @Extension 27 | public static class DescriptorImpl extends Descriptor { 28 | @Override 29 | public String getDisplayName() { 30 | return "workspace"; 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/main/java/org/waveywaves/jenkins/plugins/tekton/client/build/create/TektonWorkspaceDecl.java: -------------------------------------------------------------------------------- 1 | package org.waveywaves.jenkins.plugins.tekton.client.build.create; 2 | 3 | import hudson.Extension; 4 | import hudson.model.AbstractDescribableImpl; 5 | import hudson.model.Descriptor; 6 | import org.kohsuke.stapler.DataBoundConstructor; 7 | 8 | public final class TektonWorkspaceDecl extends AbstractDescribableImpl { 9 | private final String name; 10 | private final String description; 11 | private final String mountPath; 12 | private final Boolean readOnly; 13 | 14 | @DataBoundConstructor 15 | public TektonWorkspaceDecl(String name, String description, String mountPath, Boolean readOnly) { 16 | this.name = name; 17 | this.description = description; 18 | this.mountPath = mountPath; 19 | this.readOnly = readOnly; 20 | } 21 | 22 | public String getName() { 23 | return name; 24 | } 25 | 26 | public String getDescription() { 27 | return description; 28 | } 29 | 30 | public String getMountPath() { 31 | return mountPath; 32 | } 33 | 34 | public Boolean getReadOnly() { 35 | return readOnly; 36 | } 37 | 38 | @Extension 39 | public static class DescriptorImpl extends Descriptor { 40 | @Override 41 | public String getDisplayName() { 42 | return "workspace"; 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/main/java/org/waveywaves/jenkins/plugins/tekton/client/build/delete/DeleteRaw.java: -------------------------------------------------------------------------------- 1 | package org.waveywaves.jenkins.plugins.tekton.client.build.delete; 2 | 3 | import com.google.common.base.Strings; 4 | import hudson.Extension; 5 | import hudson.FilePath; 6 | import hudson.Launcher; 7 | import hudson.model.AbstractProject; 8 | import hudson.model.Run; 9 | import hudson.model.TaskListener; 10 | import hudson.tasks.BuildStepDescriptor; 11 | import hudson.tasks.Builder; 12 | import hudson.util.FormValidation; 13 | import hudson.util.ListBoxModel; 14 | import io.fabric8.tekton.client.TektonClient; 15 | import io.fabric8.tekton.pipeline.v1beta1.Pipeline; 16 | import io.fabric8.tekton.pipeline.v1beta1.PipelineRun; 17 | import io.fabric8.tekton.pipeline.v1beta1.Task; 18 | import io.fabric8.tekton.pipeline.v1beta1.TaskRun; 19 | import io.fabric8.tekton.resource.v1alpha1.PipelineResource; 20 | import org.jenkinsci.Symbol; 21 | import org.kohsuke.stapler.DataBoundConstructor; 22 | import org.kohsuke.stapler.DataBoundSetter; 23 | import org.kohsuke.stapler.QueryParameter; 24 | import org.waveywaves.jenkins.plugins.tekton.client.TektonUtils; 25 | import org.waveywaves.jenkins.plugins.tekton.client.TektonUtils.TektonResourceType; 26 | import org.waveywaves.jenkins.plugins.tekton.client.build.BaseStep; 27 | 28 | import javax.annotation.Nonnull; 29 | import java.io.IOException; 30 | import java.util.List; 31 | import java.util.logging.Logger; 32 | 33 | @Symbol("tektonDeleteStep") 34 | public class DeleteRaw extends BaseStep { 35 | private static final Logger logger = Logger.getLogger(DeleteRaw.class.getName()); 36 | private String resourceType; 37 | private String resourceName; 38 | private String clusterName; 39 | 40 | @DataBoundConstructor 41 | public DeleteRaw(String resourceType, String clusterName, DeleteAllBlock deleteAllStatus) { 42 | super(); 43 | this.resourceType = resourceType; 44 | this.resourceName = deleteAllStatus != null ? deleteAllStatus.resourceName : null; 45 | this.clusterName = clusterName; 46 | setTektonClient(TektonUtils.getTektonClient(getClusterName())); 47 | } 48 | 49 | public static class DeleteAllBlock { 50 | private String resourceName; 51 | 52 | @DataBoundConstructor 53 | public DeleteAllBlock(String resourceName) { 54 | this.resourceName = resourceName; 55 | } 56 | } 57 | 58 | public String getClusterName() { 59 | if (Strings.isNullOrEmpty(clusterName)) { 60 | clusterName = TektonUtils.DEFAULT_CLIENT_KEY; 61 | } 62 | return clusterName; 63 | } 64 | public String getResourceType(){ 65 | return this.resourceType; 66 | } 67 | public String getResourceName(){ 68 | return this.resourceName; 69 | } 70 | 71 | @DataBoundSetter 72 | public void setClusterName(String clusterName) { 73 | this.clusterName = clusterName; 74 | setTektonClient(TektonUtils.getTektonClient(getClusterName())); 75 | } 76 | 77 | @DataBoundSetter 78 | protected void setResourceType(String resourceType) { 79 | this.resourceType = resourceType; 80 | } 81 | 82 | @DataBoundSetter 83 | protected void setResourceName(String resourceName) { 84 | this.resourceName = resourceName; 85 | } 86 | 87 | private TektonResourceType getTypedResourceType(){ 88 | return TektonResourceType.valueOf(getResourceType()); 89 | } 90 | 91 | 92 | 93 | @Override 94 | public void perform(@Nonnull Run run, @Nonnull FilePath workspace, @Nonnull Launcher launcher, @Nonnull TaskListener listener) throws InterruptedException, IOException { 95 | runDelete(); 96 | } 97 | 98 | protected boolean runDelete(){ 99 | return deleteWithResourceSpecificClient(this.getTypedResourceType()); 100 | } 101 | 102 | private boolean deleteWithResourceSpecificClient(TektonResourceType resourceType) { 103 | switch (resourceType) { 104 | case task: 105 | return deleteTask(); 106 | case taskrun: 107 | return deleteTaskRun(); 108 | case pipeline: 109 | return deletePipeline(); 110 | case pipelinerun: 111 | return deletePipelineRun(); 112 | default: 113 | return false; 114 | } 115 | } 116 | 117 | public Boolean deleteTask() { 118 | if (taskClient == null) { 119 | TektonClient tc = (TektonClient) tektonClient; 120 | setTaskClient(tc.v1beta1().tasks()); 121 | } 122 | List taskList = taskClient.list().getItems(); 123 | Boolean isDeleted = false; 124 | if (this.getResourceName() == null) { 125 | return taskClient.delete(taskList); 126 | } 127 | for (Task task : taskList) { 128 | String taskName = task.getMetadata().getName(); 129 | if (taskName.equals(this.getResourceName())) { 130 | isDeleted = taskClient.delete(task); 131 | break; 132 | } 133 | } 134 | return isDeleted; 135 | } 136 | 137 | public Boolean deleteTaskRun() { 138 | if (taskRunClient == null) { 139 | TektonClient tc = (TektonClient) tektonClient; 140 | setTaskRunClient(tc.v1beta1().taskRuns()); 141 | } 142 | List taskRunList = taskRunClient.list().getItems(); 143 | Boolean isDeleted = false; 144 | if (this.getResourceName() == null) { 145 | return taskRunClient.delete(taskRunList); 146 | } 147 | for (TaskRun taskRun : taskRunList) { 148 | String taskRunName = taskRun.getMetadata().getName(); 149 | if (taskRunName.equals(this.getResourceName())) { 150 | isDeleted = taskRunClient.delete(taskRun); 151 | break; 152 | } 153 | } 154 | return isDeleted; 155 | } 156 | 157 | public Boolean deletePipeline() { 158 | if (pipelineClient == null) { 159 | TektonClient tc = (TektonClient) tektonClient; 160 | setPipelineClient(tc.v1beta1().pipelines()); 161 | } 162 | List pipelineList = pipelineClient.list().getItems(); 163 | Boolean isDeleted = false; 164 | if (this.getResourceName() == null) { 165 | return pipelineClient.delete(pipelineList); 166 | } 167 | for (Pipeline pipeline : pipelineList) { 168 | String pipelineName = pipeline.getMetadata().getName(); 169 | if (pipelineName.equals(this.getResourceName())) { 170 | isDeleted = pipelineClient.delete(pipeline); 171 | break; 172 | } 173 | } 174 | return isDeleted; 175 | } 176 | 177 | public Boolean deletePipelineRun() { 178 | if (pipelineRunClient == null) { 179 | TektonClient tc = (TektonClient) tektonClient; 180 | setPipelineRunClient(tc.v1beta1().pipelineRuns()); 181 | } 182 | List pipelineRunList = pipelineRunClient.list().getItems(); 183 | Boolean isDeleted = false; 184 | if (this.getResourceName() == null) { 185 | return pipelineRunClient.delete(pipelineRunList); 186 | } 187 | for (PipelineRun pipelineRun : pipelineRunList) { 188 | String pipelineRunName = pipelineRun.getMetadata().getName(); 189 | if (pipelineRunName.equals(this.getResourceName())) { 190 | isDeleted = pipelineRunClient.delete(pipelineRun); 191 | break; 192 | } 193 | } 194 | return isDeleted; 195 | } 196 | 197 | @Extension 198 | public static final class DescriptorImpl extends BuildStepDescriptor { 199 | public FormValidation doCheckResourceName(@QueryParameter(value = "resourceName") final String resourceName){ 200 | if (resourceName.length() == 0){ 201 | return FormValidation.error("Resource Name not provided"); 202 | } 203 | return FormValidation.ok(); 204 | } 205 | 206 | public ListBoxModel doFillResourceTypeItems(@QueryParameter(value = "input") final String input){ 207 | ListBoxModel items = new ListBoxModel(); 208 | items.add(TektonResourceType.task.toString()); 209 | items.add(TektonResourceType.taskrun.toString()); 210 | items.add(TektonResourceType.pipeline.toString()); 211 | items.add(TektonResourceType.pipelinerun.toString()); 212 | return items; 213 | } 214 | 215 | public ListBoxModel doFillClusterNameItems(@QueryParameter(value = "clusterName") final String clusterName){ 216 | ListBoxModel items = new ListBoxModel(); 217 | for (String cn: TektonUtils.getTektonClientMap().keySet()){ 218 | items.add(cn); 219 | } 220 | return items; 221 | } 222 | 223 | @Override 224 | public boolean isApplicable(Class jobType) { 225 | return true; 226 | } 227 | 228 | @Override 229 | public String getDisplayName() { 230 | return "Tekton : Delete Resource (Raw)"; 231 | } 232 | } 233 | } 234 | -------------------------------------------------------------------------------- /src/main/java/org/waveywaves/jenkins/plugins/tekton/client/global/ClusterConfig.java: -------------------------------------------------------------------------------- 1 | package org.waveywaves.jenkins.plugins.tekton.client.global; 2 | 3 | import hudson.Extension; 4 | import hudson.model.AbstractDescribableImpl; 5 | import hudson.model.Descriptor; 6 | import org.kohsuke.stapler.DataBoundConstructor; 7 | 8 | public final class ClusterConfig extends AbstractDescribableImpl { 9 | private final String name; 10 | private final String masterUrl; 11 | private final String defaultNamespace; 12 | 13 | @DataBoundConstructor 14 | public ClusterConfig(final String name, 15 | final String masterUrl, 16 | final String defaultNamespace) { 17 | this.name = name; 18 | this.masterUrl = masterUrl; 19 | this.defaultNamespace = defaultNamespace; 20 | } 21 | 22 | public String getMasterUrl() { 23 | return this.masterUrl; 24 | } 25 | 26 | public String getDefaultNamespace() { 27 | return this.defaultNamespace; 28 | } 29 | 30 | public String getName() { 31 | return name; 32 | } 33 | 34 | @Extension 35 | public static class DescriptorImpl extends Descriptor { 36 | @Override 37 | public String getDisplayName() { 38 | return "k8s cluster with Tekton"; 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/main/java/org/waveywaves/jenkins/plugins/tekton/client/global/TektonGlobalConfiguration.java: -------------------------------------------------------------------------------- 1 | package org.waveywaves.jenkins.plugins.tekton.client.global; 2 | 3 | import hudson.Extension; 4 | import io.fabric8.kubernetes.client.KubernetesClientException; 5 | import jenkins.model.GlobalConfiguration; 6 | import jenkins.model.Jenkins; 7 | import net.sf.json.JSONObject; 8 | import org.kohsuke.stapler.StaplerRequest; 9 | import org.waveywaves.jenkins.plugins.tekton.client.TektonUtils; 10 | 11 | import java.util.ArrayList; 12 | import java.util.List; 13 | import java.util.logging.Logger; 14 | 15 | import static java.util.logging.Level.SEVERE; 16 | 17 | @Extension 18 | public class TektonGlobalConfiguration extends GlobalConfiguration { 19 | private static final Logger logger = Logger.getLogger(TektonGlobalConfiguration.class.getName()); 20 | private transient List clusterConfigs = new ArrayList<>(); 21 | 22 | public TektonGlobalConfiguration(){ 23 | load(); 24 | configChange(); 25 | } 26 | 27 | public List getClusterConfigs() { 28 | return this.clusterConfigs; 29 | } 30 | 31 | public void setClusterConfigs(List clusterConfigs) { 32 | this.clusterConfigs = clusterConfigs; 33 | } 34 | 35 | public static TektonGlobalConfiguration get() { 36 | return GlobalConfiguration.all().get(TektonGlobalConfiguration.class); 37 | } 38 | 39 | @Override 40 | public boolean configure(final StaplerRequest req, final JSONObject formData) { 41 | Jenkins.getInstance().checkPermission(Jenkins.ADMINISTER); 42 | setClusterConfigs(req.bindJSONToList(ClusterConfig.class, formData.get("clusterConfigs"))); 43 | save(); 44 | return true; 45 | } 46 | 47 | public synchronized void configChange() { 48 | logger.info("Tekton Client Plugin processing a newly supplied configuration"); 49 | 50 | TektonUtils.shutdownKubeClients(); 51 | try { 52 | TektonUtils.initializeKubeClients(this.clusterConfigs); 53 | } catch (KubernetesClientException e){ 54 | Throwable exceptionOrCause = (e.getCause() != null) ? e.getCause() : e; 55 | logger.log(SEVERE, "Failed to configure Tekton Client Plugin: " + exceptionOrCause); 56 | } 57 | } 58 | } -------------------------------------------------------------------------------- /src/main/java/org/waveywaves/jenkins/plugins/tekton/client/logwatch/PipelineRunLogWatch.java: -------------------------------------------------------------------------------- 1 | package org.waveywaves.jenkins.plugins.tekton.client.logwatch; 2 | 3 | import com.google.common.base.Strings; 4 | import io.fabric8.kubernetes.api.model.*; 5 | import io.fabric8.kubernetes.client.KubernetesClient; 6 | import io.fabric8.tekton.client.TektonClient; 7 | import io.fabric8.tekton.pipeline.v1beta1.*; 8 | 9 | import java.io.IOException; 10 | import java.io.OutputStream; 11 | import java.nio.charset.StandardCharsets; 12 | import java.util.List; 13 | import java.util.concurrent.ConcurrentHashMap; 14 | import java.util.logging.Logger; 15 | 16 | public class PipelineRunLogWatch implements Runnable { 17 | 18 | private static final Logger LOGGER = Logger.getLogger(PipelineRunLogWatch.class.getName()); 19 | 20 | private static final String PIPELINE_TASK_LABEL_NAME = "tekton.dev/pipelineTask"; 21 | private static final String PIPELINE_RUN_LABEL_NAME = "tekton.dev/pipelineRun"; 22 | 23 | private final PipelineRun pipelineRun; 24 | 25 | private KubernetesClient kubernetesClient; 26 | private TektonClient tektonClient; 27 | private Exception exception; 28 | OutputStream consoleLogger; 29 | 30 | //ConcurrentHashMap taskRunsOnWatch = new ConcurrentHashMap(); 31 | //ConcurrentHashMap taskRunsWatchDone = new ConcurrentHashMap(); 32 | 33 | 34 | 35 | public PipelineRunLogWatch(KubernetesClient kubernetesClient, TektonClient tektonClient, PipelineRun pipelineRun, OutputStream consoleLogger) { 36 | this.kubernetesClient = kubernetesClient; 37 | this.tektonClient = tektonClient; 38 | this.pipelineRun = pipelineRun; 39 | this.consoleLogger = consoleLogger; 40 | } 41 | 42 | /** 43 | * @return the exception if the pipeline failed to succeed 44 | */ 45 | public Exception getException() { 46 | return exception; 47 | } 48 | 49 | @Override 50 | public void run() { 51 | String pipelineRunName = pipelineRun.getMetadata().getName(); 52 | String pipelineRunUid = pipelineRun.getMetadata().getUid(); 53 | String ns = pipelineRun.getMetadata().getNamespace(); 54 | 55 | List pipelineTasks = pipelineRun.getSpec().getPipelineSpec().getTasks(); 56 | 57 | for (PipelineTask pt: pipelineTasks){ 58 | String pipelineTaskName = pt.getName(); 59 | LOGGER.info("Streaming logs for PipelineTask namespace=" + ns + ", runName=" + pipelineRunName + ", taskName=" + pipelineTaskName); 60 | ListOptions lo = new ListOptions(); 61 | String selector = String.format("%s=%s,%s=%s", PIPELINE_TASK_LABEL_NAME, pipelineTaskName, PIPELINE_RUN_LABEL_NAME, pipelineRunName); 62 | lo.setLabelSelector(selector); 63 | 64 | // the tekton operator may not have created the TasksRuns yet so lets wait a little bit for them to show up 65 | for (int i = 0; i < 60; i++) { 66 | boolean taskComplete = false; 67 | List taskRunList = tektonClient.v1beta1().taskRuns().inNamespace(ns).list(lo).getItems(); 68 | LOGGER.info("Got " + taskRunList.size() + " TaskRuns"); 69 | for (TaskRun tr : taskRunList) { 70 | String trName = tr.getMetadata().getName(); 71 | if (Strings.isNullOrEmpty(tr.getMetadata().getNamespace())) { 72 | tr.getMetadata().setNamespace(ns); 73 | } 74 | LOGGER.info("streaming logs for TaskRun " + trName); 75 | 76 | List ownerReferences = tr.getMetadata().getOwnerReferences(); 77 | for (OwnerReference or : ownerReferences) { 78 | if (or.getUid().equals(pipelineRunUid)) { 79 | LOGGER.info(String.format("Streaming logs for TaskRun %s/%s owned by PipelineRun %s with selector %s", ns, trName, pipelineRunName, selector)); 80 | TaskRunLogWatch logWatch = new TaskRunLogWatch(kubernetesClient, tektonClient, tr, consoleLogger); 81 | Thread logWatchTask = new Thread(logWatch); 82 | logWatchTask.start(); 83 | try { 84 | logWatchTask.join(); 85 | } catch (InterruptedException exception) { 86 | exception.printStackTrace(); 87 | } 88 | Exception e = logWatch.getException(); 89 | if (e != null) { 90 | LOGGER.info("TaskRun " + trName + " failed"); 91 | if (exception == null) { 92 | exception = e; 93 | } 94 | } else { 95 | LOGGER.info("TaskRun " + trName + " completed"); 96 | } 97 | taskComplete = true; 98 | } 99 | } 100 | } 101 | 102 | if (taskComplete) { 103 | logMessage("[Tekton] Completed PipelineTask " + pipelineTaskName); 104 | break; 105 | } else { 106 | logMessage("[Tekton] Could not find OwnerReference for " + pipelineRunUid); 107 | } 108 | try { 109 | Thread.sleep(1000); 110 | } catch (InterruptedException e) { 111 | // ignore 112 | e.printStackTrace(); 113 | } 114 | } 115 | } 116 | } 117 | 118 | protected void logMessage(String text) { 119 | try { 120 | this.consoleLogger.write((text + "\n").getBytes(StandardCharsets.UTF_8)); 121 | } catch (IOException e) { 122 | LOGGER.warning("failed to log to console: " + e); 123 | } 124 | } 125 | } 126 | -------------------------------------------------------------------------------- /src/main/java/org/waveywaves/jenkins/plugins/tekton/client/logwatch/TaskRunLogWatch.java: -------------------------------------------------------------------------------- 1 | package org.waveywaves.jenkins.plugins.tekton.client.logwatch; 2 | 3 | import com.google.common.collect.Sets; 4 | import io.fabric8.knative.internal.pkg.apis.Condition; 5 | import io.fabric8.kubernetes.api.model.Container; 6 | import io.fabric8.kubernetes.api.model.ContainerState; 7 | import io.fabric8.kubernetes.api.model.ContainerStateTerminated; 8 | import io.fabric8.kubernetes.api.model.ContainerStatus; 9 | import io.fabric8.kubernetes.api.model.ListOptions; 10 | import io.fabric8.kubernetes.api.model.OwnerReference; 11 | import io.fabric8.kubernetes.api.model.Pod; 12 | import io.fabric8.kubernetes.api.model.PodStatus; 13 | import io.fabric8.kubernetes.client.KubernetesClient; 14 | import io.fabric8.kubernetes.client.dsl.PodResource; 15 | import io.fabric8.tekton.client.TektonClient; 16 | import io.fabric8.tekton.pipeline.v1beta1.TaskRun; 17 | import org.waveywaves.jenkins.plugins.tekton.client.TektonUtils.TektonResourceType; 18 | 19 | import java.io.IOException; 20 | import java.io.OutputStream; 21 | import java.nio.charset.StandardCharsets; 22 | import java.util.ArrayList; 23 | import java.util.HashSet; 24 | import java.util.List; 25 | import java.util.concurrent.TimeUnit; 26 | import java.util.function.Predicate; 27 | import java.util.logging.Logger; 28 | 29 | public class TaskRunLogWatch implements Runnable{ 30 | private static final Logger LOGGER = Logger.getLogger(TaskRunLogWatch.class.getName()); 31 | 32 | private static final String TASK_RUN_LABEL_NAME = "tekton.dev/taskRun"; 33 | 34 | // TODO should be final 35 | private TaskRun taskRun; 36 | 37 | private KubernetesClient kubernetesClient; 38 | private TektonClient tektonClient; 39 | 40 | private Exception exception; 41 | OutputStream consoleLogger; 42 | 43 | public TaskRunLogWatch(KubernetesClient kubernetesClient, TektonClient tektonClient, TaskRun taskRun, OutputStream consoleLogger) { 44 | this.kubernetesClient = kubernetesClient; 45 | this.tektonClient = tektonClient; 46 | this.taskRun = taskRun; 47 | this.consoleLogger = consoleLogger; 48 | } 49 | 50 | /** 51 | * @return the exception if the task run failed to succeed 52 | */ 53 | public Exception getException() { 54 | return exception; 55 | } 56 | 57 | @Override 58 | public void run() { 59 | HashSet runningPhases = Sets.newHashSet("Running", "Succeeded", "Failed"); 60 | String ns = taskRun.getMetadata().getNamespace(); 61 | ListOptions lo = new ListOptions(); 62 | String selector = String.format("%s=%s", TASK_RUN_LABEL_NAME, taskRun.getMetadata().getName()); 63 | lo.setLabelSelector(selector); 64 | List pods = null; 65 | for (int i = 0; i < 60; i++) { 66 | pods = kubernetesClient.pods().inNamespace(ns).list(lo).getItems(); 67 | LOGGER.info("Found " + pods.size() + " pod(s) for taskRun " + taskRun.getMetadata().getName()); 68 | if (pods.size() > 0) { 69 | break; 70 | } 71 | try { 72 | Thread.sleep(1000); 73 | } catch (InterruptedException e) { 74 | e.printStackTrace(); 75 | } 76 | } 77 | 78 | 79 | Pod taskRunPod = null; 80 | String podName = ""; 81 | for (Pod pod : pods) { 82 | List ownerReferences = pod.getMetadata().getOwnerReferences(); 83 | if (ownerReferences != null && ownerReferences.size() > 0) { 84 | for (OwnerReference or : ownerReferences) { 85 | String orKind = or.getKind(); 86 | String orName = or.getName(); 87 | if (orKind.toLowerCase().equals(TektonResourceType.taskrun.toString()) 88 | && orName.equals(taskRun.getMetadata().getName())){ 89 | podName = pod.getMetadata().getName(); 90 | taskRunPod = pod; 91 | } 92 | } 93 | } 94 | } 95 | 96 | final String selectedPodName = podName; 97 | if (!podName.isEmpty() && taskRunPod != null){ 98 | logMessage(String.format("[Tekton] Pod %s/%s", ns, podName)); 99 | 100 | LOGGER.info("waiting for pod " + ns + "/" + podName + " to start running..."); 101 | Predicate succeededState = i -> (runningPhases.contains(i.getStatus().getPhase())); 102 | PodResource pr = kubernetesClient.pods().inNamespace(ns).withName(podName); 103 | try { 104 | pr.waitUntilCondition(succeededState,60, TimeUnit.MINUTES); 105 | } catch ( InterruptedException e) { 106 | LOGGER.warning("Interrupted Exception Occurred"); 107 | } 108 | logMessage(String.format("[Tekton] Pod %s/%s - Running...", ns, podName)); 109 | List taskRunContainerNames = new ArrayList(); 110 | for (Container c : taskRunPod.getSpec().getContainers()) { 111 | taskRunContainerNames.add(c.getName()); 112 | } 113 | 114 | for (String containerName : taskRunContainerNames) { 115 | // lets write a little header per container 116 | logMessage(String.format("[Tekton] Container %s/%s/%s", ns, podName, containerName)); 117 | 118 | // wait for the container to start 119 | LOGGER.info("waiting for pod: " + ns + "/" + podName + " container: " + containerName + " to start:"); 120 | 121 | Predicate containerRunning = i -> { 122 | List statuses = i.getStatus().getContainerStatuses(); 123 | for (ContainerStatus status : statuses) { 124 | if (status.getName().equals(containerName)) { 125 | LOGGER.info("Found status " + status + " for container " + containerName); 126 | ContainerState state = status.getState(); 127 | if (state != null) { 128 | ContainerStateTerminated terminatedState = state.getTerminated(); 129 | if (terminatedState != null && terminatedState.getStartedAt() != null) { 130 | if (terminatedState.getExitCode() != null && terminatedState.getExitCode() != 0) { 131 | logMessage(String.format("[Tekton] Container %s/%s/%s - %s", ns, selectedPodName, containerName, terminatedState.getReason())); 132 | } else { 133 | logMessage(String.format("[Tekton] Container %s/%s/%s - Completed", ns, selectedPodName, containerName)); 134 | } 135 | return true; 136 | } 137 | } 138 | return false; 139 | } 140 | } 141 | return false; 142 | }; 143 | try { 144 | pr.waitUntilCondition(containerRunning,60, TimeUnit.MINUTES); 145 | } catch ( InterruptedException e) { 146 | LOGGER.warning("Interrupted Exception Occurred"); 147 | } 148 | 149 | pr.inContainer(containerName).watchLog(this.consoleLogger); 150 | } 151 | logPodFailures(pr.get()); 152 | } else { 153 | String message = "no pod could be found for TaskRun " + ns + "/" + taskRun.getMetadata().getName(); 154 | logMessage("[Tekton] " + message); 155 | exception = new Exception(message); 156 | 157 | // lets reload to get the latest status 158 | taskRun = tektonClient.v1beta1().taskRuns().inNamespace(ns).withName(taskRun.getMetadata().getName()).get(); 159 | logTaskRunFailure(taskRun); 160 | } 161 | } 162 | 163 | /** 164 | * Lets log any failures in the task run 165 | * 166 | * @param taskRun the task run to log 167 | */ 168 | protected void logTaskRunFailure(TaskRun taskRun) { 169 | String name = taskRun.getMetadata().getName(); 170 | if (taskRun.getStatus() != null) { 171 | List conditions = taskRun.getStatus().getConditions(); 172 | if (conditions == null || conditions.size() == 0) { 173 | logMessage("[Tekton] TaskRun " + name + " has no status conditions"); 174 | return; 175 | } 176 | 177 | for (Condition condition : conditions) { 178 | logMessage("[Tekton] TaskRun " + name + " " + condition.getType() + "/" + condition.getReason() + ": " + condition.getMessage()); 179 | } 180 | } else { 181 | logMessage("[Tekton] TaskRun " + name + " has no status"); 182 | } 183 | } 184 | 185 | /** 186 | * Lets check if the pod completed successfully otherwise log a failure message 187 | * 188 | * @param pod the pod to log 189 | */ 190 | protected void logPodFailures(Pod pod) { 191 | String ns = pod.getMetadata().getNamespace(); 192 | String podName = pod.getMetadata().getName(); 193 | PodStatus status = pod.getStatus(); 194 | String phase = status.getPhase(); 195 | String message = "Pod " + ns + "/" + podName + " Status: " + phase; 196 | logMessage("[Tekton] " + message); 197 | 198 | // TODO we could try diagnose more information from the failed pod to log 199 | 200 | if (!phase.equals("Succeeded")) { 201 | exception = new Exception(message); 202 | } 203 | } 204 | 205 | 206 | protected void logMessage(String text) { 207 | try { 208 | this.consoleLogger.write((text + "\n").getBytes(StandardCharsets.UTF_8)); 209 | } catch (IOException e) { 210 | LOGGER.warning("failed to log to console: " + e); 211 | } 212 | } 213 | } 214 | -------------------------------------------------------------------------------- /src/main/kubernetes/deployment.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: Deployment 3 | metadata: 4 | name: jenkins-deployment 5 | spec: 6 | replicas: 1 7 | selector: 8 | matchLabels: 9 | app: jenkins 10 | template: 11 | metadata: 12 | labels: 13 | app: jenkins 14 | spec: 15 | containers: 16 | - name: jenkins 17 | image: jenkins/jenkins:lts 18 | ports: 19 | - containerPort: 8080 20 | volumeMounts: 21 | - name: jenkins-home 22 | mountPath: /var/lib/jenkins 23 | volumes: 24 | - name: jenkins-home 25 | emptyDir: {} 26 | -------------------------------------------------------------------------------- /src/main/resources/index.jelly: -------------------------------------------------------------------------------- 1 | 2 | 5 |
6 | This plugin helps in the creation, and triggering of Tekton Pipeline Resources. 7 |
8 | -------------------------------------------------------------------------------- /src/main/resources/org/waveywaves/jenkins/plugins/tekton/client/.gitignore: -------------------------------------------------------------------------------- 1 | jxp/ -------------------------------------------------------------------------------- /src/main/resources/org/waveywaves/jenkins/plugins/tekton/client/build/create/CreateCustomTaskrun/config.jelly: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /src/main/resources/org/waveywaves/jenkins/plugins/tekton/client/build/create/CreateRaw/config.jelly: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /src/main/resources/org/waveywaves/jenkins/plugins/tekton/client/build/create/TektonArg/config.jelly: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 |
8 | 9 |
10 |
11 |
-------------------------------------------------------------------------------- /src/main/resources/org/waveywaves/jenkins/plugins/tekton/client/build/create/TektonCommandI/config.jelly: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 |
8 | 9 |
10 |
11 |
-------------------------------------------------------------------------------- /src/main/resources/org/waveywaves/jenkins/plugins/tekton/client/build/create/TektonEnv/config.jelly: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 |
11 | 12 |
13 |
14 |
-------------------------------------------------------------------------------- /src/main/resources/org/waveywaves/jenkins/plugins/tekton/client/build/create/TektonParam/config.jelly: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /src/main/resources/org/waveywaves/jenkins/plugins/tekton/client/build/create/TektonStep/config.jelly: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /src/main/resources/org/waveywaves/jenkins/plugins/tekton/client/build/create/TektonStringParamSpec/config.jelly: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /src/main/resources/org/waveywaves/jenkins/plugins/tekton/client/build/create/TektonTaskResult/config.jelly: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /src/main/resources/org/waveywaves/jenkins/plugins/tekton/client/build/create/TektonWorkspaceBind/config.jelly: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 |
11 | 12 |
13 |
14 |
-------------------------------------------------------------------------------- /src/main/resources/org/waveywaves/jenkins/plugins/tekton/client/build/create/TektonWorkspaceDecl/config.jelly: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 |
19 |
20 |
-------------------------------------------------------------------------------- /src/main/resources/org/waveywaves/jenkins/plugins/tekton/client/build/delete/DeleteRaw/config.jelly: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /src/main/resources/org/waveywaves/jenkins/plugins/tekton/client/global/ClusterConfig/config.jelly: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /src/main/resources/org/waveywaves/jenkins/plugins/tekton/client/global/TektonGlobalConfiguration/config.jelly: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /src/main/tekton/examples/v1beta1/taskruns/home-is-set.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: tekton.dev/v1beta1 2 | kind: TaskRun 3 | metadata: 4 | generateName: home-is-set- 5 | spec: 6 | taskSpec: 7 | steps: 8 | - image: ubuntu 9 | script: | 10 | #!/usr/bin/env bash 11 | [[ $HOME == /tekton/home ]] 12 | -------------------------------------------------------------------------------- /src/test/java/org/waveywaves/jenkins/plugins/tekton/client/FakeLogFilter.java: -------------------------------------------------------------------------------- 1 | package org.waveywaves.jenkins.plugins.tekton.client; 2 | 3 | import java.util.ArrayList; 4 | import java.util.Collections; 5 | import java.util.List; 6 | import java.util.logging.Filter; 7 | import java.util.logging.LogRecord; 8 | 9 | /** 10 | * A helper class for testing logging output 11 | */ 12 | public class FakeLogFilter implements Filter { 13 | private List records = new ArrayList<>(); 14 | 15 | @Override 16 | public boolean isLoggable(LogRecord record) { 17 | records.add(record); 18 | return true; 19 | } 20 | 21 | /** 22 | * @return the records we have seen so far 23 | */ 24 | public List getRecords() { 25 | return Collections.unmodifiableList(records); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/test/java/org/waveywaves/jenkins/plugins/tekton/client/LogUtilsTest.java: -------------------------------------------------------------------------------- 1 | package org.waveywaves.jenkins.plugins.tekton.client; 2 | 3 | import org.junit.Test; 4 | 5 | import java.io.ByteArrayInputStream; 6 | import java.io.IOException; 7 | import java.util.List; 8 | import java.util.logging.Level; 9 | import java.util.logging.LogRecord; 10 | import java.util.logging.Logger; 11 | 12 | import static org.assertj.core.api.Assertions.*; 13 | 14 | /** 15 | */ 16 | public class LogUtilsTest { 17 | String expected = "hello\nworld"; 18 | boolean verbose = System.getenv("TEST_VERBOSE") == "true"; 19 | 20 | @Test 21 | public void testLogUtilsInfo() throws Exception { 22 | assertLogOutput(false, Level.INFO); 23 | } 24 | 25 | @Test 26 | public void testLogUtilsError() throws Exception { 27 | assertLogOutput(true, Level.WARNING); 28 | } 29 | 30 | protected void assertLogOutput(boolean errorFlag, Level expectedLevel) throws IOException { 31 | ByteArrayInputStream in = new ByteArrayInputStream(expected.getBytes()); 32 | Logger log = Logger.getLogger(getClass().getName()); 33 | FakeLogFilter filter = new FakeLogFilter(); 34 | log.setFilter(filter); 35 | LogUtils.logStream(in, log, errorFlag); 36 | 37 | 38 | List records = filter.getRecords(); 39 | 40 | assertThat(records).hasSize(2).extracting("message").containsSequence("hello", "world"); 41 | 42 | for (LogRecord record : records) { 43 | assertThat(record.getLevel()).isEqualTo(expectedLevel); 44 | if (verbose) { 45 | System.out.println("got log level " + record.getLevel() + " message: " + record.getMessage()); 46 | } 47 | } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/test/java/org/waveywaves/jenkins/plugins/tekton/client/ToolUtilsTest.java: -------------------------------------------------------------------------------- 1 | package org.waveywaves.jenkins.plugins.tekton.client; 2 | 3 | import org.junit.Test; 4 | 5 | import java.io.File; 6 | import java.util.logging.Logger; 7 | 8 | import static org.assertj.core.api.Assertions.assertThat; 9 | 10 | /** 11 | */ 12 | public class ToolUtilsTest { 13 | private static final Logger logger = Logger.getLogger(ToolUtilsTest.class.getName()); 14 | 15 | @Test 16 | public void testToolUtils() throws Exception { 17 | String path = ToolUtils.getJXPipelineBinary(ToolUtilsTest.class.getClassLoader()); 18 | assertThat(path).isNotEmpty(); 19 | 20 | File file = new File(path); 21 | assertThat(file).isFile(); 22 | logger.info("got jx pipeline binary " + path + " with size " + file.length()); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/test/java/org/waveywaves/jenkins/plugins/tekton/client/build/FakeChecksPublisher.java: -------------------------------------------------------------------------------- 1 | package org.waveywaves.jenkins.plugins.tekton.client.build; 2 | 3 | import io.jenkins.plugins.checks.api.ChecksDetails; 4 | import io.jenkins.plugins.checks.api.ChecksPublisher; 5 | import java.util.ArrayList; 6 | import java.util.List; 7 | 8 | import static org.hamcrest.CoreMatchers.is; 9 | import static org.hamcrest.CoreMatchers.notNullValue; 10 | import static org.hamcrest.MatcherAssert.assertThat; 11 | 12 | public class FakeChecksPublisher extends ChecksPublisher { 13 | 14 | private int counter = 0; 15 | private List details = new ArrayList<>(); 16 | 17 | @Override 18 | public void publish(ChecksDetails detail) { 19 | details.add(detail); 20 | counter++; 21 | } 22 | 23 | public int getCounter() { 24 | return counter; 25 | } 26 | 27 | public void validate() { 28 | for (ChecksDetails c: details) { 29 | System.out.println("[FakeChecksPublisher] " + c); 30 | assertThat(c, is(notNullValue())); 31 | assertThat(c.getName().get(), is("tekton")); 32 | assertThat(c.getConclusion(), is(notNullValue())); 33 | assertThat(c.getStatus(), is(notNullValue())); 34 | 35 | assertThat(c.getOutput(), is(notNullValue())); 36 | assertThat(c.getOutput().get(), is(notNullValue())); 37 | assertThat(c.getOutput().get().getTitle().get(), is(notNullValue())); 38 | assertThat(c.getOutput().get().getSummary().get(), is(notNullValue())); 39 | //assertThat(c, is(notNullValue())); 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/test/java/org/waveywaves/jenkins/plugins/tekton/client/build/create/CreateRawMockServerTest.java: -------------------------------------------------------------------------------- 1 | package org.waveywaves.jenkins.plugins.tekton.client.build.create; 2 | 3 | import hudson.EnvVars; 4 | import io.fabric8.knative.internal.pkg.apis.Condition; 5 | import io.fabric8.kubernetes.api.model.ConditionBuilder; 6 | import io.fabric8.kubernetes.api.model.apiextensions.v1beta1.CustomResourceDefinition; 7 | import io.fabric8.kubernetes.client.KubernetesClient; 8 | 9 | import io.fabric8.kubernetes.client.dsl.MixedOperation; 10 | import io.fabric8.kubernetes.client.dsl.Resource; 11 | import io.fabric8.kubernetes.client.dsl.base.CustomResourceDefinitionContext; 12 | import io.fabric8.kubernetes.client.server.mock.KubernetesServer; 13 | 14 | import io.fabric8.tekton.pipeline.v1beta1.*; 15 | import org.junit.After; 16 | import org.junit.Before; 17 | import org.junit.Rule; 18 | import org.junit.Test; 19 | import org.waveywaves.jenkins.plugins.tekton.client.TektonUtils; 20 | 21 | import java.io.ByteArrayInputStream; 22 | import java.io.InputStream; 23 | import java.net.HttpURLConnection; 24 | import java.nio.charset.StandardCharsets; 25 | import org.waveywaves.jenkins.plugins.tekton.client.build.FakeChecksPublisher; 26 | 27 | import static org.hamcrest.CoreMatchers.is; 28 | import static org.hamcrest.MatcherAssert.assertThat; 29 | import static org.assertj.core.api.Assertions.fail; 30 | 31 | 32 | public class CreateRawMockServerTest { 33 | 34 | private boolean enableCatalog = false; 35 | private String namespace = "test"; 36 | private FakeChecksPublisher checksPublisher; 37 | 38 | @Rule 39 | public KubernetesServer server = new KubernetesServer(); 40 | 41 | @Before 42 | public void before() { 43 | checksPublisher = new FakeChecksPublisher(); 44 | } 45 | 46 | @After 47 | public void after() { 48 | checksPublisher.validate(); 49 | } 50 | 51 | @Test 52 | public void testTaskCreate() { 53 | // Given 54 | String testTaskYaml = "apiVersion: tekton.dev/v1beta1\n" + 55 | "kind: Task\n" + 56 | "metadata:\n" + 57 | " name: testTask\n"; 58 | 59 | KubernetesClient client = server.getClient(); 60 | InputStream crdAsInputStream = getClass().getResourceAsStream("/task-crd.yaml"); 61 | CustomResourceDefinition taskCrd = client.apiextensions().v1beta1().customResourceDefinitions().load(crdAsInputStream).get(); 62 | MixedOperation> taskClient = client 63 | .customResources(CustomResourceDefinitionContext.fromCrd(taskCrd), Task.class, TaskList.class); 64 | 65 | // Mocked Responses 66 | TaskBuilder taskBuilder = new TaskBuilder() 67 | .withNewMetadata() 68 | .withName("testTask") 69 | .endMetadata(); 70 | TaskList taskList = new TaskListBuilder() 71 | .addToItems(taskBuilder.build()) 72 | .build(); 73 | 74 | server.expect().post().withPath("/apis/tekton.dev/v1beta1/namespaces/test/tasks") 75 | .andReturn(HttpURLConnection.HTTP_CREATED, taskBuilder.build()).once(); 76 | server.expect().get().withPath("/apis/tekton.dev/v1beta1/namespaces/test/tasks") 77 | .andReturn(HttpURLConnection.HTTP_OK, taskList).once(); 78 | 79 | // When 80 | CreateRaw createRaw = new CreateRaw(CreateRaw.InputType.YAML.toString(), testTaskYaml); 81 | createRaw.setNamespace(namespace); 82 | createRaw.setClusterName(TektonUtils.DEFAULT_CLIENT_KEY); 83 | createRaw.setEnableCatalog(enableCatalog); 84 | 85 | createRaw.setTektonClient(client); 86 | createRaw.setTaskClient(taskClient); 87 | String createdTaskName = createRaw.createTask( 88 | new ByteArrayInputStream(testTaskYaml.getBytes(StandardCharsets.UTF_8))); 89 | 90 | // Then 91 | TaskList testTaskList = taskClient.list(); 92 | assertThat(createdTaskName, is("testTask")); 93 | assertThat(testTaskList.getItems().size(), is(1)); 94 | } 95 | 96 | @Test 97 | public void testTaskRunCreate() { 98 | // Given 99 | String testTaskRunYaml = "apiVersion: tekton.dev/v1beta1\n" + 100 | "kind: TaskRun\n" + 101 | "metadata:\n" + 102 | " generateName: home-is-set-\n"; 103 | 104 | KubernetesClient client = server.getClient(); 105 | InputStream crdAsInputStream = getClass().getResourceAsStream("/taskrun-crd.yaml"); 106 | CustomResourceDefinition taskRunCrd = client.apiextensions().v1beta1().customResourceDefinitions().load(crdAsInputStream).get(); 107 | MixedOperation> taskRunClient = client 108 | .customResources(CustomResourceDefinitionContext.fromCrd(taskRunCrd), TaskRun.class, TaskRunList.class); 109 | 110 | // Mocked Responses 111 | TaskRunBuilder taskRunBuilder = new TaskRunBuilder() 112 | .withNewMetadata() 113 | .withName("home-is-set-1234") 114 | .endMetadata(); 115 | TaskRunList taskRunList = new TaskRunListBuilder() 116 | .addToItems(taskRunBuilder.build()) 117 | .build(); 118 | 119 | server.expect().post().withPath("/apis/tekton.dev/v1beta1/namespaces/test/taskruns") 120 | .andReturn(HttpURLConnection.HTTP_CREATED, taskRunBuilder.build()).once(); 121 | server.expect().get().withPath("/apis/tekton.dev/v1beta1/namespaces/test/taskruns") 122 | .andReturn(HttpURLConnection.HTTP_OK, taskRunList).once(); 123 | 124 | // When 125 | CreateRaw createRaw = new CreateRaw(testTaskRunYaml, CreateRaw.InputType.YAML.toString()) { 126 | @Override 127 | public void streamTaskRunLogsToConsole(TaskRun taskRun) { 128 | return; 129 | } 130 | }; 131 | createRaw.setNamespace(namespace); 132 | createRaw.setClusterName(TektonUtils.DEFAULT_CLIENT_KEY); 133 | createRaw.setEnableCatalog(enableCatalog); 134 | createRaw.setTektonClient(client); 135 | createRaw.setTaskRunClient(taskRunClient); 136 | 137 | String createdTaskRunName = ""; 138 | 139 | try { 140 | createdTaskRunName = createRaw.createTaskRun( 141 | new ByteArrayInputStream(testTaskRunYaml.getBytes(StandardCharsets.UTF_8))); 142 | } catch (Exception e) { 143 | fail(e.getMessage(), e); 144 | } 145 | 146 | // Then 147 | TaskRunList testTaskRunList = taskRunClient.list(); 148 | assertThat(createdTaskRunName, is("home-is-set-1234")); 149 | assertThat(testTaskRunList.getItems().size(), is(1)); 150 | } 151 | 152 | @Test 153 | public void testPipelineCreate() { 154 | // Given 155 | String testPipelineYaml = "apiVersion: tekton.dev/v1beta1\n" + 156 | "kind: Pipeline\n" + 157 | "metadata:\n" + 158 | " name: testPipeline\n"; 159 | 160 | KubernetesClient client = server.getClient(); 161 | InputStream crdAsInputStream = getClass().getResourceAsStream("/pipeline-crd.yaml"); 162 | CustomResourceDefinition pipelineCrd = client.apiextensions().v1beta1().customResourceDefinitions().load(crdAsInputStream).get(); 163 | MixedOperation> pipelineClient = client 164 | .customResources(CustomResourceDefinitionContext.fromCrd(pipelineCrd), Pipeline.class, PipelineList.class); 165 | 166 | // Mocked Responses 167 | PipelineBuilder pipelineBuilder = new PipelineBuilder() 168 | .withNewMetadata() 169 | .withName("testPipeline") 170 | .endMetadata(); 171 | PipelineList pipelineList = new PipelineListBuilder() 172 | .addToItems(pipelineBuilder.build()) 173 | .build(); 174 | 175 | server.expect().post().withPath("/apis/tekton.dev/v1beta1/namespaces/test/pipelines") 176 | .andReturn(HttpURLConnection.HTTP_CREATED, pipelineBuilder.build()).once(); 177 | server.expect().get().withPath("/apis/tekton.dev/v1beta1/namespaces/test/pipelines") 178 | .andReturn(HttpURLConnection.HTTP_OK, pipelineList).once(); 179 | 180 | // When 181 | CreateRaw createRaw = new CreateRaw(testPipelineYaml, CreateRaw.InputType.YAML.toString()); 182 | createRaw.setNamespace(namespace); 183 | createRaw.setClusterName(TektonUtils.DEFAULT_CLIENT_KEY); 184 | createRaw.setEnableCatalog(enableCatalog); 185 | createRaw.setTektonClient(client); 186 | createRaw.setPipelineClient(pipelineClient); 187 | String createdPipelineName = createRaw.createPipeline( 188 | new ByteArrayInputStream(testPipelineYaml.getBytes(StandardCharsets.UTF_8))); 189 | 190 | // Then 191 | PipelineList testPipelineList = pipelineClient.list(); 192 | assertThat(createdPipelineName, is("testPipeline")); 193 | assertThat(testPipelineList.getItems().size(), is(1)); 194 | } 195 | 196 | @Test 197 | public void testPipelineRunCreate() { 198 | // Given 199 | String testPipelineRunYaml = "apiVersion: tekton.dev/v1beta1\n" + 200 | "kind: PipelineRun\n" + 201 | "metadata:\n" + 202 | " name: testPipelineRun\n" + 203 | "spec:\n" + 204 | " params: []\n"; 205 | 206 | KubernetesClient client = server.getClient(); 207 | InputStream crdAsInputStream = getClass().getResourceAsStream("/pipeline-crd.yaml"); 208 | CustomResourceDefinition pipelineRunCrd = client.apiextensions().v1beta1().customResourceDefinitions().load(crdAsInputStream).get(); 209 | MixedOperation> pipelineRunClient = client 210 | .customResources(CustomResourceDefinitionContext.fromCrd(pipelineRunCrd), PipelineRun.class, PipelineRunList.class); 211 | 212 | // Mocked Responses 213 | PipelineRunBuilder pipelineRunBuilder = new PipelineRunBuilder() 214 | .withNewMetadata() 215 | .withName("testPipelineRun") 216 | .endMetadata() 217 | .withNewSpec() 218 | .withParams(new Param("param", new ArrayOrString("value"))) 219 | .endSpec() 220 | .withNewStatus() 221 | .withConditions(new Condition("lastTransitionTime","","","","True","Succeeded")) 222 | .endStatus(); 223 | 224 | PipelineRunList pipelineRunList = new PipelineRunListBuilder() 225 | .addToItems(pipelineRunBuilder.build()) 226 | .build(); 227 | 228 | server.expect().post().withPath("/apis/tekton.dev/v1beta1/namespaces/test/pipelines") 229 | .andReturn(HttpURLConnection.HTTP_CREATED, pipelineRunBuilder.build()).once(); 230 | server.expect().get().withPath("/apis/tekton.dev/v1beta1/namespaces/test/pipelines") 231 | .andReturn(HttpURLConnection.HTTP_OK, pipelineRunList).once(); 232 | server.expect().get().withPath("/apis/tekton.dev/v1beta1/namespaces/test/pipelines/testPipelineRun") 233 | .andReturn(HttpURLConnection.HTTP_OK, pipelineRunBuilder.build()).once(); 234 | 235 | // When 236 | CreateRaw createRaw = new CreateRaw(testPipelineRunYaml, CreateRaw.InputType.YAML.toString()) { 237 | @Override 238 | public void streamPipelineRunLogsToConsole(PipelineRun pipelineRun) { 239 | return; 240 | } 241 | }; 242 | createRaw.setNamespace(namespace); 243 | createRaw.setClusterName(TektonUtils.DEFAULT_CLIENT_KEY); 244 | createRaw.setEnableCatalog(enableCatalog); 245 | createRaw.setTektonClient(client); 246 | createRaw.setPipelineRunClient(pipelineRunClient); 247 | 248 | createRaw.setChecksPublisher(checksPublisher); 249 | 250 | String createdPipelineName = ""; 251 | try { 252 | createdPipelineName = createRaw.createPipelineRun( 253 | new ByteArrayInputStream(testPipelineRunYaml.getBytes(StandardCharsets.UTF_8)), new EnvVars()); 254 | } catch (Exception e) { 255 | fail(e.getMessage(), e); 256 | } 257 | 258 | assertThat(checksPublisher.getCounter(), is(1)); 259 | 260 | // Then 261 | PipelineRunList testPipelineRunList = pipelineRunClient.list(); 262 | assertThat(createdPipelineName, is("testPipelineRun")); 263 | assertThat(testPipelineRunList.getItems().size(), is(1)); 264 | } 265 | 266 | @Test 267 | public void testPipelineRunCreateWithFailingPod() { 268 | // Given 269 | String testPipelineRunYaml = "apiVersion: tekton.dev/v1beta1\n" + 270 | "kind: PipelineRun\n" + 271 | "metadata:\n" + 272 | " name: testPipelineRun\n" + 273 | "spec:\n" + 274 | " params: []\n"; 275 | 276 | KubernetesClient client = server.getClient(); 277 | InputStream crdAsInputStream = getClass().getResourceAsStream("/pipeline-crd.yaml"); 278 | CustomResourceDefinition pipelineRunCrd = client.apiextensions().v1beta1().customResourceDefinitions().load(crdAsInputStream).get(); 279 | MixedOperation> pipelineRunClient = client 280 | .customResources(CustomResourceDefinitionContext.fromCrd(pipelineRunCrd), PipelineRun.class, PipelineRunList.class); 281 | 282 | // Mocked Responses 283 | PipelineRunBuilder pipelineRunBuilder = new PipelineRunBuilder() 284 | .withNewMetadata() 285 | .withName("testPipelineRun") 286 | .endMetadata() 287 | .withNewSpec() 288 | .withParams(new Param("param", new ArrayOrString("value"))) 289 | .endSpec() 290 | .withNewStatus() 291 | .withConditions(new Condition("lastTransitionTime","This is an error message","ParameterMissing","","False","Succeeded")) 292 | .endStatus(); 293 | 294 | PipelineRunList pipelineRunList = new PipelineRunListBuilder() 295 | .addToItems(pipelineRunBuilder.build()) 296 | .build(); 297 | 298 | server.expect().post().withPath("/apis/tekton.dev/v1beta1/namespaces/test/pipelines") 299 | .andReturn(HttpURLConnection.HTTP_CREATED, pipelineRunBuilder.build()).once(); 300 | server.expect().get().withPath("/apis/tekton.dev/v1beta1/namespaces/test/pipelines") 301 | .andReturn(HttpURLConnection.HTTP_OK, pipelineRunList).once(); 302 | server.expect().get().withPath("/apis/tekton.dev/v1beta1/namespaces/test/pipelines/testPipelineRun") 303 | .andReturn(HttpURLConnection.HTTP_OK, pipelineRunBuilder.build()).once(); 304 | 305 | // When 306 | CreateRaw createRaw = new CreateRaw(testPipelineRunYaml, CreateRaw.InputType.YAML.toString()) { 307 | @Override 308 | public void streamPipelineRunLogsToConsole(PipelineRun pipelineRun) { 309 | return; 310 | } 311 | }; 312 | 313 | createRaw.setNamespace(namespace); 314 | createRaw.setClusterName(TektonUtils.DEFAULT_CLIENT_KEY); 315 | createRaw.setEnableCatalog(enableCatalog); 316 | createRaw.setTektonClient(client); 317 | createRaw.setPipelineRunClient(pipelineRunClient); 318 | 319 | createRaw.setChecksPublisher(checksPublisher); 320 | 321 | try { 322 | createRaw.createPipelineRun( 323 | new ByteArrayInputStream(testPipelineRunYaml.getBytes(StandardCharsets.UTF_8)), new EnvVars()); 324 | fail("Expected this to fail"); 325 | } catch (Exception e) { 326 | assertThat(e.getMessage(), is("ParameterMissing: This is an error message")); 327 | } 328 | 329 | assertThat(checksPublisher.getCounter(), is(1)); 330 | } 331 | } -------------------------------------------------------------------------------- /src/test/java/org/waveywaves/jenkins/plugins/tekton/client/build/create/CreateRawTest.java: -------------------------------------------------------------------------------- 1 | package org.waveywaves.jenkins.plugins.tekton.client.build.create; 2 | 3 | import hudson.EnvVars; 4 | import hudson.FilePath; 5 | import hudson.model.Run; 6 | import io.fabric8.tekton.pipeline.v1beta1.Param; 7 | import io.fabric8.tekton.pipeline.v1beta1.PipelineRun; 8 | import io.fabric8.tekton.pipeline.v1beta1.PipelineRunBuilder; 9 | import java.util.List; 10 | import org.junit.After; 11 | import org.junit.Before; 12 | import org.junit.Test; 13 | import org.waveywaves.jenkins.plugins.tekton.client.TektonUtils; 14 | import org.waveywaves.jenkins.plugins.tekton.client.build.FakeChecksPublisher; 15 | import org.waveywaves.jenkins.plugins.tekton.client.build.create.mock.CreateRawMock; 16 | import org.waveywaves.jenkins.plugins.tekton.client.build.create.mock.FakeCreateRaw; 17 | 18 | import java.nio.file.Files; 19 | import java.nio.file.Path; 20 | 21 | import static org.assertj.core.api.Assertions.assertThat; 22 | import static org.hamcrest.CoreMatchers.is; 23 | import static org.hamcrest.CoreMatchers.notNullValue; 24 | import static org.hamcrest.MatcherAssert.assertThat; 25 | 26 | public class CreateRawTest { 27 | 28 | private Run run; 29 | private String namespace; 30 | private FakeChecksPublisher checksPublisher; 31 | 32 | @Before 33 | public void before() { 34 | checksPublisher = new FakeChecksPublisher(); 35 | } 36 | 37 | @After 38 | public void after() { 39 | checksPublisher.validate(); 40 | } 41 | 42 | @Test 43 | public void runCreateTaskTest() { 44 | String testTaskYaml = "apiVersion: tekton.dev/v1beta1\n" + 45 | "kind: Task\n" + 46 | "metadata:\n" + 47 | " name: testTask\n"; 48 | CreateRaw createRaw = new CreateRawMock(testTaskYaml, CreateRaw.InputType.YAML.toString()); 49 | createRaw.setNamespace(namespace); 50 | createRaw.setClusterName(TektonUtils.DEFAULT_CLIENT_KEY); 51 | createRaw.setEnableCatalog(false); 52 | String created = createRaw.runCreate(run, null, null); 53 | assertThat(created, is(TektonUtils.TektonResourceType.task.toString())); 54 | } 55 | 56 | @Test 57 | public void runCreateTaskRunTest() { 58 | String testTaskRunYaml = "apiVersion: tekton.dev/v1beta1\n" + 59 | "kind: TaskRun\n" + 60 | "metadata:\n" + 61 | " generateName: home-is-set-\n"; 62 | CreateRaw createRaw = new CreateRawMock(testTaskRunYaml, CreateRaw.InputType.YAML.toString()); 63 | createRaw.setNamespace(namespace); 64 | createRaw.setClusterName(TektonUtils.DEFAULT_CLIENT_KEY); 65 | createRaw.setEnableCatalog(false); 66 | String created = createRaw.runCreate(run, null, null); 67 | assertThat(created, is(TektonUtils.TektonResourceType.taskrun.toString())); 68 | } 69 | 70 | @Test 71 | public void runCreatePipelineTest() { 72 | String testPipelineYaml = "apiVersion: tekton.dev/v1beta1\n" + 73 | "kind: Pipeline\n" + 74 | "metadata:\n" + 75 | " name: testPipeline\n"; 76 | CreateRaw createRaw = new CreateRawMock(testPipelineYaml, CreateRaw.InputType.YAML.toString()); 77 | createRaw.setNamespace(namespace); 78 | createRaw.setClusterName(TektonUtils.DEFAULT_CLIENT_KEY); 79 | createRaw.setEnableCatalog(false); 80 | String created = createRaw.runCreate(run, null, null); 81 | assertThat(created, is(TektonUtils.TektonResourceType.pipeline.toString())); 82 | } 83 | 84 | @Test 85 | public void runCreatePipelineRunTest() { 86 | String testPipelineRunYaml = "apiVersion: tekton.dev/v1beta1\n" + 87 | "kind: PipelineRun\n" + 88 | "metadata:\n" + 89 | " name: testPipelineRun\n"; 90 | CreateRaw createRaw = new CreateRawMock(testPipelineRunYaml, CreateRaw.InputType.YAML.toString()); 91 | createRaw.setNamespace(namespace); 92 | createRaw.setClusterName(TektonUtils.DEFAULT_CLIENT_KEY); 93 | createRaw.setEnableCatalog(false); 94 | createRaw.setChecksPublisher(checksPublisher); 95 | String created = createRaw.runCreate(run, null, null); 96 | assertThat(created, is(TektonUtils.TektonResourceType.pipelinerun.toString())); 97 | } 98 | 99 | @Test 100 | public void testCreateRawWithTektonCatalog() throws Exception { 101 | String testTaskYaml = "apiVersion: tekton.dev/v1beta1\n" + 102 | "kind: Task\n" + 103 | "metadata:\n" + 104 | " name: testTask\n"; 105 | FakeCreateRaw createRaw = new FakeCreateRaw(testTaskYaml, CreateRaw.InputType.YAML.toString()); 106 | createRaw.setNamespace(namespace); 107 | createRaw.setClusterName(TektonUtils.DEFAULT_CLIENT_KEY); 108 | createRaw.setEnableCatalog(true); 109 | 110 | Path tmpDir = Files.createTempDirectory(""); 111 | FilePath workspace = new FilePath(tmpDir.toFile()); 112 | 113 | String cheese = "edam"; 114 | EnvVars envVars = new EnvVars("CHEESE", cheese); 115 | createRaw.runCreate(run, workspace, envVars); 116 | 117 | String created = createRaw.getLastResource(); 118 | 119 | String expectedYaml = testTaskYaml + 120 | "labels:\n" + 121 | " cheese: " + cheese + "\n"; 122 | assertThat(created, is(expectedYaml)); 123 | } 124 | 125 | @Test 126 | public void testEnhancePipelineWithParams() { 127 | PipelineRun pr = new PipelineRunBuilder() 128 | .withNewSpec() 129 | .withParams() 130 | .endSpec() 131 | .build(); 132 | assertThat(pr, is(notNullValue())); 133 | assertThat(pr.getSpec(), is(notNullValue())); 134 | assertThat(pr.getSpec().getParams(), is(notNullValue())); 135 | 136 | EnvVars envVars = new EnvVars(); 137 | envVars.put("GIT_URL" , "https://github.com/org/repo.git"); 138 | envVars.put("JOB_NAME" , "test-tekton-client/main"); 139 | envVars.put("BUILD_ID" , "12"); 140 | envVars.put("GIT_COMMIT", "e9f3c472fea4661b2142c5e88928c5a35e03f51f"); 141 | envVars.put("GIT_BRANCH" , "main"); 142 | 143 | new CreateRaw("", "YAML").enhancePipelineRunWithEnvVars(pr, envVars); 144 | 145 | List params = pr.getSpec().getParams(); 146 | assertThat(params.stream().filter( p -> p.getName().equals("REPO_URL")).findFirst().get(), is(notNullValue())); 147 | assertThat(getStringValue(params, "REPO_URL"), is("https://github.com/org/repo.git")); 148 | assertThat(getStringValue(params, "JOB_NAME"), is("test-tekton-client/main")); 149 | assertThat(getStringValue(params, "BUILD_ID"), is("12")); 150 | assertThat(getStringValue(params, "PULL_PULL_SHA"), is("e9f3c472fea4661b2142c5e88928c5a35e03f51f")); 151 | assertThat(getStringValue(params, "PULL_BASE_REF"), is("main")); 152 | assertThat(getStringValue(params, "REPO_NAME"), is("repo")); 153 | assertThat(getStringValue(params, "REPO_OWNER"), is("org")); 154 | } 155 | 156 | private String getStringValue(List params, String name) { 157 | Param param = params.stream().filter(p -> p.getName().equals(name)).findFirst().get(); 158 | assertThat(param, is(notNullValue())); 159 | return param.getValue().getStringVal(); 160 | } 161 | 162 | } 163 | -------------------------------------------------------------------------------- /src/test/java/org/waveywaves/jenkins/plugins/tekton/client/build/create/JenkinsFreestyleTest.java: -------------------------------------------------------------------------------- 1 | package org.waveywaves.jenkins.plugins.tekton.client.build.create; 2 | 3 | import hudson.model.FreeStyleBuild; 4 | import hudson.model.FreeStyleProject; 5 | import hudson.model.Result; 6 | import io.fabric8.knative.internal.pkg.apis.Condition; 7 | import io.fabric8.kubernetes.api.model.ContainerBuilder; 8 | import io.fabric8.kubernetes.api.model.ContainerStateBuilder; 9 | import io.fabric8.kubernetes.api.model.ContainerStateTerminatedBuilder; 10 | import io.fabric8.kubernetes.api.model.ContainerStatusBuilder; 11 | import io.fabric8.kubernetes.api.model.OwnerReference; 12 | import io.fabric8.kubernetes.api.model.Pod; 13 | import io.fabric8.kubernetes.api.model.PodBuilder; 14 | import io.fabric8.kubernetes.api.model.PodList; 15 | import io.fabric8.kubernetes.api.model.PodListBuilder; 16 | import io.fabric8.kubernetes.client.Config; 17 | import io.fabric8.kubernetes.client.KubernetesClient; 18 | import io.fabric8.kubernetes.client.server.mock.KubernetesServer; 19 | import io.fabric8.tekton.pipeline.v1beta1.PipelineRunBuilder; 20 | import io.fabric8.tekton.pipeline.v1beta1.TaskBuilder; 21 | import io.fabric8.tekton.pipeline.v1beta1.TaskRunBuilder; 22 | import io.fabric8.tekton.pipeline.v1beta1.TaskRunList; 23 | import io.fabric8.tekton.pipeline.v1beta1.TaskRunListBuilder; 24 | import java.io.IOException; 25 | import java.net.HttpURLConnection; 26 | import java.net.URL; 27 | import java.nio.charset.StandardCharsets; 28 | import org.apache.commons.io.IOUtils; 29 | import org.junit.Before; 30 | import org.junit.Rule; 31 | import org.junit.Test; 32 | import org.junit.rules.RuleChain; 33 | import org.junit.rules.TestRule; 34 | import org.jvnet.hudson.test.ExtractResourceSCM; 35 | import org.jvnet.hudson.test.JenkinsRule; 36 | import org.waveywaves.jenkins.plugins.tekton.client.TektonUtils; 37 | import org.waveywaves.jenkins.plugins.tekton.client.ToolUtils; 38 | 39 | import static org.hamcrest.CoreMatchers.containsString; 40 | import static org.hamcrest.CoreMatchers.is; 41 | import static org.hamcrest.CoreMatchers.notNullValue; 42 | import static org.hamcrest.MatcherAssert.assertThat; 43 | 44 | public class JenkinsFreestyleTest { 45 | 46 | public JenkinsRule jenkinsRule = new JenkinsRule(); 47 | public KubernetesServer kubernetesRule = new KubernetesServer(); 48 | 49 | @Rule 50 | public TestRule chain = 51 | RuleChain.outerRule(kubernetesRule) 52 | .around(jenkinsRule); 53 | 54 | @Before 55 | public void before() { 56 | KubernetesClient client = kubernetesRule.getClient(); 57 | Config config = client.getConfiguration(); 58 | TektonUtils.initializeKubeClients(config); 59 | } 60 | 61 | @Test 62 | public void testFreestyleJobWithFileInput() throws Exception { 63 | TaskBuilder taskBuilder = new TaskBuilder() 64 | .withNewMetadata() 65 | .withName("testTask") 66 | .endMetadata(); 67 | 68 | kubernetesRule.expect() 69 | .post() 70 | .withPath("/apis/tekton.dev/v1beta1/namespaces/test/tasks") 71 | .andReturn(HttpURLConnection.HTTP_OK, taskBuilder.build()).once(); 72 | 73 | FreeStyleProject p = jenkinsRule.jenkins.createProject(FreeStyleProject.class, "p"); 74 | URL zipFile = getClass().getResource("tekton-test-project.zip"); 75 | assertThat(zipFile, is(notNullValue())); 76 | 77 | p.setScm(new ExtractResourceSCM(zipFile)); 78 | p.getBuildersList().add(new CreateRaw(".tekton/task.yaml", "FILE")); 79 | 80 | FreeStyleBuild b = jenkinsRule.assertBuildStatus(Result.SUCCESS, p.scheduleBuild2(0).get()); 81 | 82 | assertThat(kubernetesRule.getMockServer().getRequestCount(), is(1)); 83 | 84 | String log = JenkinsRule.getLog(b); 85 | System.out.println(log); 86 | 87 | assertThat(log, containsString("Legacy code started this job")); 88 | } 89 | 90 | @Test 91 | public void testFreestyleJobWithYamlInput() throws Exception { 92 | TaskBuilder taskBuilder = new TaskBuilder() 93 | .withNewMetadata() 94 | .withName("testTask") 95 | .endMetadata(); 96 | 97 | kubernetesRule.expect() 98 | .post() 99 | .withPath("/apis/tekton.dev/v1beta1/namespaces/test/tasks") 100 | .andReturn(HttpURLConnection.HTTP_OK, taskBuilder.build()).once(); 101 | 102 | FreeStyleProject p = jenkinsRule.jenkins.createProject(FreeStyleProject.class, "p"); 103 | URL zipFile = getClass().getResource("tekton-test-project.zip"); 104 | assertThat(zipFile, is(notNullValue())); 105 | 106 | p.setScm(new ExtractResourceSCM(zipFile)); 107 | p.getBuildersList().add(new CreateRaw("apiVersion: tekton.dev/v1beta1\n" 108 | + "kind: Task\n" 109 | + "metadata:\n" 110 | + " name: testTask\n", "YAML")); 111 | 112 | FreeStyleBuild b = jenkinsRule.assertBuildStatus(Result.SUCCESS, p.scheduleBuild2(0).get()); 113 | 114 | assertThat(kubernetesRule.getMockServer().getRequestCount(), is(1)); 115 | 116 | String log = JenkinsRule.getLog(b); 117 | System.out.println(log); 118 | 119 | assertThat(log, containsString("Legacy code started this job")); 120 | } 121 | 122 | @Test 123 | public void testFreestyleJobWithComplexYamlInput() throws Exception { 124 | ToolUtils.getJXPipelineBinary(ToolUtils.class.getClassLoader()); 125 | 126 | PipelineRunBuilder pipelineRunBuilder = new PipelineRunBuilder() 127 | .withNewMetadata() 128 | .withName("release") 129 | .withNamespace("test") 130 | .withUid("pipeline-run-uid") 131 | .endMetadata() 132 | .withNewSpec() 133 | .withNewPipelineSpec() 134 | .addNewTask() 135 | .withName("pipelineTaskName") 136 | .endTask() 137 | .endPipelineSpec() 138 | .endSpec() 139 | .withNewStatus() 140 | .withConditions(new Condition("lastTransitionTime","","","","True","Succeeded")) 141 | .endStatus(); 142 | 143 | kubernetesRule.expect() 144 | .post() 145 | .withPath("/apis/tekton.dev/v1beta1/namespaces/test/pipelineruns") 146 | .andReturn(HttpURLConnection.HTTP_OK, pipelineRunBuilder.build()) 147 | .once(); 148 | 149 | kubernetesRule.expect() 150 | .get() 151 | .withPath("/apis/tekton.dev/v1beta1/namespaces/test/pipelineruns/release") 152 | .andReturn(HttpURLConnection.HTTP_OK, pipelineRunBuilder.build()) 153 | .once(); 154 | 155 | TaskRunList taskRunList = new TaskRunListBuilder() 156 | .addToItems( 157 | new TaskRunBuilder() 158 | .withNewMetadata() 159 | .withName("testTaskRun") 160 | .withOwnerReferences(ownerReference("pipeline-run-uid")) 161 | .endMetadata() 162 | .build()) 163 | .build(); 164 | 165 | kubernetesRule.expect() 166 | .get() 167 | .withPath("/apis/tekton.dev/v1beta1/namespaces/test/taskruns?labelSelector=tekton.dev%2FpipelineTask%3DpipelineTaskName%2Ctekton.dev%2FpipelineRun%3Drelease") 168 | .andReturn(HttpURLConnection.HTTP_OK, taskRunList) 169 | .once(); 170 | 171 | Pod pod = new PodBuilder() 172 | .withNewMetadata() 173 | .withName("hello-world-pod") 174 | .withNamespace("test") 175 | .withOwnerReferences(ownerReference("TaskRun","testTaskRun")) 176 | .endMetadata() 177 | .withNewSpec() 178 | .withContainers( 179 | new ContainerBuilder() 180 | .withName("hello-world-container") 181 | .build() 182 | ) 183 | .endSpec() 184 | .withNewStatus() 185 | .withPhase("Succeeded") 186 | .withContainerStatuses( 187 | new ContainerStatusBuilder() 188 | .withName("hello-world-container") 189 | .withState( 190 | new ContainerStateBuilder() 191 | .withTerminated(new ContainerStateTerminatedBuilder().withStartedAt("timestamp").build()) 192 | .build() 193 | ) 194 | .build()) 195 | .endStatus() 196 | .build(); 197 | 198 | PodList podList = new PodListBuilder() 199 | .addToItems(pod) 200 | .build(); 201 | 202 | kubernetesRule.expect().get().withPath("/api/v1/namespaces/test/pods?labelSelector=tekton.dev%2FtaskRun%3DtestTaskRun") 203 | .andReturn(HttpURLConnection.HTTP_OK, podList).once(); 204 | 205 | kubernetesRule.expect().get().withPath("/api/v1/namespaces/test/pods/hello-world-pod") 206 | .andReturn(HttpURLConnection.HTTP_OK, pod).always(); 207 | 208 | kubernetesRule.expect().get().withPath("/api/v1/namespaces/test/pods/hello-world-pod/log?pretty=false&container=hello-world-container&follow=true") 209 | .andReturn(HttpURLConnection.HTTP_OK, "Whoop! This is the pod log").once(); 210 | 211 | FreeStyleProject p = jenkinsRule.jenkins.createProject(FreeStyleProject.class, "p"); 212 | URL zipFile = getClass().getResource("tekton-test-project.zip"); 213 | assertThat(zipFile, is(notNullValue())); 214 | 215 | p.setScm(new ExtractResourceSCM(zipFile)); 216 | CreateRaw createRaw = new CreateRaw(contents("jx-pipeline.yaml"), "YAML"); 217 | createRaw.setEnableCatalog(true); 218 | p.getBuildersList().add(createRaw); 219 | 220 | FreeStyleBuild b = jenkinsRule.assertBuildStatus(Result.SUCCESS, p.scheduleBuild2(0).get()); 221 | 222 | String log = JenkinsRule.getLog(b); 223 | System.out.println(log); 224 | 225 | assertThat(log, containsString("Legacy code started this job")); 226 | assertThat(log, containsString("Whoop! This is the pod log")); 227 | 228 | assertThat(kubernetesRule.getMockServer().getRequestCount(), is(9)); 229 | } 230 | 231 | @Test 232 | public void testFreestyleJobWithExpandedYamlInput() throws Exception { 233 | ToolUtils.getJXPipelineBinary(ToolUtils.class.getClassLoader()); 234 | 235 | PipelineRunBuilder pipelineRunBuilder = new PipelineRunBuilder() 236 | .withNewMetadata() 237 | .withName("release") 238 | .withNamespace("test") 239 | .withUid("pipeline-run-uid") 240 | .endMetadata() 241 | .withNewSpec() 242 | .withNewPipelineSpec() 243 | .addNewTask() 244 | .withName("pipelineTaskName") 245 | .endTask() 246 | .endPipelineSpec() 247 | .endSpec() 248 | .withNewStatus() 249 | .withConditions(new Condition("lastTransitionTime","","","","True","Succeeded")) 250 | .endStatus(); 251 | 252 | kubernetesRule.expect() 253 | .post() 254 | .withPath("/apis/tekton.dev/v1beta1/namespaces/test/pipelineruns") 255 | .andReturn(HttpURLConnection.HTTP_OK, pipelineRunBuilder.build()) 256 | .once(); 257 | 258 | kubernetesRule.expect() 259 | .get() 260 | .withPath("/apis/tekton.dev/v1beta1/namespaces/test/pipelineruns/release") 261 | .andReturn(HttpURLConnection.HTTP_OK, pipelineRunBuilder.build()) 262 | .once(); 263 | 264 | TaskRunList taskRunList = new TaskRunListBuilder() 265 | .addToItems( 266 | new TaskRunBuilder() 267 | .withNewMetadata() 268 | .withName("testTaskRun") 269 | .withOwnerReferences(ownerReference("pipeline-run-uid")) 270 | .endMetadata() 271 | .build()) 272 | .build(); 273 | 274 | kubernetesRule.expect() 275 | .get() 276 | .withPath("/apis/tekton.dev/v1beta1/namespaces/test/taskruns?labelSelector=tekton.dev%2FpipelineTask%3DpipelineTaskName%2Ctekton.dev%2FpipelineRun%3Drelease") 277 | .andReturn(HttpURLConnection.HTTP_OK, taskRunList) 278 | .once(); 279 | 280 | Pod pod = new PodBuilder() 281 | .withNewMetadata() 282 | .withName("hello-world-pod") 283 | .withNamespace("test") 284 | .withOwnerReferences(ownerReference("TaskRun","testTaskRun")) 285 | .endMetadata() 286 | .withNewSpec() 287 | .withContainers( 288 | new ContainerBuilder() 289 | .withName("hello-world-container") 290 | .build() 291 | ) 292 | .endSpec() 293 | .withNewStatus() 294 | .withPhase("Succeeded") 295 | .withContainerStatuses( 296 | new ContainerStatusBuilder() 297 | .withName("hello-world-container") 298 | .withState( 299 | new ContainerStateBuilder() 300 | .withTerminated(new ContainerStateTerminatedBuilder().withStartedAt("timestamp").build()) 301 | .build() 302 | ) 303 | .build()) 304 | .endStatus() 305 | .build(); 306 | 307 | PodList podList = new PodListBuilder() 308 | .addToItems(pod) 309 | .build(); 310 | 311 | kubernetesRule.expect().get().withPath("/api/v1/namespaces/test/pods?labelSelector=tekton.dev%2FtaskRun%3DtestTaskRun") 312 | .andReturn(HttpURLConnection.HTTP_OK, podList).once(); 313 | 314 | kubernetesRule.expect().get().withPath("/api/v1/namespaces/test/pods/hello-world-pod") 315 | .andReturn(HttpURLConnection.HTTP_OK, pod).always(); 316 | 317 | kubernetesRule.expect().get().withPath("/api/v1/namespaces/test/pods/hello-world-pod/log?pretty=false&container=hello-world-container&follow=true") 318 | .andReturn(HttpURLConnection.HTTP_OK, "Whoop! This is the pod log").once(); 319 | 320 | FreeStyleProject p = jenkinsRule.jenkins.createProject(FreeStyleProject.class, "p"); 321 | URL zipFile = getClass().getResource("tekton-test-project.zip"); 322 | assertThat(zipFile, is(notNullValue())); 323 | 324 | p.setScm(new ExtractResourceSCM(zipFile)); 325 | CreateRaw createRaw = new CreateRaw(contents("jx-pipeline.expanded.yaml"), "YAML"); 326 | createRaw.setEnableCatalog(false); 327 | p.getBuildersList().add(createRaw); 328 | 329 | FreeStyleBuild b = jenkinsRule.assertBuildStatus(Result.SUCCESS, p.scheduleBuild2(0).get()); 330 | 331 | String log = JenkinsRule.getLog(b); 332 | System.out.println(log); 333 | 334 | assertThat(log, containsString("Legacy code started this job")); 335 | assertThat(log, containsString("Whoop! This is the pod log")); 336 | 337 | assertThat(kubernetesRule.getMockServer().getRequestCount(), is(9)); 338 | } 339 | 340 | private String contents(String filename) throws IOException { 341 | return IOUtils.toString(this.getClass().getResourceAsStream(filename), StandardCharsets.UTF_8.name()); 342 | } 343 | 344 | private OwnerReference ownerReference(String uid) { 345 | return new OwnerReference("", false, false, "", "", uid); 346 | } 347 | 348 | private OwnerReference ownerReference(String kind, String name) { 349 | return new OwnerReference("", false, false, kind, name, ""); 350 | } 351 | } 352 | -------------------------------------------------------------------------------- /src/test/java/org/waveywaves/jenkins/plugins/tekton/client/build/create/mock/CreateRawMock.java: -------------------------------------------------------------------------------- 1 | package org.waveywaves.jenkins.plugins.tekton.client.build.create.mock; 2 | 3 | import hudson.EnvVars; 4 | import io.fabric8.tekton.pipeline.v1beta1.PipelineRun; 5 | import io.fabric8.tekton.pipeline.v1beta1.TaskRun; 6 | import org.waveywaves.jenkins.plugins.tekton.client.TektonUtils; 7 | import org.waveywaves.jenkins.plugins.tekton.client.build.create.CreateRaw; 8 | 9 | import java.io.InputStream; 10 | 11 | public class CreateRawMock extends CreateRaw { 12 | public CreateRawMock(String input, String inputType) { 13 | super(input, inputType); 14 | } 15 | 16 | @Override 17 | public String createTaskRun(InputStream inputStream) { 18 | return TektonUtils.TektonResourceType.taskrun.toString(); 19 | } 20 | 21 | @Override 22 | public String createTask(InputStream inputStream) { 23 | return TektonUtils.TektonResourceType.task.toString(); 24 | } 25 | 26 | @Override 27 | public String createPipeline(InputStream inputStream) { 28 | return TektonUtils.TektonResourceType.pipeline.toString(); 29 | } 30 | 31 | @Override 32 | public String createPipelineRun(InputStream inputStream, EnvVars envVars) { return TektonUtils.TektonResourceType.pipelinerun.toString(); } 33 | 34 | @Override 35 | public void streamTaskRunLogsToConsole(TaskRun taskRun) { 36 | return; 37 | } 38 | 39 | @Override 40 | public void streamPipelineRunLogsToConsole(PipelineRun pipelineRun) { 41 | return; 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/test/java/org/waveywaves/jenkins/plugins/tekton/client/build/create/mock/FakeCreateRaw.java: -------------------------------------------------------------------------------- 1 | package org.waveywaves.jenkins.plugins.tekton.client.build.create.mock; 2 | 3 | import hudson.EnvVars; 4 | import org.apache.commons.io.IOUtils; 5 | 6 | import java.io.IOException; 7 | import java.io.InputStream; 8 | import java.nio.charset.Charset; 9 | 10 | /** 11 | * 12 | */ 13 | public class FakeCreateRaw extends CreateRawMock { 14 | private String name = "fakeName"; 15 | private String lastResource; 16 | 17 | public FakeCreateRaw(String input, String inputType) { 18 | super(input, inputType); 19 | } 20 | 21 | @Override 22 | public String createTaskRun(InputStream inputStream) { 23 | return createResource(inputStream); 24 | } 25 | 26 | @Override 27 | public String createTask(InputStream inputStream) { 28 | return createResource(inputStream); 29 | } 30 | 31 | @Override 32 | public String createPipeline(InputStream inputStream) { 33 | return createResource(inputStream); 34 | } 35 | 36 | @Override 37 | public String createPipelineRun(InputStream inputStream, EnvVars envVars) { 38 | return createResource(inputStream); 39 | } 40 | 41 | 42 | protected String createResource(InputStream inputStream) { 43 | try { 44 | lastResource = IOUtils.toString(inputStream, Charset.defaultCharset()); 45 | return name; 46 | } catch (IOException e) { 47 | throw new RuntimeException(e); 48 | } 49 | } 50 | 51 | public String getLastResource() { 52 | return lastResource; 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /src/test/java/org/waveywaves/jenkins/plugins/tekton/client/build/delete/DeleteRawTest.java: -------------------------------------------------------------------------------- 1 | package org.waveywaves.jenkins.plugins.tekton.client.build.delete; 2 | 3 | import org.junit.Test; 4 | import org.waveywaves.jenkins.plugins.tekton.client.TektonUtils; 5 | import org.waveywaves.jenkins.plugins.tekton.client.build.delete.mock.DeleteRawMock; 6 | 7 | import static org.hamcrest.CoreMatchers.is; 8 | import static org.hamcrest.MatcherAssert.assertThat; 9 | 10 | public class DeleteRawTest { 11 | 12 | DeleteRaw.DeleteAllBlock deleteAllBlock; 13 | 14 | @Test 15 | public void runDeleteTaskTest(){ 16 | deleteAllBlock = new DeleteRaw.DeleteAllBlock("test"); 17 | DeleteRaw deleteRaw = new DeleteRawMock(TektonUtils.TektonResourceType.task.toString(), TektonUtils.DEFAULT_CLIENT_KEY, deleteAllBlock); 18 | Boolean isDeleted = deleteRaw.runDelete(); 19 | assertThat(isDeleted, is(true)); 20 | } 21 | 22 | @Test 23 | public void runDeleteTaskRunTest(){ 24 | deleteAllBlock = new DeleteRaw.DeleteAllBlock("test"); 25 | DeleteRaw deleteRaw = new DeleteRawMock(TektonUtils.TektonResourceType.taskrun.toString(),TektonUtils.DEFAULT_CLIENT_KEY, deleteAllBlock); 26 | Boolean isDeleted = deleteRaw.runDelete(); 27 | assertThat(isDeleted, is(true)); 28 | } 29 | 30 | @Test 31 | public void runDeletePipelineTest(){ 32 | deleteAllBlock = new DeleteRaw.DeleteAllBlock("test"); 33 | DeleteRaw deleteRaw = new DeleteRawMock(TektonUtils.TektonResourceType.pipeline.toString(),TektonUtils.DEFAULT_CLIENT_KEY, deleteAllBlock); 34 | Boolean isDeleted = deleteRaw.runDelete(); 35 | assertThat(isDeleted, is(true)); 36 | } 37 | 38 | @Test 39 | public void runDeletePipelineRunTest(){ 40 | deleteAllBlock = new DeleteRaw.DeleteAllBlock("test"); 41 | DeleteRaw deleteRaw = new DeleteRawMock(TektonUtils.TektonResourceType.pipelinerun.toString(),TektonUtils.DEFAULT_CLIENT_KEY, deleteAllBlock); 42 | Boolean isDeleted = deleteRaw.runDelete(); 43 | assertThat(isDeleted, is(true)); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/test/java/org/waveywaves/jenkins/plugins/tekton/client/build/delete/mock/DeleteRawMock.java: -------------------------------------------------------------------------------- 1 | package org.waveywaves.jenkins.plugins.tekton.client.build.delete.mock; 2 | 3 | import org.waveywaves.jenkins.plugins.tekton.client.build.delete.DeleteRaw; 4 | 5 | 6 | public class DeleteRawMock extends DeleteRaw { 7 | public DeleteRawMock(String resourceType, String clusterName, DeleteAllBlock deleteAllBlock) { 8 | super(resourceType, clusterName, deleteAllBlock); 9 | } 10 | 11 | @Override 12 | public Boolean deleteTask() { 13 | return true; 14 | } 15 | 16 | @Override 17 | public Boolean deleteTaskRun() { 18 | return true; 19 | } 20 | 21 | @Override 22 | public Boolean deletePipeline() { 23 | return true; 24 | } 25 | 26 | @Override 27 | public Boolean deletePipelineRun() { 28 | return true; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/test/resources/org/waveywaves/jenkins/plugins/tekton/client/build/create/jx-pipeline.expanded.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: tekton.dev/v1beta1 2 | kind: PipelineRun 3 | metadata: 4 | creationTimestamp: null 5 | name: release 6 | spec: 7 | params: 8 | - name: BUILD_ID 9 | value: "" 10 | - name: JOB_NAME 11 | value: "" 12 | - name: JOB_SPEC 13 | value: "" 14 | - name: JOB_TYPE 15 | value: "" 16 | - name: PULL_BASE_REF 17 | value: "" 18 | - name: PULL_BASE_SHA 19 | value: "" 20 | - name: PULL_NUMBER 21 | value: "" 22 | - name: PULL_PULL_REF 23 | value: "" 24 | - name: PULL_PULL_SHA 25 | value: "" 26 | - name: PULL_REFS 27 | value: "" 28 | - name: REPO_NAME 29 | value: "" 30 | - name: REPO_OWNER 31 | value: "" 32 | - name: REPO_URL 33 | value: "" 34 | pipelineSpec: 35 | params: 36 | - description: the unique build number 37 | name: BUILD_ID 38 | type: string 39 | - description: the name of the job which is the trigger context name 40 | name: JOB_NAME 41 | type: string 42 | - description: the specification of the job 43 | name: JOB_SPEC 44 | type: string 45 | - description: '''the kind of job: postsubmit or presubmit''' 46 | name: JOB_TYPE 47 | type: string 48 | - description: the base git reference of the pull request 49 | name: PULL_BASE_REF 50 | type: string 51 | - description: the git sha of the base of the pull request 52 | name: PULL_BASE_SHA 53 | type: string 54 | - default: "" 55 | description: git pull request number 56 | name: PULL_NUMBER 57 | type: string 58 | - default: "" 59 | description: git pull request ref in the form 'refs/pull/$PULL_NUMBER/head' 60 | name: PULL_PULL_REF 61 | type: string 62 | - default: "" 63 | description: git revision to checkout (branch, tag, sha, ref…) 64 | name: PULL_PULL_SHA 65 | type: string 66 | - description: git pull reference strings of base and latest in the form 'master:$PULL_BASE_SHA,$PULL_NUMBER:$PULL_PULL_SHA:refs/pull/$PULL_NUMBER/head' 67 | name: PULL_REFS 68 | type: string 69 | - description: git repository name 70 | name: REPO_NAME 71 | type: string 72 | - description: git repository owner (user or organisation) 73 | name: REPO_OWNER 74 | type: string 75 | - description: git url to clone 76 | name: REPO_URL 77 | type: string 78 | tasks: 79 | - name: from-build-pack 80 | params: 81 | - name: BUILD_ID 82 | value: $(params.BUILD_ID) 83 | - name: JOB_NAME 84 | value: $(params.JOB_NAME) 85 | - name: JOB_SPEC 86 | value: $(params.JOB_SPEC) 87 | - name: JOB_TYPE 88 | value: $(params.JOB_TYPE) 89 | - name: PULL_BASE_REF 90 | value: $(params.PULL_BASE_REF) 91 | - name: PULL_BASE_SHA 92 | value: $(params.PULL_BASE_SHA) 93 | - name: PULL_NUMBER 94 | value: $(params.PULL_NUMBER) 95 | - name: PULL_PULL_REF 96 | value: $(params.PULL_PULL_REF) 97 | - name: PULL_PULL_SHA 98 | value: $(params.PULL_PULL_SHA) 99 | - name: PULL_REFS 100 | value: $(params.PULL_REFS) 101 | - name: REPO_NAME 102 | value: $(params.REPO_NAME) 103 | - name: REPO_OWNER 104 | value: $(params.REPO_OWNER) 105 | - name: REPO_URL 106 | value: $(params.REPO_URL) 107 | resources: {} 108 | taskSpec: 109 | metadata: {} 110 | params: 111 | - description: the unique build number 112 | name: BUILD_ID 113 | type: string 114 | - description: the name of the job which is the trigger context name 115 | name: JOB_NAME 116 | type: string 117 | - description: the specification of the job 118 | name: JOB_SPEC 119 | type: string 120 | - description: '''the kind of job: postsubmit or presubmit''' 121 | name: JOB_TYPE 122 | type: string 123 | - description: the base git reference of the pull request 124 | name: PULL_BASE_REF 125 | type: string 126 | - description: the git sha of the base of the pull request 127 | name: PULL_BASE_SHA 128 | type: string 129 | - default: "" 130 | description: git pull request number 131 | name: PULL_NUMBER 132 | type: string 133 | - default: "" 134 | description: git pull request ref in the form 'refs/pull/$PULL_NUMBER/head' 135 | name: PULL_PULL_REF 136 | type: string 137 | - default: "" 138 | description: git revision to checkout (branch, tag, sha, ref…) 139 | name: PULL_PULL_SHA 140 | type: string 141 | - description: git pull reference strings of base and latest in the form 'master:$PULL_BASE_SHA,$PULL_NUMBER:$PULL_PULL_SHA:refs/pull/$PULL_NUMBER/head' 142 | name: PULL_REFS 143 | type: string 144 | - description: git repository name 145 | name: REPO_NAME 146 | type: string 147 | - description: git repository owner (user or organisation) 148 | name: REPO_OWNER 149 | type: string 150 | - description: git url to clone 151 | name: REPO_URL 152 | type: string 153 | stepTemplate: 154 | env: 155 | - name: BUILD_ID 156 | value: $(params.BUILD_ID) 157 | - name: JOB_NAME 158 | value: $(params.JOB_NAME) 159 | - name: JOB_SPEC 160 | value: $(params.JOB_SPEC) 161 | - name: JOB_TYPE 162 | value: $(params.JOB_TYPE) 163 | - name: PULL_BASE_REF 164 | value: $(params.PULL_BASE_REF) 165 | - name: PULL_BASE_SHA 166 | value: $(params.PULL_BASE_SHA) 167 | - name: PULL_NUMBER 168 | value: $(params.PULL_NUMBER) 169 | - name: PULL_PULL_REF 170 | value: $(params.PULL_PULL_REF) 171 | - name: PULL_PULL_SHA 172 | value: $(params.PULL_PULL_SHA) 173 | - name: PULL_REFS 174 | value: $(params.PULL_REFS) 175 | - name: REPO_NAME 176 | value: $(params.REPO_NAME) 177 | - name: REPO_OWNER 178 | value: $(params.REPO_OWNER) 179 | - name: REPO_URL 180 | value: $(params.REPO_URL) 181 | name: "" 182 | resources: 183 | requests: 184 | cpu: 400m 185 | memory: 600Mi 186 | workingDir: /workspace/source 187 | steps: 188 | - image: gcr.io/tekton-releases/github.com/tektoncd/pipeline/cmd/git-init:v0.19.0 189 | name: git-clone 190 | resources: {} 191 | script: | 192 | #!/bin/sh 193 | export SUBDIR="source" 194 | echo "git cloning url: $REPO_URL version $PULL_BASE_SHA to dir: $SUBDIR" 195 | git config --global --add user.name ${GIT_AUTHOR_NAME:-jenkins-x-bot} 196 | git config --global --add user.email ${GIT_AUTHOR_EMAIL:-jenkins-x@googlegroups.com} 197 | git config --global credential.helper store 198 | git clone $REPO_URL $SUBDIR 199 | cd $SUBDIR 200 | git checkout $PULL_BASE_SHA 201 | echo "checked out revision: $PULL_BASE_SHA to dir: $SUBDIR" 202 | workingDir: /workspace 203 | - env: 204 | - name: GIT_TOKEN 205 | valueFrom: 206 | secretKeyRef: 207 | key: password 208 | name: tekton-git 209 | - name: GIT_USER 210 | valueFrom: 211 | secretKeyRef: 212 | key: username 213 | name: tekton-git 214 | image: gcr.io/jenkinsxio/jx-release-version:2.4.3 215 | name: next-version 216 | resources: {} 217 | script: | 218 | #!/usr/bin/env sh 219 | jx-release-version --tag > VERSION 220 | - image: ghcr.io/jenkins-x/jx-boot:3.2.65 221 | name: jx-variables 222 | resources: {} 223 | script: | 224 | #!/usr/bin/env sh 225 | jx gitops variables 226 | - image: golang:1.15 227 | name: build-make-build 228 | resources: {} 229 | script: | 230 | #!/bin/sh 231 | make build 232 | - image: gcr.io/jenkinsxio/jx-changelog:0.0.42 233 | name: promote-changelog 234 | resources: {} 235 | script: | 236 | #!/usr/bin/env sh 237 | source .jx/variables.sh 238 | 239 | if [ -d "charts/$REPO_NAME" ]; then 240 | jx gitops yset -p version -v "$VERSION" -f ./charts/$REPO_NAME/Chart.yaml 241 | jx gitops yset -p 'image.repository' -v $DOCKER_REGISTRY/$DOCKER_REGISTRY_ORG/$APP_NAME -f ./charts/$REPO_NAME/values.yaml 242 | jx gitops yset -p 'image.tag' -v "$VERSION" -f ./charts/$REPO_NAME/values.yaml; 243 | else echo no charts; fi 244 | 245 | git add * || true 246 | git commit -a -m "chore: release $VERSION" --allow-empty 247 | git tag -fa v$VERSION -m "Release version $VERSION" 248 | git push --force origin v$VERSION 249 | 250 | jx changelog create --version v${VERSION} 251 | workspaces: 252 | - description: The git repo will be cloned onto the volume backing this workspace 253 | mountPath: /workspace 254 | name: output 255 | workspaces: 256 | - name: output 257 | workspace: output 258 | workspaces: 259 | - description: The git repo will be cloned onto the volume backing this workspace 260 | name: output 261 | podTemplate: {} 262 | serviceAccountName: tekton-bot 263 | timeout: 240h0m0s 264 | workspaces: 265 | - emptyDir: {} 266 | name: output 267 | status: {} 268 | -------------------------------------------------------------------------------- /src/test/resources/org/waveywaves/jenkins/plugins/tekton/client/build/create/jx-pipeline.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: tekton.dev/v1beta1 3 | kind: PipelineRun 4 | metadata: 5 | creationTimestamp: null 6 | name: release 7 | spec: 8 | pipelineSpec: 9 | tasks: 10 | - name: from-build-pack 11 | resources: {} 12 | taskSpec: 13 | metadata: {} 14 | stepTemplate: 15 | image: uses:jenkins-x/jx3-pipeline-catalog/tasks/go/release.yaml@versionStream 16 | name: "" 17 | resources: 18 | requests: 19 | cpu: 400m 20 | memory: 600Mi 21 | workingDir: /workspace/source 22 | steps: 23 | - image: uses:jenkins-x/jx3-pipeline-catalog/tasks/git-clone/git-clone.yaml@versionStream 24 | name: "" 25 | resources: {} 26 | - name: next-version 27 | resources: {} 28 | - name: jx-variables 29 | resources: {} 30 | - name: build-make-build 31 | resources: {} 32 | - name: promote-changelog 33 | resources: {} 34 | podTemplate: {} 35 | serviceAccountName: tekton-bot 36 | timeout: 240h0m0s 37 | status: {} 38 | -------------------------------------------------------------------------------- /src/test/resources/org/waveywaves/jenkins/plugins/tekton/client/build/create/tekton-test-project.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jenkinsci/tekton-client-plugin/d79aec67448b9639e81e871045e324891a421362/src/test/resources/org/waveywaves/jenkins/plugins/tekton/client/build/create/tekton-test-project.zip -------------------------------------------------------------------------------- /src/test/resources/org/waveywaves/jenkins/plugins/tekton/client/jxp/linux/jx-pipeline-effective: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | echo "fake jx-pipeline-effective binary" 4 | 5 | INPUTFILE="missing-input.yaml" 6 | OUTPUTFILE="missing-output.yaml" 7 | 8 | POSITIONAL=() 9 | while [[ $# -gt 0 ]] 10 | do 11 | key="$1" 12 | 13 | case $key in 14 | -o|--out) 15 | OUTPUTFILE="$2" 16 | shift # past argument 17 | shift # past value 18 | ;; 19 | -f|--file) 20 | INPUTFILE="$2" 21 | shift # past argument 22 | shift # past value 23 | ;; 24 | *) # unknown option 25 | shift # past argument 26 | ;; 27 | esac 28 | done 29 | 30 | echo "reading input file: $INPUTFILE" 31 | echo "writing output file: $OUTPUTFILE" 32 | 33 | cat $INPUTFILE > $OUTPUTFILE 34 | echo "labels:" >> $OUTPUTFILE 35 | echo " cheese: $CHEESE" >> $OUTPUTFILE -------------------------------------------------------------------------------- /src/test/resources/org/waveywaves/jenkins/plugins/tekton/client/jxp/mac/jx-pipeline-effective: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | echo "fake jx-pipeline-effective binary" 4 | 5 | INPUTFILE="missing-input.yaml" 6 | OUTPUTFILE="missing-output.yaml" 7 | 8 | POSITIONAL=() 9 | while [[ $# -gt 0 ]] 10 | do 11 | key="$1" 12 | 13 | case $key in 14 | -o|--out) 15 | OUTPUTFILE="$2" 16 | shift # past argument 17 | shift # past value 18 | ;; 19 | -f|--file) 20 | INPUTFILE="$2" 21 | shift # past argument 22 | shift # past value 23 | ;; 24 | *) # unknown option 25 | shift # past argument 26 | ;; 27 | esac 28 | done 29 | 30 | echo "reading input file: $INPUTFILE" 31 | echo "writing output file: $OUTPUTFILE" 32 | 33 | cat $INPUTFILE > $OUTPUTFILE 34 | echo "labels:" >> $OUTPUTFILE 35 | echo " cheese: $CHEESE" >> $OUTPUTFILE -------------------------------------------------------------------------------- /src/test/resources/org/waveywaves/jenkins/plugins/tekton/client/jxp/windows/jx-pipeline-effective: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | echo "fake jx-pipeline-effective binary" 4 | 5 | INPUTFILE="missing-input.yaml" 6 | OUTPUTFILE="missing-output.yaml" 7 | 8 | POSITIONAL=() 9 | while [[ $# -gt 0 ]] 10 | do 11 | key="$1" 12 | 13 | case $key in 14 | -o|--out) 15 | OUTPUTFILE="$2" 16 | shift # past argument 17 | shift # past value 18 | ;; 19 | -f|--file) 20 | INPUTFILE="$2" 21 | shift # past argument 22 | shift # past value 23 | ;; 24 | *) # unknown option 25 | shift # past argument 26 | ;; 27 | esac 28 | done 29 | 30 | echo "reading input file: $INPUTFILE" 31 | echo "writing output file: $OUTPUTFILE" 32 | 33 | cat $INPUTFILE > $OUTPUTFILE 34 | echo "labels:" >> $OUTPUTFILE 35 | echo " cheese: $CHEESE" >> $OUTPUTFILE -------------------------------------------------------------------------------- /src/test/resources/pipeline-crd.yaml: -------------------------------------------------------------------------------- 1 | # Copyright 2019 The Tekton Authors 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # https://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | apiVersion: apiextensions.k8s.io/v1beta1 16 | kind: CustomResourceDefinition 17 | metadata: 18 | name: pipelines.tekton.dev 19 | labels: 20 | app.kubernetes.io/instance: default 21 | app.kubernetes.io/part-of: tekton-pipelines 22 | pipeline.tekton.dev/release: "devel" 23 | version: "devel" 24 | spec: 25 | group: tekton.dev 26 | preserveUnknownFields: false 27 | validation: 28 | openAPIV3Schema: 29 | type: object 30 | # One can use x-kubernetes-preserve-unknown-fields: true 31 | # at the root of the schema (and inside any properties, additionalProperties) 32 | # to get the traditional CRD behaviour that nothing is pruned, despite 33 | # setting spec.preserveUnknownProperties: false. 34 | # 35 | # See https://kubernetes.io/blog/2019/06/20/crd-structural-schema/ 36 | # See issue: https://github.com/knative/serving/issues/912 37 | x-kubernetes-preserve-unknown-fields: true 38 | versions: 39 | - name: v1alpha1 40 | served: true 41 | storage: false 42 | - name: v1beta1 43 | served: true 44 | storage: true 45 | names: 46 | kind: Pipeline 47 | plural: pipelines 48 | categories: 49 | - tekton 50 | - tekton-pipelines 51 | scope: Namespaced 52 | # Opt into the status subresource so metadata.generation 53 | # starts to increment 54 | subresources: 55 | status: {} 56 | conversion: 57 | strategy: Webhook 58 | webhookClientConfig: 59 | service: 60 | name: tekton-pipelines-webhook 61 | namespace: tekton-pipelines 62 | -------------------------------------------------------------------------------- /src/test/resources/pipelinerun-crd.yaml: -------------------------------------------------------------------------------- 1 | # Copyright 2019 The Tekton Authors 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # https://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | apiVersion: apiextensions.k8s.io/v1beta1 16 | kind: CustomResourceDefinition 17 | metadata: 18 | name: pipelineruns.tekton.dev 19 | labels: 20 | app.kubernetes.io/instance: default 21 | app.kubernetes.io/part-of: tekton-pipelines 22 | pipeline.tekton.dev/release: "devel" 23 | version: "devel" 24 | spec: 25 | group: tekton.dev 26 | preserveUnknownFields: false 27 | validation: 28 | openAPIV3Schema: 29 | type: object 30 | # One can use x-kubernetes-preserve-unknown-fields: true 31 | # at the root of the schema (and inside any properties, additionalProperties) 32 | # to get the traditional CRD behaviour that nothing is pruned, despite 33 | # setting spec.preserveUnknownProperties: false. 34 | # 35 | # See https://kubernetes.io/blog/2019/06/20/crd-structural-schema/ 36 | # See issue: https://github.com/knative/serving/issues/912 37 | x-kubernetes-preserve-unknown-fields: true 38 | versions: 39 | - name: v1alpha1 40 | served: true 41 | storage: false 42 | - name: v1beta1 43 | served: true 44 | storage: true 45 | names: 46 | kind: PipelineRun 47 | plural: pipelineruns 48 | categories: 49 | - tekton 50 | - tekton-pipelines 51 | shortNames: 52 | - pr 53 | - prs 54 | scope: Namespaced 55 | additionalPrinterColumns: 56 | - name: Succeeded 57 | type: string 58 | JSONPath: ".status.conditions[?(@.type==\"Succeeded\")].status" 59 | - name: Reason 60 | type: string 61 | JSONPath: ".status.conditions[?(@.type==\"Succeeded\")].reason" 62 | - name: StartTime 63 | type: date 64 | JSONPath: .status.startTime 65 | - name: CompletionTime 66 | type: date 67 | JSONPath: .status.completionTime 68 | # Opt into the status subresource so metadata.generation 69 | # starts to increment 70 | subresources: 71 | status: {} 72 | conversion: 73 | strategy: Webhook 74 | webhookClientConfig: 75 | service: 76 | name: tekton-pipelines-webhook 77 | namespace: tekton-pipelines 78 | -------------------------------------------------------------------------------- /src/test/resources/resource-crd.yaml: -------------------------------------------------------------------------------- 1 | # Copyright 2019 The Tekton Authors 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # https://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | apiVersion: apiextensions.k8s.io/v1beta1 16 | kind: CustomResourceDefinition 17 | metadata: 18 | name: pipelineresources.tekton.dev 19 | labels: 20 | app.kubernetes.io/instance: default 21 | app.kubernetes.io/part-of: tekton-pipelines 22 | pipeline.tekton.dev/release: "devel" 23 | version: "devel" 24 | spec: 25 | group: tekton.dev 26 | names: 27 | kind: PipelineResource 28 | plural: pipelineresources 29 | categories: 30 | - tekton 31 | - tekton-pipelines 32 | scope: Namespaced 33 | # Opt into the status subresource so metadata.generation 34 | # starts to increment 35 | subresources: 36 | status: {} 37 | version: v1alpha1 38 | -------------------------------------------------------------------------------- /src/test/resources/task-crd.yaml: -------------------------------------------------------------------------------- 1 | # Copyright 2019 The Tekton Authors 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # https://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | apiVersion: apiextensions.k8s.io/v1beta1 16 | kind: CustomResourceDefinition 17 | metadata: 18 | name: tasks.tekton.dev 19 | labels: 20 | app.kubernetes.io/instance: default 21 | app.kubernetes.io/part-of: tekton-pipelines 22 | pipeline.tekton.dev/release: "devel" 23 | version: "devel" 24 | spec: 25 | group: tekton.dev 26 | preserveUnknownFields: false 27 | validation: 28 | openAPIV3Schema: 29 | type: object 30 | # One can use x-kubernetes-preserve-unknown-fields: true 31 | # at the root of the schema (and inside any properties, additionalProperties) 32 | # to get the traditional CRD behaviour that nothing is pruned, despite 33 | # setting spec.preserveUnknownProperties: false. 34 | # 35 | # See https://kubernetes.io/blog/2019/06/20/crd-structural-schema/ 36 | # See issue: https://github.com/knative/serving/issues/912 37 | x-kubernetes-preserve-unknown-fields: true 38 | versions: 39 | - name: v1alpha1 40 | served: true 41 | storage: false 42 | - name: v1beta1 43 | served: true 44 | storage: true 45 | names: 46 | kind: Task 47 | plural: tasks 48 | categories: 49 | - tekton 50 | - tekton-pipelines 51 | scope: Namespaced 52 | # Opt into the status subresource so metadata.generation 53 | # starts to increment 54 | subresources: 55 | status: {} 56 | conversion: 57 | strategy: Webhook 58 | webhookClientConfig: 59 | service: 60 | name: tekton-pipelines-webhook 61 | namespace: tekton-pipelines 62 | -------------------------------------------------------------------------------- /src/test/resources/taskrun-crd.yaml: -------------------------------------------------------------------------------- 1 | # Copyright 2019 The Tekton Authors 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # https://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | apiVersion: apiextensions.k8s.io/v1beta1 16 | kind: CustomResourceDefinition 17 | metadata: 18 | name: taskruns.tekton.dev 19 | labels: 20 | app.kubernetes.io/instance: default 21 | app.kubernetes.io/part-of: tekton-pipelines 22 | pipeline.tekton.dev/release: "devel" 23 | version: "devel" 24 | spec: 25 | group: tekton.dev 26 | preserveUnknownFields: false 27 | validation: 28 | openAPIV3Schema: 29 | type: object 30 | # One can use x-kubernetes-preserve-unknown-fields: true 31 | # at the root of the schema (and inside any properties, additionalProperties) 32 | # to get the traditional CRD behaviour that nothing is pruned, despite 33 | # setting spec.preserveUnknownProperties: false. 34 | # 35 | # See https://kubernetes.io/blog/2019/06/20/crd-structural-schema/ 36 | # See issue: https://github.com/knative/serving/issues/912 37 | x-kubernetes-preserve-unknown-fields: true 38 | versions: 39 | - name: v1beta1 40 | served: true 41 | storage: true 42 | - name: v1alpha1 43 | served: true 44 | storage: false 45 | names: 46 | kind: TaskRun 47 | plural: taskruns 48 | categories: 49 | - tekton 50 | - tekton-pipelines 51 | shortNames: 52 | - tr 53 | - trs 54 | scope: Namespaced 55 | additionalPrinterColumns: 56 | - name: Succeeded 57 | type: string 58 | JSONPath: ".status.conditions[?(@.type==\"Succeeded\")].status" 59 | - name: Reason 60 | type: string 61 | JSONPath: ".status.conditions[?(@.type==\"Succeeded\")].reason" 62 | - name: StartTime 63 | type: date 64 | JSONPath: .status.startTime 65 | - name: CompletionTime 66 | type: date 67 | JSONPath: .status.completionTime 68 | # Opt into the status subresource so metadata.generation 69 | # starts to increment 70 | subresources: 71 | status: {} 72 | conversion: 73 | strategy: Webhook 74 | webhookClientConfig: 75 | service: 76 | name: tekton-pipelines-webhook 77 | namespace: tekton-pipelines 78 | --------------------------------------------------------------------------------