Newer
Older
environment {
BLUE_OCEAN_URL = "https://jenkins.ozg-sh.de/blue/organizations/jenkins/goofy/detail/${env.BRANCH_NAME}/${env.BUILD_NUMBER}/pipeline"
RELEASE_REGEX = /\d+.\d+.\d+/
SNAPSHOT_REGEX = /\d+.\d+.\d+-SNAPSHOT/
FAILED_STAGE = ""
IMAGE_TAG = ""
VERSION = ""
E2E_FAILED = ""
options {
timeout(time: 1, unit: 'HOURS')
disableConcurrentBuilds()
stage('Check Version') {
steps {
script {
FAILED_STAGE = env.STAGE_NAME
def rootPom = readMavenPom file: 'pom.xml'
VERSION = rootPom.version
def serverPom = readMavenPom file: 'goofy-server/pom.xml'
def serverVersion = serverPom.parent.version
def clientPom = readMavenPom file: 'goofy-client/pom.xml'
def clientVersion = clientPom.parent.version
if(env.BRANCH_NAME == 'release'){
if ( !(VERSION ==~ RELEASE_REGEX) || !(serverVersion ==~ RELEASE_REGEX) || !(clientVersion ==~ RELEASE_REGEX)) {
error("Keine Release Version für Branch ${env.BRANCH_NAME}.")
}
} else {
if ( !(VERSION ==~ SNAPSHOT_REGEX) || !(serverVersion ==~ SNAPSHOT_REGEX) || !(clientVersion ==~ SNAPSHOT_REGEX)) {
error("Keine Snapshot Version für Branch ${env.BRANCH_NAME}.")
}
}
if( !(VERSION == serverVersion && VERSION == clientVersion)){
error("Versionen sind nicht identisch")
}
}
}
}
stage('Client') {
steps {
container("nodejs"){
script {
FAILED_STAGE=env.STAGE_NAME
sh 'npm --version'
dir('goofy-client') {
sh 'echo "registry=https://nexus.ozg-sh.de/repository/npm-proxy" >> ~/.npmrc'
sh 'echo "_auth=amVua2luczpQaihzX0ZNNFU5ZC8=" >> ~/.npmrc'
sh 'npm cache verify'
sh 'npm install --no-optional --legacy-peer-deps'
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
if (env.BRANCH_NAME == 'release') {
sh 'npm run ci-prodBuild'
}
else {
sh 'npm run ci-build'
}
sh 'npm run ci-test'
try {
if (env.BRANCH_NAME == 'master') {
withSonarQubeEnv('sonarqube-ozg-sh'){
sh 'npm run ci-sonar'
}
}
} catch (Exception e) {
unstable("SonarQube failed")
}
}
}
}
}
// post {
// always{
// junit testResults: 'goofy-client/test-report.xml', skipPublishingChecks: true
// }
// }
}
stage('Server') {
steps {
script {
FAILED_STAGE=env.STAGE_NAME
IMAGE_TAG = "${env.BRANCH_NAME}-${VERSION}"
container("maven-17"){
configFileProvider([configFile(fileId: 'maven-settings', variable: 'MAVEN_SETTINGS')]) {
sh 'mvn --version'
sh "mvn -s $MAVEN_SETTINGS -pl -goofy-client clean install spring-boot:build-image -Dspring-boot.build-image.imageName=docker.ozg-sh.de/goofy:${IMAGE_TAG} -Dspring-boot.build-image.publish -Dmaven.wagon.http.retryHandler.count=3"
try {
if (env.BRANCH_NAME == 'master') {
dir('goofy-server'){
withSonarQubeEnv('sonarqube-ozg-sh'){
sh 'mvn -s $MAVEN_SETTINGS sonar:sonar'
}
}
} catch (Exception e) {
unstable("SonarQube failed")
}
}
}
}
}
post {
always{
junit testResults: '**/target/surefire-reports/*.xml', skipPublishingChecks: true
}
}
}
}
container("k8s") {
configFileProvider([configFile(fileId: 'jenkins-kuby-kubeconfig', variable: 'KUBE_CONFIG')]) {
sh 'mkdir ~/.kube'
sh 'cp ${KUBE_CONFIG} ~/.kube/config'
}
stage('Deploy Maven Artifacts to Nexus') {
when {
anyOf {
branch 'master'
branch 'release'
}
}
steps {
script {
FAILED_STAGE = env.STAGE_NAME
}
container('maven-17') {
configFileProvider([configFile(fileId: 'maven-settings', variable: 'MAVEN_SETTINGS')]) {
sh 'mvn -s $MAVEN_SETTINGS -pl -goofy-client -DskipTests deploy'
}
}
}
}
stage('Deploy Goofy') {
when {
anyOf {
branch 'master'
branch 'release'
}
}
steps {
script {
FAILED_STAGE = env.STAGE_NAME
}
container("docker") {
script {
withCredentials([usernamePassword(credentialsId: 'jenkins-docker-login', usernameVariable: 'USER', passwordVariable: 'PASSWORD')]) {
sh 'docker login docker.ozg-sh.de -u ${USER} -p ${PASSWORD}'
sh "docker tag docker.ozg-sh.de/goofy:${IMAGE_TAG} docker.ozg-sh.de/goofy:latest"
sh 'docker push docker.ozg-sh.de/goofy:latest'
}
if (env.BRANCH_NAME == 'master') {
sh "docker tag docker.ozg-sh.de/goofy:${IMAGE_TAG} docker.ozg-sh.de/goofy:snapshot-latest"
sh 'docker push docker.ozg-sh.de/goofy:snapshot-latest'
}
}
}
}
}
}
stage('Rollout (kiel-dev & ea-dev) | (kiel-test $ sl-test) Goofy') {
when {
anyOf {
branch 'master'
branch 'release'
}
}
steps {
script {
FAILED_STAGE = env.STAGE_NAME
}
container("k8s"){
script {
if (env.BRANCH_NAME == 'master') {
sh 'kubectl rollout restart deployment/goofy -n sh-kiel-dev'
sh 'kubectl rollout status deployment/goofy -n sh-kiel-dev'
sh 'kubectl rollout restart deployment/goofy -n sh-ea-dev'
sh 'kubectl rollout status deployment/goofy -n sh-ea-dev'
}
}
}
}
}
stage('E2E') {
failFast false
parallel {
stage('E2E-EA') {
steps {
script {
def stageName = env.STAGE_NAME
def bezeichner = generateBezeichner(stageName)
startEnvironment(bezeichner, stageName, IMAGE_TAG, true)
def testResult = runTests(stageName, bezeichner, 'einheitlicher-ansprechpartner')
if(!testResult) {
error("Fehler in Stage ${stageName}")
}
}
post {
always {
script {
publishE2ETestResult("einheitlicher-ansprechpartner", "Goofy E2E-Tests EA")
}
}
}
}
stage('E2E-main') {
steps {
script {
def stageName = env.STAGE_NAME
def bezeichner = generateBezeichner(stageName)
startEnvironment(bezeichner, stageName, IMAGE_TAG, false)
def testResult = runTests(stageName, bezeichner, 'main-tests')
error("Fehler in Stage ${stageName}")
}
}
post {
always {
script {
publishE2ETestResult("main-tests", "Goofy E2E-Tests main")
}
}
}
}
}
post {
always {
script {
if (E2E_FAILED) {
FAILED_STAGE = "E2E (${E2E_FAILED.substring(0, E2E_FAILED.length() - 2)})"
error("Fehler in E2E-Tests")
}
}
}
}
}
if (env.BRANCH_NAME == 'master' || env.BRANCH_NAME == 'release') {
sendFailureMessage()
Void startEnvironment(String bezeichner, String stage, String imageTag, Boolean isEa) {
setupAnsible(imageTag, stage, isEa)
try {
deleteKopStack(bezeichner, stage)
} catch (Exception e) {
echo "deleteKopStack Exception"
}
rolloutKopStack(bezeichner, stage)
addKeycloakGroups(bezeichner, stage)
addKeycloakUser(bezeichner, stage)
Void setupAnsible(String imageTag, String stage, Boolean isEa) {
checkoutProvisioningRepo(stage)
if (env.BRANCH_NAME == 'release') {
copyTestEnvironmentToDev(stage)
}
editEnvironemntVersion(stage, imageTag, isEa)
if (isEa) {
setupEaEnvironment(stage)
}
setPlutoDatabasePassword(stage)
}
Void setAnsibleKubeConfig() {
container("ansible") {
configFileProvider([configFile(fileId: 'jenkins-kuby-kubeconfig', variable: 'KUBE_CONFIG')]) {
sh 'mkdir ~/.kube'
sh 'cp ${KUBE_CONFIG} ~/.kube/config'
}
}
withCredentials([usernamePassword(credentialsId: 'jenkins-gitea-access-token', passwordVariable: 'TOKEN', usernameVariable: 'USER')]) {
sh 'git clone https://${USER}:${TOKEN}@git.ozg-sh.de/mgm/provisioning.git'
// todo remove git checkout
dir('provisioning') {
sh 'git checkout ozg-2552-jenkins-e2e'
}
}
Void copyTestEnvironmentToDev(stage) {
dir("${stage}/provisioning") {
def devEnvFile = "playbook/inventory/versions/dev.yml"
def testEnvFile = "playbook/inventory/versions/test.yml"
def devVersions = readYaml file: devEnvFile
def testVersions = readYaml file: testEnvFile
devVersions.charts = testVersions.charts
devVersions.versions = testVersions.versions
writeYaml file: devEnvFile, data: devVersions, overwrite: true
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
Void editEnvironemntVersion(String stage, String imageTag, Boolean isEa) {
dir("${stage}/provisioning") {
def editFile = "playbook/inventory/versions/dev.yml"
def devVersions = readYaml file: editFile
overrideSpringProfiles = getSpringProfile(isEa)
devVersions.values.goofy.put('env', ['overrideSpringProfiles': overrideSpringProfiles])
devVersions.values.pluto.put('env', ['overrideSpringProfiles': overrideSpringProfiles])
devVersions.versions.goofy.image.tag = imageTag
writeYaml file: editFile, data: devVersions, overwrite: true
sh "cat ${editFile}"
}
}
String getSpringProfile(Boolean isEa) {
if (isEa) {
return "oc,ea,e2e,dev"
}
return "oc,e2e,dev"
}
Void setupEaEnvironment(String stage) {
dir("${stage}/provisioning") {
def editFile = "playbook/inventory/group_vars/all.yml"
def groupVars = readYaml file: editFile
groupVars.kop_einheitlicher_ansprechpartner = true
writeYaml file: editFile, data: groupVars, overwrite: true
}
}
Void setPlutoDatabasePassword(String stage) {
dir("${stage}/provisioning") {
def editFile = "playbook/inventory/versions/dev.yml"
def devVars = readYaml file: editFile
devVars.values.pluto.database.password = "XnHhfznNWg65NNd"
writeYaml file: editFile, data: devVars, overwrite: true
}
}
Void rolloutKopStack(String bezeichner, String stage) {
dir("${stage}/provisioning") {
def ansibleVars = """{"k8s_context":"ozg-dev", \
"kop_env":"dev", \
"kop_bezeichner":${bezeichner}, \
"kop_displayname":${bezeichner}, \
"kop_postfach_api_key":"", \
"install_afm_adapter":false, \
"install_fs_adapter":false, \
"external_db_enabled":false}"""
sh "ansible-playbook playbook/rollout.yml --extra-vars '${ansibleVars}'"
Void addKeycloakGroups(String bezeichner, String stage) {
container("ansible") {
def groupFiles = sh (script: 'ls goofy-client/apps/goofy-e2e/src/fixtures/group', returnStdout: true)
groupFiles.split("\\n").each { group ->
def groupJson = sh (script: "cat goofy-client/apps/goofy-e2e/src/fixtures/group/${group}", returnStdout: true)
def ansibleVars = """{"k8s_context":"ozg-dev", \
"kop_env":"dev", \
"keycloak_realm":"sh-${bezeichner}-dev", \
"group":${groupJson}
dir("${stage}/provisioning") {
sh "ansible-playbook playbook/add-keycloak-group.yml --extra-vars '${ansibleVars}'"
}
Void addKeycloakUser(String bezeichner, String stage) {
container("ansible") {
def userFiles = sh (script: 'ls goofy-client/apps/goofy-e2e/src/fixtures/user', returnStdout: true)
userFiles.split("\\n").each { user ->
def userJson = sh (script: "cat goofy-client/apps/goofy-e2e/src/fixtures/user/${user}", returnStdout: true)
def ansibleVars = """{"k8s_context":"ozg-dev", \
"kop_env":"dev", \
"keycloak_realm":"sh-${bezeichner}-dev", \
"user":${userJson}
}"""
dir("${stage}/provisioning") {
sh "ansible-playbook playbook/add-keycloak-user.yml --extra-vars '${ansibleVars}'"
}
}
}
}
Void deleteKopStack(String bezeichner, String stage) {
container("ansible") {
dir("${stage}/provisioning") {
def ansibleVars = """{"k8s_context":"ozg-dev", \
"kop_env":"dev", \
"kop_bezeichner":${bezeichner}}"""
sh "ansible-playbook playbook/delete-commune.yml --extra-vars '${ansibleVars}'"
}
}
Void publishE2ETestResult(String reportFolder, String reportName) {
publishHTML (
target: [
allowMissing: false,
alwaysLinkToLastBuild: false,
keepAll: true,
reportDir: "goofy-client/apps/goofy-e2e/reports/${reportFolder}",
reportFiles: 'report.html',
reportName: reportName
]
)
}
String runTests(String stageName, String bezeichner, String reportFolder) {
def configFile = generateCypressConfig(stageName, bezeichner, reportFolder)
try {
dir("goofy-client") {
sh "npm run cypress:version"
sh "npm run cypress:ci-run --CONFIG_FILE=${configFile} --REPORT_FOLDER=${reportFolder}"
return true
}
} catch (Exception e) {
sh "ls -l /root/.npm/_logs/*-debug.log"
sh "cat /root/.npm/_logs/*-debug.log"
}
}
}
String makeUrlConform(String input) {
return input.replaceAll(/[^a-zA-Z0-9]+/, "").toLowerCase()
def branchName = makeUrlConform(env.BRANCH_NAME)
def stageName = makeUrlConform(stage)
return "${cutBranchNameForKeycloakRealm(branchName, stageName)}${stageName}"
}
String cutBranchNameForKeycloakRealm(String branchName, String stageName) {
def maxKeycloakRealmLength = 30
def postPrefixLength = 8
def cutBranchNamePosition = maxKeycloakRealmLength - (branchName.length() + stageName.length() + postPrefixLength)
if(cutBranchNamePosition < 0) {
branchName = branchName[0..cutBranchNamePosition]
}
String generateCypressConfig(String stage, String bezeichner, String testFolder) {
def namespace = "sh-${bezeichner}-dev"
def configName = "cypress-ci-"+testFolder+".json"
dir('goofy-client/apps/goofy-e2e/'){
def config = readJSON file: 'cypress-ci.json'
config.baseUrl = "https://${bezeichner}.dev.ozg-sh.de" as String
config.env.dbUrl = "mongodb+srv://pluto-database-user:XnHhfznNWg65NNd@pluto-database-svc.${namespace}.svc.cluster.local/admin?ssl=false" as String
config.env.keycloakRealm = namespace as String
config.env.keycloakClient = namespace + "-goofy" as String
config.env.sabineUuid = getKeycloakUuid(namespace, "sabine") as String
config.integrationFolder = "./src/integration/${testFolder}" as String
config.videosFolder = "./reports/${testFolder}/videos" as String
config.screenshotsFolder = "./reports/${testFolder}/screenshots" as String
config.reporterOptions.reportDir = "./reports/${testFolder}/mochawesome-report" as String
writeJSON file: configName, json: config
return configName
}
String getKeycloakUuid(realm, userName) {
def shScript = """curl -H 'Content-Type: application/json' \
-H 'Authorization: bearer ${getKeycloakAccessToken()}' \
'https://sso.dev.ozg-sh.de/auth/admin/realms/${realm}/users'
"""
def users = readJSON text: sh(script: shScript, returnStdout: true)
for(user in users) {
}
}
}
String getKeycloakAccessToken() {
withCredentials([usernamePassword(credentialsId: 'keycloak-login', usernameVariable: 'USER', passwordVariable: 'PASSWORD')]) {
def token = readJSON text: sh (script: 'curl -d "client_id=admin-cli" -d "username=$USER" -d "password=$PASSWORD" -d "grant_type=password" "https://sso.dev.ozg-sh.de/auth/realms/master/protocol/openid-connect/token"', returnStdout: true)
return token.access_token
Void sendFailureMessage() {
def room = ''
def data = """{"msgtype":"m.text", \
"body":"Goofy: Build Failed. Stage: ${FAILED_STAGE} Build-ID: ${env.BUILD_NUMBER} Link: ${BLUE_OCEAN_URL}", \
"format": "org.matrix.custom.html", \
"formatted_body":"Goofy: Build Failed. Stage: ${FAILED_STAGE} Build-ID: <a href='${BLUE_OCEAN_URL}'>${env.BUILD_NUMBER}</a>"}"""
if (env.BRANCH_NAME == 'master') {
room = "!iQPAvQIiRwRpNOszjw:matrix.ozg-sh.de"
}
else if (env.BRANCH_NAME == 'release') {
room = "!oWZpUGTFsxkJIYNfYg:matrix.ozg-sh.de"
}
sh "curl -XPOST -H 'authorization: Bearer ${getElementAccessToken()}' -d '${data}' https://matrix.ozg-sh.de/_matrix/client/v3/rooms/$room/send/m.room.message"
}
String getElementAccessToken() {
withCredentials([string(credentialsId: 'element-login-json', variable: 'LOGIN_JSON')]) {
return readJSON ( text: sh (script: '''curl -XPOST -d \"$LOGIN_JSON\" https://matrix.ozg-sh.de/_matrix/client/v3/login''', returnStdout: true)).access_token
}
}
Void initHelmRepo() {
withCredentials([usernamePassword(credentialsId: 'jenkins-nexus-login', usernameVariable: 'USER', passwordVariable: 'PASSWORD')]) {
sh 'helm repo add ozg-base-apps-snapshot https://nexus.ozg-sh.de/repository/ozg-base-apps-snapshot --username ${USER} --password ${PASSWORD}'
sh 'helm repo add ozg-base-apps https://nexus.ozg-sh.de/repository/ozg-base-apps --username ${USER} --password ${PASSWORD}'
}