Skip to content

Instantly share code, notes, and snippets.

@nmasse-itix
Created May 18, 2017 15:49
Show Gist options
  • Save nmasse-itix/dece6010f88cfff5fefe2c076e5eade5 to your computer and use it in GitHub Desktop.
Save nmasse-itix/dece6010f88cfff5fefe2c076e5eade5 to your computer and use it in GitHub Desktop.
Complete Jenkinsfile to do blue/green deployment in OpenShift
#!groovy
// Define Maven Command. Make sure it points to the correct settings for our
// Nexus installation. The file nexus_openshift_settings.xml needs to be in the
// Source Code repository.
def mvn = "mvn -s ./nexus_openshift_settings.xml"
def sonarqube = "http://sonarqube-cicd.app.openshift.test/"
def nexusRepo = "openshift-task"
def nexusUrl = "http://nexus3-cicd.app.openshift.test/repository/${nexusRepo}/"
def credentialsId = "749214e0-4aa1-444b-8b17-599bfa084e3f"
def openshiftBuildConfig = "tasks"
def gitRepo = 'http://gogs.app.openshift.test/CICDLabs/openshift-tasks.git'
def targetAppTestUrl = "http://tasks.test.app.openshift.test"
def targetAppProdUrl = "http://tasks.prod.app.openshift.test"
// Run this node on a Maven Slave
// Maven Slaves have JDK and Maven already installed
node('maven') {
stage('Checkout Source') {
// Get Source Code from SCM (Git) as configured in the Jenkins Project
// Next line for inline script, "checkout scm" for Jenkinsfile from Gogs
//git url: "${gitRepo}", credentialsId: "${credentialsId}"
checkout scm
}
// The following variables need to be defined at the top level and not inside
// the scope of a stage - otherwise they would not be accessible from other stages.
// Extract version and other properties from the pom.xml
def pom = readMavenPom file: 'pom.xml'
def version = pom.version
def artifactId = pom.artifactId
def groupId = pom.groupId
// Using Maven build the war file
// Do not run tests in this step
stage('Build war') {
sh "${mvn} clean install -DskipTests=true"
}
// Using Maven run the unit tests
stage('Unit Tests') {
sh "${mvn} test"
}
// Using Maven call SonarQube for Code Analysis
stage('Code Analysis') {
sh "${mvn} sonar:sonar -Dsonar.host.url=${sonarqube}"
}
// Publish the latest war file to Nexus. This needs to go into <nexusurl>/repository/releases.
// Using the properties from the pom.xml file construct a filename that includes the version number from the pom.xml file
// Also update the Gogs project openshift-tasks-ocp by editing the .s2i/environment file. This file needs to have a line
// WAR_FILE_LOCATION=<actual URL of the war file in nexus>
// It is also a good idea to add another line like "BUILD_NUMBER=${BUILD_NUMBER}" to the environment file. Otherwise the
// push to Gig/Gogs will fail in case the version number didn't change. ${BUILD_NUMBER} is one of the Jenkins built-in
// variables.
stage('Publish to Nexus') {
sh "${mvn} deploy -DskipTests=true -DaltDeploymentRepository=nexus::default::${nexusUrl}"
}
// Build the OpenShift Image in OpenShift. Make sure to use the "oc new-build" command
// for the .s2i/bin/assemble script to retrieve the war file from the environment variable WAR_FILE_LOCATION.
// Also tag the image with "TestingCandidate-${version}" - e.g. TestingCandidate-1.5
stage('Build OpenShift Image') {
// Determine the war filename that we need to use later in the process
String warFileName = "${groupId}.${artifactId}"
warFileName = warFileName.replace('.', '/')
def WAR_FILE_LOCATION = "${nexusUrl}/${warFileName}/${version}/${artifactId}-${version}.war"
echo "Will use WAR at ${WAR_FILE_LOCATION}"
// Trigger an OpenShift build in the dev environment
openshiftBuild bldCfg: "${openshiftBuildConfig}", checkForTriggeredDeployments: 'false',
namespace: "${openshiftBuildConfig}-dev", showBuildLogs: 'true',
verbose: 'false', waitTime: '', waitUnit: 'sec',
env: [ [ name: 'WAR_FILE_LOCATION', value: "${WAR_FILE_LOCATION}" ] ]
// Tag the new build as "x.y.build-z"
openshiftTag alias: 'false', destStream: 'tasks', destTag: "${version}.build-${BUILD_NUMBER}",
destinationNamespace: 'tasks-dev', namespace: 'tasks-dev',
srcStream: 'tasks', srcTag: 'latest', verbose: 'false'
}
// Deploy the built image to the Development Environment. Pay close attention to WHICH image you are deploying.
// Make sure it is the one you just tagged in the previous step. You may need to patch the deployment configuration
// of your application.
stage('Deploy to Dev') {
// Tag the new build as "ready-for-testing"
openshiftTag alias: 'false', destStream: 'tasks', srcTag: "${version}.build-${BUILD_NUMBER}",
destinationNamespace: 'tasks-dev', namespace: 'tasks-dev',
srcStream: 'tasks', destTag: 'ready-for-testing', verbose: 'false'
// Trigger a new deployment
openshiftDeploy deploymentConfig: 'tasks', namespace: 'tasks-test'
}
// Run some integration tests.
// Once the tests succeed tag the image as ProdReady-${version}
stage('Integration Test') {
// Run integration tests that are in the GIT repo
sh "./run-integration-tests.sh '${targetAppTestUrl}'"
}
// Blue/Green Deployment into Production
// -------------------------------------
// Next two stages could be one.
// Make sure to deploy the right version. If green is active then deploy blue, and vice versa.
// You will need to figure out which application is active and set the target to the other.
stage('Prep Production Deployment') {
// Tag the new build as "ready-for-prod"
openshiftTag alias: 'false', destStream: 'tasks', srcTag: "${version}.build-${BUILD_NUMBER}",
destinationNamespace: 'tasks-dev', namespace: 'tasks-dev',
srcStream: 'tasks', destTag: 'ready-for-prod', verbose: 'false'
// Yes, this is mandatory for the next command to succeed. Don't know why...
sh "oc project tasks-prod"
// Extract the route target (tasks-green or tasks-blue)
// This will be used by getCurrentTarget and getNewTarget methods
sh "oc get route tasks -n tasks-prod -o template --template='{{ .spec.to.name }}' > route-target"
}
// Deploy the ProdReady-${version} image. Make sure this is the actual tagged image deployed!
// Do not activate the new version yet.
stage('Deploy new Version') {
def newTarget = getNewTarget()
// Trigger a new deployment
openshiftDeploy deploymentConfig: "${newTarget}", namespace: 'tasks-prod'
openshiftVerifyDeployment deploymentConfig: "${newTarget}", namespace: 'tasks-prod'
}
// Once approved (input step) switch production over to the new version.
stage('Switch over to new Version') {
def newTarget = getNewTarget()
def currentTarget = getCurrentTarget()
// Wait for administrator confirmation
input "Switch Production from ${currentTarget} to ${newTarget} ?"
// Switch blue/green
sh "oc patch -n tasks-prod route/tasks --patch '{\"spec\":{\"to\":{\"name\":\"${newTarget}\"}}}'"
}
}
def getCurrentTarget() {
def currentTarget = readFile 'route-target'
return currentTarget
}
def getNewTarget() {
def currentTarget = getCurrentTarget()
def newTarget = ""
if (currentTarget == 'tasks-blue') {
newTarget = 'tasks-green'
} else if (currentTarget == 'tasks-green') {
newTarget = 'tasks-blue'
} else {
echo "OOPS, wrong target"
}
return newTarget
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment