Skip to content
Snippets Groups Projects
Commit e2b1fb36 authored by Jan Zickermann's avatar Jan Zickermann
Browse files

Merge remote-tracking branch 'origin/master' into OZG-4799-SetupErrorReponseRFC7807

# Conflicts:
#	pom.xml
#	src/main/java/de/ozgcloud/admin/Root.java
#	src/test/java/de/ozgcloud/admin/AdministrationApplicationTest.java
#	src/test/java/de/ozgcloud/admin/web/controller/BasicAppInfoControllerTest.java
parents adb065ec 27f26278
Branches
Tags
No related merge requests found
pipeline {
agent {
node {
label 'ozgcloud-jenkins-build-agent-jdk21'
}
}
environment {
RELEASE_REGEX = /\d+.\d+.\d+/
SNAPSHOT_REGEX = /\d+.\d+.\d+-SNAPSHOT/
FAILED_STAGE = ""
SH_SUCCESS_STATUS_CODE = 0
IMAGE_TAG = generateImageTag()
HELM_CHART_VERSION = generateHelmChartVersion()
BUILD_PROFILE = getBuildProfile()
}
options {
timeout(time: 1, unit: 'HOURS')
disableConcurrentBuilds()
buildDiscarder(logRotator(numToKeepStr: '5'))
}
stages {
stage('Check Version') {
steps {
script {
FAILED_STAGE = env.STAGE_NAME
def version = getPomVersion()
if(isReleaseBranch()){
if ( !(version ==~ RELEASE_REGEX) ) {
error("Keine Release Version für Branch ${env.BRANCH_NAME}.")
}
} else {
if ( !(version ==~ SNAPSHOT_REGEX) ) {
error("Keine Snapshot Version für Branch ${env.BRANCH_NAME}.")
}
}
}
}
}
stage('Build Administration') {
steps {
script {
FAILED_STAGE=env.STAGE_NAME
}
configFileProvider([configFile(fileId: 'maven-settings', variable: 'MAVEN_SETTINGS')]) {
sh "mvn -s $MAVEN_SETTINGS clean install -Dmaven.wagon.http.retryHandler.count=3 -DelasticTests.disabled=true -Dbuild.number=$BUILD_NUMBER"
}
}
}
stage('Deploy to Nexus'){
when {
anyOf {
branch 'master'
branch 'release'
}
}
steps {
script {
FAILED_STAGE = env.STAGE_NAME
}
configFileProvider([configFile(fileId: 'maven-settings', variable: 'MAVEN_SETTINGS')]) {
sh 'mvn -s $MAVEN_SETTINGS -DskipTests deploy -Dmaven.wagon.http.retryHandler.count=3'
}
}
}
stage('Build and publish Docker image') {
steps {
script {
FAILED_STAGE=env.STAGE_NAME
}
configFileProvider([configFile(fileId: 'maven-settings', variable: 'MAVEN_SETTINGS')]) {
withCredentials([usernamePassword(credentialsId: 'jenkins-nexus-login', usernameVariable: 'USER', passwordVariable: 'PASSWORD')]) {
sh 'mvn -s $MAVEN_SETTINGS spring-boot:build-image -DskipTests -Dmaven.wagon.http.retryHandler.count=3 $BUILD_PROFILE -Ddocker.publishRegistry.username=${USER} -Ddocker.publishRegistry.password=${PASSWORD}'
}
}
}
}
stage('Sonar Checks') {
when {
branch 'master'
}
steps {
script {
FAILED_STAGE=env.STAGE_NAME
configFileProvider([configFile(fileId: 'maven-settings', variable: 'MAVEN_SETTINGS')]) {
try {
withSonarQubeEnv('sonarqube-ozg-sh'){
sh 'mvn -s $MAVEN_SETTINGS sonar:sonar'
}
} catch (Exception e) {
unstable("SonarQube failed")
}
}
}
}
}
stage ('OWASP Dependency-Check Vulnerabilities') {
steps {
dependencyCheck additionalArguments: '''
-o "./"
-s "./"
-f "ALL"
-d /dependency-check-data
--suppression dependency-check-supressions.xml
--disableKnownExploited
--noupdate
--disableArchive
--prettyPrint''', odcInstallation: 'dependency-check-owasp'
dependencyCheckPublisher pattern: 'dependency-check-report.xml'
}
}
stage('Trigger Dev rollout') {
when {
branch 'master'
}
steps {
script {
FAILED_STAGE = env.STAGE_NAME
cloneGitopsRepo()
setNewDevAdministrationVersion()
pushDevGitopsRepo()
}
}
}
}
post {
always{
junit testResults: '**/target/surefire-reports/*.xml', skipPublishingChecks: true
}
failure {
script {
if (isMasterBranch() || isReleaseBranch()) {
sendFailureMessage()
}
}
}
}
}
String generateImageTag() {
return "${env.BRANCH_NAME}-${getPomVersion()}"
}
String getPomVersion() {
def pom = readMavenPom file: 'pom.xml'
return pom.version
}
String getBuildProfile() {
if (isMasterBranch()) {
return "-P dev"
} else if (isReleaseBranch()) {
return "-P release"
} else {
return ""
}
}
Void sendFailureMessage() {
def room = getRoom()
def data = getFailureData()
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
}
}
String getFailureData() {
return """{"msgtype":"m.text", \
"body":"Administration: Build Failed. Stage: ${FAILED_STAGE} Build-ID: ${env.BUILD_NUMBER}", \
"format": "org.matrix.custom.html", \
"formatted_body":"Administration: Build Failed. Stage: ${FAILED_STAGE} Build-ID: ${env.BUILD_NUMBER}"}"""
}
String getRoom() {
if (isReleaseBranch()) {
return "!oWZpUGTFsxkJIYNfYg:matrix.ozg-sh.de"
} else {
return "!iQPAvQIiRwRpNOszjw:matrix.ozg-sh.de"
}
}
Void configureGit() {
def email = "jenkins@ozg-sh.de"
def name = "jenkins"
dir("gitops") {
sh "git config user.email '${email}'"
sh "git config user.name '${name}'"
}
}
Void cloneGitopsRepo() {
withCredentials([usernamePassword(credentialsId: 'jenkins-gitea-access-token', passwordVariable: 'TOKEN', usernameVariable: 'USER')]) {
sh 'git clone https://${USER}:${TOKEN}@git.ozg-sh.de/ozgcloud-devops/gitops.git'
}
configureGit()
}
Void setNewDevAdministrationVersion() {
setNewAdministrationGitopsVersion("dev")
}
Void setNewTestAdministrationVersion() {
setNewAdministrationGitopsVersion("test")
}
Void setNewAdministrationGitopsVersion(String environment) {
dir("gitops") {
def envFile = "${environment}/application/values/administration-values.yaml"
def envVersions = readYaml file: envFile
envVersions.administration.image.tag = IMAGE_TAG
envVersions.administration.helm.version = HELM_CHART_VERSION
writeYaml file: envFile, data: envVersions, overwrite: true
}
}
Void pushDevGitopsRepo() {
pushNewGitopsVersion('dev')
}
Void pushTestGitopsRepo() {
pushNewGitopsVersion('test')
}
Void pushNewGitopsVersion(String environment) {
dir('gitops') {
if (!hasAdministrationValuesFileChanged(environment)) {
return
}
withCredentials([usernamePassword(credentialsId: 'jenkins-gitea-access-token', passwordVariable: 'TOKEN', usernameVariable: 'USER')]) {
sh "git add ${environment}/application/values/administration-values.yaml"
sh "git commit -m 'jenkins rollout ${environment} administration version ${IMAGE_TAG}'"
sh 'git push https://${USER}:${TOKEN}@git.ozg-sh.de/ozgcloud-devops/gitops.git'
}
}
}
Boolean hasAdministrationValuesFileChanged(String environment) {
return sh (script: "git status | grep '${environment}/application/values/administration-values.yaml'", returnStatus: true) == env.SH_SUCCESS_STATUS_CODE as Integer
}
Boolean isMasterBranch() {
return env.BRANCH_NAME == 'master'
}
Boolean isReleaseBranch() {
return env.BRANCH_NAME == 'release'
}
String generateHelmChartVersion() {
def chartVersion = getPomVersion()
if (isMasterBranch()) {
chartVersion += "-${env.GIT_COMMIT.take(7)}"
}
else if (!isReleaseBranch()) {
chartVersion += "-${env.BRANCH_NAME}"
}
return chartVersion.replaceAll("_", "-")
}
\ No newline at end of file
......@@ -12,16 +12,16 @@
<groupId>de.ozgcloud</groupId>
<artifactId>administration</artifactId>
<version>1.0.0-SNAPSHOT</version>
<name>administration</name>
<name>Administration</name>
<description>Administration Backend Project</description>
<properties>
<java.version>21</java.version>
<!--<maven.compiler.source>21</maven.compiler.source>
<maven.compiler.target>21</maven.compiler.target>-->
<build.number>x</build.number>
<build.url>no-url</build.url>
<imageName>docker.ozg-sh.de/administration</imageName>
<build.number>SET_BY_JENKINS</build.number>
</properties>
<dependencies>
<!-- Spring -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
......@@ -35,6 +35,7 @@
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- Dev -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
......@@ -46,19 +47,68 @@
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<!-- Test -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<profiles>
<profile>
<id>dev</id>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<image>
<tags>${imageName}:snapshot-latest</tags>
</image>
</configuration>
</plugin>
</plugins>
</build>
</profile>
<profile>
<id>release</id>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<image>
<tags>${imageName}:latest</tags>
</image>
</configuration>
</plugin>
</plugins>
</build>
</profile>
</profiles>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-failsafe-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<image>
<name>${imageName}:${env.BRANCH_NAME}-${project.version}</name>
<publish>true</publish>
</image>
<docker>
<publishRegistry>
<username>${docker.publishRegistry.username}</username>
<password>${docker.publishRegistry.password}</password>
</publishRegistry>
</docker>
<excludes>
<exclude>
<groupId>org.projectlombok</groupId>
......@@ -75,7 +125,6 @@
<configuration>
<additionalProperties>
<number>${build.number}</number>
<url>${build.url}</url>
</additionalProperties>
</configuration>
</execution>
......
package de.ozgcloud.admin;
import java.time.Instant;
import lombok.Builder;
import lombok.Getter;
@Builder
@Getter
public class Root {
private String javaVersion;
private String buildVersion;
private Instant buildTime;
private String buildNumber;
}
package de.ozgcloud.admin;
import org.springframework.boot.info.BuildProperties;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import lombok.RequiredArgsConstructor;
@RestController
@RequiredArgsConstructor
@RequestMapping(RootController.PATH)
public class RootController {
static final String PATH = "/api"; // NOSONAR
private final BuildProperties buildProperties;
@GetMapping
public Root getRoot() {
return buildRoot();
}
private Root buildRoot() {
return Root.builder()
.javaVersion(System.getProperty("java.version"))
.buildTime(buildProperties.getTime())
.buildVersion(buildProperties.getVersion())
.buildNumber(buildProperties.get("number"))
.build();
}
}
package de.ozgcloud.admin;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
@SpringBootTest
class AdministrationApplicationTest {
@Test
void shouldSpringContextLoad() { // NOSONAR
}
}
package de.ozgcloud.admin;
import static org.mockito.Mockito.*;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.Spy;
import org.mockito.junit.jupiter.MockitoExtension;
import org.springframework.boot.info.BuildProperties;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.ResultActions;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import lombok.SneakyThrows;
@ExtendWith(MockitoExtension.class)
class RootControllerTest {
@Spy
@InjectMocks
private RootController rootController;
@Mock
private BuildProperties buildProperties;
private MockMvc mockMvc;
@BeforeEach
void mock() {
mockMvc = MockMvcBuilders.standaloneSetup(rootController).build();
}
@DisplayName("Root")
@Nested
class TestInfo {
@Test
@SneakyThrows
void shouldHaveJavaVersion() {
System.setProperty("java.version", RootTestFactory.JAVA_VERSION);
ResultActions result = doRequest();
result.andExpect(jsonPath("$.javaVersion").value(RootTestFactory.JAVA_VERSION));
}
@Test
@SneakyThrows
void shouldHaveVersion() {
when(buildProperties.getVersion()).thenReturn(RootTestFactory.BUILD_VERSION);
ResultActions result = doRequest();
result.andExpect(jsonPath("$.buildVersion").value(RootTestFactory.BUILD_VERSION));
}
@Test
@SneakyThrows
void shouldHaveBuildTime() {
when(buildProperties.getTime()).thenReturn(RootTestFactory.BUILD_TIME);
ResultActions result = doRequest();
result.andExpect(jsonPath("$.buildTime").value(RootTestFactory.BUILD_TIME.getEpochSecond()));
}
@Test
@SneakyThrows
void shouldHaveBuildNumber() {
when(buildProperties.get("number")).thenReturn(RootTestFactory.BUILD_NUMBER);
ResultActions result = doRequest();
result.andExpect(jsonPath("$.buildNumber").value(RootTestFactory.BUILD_NUMBER));
}
@SneakyThrows
private ResultActions doRequest() {
return mockMvc.perform(get(RootController.PATH)).andExpect(status().isOk());
}
}
}
package de.ozgcloud.admin;
import java.time.Instant;
import java.time.LocalDateTime;
import java.time.ZoneOffset;
public class RootTestFactory {
public static final Instant BUILD_TIME = LocalDateTime.parse("2021-04-01T10:30").toInstant(ZoneOffset.UTC);
public static final String JAVA_VERSION = "1";
public static final String BUILD_VERSION = "2";
public static final String BUILD_NUMBER = "3";
}
/*
* Copyright (c) 2024. Das Land Schleswig-Holstein vertreten durch das Ministerium für Energiewende, Klimaschutz, Umwelt und Natur
* Zentrales IT-Management
*
* Lizenziert unter der EUPL, Version 1.2 oder - sobald
* diese von der Europäischen Kommission genehmigt wurden -
* Folgeversionen der EUPL ("Lizenz");
* Sie dürfen dieses Werk ausschließlich gemäß
* dieser Lizenz nutzen.
* Eine Kopie der Lizenz finden Sie hier:
*
* https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12
*
* Sofern nicht durch anwendbare Rechtsvorschriften
* gefordert oder in schriftlicher Form vereinbart, wird
* die unter der Lizenz verbreitete Software "so wie sie
* ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN -
* ausdrücklich oder stillschweigend - verbreitet.
* Die sprachspezifischen Genehmigungen und Beschränkungen
* unter der Lizenz sind dem Lizenztext zu entnehmen.
*/
package de.ozgcloud.admin.web.controller;
import static org.mockito.Mockito.*;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
import java.time.Instant;
import java.time.LocalDateTime;
import java.time.ZoneOffset;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.Spy;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.ResultActions;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import de.ozgcloud.admin.service.BasicAppInfoService;
@SpringBootTest
class BasicAppInfoControllerTest {
final Instant sampleTime = LocalDateTime.parse("2021-04-01T10:30").toInstant(ZoneOffset.UTC);
final String javaVersion = "1";
final String buildVersion = "2";
final String buildNumber = "3";
final String buildUrl = "url://test";
@Spy
@InjectMocks
private BasicAppInfoController basicAppInfoController;
@Mock
private BasicAppInfoService basicAppInfoService;
private MockMvc mockMvc;
@BeforeEach
void mock() {
mockMvc = MockMvcBuilders.standaloneSetup(basicAppInfoController).build();
}
@DisplayName("Basic info")
@Nested
class TestInfo {
@Test
void shouldHaveJavaVersion() throws Exception {
when(basicAppInfoService.getJavaVersion()).thenReturn(javaVersion);
doRequest()
.andExpect(jsonPath("$.javaVersion").value(javaVersion));
}
@Test
void shouldHaveVersion() throws Exception {
when(basicAppInfoService.getVersion()).thenReturn(buildVersion);
doRequest()
.andExpect(jsonPath("$.buildVersion").value(buildVersion));
}
@Test
void shouldHaveBuildTime() throws Exception {
when(basicAppInfoService.getBuildTime()).thenReturn(sampleTime);
doRequest()
.andExpect(jsonPath("$.buildTime").value(sampleTime.getEpochSecond()));
}
@Test
void shouldHaveBuildNumber() throws Exception {
when(basicAppInfoService.getBuildNumber()).thenReturn(buildNumber);
doRequest()
.andExpect(jsonPath("$.buildNumber").value(buildNumber));
}
@Test
void shouldHaveBuildUrl() throws Exception {
when(basicAppInfoService.getBuildUrl()).thenReturn(buildUrl);
doRequest()
.andExpect(jsonPath("$.buildUrl").value(buildUrl));
}
private ResultActions doRequest() throws Exception {
return mockMvc.perform(get(BasicAppInfoController.PATH)).andExpect(status().isOk());
}
}
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment