From ca9e5fded23d004e6a5aa6c24d08d2abed0f8d35 Mon Sep 17 00:00:00 2001 From: Austin Dewey Date: Tue, 20 Oct 2020 08:05:17 -0500 Subject: [PATCH] adding basic-helm-spring-boot pipeline (#153) * adding basic-helm-spring-boot pipeline * updating jenkins-agent-helm BC triggers. Adding validation documentation * adding image trigger annotation to deployment --- README.md | 1 + .../.helm/jenkins-agent-helm/Chart.yaml | 4 ++ .../templates/buildconfig.yaml | 24 +++++++ .../templates/imagestream.yaml | 8 +++ .../.helm/jenkins-agent-helm/values.yaml | 8 +++ .../.helm/spring-boot-build/Chart.yaml | 4 ++ .../templates/buildconfig.yaml | 23 +++++++ .../templates/imagestream.yaml | 7 ++ .../.helm/spring-boot-build/values.yaml | 5 ++ .../.helm/spring-boot/Chart.yaml | 4 ++ .../spring-boot/templates/deployment.yaml | 40 +++++++++++ .../.helm/spring-boot/templates/route.yaml | 11 +++ .../.helm/spring-boot/templates/service.yaml | 12 ++++ .../.helm/spring-boot/values.yaml | 10 +++ basic-helm-spring-boot/Jenkinsfile | 69 +++++++++++++++++++ basic-helm-spring-boot/README.md | 65 +++++++++++++++++ 16 files changed, 295 insertions(+) create mode 100644 basic-helm-spring-boot/.helm/jenkins-agent-helm/Chart.yaml create mode 100644 basic-helm-spring-boot/.helm/jenkins-agent-helm/templates/buildconfig.yaml create mode 100644 basic-helm-spring-boot/.helm/jenkins-agent-helm/templates/imagestream.yaml create mode 100644 basic-helm-spring-boot/.helm/jenkins-agent-helm/values.yaml create mode 100644 basic-helm-spring-boot/.helm/spring-boot-build/Chart.yaml create mode 100644 basic-helm-spring-boot/.helm/spring-boot-build/templates/buildconfig.yaml create mode 100644 basic-helm-spring-boot/.helm/spring-boot-build/templates/imagestream.yaml create mode 100644 basic-helm-spring-boot/.helm/spring-boot-build/values.yaml create mode 100644 basic-helm-spring-boot/.helm/spring-boot/Chart.yaml create mode 100644 basic-helm-spring-boot/.helm/spring-boot/templates/deployment.yaml create mode 100644 basic-helm-spring-boot/.helm/spring-boot/templates/route.yaml create mode 100644 basic-helm-spring-boot/.helm/spring-boot/templates/service.yaml create mode 100644 basic-helm-spring-boot/.helm/spring-boot/values.yaml create mode 100644 basic-helm-spring-boot/Jenkinsfile create mode 100644 basic-helm-spring-boot/README.md diff --git a/README.md b/README.md index 5b5b907a..dd10d4ea 100644 --- a/README.md +++ b/README.md @@ -10,6 +10,7 @@ The following is a list of the pipeline samples available in this repository: - [Basic Tomcat](./basic-tomcat) - Builds a Java Application like Ticket Monster and deploys it to Tomcat - [Basic Spring Boot](./basic-spring-boot) - Builds a Spring Boot application and deploys using an Embedded Servlet jar file +- [Basic Helm Spring Boot](./basic-helm-spring-boot) - Builds and deploys a Spring Boot application using Helm charts - [Blue Green Spring Boot](./blue-green-spring) - Build a Spring Boot application and deploys it using a blue-green deployment - [Secure Spring Boot](./secure-spring-boot) - Build a Spring Boot app and deploy with a pipeline that includes code coverage reports, dependency scanning, sonarqube analysis - [Cross Cluster Promotion Pipeline](./multi-cluster-spring-boot) - A [declarative syntax](https://jenkins.io/doc/book/pipeline/syntax/#declarative-pipeline) pipeline that demonstrates promoting a microservice between clusters (i.e. a Non-Production to a Production cluster) diff --git a/basic-helm-spring-boot/.helm/jenkins-agent-helm/Chart.yaml b/basic-helm-spring-boot/.helm/jenkins-agent-helm/Chart.yaml new file mode 100644 index 00000000..7135d73f --- /dev/null +++ b/basic-helm-spring-boot/.helm/jenkins-agent-helm/Chart.yaml @@ -0,0 +1,4 @@ +apiVersion: v2 +name: jenkins-agent-helm +description: A chart for building the jenkins-agent-helm Jenkins agent, containing Helm, ct, and oc +version: 1.0.0 \ No newline at end of file diff --git a/basic-helm-spring-boot/.helm/jenkins-agent-helm/templates/buildconfig.yaml b/basic-helm-spring-boot/.helm/jenkins-agent-helm/templates/buildconfig.yaml new file mode 100644 index 00000000..d5639277 --- /dev/null +++ b/basic-helm-spring-boot/.helm/jenkins-agent-helm/templates/buildconfig.yaml @@ -0,0 +1,24 @@ +apiVersion: build.openshift.io/v1 +kind: BuildConfig +metadata: + labels: + application: {{ .Release.Name }} + name: {{ .Release.Name }} + namespace: {{ .Release.Namespace }} +spec: + output: + to: + kind: ImageStreamTag + name: {{ .Release.Name }}:{{ .Values.destTag }} + source: + type: Git + git: + uri: {{ .Values.uri }} + ref: {{ .Values.ref }} + contextDir: {{ .Values.contextDir }} + strategy: + dockerStrategy: + dockerfilePath: {{ .Values.dockerfilePath }} + type: Docker + triggers: + - type: ConfigChange \ No newline at end of file diff --git a/basic-helm-spring-boot/.helm/jenkins-agent-helm/templates/imagestream.yaml b/basic-helm-spring-boot/.helm/jenkins-agent-helm/templates/imagestream.yaml new file mode 100644 index 00000000..cdf07b2f --- /dev/null +++ b/basic-helm-spring-boot/.helm/jenkins-agent-helm/templates/imagestream.yaml @@ -0,0 +1,8 @@ +apiVersion: image.openshift.io/v1 +kind: ImageStream +metadata: + labels: + application: {{ .Release.Name }} + role: jenkins-slave + name: {{ .Release.Name }} + namespace: {{ .Release.Namespace }} \ No newline at end of file diff --git a/basic-helm-spring-boot/.helm/jenkins-agent-helm/values.yaml b/basic-helm-spring-boot/.helm/jenkins-agent-helm/values.yaml new file mode 100644 index 00000000..9ea4940e --- /dev/null +++ b/basic-helm-spring-boot/.helm/jenkins-agent-helm/values.yaml @@ -0,0 +1,8 @@ +destTag: latest + +uri: https://github.com/redhat-cop/containers-quickstarts.git +ref: master + +contextDir: jenkins-agents/jenkins-agent-helm + +dockerfilePath: Dockerfile \ No newline at end of file diff --git a/basic-helm-spring-boot/.helm/spring-boot-build/Chart.yaml b/basic-helm-spring-boot/.helm/spring-boot-build/Chart.yaml new file mode 100644 index 00000000..aaa02771 --- /dev/null +++ b/basic-helm-spring-boot/.helm/spring-boot-build/Chart.yaml @@ -0,0 +1,4 @@ +apiVersion: v2 +name: spring-boot-build +description: A chart for building your Spring Boot app +version: 1.0.0 \ No newline at end of file diff --git a/basic-helm-spring-boot/.helm/spring-boot-build/templates/buildconfig.yaml b/basic-helm-spring-boot/.helm/spring-boot-build/templates/buildconfig.yaml new file mode 100644 index 00000000..590795a1 --- /dev/null +++ b/basic-helm-spring-boot/.helm/spring-boot-build/templates/buildconfig.yaml @@ -0,0 +1,23 @@ +apiVersion: build.openshift.io/v1 +kind: BuildConfig +metadata: + labels: + app.kubernetes.io/name: {{ .Values.name }} + app.kubernetes.io/managed-by: {{ .Release.Service }} + name: {{ .Values.name }} +spec: + output: + to: + kind: ImageStreamTag + name: {{ .Values.name }}:{{ required "value 'tag' is required" .Values.tag }} + source: + type: Binary + binary: {} + strategy: + sourceStrategy: + from: + kind: ImageStreamTag + name: {{ .Values.builderImageStreamTag }} + namespace: {{ .Values.builderImageStreamNamespace }} + type: Source + triggers: [] \ No newline at end of file diff --git a/basic-helm-spring-boot/.helm/spring-boot-build/templates/imagestream.yaml b/basic-helm-spring-boot/.helm/spring-boot-build/templates/imagestream.yaml new file mode 100644 index 00000000..a00a0ff2 --- /dev/null +++ b/basic-helm-spring-boot/.helm/spring-boot-build/templates/imagestream.yaml @@ -0,0 +1,7 @@ +apiVersion: image.openshift.io/v1 +kind: ImageStream +metadata: + labels: + app.kubernetes.io/name: {{ .Values.name }} + app.kubernetes.io/managed-by: {{ .Release.Service }} + name: {{ .Values.name }} \ No newline at end of file diff --git a/basic-helm-spring-boot/.helm/spring-boot-build/values.yaml b/basic-helm-spring-boot/.helm/spring-boot-build/values.yaml new file mode 100644 index 00000000..7b229430 --- /dev/null +++ b/basic-helm-spring-boot/.helm/spring-boot-build/values.yaml @@ -0,0 +1,5 @@ +## Required (provided by pipeline) +tag: + +builderImageStreamTag: java:8 +builderImageStreamNamespace: openshift \ No newline at end of file diff --git a/basic-helm-spring-boot/.helm/spring-boot/Chart.yaml b/basic-helm-spring-boot/.helm/spring-boot/Chart.yaml new file mode 100644 index 00000000..debe536e --- /dev/null +++ b/basic-helm-spring-boot/.helm/spring-boot/Chart.yaml @@ -0,0 +1,4 @@ +apiVersion: v2 +name: spring-boot +description: A chart for deploying your Spring Boot app +version: 1.0.0 \ No newline at end of file diff --git a/basic-helm-spring-boot/.helm/spring-boot/templates/deployment.yaml b/basic-helm-spring-boot/.helm/spring-boot/templates/deployment.yaml new file mode 100644 index 00000000..95a1d059 --- /dev/null +++ b/basic-helm-spring-boot/.helm/spring-boot/templates/deployment.yaml @@ -0,0 +1,40 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: {{ .Release.Name }} + labels: + app.kubernetes.io/name: {{ .Release.Name }} + app.kubernetes.io/managed-by: {{ .Release.Service }} + annotations: + image.openshift.io/triggers: |- + [ + { + "from":{ + "kind":"ImageStreamTag", + "name":"{{ .Release.Name }}:{{ .Values.tag }}" + }, + "fieldPath":"spec.template.spec.containers[0].image" + } + ] +spec: + replicas: {{ .Values.replicas }} + selector: + matchLabels: + app.kubernetes.io/name: {{ .Release.Name }} + strategy: + type: {{ .Values.strategy }} + template: + metadata: + labels: + app.kubernetes.io/name: {{ .Release.Name }} + spec: + containers: + - name: {{ .Release.Name }} + image: {{ .Release.Name }}:{{ .Values.tag }} + imagePullPolicy: Always +{{- if .Values.enableLiveness }} + readinessProbe: + httpGet: + path: / + port: {{ .Values.port }} +{{- end }} \ No newline at end of file diff --git a/basic-helm-spring-boot/.helm/spring-boot/templates/route.yaml b/basic-helm-spring-boot/.helm/spring-boot/templates/route.yaml new file mode 100644 index 00000000..43986504 --- /dev/null +++ b/basic-helm-spring-boot/.helm/spring-boot/templates/route.yaml @@ -0,0 +1,11 @@ +apiVersion: route.openshift.io/v1 +kind: Route +metadata: + name: {{ .Release.Name }} + labels: + app.kubernetes.io/name: {{ .Release.Name }} + app.kubernetes.io/managed-by: {{ .Release.Service }} +spec: + to: + kind: Service + name: {{ .Release.Name }} \ No newline at end of file diff --git a/basic-helm-spring-boot/.helm/spring-boot/templates/service.yaml b/basic-helm-spring-boot/.helm/spring-boot/templates/service.yaml new file mode 100644 index 00000000..8cdcd034 --- /dev/null +++ b/basic-helm-spring-boot/.helm/spring-boot/templates/service.yaml @@ -0,0 +1,12 @@ +apiVersion: v1 +kind: Service +metadata: + name: {{ .Release.Name }} + labels: + app.kubernetes.io/name: {{ .Release.Name }} + app.kubernetes.io/managed-by: {{ .Release.Service }} +spec: + ports: + - port: {{ .Values.port }} + selector: + app.kubernetes.io/name: {{ .Release.Name }} \ No newline at end of file diff --git a/basic-helm-spring-boot/.helm/spring-boot/values.yaml b/basic-helm-spring-boot/.helm/spring-boot/values.yaml new file mode 100644 index 00000000..b37bd67c --- /dev/null +++ b/basic-helm-spring-boot/.helm/spring-boot/values.yaml @@ -0,0 +1,10 @@ +## Required (provided by pipeline) +tag: + +replicas: 1 + +port: 8080 + +enableReadiness: true + +strategy: Recreate \ No newline at end of file diff --git a/basic-helm-spring-boot/Jenkinsfile b/basic-helm-spring-boot/Jenkinsfile new file mode 100644 index 00000000..15432932 --- /dev/null +++ b/basic-helm-spring-boot/Jenkinsfile @@ -0,0 +1,69 @@ +library identifier: "pipeline-library@v1.5", +retriever: modernSCM( + [ + $class: "GitSCMSource", + remote: "https://github.com/redhat-cop/pipeline-library.git" + ] +) + +// URL and Ref to the Spring Boot application +appSourceUrl = "https://github.com/redhat-cop/spring-rest.git" +appSourceRef = "master" + +// Folder containing the Spring Boot application +appFolder = "spring-rest" + +// Folder containing the Helm pipeline +helmFolder = "basic-helm-spring-boot" + +// The name you want to give your Spring Boot application +// Each resource related to your app will be given this name +appName = "my-app" + +pipeline { + agent { label "jenkins-agent-helm" } + stages { + stage("Checkout") { + steps { + // This creates a separate folder to clone the Spring Boot app to + sh "mkdir ${appFolder}" + dir(appFolder) { + git url: "${appSourceUrl}", branch: "${appSourceRef}" + } + } + } + stage("Get Version from POM") { + steps { + script { + dir(appFolder) { + tag = readMavenPom().getVersion() + } + } + } + } + stage("S2I Build") { + steps { + // This installs or upgrades the spring-boot-build Helm chart. + // It creates or updates your application's BuildConfig and ImageStream + dir(helmFolder) { + sh "helm upgrade --install ${appName}-build .helm/spring-boot-build --set name=${appName} --set tag=${tag}" + } + + // This uploads your application's source code and performs a binary build in OpenShift + dir(appFolder) { + binaryBuild(buildConfigName: appName, buildFromPath: ".") + } + } + } + stage("Deploy") { + steps { + // This installs or upgrades the spring-boot Helm chart + // It creates or updates your application's Kubernetes resources + // It also waits until the readiness probe returns successfully + dir(helmFolder) { + sh "helm upgrade --install ${appName} .helm/spring-boot --set tag=${tag} --wait" + } + } + } + } +} \ No newline at end of file diff --git a/basic-helm-spring-boot/README.md b/basic-helm-spring-boot/README.md new file mode 100644 index 00000000..6373ed8f --- /dev/null +++ b/basic-helm-spring-boot/README.md @@ -0,0 +1,65 @@ +# A Sample OpenShift Pipeline for a Spring Boot Application Using Helm + +This Jenkins pipeline provides an example of how to build a [basic Spring Boot application](https://github.com/redhat-cop/spring-rest) using Helm. The pipeline runs using the CoP's [jenkins-agent-helm](https://github.com/redhat-cop/containers-quickstarts/tree/master/jenkins-agents/jenkins-agent-helm) agent and contains the following steps: + +1. `Checkout`: Checks out the spring-rest application +1. `Get Version From POM`: Gets the version defined in the pom.xml file. This version is used to set your image's tag. +1. `S2I Build`: Performs an [s2i build](https://docs.openshift.com/container-platform/4.5/builds/understanding-image-builds.html#build-strategy-s2i_understanding-image-builds). This stage runs your unit tests, builds your jar file, and builds your container image. You could split this stage like the [basic-spring-boot](../basic-spring-boot) example if desired, but the jenkins-agent-helm agent doesn't have maven on it, so in this case, it's easier to perform an s2i build instead. +1. `Deploy`: Deploys your application to OpenShift. + +This pipeline uses two different Helm charts called [spring-boot-build](./spring-boot-build) and [spring-boot](./spring-boot). The `spring-boot-build` chart is used in the `S2I Build` stage to create and update your BuildConfig and ImageStream. The `spring-boot` chart is used in the `Deploy` stage to create and update your application's Kubernetes resources. While you could combine both of these charts into one, splitting into separate charts for building and deploying provides a greater separation of concerns. + +Before you configure this pipeline, you must first build a Jenkins agent containing Helm. Let's look at how you can build the [jenkins-agent-helm](https://github.com/redhat-cop/containers-quickstarts/tree/master/jenkins-agents/jenkins-agent-helm) agent. + +## Building the jenkins-agent-helm Agent +While the pipeline itself only uses the spring-boot and spring-boot-build Helm charts, a third Helm chart is provided under the `.helm/` folder called `jenkins-agent-helm` that you can use to easily build this Jenkins agent. First, install the jenkins-agent-helm Helm chart, which creates a BuildConfig and ImageStream: + +```bash +helm install jenkins-agent-helm .helm/jenkins-agent-helm +``` + +The first build will automatically run. You can follow this build by running: + +```bash +oc logs bc/jenkins-agent-helm -f +``` + +This ImageStream created by Helm already has the `role: jenkins-slave` label, so Jenkins will see this and automatically use this image to configure a new Jenkins agent. + +Next, let's configure the Jenkins pipeline. + +## Configuring This Jenkins Pipeline + +Because the `JenkinsPipeline` build strategy is deprecated in OpenShift 4.x, this doc will describe how you can configure this pipeline manually within the Jenkins console. + +First, deploy a Jenkins instance to OpenShift. You can deploy Jenkins by running this command: + +```bash +oc new-app jenkins-persistent +``` + +Once Jenkins is up, find and access your route URL: + +```bash +oc get route jenkins -o jsonpath='{.spec.host}' +``` + +Then, follow these steps: + +1. Click `New Item` on the lefthand side of the screen +1. Enter a name for this pipeline. The suggested name is `basic-helm-spring-boot`. +1. Select `Pipeline` and click `Ok` +1. Scroll down to the bottom of the screen to the `Pipeline` section. Change the "Pipeline script" dropdown to `Pipeline script from SCM`. +1. For "SCM", select `Git` and enter the Repository URL `https://github.com/redhat-cop/container-pipelines.git` +1. For "Script Path", enter `basic-helm-spring-boot/Jenkinsfile` +1. Click `Save` + +Once you configure your pipeline, you can click `Build Now` on the lefthand side of your screen to start the pipeline. + +## Validating Your Deployment +You can validate your deployment was successful with the following commands: + +```bash +ROUTE=$(oc get route my-app -o jsonpath='{.spec.host}') +curl $ROUTE/v1/greeting +``` \ No newline at end of file