Created
July 10, 2017 19:36
-
-
Save frezbo/caf1ae977484107034b4e01b2c692c8b to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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