diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..971748c69190d788a824bbf95ae4d995393a08b1 --- /dev/null +++ b/.gitignore @@ -0,0 +1,6 @@ +# Generated files +gen/ + +# IntelliJ +*.iml +.idea diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000000000000000000000000000000000000..334b6d42e77781ba2a56fc998010c432ce29c33b --- /dev/null +++ b/Dockerfile @@ -0,0 +1,7 @@ + +FROM scratch + +WORKDIR / +COPY main / +ADD config /config +CMD ["/main"] \ No newline at end of file diff --git a/Jenkinsfile b/Jenkinsfile new file mode 100644 index 0000000000000000000000000000000000000000..d865fd8d5b9d246bc3a8104157af8bcbd2a47bb2 --- /dev/null +++ b/Jenkinsfile @@ -0,0 +1,336 @@ +pipeline { + agent { + node { + label 'ozgcloud-jenkins-build-agent' + } + } + +tools { go 'go-1.22.0' } + environment { + RELEASE_REGEX = /v\d+.\d+.\d+/ + BETA_REGEX = /v\d+.\d+.\d+-beta.\d/ + FAILED_STAGE = "" + SH_SUCCESS_STATUS_CODE = 0 + } + + options { + timeout(time: 1, unit: 'HOURS') + disableConcurrentBuilds() + buildDiscarder(logRotator(numToKeepStr: '5')) + } + + stages { + stage('Get and Check Version') { + steps { + script { + FAILED_STAGE = env.STAGE_NAME + + // Read the file content + def fileContent = readFile('cmd/info-manager-proxy/main.go') + + // Define the regex pattern to match "version= <version>" + def versionPattern = /var version = \"(.*?)\"/ + + // Find the line that matches the pattern + def matcher = fileContent =~ versionPattern + if (matcher.find()) { + env.APP_VERSION = matcher[0][1].trim() + echo "Version found: ${env.APP_VERSION}" + } else { + error("Version not found in main.go") + } + + if(isReleaseBranch()){ + if ( !(env.APP_VERSION ==~ RELEASE_REGEX) ) { + error("Keine Release Version für Branch ${env.BRANCH_NAME}.") + } + } else { + if ( !(env.APP_VERSION ==~ BETA_REGEX) ) { + error("Keine Beta Version für Branch ${env.BRANCH_NAME}.") + } + } + } + } + } + + stage('Build Info-Manager-Proxy') { + steps { + script { + FAILED_STAGE=env.STAGE_NAME + + sh ''' + #export GOPATH so that installed dependecies could be found + export GOPATH=$GOROOT + + go mod download + go install \ + github.com/grpc-ecosystem/grpc-gateway/v2/protoc-gen-grpc-gateway \ + github.com/grpc-ecosystem/grpc-gateway/v2/protoc-gen-openapiv2 \ + google.golang.org/protobuf/cmd/protoc-gen-go \ + google.golang.org/grpc/cmd/protoc-gen-go-grpc + ''' + + sh ''' + curl -sSL "https://github.com/bufbuild/buf/releases/download/v1.34.0/buf-$(uname -s)-$(uname -m)" -o ./buf + chmod +x ./buf + ./buf generate + #to compile go statically with these tags, so that the binary is not dynamically linked and from scratch in Dockerfile will work + go build -tags osusergo,netgo cmd/info-manager-proxy/main.go + + ''' + } + } + } + + 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('Build and publish Docker image') { + steps { + script { + FAILED_STAGE=env.STAGE_NAME + + sh "docker build -t docker.ozg-sh.de/info-manager-proxy:${env.APP_VERSION} ." + + IMAGE_TAG = generateImageTag() + + tagAndPushDockerImage('info-manager-proxy', IMAGE_TAG) + + if (env.BRANCH_NAME == 'dev') { + tagAndPushDockerImage('info-manager-proxy', 'snapshot-latest') + } + else if (env.BRANCH_NAME == 'release') { + tagAndPushDockerImage('info-manager-proxy', 'latest') + } + } + } + } + + stage('Test, build and deploy Helm Chart') { + steps { + script { + FAILED_STAGE=env.STAGE_NAME + HELM_CHART_VERSION = generateHelmChartVersion() + + dir('src/main/helm') { + sh "helm lint -f ../../test/helm-linter-values.yaml" + + sh "helm unittest -f '../../test/helm/*.yaml' ." + + sh "helm package --version=${HELM_CHART_VERSION} ." + + deployHelmChart(HELM_CHART_VERSION) + } + } + } + } + + stage('Trigger Dev rollout') { + when { + branch 'dev' + } + steps { + script { + FAILED_STAGE = env.STAGE_NAME + + cloneGitopsRepo() + + setNewDevInfoManagerProxyVersion() + + pushDevGitopsRepo() + } + } + } + + stage('Trigger Test rollout') { + when { + branch 'release' + } + steps { + script { + FAILED_STAGE = env.STAGE_NAME + + cloneGitopsRepo() + + setNewTestInfoManagerProxyVersion() + + pushTestGitopsRepo() + } + } + } + } + +} + + +Void deployHelmChart(String helmChartVersion) { + withCredentials([usernamePassword(credentialsId: 'jenkins-nexus-login', usernameVariable: 'USERNAME', passwordVariable: 'PASSWORD')]){ + String fileName = '@info-manager-proxy-' + helmChartVersion + '.tgz' + result = sh script: '''curl -u $USERNAME:$PASSWORD https://nexus.ozg-sh.de/service/rest/v1/components?repository=''' + getHelmRepository() + ''' -F file=''' + fileName, returnStdout: true + + if (result != '') { + error(result) + } + } +} + +String getHelmRepository(){ + if (isReleaseBranch()) { + return 'ozg-base-apps'; + } + return 'ozg-base-apps-snapshot'; +} + +String generateImageTag() { + return "${env.APP_VERSION}-${env.BRANCH_NAME}" +} + + + +String getBuildProfile() { + if (isDevBranch()) { + 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":"Info-Manager-Proxy: Build Failed. Stage: ${FAILED_STAGE} Build-ID: ${env.BUILD_NUMBER}", \ + "format": "org.matrix.custom.html", \ + "formatted_body":"Info-Manager-Proxy: 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 setNewDevInfoManagerProxyVersion() { + setNewInfoManagerProxyGitopsVersion("dev") +} + +Void setNewTestInfoManagerProxyVersion() { + setNewInfoManagerProxyGitopsVersion("test") +} + +Void setNewInfoManagerProxyGitopsVersion(String environment) { + dir("gitops") { + def envFile = "${environment}/application/values/info-manager-proxy-values.yaml" + + def envVersions = readYaml file: envFile + + envVersions.info_manager_proxy.image.tag = IMAGE_TAG + envVersions.info_manager_proxy.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 (!hasInfoManagerProxyValuesFileChanged(environment)) { + return + } + + withCredentials([usernamePassword(credentialsId: 'jenkins-gitea-access-token', passwordVariable: 'TOKEN', usernameVariable: 'USER')]) { + sh "git add ${environment}/application/values/info-manager-proxy-values.yaml" + + sh "git commit -m 'jenkins rollout ${environment} info-manager-proxy version ${IMAGE_TAG}'" + sh 'git push https://${USER}:${TOKEN}@git.ozg-sh.de/ozgcloud-devops/gitops.git' + } + } +} + +Boolean hasInfoManagerProxyValuesFileChanged(String environment) { + return sh (script: "git status | grep '${environment}/application/values/info-manager-proxy-values.yaml'", returnStatus: true) == env.SH_SUCCESS_STATUS_CODE as Integer +} + +Boolean isDevBranch() { + return env.BRANCH_NAME == 'dev' +} + +Boolean isReleaseBranch() { + return env.BRANCH_NAME == 'release' +} + +String generateHelmChartVersion() { + def chartVersion = "${env.APP_VERSION}" + + if (isDevBranch()) { + chartVersion += "-${env.GIT_COMMIT.take(7)}" + } + else if (!isReleaseBranch()) { + chartVersion += "-${env.BRANCH_NAME}" + } + + return chartVersion.replaceAll("_", "-") +} +Void tagAndPushDockerImage(String imageName, String newTag){ + withCredentials([usernamePassword(credentialsId: 'jenkins-nexus-login', usernameVariable: 'USER', passwordVariable: 'PASSWORD')]) { + sh 'docker login docker.ozg-sh.de -u ${USER} -p ${PASSWORD}' + sh "docker tag docker.ozg-sh.de/${imageName}:${env.APP_VERSION} docker.ozg-sh.de/${imageName}:${newTag}" + sh "docker push docker.ozg-sh.de/${imageName}:${newTag}" + } +} \ No newline at end of file diff --git a/README.md b/README.md index 52e153d00eef9ce641c24777ec70bf48e4255008..f7fc1a17a064d81f68855aeedbe36baf7be649f9 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,46 @@ -# info-manager-proxy +# Info-manager-proxy +## Getting Started + +### Dependencies installieren + +``` +go mod download +``` + +``` +go install \ + github.com/grpc-ecosystem/grpc-gateway/v2/protoc-gen-grpc-gateway \ + github.com/grpc-ecosystem/grpc-gateway/v2/protoc-gen-openapiv2 \ + google.golang.org/protobuf/cmd/protoc-gen-go \ + google.golang.org/grpc/cmd/protoc-gen-go-grpc +``` + +### Buf installieren + +https://github.com/bufbuild/buf?tab=readme-ov-file + +### Protobuf-Code generieren + +``` +buf generate +``` + +### Anwendung starten + +``` +go run cmd/info-manager-proxy/main.go +``` + +## Config + +[Config-Datei](./config/config.yml) + +``` +server: + port: Port des HTTP Servers (int) +grpc: + mock: lokalen gRPC Server mocken (bool) + url: URL des Ziel-gRPC-Servers im host:port Format (string) +logging: + level: "ERROR" | "WARN" | "INFO" | "DEBUG" \ No newline at end of file diff --git a/api/gateway-config.yml b/api/gateway-config.yml new file mode 100644 index 0000000000000000000000000000000000000000..f482a49f78e3337e625f30c809a269195e62a8f2 --- /dev/null +++ b/api/gateway-config.yml @@ -0,0 +1,11 @@ +type: google.api.Service +config_version: 3 + +http: + rules: + - selector: de.ozgcloud.info.InformationService.GetInformation + post: /api/information + body: "*" + - selector: de.ozgcloud.info.InformationService.GetInformationById + post: /api/information/{id} + body: "*" diff --git a/api/proto/information.model.proto b/api/proto/information.model.proto new file mode 100644 index 0000000000000000000000000000000000000000..80fc9079b7fe632de470a4b313e3f60e5035e5b1 --- /dev/null +++ b/api/proto/information.model.proto @@ -0,0 +1,27 @@ +syntax = "proto3"; + +package de.ozgcloud.info; + +option go_package = "de.ozgcloud.info"; + +message GrpcInformationRequest { + string postfachId = 1; +} + +message GrpcInformationResponse { + repeated GrpcInformationNachricht nachrichten = 1; +} + +message GrpcInformationByIdRequest { + string id = 1; +} + +message GrpcInformationByIdResponse { + GrpcInformationNachricht nachricht = 1; +} + +message GrpcInformationNachricht { + string id = 1; + string postfachId = 2; + string ozgCloudAddress = 3; +} \ No newline at end of file diff --git a/api/proto/information.proto b/api/proto/information.proto new file mode 100644 index 0000000000000000000000000000000000000000..bd78bbc5e15ec2ab6bb3c411d9e92ad5103d6f1d --- /dev/null +++ b/api/proto/information.proto @@ -0,0 +1,12 @@ +syntax = "proto3"; + +package de.ozgcloud.info; + +import "information.model.proto"; + +option go_package = "de.ozgcloud.info"; + +service InformationService { + rpc GetInformation(GrpcInformationRequest) returns (GrpcInformationResponse); + rpc GetInformationById(GrpcInformationByIdRequest) returns (GrpcInformationByIdResponse); +} \ No newline at end of file diff --git a/buf.gen.yaml b/buf.gen.yaml new file mode 100644 index 0000000000000000000000000000000000000000..611034ed3c665110980e0ab16e84936152522a7a --- /dev/null +++ b/buf.gen.yaml @@ -0,0 +1,17 @@ +version: v1 +plugins: + - plugin: go + out: gen/go + opt: + - paths=source_relative + - plugin: go-grpc + out: gen/go + opt: + - paths=source_relative + - plugin: grpc-gateway + out: gen/go + opt: + - paths=source_relative + - grpc_api_configuration=api/gateway-config.yml + - plugin: openapiv2 + out: gen/openapiv2 \ No newline at end of file diff --git a/buf.yaml b/buf.yaml new file mode 100644 index 0000000000000000000000000000000000000000..ee350234c83408d0b2f11bc08c62e17e20344909 --- /dev/null +++ b/buf.yaml @@ -0,0 +1,7 @@ +version: v1beta1 +build: + roots: + - api/proto +deps: + - buf.build/googleapis/googleapis + - buf.build/grpc-ecosystem/grpc-gateway \ No newline at end of file diff --git a/cmd/info-manager-proxy/main.go b/cmd/info-manager-proxy/main.go new file mode 100644 index 0000000000000000000000000000000000000000..92a8e3cb96a30f0933f27ac6088ffb607bfb68f0 --- /dev/null +++ b/cmd/info-manager-proxy/main.go @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2023-2024 + * Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * 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 main + +import ( + "info-manager-proxy/internal/config" + "info-manager-proxy/internal/mock" + "info-manager-proxy/internal/server" +) + +var version = "v1.0.0-beta.1" + +func main() { + conf := config.LoadConfig() + + if conf.Grpc.Mock { + go mock.StartGrpcServer() + } + + server.StartHttpGateway(conf) +} diff --git a/config/config.yml b/config/config.yml new file mode 100755 index 0000000000000000000000000000000000000000..8605b248d11050d5a8ad3c71b3e41e20eb8993d6 --- /dev/null +++ b/config/config.yml @@ -0,0 +1,6 @@ +server: + port: 8082 +grpc: + mock: true +logging: + level: "INFO" \ No newline at end of file diff --git a/go.mod b/go.mod new file mode 100644 index 0000000000000000000000000000000000000000..849f6380956eb4bedc2fe5e39aa345e391c7fe14 --- /dev/null +++ b/go.mod @@ -0,0 +1,20 @@ +module info-manager-proxy + +go 1.22.0 + +require ( + github.com/davecgh/go-spew v1.1.1 // indirect + github.com/grpc-ecosystem/grpc-gateway/v2 v2.20.0 // indirect + github.com/kelseyhightower/envconfig v1.4.0 + github.com/pmezard/go-difflib v1.0.0 // indirect + github.com/stretchr/testify v1.9.0 // indirect + golang.org/x/net v0.26.0 // indirect + golang.org/x/sys v0.21.0 // indirect + golang.org/x/text v0.16.0 // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20240513163218-0867130af1f8 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20240513163218-0867130af1f8 // indirect + google.golang.org/grpc v1.64.0 // indirect + google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.4.0 // indirect + google.golang.org/protobuf v1.34.2 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect +) diff --git a/go.sum b/go.sum new file mode 100644 index 0000000000000000000000000000000000000000..96e321835b1641b097867e86d0d3f285cfbf5614 --- /dev/null +++ b/go.sum @@ -0,0 +1,33 @@ +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.20.0 h1:bkypFPDjIYGfCYD5mRBvpqxfYX1YCS1PXdKYWi8FsN0= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.20.0/go.mod h1:P+Lt/0by1T8bfcF3z737NnSbmxQAppXMRziHUxPOC8k= +github.com/kelseyhightower/envconfig v1.4.0 h1:Im6hONhd3pLkfDFsbRgu68RDNkGF1r3dvMUtDTo2cv8= +github.com/kelseyhightower/envconfig v1.4.0/go.mod h1:cccZRl6mQpaq41TPp5QxidR+Sa3axMbJDNb//FQX6Gg= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= +github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +golang.org/x/net v0.26.0 h1:soB7SVo0PWrY4vPW/+ay0jKDNScG2X9wFeYlXIvJsOQ= +golang.org/x/net v0.26.0/go.mod h1:5YKkiSynbBIh3p6iOc/vibscux0x38BZDkn8sCUPxHE= +golang.org/x/sys v0.21.0 h1:rF+pYz3DAGSQAxAu1CbC7catZg4ebC4UIeIhKxBZvws= +golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/text v0.15.0 h1:h1V/4gjBv8v9cjcR6+AR5+/cIYK5N/WAgiv4xlsEtAk= +golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= +golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4= +golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI= +google.golang.org/genproto/googleapis/api v0.0.0-20240513163218-0867130af1f8 h1:W5Xj/70xIA4x60O/IFyXivR5MGqblAb8R3w26pnD6No= +google.golang.org/genproto/googleapis/api v0.0.0-20240513163218-0867130af1f8/go.mod h1:vPrPUTsDCYxXWjP7clS81mZ6/803D8K4iM9Ma27VKas= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240513163218-0867130af1f8 h1:mxSlqyb8ZAHsYDCfiXN1EDdNTdvjUJSLY+OnAUtYNYA= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240513163218-0867130af1f8/go.mod h1:I7Y+G38R2bu5j1aLzfFmQfTcU/WnFuqDwLZAbvKTKpM= +google.golang.org/grpc v1.64.0 h1:KH3VH9y/MgNQg1dE7b3XfVK0GsPSIzJwdF617gUSbvY= +google.golang.org/grpc v1.64.0/go.mod h1:oxjF8E3FBnjp+/gVFYdWacaLDx9na1aqy9oovLpxQYg= +google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.4.0 h1:9SxA29VM43MF5Z9dQu694wmY5t8E/Gxr7s+RSxiIDmc= +google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.4.0/go.mod h1:yZOK5zhQMiALmuweVdIVoQPa6eIJyXn2B9g5dJDhqX4= +google.golang.org/protobuf v1.34.1 h1:9ddQBjfCyZPOHPUiPxpYESBLc+T8P3E+Vo4IbKZgFWg= +google.golang.org/protobuf v1.34.1/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= +google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg= +google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/info-manager-proxy/main.go b/info-manager-proxy/main.go new file mode 100644 index 0000000000000000000000000000000000000000..14bf7dd754fdb0b114797fb03c78818e7bbf09ef --- /dev/null +++ b/info-manager-proxy/main.go @@ -0,0 +1,19 @@ +package main + +import ( + "info-manager-proxy/internal/config" + "info-manager-proxy/internal/mock" + "info-manager-proxy/internal/server" +) + +var version = "v1.0.0-beta.1" + +func main() { + conf := config.LoadConfig() + + if conf.Grpc.Mock { + go mock.StartGrpcServer() + } + + server.StartHttpGateway(conf) +} diff --git a/internal/config/config.go b/internal/config/config.go new file mode 100644 index 0000000000000000000000000000000000000000..b246c3d63d3ad026e142c890f9532722f122358e --- /dev/null +++ b/internal/config/config.go @@ -0,0 +1,90 @@ +/* + * Copyright (C) 2023-2024 + * Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * 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 config + +import ( + "github.com/kelseyhightower/envconfig" + "gopkg.in/yaml.v3" + "log" + "os" + "regexp" + "testing" +) + +const ( + defaultFilePath = "config/config.yml" + testFilePath = "testdata/test_config.yml" + grpcUrlRegex = `^[A-Za-z\d-]+(\.[A-Za-z\d-]+){0,512}:\d{1,5}$` +) + +type Config struct { + Server struct { + Port int `yaml:"port" envconfig:"SERVER_PORT"` + } `yaml:"server"` + Grpc struct { + Mock bool `yaml:"mock" envconfig:"GRPC_MOCK"` + Url string `yaml:"url" envconfig:"GRPC_URL"` + } `yaml:"grpc"` + Logging struct { + Level string `yaml:"level" envconfig:"LOGGING_LEVEL"` + } `yaml:"logging"` +} + +func LoadConfig(configFilePath ...string) Config { + var config Config + + fp := defaultFilePath + if len(configFilePath) > 0 { + fp = configFilePath[0] + } else if testing.Testing() { + fp = testFilePath + } + + file, err := os.ReadFile(fp) + if err != nil { + log.Fatalf("FATAL: error reading YAML file: %v", err) + } + + err = yaml.Unmarshal(file, &config) + if err != nil { + log.Fatalf("FATAL: error unmarshalling YAML file: %v", err) + } + + err = envconfig.Process("", &config) + if err != nil { + log.Fatalf("FATAL: error reading env: %v", err) + } + + if len(config.Grpc.Url) > 0 && !ValidateGrpcUrl(config.Grpc.Url) { + log.Fatalf("FATAL: gRPC URL is not in host:port format") + } + + return config +} + +func ValidateGrpcUrl(grpcUrl string) bool { + return regexp.MustCompile(grpcUrlRegex).MatchString(grpcUrl) +} diff --git a/internal/config/config_test.go b/internal/config/config_test.go new file mode 100644 index 0000000000000000000000000000000000000000..38cd14245895c0ba67c51af3f41cd7d3faf899f9 --- /dev/null +++ b/internal/config/config_test.go @@ -0,0 +1,112 @@ +/* + * Copyright (C) 2023-2024 + * Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * 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 config + +import ( + "os" + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestLoadConfig(t *testing.T) { + t.Run("should load config from file", func(t *testing.T) { + config := LoadConfig() + + expectedConfig := Config{} + expectedConfig.Server.Port = 8080 + expectedConfig.Grpc.Url = "localhost:50051" + expectedConfig.Grpc.Mock = false + expectedConfig.Logging.Level = "DEBUG" + + assert.Equal(t, expectedConfig, config) + }) + + t.Run("should load config from env", func(t *testing.T) { + envVars := map[string]string{ + "SERVER_PORT": "9090", + "GRPC_URL": "localhost:99999", + "LOGGING_LEVEL": "ERROR", + } + + for key, value := range envVars { + assert.NoError(t, os.Setenv(key, value), "Setenv "+key+" should not return an error") + } + + config := LoadConfig("testdata/empty_test_config.yml") + + for key := range envVars { + assert.NoError(t, os.Unsetenv(key), "Unsetenv "+key+" should not return an error") + } + + expectedConfig := Config{} + expectedConfig.Server.Port = 9090 + expectedConfig.Grpc.Url = "localhost:99999" + expectedConfig.Grpc.Mock = false + expectedConfig.Logging.Level = "ERROR" + + assert.Equal(t, expectedConfig, config) + }) + + t.Run("should overwrite config with env", func(t *testing.T) { + assert.NoError(t, os.Setenv("SERVER_PORT", "9090"), "Setenv SERVER_PORT should not return an error") + + config := LoadConfig() + + assert.NoError(t, os.Unsetenv("SERVER_PORT"), "Unsetenv SERVER_PORT should not return an error") + + expectedConfig := Config{} + expectedConfig.Server.Port = 9090 + expectedConfig.Grpc.Url = "localhost:50051" + expectedConfig.Grpc.Mock = false + expectedConfig.Logging.Level = "DEBUG" + + assert.Equal(t, expectedConfig, config) + }) +} + +func TestValidateGrpcUrl(t *testing.T) { + testData := []struct { + grpcUrl string + expectedResult bool + }{ + {"localhost:56789", true}, + {"test.example.com:20", true}, + {"invalidUrl", false}, + {"%test%:2345", false}, + {"localhost:234567", false}, + {"example.com:", false}, + {":6789", false}, + {"example..com:8080", false}, + {"example.com:abc", false}, + } + + for _, data := range testData { + result := ValidateGrpcUrl(data.grpcUrl) + + assert.Equal(t, data.expectedResult, result) + } +} diff --git a/internal/config/testdata/empty_test_config.yml b/internal/config/testdata/empty_test_config.yml new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/internal/config/testdata/test_config.yml b/internal/config/testdata/test_config.yml new file mode 100644 index 0000000000000000000000000000000000000000..ea8925db9cb5af5c351a1af92d6fb7f878de4359 --- /dev/null +++ b/internal/config/testdata/test_config.yml @@ -0,0 +1,6 @@ +server: + port: 8080 +grpc: + url: "localhost:50051" +logging: + level: "DEBUG" \ No newline at end of file diff --git a/internal/logging/logger.go b/internal/logging/logger.go new file mode 100644 index 0000000000000000000000000000000000000000..aedbf57d1b6247f836c11b17f62c65e44e140854 --- /dev/null +++ b/internal/logging/logger.go @@ -0,0 +1,113 @@ +/* + * Copyright (C) 2023-2024 + * Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * 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 logging + +import ( + "info-manager-proxy/internal/config" + "fmt" + "log" + "os" + "sync" +) + +const ( + logLevelNumError = iota + logLevelNumWarning + logLevelNumInfo + logLevelNumDebug + LogLevelError = "ERROR" + LogLevelWarning = "WARN" + LogLevelInfo = "INFO" + LogLevelDebug = "DEBUG" +) + +type Logger struct { + BaseLogger *log.Logger + level int +} + +var ( + logger *Logger + once sync.Once +) + +func GetLogger() *Logger { + initializeLogger() + + return logger +} + +func initializeLogger() { + once.Do(func() { + logger = &Logger{ + BaseLogger: log.New(os.Stdout, "", log.LstdFlags), + level: getLoggingLevel(), + } + }) +} + +func getLoggingLevel() int { + logLevelMap := map[string]int{ + LogLevelError: logLevelNumError, + LogLevelWarning: logLevelNumWarning, + LogLevelInfo: logLevelNumInfo, + LogLevelDebug: logLevelNumDebug, + } + + conf := config.LoadConfig() + logLevel, exists := logLevelMap[conf.Logging.Level] + if !exists { + logLevel = logLevelMap[LogLevelInfo] + } + + return logLevel +} + +func (l *Logger) log(level int, prefix, format string, v ...any) { + if l.level >= level { + l.BaseLogger.Println(fmt.Sprintf("%v: %v", prefix, fmt.Sprintf(format, v...))) + } +} + +func (l *Logger) Fatal(format string, v ...any) { + l.BaseLogger.Fatalln(fmt.Sprintf("FATAL: "+format, v...)) +} + +func (l *Logger) Error(format string, v ...any) { + l.log(logLevelNumError, "ERROR", format, v...) +} + +func (l *Logger) Warning(format string, v ...any) { + l.log(logLevelNumWarning, "WARNING", format, v...) +} + +func (l *Logger) Info(format string, v ...any) { + l.log(logLevelNumInfo, "INFO", format, v...) +} + +func (l *Logger) Debug(format string, v ...any) { + l.log(logLevelNumDebug, "DEBUG", format, v...) +} diff --git a/internal/logging/logger_test.go b/internal/logging/logger_test.go new file mode 100644 index 0000000000000000000000000000000000000000..11eff2f85f749fc9bcd884ee9d57c7a9b13fe12d --- /dev/null +++ b/internal/logging/logger_test.go @@ -0,0 +1,82 @@ +/* + * Copyright (C) 2023-2024 + * Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * 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 logging + +import ( + "bytes" + "github.com/stretchr/testify/assert" + "log" + "testing" +) + +func setUpLogger(t *testing.T, level string, msg string) *bytes.Buffer { + logger := GetLogger() + + var buf bytes.Buffer + logger.BaseLogger.SetOutput(&buf) + + originalFlags := log.Flags() + t.Cleanup(func() { + log.SetOutput(nil) + log.SetFlags(originalFlags) + }) + + if level == LogLevelError { + logger.Error(msg) + } else if level == LogLevelWarning { + logger.Warning(msg) + } else if level == LogLevelInfo { + logger.Info(msg) + } else { + logger.Debug(msg) + } + + return &buf +} + +func TestError(t *testing.T) { + buf := setUpLogger(t, LogLevelError, "test error") + logOutput := buf.String() + assert.Contains(t, logOutput, "ERROR: test error") +} + +func TestWarning(t *testing.T) { + buf := setUpLogger(t, LogLevelWarning, "test warning") + logOutput := buf.String() + assert.Contains(t, logOutput, "WARNING: test warning") +} + +func TestInfo(t *testing.T) { + buf := setUpLogger(t, LogLevelInfo, "test info") + logOutput := buf.String() + assert.Contains(t, logOutput, "INFO: test info") +} + +func TestDebug(t *testing.T) { + buf := setUpLogger(t, LogLevelDebug, "test debug") + logOutput := buf.String() + assert.Empty(t, logOutput) +} diff --git a/internal/logging/testdata/test_config.yml b/internal/logging/testdata/test_config.yml new file mode 100644 index 0000000000000000000000000000000000000000..6021893b2e1982f4f8d4215707fa6300e2cd6e8e --- /dev/null +++ b/internal/logging/testdata/test_config.yml @@ -0,0 +1,6 @@ +server: + port: 8080 +grpc: + url: "localhost:50051" +logging: + level: "INFO" \ No newline at end of file diff --git a/internal/mock/globals.go b/internal/mock/globals.go new file mode 100644 index 0000000000000000000000000000000000000000..04a1d31ce771922c8e9270ea9eedcf0c68df7d34 --- /dev/null +++ b/internal/mock/globals.go @@ -0,0 +1,5 @@ +package mock + +import "info-manager-proxy/internal/logging" + +var logger = logging.GetLogger() diff --git a/internal/mock/grpc_server.go b/internal/mock/grpc_server.go new file mode 100644 index 0000000000000000000000000000000000000000..e4d5e9152b49e01fcfeae91ca511f4d42e1eb0cc --- /dev/null +++ b/internal/mock/grpc_server.go @@ -0,0 +1,56 @@ +package mock + +import ( + "context" + pb "info-manager-proxy/gen/go" + "fmt" + "google.golang.org/grpc" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" + "net" +) + +const GrpcMockPort = 50051 + +type server struct { + pb.UnimplementedInformationServiceServer +} + +func (s *server) GetInformation(ctx context.Context, in *pb.GrpcInformationRequest) (*pb.GrpcInformationResponse, error) { + if in.PostfachId == "" { + return nil, status.Error(codes.InvalidArgument, "PostfachId is missing") + } + + if in.PostfachId == "service_unavailable" { + return nil, status.Error(codes.Unavailable, "Unavailable") + } else if in.PostfachId == "internal_server_error" { + return nil, status.Error(codes.Internal, "Error") + } + + return &pb.GrpcInformationResponse{}, nil + } + +func (s *server) GetInformationById(ctx context.Context, in *pb.GrpcInformationByIdRequest) (*pb.GrpcInformationByIdResponse, error) { + if in.Id == "" { + return nil, status.Error(codes.InvalidArgument, "Id is missing") + } + + return &pb.GrpcInformationByIdResponse{}, nil +} + +func StartGrpcServer() *grpc.Server { + s := grpc.NewServer() + pb.RegisterInformationServiceServer(s, &server{}) + + lis, err := net.Listen("tcp", fmt.Sprintf(":%d", GrpcMockPort)) + if err != nil { + logger.Fatal("gRPC server failed to listen: %v", err) + } + + logger.Info("gRPC server listening on port %v", GrpcMockPort) + if err := s.Serve(lis); err != nil { + logger.Fatal("gRPC server failed to serve: %v", err) + } + + return s +} diff --git a/internal/mock/grpc_server_test.go b/internal/mock/grpc_server_test.go new file mode 100644 index 0000000000000000000000000000000000000000..5f1312bcd00c025c12f670080606740088604759 --- /dev/null +++ b/internal/mock/grpc_server_test.go @@ -0,0 +1,67 @@ +package mock + +import ( + "context" + pb "info-manager-proxy/gen/go" + "fmt" + "github.com/stretchr/testify/assert" + "google.golang.org/grpc" + "google.golang.org/grpc/credentials/insecure" + "testing" +) + +func TestStartGrpcServer(t *testing.T) { + setUpGrpcEnv := func() (pb.InformationServiceClient, func()) { + SetUpGrpcServer() + + conn, err := grpc.NewClient(fmt.Sprintf("localhost:%d", GrpcMockPort), grpc.WithTransportCredentials(insecure.NewCredentials())) + assert.NoError(t, err) + + cleanup := func() { + conn.Close() + } + client := pb.NewInformationServiceClient(conn) + + return client, cleanup + } + + t.Run("should have no error on get information with valid postfachId", func(t *testing.T) { + client, cleanUp := setUpGrpcEnv() + defer cleanUp() + + resp, err := client.GetInformation(context.Background(), &pb.GrpcInformationRequest{PostfachId: "testPostfachId"}) + + assert.NoError(t, err) + assert.NotNil(t, resp) + }) + + t.Run("should have error on get information with empty postfachId", func(t *testing.T) { + client, cleanUp := setUpGrpcEnv() + defer cleanUp() + + resp, err := client.GetInformation(context.Background(), &pb.GrpcInformationRequest{PostfachId: ""}) + + assert.Error(t, err) + assert.Nil(t, resp) + }) + + t.Run("should have no error on get information by id", func(t *testing.T) { + client, cleanUp := setUpGrpcEnv() + defer cleanUp() + + resp, err := client.GetInformationById(context.Background(), &pb.GrpcInformationByIdRequest{Id: "testId"}) + + assert.NoError(t, err) + assert.NotNil(t, resp) + }) + + t.Run("should have error on get information by id with empty id", func(t *testing.T) { + client, cleanUp := setUpGrpcEnv() + defer cleanUp() + + resp, err := client.GetInformationById(context.Background(), &pb.GrpcInformationByIdRequest{Id: ""}) + + assert.Error(t, err) + assert.Nil(t, resp) + }) +} \ No newline at end of file diff --git a/internal/mock/test_setup.go b/internal/mock/test_setup.go new file mode 100644 index 0000000000000000000000000000000000000000..7ed07c6aabc3068f6cbb0710357b1bcde89e3985 --- /dev/null +++ b/internal/mock/test_setup.go @@ -0,0 +1,15 @@ +package mock + +import ( + "sync" + "time" +) + +var setUpOnce sync.Once + +func SetUpGrpcServer() { + setUpOnce.Do(func() { + go StartGrpcServer() + time.Sleep(time.Second) // Wait for the server to start + }) +} diff --git a/internal/mock/testdata/test_config.yml b/internal/mock/testdata/test_config.yml new file mode 100644 index 0000000000000000000000000000000000000000..ea8925db9cb5af5c351a1af92d6fb7f878de4359 --- /dev/null +++ b/internal/mock/testdata/test_config.yml @@ -0,0 +1,6 @@ +server: + port: 8080 +grpc: + url: "localhost:50051" +logging: + level: "DEBUG" \ No newline at end of file diff --git a/internal/server/globals.go b/internal/server/globals.go new file mode 100644 index 0000000000000000000000000000000000000000..c07fe8addab19f2d50f8c66e9d1f440475424d8f --- /dev/null +++ b/internal/server/globals.go @@ -0,0 +1,5 @@ +package server + +import "info-manager-proxy/internal/logging" + +var logger = logging.GetLogger() diff --git a/internal/server/handler.go b/internal/server/handler.go new file mode 100644 index 0000000000000000000000000000000000000000..3d22eb8fc72bcd32d2e28f676fb07f87aeafdd13 --- /dev/null +++ b/internal/server/handler.go @@ -0,0 +1,78 @@ +/* + * Copyright (C) 2023-2024 + * Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * 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 server + +import ( + "context" + "fmt" + "github.com/grpc-ecosystem/grpc-gateway/v2/runtime" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" + "net/http" +) + +func RegisterHomeEndpoint(mux *runtime.ServeMux) { + err := mux.HandlePath("GET", "/", func(w http.ResponseWriter, r *http.Request, pathParams map[string]string) { + defer func() { + if err := recover(); err != nil { + logger.Error("failed to recover: %v", err) + http.Error(w, fmt.Sprintf("failed to recover: %v", err), http.StatusInternalServerError) + } + }() + }) + + if err != nil { + logger.Fatal("failed to register home endpoint: %v", err) + } +} + +func ErrorHandler(ctx context.Context, mux *runtime.ServeMux, marshaler runtime.Marshaler, w http.ResponseWriter, r *http.Request, err error) { + st, ok := status.FromError(err) + if !ok { + runtime.DefaultHTTPErrorHandler(ctx, mux, marshaler, w, r, err) + return + } + + statusCodeMap := map[codes.Code]int{ + codes.InvalidArgument: http.StatusBadRequest, + codes.AlreadyExists: http.StatusConflict, + codes.Unavailable: http.StatusServiceUnavailable, + } + + httpStatusCode := http.StatusInternalServerError + + if code, exists := statusCodeMap[st.Code()]; exists { + httpStatusCode = code + } + + w.Header().Set("Content-Type", marshaler.ContentType(r)) + w.WriteHeader(httpStatusCode) + + _, writeErr := w.Write([]byte(st.Message())) + if writeErr != nil { + logger.Fatal("failed to handle grpc error: %v", writeErr) + } +} diff --git a/internal/server/handler_test.go b/internal/server/handler_test.go new file mode 100644 index 0000000000000000000000000000000000000000..0ae84cce5bb076b3bc40dde058dada58e12389aa --- /dev/null +++ b/internal/server/handler_test.go @@ -0,0 +1,74 @@ +/* + * Copyright (C) 2023-2024 + * Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * 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 server + +import ( + "bytes" + "info-manager-proxy/internal/mock" + "fmt" + "github.com/stretchr/testify/assert" + "net/http" + "testing" +) + +func TestRegisterHomeEndpoint(t *testing.T) { + SetUpHttpGateway() + + resp, err := http.Get("http://localhost:8080/") + + assert.NoError(t, err) + assert.Equal(t, http.StatusOK, resp.StatusCode) +} + +func TestErrorHandler(t *testing.T) { + assertErroneousRequest := func(jsonData []byte, expectedStatusCode int) { + mock.SetUpGrpcServer() + SetUpHttpGateway() + + resp, err := http.Post(fmt.Sprintf("http://localhost:8080%v", RegisterInformationUrlPath), "application/json", bytes.NewBuffer(jsonData)) + + assert.NoError(t, err) + assert.Equal(t, expectedStatusCode, resp.StatusCode) + } + + t.Run("should have bad request error", func(t *testing.T) { + jsonData := []byte(`{}`) + + assertErroneousRequest(jsonData, http.StatusBadRequest) + }) + + t.Run("should have unavailable error", func(t *testing.T) { + jsonData := []byte(`{"postfachId": "service_unavailable"}`) + + assertErroneousRequest(jsonData, http.StatusServiceUnavailable) + }) + + t.Run("should have internal server error", func(t *testing.T) { + jsonData := []byte(`{"postfachId": "internal_server_error"}`) + + assertErroneousRequest(jsonData, http.StatusInternalServerError) + }) +} diff --git a/internal/server/http_gateway.go b/internal/server/http_gateway.go new file mode 100644 index 0000000000000000000000000000000000000000..d7919b83ece76829f4bcc47a6adb5c80a940c2cb --- /dev/null +++ b/internal/server/http_gateway.go @@ -0,0 +1,72 @@ +/* + * Copyright (C) 2023-2024 + * Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * 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 server + +import ( + "context" + "info-manager-proxy/internal/config" + "info-manager-proxy/internal/mock" + "fmt" + "google.golang.org/grpc/credentials/insecure" + "net/http" + + "github.com/grpc-ecosystem/grpc-gateway/v2/runtime" + "google.golang.org/grpc" + + pb "info-manager-proxy/gen/go" +) + +func StartHttpGateway(conf config.Config) *http.Server { + ctx := context.Background() + ctx, cancel := context.WithCancel(ctx) + defer cancel() + + grpcEndpoint := conf.Grpc.Url + if conf.Grpc.Mock { + grpcEndpoint = fmt.Sprintf("localhost:%d", mock.GrpcMockPort) + } + + mux := runtime.NewServeMux(runtime.WithErrorHandler(ErrorHandler)) + opts := []grpc.DialOption{grpc.WithTransportCredentials(insecure.NewCredentials())} + err := pb.RegisterInformationServiceHandlerFromEndpoint(ctx, mux, grpcEndpoint, opts) + if err != nil { + logger.Fatal("failed to start HTTP gateway: %v", err) + } + + RegisterHomeEndpoint(mux) + + httpServer := &http.Server{ + Addr: fmt.Sprintf(":%d", conf.Server.Port), + Handler: RequestLoggingMiddleware(mux), + } + + logger.Info("HTTP gateway listening on port %d", conf.Server.Port) + if err := httpServer.ListenAndServe(); err != nil { + logger.Fatal("HTTP gateway failed to serve: %v", err) + } + + return httpServer +} diff --git a/internal/server/http_gateway_test.go b/internal/server/http_gateway_test.go new file mode 100644 index 0000000000000000000000000000000000000000..68d875c04d4554e09d83eba7145506d069fcf68e --- /dev/null +++ b/internal/server/http_gateway_test.go @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2023-2024 + * Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * 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 server + +import ( + "bytes" + "info-manager-proxy/internal/mock" + "fmt" + "github.com/stretchr/testify/assert" + "net/http" + "testing" +) + +func TestStartHttpGateway(t *testing.T) { + mock.SetUpGrpcServer() + SetUpHttpGateway() + + jsonData := []byte(`{"postfachId": "testPostfachId"}}`) + resp, err := http.Post(fmt.Sprintf("http://localhost:8080%v", RegisterInformationUrlPath), "application/json", bytes.NewBuffer(jsonData)) + + assert.NoError(t, err) + assert.Equal(t, http.StatusOK, resp.StatusCode) +} diff --git a/internal/server/middleware.go b/internal/server/middleware.go new file mode 100644 index 0000000000000000000000000000000000000000..42aa86a1e203e1a929126b866c0f5f893e3b9a55 --- /dev/null +++ b/internal/server/middleware.go @@ -0,0 +1,73 @@ +/* + * Copyright (C) 2023-2024 + * Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * 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 server + +import ( + "bytes" + "fmt" + "io" + "net/http" +) + +const RegisterInformationUrlPath = "/api/information" + +type logResponseWriter struct { + http.ResponseWriter + statusCode int +} + +func (rsp *logResponseWriter) WriteHeader(statusCode int) { + rsp.statusCode = statusCode + rsp.ResponseWriter.WriteHeader(statusCode) +} + +func RequestLoggingMiddleware(h http.Handler) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + if r.URL.Path != RegisterInformationUrlPath { + h.ServeHTTP(w, r) + return + } + + body, err := io.ReadAll(r.Body) + if err != nil { + logger.Error("failed to read request body: %v", err) + http.Error(w, fmt.Sprintf("failed to read request body: %v", err), http.StatusBadRequest) + return + } + r.Body = io.NopCloser(bytes.NewReader(body)) + + logger.Debug("received request with body: %v", string(body)) + + lw := &logResponseWriter{w, http.StatusOK} + h.ServeHTTP(lw, r) + + if lw.statusCode == http.StatusOK { + logger.Debug("successfully handled request with body: %v", string(body)) + } else { + logger.Error("failed handling request with body: %v, status code: %d", string(body), lw.statusCode) + } + }) +} diff --git a/internal/server/middleware_test.go b/internal/server/middleware_test.go new file mode 100644 index 0000000000000000000000000000000000000000..7755e84fe80d3f0ec5bfa07b97a84e0b64d821f6 --- /dev/null +++ b/internal/server/middleware_test.go @@ -0,0 +1,57 @@ +/* + * Copyright (C) 2023-2024 + * Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * 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 server + +import ( + "bytes" + "info-manager-proxy/internal/mock" + "fmt" + "github.com/stretchr/testify/assert" + "log" + "net/http" + "testing" +) + +func TestRequestLoggingMiddleware(t *testing.T) { + mock.SetUpGrpcServer() + SetUpHttpGateway() + + var buf bytes.Buffer + logger.BaseLogger.SetOutput(&buf) + + originalFlags := log.Flags() + defer func() { + log.SetOutput(nil) + log.SetFlags(originalFlags) + }() + + jsonData := []byte(`{"postfachId": "testPostfachId"}`) + http.Post(fmt.Sprintf("http://localhost:8080%v", RegisterInformationUrlPath), "application/json", bytes.NewBuffer(jsonData)) + + logOutput := buf.String() + assert.Contains(t, logOutput, "received request with body: {\"postfachId\": \"testPostfachId\"}") + assert.Contains(t, logOutput, "successfully handled request with body: {\"postfachId\": \"testPostfachId\"}") +} diff --git a/internal/server/test_setup.go b/internal/server/test_setup.go new file mode 100644 index 0000000000000000000000000000000000000000..27d4afab6b909297c93b7ecddf2dd7fbb66e34c8 --- /dev/null +++ b/internal/server/test_setup.go @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2023-2024 + * Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * 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 server + +import ( + "info-manager-proxy/internal/config" + "sync" + "time" +) + +var setUpOnce sync.Once + +func SetUpHttpGateway() { + setUpOnce.Do(func() { + conf := config.LoadConfig() + + go StartHttpGateway(conf) + time.Sleep(time.Second) // Wait for the server to start + }) +} diff --git a/internal/server/testdata/test_config.yml b/internal/server/testdata/test_config.yml new file mode 100644 index 0000000000000000000000000000000000000000..6235966c8828f67d39820aeb96b70781ca9300f7 --- /dev/null +++ b/internal/server/testdata/test_config.yml @@ -0,0 +1,6 @@ +server: + port: 8080 +grpc: + mock: true +logging: + level: "DEBUG" \ No newline at end of file diff --git a/run_helm_test.sh b/run_helm_test.sh new file mode 100755 index 0000000000000000000000000000000000000000..2bae426e6327e0d169891a6cd4eec42cd5377d87 --- /dev/null +++ b/run_helm_test.sh @@ -0,0 +1,8 @@ +#!/bin/sh + +set -e +set -x + +helm template ./src/main/helm/ -f src/test/helm-linter-values.yaml +helm lint -f src/test/helm-linter-values.yaml ./src/main/helm/ +cd src/main/helm && helm unittest -f '../../test/helm/**/*test.yaml' . \ No newline at end of file diff --git a/src/main/helm/.helmignore b/src/main/helm/.helmignore new file mode 100644 index 0000000000000000000000000000000000000000..3d0dbe446770be5127eb719b695414c427b09715 --- /dev/null +++ b/src/main/helm/.helmignore @@ -0,0 +1 @@ +tests/ \ No newline at end of file diff --git a/src/main/helm/Chart.yaml b/src/main/helm/Chart.yaml new file mode 100644 index 0000000000000000000000000000000000000000..a14f8ea14586a4a0103444ed0e8145abba546d5c --- /dev/null +++ b/src/main/helm/Chart.yaml @@ -0,0 +1,6 @@ +apiVersion: v1 +appVersion: "v1.0.0-beta.1" +description: A Helm chart for Info-Manager-Proxy +name: info-manager-proxy +version: 0.0.0-MANAGED-BY-JENKINS +icon: https://simpleicons.org/icons/helm.svg diff --git a/src/main/helm/templates/_helpers.tpl b/src/main/helm/templates/_helpers.tpl new file mode 100644 index 0000000000000000000000000000000000000000..51debec777d8d5519d3a018b1b29f6077a1a9012 --- /dev/null +++ b/src/main/helm/templates/_helpers.tpl @@ -0,0 +1,42 @@ + +{{/* Default Labels: Helm recommended best-practice labels https://helm.sh/docs/chart_best_practices/labels/ */}} +{{- define "app.defaultLabels" }} +app.kubernetes.io/instance: info-manager-proxy +app.kubernetes.io/managed-by: {{ .Release.Service }} +app.kubernetes.io/name: {{ .Release.Name }} +app.kubernetes.io/namespace: {{ .Release.Namespace }} +app.kubernetes.io/part-of: ozgcloud +app.kubernetes.io/version: {{ .Chart.Version }} +helm.sh/chart: {{ printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" }} +app.kubernetes.io/component: info-manager-proxy +component: info-manager-proxy +{{- end -}} + +{{- define "app.matchLabels" }} +app.kubernetes.io/name: {{ .Release.Name }} +app.kubernetes.io/namespace: {{ .Release.Namespace }} +component: info-manager-proxy +{{- end -}} + + +{{- define "app.serviceAccountName" -}} +{{ printf "%s" ( (.Values.serviceAccount).name | default "info-manager-proxy-service-account" ) }} +{{- end -}} + +{{- define "app.getCustomList" -}} +{{- with (.Values.env).customList -}} +{{- if kindIs "map" . -}} +{{ include "app.dictToList" . }} +{{- else if kindIs "slice" . -}} +{{ . | toYaml }} +{{- end -}} +{{- end -}} +{{- end -}} + +{{- define "app.dictToList" -}} +{{- $customList := list -}} +{{- range $key, $value := . -}} +{{- $customList = append $customList (dict "name" $key "value" $value) }} +{{- end -}} +{{- $customList | toYaml -}} +{{- end -}} \ No newline at end of file diff --git a/src/main/helm/templates/deployment.yaml b/src/main/helm/templates/deployment.yaml new file mode 100644 index 0000000000000000000000000000000000000000..ecb6533d636e1a71a19ec9e2d443cb1139700c50 --- /dev/null +++ b/src/main/helm/templates/deployment.yaml @@ -0,0 +1,104 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: {{ .Release.Name }} + namespace: {{ .Release.Namespace }} + labels: + {{- include "app.defaultLabels" . | indent 4 }} +spec: + progressDeadlineSeconds: 600 + replicas: {{ .Values.replicaCount }} + revisionHistoryLimit: 10 + selector: + matchLabels: + {{- include "app.matchLabels" . | indent 6 }} + strategy: + rollingUpdate: + maxSurge: 1 + maxUnavailable: 0 + type: RollingUpdate + template: + metadata: + labels: + {{- include "app.defaultLabels" . | indent 8 }} + spec: + {{- if (.Values.serviceAccount).create }} + serviceAccountName: {{ include "app.serviceAccountName" . }} + {{- end }} + topologySpreadConstraints: + - maxSkew: 1 + topologyKey: kubernetes.io/hostname + whenUnsatisfiable: ScheduleAnyway + labelSelector: + matchLabels: + app.kubernetes.io/name: {{ .Release.Name }} + containers: + - env: + - name: SERVER_PORT + value: "8082" + - name: GRPC_MOCK + value: "{{ (.Values.grpc).mock | default false }}" + - name: GRPC_URL + value: "{{ required ".Values.grpc.url must be set" (.Values.grpc).url }}" + {{- with include "app.getCustomList" . }} +{{ . | indent 10 }} + {{- end }} + + image: "{{ .Values.image.repo }}/{{ .Values.image.name }}:{{ coalesce (.Values.image).tag "latest" }}" + imagePullPolicy: Always + name: info-manager-proxy + ports: + - containerPort: 8082 + name: 8082tcp1 + protocol: TCP + readinessProbe: + httpGet: + path: / + port: 8082 + scheme: HTTP + timeoutSeconds: 1 + periodSeconds: 10 + successThreshold: 1 + failureThreshold: 3 + startupProbe: + httpGet: + path: / + port: 8082 + scheme: HTTP + timeoutSeconds: 1 + periodSeconds: 10 + successThreshold: 1 + failureThreshold: 3 + resources: + {{- with .Values.resources }} +{{ toYaml . | indent 10 }} + {{- end }} + securityContext: + allowPrivilegeEscalation: false + privileged: false + readOnlyRootFilesystem: false + runAsNonRoot: true + {{- with (.Values.securityContext).runAsUser }} + runAsUser: {{ . }} + {{- end }} + {{- with (.Values.securityContext).runAsGroup }} + runAsGroup: {{ . }} + {{- end }} + stdin: true + terminationMessagePath: /dev/termination-log + terminationMessagePolicy: File + tty: true + + dnsConfig: {} + dnsPolicy: ClusterFirst + imagePullSecrets: + - name: {{ required "imagePullSecret must be set" .Values.imagePullSecret }} + + restartPolicy: Always + {{- with .Values.hostAliases }} + hostAliases: +{{ toYaml . | indent 8 }} + {{- end }} + schedulerName: default-scheduler + securityContext: {} + terminationGracePeriodSeconds: 30 diff --git a/src/main/helm/templates/network_policy.yaml b/src/main/helm/templates/network_policy.yaml new file mode 100644 index 0000000000000000000000000000000000000000..4d606e0eda30f43a0d1013840d8a47c704f04c9c --- /dev/null +++ b/src/main/helm/templates/network_policy.yaml @@ -0,0 +1,83 @@ +# +# Copyright (C) 2024 Das Land Schleswig-Holstein vertreten durch den +# Ministerpräsidenten des Landes Schleswig-Holstein +# Staatskanzlei +# Abteilung Digitalisierung und zentrales IT-Management der Landesregierung +# +# 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. +# + +{{- if not (.Values.networkPolicy).disabled }} +apiVersion: networking.k8s.io/v1 +kind: NetworkPolicy +metadata: + name: network-policy-info-manager-proxy + namespace: {{ .Release.Namespace }} +spec: + podSelector: + matchLabels: + component: info-manager-proxy + policyTypes: + - Ingress + - Egress + ingress: + - ports: + - port: 8082 + from: + - namespaceSelector: {} + podSelector: + matchLabels: + component: info-manager-server +{{- with (.Values.networkPolicy).additionalIngressConfigLocal }} +{{ toYaml . | indent 2 }} +{{- end }} +{{- with (.Values.networkPolicy).additionalIngressConfigGlobal }} +{{ toYaml . | indent 2 }} +{{- end }} + egress: + - to: + - namespaceSelector: + matchLabels: + kubernetes.io/metadata.name: {{ required "zufiManager.namespace must be set" (.Values.zufiManager).namespace }} + podSelector: + matchLabels: + component: zufi-server + ports: + - port: 9090 + protocol: TCP + - to: + - namespaceSelector: + matchLabels: + kubernetes.io/metadata.name: {{ required "networkPolicy.dnsServerNamespace must be set" (.Values.networkPolicy).dnsServerNamespace }} + ports: + - port: 53 + protocol: UDP + - port: 53 + protocol: TCP + - port: 5353 + protocol: UDP + - port: 5353 + protocol: TCP +{{- with (.Values.networkPolicy).additionalEgressConfigLocal }} +{{ toYaml . | indent 2 }} +{{- end }} +{{- with (.Values.networkPolicy).additionalEgressConfigGlobal }} +{{ toYaml . | indent 2 }} +{{- end }} + +{{- end }} \ No newline at end of file diff --git a/src/main/helm/templates/service.yaml b/src/main/helm/templates/service.yaml new file mode 100644 index 0000000000000000000000000000000000000000..c0cdae94bced93b2eadfdd06e1a342a4aace1292 --- /dev/null +++ b/src/main/helm/templates/service.yaml @@ -0,0 +1,16 @@ +apiVersion: v1 +kind: Service +metadata: + name: {{.Release.Name }} + namespace: {{ .Release.Namespace }} + labels: + {{- include "app.defaultLabels" . | indent 4 }} +spec: + type: ClusterIP + ports: + - name: 8082tcp1 + port: 8082 + targetPort: 8082 + protocol: TCP + selector: + {{- include "app.matchLabels" . | indent 4 }} diff --git a/src/main/helm/values.yaml b/src/main/helm/values.yaml new file mode 100644 index 0000000000000000000000000000000000000000..7d8b6730378c03b6635cc3f7e170cccc489af985 --- /dev/null +++ b/src/main/helm/values.yaml @@ -0,0 +1,11 @@ +replicaCount: 1 + + +image: + repo: docker.ozg-sh.de + name: info-manager-proxy + tag: latest # [default: latest] + + + + diff --git a/src/test/helm-linter-values.yaml b/src/test/helm-linter-values.yaml new file mode 100644 index 0000000000000000000000000000000000000000..f8f960bcba9422bd61e154d9d602843e654c1ad1 --- /dev/null +++ b/src/test/helm-linter-values.yaml @@ -0,0 +1,12 @@ +networkPolicy: + dnsServerNamespace: test-dns-namespace + + +zufiManager: + namespace: zufi + +imagePullSecret: ozgcloud-image-pull-secret +server: + port: 8080 +grpc: + url: "zufi.de:9090" \ No newline at end of file diff --git a/src/test/helm/deployment_defaults_labels_test.yaml b/src/test/helm/deployment_defaults_labels_test.yaml new file mode 100644 index 0000000000000000000000000000000000000000..e015d275a560860e11af8f0933918dc478215b6a --- /dev/null +++ b/src/test/helm/deployment_defaults_labels_test.yaml @@ -0,0 +1,75 @@ +# +# Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den +# Ministerpräsidenten des Landes Schleswig-Holstein +# Staatskanzlei +# Abteilung Digitalisierung und zentrales IT-Management der Landesregierung +# +# 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. +# + +suite: test deployment default labels +release: + name: info-manager-proxy + namespace: sh-helm-test +templates: + - templates/deployment.yaml +set: + imagePullSecret: image-pull-secret + server: + port: 8080 + grpc: + url: "zufi.de:9090" +tests: + - it: check metadata.labels + asserts: + - equal: + path: metadata.labels + value: + app.kubernetes.io/instance: info-manager-proxy + app.kubernetes.io/managed-by: Helm + app.kubernetes.io/name: info-manager-proxy + app.kubernetes.io/namespace: sh-helm-test + app.kubernetes.io/part-of: ozgcloud + app.kubernetes.io/version: 0.0.0-MANAGED-BY-JENKINS + helm.sh/chart: info-manager-proxy-0.0.0-MANAGED-BY-JENKINS + app.kubernetes.io/component: info-manager-proxy + component: info-manager-proxy + + - it: should set spec.selector.matchLabels + asserts: + - equal: + path: spec.selector.matchLabels + value: + app.kubernetes.io/name: info-manager-proxy + app.kubernetes.io/namespace: sh-helm-test + component: info-manager-proxy + + - it: should have correct deyploment spec.template.metadata.labels + asserts: + - equal: + path: spec.template.metadata.labels + value: + app.kubernetes.io/instance: info-manager-proxy + app.kubernetes.io/managed-by: Helm + app.kubernetes.io/name: info-manager-proxy + app.kubernetes.io/namespace: sh-helm-test + app.kubernetes.io/part-of: ozgcloud + app.kubernetes.io/version: 0.0.0-MANAGED-BY-JENKINS + helm.sh/chart: info-manager-proxy-0.0.0-MANAGED-BY-JENKINS + app.kubernetes.io/component: info-manager-proxy + component: info-manager-proxy diff --git a/src/test/helm/deployment_env_test.yaml b/src/test/helm/deployment_env_test.yaml new file mode 100644 index 0000000000000000000000000000000000000000..291459906207c5e4cceb50f29962d43e5e58ed6c --- /dev/null +++ b/src/test/helm/deployment_env_test.yaml @@ -0,0 +1,94 @@ +# +# Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den +# Ministerpräsidenten des Landes Schleswig-Holstein +# Staatskanzlei +# Abteilung Digitalisierung und zentrales IT-Management der Landesregierung +# +# 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. +# + +suite: test deployment container environments +templates: + - templates/deployment.yaml +set: + imagePullSecret: image-pull-secret +tests: + - it: check customList as list + set: + env.customList: + - name: my_test_environment_name + value: "A test value" + - name: test_environment + value: "B test value" + grpc: + url: "zufi.de:9090" + asserts: + - contains: + path: spec.template.spec.containers[0].env + content: + name: my_test_environment_name + value: "A test value" + - contains: + path: spec.template.spec.containers[0].env + content: + name: test_environment + value: "B test value" + - it: check customList as dict + set: + env.customList: + my_test_environment_name: "A test value" + test_environment: "B test value" + grpc: + url: "zufi.de:9090" + asserts: + - contains: + path: spec.template.spec.containers[0].env + content: + name: my_test_environment_name + value: "A test value" + - contains: + path: spec.template.spec.containers[0].env + content: + name: test_environment + value: "B test value" + + - it: check envs + set: + grpc: + url: "zufi.de:9090" + asserts: + - contains: + path: spec.template.spec.containers[0].env + content: + name: GRPC_URL + value: "zufi.de:9090" + - contains: + path: spec.template.spec.containers[0].env + content: + name: GRPC_MOCK + value: "false" + - contains: + path: spec.template.spec.containers[0].env + content: + name: SERVER_PORT + value: "8082" + - it: should fail template if grpc.url not set + set: + asserts: + - failedTemplate: + errormessage: ".Values.grpc.url must be set" \ No newline at end of file diff --git a/src/test/helm/deployment_imagepull_secret_test.yaml b/src/test/helm/deployment_imagepull_secret_test.yaml new file mode 100644 index 0000000000000000000000000000000000000000..81ceb259ad5793c38e3ba3bc692840b49ddf86bc --- /dev/null +++ b/src/test/helm/deployment_imagepull_secret_test.yaml @@ -0,0 +1,47 @@ +# +# Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den +# Ministerpräsidenten des Landes Schleswig-Holstein +# Staatskanzlei +# Abteilung Digitalisierung und zentrales IT-Management der Landesregierung +# +# 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. +# + +suite: test deployment image pull secret +release: + name: info-manager-proxy + namespace: sh-helm-test +templates: + - templates/deployment.yaml +set: + server: + port: 8080 + grpc: + url: "zufi.de:9090" +tests: + - it: should use correct imagePull secret + set: + imagePullSecret: ozgcloud-image-pull-secret + asserts: + - equal: + path: spec.template.spec.imagePullSecrets[0].name + value: ozgcloud-image-pull-secret + - it: should fail template if imagepullsecret not set + asserts: + - failedTemplate: + errormessage: imagePullSecret must be set \ No newline at end of file diff --git a/src/test/helm/deployment_resources_test.yaml b/src/test/helm/deployment_resources_test.yaml new file mode 100644 index 0000000000000000000000000000000000000000..965ad12a2d74d63fabe20106d8687af9e73f3a69 --- /dev/null +++ b/src/test/helm/deployment_resources_test.yaml @@ -0,0 +1,64 @@ +# +# Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den +# Ministerpräsidenten des Landes Schleswig-Holstein +# Staatskanzlei +# Abteilung Digitalisierung und zentrales IT-Management der Landesregierung +# +# 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. +# + +suite: test deployment container resources +release: + name: info-manager-proxy +templates: + - templates/deployment.yaml +set: + imagePullSecret: image-pull-secret + server: + port: 8080 + grpc: + url: "zufi.de:9090" + +tests: + - it: should generate resources when values set + set: + resources: + limits: + cpu: 11m + memory: 22Mi + requests: + cpu: 33m + memory: 44Mi + asserts: + - equal: + path: spec.template.spec.containers[0].resources.limits.cpu + value: 11m + - equal: + path: spec.template.spec.containers[0].resources.limits.memory + value: 22Mi + - equal: + path: spec.template.spec.containers[0].resources.requests.cpu + value: 33m + - equal: + path: spec.template.spec.containers[0].resources.requests.memory + value: 44Mi + - it: should not generate resources when values not set + asserts: + - isEmpty: + path: spec.template.spec.containers[0].resources + diff --git a/src/test/helm/deployment_test.yaml b/src/test/helm/deployment_test.yaml new file mode 100644 index 0000000000000000000000000000000000000000..108210c281cab1df1efa94cd4e4edfdfd8f8ea34 --- /dev/null +++ b/src/test/helm/deployment_test.yaml @@ -0,0 +1,201 @@ +# +# Copyright (C) 2024 Das Land Schleswig-Holstein vertreten durch den +# Ministerpräsidenten des Landes Schleswig-Holstein +# Staatskanzlei +# Abteilung Digitalisierung und zentrales IT-Management der Landesregierung +# +# 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. +# + +suite: deployment +release: + name: info-manager-proxy +templates: + - templates/deployment.yaml +set: + imagePullSecret: image-pull-secret + server: + port: 8080 + grpc: + url: "zufi.de:9090" + +tests: + - it: should have metadata values + asserts: + - isKind: + of: Deployment + - isAPIVersion: + of: apps/v1 + - equal: + path: metadata.name + value: "info-manager-proxy" + - equal: + path: metadata.namespace + value: "NAMESPACE" + - exists: + path: metadata.labels + - it: should have correct general settings + asserts: + - equal: + path: spec.progressDeadlineSeconds + value: 600 + - equal: + path: spec.replicas + value: 1 + - equal: + path: spec.revisionHistoryLimit + value: 10 + - exists: + path: spec.selector + - equal: + path: spec.selector.matchLabels["app.kubernetes.io/name"] + value: "info-manager-proxy" + - equal: + path: spec.selector.matchLabels["app.kubernetes.io/namespace"] + value: "NAMESPACE" + - equal: + path: spec.strategy.rollingUpdate.maxSurge + value: 1 + - equal: + path: spec.strategy.rollingUpdate.maxUnavailable + value: 0 + - equal: + path: spec.strategy.type + value: "RollingUpdate" + - it: should have correct template configuration + asserts: + - equal: + path: spec.template.metadata.labels["app.kubernetes.io/instance"] + value: "info-manager-proxy" + - equal: + path: spec.template.metadata.labels.component + value: "info-manager-proxy" + - equal: + path: spec.template.spec.topologySpreadConstraints[0].maxSkew + value: 1 + - equal: + path: spec.template.spec.topologySpreadConstraints[0].topologyKey + value: "kubernetes.io/hostname" + - equal: + path: spec.template.spec.topologySpreadConstraints[0].whenUnsatisfiable + value: "ScheduleAnyway" + - equal: + path: spec.template.spec.topologySpreadConstraints[0].labelSelector.matchLabels["app.kubernetes.io/name"] + value: "info-manager-proxy" + - isEmpty: + path: spec.template.spec.dnsConfig + - equal: + path: spec.template.spec.dnsPolicy + value: "ClusterFirst" + - equal: + path: spec.template.spec.restartPolicy + value: "Always" + - equal: + path: spec.template.spec.schedulerName + value: "default-scheduler" + - isEmpty: + path: spec.template.spec.securityContext + - equal: + path: spec.template.spec.terminationGracePeriodSeconds + value: 30 + - it: should have correct container configuration + asserts: + - equal: + path: spec.template.spec.containers[0].name + value: "info-manager-proxy" + - equal: + path: spec.template.spec.containers[0].image + value: "docker.ozg-sh.de/info-manager-proxy:latest" + - equal: + path: spec.template.spec.containers[0].imagePullPolicy + value: "Always" + - equal: + path: spec.template.spec.containers[0].ports[0].containerPort + value: 8082 + - equal: + path: spec.template.spec.containers[0].ports[0].name + value: "8082tcp1" + - equal: + path: spec.template.spec.containers[0].ports[0].protocol + value: "TCP" + + + - equal: + path: spec.template.spec.containers[0].securityContext.allowPrivilegeEscalation + value: false + - equal: + path: spec.template.spec.containers[0].securityContext.privileged + value: false + - equal: + path: spec.template.spec.containers[0].securityContext.readOnlyRootFilesystem + value: false + - equal: + path: spec.template.spec.containers[0].securityContext.runAsNonRoot + value: true + - equal: + path: spec.template.spec.containers[0].stdin + value: true + - equal: + path: spec.template.spec.containers[0].terminationMessagePath + value: "/dev/termination-log" + - equal: + path: spec.template.spec.containers[0].terminationMessagePolicy + value: "File" + - equal: + path: spec.template.spec.containers[0].tty + value: true + - equal: + path: spec.template.spec.containers[0].readinessProbe.failureThreshold + value: 3 + - equal: + path: spec.template.spec.containers[0].readinessProbe.httpGet.path + value: "/" + - equal: + path: spec.template.spec.containers[0].readinessProbe.httpGet.port + value: 8082 + - equal: + path: spec.template.spec.containers[0].readinessProbe.httpGet.scheme + value: "HTTP" + - equal: + path: spec.template.spec.containers[0].readinessProbe.periodSeconds + value: 10 + - equal: + path: spec.template.spec.containers[0].readinessProbe.successThreshold + value: 1 + - equal: + path: spec.template.spec.containers[0].readinessProbe.timeoutSeconds + value: 1 + - equal: + path: spec.template.spec.containers[0].startupProbe.httpGet.path + value: "/" + - equal: + path: spec.template.spec.containers[0].startupProbe.httpGet.port + value: 8082 + - equal: + path: spec.template.spec.containers[0].startupProbe.failureThreshold + value: 3 + + - equal: + path: spec.template.spec.containers[0].startupProbe.periodSeconds + value: 10 + - equal: + path: spec.template.spec.containers[0].startupProbe.successThreshold + value: 1 + - equal: + path: spec.template.spec.containers[0].startupProbe.timeoutSeconds + value: 1 diff --git a/src/test/helm/network_policy_test.yaml b/src/test/helm/network_policy_test.yaml new file mode 100644 index 0000000000000000000000000000000000000000..9decc82e2ed391269ec64c99028bf8c68626d786 --- /dev/null +++ b/src/test/helm/network_policy_test.yaml @@ -0,0 +1,234 @@ +# +# Copyright (C) 2024 Das Land Schleswig-Holstein vertreten durch den +# Ministerpräsidenten des Landes Schleswig-Holstein +# Staatskanzlei +# Abteilung Digitalisierung und zentrales IT-Management der Landesregierung +# +# 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. +# + +suite: network policy test +release: + namespace: by-helm-test +templates: + - templates/network_policy.yaml + + +tests: + - it: should match apiVersion + set: + zufiManager: + namespace: zufi + networkPolicy: + dnsServerNamespace: test-dns-namespace + asserts: + - isAPIVersion: + of: networking.k8s.io/v1 + + - it: should match kind + set: + zufiManager: + namespace: zufi + networkPolicy: + dnsServerNamespace: test-dns-namespace + asserts: + - isKind: + of: NetworkPolicy + + - it: should match metadata + set: + zufiManager: + namespace: zufi + networkPolicy: + dnsServerNamespace: test-dns-namespace + asserts: + - equal: + path: metadata + value: + name: network-policy-info-manager-proxy + namespace: by-helm-test + + - it: should generate spec + set: + zufiManager: + namespace: zufi + networkPolicy: + dnsServerNamespace: test-dns-namespace + asserts: + - equal: + path: spec + value: + egress: + - ports: + - port: 9090 + protocol: TCP + to: + - namespaceSelector: + matchLabels: + kubernetes.io/metadata.name: zufi + podSelector: + matchLabels: + component: zufi-server + - ports: + - port: 53 + protocol: UDP + - port: 53 + protocol: TCP + - port: 5353 + protocol: UDP + - port: 5353 + protocol: TCP + to: + - namespaceSelector: + matchLabels: + kubernetes.io/metadata.name: test-dns-namespace + ingress: + - from: + - namespaceSelector: {} + podSelector: + matchLabels: + component: info-manager-server + ports: + - port: 8082 + podSelector: + matchLabels: + component: info-manager-proxy + policyTypes: + - Ingress + - Egress + + - it: add ingress rule by values local + set: + zufiManager: + namespace: zufi + networkPolicy: + dnsServerNamespace: test-namespace-dns + additionalIngressConfigLocal: + - from: + - podSelector: + matchLabels: + component: client2 + asserts: + - contains: + path: spec.ingress + content: + from: + - podSelector: + matchLabels: + component: client2 + - it: add ingress rule by values global + set: + zufiManager: + namespace: zufi + networkPolicy: + dnsServerNamespace: test-namespace-dns + additionalIngressConfigGlobal: + - from: + - podSelector: + matchLabels: + component: client2 + asserts: + - contains: + path: spec.ingress + content: + from: + - podSelector: + matchLabels: + component: client2 + + - it: add egress rules by values local + set: + zufiManager: + namespace: zufi + networkPolicy: + dnsServerNamespace: test-dns-namespace + additionalEgressConfigLocal: + - to: + - ipBlock: + cidr: 1.2.3.4/32 + asserts: + - contains: + path: spec.egress + content: + to: + - ipBlock: + cidr: 1.2.3.4/32 + - it: add egress rules by values Global + set: + zufiManager: + namespace: zufi + networkPolicy: + dnsServerNamespace: test-dns-namespace + additionalEgressConfigGlobal: + - to: + - ipBlock: + cidr: 1.2.3.4/32 + asserts: + - contains: + path: spec.egress + content: + to: + - ipBlock: + cidr: 1.2.3.4/32 + + - it: test network policy disabled + set: + networkPolicy: + disabled: true + asserts: + - hasDocuments: + count: 0 + + - it: test network policy unset should be disabled + set: + zufiManager: + namespace: zufi + networkPolicy: + disabled: false + dnsServerNamespace: test-dns-namespace + asserts: + - hasDocuments: + count: 1 + - it: test dnsServerNamespace must be set message + set: + zufiManager: + namespace: zufi + networkPolicy: + disabled: false + asserts: + - failedTemplate: + errorMessage: networkPolicy.dnsServerNamespace must be set + + - it: test zufiManager Namespace must be set message + set: + networkPolicy: + disabled: false + dnsServerNamespace: test-dns-server-namespace + asserts: + - failedTemplate: + errorMessage: zufiManager.namespace must be set + + - it: test network policy should be enabled by default + set: + zufiManager: + namespace: zufi + networkPolicy: + dnsServerNamespace: test-dns-server-namespace + asserts: + - hasDocuments: + count: 1 \ No newline at end of file diff --git a/src/test/helm/service_test.yaml b/src/test/helm/service_test.yaml new file mode 100644 index 0000000000000000000000000000000000000000..bb8ebd7716a220dbd0a426dd74a696229169f857 --- /dev/null +++ b/src/test/helm/service_test.yaml @@ -0,0 +1,77 @@ +# +# Copyright (C) 2022 Das Land Schleswig-Holstein vertreten durch den +# Ministerpräsidenten des Landes Schleswig-Holstein +# Staatskanzlei +# Abteilung Digitalisierung und zentrales IT-Management der Landesregierung +# +# 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. +# + +suite: test service +release: + name: info-manager-proxy + namespace: sh-helm-test +templates: + - templates/service.yaml +tests: + - it: should have the label component with correct value + asserts: + - isKind: + of: Service + - isAPIVersion: + of: v1 + - equal: + path: metadata.labels.component + value: info-manager-proxy + - it: should be of type ClusterIP + asserts: + - equal: + path: spec.type + value: ClusterIP + - it: ports should contain the grpc port + asserts: + - contains: + path: spec.ports + content: + name: 8082tcp1 + port: 8082 + targetPort: 8082 + protocol: TCP + count: 1 + any: true + + - it: selector should contain the component label with correct value + asserts: + - equal: + path: spec.selector.component + value: info-manager-proxy + + - it: selector should contain helm recommended labels name and namespace + asserts: + - equal: + path: spec.selector + value: + app.kubernetes.io/name: info-manager-proxy + app.kubernetes.io/namespace: sh-helm-test + component: info-manager-proxy + + - it: check component label for service + asserts: + - equal: + path: metadata.labels["component"] + value: info-manager-proxy \ No newline at end of file