Skip to content

Commit

Permalink
added templates
Browse files Browse the repository at this point in the history
  • Loading branch information
raffaelespazzoli committed Jun 12, 2018
1 parent 9a3950c commit f5cb20e
Show file tree
Hide file tree
Showing 14 changed files with 687 additions and 0 deletions.
11 changes: 11 additions & 0 deletions .project
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<?xml version="1.0" encoding="UTF-8"?>
<projectDescription>
<name>container-pipelines</name>
<comment></comment>
<projects>
</projects>
<buildSpec>
</buildSpec>
<natures>
</natures>
</projectDescription>
159 changes: 159 additions & 0 deletions cucumber-selenium-grid/Jenkinsfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,159 @@
openshift.withCluster() {
env.NAMESPACE = openshift.project()
env.POM_FILE = env.BUILD_CONTEXT_DIR ? "${env.BUILD_CONTEXT_DIR}/pom.xml" : "pom.xml"
env.APP_NAME = "${env.JOB_NAME}".replaceAll(/-?pipeline-?/, '').replaceAll(/-?${env.NAMESPACE}-?/, '').replaceAll("/", '')
echo "Starting Pipeline for ${APP_NAME}..."
def projectBase = "${env.NAMESPACE}".replaceAll(/-build/, '')
env.STAGE0 = "${projectBase}-build"
env.STAGE1 = "${projectBase}-dev"
env.STAGE2 = "${projectBase}-stage"
env.STAGE3 = "${projectBase}-prod"
}

pipeline {
// Use Jenkins Maven slave
// Jenkins will dynamically provision this as OpenShift Pod
// All the stages and steps of this Pipeline will be executed on this Pod
// After Pipeline completes the Pod is killed so every run will have clean
// workspace
agent {
label 'maven'
}

// Pipeline Stages start here
// Requeres at least one stage
stages {

// Checkout source code
// This is required as Pipeline code is originally checkedout to
// Jenkins Master but this will also pull this same code to this slave
stage('Git Checkout') {
steps {
// Turn off Git's SSL cert check, uncomment if needed
// sh 'git config --global http.sslVerify false'
git url: "${APPLICATION_SOURCE_REPO}"
}
}

// Run Maven build, skipping tests
stage('Build'){
steps {
sh "mvn clean install -DskipTests=true -f ${POM_FILE}"
}
}

// Run Maven unit tests
stage('Unit Test'){
steps {
sh "mvn test -f ${POM_FILE}"
}
}

// Build Container Image using the artifacts produced in previous stages
stage('Build Container Image'){
steps {
// Copy the resulting artifacts into common directory
sh """
ls target/*
rm -rf oc-build && mkdir -p oc-build/deployments
for t in \$(echo "jar;war;ear" | tr ";" "\\n"); do
cp -rfv ./target/*.\$t oc-build/deployments/ 2> /dev/null || echo "No \$t files"
done
"""

// Build container image using local Openshift cluster
// Giving all the artifacts to OpenShift Binary Build
// This places your artifacts into right location inside your S2I image
// if the S2I image supports it.
script {
openshift.withCluster() {
openshift.withProject("${STAGE0}") {
openshift.selector("bc", "${APP_NAME}").startBuild("--from-dir=oc-build").logs("-f")
}
}
}
}
}

stage('Promote from Build to Dev') {
steps {
script {
openshift.withCluster() {
openshift.tag("${env.STAGE0}/${env.APP_NAME}:latest", "${env.STAGE1}/${env.APP_NAME}:latest")
}
}
}
}

stage ('Verify Deployment to Dev') {
steps {
script {
openshift.withCluster() {
openshift.withProject("${STAGE1}") {
def dcObj = openshift.selector('dc', env.APP_NAME).object()
def podSelector = openshift.selector('pod', [deployment: "${APP_NAME}-${dcObj.status.latestVersion}"])
podSelector.untilEach {
echo "pod: ${it.name()}"
return it.object().status.containerStatuses[0].ready
}
}
}
}
}
}

stage('Promote from Dev to Stage') {
steps {
script {
openshift.withCluster() {
openshift.tag("${env.STAGE1}/${env.APP_NAME}:latest", "${env.STAGE2}/${env.APP_NAME}:latest")
}
}
}
}

stage ('Verify Deployment to Stage') {
steps {
script {
openshift.withCluster() {
openshift.withProject("${STAGE2}") {
def dcObj = openshift.selector('dc', env.APP_NAME).object()
def podSelector = openshift.selector('pod', [deployment: "${APP_NAME}-${dcObj.status.latestVersion}"])
podSelector.untilEach {
echo "pod: ${it.name()}"
return it.object().status.containerStatuses[0].ready
}
}
}
}
}
}
stage('Promote from Stage to Prod') {
steps {
script {
openshift.withCluster() {
openshift.tag("${env.STAGE2}/${env.APP_NAME}:latest", "${env.STAGE3}/${env.APP_NAME}:latest")
}
}
}
}

stage ('Verify Deployment to Prod') {
steps {
script {
openshift.withCluster() {
openshift.withProject("${STAGE3}") {
def dcObj = openshift.selector('dc', env.APP_NAME).object()
def podSelector = openshift.selector('pod', [deployment: "${APP_NAME}-${dcObj.status.latestVersion}"])
podSelector.untilEach {
echo "pod: ${it.name()}"
return it.object().status.containerStatuses[0].ready
}
}
}
}
}
}

}
}
154 changes: 154 additions & 0 deletions cucumber-selenium-grid/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
# A Sample OpenShift Pipeline for a Spring Boot Application

This example demonstrates how to implement a full end-to-end Jenkins Pipeline for a Java application in OpenShift Container Platform. This sample demonstrates the following capabilities:

* Deploying an integrated Jenkins server inside of OpenShift
* Running both custom and oob Jenkins slaves as pods in OpenShift
* "One Click" instantiation of a Jenkins Pipeline using OpenShift's Jenkins Pipeline Strategy feature
* Promotion of an application's container image within an OpenShift Cluster (using `oc tag`)
* Run of integration tests using selenium in the stage environment
* Automated rollout using the [openshift-appler](https://github.com/redhat-cop/openshift-applier) project.

## Automated Quickstart

This quickstart can be deployed quickly using Ansible. Here are the steps.

1. Clone [this repo](https://github.com/redhat-cop/container-pipelines) and the [openshift-applier](https://github.com/redhat-cop/openshift-applier) repo.
2. Log into an OpenShift cluster, then run the following command.
```
$ oc login
$ ansible-playbook -i ./applier/inventory/ /path/to/openshift-applier/playbooks/openshift-cluster-seed.yml
```

At this point you should have 3 projects deployed (`todomvc-build`, `todomvc-dev`, `todomvc-stage`, and `todomvc-prod`) with our [Spring Rest](https://github.com/redhat-cop/spring-rest) demo application deployed to all 3.

## Architecture

The following breaks down the architecture of the pipeline deployed, as well as walks through the manual deployment steps

### OpenShift Templates

The components of this pipeline are divided into two templates.

The first template, `applier/templates/build.yml` is what we are calling the "Build" template. It contains:

* A `jenkinsPipelineStrategy` BuildConfig
* An `s2i` BuildConfig
* An ImageStream for the s2i build config to push to

The build template contains a default source code repo for a java application compatible with this pipelines architecture (https://github.com/redhat-cop/spring-rest).

The second template, `applier/templates/deployment.yml` is the "Deploy" template. It contains:

* A tomcat8 DeploymentConfig
* A Service definition
* A Route

The idea behind the split between the templates is that I can deploy the build template only once (to my dev project) and that the pipeline will promote my image through all of the various stages of my application's lifecycle. The deployment template gets deployed once to each of the stages of the application lifecycle (once per OpenShift project).

### Pipeline Script

This project includes a sample `Jenkinsfile` pipeline script that could be included with a Java project in order to implement a basic CI/CD pipeline for that project, under the following assumptions:

* The project is built with Maven
* The OpenShift projects that represent the Application's lifecycle stages are of the naming format: `<app-name>-dev`, `<app-name>-stage`, `<app-name>-prod`.

This pipeline defaults to use our [Spring Boot Demo App](https://github.com/redhat-cop/spring-rest).

## Bill of Materials

* One or Two OpenShift Container Platform Clusters
* OpenShift 3.5+ is required
* [Red Hat OpenJDK 1.8](https://access.redhat.com/containers/?tab=overview#/registry.access.redhat.com/redhat-openjdk-18/openjdk18-openshift) image is required
* Access to GitHub

## Manual Deployment Instructions

### 1. Create Lifecycle Stages

For the purposes of this demo, we are going to create three stages for our application to be promoted through.

- `todomvc-build`
- `todomvc-dev`
- `todomvc-stage`
- `todomvc-prod`

In the spirit of _Infrastructure as Code_ we have a YAML file that defines the `ProjectRequests` for us. This is as an alternative to running `oc new-project`, but will yeild the same result.

```
$ oc process -f applier/projects/projects.yml | oc apply -f -
projectrequest "todomvc-build" created
projectrequest "todomvc-dev" created
projectrequest "todomvc-stage" created
projectrequest "todomvc-prod" created
```

### 2. Stand up Jenkins master in dev

For this step, the OpenShift default template set provides exactly what we need to get jenkins up and running.

```
$ oc process openshift//jenkins-ephemeral | oc apply -f- -n todomvc-build
route "jenkins" created
deploymentconfig "jenkins" created
serviceaccount "jenkins" created
rolebinding "jenkins_edit" created
service "jenkins-jnlp" created
service "jenkins" created
```

### 4. Instantiate Pipeline

A _deploy template_ is provided at `applier/templates/deployment.yml` that defines all of the resources required to run our Tomcat application. It includes:

* A `Service`
* A `Route`
* An `ImageStream`
* A `DeploymentConfig`
* A `RoleBinding` to allow Jenkins to deploy in each namespace.

This template should be instantiated once in each of the namespaces that our app will be deployed to. For this purpose, we have created a param file to be fed to `oc process` to customize the template for each environment.

Deploy the deployment template to all three projects.
```
$ oc process -f applier/templates/deployment.yml --param-file=applier/params/deployment-dev | oc apply -f-
service "spring-rest" created
route "spring-rest" created
imagestream "spring-rest" created
deploymentconfig "spring-rest" created
rolebinding "jenkins_edit" configured
$ oc process -f applier/templates/deployment.yml --param-file=applier/params/deployment-stage | oc apply -f-
service "spring-rest" created
route "spring-rest" created
imagestream "spring-rest" created
deploymentconfig "spring-rest" created
rolebinding "jenkins_edit" created
$ oc process -f applier/templates/deployment.yml --param-file=applier/params/deployment-prod | oc apply -f-
service "spring-rest" created
route "spring-rest" created
imagestream "spring-rest" created
deploymentconfig "spring-rest" created
rolebinding "jenkins_edit" created
```

A _build template_ is provided at `applier/templates/build.yml` that defines all the resources required to build our java app. It includes:

* A `BuildConfig` that defines a `JenkinsPipelineStrategy` build, which will be used to define out pipeline.
* A `BuildConfig` that defines a `Source` build with `Binary` input. This will build our image.

Deploy the pipeline template in dev only.
```
$ oc process -f applier/templates/build.yml --param-file applier/params/build-dev | oc apply -f-
buildconfig "spring-rest-pipeline" created
buildconfig "spring-rest" created
```

At this point you should be able to go to the Web Console and follow the pipeline by clicking in your `todomvc-dev` project, and going to *Builds* -> *Pipelines*. At several points you will be prompted for input on the pipeline. You can interact with it by clicking on the _input required_ link, which takes you to Jenkins, where you can click the *Proceed* button. By the time you get through the end of the pipeline you should be able to visit the Route for your app deployed to the `myapp-prod` project to confirm that your image has been promoted through all stages.

## Cleanup

Cleaning up this example is as simple as deleting the projects we created at the beginning.

```
oc delete project todomvc-build todomvc-dev todomvc-prod todomvc-stage
```
5 changes: 5 additions & 0 deletions cucumber-selenium-grid/README1.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
oc new-project selenium
oc new-app registry.access.redhat.com/rhoar-nodejs/nodejs-8~https://github.com/raffaelespazzoli/todomvc --context-dir examples/angularjs --name todomvc

oc new-build --strategy=docker --binary=true --name=jenkins-slave-protractor
oc start-build -F jenkins-slave-protractor --from-dir=.
22 changes: 22 additions & 0 deletions cucumber-selenium-grid/applier/inventory/group_vars/seed-hosts.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
openshift_cluster_content:
- object: projects
content:
- name: "create environments"
file: "{{ inventory_dir }}/../projects/projects.yml"
file_action: create
- object: deployments
content:
- name: "deploy dev environment"
template: "{{ inventory_dir }}/../templates/deployment.yml"
params: "{{ inventory_dir }}/../params/deployment-dev"
- name: "deply stage environment"
template: "{{ inventory_dir }}/../templates/deployment.yml"
params: "{{ inventory_dir }}/../params/deployment-stage"
- name: "deply prod environment"
template: "{{ inventory_dir }}/../templates/deployment.yml"
params: "{{ inventory_dir }}/../params/deployment-prod"
- object: builds
content:
- name: "deploy build pipeline to dev"
template: "{{ inventory_dir }}/../templates/build.yml"
params: "{{ inventory_dir }}/../params/build-dev"
2 changes: 2 additions & 0 deletions cucumber-selenium-grid/applier/inventory/hosts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
[seed-hosts]
localhost ansible_connection=local
4 changes: 4 additions & 0 deletions cucumber-selenium-grid/applier/params/build-dev
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
APPLICATION_NAME=todo-mvc
NAMESPACE=todomvc-build
SOURCE_REPOSITORY_URL=https://github.com/raffaelespazzoli/container-pipelines
SOURCE_REPOSITORY_REF=master
5 changes: 5 additions & 0 deletions cucumber-selenium-grid/applier/params/deployment-dev
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
APPLICATION_NAME=spring-rest
NAMESPACE=basic-spring-boot-dev
SA_NAMESPACE=basic-spring-boot-build
READINESS_RESPONSE=status.:.UP
READINESS_PATH=/health
5 changes: 5 additions & 0 deletions cucumber-selenium-grid/applier/params/deployment-prod
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
APPLICATION_NAME=spring-rest
NAMESPACE=basic-spring-boot-prod
SA_NAMESPACE=basic-spring-boot-build
READINESS_RESPONSE=status.:.UP
READINESS_PATH=/health
6 changes: 6 additions & 0 deletions cucumber-selenium-grid/applier/params/deployment-stage
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
APPLICATION_NAME=spring-rest
NAMESPACE=basic-spring-boot-stage
SA_NAME=jenkins
SA_NAMESPACE=basic-spring-boot-build
READINESS_RESPONSE=status.:.UP
READINESS_PATH=/health
Loading

0 comments on commit f5cb20e

Please sign in to comment.