Newer
Older
#!/bin/groovy
/*
* Licensed to the OpenAirInterface (OAI) Software Alliance under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The OpenAirInterface Software Alliance licenses this file to You under
* the OAI Public License, Version 1.1 (the "License"); you may not use this file
* except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.openairinterface.org/?page_id=698
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*-------------------------------------------------------------------------------
* For more information about the OpenAirInterface (OAI) Software Alliance:
* contact@openairinterface.org
*/
//-------------------------------------------------------------------------------
// Location of the CN executor node
// Its main purpose is the Ubuntu Build
ubuntuNode = params.UbuntuBuildNode
ubuntuBuildResource = params.UbuntuBuildResource
// Location of the RHEL CN executor
rhelNode = params.RhelBuildNode
rhelResource = params.RhelBuildResource
rhelOcCredentials = params.RhelOcCredentials
// Location of the CPPCHECK executor
cppcheckNode = params.CppCheckNode
cppcheckResource = params.CppCheckResource
// Location of the CLANG-FORMAT-CHECK executor
formatCheckNode = params.FormatCheckNode
formatCheckResource = params.FormatCheckResource
// Tags/Branches to use
def pcf_tag = "develop"
def pcf_branch = "develop"
// Merge Request Link
gitlabMergeRequestLink = ''
gitCommittorEmailAddr = ''
// Docker Hub account to push to
DH_Account = "oaisoftwarealliance"
// Private Local Registry URL
PrivateRegistryURL = 'selfix.sboai.cs.eurecom.fr'
//-------------------------------------------------------------------------------
// Pipeline start
pipeline {
agent {
}
options {
disableConcurrentBuilds()
timestamps()
ansiColor('xterm')
gitLabConnection('OAI GitLab')
"Build RHEL PCF Image",
"Static Code Analysis",
"Code Formatting Checker"
])
}
stages {
stage ('Verify Parameters') {
steps {
script {
echo '\u2705 \u001B[32mVerify Parameters\u001B[0m'
JOB_TIMESTAMP = sh returnStdout: true, script: 'date --utc --rfc-3339=seconds | sed -e "s#+00:00##"'
JOB_TIMESTAMP = JOB_TIMESTAMP.trim()
if (params.RhelOcCredentials == null) {
echo '\u26D4 \u001B[31mNo Credentials to connect to Openshift!\u001B[0m'
error "Stopping pipeline!"
}
if (params.DockerHubCredentials == null) {
echo '\u26D4 \u001B[31mNo Credentials to push to DockerHub!\u001B[0m'
error "Stopping pipeline!"
}
}
}
}
stage ('Prepare Source Code') {
steps {
script {
if ("MERGE".equals(env.gitlabActionType)) {
gitlabMergeRequestLink = sh returnStdout: true, script: "curl --silent 'https://gitlab.eurecom.fr/api/v4/projects/oai%2Fcn5g%2Foai-cn5g-pcf/merge_requests/${env.gitlabMergeRequestIid}' | jq .web_url | sed 's#\"##g'"
gitlabMergeRequestLink = gitlabMergeRequestLink.trim()
gitCommittorEmailAddr = env.gitlabUserEmail
shortenShaOne = sh returnStdout: true, script: 'git log -1 --pretty=format:"%h" --abbrev=8 ' + env.gitlabMergeRequestLastCommit
shortenShaOne = shortenShaOne.trim()
pcf_tag = 'ci-tmp-pr-' + env.gitlabMergeRequestIid + '-' + shortenShaOne
pcf_branch = env.gitlabSourceBranch
echo "========= THIS IS A MERGE REQUEST =========="
echo "MR ID is ${env.gitlabMergeRequestIid}"
echo "MR LINK is ${gitlabMergeRequestLink}"
echo "MR TITLE is ${env.gitlabMergeRequestTitle}"
echo "MR Usermail is ${gitCommittorEmailAddr}"
echo "MR TAG is ${pcf_tag}"
gitCommittorEmailAddr = sh returnStdout: true, script: 'git log -n1 --pretty=format:%ae ${GIT_COMMIT}'
gitCommittorEmailAddr = gitCommittorEmailAddr.trim()
shortenShaOne = sh returnStdout: true, script: 'git log -1 --pretty=format:"%h" --abbrev=8 ' + env.GIT_COMMIT
shortenShaOne = shortenShaOne.trim()
pcf_tag = 'develop-' + shortenShaOne
pcf_branch = env.GIT_COMMIT
echo "======== THIS IS A PUSH REQUEST ========"
echo "Git Branch is ${GIT_BRANCH}"
echo "Git Commit is ${GIT_COMMIT}"
echo "CI Usermail is ${gitCommittorEmailAddr}"
echo "CI develop TAG is ${pcf_tag}"
prepareWorkspaceMergeCase()
}
}
post {
failure {
script {
def message = "OAI " + JOB_NAME + " build (" + BUILD_ID + "): Merge Conflicts -- Cannot perform CI"
addGitLabMRComment comment: message
currentBuild.result = 'FAILURE'
}
}
}
}
stage('Build Core Network Function') {
parallel {
stage ('Build Ubuntu PCF Image') {
// Now it is only locked during this build stage and not for the whole pipeline
lock(ubuntuBuildResource) {
script {
gitlabCommitStatus(name: "Build Ubuntu PCF Image") {
sh "docker image rm oai-pcf:${pcf_tag} || true"
sh "docker image prune --force"
if ("PUSH".equals(env.gitlabActionType)) {
dockerBuildOptions = '--no-cache '
if ("MERGE".equals(env.gitlabActionType)) {
dockerBuildOptions = ''
sh "docker buildx build ${dockerBuildOptions} --target oai-pcf --tag oai-pcf:${pcf_tag} --file docker/Dockerfile.pcf.ubuntu . > archives/pcf_ubuntu_image_build.log 2>&1"
// Putting a place holder to try out on the flattening of image.
// If not satisfactory, we can remove it.
sh "python3 ./ci-scripts/flatten_image.py --tag oai-pcf:${pcf_tag}"
sh "docker image prune --force"
sh "docker image ls | egrep --color=never 'pcf|REPOSITORY' >> archives/pcf_ubuntu_image_build.log"
// Pushing to local private registry for testing purpose
sh "docker login -u oaicicd -p oaicicd ${PrivateRegistryURL}"
sh "docker image tag oai-pcf:${pcf_tag} ${PrivateRegistryURL}/oai-pcf:${pcf_tag}"
sh "docker push ${PrivateRegistryURL}/oai-pcf:${pcf_tag}"
// Remove all images locally
sh "docker rmi oai-pcf:${pcf_tag} ${PrivateRegistryURL}/oai-pcf:${pcf_tag}"
sh "docker logout ${PrivateRegistryURL}"
}
}
}
}
post {
success {
sh "echo 'OAI-PCF UBUNTU IMAGE BUILD: OK' >> archives/pcf_ubuntu_image_build.log"
sh "echo 'OAI-PCF UBUNTU IMAGE BUILD: KO' >> archives/pcf_ubuntu_image_build.log"
stage ('Build RHEL PCF Image') {
gitlabCommitStatus(name: "Build RHEL PCF Image") {
// It's a different agent from main one.
prepareWorkspaceMergeCase()
withCredentials([
[$class: 'UsernamePasswordMultiBinding', credentialsId: "${rhelOcCredentials}", usernameVariable: 'OC_Username', passwordVariable: 'OC_Password']
]) {
sh "oc login -u ${OC_Username} -p ${OC_Password}"
}
sh "oc delete istag oai-pcf:${pcf_tag} || true"
// Copy the RHEL Host certificates for building
sh "./ci-scripts/common/python/recreate_entitlement.py"
sh "oc delete -f openshift/build-config.yaml || true"
sh "sed -i -e 's@oai-pcf:latest@oai-pcf:${pcf_tag}@g' openshift/build-config.yaml"
sh "oc create -f openshift/build-config.yaml"
sh 'oc start-build pcf-build-cfg --from-dir=./ --exclude=""'
// need python to wait for pod pcf-build-cfg-1-build to be Completed or Error
// it fails if it detects error or timeout at 20 minutes
sh "./ci-scripts/common/python/check_build_pod_status.py --pod-name pcf-build-cfg-1-build --log-file archives/pcf_rhel_image_build.log"
sh "oc describe istag oai-pcf:${pcf_tag} | grep 'Image Size:' >> archives/pcf_rhel_image_build.log"
}
}
}
}
post {
success {
sh "echo 'OAI-PCF RHEL IMAGE BUILD: OK' >> archives/pcf_rhel_image_build.log"
sh "echo 'OAI-PCF RHEL IMAGE BUILD: KO' >> archives/pcf_rhel_image_build.log"
sh "oc delete build pcf-build-cfg-1 || true"
sh "oc logout || true"
stash allowEmpty: true, includes: 'archives/pcf_rhel_image_build.log', name: 'rhelBuildLog'
}
}
// Running CPPCHECK in parallel to gain time
stage ('Static Code Analysis') {
agent { label cppcheckNode }
lock (cppcheckResource) {
script {
gitlabCommitStatus(name: "Static Code Analysis") {
// It's a different agent from main one.
prepareWorkspaceMergeCase()
// Moving to focal and cppcheck 1.90 and a dockerfile approach

Raphael Defosseux
committed
sh 'sed -i -e "s@nfName@pcf@" ci-scripts/common/docker/Dockerfile.ci.cppcheck'
sh 'docker build --target pcf-cppcheck --tag pcf-cppcheck:test --file ci-scripts/common/docker/Dockerfile.ci.cppcheck . > archives/cppcheck_install.log 2>&1'
sh 'docker run --name pcf-ci-cppcheck --entrypoint /bin/true pcf-cppcheck:test'
sh 'docker cp pcf-ci-cppcheck:/home/cppcheck.xml archives'
sh 'docker cp pcf-ci-cppcheck:/home/cppcheck_build.log archives'
sh 'docker rm -f pcf-ci-cppcheck'
sh 'docker rmi pcf-cppcheck:test'
}
}
}
}
post {
success {
sh "echo 'CPPCHECK: OK' >> archives/cppcheck_install.log"
}
unsuccessful {
sh "echo 'CPPCHECK: KO' >> archives/cppcheck_install.log"
}
stash allowEmpty: true, includes: 'archives/cppcheck*.*', name: 'cppcheckLogs'
// no need to keep the cppcheck container
sh 'docker rm -f pcf-ci-cppcheck || true'
sh 'docker rmi pcf-cppcheck:test || true'
}
}
}
}
// Running CLANG-FORMATTING check in parallel to gain time
stage ('Code Formatting Checker') {
agent { label formatCheckNode }
lock (formatCheckResource) {
script {
gitlabCommitStatus(name: "Code Formatting Checker") {
// It's a different agent from main one.
prepareWorkspaceMergeCase()

Raphael Defosseux
committed
sh 'sed -i -e "s@nfName@pcf@" ci-scripts/common/docker/Dockerfile.ci.clang-format'
if ("MERGE".equals(env.gitlabActionType)) {

Raphael Defosseux
committed
sh 'docker build --target pcf-clang-format-check --tag pcf-clang-format-check:test --file ci-scripts/common/docker/Dockerfile.ci.clang-format --build-arg MERGE_REQUEST_CHECK=True --build-arg SOURCE_BRANCH=' + env.gitlabSourceBranch + ' --build-arg TARGET_BRANCH=' + env.gitlabTargetBranch + ' . > archives/clang_format_install.log 2>&1'

Raphael Defosseux
committed
sh 'docker build --target pcf-clang-format-check --tag pcf-clang-format-check:test --file ci-scripts/common/docker/Dockerfile.ci.clang-format . > archives/clang_format_install.log 2>&1'
}
sh 'docker run --name pcf-ci-clang-format --entrypoint /bin/true pcf-clang-format-check:test'
sh 'docker cp pcf-ci-clang-format:/home/src/oai_rules_result.txt src'
sh 'docker cp pcf-ci-clang-format:/home/src/oai_rules_result_list.txt src || true'
sh 'docker rm -f pcf-ci-clang-format'
sh 'docker rmi pcf-clang-format-check:test'
// The check is done now here
sh 'grep -L "NB_FILES_FAILING_CHECK=0" src/oai_rules_result.txt'
}
}
}
}
post {
stash allowEmpty: true, includes: 'src/oai_rules_result*.txt, archives/clang_format_install.log', name: 'formatCheckLogs'
sh 'docker rm -f pcf-ci-clang-format || true'
sh 'docker rmi pcf-clang-format-check:test || true'
}
}
}
}
}
post {
always {
script {
unstash 'rhelBuildLog'
unstash 'cppcheckLogs'
unstash 'formatCheckLogs'
}
}
}
stage('Testing whole 5g Core Network Functions') {
stage ('Testing the tutorials') {
gitlabCommitStatus(name: "Test tutorials") {
localStatus = build job: 'OAI-CN5G-Tutorials-Check',
parameters: [
string(name: 'PCF_TAG', value: String.valueOf(pcf_tag)),
string(name: 'PCF_BRANCH', value: String.valueOf(pcf_branch))
], propagate: false
localResult = localStatus.getResult()
if (localStatus.resultIsBetterOrEqualTo('SUCCESS')) {
echo "Tutorials Test Job is OK"
} else {

Raphael Defosseux
committed
error "Tutorials Test Job is KO"
post {
always {
script {
copyArtifacts(projectName: 'OAI-CN5G-Tutorials-Check',
filter: '*_results_oai_cn5g*.html',
selector: lastCompleted())
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
// Home-made RAN emulator
stage ('Robot-Test') {
steps {
script {
gitlabCommitStatus(name: "Robot-Test") {
localStatus = build job: 'OAI-CN5G-RobotTest',
parameters: [
string(name: 'PCF_TAG', value: String.valueOf(pcf_tag)),
string(name: 'PCF_BRANCH', value: String.valueOf(pcf_branch))
], propagate: false
localResult = localStatus.getResult()
if (localStatus.resultIsBetterOrEqualTo('SUCCESS')) {
echo "Robot-Test is OK"
} else {
error "Robot-Test is is KO"
}
}
}
}
post {
always {
script {
copyArtifacts(projectName: 'OAI-CN5G-RobotTest',
filter: '*.html',
selector: lastCompleted())
}
}
}
}
// We are only publishing the Ubuntu image to Docker-Hub
// For Post-Merge events.
// Temporary Images from Merge-Request Runs are kept in local private registry
stage ('Pushing Image to Official Registry') {
steps {
lock(ubuntuBuildResource) {
script {
// Only in case of push to target branch!
if ("PUSH".equals(env.gitlabActionType)) {
withCredentials([
[$class: 'UsernamePasswordMultiBinding', credentialsId: "${params.DockerHubCredentials}", usernameVariable: 'DH_Username', passwordVariable: 'DH_Password']
]) {
sh "echo ${DH_Password} | docker login --username ${DH_Username} --password-stdin"
}
sh "docker login -u oaicicd -p oaicicd ${PrivateRegistryURL}"
sh "docker pull ${PrivateRegistryURL}/oai-pcf:${pcf_tag}"
sh "docker image tag ${PrivateRegistryURL}/oai-pcf:${pcf_tag} ${DH_Account}/oai-pcf:develop"
sh "docker push ${DH_Account}/oai-pcf:develop"
sh "docker rmi ${DH_Account}/oai-pcf:develop ${PrivateRegistryURL}/oai-pcf:${pcf_tag}"
sh "docker logout ${PrivateRegistryURL}"
sh "docker logout"
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
}
}
}
}
}
}
post {
success {
script {
if ("MERGE".equals(env.gitlabActionType)) {
def message = "OAI " + JOB_NAME + " build (" + BUILD_ID + "): passed (" + BUILD_URL + ")"
echo "This is a MERGE event"
addGitLabMRComment comment: message
}
}
}
unsuccessful {
script {
if ("MERGE".equals(env.gitlabActionType)) {
def message = "OAI " + JOB_NAME + " build (" + BUILD_ID + "): failed (" + BUILD_URL + ")"
echo "This is a MERGE event"
addGitLabMRComment comment: message
}
}
}
cleanup {
script {
// Zipping all archived log files
sh "zip -r -qq docker_logs.zip archives"
if (fileExists('docker_logs.zip')) {
archiveArtifacts artifacts: 'docker_logs.zip'
}
// Generating the HTML report(s)
if ("MERGE".equals(env.gitlabActionType)) {

Raphael Defosseux
committed
sh "./ci-scripts/generateHtmlReport.py --job-name ${JOB_NAME} --build-id ${BUILD_ID} --build-url ${BUILD_URL} --git-url ${GIT_URL} --git-src-branch ${env.gitlabSourceBranch} --git-src-commit ${env.gitlabMergeRequestLastCommit} --git-merge-request --git-dst-branch ${env.gitlabTargetBranch} --git-dst-commit ${GIT_COMMIT}"

Raphael Defosseux
committed
sh "./ci-scripts/generateHtmlReport.py --job-name ${JOB_NAME} --build-id ${BUILD_ID} --build-url ${BUILD_URL} --git-url ${GIT_URL} --git-src-branch ${GIT_BRANCH} --git-src-commit ${GIT_COMMIT}"
}
listOfFiles = sh returnStdout: true, script: 'ls test_results*.html'
String[] htmlFiles = listOfFiles.split("\\n")
for (htmlFile in htmlFiles) {
if ("MERGE".equals(env.gitlabActionType)) {
sh "sed -i -e 's#TEMPLATE_MERGE_REQUEST_LINK#${gitlabMergeRequestLink}#g' ${htmlFile}"
sh "sed -i -e 's#TEMPLATE_MERGE_REQUEST_TEMPLATE#${env.gitlabMergeRequestTitle}#' ${htmlFile}"
}
sh "sed -i -e 's#TEMPLATE_TIME#${JOB_TIMESTAMP}#' ${htmlFile}"
archiveArtifacts artifacts: htmlFile
}
// Sending email to commiter
if (params.sendToCommitterEmail != null) {
if (params.sendToCommitterEmail) {
emailext attachmentsPattern: '*results*.html',
body: '''Hi,
Here are attached HTML report files for $PROJECT_NAME - Build # $BUILD_NUMBER - $BUILD_STATUS!
Regards,
OAI CI Team''',
replyTo: 'no-reply@openairinterface.org',
subject: '$PROJECT_NAME - Build # $BUILD_NUMBER - $BUILD_STATUS!',
}
}
}
}
}
}
def prepareWorkspaceMergeCase () {
sh "git clean -x -d -f > /dev/null 2>&1"

Raphael Defosseux
committed
sh "git submodule foreach --recursive 'git clean -x -d -ff' > /dev/null 2>&1"
sh "git submodule deinit --force --all > /dev/null 2>&1"
if ("MERGE".equals(env.gitlabActionType)) {
sh "./ci-scripts/doGitLabMerge.sh --src-branch ${env.gitlabSourceBranch} --src-commit ${env.gitlabMergeRequestLastCommit} --target-branch ${env.gitlabTargetBranch} --target-commit ${GIT_COMMIT}"
sh "git submodule update --init --recursive"
sh "mkdir -p archives"