Skip to content

Instantly share code, notes, and snippets.

@frezbo
Created July 10, 2017 19:36
Show Gist options
  • Save frezbo/caf1ae977484107034b4e01b2c692c8b to your computer and use it in GitHub Desktop.
Save frezbo/caf1ae977484107034b4e01b2c692c8b to your computer and use it in GitHub Desktop.
import static groovy.io.FileType.DIRECTORIES
@NonCPS
def traverseHelper() {
final excludeDirs = ['.git']
def dirs = [];
new File("${WORKSPACE}").eachDir() { dir ->
if (dir.name in excludeDirs) return true else dirs.add(dir)
}
return dirs
}
def transformIntoStep(dirName) {
return {
node("${NODE}") {
def dirFullPath = "${WORKSPACE}"/dirName
// Getting the repository name
def gitRepo = sh returnStdout: true, script: 'basename -s .git $(git config --get remote.origin.url)'
dir (dirFullPath) {
wrap([$class: 'AnsiColorBuildWrapper', 'colorMapName': 'XTerm']) {
try {
stage('Adding pipeline') {
echo "\u001B[34m\u001B[1mAdding PR pipeline if it does not exist for the cookbook\u001B[0m"
try {
sh '''
#!/bin/bash
# Script should exit if any command fails
set -e
if [[ -f Jenkinsfile ]]; then
cat << EOF > Jenkinsfile
EOF
'''
}
catch (Exception PRCreationFailed) {
println "\u001B[31mPR creation failed\u001B[0m"
sh "exit 1"
}
}
stage('Checkout to latest version') {
echo "\u001B[34m\u001B[1mChecking out the latest version from git\u001B[0m"
try {
sh '''
#!/bin/bash
# Script should exit if any command fails
set -e
# Get the latest tag from GitHub
repo_tag=$(git for-each-ref --sort=creatordate refs/tags | tail -1 | cut -d "/" -f 3)
# Checkout to the latest tag
git checkout "${repo_tag}"
'''
}
catch (Exception tagCheckout) {
println "\u001B[31mCheckout to latest version failed\u001B[0m"
sh "exit 1"
}
}
stage('Check the git tag version and metadata version') {
echo "\u001B[34m\u001B[1mChecking the git tag version and metadata version\u001B[0m"
try {
sh '''
#!/bin/bash
# Script should exit if any command fails
set -e
# Checking whether the cookbook version matches the git tag
if [[ "v$(grep -w version metadata.rb | awk '{print $2}' | sed "s/\'//g")" != "${repo_tag}" ]]; then
exit 1
fi
'''
}
catch (Exception versionMismatch) {
println "\u001B[31mThe cookbook version and git tag does not match, please fix this\u001B[0m"
sh "exit 1"
}
}
stage('Checking existence of kitchen.yml file') {
echo "\u001B[34m\u001B[1mChecking the existence of kitchen file\u001B[0m"
try {
sh '''
#!/bin/bash
# Script should exit if any command fails
set -e
# Checking whether kitchen file is present, else exiting with a status 1
if [[ 1 -f "${KITCHEN_FILE}" ]]; then
exit 1
fi
'''
}
catch (Exception noKitchenFile) {
println "\u001B[31mThe cookbook does not contain a kitchen file.\u001B[0m"
sh "exit 1"
}
}
stage('foodcritic linting') {
echo "\u001B[34m\u001B[1mRunning foodcritic linting\u001B[0m"
try {
sh '''
#!/bin/bash
# Script should exit if any command fails
set -e
# Running foodcritic for checking cookbook DSL
foodcritic .
'''
}
catch (Exception foodcriticFailure) {
println "\u001B[31mfoodcritic linting failed\u001B[0m"
sh "exit 1"
}
}
stage('cookstyle linting') {
echo "\u001B[34m\u001B[1mRunning cookstyle linting\u001B[0m"
try {
sh '''
#!/bin/bash
# Script should exit if any command fails
set -e
# Running cookstyle for checking cookbook syntax best practices
cookstyle
'''
}
catch (Exception cookstyleFailure) {
println "\u001B[31mcookstyle linting failed\u001B[0m"
sh "exit 1"
}
}
stage('Test suites checking') {
echo "\u001B[34m\u001B[1mchecking the correct format for test suites\u001B[0m"
try {
sh '''
#!/bin/bash
# Script should exit if any command fails
set -e
# Checking whether test suites are present in integration folder
if [[ ! -d "test/integration/" ]]; then
if [[ $(find test/integration -type f -name "*.rb" | wc -l) == 0 ]]; then
exit 1
fi
exit 1
fi
'''
}
catch (Exception testLocationFailure) {
println "\u001B[31mIntegration tests are not present or not in correct hierarchy\u001B[0m"
sh "exit 1"
}
}
stage('Check minimum test cases') {
echo "\u001B[34m\u001B[1mChecking whether the minimum number of test cases are met\u001B[0m"
try {
sh '''
#!/bin/bash
# Script should exit if any command fails
set -e
# Checking whether at least the test cases consist of 10 lines
for i in $(find test/integration/ -name "*.rb"); do
if [[ $(wc -l $i | awk '{print $1}') -le 10 ]]; then
exit 1
fi
done
'''
}
catch (Exception insufficientTestCasesFailure) {
println "\u001B[31mThe cookbook does not contain enough test cases, please add more test cases\u001B[0m"
sh "exit 1"
}
}
stage('Creating temporary aws key pair') {
echo "\u001B[34m\u001B[1mCreating temporary AWS key pair\u001B[0m"
try {
sh '''
#!/bin/bash
# Script should exit if any command fails
set -e
# Delete key pair if already exists, this script always return an exit status of zero, even if key is not present in AWS
aws ec2 delete-key-pair --key-name '''+gitRepo+'''
# Create a key pair in amazon with the cookbook name and save it to a file with the cookbook name
aws ec2 create-key-pair --key-name '''+gitRepo+''' | ruby -e "require 'json'; puts JSON.parse(STDIN.read)['KeyMaterial']" > ./'''+gitRepo+'''
# Setting correct permissions for the private key
chmod 400 '''+gitRepo+'''
'''
}
catch (Exception awsKeyPairCreationFailure) {
println "\u001B[31mAWS key pair creation failed\u001B[0m"
sh "exit 1"
}
}
stage('Creating custom kitchen file') {
echo "\u001B[34m\u001B[1mEditing kitchen file\u001B[0m"
try {
sh '''
#!/bin/bash
# Script should exit if any command fails
set -e
cat << EOF > .kitchen.custom.yml
---
driver:
name: ec2
region: ${REGION}
subnet_id: ${SUBNET_ID}
security_group_ids: [\"${SECURITY_GROUP_ID}\"]
iam_profile_name: ${IAM_PROFILE}
aws_ssh_key_id: ${repo_name}
tags:
Owner: ${TAG_OWNER}
Environment: ${TAG_ENVIRONMENT}
Project: ${TAG_PROJECT}
transport:
ssh_key: ./'''+gitRepo+'''
sed -n "$(cat -n .kitchen.yml | grep platforms | awk '{print $1}' | sed '1!d'),\\$p" .kitchen.yml
EOF
'''
}
catch (Exception kitchenFileCreationFailure) {
println "\u001B[31mKitchen file creation failed\u001B[0m"
sh "exit 1"
}
}
stage('Integration tests') {
echo "\u001B[34m\u001B[1mIntegration tests using kitchen\u001B[0m"
try {
sh '''
#!/bin/bash
# Script should exit if any command fails
set -e
kitchen test -c || ( kitchen destroy && aws ec2 delete-key-pair --key-name '''+gitRepo+''' && rm -rf ${repo_name} && exit 1 )
'''
}
catch (Exception kitchenConvergeFailure) {
println "\u001B[31mCookbook integration test failed\u001B[0m"
}
}
}
catch (Exception pipelineFailure) {
println "\u001B[31mOverall pipeline failed\u001B[0m"
currentBuild.result = 'FAILURE'
}
}
}
}
}
}
pipeline {
agent {
node {
label 'master'
customWorkspace 'workspace/REAN-Deploy-packages'
}
}
parameters {
string(name: 'NODE', defaultValue: 'master', description: 'The node to run the builds on')
string(name: 'JOB_NAME', defaultValue: 'cookbooks', description: 'Custom job name')
string(name: 'REGION', defaultValue: 'us-east-1', description: 'The AWS region to use')
string(name: 'SUBNET_ID', defaultValue: 'subnet-476a1e21', description: 'The subnet id to use')
string(name: 'SECURITY_GROUP_ID', defaultValue: 'sg-056a4gh6', description: 'The security group to attach to the launched instance')
string(name: 'IAM_PROFILE', defaultValue: 'iamrole', description: 'The IAM profile to use for the instance')
string(name: 'SPOT_PRICE', defaultValue: '5', description: 'The AWS spot instance price')
string(name: 'TAG_OWNER', defaultValue: 'owner', description: 'The owner tag for the created instance')
string(name: 'TAG_ENVIRONMENT', defaultValue: 'Testing', description: 'The tag specifying the environment')
string(name: 'TAG_PROJECT', defaultValue: 'deployment', description: 'The tag specifying the project')
}
environment {
node = "${params.NODE}"
jobName = "${params.JOB_NAME}"
region = "${params.REGION}"
subnetID = "${params.SUBNET_ID}"
securityGroupID = "${params.SECURITY_GROUP_ID}"
iamProfile = "${params.IAM_PROFILE}"
spotPrice = "${params.SPOT_PRICE}"
tagOwner = "${params.TAG_OWNER}"
tagEnvironment = "${params.TAG_ENVIRONMENT}"
tagProject = "${TAG_PROJECT}"
}
triggers {
cron('@weekly')
}
stages {
stage('Submodules pull') {
echo "Pulling the submodules"
steps {
try {
sh '''
#!/bin/bash
set -e
git submodule update --init --recursive
'''
}
catch (Exception gitClone) {
println "Cloning submodules failed"
sh "exit 1"
}
}
}
stage('Create parallel jobs') {
echo "Creating jobs and PR pipeline for each repo"
steps {
script {
def stepsForParallel = [:]
def dirs = traverseHelper()
for (int i=0; i < dirs.size(); i++) {
def s = dirs.get(i).getName()
def stepName = "${s}"
stepsForParallel[stepName] = transformIntoStep(s)
}
parallel stepsForParallel
}
}
}
}
post {
always {
deleteDir()
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment