├── .gitignore ├── Dockerfile ├── Dockerfile.rhel7 ├── Jenkinsfile ├── README.md ├── apb └── .gitignore ├── docs ├── index.adoc └── index.html ├── examples ├── REAME.md └── containerzone │ ├── docker │ ├── .gitignore │ └── jobs │ │ └── buildPushScan.groovy │ ├── generic │ └── jobs │ │ └── CheckHealthGrade │ │ └── checkHealthGradePipeline.groovy │ ├── openshift │ ├── .gitignore │ ├── buildconfig-pipeline.yaml │ └── jobs │ │ └── buildPushScan.groovy │ └── playbooks │ ├── installseedjob.yml │ └── templates │ └── checkImageHealthSeed.xml ├── jenkins ├── README.md ├── configuration │ ├── jobs │ │ └── gitHubOrgSeed │ │ │ └── config.xml │ ├── org.jenkinsci.plugins.workflow.libs.GlobalLibraries.xml │ └── scriptApproval.xml ├── jobs │ ├── configureJenkinsOpenShift.groovy │ └── githubOrgJob.groovy ├── openshift │ ├── pipeline.yaml │ └── template.yaml └── plugins.txt ├── src └── com │ └── redhat │ ├── GitHubUtils.groovy │ ├── JenkinsUtils.groovy │ └── Utils.groovy ├── tests ├── README.md ├── jobs │ ├── __files │ │ ├── publish-200-error.json │ │ ├── publish-200-publish.json │ │ ├── publish-200-unable.json │ │ ├── publish-401-error.json │ │ ├── scanResults-200-error.json │ │ ├── scanResults-401-error.json │ │ ├── status-200-error.json │ │ ├── status-200-mand.json │ │ ├── status-200-none.json │ │ ├── status-200-recommend.json │ │ ├── status-401-error.json │ │ ├── test.py │ │ └── tests.yml │ ├── mockHealthCheck.groovy │ ├── mockPipeline.groovy │ ├── mockPublish.groovy │ ├── mockRebuild.groovy │ ├── mockScan.groovy │ └── postUrlJob.groovy ├── one │ └── Dockerfile └── two │ └── Dockerfile.rhel7 └── vars ├── README.md ├── containerZoneHealthCheck.groovy ├── containerZonePublish.groovy ├── containerZoneScan.groovy ├── createDockerCfgJenkinsCredential.groovy ├── dockerBuildPush.groovy ├── getImageStreamRepo.groovy ├── newBuildOpenShift.groovy └── runOpenShift.groovy /.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | *.iml 3 | out 4 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM centos 2 | 3 | # This is just a temp example... 4 | RUN yum install -y vim wget 5 | 6 | CMD exit 0 7 | -------------------------------------------------------------------------------- /Dockerfile.rhel7: -------------------------------------------------------------------------------- 1 | FROM registry.access.redhat.com/rhel7 2 | 3 | # This is just a temp example... 4 | RUN yum install -y vim wget 5 | 6 | CMD tail -f /dev/null 7 | -------------------------------------------------------------------------------- /Jenkinsfile: -------------------------------------------------------------------------------- 1 | #!groovy 2 | @Library('Utils') 3 | import com.redhat.* 4 | 5 | properties([disableConcurrentBuilds()]) 6 | 7 | node { 8 | def source = "" 9 | def dockerfiles = null 10 | def gitHubUtils = new com.redhat.GitHubUtils() 11 | String scmRef = scm.branches[0] 12 | String scmUrl = scm.browser.url 13 | 14 | /* Checkout source and find all the Dockerfiles. 15 | * This will not include Dockerfiles with extensions. Currently the issue 16 | * with using a Dockerfile with an extension is the oc new-build command 17 | * does not offer an option to provide the dockerfilePath. 18 | */ 19 | stage('checkout') { 20 | checkout scm 21 | dockerfiles = findFiles(glob: '**/Dockerfile') 22 | } 23 | 24 | 25 | /* if CHANGE_URL is defined then this is a pull request 26 | * additional steps are required to determine the git url 27 | * and branch name to pass to new-build. 28 | * Otherwise just use the scm.browser.url and scm.branches[0] 29 | * for new-build. 30 | */ 31 | if (env.CHANGE_URL) { 32 | def pull = null 33 | stage('Github Url and Ref') { 34 | // Query the github repo api to return the clone_url and the ref (branch name) 35 | withCredentials([[$class: 'UsernamePasswordMultiBinding', credentialsId: "github", usernameVariable: 'USERNAME', passwordVariable: 'PASSWORD']]) { 36 | pull = gitHubUtils.getGitHubPR(env.USERNAME, env.PASSWORD, env.CHANGE_URL) 37 | scmUrl = pull.url 38 | scmRef = pull.ref 39 | deleteBuild = true 40 | } 41 | } 42 | } 43 | for (int i = 0; i < dockerfiles.size(); i++) { 44 | 45 | def resources = null 46 | try { 47 | /* Execute oc new-build on each dockerfile available 48 | * in the repo. The context-dir is the path removing the 49 | * name (i.e. Dockerfile) 50 | */ 51 | def is = "" 52 | def dockerImageRepository = "" 53 | String path = dockerfiles[i].path.replace(dockerfiles[i].name, "") 54 | newBuild = newBuildOpenShift() { 55 | url = scmUrl 56 | branch = scmRef 57 | contextDir = path 58 | deleteBuild = false 59 | randomName = true 60 | } 61 | dockerImageRepository = getImageStreamRepo(newBuild.buildConfigName).dockerImageRepository 62 | 63 | runOpenShift { 64 | deletePod = true 65 | branch = scmRef 66 | image = dockerImageRepository 67 | env = ["foo=goo"] 68 | } 69 | 70 | resources = newBuild.names 71 | currentBuild.result = 'SUCCESS' 72 | } 73 | catch(all) { 74 | currentBuild.result = 'FAILURE' 75 | echo "Exception: ${all}" 76 | } 77 | finally { 78 | stage('Clean Up Resources') { 79 | openshift.withCluster() { 80 | openshift.withProject() { 81 | for (r in resources) { 82 | openshift.selector(r).delete() 83 | } 84 | } 85 | } 86 | } 87 | } 88 | } 89 | } 90 | 91 | // vim: ft=groovy 92 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ### OpenShift Pipeline Library 2 | 3 | Current Goal: Build and test container images on OpenShift with Jenkins and supporting 4 | multibranches (pull requests). 5 | 6 | 1. Uses ephermeral Jenkins and the configuration is stored in OpenShift project 7 | 2. S2I to include jobs and additional plugins 8 | 9 | #### Quickstart 10 | 11 | 1. Add Jenkinsfile to your GitHub project (use the example in this project). 12 | 2. Create a new project `oc new-project ` 13 | 3. Add the template to OpenShift `oc create -f https://raw.githubusercontent.com/RHsyseng/openshift-pipeline-library/master/jenkins/openshift/template.yaml` 14 | 4. Process template 15 | ``` 16 | oc new-app --template docker-image-testing \ 17 | -p JENKINS_ORG_FOLDER_NAME=RHsyseng \ 18 | -p JENKINS_GITHUB_OWNER=RHsyseng-user \ 19 | -p JENKINS_GITHUB_REPO=openshift-client-library \ 20 | -p JENKINS_GITHUB_CRED_ID=github \ 21 | -p GITHUB_USERNAME=RHsyseng-user \ 22 | -p GITHUB_TOKEN=token 23 | ``` 24 | 5. Add Jenkins to the project `oc new-app --template jenkins-ephemeral -p NAMESPACE=$(oc project -q) -p MEMORY_LIMIT=2Gi` 25 | 6. Add the pipeline `oc create -f https://raw.githubusercontent.com/RHsyseng/openshift-pipeline-library/master/jenkins/openshift/pipeline.yaml` 26 | 7. And finally start the pipeline `oc start-build createcred-pipeline` 27 | 28 | -------------------------------------------------------------------------------- /apb/.gitignore: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RHsyseng/openshift-pipeline-library/6034ae602b4d6cc56bb66bcd0bdbc8f6b218932f/apb/.gitignore -------------------------------------------------------------------------------- /docs/index.adoc: -------------------------------------------------------------------------------- 1 | = OpenShift Pipeline Library 2 | :toc: 3 | 4 | 5 | == Focus / Goals 6 | 7 | * Show the benefits of using Jenkins and OpenShift together 8 | * The Jenkins master should be ephemeral: 9 | ** Configuration stored in OpenShift 10 | ** Pipeline, Jobs and libraries in source control 11 | * GitHub Organizational level and project pull request testing 12 | 13 | [[GettingStarted]] 14 | == Getting Started 15 | 16 | === Installation 17 | 18 | 19 | .Create New Project 20 | [source,bash] 21 | .... 22 | oc new-project 23 | .... 24 | 25 | .Add Template to Project 26 | [source,bash] 27 | .... 28 | oc create -f https://raw.githubusercontent.com/RHsyseng/openshift-pipeline-library/master/jenkins/openshift/template.yaml 29 | .... 30 | 31 | At this point you will need to create a https://github.com/settings/tokens/new?scopes=repo,read:user,user:email[GitHub token] to include when processing the template. 32 | 33 | .Process Template 34 | [source,bash] 35 | .... 36 | oc new-app --template docker-image-testing \ 37 | -p JENKINS_ORG_FOLDER_NAME=RHsyseng \ 38 | -p JENKINS_GITHUB_OWNER=RHsyseng-user \ 39 | -p JENKINS_GITHUB_REPO=openshift-client-library \ 40 | -p JENKINS_GITHUB_CRED_ID=github \ 41 | -p GITHUB_USERNAME=RHsyseng-user \ 42 | -p GITHUB_TOKEN=token 43 | .... 44 | 45 | .Add Jenkins to the project 46 | [source,bash] 47 | .... 48 | oc new-app --template jenkins-ephemeral -p NAMESPACE=$(oc project -q) -p MEMORY_LIMIT=2Gi 49 | .... 50 | 51 | 52 | .Add the pipeline 53 | [source,bash] 54 | .... 55 | oc create -f https://raw.githubusercontent.com/RHsyseng/openshift-pipeline-library/master/jenkins/openshift/pipeline.yaml 56 | .... 57 | 58 | .Start the build (pipeline) 59 | [source,bash] 60 | .... 61 | oc start-build createcred-pipeline 62 | .... 63 | 64 | 65 | === How does this work? 66 | 67 | ==== Source to Image Jenkins 68 | 69 | The Jenkins image is S2I enabled which is a huge benefit to this project because it requires additional items 70 | to be successful. This includes: 71 | 72 | * Additional plugins: 73 | ** OpenShift Jenkins client (master) 74 | * Jobs: 75 | ** Seed 76 | ** Organizational Folder 77 | * Configuration: 78 | ** Global Libraries 79 | ** Script approval 80 | 81 | 82 | ==== OpenShift 83 | 84 | ===== ConfigMap 85 | The `orgfolder` ConfigMap stores the configuration parameters for the seed job. 86 | 87 | ===== Secret 88 | The `github` secret stores the github username and the token. 89 | 90 | ==== Jobs / Pipeline 91 | 92 | ===== OpenShift Pipeline 93 | ====== Files 94 | * `jenkins/jobs/credPipelineJob.groovy` 95 | * `jenkins/openshift/pipeline.yaml` 96 | 97 | ====== Steps 98 | 99 | 1. Configures the Jenkins URL (Manage Jenkins - Configure System - Jenkins Location). This is required to provide the URL when adding the build status to GitHub. 100 | 2. Create a `List` from a OpenShift ConfigMap to be used for seed job. 101 | 3. Create Jenkins credentials from OpenShift secret. This adds the GitHub username and token to Jenkins for use in GitHub related plugins. 102 | 4. Configure the anonymous user for embedded build status icon. 103 | 5. Finally run the seed job with the parameters is ran to build the Organizational Folder job. 104 | 105 | ===== Seed Job 106 | ====== Files 107 | 108 | * `jenkins/configuration/jobs/seed` 109 | 110 | ====== Details 111 | This is the https://github.com/jenkinsci/job-dsl-plugin/wiki#getting-started[seed job] that will be executed to create the `orgFolderJob` using the Jenkins Job DSL. 112 | 113 | ===== Organizational Folder Job DSL 114 | ====== Files 115 | 116 | * `jenkins/jobs/orgFolderJob.groovy` 117 | 118 | ====== Details 119 | This Job DSL will create a Organizational Folder job configured for: 120 | 121 | * GitHub 122 | * Specific repository owner (or organization) 123 | * Repository pattern 124 | * Checkout credentials 125 | 126 | ===== Jenkinsfile 127 | ====== Files 128 | * `Jenkinsfile` 129 | 130 | ====== Details 131 | This is used for the openshift-pipeline-library project but it can also be used as an example for your own project. 132 | 133 | == Library Methods 134 | 135 | [[FunctionVariables]] 136 | === Function Variables 137 | 138 | ==== newBuildOpenShift 139 | 140 | Starts a `oc new-build` process and monitors while the related objects 141 | are executing. 142 | 143 | .Returned HashMap 144 | [source,java] 145 | ---- 146 | [names: newBuild.names(), buildConfigName: buildConfigName] 147 | ---- 148 | 149 | .Example 150 | [source,java] 151 | .... 152 | def newBuild = newBuildOpenShift() { 153 | url = scmUrl <1> 154 | branch = scmRef <2> 155 | contextDir = path <3> 156 | deleteBuild = false <4> 157 | randomName = true <5> 158 | image = "docker.io/aweiteka/playbook2image" <6> 159 | imageStream = "project/isname" <7> 160 | } 161 | .... 162 | <1> Create a build config from a remote repository. 163 | <2> Create a build config from a remote repository using a specific branch. 164 | <3> The directory in the repository to be used for the build 165 | <4> Delete build config and related objects after complete. 166 | <5> Generate a random name (UUID) for the build config objects. 167 | <6> External registry location for a container image. 168 | <7> Name of an image stream to be used as a builder. 169 | 170 | [NOTE] 171 | ==== 172 | Do not use `image` and `imageStream` options together. 173 | ==== 174 | 175 | 176 | ==== runOpenShift 177 | Starts a `oc run` process and monitors the pod object while executing. Current expectations for this function is that it will do some process and exit. The exit code will determine success or failure. 178 | 179 | .Example 180 | [source,java] 181 | .... 182 | runOpenShift { 183 | deletePod = true <1> 184 | branch = scmRef <2> 185 | image = dockerImageRepository <3> 186 | env = ["foo=goo"] <4> 187 | } 188 | .... 189 | <1> Delete pod after execution 190 | <2> Name of the pod 191 | <3> Docker image URL 192 | <4> Environmental variables that the pod requires to run. 193 | 194 | ==== getImageStreamRepo 195 | Both the newBuildOpenShift and runOpenShift require either the ImageStream or Docker image repoistory url. This function returns both as a HashMap for a given ImageStream name. 196 | 197 | .Returned HashMap 198 | [source,java] 199 | ---- 200 | [dockerImageRepository: is.status.dockerImageRepository, 201 | imageStream: imageStream] 202 | ---- 203 | 204 | .Example 205 | [source,java] 206 | .... 207 | isRepo = getImageStreamRepo(newBuild.buildConfigName) 208 | .... 209 | 210 | 211 | [[Utils]] 212 | === Utils 213 | 214 | ==== createJobParameters(HashMap configMap) 215 | Converts an OpenShift ConfigMap to a List of ParameterValues which can be used in a parameterized Jenkins Job. 216 | 217 | ==== getGitHubPR(String login, String oauthAccessToken, String changeUrl) 218 | Returns the originating repository and branch name of a pull request. 219 | 220 | ==== configureRootUrl(String url) 221 | Sets the Jenkins Location Configuration URL in an ephemeral Jenkins instance. 222 | 223 | ==== createCredentialsFromOpenShift(HashMap secret, String id) 224 | From an OpenShift secret creates Jenkins credentials to be used in a Jenkins Job or plugin. 225 | 226 | 227 | -------------------------------------------------------------------------------- /docs/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | OpenShift Pipeline Library 9 | 10 | 420 | 421 | 422 | 443 |
444 |
445 |

Focus / Goals

446 |
447 |
448 |
    449 |
  • 450 |

    Show the benefits of using Jenkins and OpenShift together

    451 |
  • 452 |
  • 453 |

    The Jenkins master should be ephemeral:

    454 |
    455 |
      456 |
    • 457 |

      Configuration stored in OpenShift

      458 |
    • 459 |
    • 460 |

      Pipeline, Jobs and libraries in source control

      461 |
    • 462 |
    463 |
    464 |
  • 465 |
  • 466 |

    GitHub Organizational level and project pull request testing

    467 |
  • 468 |
469 |
470 |
471 |
472 |
473 |

Getting Started

474 |
475 |
476 |

Installation

477 |
478 |
Create New Project
479 |
480 |
oc new-project <project-name>
481 |
482 |
483 |
484 |
Add Template to Project
485 |
486 |
oc create -f https://raw.githubusercontent.com/RHsyseng/openshift-pipeline-library/master/jenkins/openshift/template.yaml
487 |
488 |
489 |
490 |

At this point you will need to create a GitHub token to include when processing the template.

491 |
492 |
493 |
Process Template
494 |
495 |
oc new-app --template docker-image-testing \
496 |   -p JENKINS_ORG_FOLDER_NAME=RHsyseng \
497 |   -p JENKINS_GITHUB_OWNER=RHsyseng-user \
498 |   -p JENKINS_GITHUB_REPO=openshift-client-library \
499 |   -p JENKINS_GITHUB_CRED_ID=github \
500 |   -p GITHUB_USERNAME=RHsyseng-user \
501 |   -p GITHUB_TOKEN=token
502 |
503 |
504 |
505 |
Add Jenkins to the project
506 |
507 |
oc new-app --template jenkins-ephemeral -p NAMESPACE=$(oc project -q) -p MEMORY_LIMIT=2Gi
508 |
509 |
510 |
511 |
Add the pipeline
512 |
513 |
oc create -f https://raw.githubusercontent.com/RHsyseng/openshift-pipeline-library/master/jenkins/openshift/pipeline.yaml
514 |
515 |
516 |
517 |
Start the build (pipeline)
518 |
519 |
oc start-build createcred-pipeline
520 |
521 |
522 |
523 |
524 |

How does this work?

525 |
526 |

Source to Image Jenkins

527 |
528 |

The Jenkins image is S2I enabled which is a huge benefit to this project because it requires additional items 529 | to be successful. This includes:

530 |
531 |
532 |
    533 |
  • 534 |

    Additional plugins:

    535 |
    536 |
      537 |
    • 538 |

      OpenShift Jenkins client (master)

      539 |
    • 540 |
    541 |
    542 |
  • 543 |
  • 544 |

    Jobs:

    545 |
    546 |
      547 |
    • 548 |

      Seed

      549 |
    • 550 |
    • 551 |

      Organizational Folder

      552 |
    • 553 |
    554 |
    555 |
  • 556 |
  • 557 |

    Configuration:

    558 |
    559 |
      560 |
    • 561 |

      Global Libraries

      562 |
    • 563 |
    • 564 |

      Script approval

      565 |
    • 566 |
    567 |
    568 |
  • 569 |
570 |
571 |
572 |
573 |

OpenShift

574 |
575 |
ConfigMap
576 |
577 |

The orgfolder ConfigMap stores the configuration parameters for the seed job.

578 |
579 |
580 |
581 |
Secret
582 |
583 |

The github secret stores the github username and the token.

584 |
585 |
586 |
587 |
588 |

Jobs / Pipeline

589 |
590 |
OpenShift Pipeline
591 |
592 |
Files
593 |
594 |
    595 |
  • 596 |

    jenkins/jobs/credPipelineJob.groovy

    597 |
  • 598 |
  • 599 |

    jenkins/openshift/pipeline.yaml

    600 |
  • 601 |
602 |
603 |
604 |
605 |
Steps
606 |
607 |
    608 |
  1. 609 |

    Configures the Jenkins URL (Manage Jenkins - Configure System - Jenkins Location). This is required to provide the URL when adding the build status to GitHub.

    610 |
  2. 611 |
  3. 612 |

    Create a List<hudson.model.ParameterValue> from a OpenShift ConfigMap to be used for seed job.

    613 |
  4. 614 |
  5. 615 |

    Create Jenkins credentials from OpenShift secret. This adds the GitHub username and token to Jenkins for use in GitHub related plugins.

    616 |
  6. 617 |
  7. 618 |

    Configure the anonymous user for embedded build status icon.

    619 |
  8. 620 |
  9. 621 |

    Finally run the seed job with the parameters is ran to build the Organizational Folder job.

    622 |
  10. 623 |
624 |
625 |
626 |
627 |
628 |
Seed Job
629 |
630 |
Files
631 |
632 |
    633 |
  • 634 |

    jenkins/configuration/jobs/seed

    635 |
  • 636 |
637 |
638 |
639 |
640 |
Details
641 |
642 |

This is the seed job that will be executed to create the orgFolderJob using the Jenkins Job DSL.

643 |
644 |
645 |
646 |
647 |
Organizational Folder Job DSL
648 |
649 |
Files
650 |
651 |
    652 |
  • 653 |

    jenkins/jobs/orgFolderJob.groovy

    654 |
  • 655 |
656 |
657 |
658 |
659 |
Details
660 |
661 |

This Job DSL will create a Organizational Folder job configured for:

662 |
663 |
664 |
    665 |
  • 666 |

    GitHub

    667 |
  • 668 |
  • 669 |

    Specific repository owner (or organization)

    670 |
  • 671 |
  • 672 |

    Repository pattern

    673 |
  • 674 |
  • 675 |

    Checkout credentials

    676 |
  • 677 |
678 |
679 |
680 |
681 |
682 |
Jenkinsfile
683 |
684 |
Files
685 |
686 |
    687 |
  • 688 |

    Jenkinsfile

    689 |
  • 690 |
691 |
692 |
693 |
694 |
Details
695 |
696 |

This is used for the openshift-pipeline-library project but it can also be used as an example for your own project.

697 |
698 |
699 |
700 |
701 |
702 |
703 |
704 |
705 |

Library Methods

706 |
707 |
708 |

Function Variables

709 |
710 |

newBuildOpenShift

711 |
712 |

Starts a oc new-build process and monitors while the related objects 713 | are executing.

714 |
715 |
716 |
Returned HashMap
717 |
718 |
[names: newBuild.names(), buildConfigName: buildConfigName]
719 |
720 |
721 |
722 |
Example
723 |
724 |
def newBuild = newBuildOpenShift() {
725 |     url = scmUrl                                    (1)
726 |     branch = scmRef                                 (2)
727 |     contextDir = path                               (3)
728 |     deleteBuild = false                             (4)
729 |     randomName = true                               (5)
730 |     image = "docker.io/aweiteka/playbook2image"     (6)
731 |     imageStream = "project/isname"                  (7)
732 | }
733 |
734 |
735 |
736 |
    737 |
  1. 738 |

    Create a build config from a remote repository.

    739 |
  2. 740 |
  3. 741 |

    Create a build config from a remote repository using a specific branch.

    742 |
  4. 743 |
  5. 744 |

    The directory in the repository to be used for the build

    745 |
  6. 746 |
  7. 747 |

    Delete build config and related objects after complete.

    748 |
  8. 749 |
  9. 750 |

    Generate a random name (UUID) for the build config objects.

    751 |
  10. 752 |
  11. 753 |

    External registry location for a container image.

    754 |
  12. 755 |
  13. 756 |

    Name of an image stream to be used as a builder.

    757 |
  14. 758 |
759 |
760 |
761 | 762 | 763 | 766 | 771 | 772 |
764 |
Note
765 |
767 |
768 |

Do not use image and imageStream options together.

769 |
770 |
773 |
774 |
775 |
776 |

runOpenShift

777 |
778 |

Starts a oc run process and monitors the pod object while executing. Current expectations for this function is that it will do some process and exit. The exit code will determine success or failure.

779 |
780 |
781 |
Example
782 |
783 |
runOpenShift {
784 |     deletePod = true                (1)
785 |     branch = scmRef                 (2)
786 |     image = dockerImageRepository   (3)
787 |     env = ["foo=goo"]               (4)
788 | }
789 |
790 |
791 |
792 |
    793 |
  1. 794 |

    Delete pod after execution

    795 |
  2. 796 |
  3. 797 |

    Name of the pod

    798 |
  4. 799 |
  5. 800 |

    Docker image URL

    801 |
  6. 802 |
  7. 803 |

    Environmental variables that the pod requires to run.

    804 |
  8. 805 |
806 |
807 |
808 |
809 |

getImageStreamRepo

810 |
811 |

Both the newBuildOpenShift and runOpenShift require either the ImageStream or Docker image repoistory url. This function returns both as a HashMap for a given ImageStream name.

812 |
813 |
814 |
Returned HashMap
815 |
816 |
[dockerImageRepository: is.status.dockerImageRepository,
817 |                  imageStream: imageStream]
818 |
819 |
820 |
821 |
Example
822 |
823 |
isRepo = getImageStreamRepo(newBuild.buildConfigName)
824 |
825 |
826 |
827 |
828 |
829 |

Utils

830 |
831 |

createJobParameters(HashMap configMap)

832 |
833 |

Converts an OpenShift ConfigMap to a List of ParameterValues which can be used in a parameterized Jenkins Job.

834 |
835 |
836 |
837 |

getGitHubPR(String login, String oauthAccessToken, String changeUrl)

838 |
839 |

Returns the originating repository and branch name of a pull request.

840 |
841 |
842 |
843 |

configureRootUrl(String url)

844 |
845 |

Sets the Jenkins Location Configuration URL in an ephemeral Jenkins instance.

846 |
847 |
848 |
849 |

createCredentialsFromOpenShift(HashMap secret, String id)

850 |
851 |

From an OpenShift secret creates Jenkins credentials to be used in a Jenkins Job or plugin.

852 |
853 |
854 |
855 |
856 |
857 |
858 | 863 | 864 | -------------------------------------------------------------------------------- /examples/REAME.md: -------------------------------------------------------------------------------- 1 | Example pipelines and playbooks. 2 | -------------------------------------------------------------------------------- /examples/containerzone/docker/.gitignore: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RHsyseng/openshift-pipeline-library/6034ae602b4d6cc56bb66bcd0bdbc8f6b218932f/examples/containerzone/docker/.gitignore -------------------------------------------------------------------------------- /examples/containerzone/docker/jobs/buildPushScan.groovy: -------------------------------------------------------------------------------- 1 | #!groovy 2 | 3 | @Library('Utils') 4 | import com.redhat.* 5 | 6 | node { 7 | stage('checkout') { 8 | checkout scm 9 | } 10 | 11 | /** vars/dockerBuildPush.groovy 12 | * This DSL (groovy closure) will build the dockerfile located in the 13 | * contextDir. Once created an optional parameters testJobName and testJobParameters 14 | * can be used to test the resulting image. Finally the image will be pushed 15 | * to the partner registry. 16 | */ 17 | dockerBuildPush { 18 | credentialsId = "ContainerZone" 19 | contextDir = "examples/docker" 20 | imageName = "czone" 21 | imageTag = "latest" 22 | } 23 | 24 | /** vars/containerZoneScan.groovy 25 | * This DSL will use the connect API to determine the status of the scan 26 | * and display the scan results with the Jenkins console. 27 | */ 28 | containerZoneScan { 29 | credentialsId = "ContainerZone" 30 | openShiftUri = "insecure://api.rhc4tp.openshift.com" 31 | imageName = "czone" 32 | imageTag = "latest" 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /examples/containerzone/generic/jobs/CheckHealthGrade/checkHealthGradePipeline.groovy: -------------------------------------------------------------------------------- 1 | #!groovy 2 | 3 | @Library('Utils') 4 | import com.redhat.* 5 | 6 | 7 | node { 8 | 9 | /** 10 | * This DSL (groovy closure) checks the container status 11 | * rebuilding the image if required 12 | */ 13 | def jobParameters = new JenkinsUtils().createJobParameters([name: "foo"]) 14 | 15 | def jobName = JOBNAME 16 | 17 | containerZoneHealthCheck { 18 | credentialsId = "ContainerZone" 19 | rebuildJobName = jobName 20 | rebuildJobParameters = jobParameters 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /examples/containerzone/openshift/.gitignore: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RHsyseng/openshift-pipeline-library/6034ae602b4d6cc56bb66bcd0bdbc8f6b218932f/examples/containerzone/openshift/.gitignore -------------------------------------------------------------------------------- /examples/containerzone/openshift/buildconfig-pipeline.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: v1 3 | kind: BuildConfig 4 | metadata: 5 | name: "ContainerZonePipeline" 6 | spec: 7 | source: 8 | type: "Git" 9 | git: 10 | uri: "https://github.com/RHsyseng/openshift-pipeline-library" 11 | ref: "master" 12 | strategy: 13 | type: "JenkinsPipeline" 14 | jenkinsPipelineStrategy: 15 | jenkinsfilePath: examples/containerzone/openshift/jobs/buildPushScan.groovy 16 | -------------------------------------------------------------------------------- /examples/containerzone/openshift/jobs/buildPushScan.groovy: -------------------------------------------------------------------------------- 1 | #!groovy 2 | 3 | @Library('Utils') 4 | import com.redhat.* 5 | 6 | /* 7 | * TODO: Add build parameters 8 | * TODO: Maybe use newBuildOpenShift? 9 | */ 10 | 11 | node { 12 | 13 | /** vars/createDockerCfgJenkinsCredential.groovy 14 | * This DSl will create a Jenkins credential from 15 | * an OpenShift DockerCfg secret. 16 | */ 17 | createDockerCfgJenkinsCredential { 18 | secretName = "ContainerZone" 19 | } 20 | 21 | stage('Start OpenShift Build') { 22 | openshiftBuild(buildConfig: "${EXTERNAL_REGISTRY_IMAGE_NAME}-ex-reg", showBuildLogs: 'true') 23 | } 24 | 25 | /** vars/containerZoneScan.groovy 26 | * This DSL will use the connect API to determine the status of the scan 27 | * and display the scan results with the Jenkins console. 28 | */ 29 | containerZoneScan { 30 | credentialsId = "ContainerZone" 31 | openShiftUri = "insecure://api.rhc4tp.openshift.com" 32 | imageName = "czone" 33 | imageTag = "latest" 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /examples/containerzone/playbooks/installseedjob.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # ---------------------------- 3 | # Running 4 | # ---------------------------- 5 | # TODO: This should be modified to include secrets 6 | # The jenkins modules execute using the API so the play can just execute locally. 7 | # ansible-playbook -i localhost, -c local installseedjob.yml 8 | # ---------------------------- 9 | - name: Install Container Zone Seed Jobs 10 | hosts: localhost 11 | gather_facts: False 12 | vars: 13 | start_build: | 14 | import jenkins.model.* 15 | import hudson.model.* 16 | Jenkins.instance.getAllItems(FreeStyleProject).each {it -> 17 | def pattern = 'checkImageHealthSeed' 18 | def m = it.getName() =~ pattern 19 | if(m) { 20 | it.scheduleBuild() 21 | } 22 | } 23 | 24 | approve_script: | 25 | import org.jenkinsci.plugins.scriptsecurity.scripts.* 26 | ScriptApproval sa = ScriptApproval.get(); 27 | for (ScriptApproval.PendingScript pending : sa.getPendingScripts()) { 28 | sa.approveScript(pending.getHash()); 29 | } 30 | 31 | tasks: 32 | # ---------------------------- 33 | # Create Seed Job 34 | # ---------------------------- 35 | # This is an example of creating a seed job to build a pipeline job 36 | # that uses the Job DSL plugin. 37 | # ---------------------------- 38 | - name: Create Seed Job for checkHealthGradePipeline (Job DSL) 39 | jenkins_job: 40 | config: "{{ lookup('file', 'templates/checkImageHealthSeed.xml') }}" 41 | name: checkImageHealthSeed 42 | url: http://10.53.252.84:8080 43 | user: admin 44 | token: 66d28ba4dbd8731e1148273158eb4add 45 | 46 | # ---------------------------- 47 | # Jenkins Start Job 48 | # ---------------------------- 49 | # This will start the seed job that we just added 50 | # NOTE: this will most likely fail. 51 | # ---------------------------- 52 | - name: Jenkins Start Job 53 | jenkins_script: 54 | script: "{{ start_build }}" 55 | url: http://10.53.252.84:8080 56 | user: admin 57 | password: admin 58 | -------------------------------------------------------------------------------- /examples/containerzone/playbooks/templates/checkImageHealthSeed.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | false 6 | 7 | 8 | 9 | 10 | JOBNAME 11 | Name of rebuild job 12 | foo 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | @daily 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 2 30 | 31 | 32 | openshift-pipeline-library 33 | https://github.com/RHsyseng/openshift-pipeline-library 34 | 35 | 36 | 37 | 38 | master 39 | 40 | 41 | false 42 | Default 43 | 44 | 45 | examples/containerzone/generic/jobs/CheckHealthGrade/checkHealthGradePipeline.groovy 46 | false 47 | 48 | false 49 | -------------------------------------------------------------------------------- /jenkins/README.md: -------------------------------------------------------------------------------- 1 | This directory contains configuration files and scripts for Jenkins. 2 | -------------------------------------------------------------------------------- /jenkins/configuration/jobs/gitHubOrgSeed/config.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | true 6 | 7 | 8 | 9 | 10 | name 11 | 12 | 13 | 14 | 15 | displayName 16 | 17 | 18 | 19 | 20 | repoOwner 21 | 22 | 23 | 24 | 25 | credentialsId 26 | 27 | 28 | 29 | 30 | pattern 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 2 39 | 40 | 41 | https://github.com/RHsyseng/openshift-pipeline-library 42 | 43 | 44 | 45 | 46 | master 47 | 48 | 49 | false 50 | 51 | 52 | 53 | true 54 | false 55 | false 56 | false 57 | 58 | false 59 | 60 | 61 | jenkins/jobs/githubOrgJob.groovy 62 | false 63 | false 64 | false 65 | false 66 | false 67 | IGNORE 68 | IGNORE 69 | JENKINS_ROOT 70 | 71 | 72 | 73 | 74 | 75 | -------------------------------------------------------------------------------- /jenkins/configuration/org.jenkinsci.plugins.workflow.libs.GlobalLibraries.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Utils 6 | 7 | 8 | 44c97d51-cf48-4690-b645-72e0bc32773e 9 | SAME 10 | RHsyseng 11 | openshift-pipeline-library 12 | * 13 | 14 | true 15 | true 16 | false 17 | false 18 | true 19 | false 20 | 21 | 22 | master 23 | false 24 | true 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /jenkins/configuration/scriptApproval.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | method hudson.plugins.git.GitSCM getBranches 6 | method hudson.plugins.git.browser.GitRepositoryBrowser getUrl 7 | method hudson.scm.SCM getBrowser 8 | staticMethod org.codehaus.groovy.runtime.DefaultGroovyMethods dump java.lang.Object 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /jenkins/jobs/configureJenkinsOpenShift.groovy: -------------------------------------------------------------------------------- 1 | #!groovy 2 | 3 | @Library('Utils') 4 | import com.redhat.* 5 | 6 | node { 7 | def id = null 8 | def seedJobParameters = null 9 | def jenkinsUtils = new com.redhat.JenkinsUtils() 10 | 11 | /* The Jenkins root url is configured under 12 | * Manage Jenkins -> Configure System -> Jenkins Location 13 | * This is not configured properly in a ephemeral deployment. 14 | * Use the OpenShift route object to determine the correct url. 15 | */ 16 | stage('Configure URL') { 17 | openshift.withCluster() { 18 | def route = openshift.selector('route', 'jenkins').object() 19 | jenkinsUtils.configureRootUrl("https://${route.spec.host}") 20 | } 21 | } 22 | /* Create Jenkins build job parameters from a OpenShift ConfigMap */ 23 | stage('Extract ConfigMap') { 24 | openshift.withCluster() { 25 | def configMap = openshift.selector( "configmap/orgfolder" ).object().data 26 | seedJobParameters = jenkinsUtils.createJobParameters(configMap) 27 | } 28 | } 29 | 30 | /* Create a Jenkins credential from a OpenShift secret for GitHub user and token */ 31 | stage('OpenShift -> Jenkins credentials') { 32 | openshift.withCluster() { 33 | def secret = openshift.selector( "secret/github" ).object() 34 | id = jenkinsUtils.createCredentialsFromOpenShift(secret, "github") 35 | } 36 | } 37 | /* To use the embeddable build status plugin the anonymous user must 38 | have the ability to ViewStatus and Read. 39 | */ 40 | stage('Configure Anonymous User') { 41 | jenkinsUtils.setAnonPermBuildStatusIcon() 42 | } 43 | /* This builds the Seed job for the GitHub Organizational folder Job DSL 44 | * to create a job for the github org configured in seedJobParameters. 45 | */ 46 | stage('Run Seed Job') { 47 | build job: 'gitHubOrgSeed', parameters: seedJobParameters 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /jenkins/jobs/githubOrgJob.groovy: -------------------------------------------------------------------------------- 1 | #!groovy 2 | 3 | organizationFolder("${name}") { 4 | description('This contains branch source jobs for GitHub') 5 | displayName("${displayName}") 6 | orphanedItemStrategy { 7 | discardOldItems { 8 | daysToKeep(0) 9 | numToKeep(0) 10 | } 11 | } 12 | organizations { 13 | github { 14 | apiUri('https://api.github.com') 15 | repoOwner("${repoOwner}") 16 | scanCredentialsId("${credentialsId}") 17 | pattern("${pattern}") 18 | checkoutCredentialsId("${credentialsId}") 19 | buildOriginBranch(true) 20 | buildOriginBranchWithPR(true) 21 | buildOriginPRMerge(false) 22 | buildOriginPRHead(false) 23 | buildForkPRMerge(true) 24 | buildForkPRHead(false) 25 | } 26 | } 27 | triggers { 28 | periodic(10) 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /jenkins/openshift/pipeline.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: v1 3 | kind: BuildConfig 4 | metadata: 5 | name: "configurejenkinspipeline" 6 | spec: 7 | source: 8 | type: "Git" 9 | git: 10 | uri: "https://github.com/RHsyseng/openshift-pipeline-library" 11 | ref: "master" 12 | strategy: 13 | type: "JenkinsPipeline" 14 | jenkinsPipelineStrategy: 15 | jenkinsfilePath: jenkins/jobs/configureJenkinsOpenShift.groovy 16 | -------------------------------------------------------------------------------- /jenkins/openshift/template.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: v1 3 | kind: Template 4 | labels: 5 | template: docker-image-testing-template 6 | message: A Jenkins service has been created in your project. Log into Jenkins with 7 | your OpenShift account. The tutorial at URL contains more information about using this template. 8 | metadata: 9 | name: docker-image-testing 10 | annotations: 11 | description: |- 12 | Jenkins service, without persistent storage. 13 | 14 | WARNING: Any data stored will be lost upon pod destruction. Only use this template for testing. 15 | iconClass: icon-jenkins 16 | openshift.io/display-name: Docker image testing in Jenkins (Ephemeral) 17 | tags: instant-app,jenkins 18 | template.openshift.io/documentation-url: https://docs.openshift.org/latest/using_images/other_images/jenkins.html 19 | template.openshift.io/long-description: This template deploys a Jenkins server 20 | capable of managing OpenShift Pipeline builds and supporting OpenShift-based 21 | oauth login. 22 | template.openshift.io/provider-display-name: RHsyseng 23 | template.openshift.io/support-url: https://github.com/RHsyseng/openshift-pipeline-library 24 | objects: 25 | - kind: ConfigMap 26 | apiVersion: v1 27 | metadata: 28 | name: orgfolder 29 | data: 30 | name: ${JENKINS_ORG_FOLDER_NAME} 31 | displayName: ${JENKINS_ORG_FOLDER_NAME} 32 | repoOwner: ${JENKINS_GITHUB_OWNER} 33 | pattern: ${JENKINS_GITHUB_REPO} 34 | credentialsId: ${JENKINS_GITHUB_CRED_ID} 35 | - apiVersion: v1 36 | kind: BuildConfig 37 | metadata: 38 | name: jenkins 39 | spec: 40 | nodeSelector: null 41 | output: 42 | to: 43 | kind: ImageStreamTag 44 | name: jenkins:latest 45 | postCommit: {} 46 | resources: {} 47 | runPolicy: Serial 48 | source: 49 | contextDir: jenkins 50 | git: 51 | uri: https://github.com/RHsyseng/openshift-pipeline-library 52 | type: Git 53 | strategy: 54 | sourceStrategy: 55 | from: 56 | kind: ImageStreamTag 57 | name: jenkins:2 58 | namespace: openshift 59 | type: Source 60 | triggers: 61 | - type: ConfigChange 62 | - imageChange: {} 63 | type: ImageChange 64 | - apiVersion: v1 65 | kind: ImageStream 66 | metadata: 67 | name: jenkins 68 | - apiVersion: v1 69 | stringData: 70 | password: ${GITHUB_TOKEN} 71 | username: ${GITHUB_USERNAME} 72 | kind: Secret 73 | metadata: 74 | creationTimestamp: null 75 | name: github 76 | type: kubernetes.io/basic-auth 77 | parameters: 78 | - description: The simple organization folder name 79 | displayName: Organization folder name 80 | name: JENKINS_ORG_FOLDER_NAME 81 | required: true 82 | - description: Github Owner (https://github.com/owner/repo) 83 | displayName: Github Owner 84 | name: JENKINS_GITHUB_OWNER 85 | required: true 86 | - description: Github repository pattern (https://github.com/owner/repo) 87 | displayName: Github Repo Pattern 88 | name: JENKINS_GITHUB_REPO 89 | required: true 90 | - description: Github username and token secret name 91 | displayName: GitHub Credentials Name 92 | name: JENKINS_GITHUB_CRED_ID 93 | required: true 94 | value: github 95 | - description: Github Username 96 | displayName: GitHub Username 97 | name: GITHUB_USERNAME 98 | required: true 99 | - description: | 100 | Github Token 101 | https://github.com/settings/tokens/new?scopes=repo,read:user,user:email 102 | displayName: GitHub Token 103 | name: GITHUB_TOKEN 104 | required: true 105 | -------------------------------------------------------------------------------- /jenkins/plugins.txt: -------------------------------------------------------------------------------- 1 | # OpenShift Jenkins plugins 2 | openshift-pipeline:1.0.47 3 | openshift-login:0.12 4 | openshift-client:0.9.6 5 | 6 | # lockable resource - Used with openshift-client. Make sure a buildconfig is not running 7 | # concurrently - https://wiki.jenkins-ci.org/display/JENKINS/Lockable+Resources+Plugin 8 | lockable-resources:1.11.2 9 | 10 | # kubernetes plugin - https://wiki.jenkins-ci.org/display/JENKINS/Kubernetes+Plugin 11 | kubernetes:0.10 12 | 13 | # fabric8 openshift sync 14 | openshift-sync:0.1.20 15 | 16 | workflow-scm-step:2.4 17 | 18 | # https://wiki.jenkins-ci.org/display/JENKINS/Pipeline+Job+Plugin 19 | workflow-job:2.11.1 20 | # Pipeline plugin - https://wiki.jenkins-ci.org/display/JENKINS/Pipeline+Plugin 21 | # 2.5 now includes pipeline-model-definition (declaritive pipeline) 22 | # 2.4 brought in pipeline-milestone-step 23 | workflow-aggregator:2.5 24 | 25 | # remote loader https://wiki.jenkins-ci.org/display/JENKINS/Pipeline+Remote+Loader+Plugin 26 | workflow-remote-loader:1.2 27 | 28 | matrix-project:1.7.1 29 | multiple-scms:0.6 30 | ssh-credentials:1.13 31 | 32 | git:3.4.0 33 | github:1.27.0 34 | git-client:2.4.6 35 | github-api:1.86 36 | 37 | # Pipeline Utility Steps Plugin - https://wiki.jenkins-ci.org/display/JENKINS/Pipeline+Utility+Steps+Plugin 38 | pipeline-utility-steps:1.1.5 39 | 40 | # Jenkins v2 specific 41 | matrix-auth:1.5 42 | blueocean:1.1.4 43 | 44 | # Legacy stuff 45 | mapdb-api:1.0.1.0 46 | subversion:2.5.7 47 | 48 | 49 | job-dsl:1.63 50 | 51 | embeddable-build-status:1.9 52 | slack:2.2 53 | generic-webhook-trigger:1.10 54 | 55 | monitoring:1.67.0 56 | -------------------------------------------------------------------------------- /src/com/redhat/GitHubUtils.groovy: -------------------------------------------------------------------------------- 1 | #!groovy 2 | 3 | package com.redhat 4 | 5 | import org.kohsuke.github.* 6 | import java.util.logging.Level 7 | import java.util.logging.Logger 8 | 9 | 10 | @NonCPS 11 | HashMap getGitHubPR(String login, String oauthAccessToken, String changeUrl) { 12 | try { 13 | String[] changeUrlArray = changeUrl.split('[/]') 14 | String organization = changeUrlArray[3] 15 | String repository = changeUrlArray[4] 16 | int pullRequest = Integer.parseInt(changeUrlArray[6]) 17 | 18 | return getGitHubPR(login, oauthAccessToken, organization, repository, pullRequest) 19 | } 20 | catch(all){ 21 | Logger.getLogger("com.redhat.Utils").log(Level.SEVERE, all.toString()) 22 | throw all 23 | } 24 | } 25 | 26 | @NonCPS 27 | HashMap getGitHubPR(String login, String oauthAccessToken, String organization, String repository, int pullRequest) { 28 | HashMap map = [:] 29 | try { 30 | GitHub github = GitHub.connect(login, oauthAccessToken) 31 | 32 | GHCommitPointer pointer = github.getRepository("${organization}/${repository}") 33 | .getPullRequest(pullRequest).getHead() 34 | 35 | map['ref'] = pointer.ref 36 | map['url'] = pointer.repository.gitHttpTransportUrl().toString() 37 | return map 38 | } 39 | catch (all) { 40 | Logger.getLogger("com.redhat.Utils").log(Level.SEVERE, all.toString()) 41 | throw all 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/com/redhat/JenkinsUtils.groovy: -------------------------------------------------------------------------------- 1 | #!groovy 2 | package com.redhat 3 | 4 | import com.cloudbees.groovy.cps.NonCPS 5 | import com.cloudbees.plugins.credentials.impl.* 6 | import com.cloudbees.plugins.credentials.* 7 | import com.cloudbees.plugins.credentials.domains.* 8 | import groovy.json.JsonSlurperClassic 9 | import jenkins.model.* 10 | import jenkins.model.Jenkins 11 | import hudson.security.* 12 | import jenkins.model.JenkinsLocationConfiguration 13 | import hudson.model.* 14 | 15 | import java.util.logging.Level 16 | import java.util.logging.Logger 17 | 18 | 19 | @NonCPS 20 | List createJobParameters(HashMap configMap) { 21 | try { 22 | List parameters = new ArrayList() 23 | configMap.each{ k, v -> 24 | parameters.add(new StringParameterValue("${k}", "${v}")) 25 | } 26 | return parameters 27 | } 28 | catch(all) { 29 | Logger.getLogger("com.redhat.Utils").log(Level.SEVERE, all.toString()) 30 | throw all 31 | } 32 | } 33 | 34 | @NonCPS 35 | Boolean configureRootUrl(String url) { 36 | try { 37 | JenkinsLocationConfiguration jlc = JenkinsLocationConfiguration.get() 38 | jlc.setUrl(url) 39 | jlc.save() 40 | return true 41 | } 42 | catch (all) { 43 | Logger.getLogger("com.redhat.Utils").log(Level.SEVERE, all.toString()) 44 | return false 45 | } 46 | } 47 | 48 | @NonCPS 49 | String createCredentialsFromOpenShift(HashMap secret, String id) { 50 | try { 51 | String username = new String(secret.data.username.decodeBase64()) 52 | String password = new String(secret.data.password.decodeBase64()) 53 | return createCredentials(id, username, password, "secret from openshift") 54 | } 55 | catch(all) { 56 | Logger.getLogger("com.redhat.Utils").log(Level.SEVERE, all.toString()) 57 | throw all 58 | } 59 | } 60 | 61 | @NonCPS 62 | String createCredentialsFromOpenShiftDockerCfg(HashMap secret, String id) { 63 | try { 64 | JsonSlurperClassic parser = new JsonSlurperClassic() 65 | /** Base64 decode the dockercfg map. There should only be one so 66 | * parse the string, use an iterator to get the first entry value which is 67 | * a map. Use the username and password from that map to create a Jenkins 68 | * credential. 69 | */ 70 | String decoded = new String(secret['data']['.dockercfg'].decodeBase64()) 71 | HashMap extractedMap = ((HashMap) parser 72 | .parseText(decoded) 73 | .entrySet() 74 | .iterator() 75 | .next() 76 | .getValue()) 77 | 78 | parser = null 79 | decoded = null 80 | 81 | return createCredentials(id, (String) extractedMap.username, (String) extractedMap.password, 82 | "DockerCfg from OpenShift") 83 | 84 | } 85 | catch (all) { 86 | Logger.getLogger("com.redhat.Utils").log(Level.SEVERE, all.toString()) 87 | throw all 88 | } 89 | } 90 | 91 | @NonCPS 92 | String createCredentials(String id = null, String username, String password, String description) { 93 | try { 94 | if (id == null) { 95 | id = java.util.UUID.randomUUID().toString() 96 | } 97 | Credentials c = (Credentials) new UsernamePasswordCredentialsImpl(CredentialsScope.GLOBAL, id, description, username, password) 98 | SystemCredentialsProvider.getInstance().getStore().addCredentials(Domain.global(), c) 99 | return id 100 | } 101 | catch (all) { 102 | Logger.getLogger("com.redhat.Utils").log(Level.SEVERE, all.toString()) 103 | throw all 104 | } 105 | } 106 | 107 | @NonCPS 108 | Boolean setAnonPermBuildStatusIcon() { 109 | 110 | def permissions = ["hudson.model.Item.ViewStatus", "hudson.model.View.Read"] 111 | def sid = "anonymous" 112 | 113 | return setJenkinsPermissions(permissions, sid) 114 | } 115 | 116 | //https://wiki.jenkins-ci.org/display/JENKINS/Grant+Cancel+Permission+for+user+and+group+that+have+Build+permission 117 | 118 | @NonCPS 119 | Boolean setJenkinsPermissions(def perms, def sid) { 120 | try { 121 | perms.each { 122 | Jenkins.instance.authorizationStrategy.add(Permission.fromId(it), sid) 123 | } 124 | Jenkins.instance.save() 125 | return true 126 | } 127 | catch (all) { 128 | Logger.getLogger("com.redhat.Utils").log(Level.SEVERE, all.toString()) 129 | return false 130 | } 131 | } 132 | -------------------------------------------------------------------------------- /src/com/redhat/Utils.groovy: -------------------------------------------------------------------------------- 1 | #!groovy 2 | package com.redhat 3 | 4 | //@Grab('org.kohsuke:github-api:1.85') 5 | 6 | @Grab(group='org.apache.httpcomponents', module='httpclient', version='4.5.2') 7 | 8 | import com.cloudbees.groovy.cps.NonCPS 9 | import com.cloudbees.plugins.credentials.impl.* 10 | import com.cloudbees.plugins.credentials.* 11 | import com.cloudbees.plugins.credentials.domains.* 12 | 13 | 14 | import org.apache.http.protocol.* 15 | import org.apache.http.conn.* 16 | import org.apache.http.client.* 17 | import org.apache.http.client.methods.* 18 | import org.apache.http.entity.* 19 | import org.apache.http.impl.client.* 20 | import org.apache.http.client.config.* 21 | 22 | import jenkins.model.* 23 | import hudson.security.* 24 | import hudson.model.* 25 | import groovy.json.* 26 | 27 | import java.util.logging.Level 28 | import java.util.logging.Logger 29 | import javax.net.ssl.SSLException 30 | 31 | 32 | /** 33 | * This method POSTs to a HTTP uri with the requestJsonString as the payload. 34 | * The retry boolean is available for HTTP-based APIs that incorrectly implement 35 | * the POST method. This will force a retry if a POST is used in place of the GET 36 | * HTTP method. ** Using retry should be avoided. ** 37 | * 38 | * http://restcookbook.com/HTTP%20Methods/idempotency/ 39 | * https://hc.apache.org/httpcomponents-client-ga/tutorial/html/fundamentals.html 40 | * @param uri 41 | * @param requestJsonString 42 | * @param retry 43 | * @return 44 | */ 45 | //static final HashMap postUrl(String uri, String requestJsonString, boolean retry = false) { 46 | static final HashMap postUrl(String uri, String requestJsonString, boolean retry = false) { 47 | 48 | CloseableHttpClient client 49 | int timeout = 3; 50 | int socketTimeout = 30; 51 | RequestConfig config = RequestConfig.custom(). 52 | setConnectTimeout(timeout * 1000). 53 | setConnectionRequestTimeout(timeout * 1000). 54 | setSocketTimeout(socketTimeout * 1000).build(); 55 | 56 | if (retry) { 57 | HttpRequestRetryHandler postRequestHandler = new HttpRequestRetryHandler() { 58 | 59 | public boolean retryRequest(IOException exception, 60 | int executionCount, 61 | HttpContext context) { 62 | if (executionCount >= 5) { 63 | return false 64 | } 65 | if (exception instanceof InterruptedException) { 66 | // timeout 67 | return false 68 | } 69 | if (exception instanceof UnknownHostException) { 70 | // Unknown host 71 | return false 72 | } 73 | if (exception instanceof ConnectTimeoutException) { 74 | // Connection refused 75 | return false 76 | } 77 | if (exception instanceof SSLException) { 78 | // SSL handshake exception 79 | return false 80 | } 81 | return true 82 | } 83 | } 84 | 85 | client = HttpClientBuilder.create() 86 | .setRetryHandler(postRequestHandler) 87 | .setDefaultRequestConfig(config).build() 88 | } 89 | else { 90 | client = HttpClientBuilder.create() 91 | .setDefaultRequestConfig(config).build(); 92 | } 93 | 94 | HttpPost httpPost = new HttpPost(uri) 95 | 96 | httpPost.addHeader("content-type", "application/json") 97 | HashMap resultMap = new HashMap() 98 | 99 | try { 100 | //Logger.getLogger("com.redhat.Utils").log(Level.INFO, "requestJsonString: ${requestJsonString}") 101 | httpPost.setEntity(new StringEntity(requestJsonString)) 102 | CloseableHttpResponse response = client.execute(httpPost) 103 | 104 | 105 | BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(response.getEntity().getContent())) 106 | String jsonResponse = bufferedReader.getText() 107 | // No longer need the reader or the response 108 | bufferedReader.close() 109 | response.close() 110 | /* The JsonSluperClassic must be used vs JsonSlurper 111 | * since it returns a LazyMap which is not serializable. 112 | */ 113 | //Logger.getLogger("com.redhat.Utils").log(Level.INFO, "jsonResponse: ${jsonResponse}") 114 | Logger.getLogger("com.redhat.Utils").log(Level.INFO, ">>>>>>>> BEFORE PARSER <<<<<<<<<") 115 | JsonSlurperClassic parser = new JsonSlurperClassic() 116 | 117 | Object result = parser.parseText(jsonResponse) 118 | parser = null 119 | jsonResponse = null 120 | 121 | /** TODO: Maybe add an argument to specify the key 122 | * TODO: that will represent the ArrayList value 123 | */ 124 | if( result instanceof ArrayList) { 125 | return (HashMap) [errors: result] 126 | } 127 | 128 | return (HashMap) result 129 | } 130 | catch (IOException ioe) { 131 | Logger.getLogger("com.redhat.Utils").log(Level.SEVERE, ioe.toString()) 132 | throw ioe 133 | } 134 | catch (ClientProtocolException cpe) { 135 | Logger.getLogger("com.redhat.Utils").log(Level.SEVERE, cpe.toString()) 136 | throw cpe 137 | } 138 | catch (Exception e) { 139 | Logger.getLogger("com.redhat.Utils").log(Level.SEVERE, e.toString()) 140 | throw e 141 | } 142 | finally { 143 | client.close() 144 | } 145 | } 146 | -------------------------------------------------------------------------------- /tests/README.md: -------------------------------------------------------------------------------- 1 | ### Pipeline Library Testing 2 | 3 | #### tests/jobs 4 | 5 | ##### Mock Pipelines 6 | 7 | - mockHealthCheck.groovy - Tests the vars/containerZoneHealthCheck.groovy function 8 | - mockPipeline.groovy - Tests all the vars/containerZone.groovy functions 9 | - mockPublish.groovy - Tests the vars/containerZonePublish.groovy function 10 | - mockRebuild.groovy - Job to be called by mockHealthCheck 11 | - mockScan.groovy - Tests the vars/containerZoneScan.groovy function 12 | 13 | #### tests/jobs/__files/ 14 | 15 | `test.py` - This python script uses `tests.yml` to iterate through the mock pipelines to test each var function. 16 | ##### requirements 17 | 18 | - Jenkins 19 | - pipeline job named `mock` to copy and replace the `scriptPath` 20 | - Username/password credential with the id of `ContainerZone` 21 | - The `username` will be the Container Zone project id (pid) 22 | - The `password` will be the Container Zone project `secret` 23 | - Required plugins see [plugins.txt](../jenkins/plugins.txt) 24 | - This global pipeline library configured with the name `Utils` 25 | - OpenShift Project 26 | - [openshift-wiremock project](https://github.com/jcpowermac/openshift-wiremock) 27 | - OpenShift generic secret named `containerzone` with one parameter named `secret` 28 | - Local 29 | - `pip install python-jenkins wiremock PyYAML` 30 | -------------------------------------------------------------------------------- /tests/jobs/__files/publish-200-error.json: -------------------------------------------------------------------------------- 1 | ["Image not found"] 2 | -------------------------------------------------------------------------------- /tests/jobs/__files/publish-200-publish.json: -------------------------------------------------------------------------------- 1 | { 2 | "publish": { 3 | "success": true, 4 | "timestamp": 1496174096, 5 | "published_url": "https://nowhere" 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /tests/jobs/__files/publish-200-unable.json: -------------------------------------------------------------------------------- 1 | {"errors":["Unable to publish container."]} 2 | -------------------------------------------------------------------------------- /tests/jobs/__files/publish-401-error.json: -------------------------------------------------------------------------------- 1 | ["Missing required argument pid"] 2 | -------------------------------------------------------------------------------- /tests/jobs/__files/scanResults-200-error.json: -------------------------------------------------------------------------------- 1 | {"errors":["No scan results found for project."]} 2 | -------------------------------------------------------------------------------- /tests/jobs/__files/scanResults-401-error.json: -------------------------------------------------------------------------------- 1 | ["Missing required argument pid"] 2 | -------------------------------------------------------------------------------- /tests/jobs/__files/status-200-error.json: -------------------------------------------------------------------------------- 1 | {"errors":["No status found for project."]} 2 | -------------------------------------------------------------------------------- /tests/jobs/__files/status-200-mand.json: -------------------------------------------------------------------------------- 1 | { 2 | "rebuild": "mandatory", 3 | "grade": "C", 4 | "start_date": "20161016T05:32:00.000-0400", 5 | "end_date": "20170318T05:32:00.000-0400", 6 | "creation_date": "20170215T08:19:43.625-0500" 7 | } 8 | -------------------------------------------------------------------------------- /tests/jobs/__files/status-200-none.json: -------------------------------------------------------------------------------- 1 | { 2 | "rebuild": "none" 3 | } 4 | -------------------------------------------------------------------------------- /tests/jobs/__files/status-200-recommend.json: -------------------------------------------------------------------------------- 1 | { 2 | "rebuild": "recommended", 3 | "grade": "B", 4 | "start_date": "20161016T05:32:00.000-0400", 5 | "end_date": "20170318T05:32:00.000-0400", 6 | "creation_date": "20170215T08:19:43.625-0500" 7 | } 8 | -------------------------------------------------------------------------------- /tests/jobs/__files/status-401-error.json: -------------------------------------------------------------------------------- 1 | ["Missing required argument pid"] 2 | -------------------------------------------------------------------------------- /tests/jobs/__files/test.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | 3 | import jenkins 4 | import xml.etree.ElementTree as ET 5 | import pprint 6 | import yaml 7 | from time import sleep 8 | from wiremock.constants import Config 9 | from wiremock.client import * 10 | 11 | # *** VERY IMPORTANT *** 12 | # DO NOT ADD A TRAILING SLASH / on __admin/ 13 | # MUST BE __admin 14 | 15 | Config.base_url = 'http://wiremock.router.default.svc.cluster.local/__admin' 16 | 17 | def find_id_endpoint(all_mappings, endpoint): 18 | 19 | for mapping in all_mappings.mappings: 20 | try: 21 | if endpoint in mapping.request.url_path: 22 | return mapping.id 23 | except: 24 | pass 25 | return None 26 | 27 | 28 | def create_job(job_config, server, t): 29 | xml = ET.fromstring(job_config) 30 | sp = xml.find('./definition/scriptPath') 31 | sp.text = 'tests/jobs/%s.groovy' % t['jobname'] 32 | 33 | server.create_job(t['jobname'], ET.tostring(xml)) 34 | 35 | 36 | def main(): 37 | 38 | pp = pprint.PrettyPrinter(indent=4) 39 | endpoint_id_mapping = {} 40 | server = jenkins.Jenkins('http://10.53.252.84:8080', username='admin', password='admin') 41 | 42 | job_config = server.get_job_config("mock") 43 | all_mappings = Mappings.retrieve_all_mappings() 44 | with open("tests.yml", 'r') as stream: 45 | api_responses = yaml.load(stream) 46 | 47 | for t in api_responses['tests']: 48 | id = endpoint_id_mapping.get(t['endpoint']) 49 | if not id: 50 | id = find_id_endpoint(all_mappings, t['endpoint']) 51 | create_job(job_config, server, t) 52 | endpoint_id_mapping[t['endpoint']] = id 53 | 54 | mapping = Mappings.retrieve_mapping(id) 55 | mapping.response.status = t['status'] 56 | mapping.response.body_file_name = t['filename'] 57 | Mappings.update_mapping(mapping) 58 | 59 | next_build_number = server.get_job_info(t['jobname'])['nextBuildNumber'] 60 | server.build_job(t['jobname']) 61 | 62 | while True: 63 | sleep(10) 64 | build_info = server.get_build_info(t['jobname'], next_build_number) 65 | if not build_info['building']: 66 | print "scenario: %s\nstatus: %d\nfilename: %s" % (t['name'], t['status'], t['filename']) 67 | pp.pprint(build_info) 68 | break 69 | 70 | if __name__ == '__main__': 71 | main() 72 | -------------------------------------------------------------------------------- /tests/jobs/__files/tests.yml: -------------------------------------------------------------------------------- 1 | --- 2 | tests: 3 | - name: publish with 200 error 4 | filename: publish-200-error.json 5 | endpoint: publish 6 | status: 200 7 | jobname: mockPublish 8 | - name: publish with 200 publish 9 | filename: publish-200-publish.json 10 | endpoint: publish 11 | status: 200 12 | jobname: mockPublish 13 | - name: publish with 200 error unable to publish 14 | filename: publish-200-unable.json 15 | endpoint: publish 16 | status: 200 17 | jobname: mockPublish 18 | - name: publish 401 error 19 | filename: publish-401-error.json 20 | endpoint: publish 21 | status: 401 22 | jobname: mockPublish 23 | - name: scanResults with 200 error 24 | filename: scanResults-200-error.json 25 | endpoint: scanResults 26 | status: 200 27 | jobname: mockScan 28 | - name: scanResults with 401 error 29 | filename: scanResults-401-error.json 30 | endpoint: scanResults 31 | status: 401 32 | jobname: mockScan 33 | - name: status with 200 error 34 | filename: status-200-error.json 35 | endpoint: status 36 | status: 200 37 | jobname: mockHealthCheck 38 | - name: status with rebuild mandatory 39 | filename: status-200-mand.json 40 | endpoint: status 41 | status: 200 42 | jobname: mockHealthCheck 43 | - name: status with rebuild none 44 | filename: status-200-none.json 45 | endpoint: status 46 | status: 200 47 | jobname: mockHealthCheck 48 | - name: status with rebuild recommended 49 | filename: status-200-recommend.json 50 | endpoint: status 51 | status: 200 52 | jobname: mockHealthCheck 53 | - name: status with 401 error 54 | filename: status-401-error.json 55 | endpoint: status 56 | status: 401 57 | jobname: mockHealthCheck 58 | -------------------------------------------------------------------------------- /tests/jobs/mockHealthCheck.groovy: -------------------------------------------------------------------------------- 1 | #!groovy 2 | @Library('Utils') 3 | import com.redhat.* 4 | 5 | /** This pipeline job uses wiremock to test the vars associated with 6 | * the Container Zone. 7 | * see https://github.com/jcpowermac/openshift-wiremock 8 | */ 9 | 10 | 11 | node { 12 | def jobParameters = new JenkinsUtils().createJobParameters([name: "foo"]) 13 | def jobName = "mockRebuild" 14 | 15 | containerZoneHealthCheck { 16 | uri = "http://wiremock.router.default.svc.cluster.local" 17 | credentialsId = "ContainerZone" 18 | rebuildJobName = jobName 19 | rebuildJobParameters = jobParameters 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /tests/jobs/mockPipeline.groovy: -------------------------------------------------------------------------------- 1 | #!groovy 2 | @Library('Utils') 3 | import com.redhat.* 4 | 5 | /** This pipeline job uses wiremock to test the vars associated with 6 | * the Container Zone. 7 | * see https://github.com/jcpowermac/openshift-wiremock 8 | */ 9 | 10 | 11 | node { 12 | 13 | def jobParameters = new JenkinsUtils().createJobParameters([name: "foo"]) 14 | def jobName = "mockRebuild" 15 | 16 | containerZoneScan { 17 | uri = "http://wiremock.router.default.svc.cluster.local" 18 | credentialsId = "ContainerZone" 19 | openShiftUri = "insecure://wiremock-ssl.router.default.svc.cluster.local" 20 | imageName = "starter-arbitrary-uid" 21 | imageTag = "1.0" 22 | } 23 | 24 | containerZoneHealthCheck { 25 | uri = "http://wiremock.router.default.svc.cluster.local" 26 | credentialsId = "ContainerZone" 27 | rebuildJobName = jobName 28 | rebuildJobParameters = jobParameters 29 | } 30 | 31 | containerZonePublish { 32 | uri = "http://wiremock.router.default.svc.cluster.local" 33 | credentialsId = "ContainerZone" 34 | openShiftUri = "insecure://wiremock-ssl.router.default.svc.cluster.local" 35 | imageName = "starter-arbitrary-uid" 36 | imageTag = "1.0" 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /tests/jobs/mockPublish.groovy: -------------------------------------------------------------------------------- 1 | #!groovy 2 | @Library('Utils') 3 | import com.redhat.* 4 | 5 | /** This pipeline job uses wiremock to test the vars associated with 6 | * the Container Zone. 7 | * see https://github.com/jcpowermac/openshift-wiremock 8 | */ 9 | 10 | node { 11 | containerZonePublish { 12 | uri = "http://wiremock.router.default.svc.cluster.local" 13 | credentialsId = "ContainerZone" 14 | openShiftUri = "insecure://wiremock-ssl.router.default.svc.cluster.local" 15 | imageName = "starter-arbitrary-uid" 16 | imageTag = "1.0" 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /tests/jobs/mockRebuild.groovy: -------------------------------------------------------------------------------- 1 | #!groovy 2 | 3 | node { 4 | println("In mockRebuild") 5 | } 6 | -------------------------------------------------------------------------------- /tests/jobs/mockScan.groovy: -------------------------------------------------------------------------------- 1 | #!groovy 2 | @Library('Utils') 3 | import com.redhat.* 4 | 5 | /** This pipeline job uses wiremock to test the vars associated with 6 | * the Container Zone. 7 | * see https://github.com/jcpowermac/openshift-wiremock 8 | */ 9 | 10 | 11 | node { 12 | containerZoneScan { 13 | uri = "http://wiremock.router.default.svc.cluster.local" 14 | credentialsId = "ContainerZone" 15 | openShiftUri = "insecure://wiremock-ssl.router.default.svc.cluster.local" 16 | imageName = "starter-arbitrary-uid" 17 | imageTag = "1.0" 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /tests/jobs/postUrlJob.groovy: -------------------------------------------------------------------------------- 1 | #!groovy 2 | 3 | @Library('Utils') 4 | import com.redhat.* 5 | 6 | 7 | node { 8 | 9 | def registrySecret = "${secret}" 10 | 11 | def jobParameters = new JenkinsUtils().createJobParameters([name: "foo"]) 12 | 13 | 14 | rebuildImage { 15 | pid = "p17633880910e488f5949aab3ad76cd4317542a7a06" 16 | secret = registrySecret 17 | rebuildJobName = "foo" 18 | rebuildJobParameters = jobParameters 19 | } 20 | } -------------------------------------------------------------------------------- /tests/one/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM centos 2 | 3 | # This is just a temp example... 4 | RUN yum install -y vim wget 5 | 6 | CMD exit 0 7 | -------------------------------------------------------------------------------- /tests/two/Dockerfile.rhel7: -------------------------------------------------------------------------------- 1 | FROM registry.access.redhat.com/rhel7 2 | 3 | # This is just a temp example... 4 | RUN yum install -y vim wget 5 | 6 | CMD tail -f /dev/null 7 | -------------------------------------------------------------------------------- /vars/README.md: -------------------------------------------------------------------------------- 1 | This directory contains a library of functions that may be used in pipeline scripts. See the `examples` directory. 2 | -------------------------------------------------------------------------------- /vars/containerZoneHealthCheck.groovy: -------------------------------------------------------------------------------- 1 | #!groovy 2 | 3 | import com.redhat.* 4 | 5 | def call(Closure body) { 6 | def config = [:] 7 | body.resolveStrategy = Closure.DELEGATE_FIRST 8 | body.delegate = config 9 | body() 10 | 11 | String uri = "https://connect.redhat.com" 12 | String path = "/api/container/status" 13 | String url = uri + path 14 | 15 | if (config.get('uri')) { 16 | url = config.uri + path 17 | } 18 | 19 | withCredentials([[$class : 'UsernamePasswordMultiBinding', credentialsId: "${config.credentialsId}", 20 | usernameVariable: 'USERNAME', passwordVariable: 'PASSWORD']]) { 21 | stage('Check Container Status') { 22 | def json = new groovy.json.JsonBuilder() 23 | def root = json secret: env.PASSWORD, pid: env.USERNAME 24 | def jsonString = json.toString() 25 | // No longer needed remove since JsonBuilder is not serializable 26 | root = null 27 | json = null 28 | 29 | def results = new Utils().postUrl(url, jsonString, true) 30 | 31 | println("DEBUG: Container Status Results: ${results}") 32 | 33 | if (results.containsKey("errors")) { 34 | println(results.errors) 35 | currentBuild.result = 'FAILURE' 36 | } else if (results.containsKey("rebuild")) { 37 | if (results.rebuild == "none") { 38 | println("Rebuild is not necessary at this time.") 39 | } else if (results.rebuild == "recommended") { 40 | println("The rebuild status is ${results.rebuild} and freshness grade: ${results.grade}") 41 | 42 | } else if (results.rebuild == "mandatory") { 43 | println("The rebuild status is ${results.rebuild} and freshness grade: ${results.grade}") 44 | if (config.get('rebuildJobName')) { 45 | build job: config['rebuildJobName'], 46 | parameters: config['rebuildJobParameters'] 47 | } 48 | } else { 49 | error "ERROR Unknown response from container status: ${results}" 50 | currentBuild.result = 'FAILURE' 51 | } 52 | } 53 | } 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /vars/containerZonePublish.groovy: -------------------------------------------------------------------------------- 1 | #!groovy 2 | 3 | import com.cloudbees.groovy.cps.NonCPS 4 | import com.redhat.* 5 | 6 | def call(Closure body) { 7 | def config = [:] 8 | body.resolveStrategy = Closure.DELEGATE_FIRST 9 | body.delegate = config 10 | body() 11 | 12 | def dockerImageDigest = null 13 | //def results = null 14 | 15 | String uri = "https://connect.redhat.com" 16 | String path = "/api/container/publish" 17 | String url = uri + path 18 | 19 | if( config.get('uri') ) { 20 | url = config.uri + path 21 | } 22 | 23 | withCredentials([[$class: 'UsernamePasswordMultiBinding', credentialsId: "${config.credentialsId}", 24 | usernameVariable: 'USERNAME', passwordVariable: 'PASSWORD']]) { 25 | 26 | /** Retrieve the docker image digest from the Red Hat Connect 27 | * OpenShift environment - which will be used with the scanning API. 28 | */ 29 | stage('Retrieve Docker Digest') { 30 | openshift.withCluster( config.openShiftUri, env.PASSWORD ) { 31 | openshift.withProject( env.USERNAME ) { 32 | def istagobj = openshift.selector( "istag/${config.imageName}:${config.imageTag}" ).object() 33 | dockerImageDigest = istagobj.image.metadata.name 34 | } 35 | } 36 | } 37 | /** Create the payload and wait for results to be returned from 38 | * the API. Once the certifications key is available break out of the loop 39 | * and continue. 40 | */ 41 | stage('Wait for scan') { 42 | def json = new groovy.json.JsonBuilder() 43 | def root = json secret: env.PASSWORD, pid: env.USERNAME, docker_image_digest: dockerImageDigest 44 | def jsonString = json.toString() 45 | json = null 46 | root = null 47 | HashMap results = new Utils().postUrl(url, jsonString, true) 48 | 49 | if (results.containsKey("publish")) { 50 | if (results.publish.containsKey("success")){ 51 | if (results.publish.success) { 52 | currentBuild.result = 'SUCCESS' 53 | } 54 | } 55 | } 56 | else if (results.containsKey("errors")) { 57 | if (results.errors instanceof ArrayList) { 58 | error "${results.errors}" 59 | } 60 | else { 61 | printErrorCriteria(results.errors.criteria) 62 | } 63 | currentBuild.result = 'FAILURE' 64 | } 65 | else { 66 | println("Unknown response") 67 | } 68 | } 69 | } 70 | } 71 | 72 | /** sortPrintScanResults 73 | * Extracts the required and optional items for certification. 74 | * Determines if there are any items that failed in the required scanning 75 | * @param results 76 | * @return boolean 77 | */ 78 | @NonCPS 79 | void printErrorCriteria(def results) { 80 | results.fail.each { 81 | println("FAILED - Please Review: ${it.label}\n${it.url}") 82 | } 83 | } 84 | 85 | -------------------------------------------------------------------------------- /vars/containerZoneScan.groovy: -------------------------------------------------------------------------------- 1 | #!groovy 2 | 3 | import com.cloudbees.groovy.cps.NonCPS 4 | import com.redhat.* 5 | 6 | def call(Closure body) { 7 | def config = [:] 8 | body.resolveStrategy = Closure.DELEGATE_FIRST 9 | body.delegate = config 10 | body() 11 | 12 | def dockerImageDigest = null 13 | def results = null 14 | 15 | def uri = "https://connect.redhat.com" 16 | def path = "/api/container/scanResults" 17 | def url = uri + path 18 | 19 | if( config.get('uri') ) { 20 | url = config.uri + path 21 | } 22 | 23 | 24 | println(url) 25 | withCredentials([[$class: 'UsernamePasswordMultiBinding', credentialsId: "${config.credentialsId}", 26 | usernameVariable: 'USERNAME', passwordVariable: 'PASSWORD']]) { 27 | 28 | /** Retrieve the docker image digest from the Red Hat Connect 29 | * OpenShift environment - which will be used with the scanning API. 30 | */ 31 | stage('Retrieve Docker Digest') { 32 | openshift.withCluster( config.openShiftUri, env.PASSWORD ) { 33 | openshift.withProject( env.USERNAME ) { 34 | def istagobj = openshift.selector( "istag/${config.imageName}:${config.imageTag}" ).object() 35 | dockerImageDigest = istagobj.image.metadata.name 36 | } 37 | } 38 | } 39 | /** Create the payload and wait for results to be returned from 40 | * the API. Once the certifications key is available break out of the loop 41 | * and continue. 42 | */ 43 | stage('Wait for scan') { 44 | def json = new groovy.json.JsonBuilder() 45 | def root = json secret: env.PASSWORD, pid: env.USERNAME, docker_image_digest: dockerImageDigest 46 | def jsonString = json.toString() 47 | json = null 48 | root = null 49 | 50 | timeout(30) { 51 | waitUntil { 52 | results = new Utils().postUrl(url, jsonString, true) 53 | 54 | if (results.containsKey("certifications")) { 55 | return true 56 | } else if( results.containsKey("errors")) { 57 | currentBuild.result = 'FAILURE' 58 | error "${results.errors}" 59 | } 60 | else return false 61 | } 62 | } 63 | 64 | currentBuild.result = 'SUCCESS' 65 | if( sortPrintScanResults(results["certifications"][0]["assessment"]) ) { 66 | currentBuild.result = 'FAILURE' 67 | } 68 | } 69 | } 70 | } 71 | 72 | /** sortPrintScanResults 73 | * Extracts the required and optional items for certification. 74 | * Determines if there are any items that failed in the required scanning 75 | * @param results 76 | * @return boolean 77 | */ 78 | @NonCPS 79 | def sortPrintScanResults(def results) { 80 | def requiredForCert = results.findAll{ it["required_for_certification"] } 81 | 82 | /** In the requiredForCert list findall items in the dictionary with the 83 | * key 'value' that is false. Calculate the size as a boolean value. 84 | */ 85 | def failed = requiredForCert.findAll( { !it.value } ).size().asBoolean() 86 | def optional = results.findAll{ !it["required_for_certification"] } 87 | 88 | printScanResults(requiredForCert) 89 | printScanResults(optional) 90 | 91 | return failed 92 | } 93 | 94 | @NonCPS 95 | def printScanResults(def results) { 96 | results.each { 97 | String name = it.name.replaceAll('_', ' ').minus(" exists").capitalize() 98 | println("${name}: ${it.value ? "PASSED" : "FAILED"}") 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /vars/createDockerCfgJenkinsCredential.groovy: -------------------------------------------------------------------------------- 1 | #!groovy 2 | 3 | import com.redhat.* 4 | 5 | /** createDockerCfgJenkinsCredential 6 | * Required: 7 | * secretName 8 | * 9 | * @param body 10 | * @return 11 | */ 12 | def call(Closure body) { 13 | def config = [:] 14 | body.resolveStrategy = Closure.DELEGATE_FIRST 15 | body.delegate = config 16 | body() 17 | 18 | stage('Retrieve DockerCfg OpenShift and Create in Jenkins') { 19 | openshift.withCluster() { 20 | def secret = openshift.selector( "secret/${config.secretName}" ).object() 21 | new JenkinsUtils().createCredentialsFromOpenShiftDockerCfg(secret, "${config.secretName}") 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /vars/dockerBuildPush.groovy: -------------------------------------------------------------------------------- 1 | #!groovy 2 | 3 | /** dockerBuildPush 4 | * Required: 5 | * credentialsId 6 | * contextDir 7 | * imageName 8 | * imagetag 9 | * 10 | * Optional: 11 | * testJobName 12 | * testJobParameters 13 | * 14 | * @param body 15 | * @return 16 | */ 17 | def call(Closure body) { 18 | def config = [:] 19 | body.resolveStrategy = Closure.DELEGATE_FIRST 20 | body.delegate = config 21 | body() 22 | 23 | def image = null 24 | 25 | withCredentials([[$class: 'UsernamePasswordMultiBinding', credentialsId: "${config.credentialsId}" , usernameVariable: 'USERNAME', passwordVariable: 'PASSWORD']]) { 26 | docker.withRegistry('https://registry.rhc4tp.openshift.com', "${config.credentialsId}" ) { 27 | stage('Build Docker Image') { 28 | dir("${config.contextDir}") { 29 | image = docker.build("${env.USERNAME}/${config.imageName}:${config.imageTag}") 30 | } 31 | } 32 | 33 | if( config.get('testJobName') ) { 34 | stage('Run ${config.testJobName}'){ 35 | build job: config.testJobName, 36 | parameters: config.testJobParameters 37 | } 38 | } 39 | 40 | stage('Push Image') { 41 | image.push() 42 | } 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /vars/getImageStreamRepo.groovy: -------------------------------------------------------------------------------- 1 | #!groovy 2 | 3 | def call(String imageStreamName) { 4 | stage('OpenShift Get ImageStream') { 5 | openshift.withCluster() { 6 | openshift.withProject() { 7 | try { 8 | def is = openshift.selector("is/${imageStreamName}").object() 9 | String imageStream = "${is.metadata.namespace}/${is.metadata.name}" 10 | HashMap isMap = [dockerImageRepository: is.status.dockerImageRepository, 11 | imageStream: imageStream] 12 | return isMap 13 | } 14 | catch(all) { 15 | currentBuild.result = 'FAILURE' 16 | } 17 | } 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /vars/newBuildOpenShift.groovy: -------------------------------------------------------------------------------- 1 | #!groovy 2 | import groovy.json.JsonSlurperClassic 3 | 4 | import java.util.UUID 5 | 6 | def call(Closure body) { 7 | def config = [:] 8 | body.resolveStrategy = Closure.DELEGATE_FIRST 9 | body.delegate = config 10 | body() 11 | 12 | def newBuild = null 13 | def buildConfig = null 14 | def builds = null 15 | def newBuildObjectNames = null 16 | String contextDir = config['contextDir'] ?: "" 17 | String image = "" 18 | String name = config['name'] ?: "" 19 | String imageStream = "" 20 | String baseImageStreamName = "" 21 | String strategy = "" 22 | 23 | if(config['image']) { 24 | image = "${config.image}~" 25 | } 26 | 27 | if(config['imageStream']) { 28 | imageStream = "--image-stream=${config.imageStream}" 29 | } 30 | 31 | if(config['randomName']) { 32 | name = UUID.randomUUID().toString() 33 | if(name[0].isNumber()) { 34 | name = name.replaceAll(name[0], 'a') 35 | } 36 | } 37 | 38 | def deleteBuild = config['deleteBuild'] ?: false 39 | 40 | stage("${name} - OpenShift Build") { 41 | openshift.withCluster() { 42 | openshift.withProject() { 43 | Boolean buildConfigExists = false 44 | 45 | try { 46 | buildConfig = openshift.selector('buildconfig', name) 47 | buildConfigExists = buildConfig.exists() 48 | } 49 | catch (err) { 50 | // This resolves an error that will occur if using 3.4.x oc binary 51 | buildConfigExists = false 52 | } 53 | 54 | 55 | try { 56 | def buildConfigName = null 57 | 58 | 59 | /* If the OpenShift oc new-build command is ran in succession it can cause a 60 | * race if the base imagestream does not already exist. In normal situations 61 | * this is not a problem but in Jenkins when multiple jobs could be occurring 62 | * simultaneously this will happen. Adding a lock in this section resolves 63 | * that issue. 64 | */ 65 | 66 | lock(resource: 'openshift.newBuild', inversePrecedence: true) { 67 | /* Use oc new-build to build the image using the clone_url and ref 68 | * TODO: Determine a method to new-build with a "Dockerfile" with a 69 | * TODO: different filename e.g. Dockerfile.rhel7. 70 | */ 71 | 72 | if(buildConfigExists) { 73 | /* With the 3.4 oc binary this will not function */ 74 | buildConfig.startBuild() 75 | } 76 | else { 77 | newBuildRaw = openshift.raw("-o json", 78 | "new-build", "${image}${config.url}#${config.branch}", 79 | "--name=${name}", 80 | "--context-dir=${contextDir}", 81 | "${imageStream}", "--dry-run") 82 | 83 | baseImageStreamName = findBaseImageStream((String)(name), (String)(newBuildRaw.out)) 84 | 85 | if (openshift.selector('is', baseImageStreamName).exists()) { 86 | println("openshift.project()") 87 | image = "${openshift.project()}/${baseImageStreamName}~" 88 | strategy = "--strategy=docker" 89 | } 90 | 91 | newBuild = openshift.newBuild("${image}${config.url}#${config.branch}", 92 | "--name=${name}", 93 | "--context-dir=${contextDir}", 94 | "${imageStream}", 95 | "${strategy}") 96 | echo "newBuild created: ${newBuild.count()} objects : ${newBuild.names()}" 97 | 98 | buildConfig = newBuild.narrow("bc") 99 | } 100 | buildConfigName = buildConfig.object().metadata.name 101 | 102 | builds = buildConfig.related("builds") 103 | timeout(5) { 104 | builds.watch { 105 | if (it.count() == 0) { 106 | return false 107 | } 108 | echo "Detected new builds created by buildconfig: ${it.names()}" 109 | return true 110 | } 111 | } 112 | } 113 | 114 | timeout(10) { 115 | builds.untilEach(1) { 116 | switch (it.object().status.phase) { 117 | case "Complete": 118 | return true 119 | case "Error": 120 | println("message: ${it.object().status.message}") 121 | currentBuild.result = 'FAILURE' 122 | return true 123 | case "Failed": 124 | println("message: ${it.object().status.message}") 125 | currentBuild.result = 'FAILURE' 126 | return true 127 | default: 128 | return false 129 | } 130 | } 131 | } 132 | 133 | 134 | if (newBuild) { 135 | newBuildObjectNames = newBuild.names() 136 | } else { 137 | newBuildObjectNames = ["bc/${buildConfigName}", 138 | "is/${buildConfigName}"] 139 | } 140 | 141 | return new HashMap([names: newBuildObjectNames, buildConfigName: buildConfigName]) 142 | } 143 | catch(all) { 144 | println(all) 145 | } 146 | finally { 147 | if (buildConfig) { 148 | def result = buildConfig.logs() 149 | 150 | if(deleteBuild) { 151 | newBuild.delete() 152 | } 153 | // After the build is complete clean up the builds 154 | builds.delete() 155 | } 156 | } 157 | } 158 | } 159 | } 160 | } 161 | 162 | @NonCPS 163 | String findBaseImageStream(String name, String newBuildRawOutput) { 164 | String baseImageStreamName = "" 165 | JsonSlurperClassic parser = new JsonSlurperClassic() 166 | 167 | HashMap newBuildMap = (HashMap) parser.parseText(newBuildRawOutput) 168 | parser = null 169 | 170 | for (item in newBuildMap["items"]) { 171 | if( item.kind == 'ImageStream') { 172 | if (item["metadata"]["name"] != name) { 173 | baseImageStreamName = item["metadata"]["name"] 174 | } 175 | } 176 | } 177 | return baseImageStreamName 178 | } 179 | -------------------------------------------------------------------------------- /vars/runOpenShift.groovy: -------------------------------------------------------------------------------- 1 | #!groovy 2 | 3 | def call(Closure body) { 4 | def config = [:] 5 | body.resolveStrategy = Closure.DELEGATE_FIRST 6 | body.delegate = config 7 | body() 8 | 9 | def pod = null 10 | def podObject = null 11 | boolean deletePod = config['deletePod'] ?: false 12 | def args = [] 13 | 14 | 15 | String podName = config['name'] ?: "" 16 | /* The name of the pod must be alphanumeric lowercase with a hyphen or period */ 17 | //def podName = config['branch'].replaceAll('_','-').toLowerCase() 18 | 19 | args << "${podName}" 20 | args << "--image=${config.image}" 21 | args << "--restart=Never" 22 | args << "--image-pull-policy=Always" 23 | 24 | if(config['env']) { 25 | /* BUG: config.env.each works but only 26 | * iterated through the first item in the list 27 | * DO NOT USE [].each 28 | */ 29 | for(env in config.env) { 30 | args << "--env=\"${env}\"" 31 | println(env) 32 | } 33 | } 34 | 35 | stage('OpenShift Run') { 36 | openshift.withCluster() { 37 | openshift.withProject() { 38 | try { 39 | 40 | openshift.run(args) 41 | pod = openshift.selector("pod/${podName}") 42 | 43 | timeout(10) { 44 | pod.watch { 45 | podObject = it.object() 46 | if (podObject.status.phase == 'Running' || podObject.status.phase == 'Succeeded' || podObject.status.phase == 'Failed') { 47 | return true 48 | } else { 49 | return false 50 | } 51 | } 52 | } 53 | } 54 | finally { 55 | if (pod) { 56 | podObject = pod.object() 57 | def result = pod.logs() 58 | //def exitCode = podObject.status.containerStatuses[0].state.terminated.exitCode 59 | echo "status: ${result.status}" 60 | echo "output: ${result.out}" 61 | 62 | if (deletePod) { 63 | pod.delete() 64 | } 65 | } 66 | } 67 | } 68 | } 69 | } 70 | } 71 | --------------------------------------------------------------------------------