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/
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 install --no-optional'
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'
}
}
}
}
}
219
220
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
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){
checkIfNamespaceExists(namespace)
parallel(
startGoofy: {
}
)
}
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)) {
error("Namespace: ${namespace} existiert bereits")
}
}
}
Void startPluto(String namespace, String values, String bezeichner) {
container("k8s") {
dir('goofy-client/apps/goofy-e2e/deployment-values/pluto') {
withCredentials([usernamePassword(credentialsId: 'jenkins-nexus-login', usernameVariable: 'USER', passwordVariable: 'PASSWORD')]) {
sh "helm upgrade --install --create-namespace pluto pluto -f ${values} --set kop.bezeichner=${bezeichner} --namespace ${namespace} --version 0.12.0-SNAPSHOT" + ' --repo https://nexus.ozg-sh.de/repository/ozg-base-apps-snapshot --username ${USER} --password ${PASSWORD} --wait --wait-for-jobs'
}
}
sh "kubectl rollout status statefulset/pluto-database -n ${namespace}"
}
}
Void startGoofy(String namespace, String values, String imageTag, String bezeichner) {
container("k8s") {
dir('goofy-client/apps/goofy-e2e/deployment-values/goofy') {
withCredentials([usernamePassword(credentialsId: 'jenkins-nexus-login', usernameVariable: 'USER', passwordVariable: 'PASSWORD')]) {
sh "helm upgrade --install --create-namespace goofy goofy -f ${values} --set image.tag=${imageTag} --set kop.bezeichner=${bezeichner} --namespace ${namespace} --version 0.14.0-SNAPSHOT" + ' --repo https://nexus.ozg-sh.de/repository/ozg-base-apps-snapshot --username ${USER} --password ${PASSWORD} --wait --wait-for-jobs'
createKeycloakGroups(namespace)
generateKeycloakUserYaml(namespace)
applyKeycloakUser(namespace)
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)
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 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
}