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'
}
}
}
}
}
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
stage('E2E') {
failFast false
parallel {
stage('E2E-EA') {
steps {
script {
def stageName = env.STAGE_NAME
def bezeichner = generateBezeichner(stageName)
def namespace = generateNamespaceName(bezeichner)
startEnvironment(namespace, 'ea-values.yaml', 'ea-values.yaml', IMAGE_TAG, bezeichner)
def testResult = runTests(stageName, 'einheitlicher-ansprechpartner')
shutdownEnvironment(namespace)
if(!testResult) {
E2E_FAILED += "${stageName}, "
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)
def namespace = generateNamespaceName(bezeichner)
startEnvironment(namespace, 'values.yaml', 'values.yaml', IMAGE_TAG, bezeichner)
def testResult = runTests(stageName, 'main-tests')
shutdownEnvironment(namespace)
if(!testResult) {
E2E_FAILED += "${stageName}, "
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 namespace, String goofyValues, String plutoValues, String imageTag, String bezeichner){
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
setupAnsible(imageTag)
rolloutKopStack(bezeichner)
addKeycloakGroups(bezeichner)
addKeycloakUser(bezeichner)
}
Void setupAnsible(String goofyImageTag) {
checkoutProvisioningRepo()
setGoofyImageVersion(goofyImageTag)
}
Void checkoutProvisioningRepo() {
withCredentials([usernamePassword(credentialsId: 'jenkins-gitea-access-token', passwordVariable: 'TOKEN', usernameVariable: 'USER')]) {
sh 'git clone https://${USER}:${TOKEN}@git.ozg-sh.de/mgm/provisioning.git'
sh 'git checkout ozg-2552'
}
}
Void setGoofyImageVersion(String goofyImageTag) {
dir('provisioning') {
def devVersions = readYaml file: "playbook/versions/dev.yaml"
devVersions.versions.goofy.image.tag = goofyImageTag
writeYaml file: "playbook/versions/dev.yaml", data: devVersions
sh "cat playbook/versions/dev.yaml"
}
}
Void rolloutKopStack(String bezeichner) {
container("ansible") {
dir('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}"
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
}
}
Void addKeycloakGroups(bezeichner) {
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}
}"""
sh "ansible-playbook playbook/rollout.yml --extra-vars ${ansibleVars}"
}
}
}
Void addKeycloakUser(String bezeichner) {
def userFiles = sh (script: 'ls goofy-client/apps/goofy-e2e/src/fixtures/user', returnStdout: true)
userFiles.split("\\n").each { user ->
def userJson = readJSON file: 'goofy-client/apps/goofy-e2e/src/fixtures/user/'+user
def ansibleVars = """{
"k8s_context": "ozg-dev",
"kop_env": "dev",
"keycloak_realm": "sh-${bezeichner}-dev",
"user": ${userJson}
}"""
sh "ansible-playbook playbook/rollout.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
]
)
}
Void checkIfNamespaceExists(String namespace) {
container("k8s") {
def namespaceList = sh (script: 'kubectl get namespaces', returnStdout: true)
if(namespaceList.contains(namespace)) {
Void deleteNamespace(String namespace) {
sh "kubectl delete namespace ${namespace}"
}
Void startPluto(String namespace, String values, String bezeichner) {
container("k8s") {
dir('goofy-client/apps/goofy-e2e/deployment-values/pluto') {
if(env.BRANCH_NAME == 'release') {
startPlutoForReleaseTests(namespace, values, bezeichner)
startPlutoForSnapshotTests(namespace, values, bezeichner)
}
sh "kubectl rollout status statefulset/pluto-database -n ${namespace}"
}
}
Void startPlutoForReleaseTests(String namespace, String values, String bezeichner) {
sh "helm upgrade --install --create-namespace pluto ozg-base-apps/pluto -f ${values} --set kop.bezeichner=${bezeichner} --namespace ${namespace} --wait --wait-for-jobs"
}
Void startPlutoForSnapshotTests(String namespace, String values, String bezeichner) {
sh "helm upgrade --install --create-namespace pluto ozg-base-apps-snapshot/pluto -f ${values} --set kop.bezeichner=${bezeichner} --namespace ${namespace} --version ${getLatestChartVersion('pluto').trim()} --wait --wait-for-jobs"
}
Void startGoofy(String namespace, String values, String imageTag, String bezeichner) {
container("k8s") {
dir('goofy-client/apps/goofy-e2e/deployment-values/goofy') {
if(env.BRANCH_NAME == 'release') {
startGoofyForReleaseTests(namespace, values, bezeichner, imageTag)
startGoofyForSnapshotTests(namespace, values, bezeichner, imageTag)
createKeycloakGroups(namespace)
generateKeycloakUserYaml(namespace)
applyKeycloakUser(namespace)
Void startGoofyForReleaseTests(String namespace, String values, String bezeichner, String imageTag) {
sh "helm upgrade --install --create-namespace goofy ozg-base-apps/goofy -f ${values} --set image.tag=${imageTag} --set kop.bezeichner=${bezeichner} --namespace ${namespace} --wait --wait-for-jobs"
}
Void startGoofyForSnapshotTests(String namespace, String values, String bezeichner, String imageTag) {
sh "helm upgrade --install --create-namespace goofy ozg-base-apps-snapshot/goofy -f ${values} --set image.tag=${imageTag} --set kop.bezeichner=${bezeichner} --namespace ${namespace} --version ${getLatestChartVersion('goofy').trim()} --wait --wait-for-jobs"
}
String getLatestChartVersion(String chart) {
container("k8s") {
return sh (script: "helm search repo ozg-base-apps-snapshot --devel -l -o json | jq -r 'first(.[] | select((.name==\"ozg-base-apps-snapshot/${chart}\") and (.version|match(\"SNAPSHOT\$\"))) | .version)'", returnStdout: true)
}
}
String runTests(String stageName, String reportFolder) {
try {
def configFile = generateCypressConfig(stageName, reportFolder)
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"
}
}
}
Void shutdownEnvironment(String namespace) {
container("k8s") {
sh "helm uninstall goofy --namespace ${namespace} --wait"
sh "helm uninstall pluto --namespace ${namespace} --wait"
sh "kubectl delete namespace ${namespace}"
}
}
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 generateNamespaceName(String bezeichner) {
def e2eUserFiles = sh (script: 'ls goofy-client/apps/goofy-e2e/src/fixtures/user', returnStdout: true)
def newUserYaml = readYaml file: "goofy-client/apps/goofy-e2e/deployment-values/goofy/user/user.yaml"
def userJson = readJSON file: 'goofy-client/apps/goofy-e2e/src/fixtures/user/'+user
newUserYaml.metadata.name = namespace + "-" + userJson.name
newUserYaml.metadata.labels.realm = namespace
newUserYaml.spec.realmSelector.matchLabels.realm = namespace
newUserYaml.spec.user.username = userJson.name
newUserYaml.spec.user.credentials = [[type: 'password', value: userJson.password]]
if(userJson.firstName) {
newUserYaml.spec.user.firstName = userJson.firstName
}
if(userJson.lastName) {
newUserYaml.spec.user.lastName = userJson.lastName
}
newUserYaml.spec.user.clientRoles = [(namespace+"-goofy"): userJson.clientRoles]
if(userJson.groups) {
newUserYaml.spec.user.groups = userJson.groups
}
dir (namespace) {
writeYaml file: userJson.name+".yaml", data: newUserYaml
}
}
}
Void createKeycloakGroups(String realm) {
def groupFiles = sh (script: 'ls goofy-client/apps/goofy-e2e/src/fixtures/group', returnStdout: true)
def groupJson = sh (script: "cat goofy-client/apps/goofy-e2e/src/fixtures/group/${group}", returnStdout: true)
sh """curl -X POST 'https://sso.dev.ozg-sh.de/auth/admin/realms/${realm}/groups' \
-H 'Content-Type: application/json' \
-H 'Authorization: bearer ${getKeycloakAccessToken()}' \
--data-raw '${groupJson}'
"""
}
}
Void applyKeycloakUser(String namespace) {
dir(namespace){
def kcUserFiles = sh (script: "ls", returnStdout: true)
kcUserFiles.split("\\n").each { user ->
sh "kubectl apply -f ${user}"
}
}
}
Void removeKeycloakUser(String namespace) {
def kcUserFiles = sh (script: "ls", returnStdout: true)
kcUserFiles.split("\\n").each { user ->
sh "kubectl delete -f ${user}"
}
}
}
String generateCypressConfig(String stage, String testFolder) {
def bezeichner = generateBezeichner(stage)
def namespace = generateNamespaceName(bezeichner)
def configName = "cypress-ci-"+testFolder+".json"
dir('goofy-client/apps/goofy-e2e/'){
def config = readJSON file: 'cypress-ci.json'
config.baseUrl = "https://${makeUrlConform(env.BRANCH_NAME)}${makeUrlConform(stage)}.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 waitForKeycloakClientCreation(realm) {
def shScript = """curl -H 'Content-Type: application/json' \
-H 'Authorization: bearer ${getKeycloakAccessToken()}' \
'https://sso.dev.ozg-sh.de/auth/admin/realms/${realm}/clients' \
| grep -q ${realm}-goofy
"""
while(sh(script: shScript, returnStatus: true)) {
sh 'sleep 5'
}
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}'
}