diff --git a/cross_cutting/k8s/basic_example/README.adoc b/cross_cutting/k8s/basic_example/README.adoc new file mode 100644 index 0000000..27b9312 --- /dev/null +++ b/cross_cutting/k8s/basic_example/README.adoc @@ -0,0 +1,93 @@ +== Run Example + +UPDATE + +In this example, a server (Original Server) inside the cluster connects to a database inside the cluster. To access data of the original server, another server(Access Server) inside the cluster is used. The aceess server can be accessed from outside the cluster. The access server will communicate to the original server to retrieve data and sends it to the user. + +image::./docs/img/ClusterOverviewSimple.drawio.svg["ClusterOverviewSimple"] + +image::./docs/img/ClusterOverview.png["ClusterOverview"] + +=== Requirements + +- link:https://maven.apache.org/download.cgi[Maven] installed +- link:https://taskfile.dev/installation/[Taskfile] installed +- link:https://rancherdesktop.io/[Rancher Desktop] installed with link:https://github.com/devonfw/ide[DevonIDE] +- locale Kubernetes Cluster started +- Behind VPN's activate wsl-vpnkit + +=== Installation +[tabs] +==== +Docker:: ++ +-- + - Run `task deployDocker` +-- +Nerdctl:: ++ +-- + - Run `task deployNerd` +-- +Docker behind VPN:: ++ +-- + - Run `task deployDockerVPN` +-- +Nerdctl behind VPN:: ++ +-- + - Run `task deployNerdVPN` +-- +==== + +=== Uninstallation +[tabs] +==== +Docker:: ++ +-- + - Run `task removeDocker` +-- +Nerdctl:: ++ +-- + - Run `task removeNerd` +-- +Docker behind VPN:: ++ +-- + - Run `task removeDockerVPN` +-- +Nerdctl behind VPN:: ++ +-- + - Run `task removeNerdVPN` +-- +==== + +=== Using VPN +Using inside a VPN is currently only working with a work around: +When running `kubectl` commands prepend `wsl` + +1. Start Rancher Desktop with vpn disabled +2. Enable VPN +3. Test connection with `wsl kubectl get all` + +=== What happened + +1. For the original and access Server, a Dockerimage got created into a locale image repository +2. The helm chart inside openapi_helm_chart got installed +3. The Server and Database started and connected to each other +4. When using a vpn the helm commands got routed through the wsl vpn kit + +=== Test +At `http://localhost:5030/guest` there is a list of invited guests + +If localhost is not working run + +``` +kubectl get servie +``` + +Use the External Ip of the `openapi-server-access-service` instead of localhost diff --git a/cross_cutting/k8s/basic_example/Taskfile.yml b/cross_cutting/k8s/basic_example/Taskfile.yml new file mode 100644 index 0000000..ce4e0c7 --- /dev/null +++ b/cross_cutting/k8s/basic_example/Taskfile.yml @@ -0,0 +1,59 @@ +version: '3' + +tasks: + deployNerd: + cmds: + - mvn -f ./server/access_server/pom.xml clean install + - mvn -f ./server/original_server/pom.xml clean install + - nerdctl build -t original-server-spring --namespace k8s.io ./server/original_server/ + - nerdctl build -t access-server-spring --namespace k8s.io ./server/access_server/ + - helm install openapi-example ./openapi_helm_chart/ + + removeNerd: + cmds: + - nerdctl image rm original-server-spring --namespace k8s.io -f + - nerdctl image rm access-server-spring --namespace k8s.io -f + - helm uninstall openapi-example + + deployDocker: + cmds: + - mvn -f ./server/access_server/pom.xml clean install + - mvn -f ./server/original_server/pom.xml clean install + - docker build -t original-server-spring ./server/original_server/ + - docker build -t access-server-spring ./server/access_server/ + - helm install openapi-example ./openapi_helm_chart/ + + removeDocker: + cmds: + - docker image rm original-server-spring -f + - docker image rm access-server-spring -f + - helm uninstall openapi-example + + + deployNerdVPN: + cmds: + - mvn -f ./server/access_server/pom.xml clean install + - mvn -f ./server/original_server/pom.xml clean install + - nerdctl build -t original-server-spring --namespace k8s.io ./server/original_server/ + - nerdctl build -t access-server-spring --namespace k8s.io ./server/access_server/ + - wsl helm install openapi-example ./openapi_helm_chart/ + + removeNerdVPN: + cmds: + - nerdctl image rm original-server-spring --namespace k8s.io -f + - nerdctl image rm access-server-spring --namespace k8s.io -f + - wsl helm uninstall openapi-example + + deployDockerVPN: + cmds: + - mvn -f ./server/access_server/pom.xml clean install + - mvn -f ./server/original_server/pom.xml clean install + - docker build -t original-server-spring ./server/original_server/ + - docker build -t access-server-spring ./server/access_server/ + - wsl helm install openapi-example ./openapi_helm_chart/ + + removeDockerVPN: + cmds: + - docker image rm original-server-spring -f + - docker image rm access-server-spring -f + - wsl helm uninstall openapi-example \ No newline at end of file diff --git a/cross_cutting/k8s/basic_example/docs/img/ClusterOverview.png b/cross_cutting/k8s/basic_example/docs/img/ClusterOverview.png new file mode 100644 index 0000000..798f2d5 Binary files /dev/null and b/cross_cutting/k8s/basic_example/docs/img/ClusterOverview.png differ diff --git a/cross_cutting/k8s/basic_example/docs/img/ClusterOverviewSimple.drawio.svg b/cross_cutting/k8s/basic_example/docs/img/ClusterOverviewSimple.drawio.svg new file mode 100644 index 0000000..f4fa018 --- /dev/null +++ b/cross_cutting/k8s/basic_example/docs/img/ClusterOverviewSimple.drawio.svg @@ -0,0 +1,4 @@ + + + +
k8s cluster
k8s cluster
Access Server
Access Server
Original Server
Original Server
Database
Database
Actor
Actor
Text is not SVG - cannot display
\ No newline at end of file diff --git a/cross_cutting/k8s/basic_example/openapi_helm_chart/.helmignore b/cross_cutting/k8s/basic_example/openapi_helm_chart/.helmignore new file mode 100644 index 0000000..0e8a0eb --- /dev/null +++ b/cross_cutting/k8s/basic_example/openapi_helm_chart/.helmignore @@ -0,0 +1,23 @@ +# Patterns to ignore when building packages. +# This supports shell glob matching, relative path matching, and +# negation (prefixed with !). Only one pattern per line. +.DS_Store +# Common VCS dirs +.git/ +.gitignore +.bzr/ +.bzrignore +.hg/ +.hgignore +.svn/ +# Common backup files +*.swp +*.bak +*.tmp +*.orig +*~ +# Various IDEs +.project +.idea/ +*.tmproj +.vscode/ diff --git a/cross_cutting/k8s/basic_example/openapi_helm_chart/Chart.yaml b/cross_cutting/k8s/basic_example/openapi_helm_chart/Chart.yaml new file mode 100644 index 0000000..3fd6e2f --- /dev/null +++ b/cross_cutting/k8s/basic_example/openapi_helm_chart/Chart.yaml @@ -0,0 +1,24 @@ +apiVersion: v2 +name: openapi-example +description: A Helm chart for Kubernetes to depoy the open api server example + +# A chart can be either an 'application' or a 'library' chart. +# +# Application charts are a collection of templates that can be packaged into versioned archives +# to be deployed. +# +# Library charts provide useful utilities or functions for the chart developer. They're included as +# a dependency of application charts to inject those utilities and functions into the rendering +# pipeline. Library charts do not define any templates and therefore cannot be deployed. +type: application + +# This is the chart version. This version number should be incremented each time you make changes +# to the chart and its templates, including the app version. +# Versions are expected to follow Semantic Versioning (https://semver.org/) +version: 0.1.0 + +# This is the version number of the application being deployed. This version number should be +# incremented each time you make changes to the application. Versions are not expected to +# follow Semantic Versioning. They should reflect the version the application is using. +# It is recommended to use it with quotes. +appVersion: "1.16.0" diff --git a/cross_cutting/k8s/basic_example/openapi_helm_chart/templates/config/accessServerConfig.yaml b/cross_cutting/k8s/basic_example/openapi_helm_chart/templates/config/accessServerConfig.yaml new file mode 100644 index 0000000..588f576 --- /dev/null +++ b/cross_cutting/k8s/basic_example/openapi_helm_chart/templates/config/accessServerConfig.yaml @@ -0,0 +1,7 @@ +--- +apiVersion: v1 +kind: ConfigMap +metadata: + name: openapi-server-access-config +data: + mythaiapi_host_uri: "http://openapi-server-original-service.default.svc.cluster.local:5031/api/v1" diff --git a/cross_cutting/k8s/basic_example/openapi_helm_chart/templates/config/databaseInitConfig.yaml b/cross_cutting/k8s/basic_example/openapi_helm_chart/templates/config/databaseInitConfig.yaml new file mode 100644 index 0000000..50a0faa --- /dev/null +++ b/cross_cutting/k8s/basic_example/openapi_helm_chart/templates/config/databaseInitConfig.yaml @@ -0,0 +1,7 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + name: mysql-initdb-config +data: + init.sql: | + CREATE DATABASE IF NOT EXISTS db_example; \ No newline at end of file diff --git a/cross_cutting/k8s/basic_example/openapi_helm_chart/templates/config/databaseSecret.yaml b/cross_cutting/k8s/basic_example/openapi_helm_chart/templates/config/databaseSecret.yaml new file mode 100644 index 0000000..4f10264 --- /dev/null +++ b/cross_cutting/k8s/basic_example/openapi_helm_chart/templates/config/databaseSecret.yaml @@ -0,0 +1,7 @@ +apiVersion: v1 +kind: Secret +metadata: + name: mysql-database-secret +data: + mysql_root_username: cm9vdA== + mysql_root_password: cGFzc3dvcmQ= \ No newline at end of file diff --git a/cross_cutting/k8s/basic_example/openapi_helm_chart/templates/config/originalServerConfig.yaml b/cross_cutting/k8s/basic_example/openapi_helm_chart/templates/config/originalServerConfig.yaml new file mode 100644 index 0000000..f621989 --- /dev/null +++ b/cross_cutting/k8s/basic_example/openapi_helm_chart/templates/config/originalServerConfig.yaml @@ -0,0 +1,7 @@ +--- +apiVersion: v1 +kind: ConfigMap +metadata: + name: openapi-server-original-config +data: + mysql_host: "mysql.default.svc.cluster.local" \ No newline at end of file diff --git a/cross_cutting/k8s/basic_example/openapi_helm_chart/templates/deployment/accessDeployment.yaml b/cross_cutting/k8s/basic_example/openapi_helm_chart/templates/deployment/accessDeployment.yaml new file mode 100644 index 0000000..d355ebb --- /dev/null +++ b/cross_cutting/k8s/basic_example/openapi_helm_chart/templates/deployment/accessDeployment.yaml @@ -0,0 +1,44 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: openapi-server-access-deployment + labels: + app: openapi-server-access +spec: + replicas: 1 + selector: + matchLabels: + app: openapi-server-access + template: + metadata: + labels: + app: openapi-server-access + spec: + containers: + - name: spring-access-server-spring + image: access-server-spring + imagePullPolicy: Never + ports: + - containerPort: 8080 + env: + - name: MYTHAIAPI_HOST_URI + valueFrom: + configMapKeyRef: + name: openapi-server-access-config + key: mythaiapi_host_uri + +--- +apiVersion: v1 +kind: Service +metadata: + name: openapi-server-access-service +spec: + type: LoadBalancer + selector: + app: openapi-server-access + ports: + - protocol: TCP + port: 5030 + targetPort: 8080 + + diff --git a/cross_cutting/k8s/basic_example/openapi_helm_chart/templates/deployment/originalDeployment.yaml b/cross_cutting/k8s/basic_example/openapi_helm_chart/templates/deployment/originalDeployment.yaml new file mode 100644 index 0000000..d44c13b --- /dev/null +++ b/cross_cutting/k8s/basic_example/openapi_helm_chart/templates/deployment/originalDeployment.yaml @@ -0,0 +1,54 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: openapi-server-original-deployment + labels: + app: openapi-server-original +spec: + replicas: 1 + selector: + matchLabels: + app: openapi-server-original + template: + metadata: + labels: + app: openapi-server-original + spec: + containers: + - name: spring-original-server-spring + image: original-server-spring + imagePullPolicy: Never + ports: + - containerPort: 8080 + env: + - name: MYSQL_HOST + valueFrom: + configMapKeyRef: + name: openapi-server-original-config + key: mysql_host + + - name: MYSQL_ROOT_PASSWORD + valueFrom: + secretKeyRef: + name: mysql-database-secret + key: mysql_root_password + + - name: MYSQL_ROOT_USERNAME + valueFrom: + secretKeyRef: + name: mysql-database-secret + key: mysql_root_username + +--- +apiVersion: v1 +kind: Service +metadata: + name: openapi-server-original-service +spec: + selector: + app: openapi-server-original + ports: + - protocol: TCP + port: 5031 + targetPort: 8080 + diff --git a/cross_cutting/k8s/basic_example/openapi_helm_chart/templates/storage/createVolume.yaml b/cross_cutting/k8s/basic_example/openapi_helm_chart/templates/storage/createVolume.yaml new file mode 100644 index 0000000..19d959b --- /dev/null +++ b/cross_cutting/k8s/basic_example/openapi_helm_chart/templates/storage/createVolume.yaml @@ -0,0 +1,14 @@ +apiVersion: v1 +kind: PersistentVolume +metadata: + name: mysql-pv-volume + labels: + type: local +spec: + storageClassName: manual + capacity: + storage: 2Gi + accessModes: + - ReadWriteOnce + hostPath: + path: "/mnt/data" diff --git a/cross_cutting/k8s/basic_example/openapi_helm_chart/templates/storage/mySqlDatabase.yaml b/cross_cutting/k8s/basic_example/openapi_helm_chart/templates/storage/mySqlDatabase.yaml new file mode 100644 index 0000000..1a007e7 --- /dev/null +++ b/cross_cutting/k8s/basic_example/openapi_helm_chart/templates/storage/mySqlDatabase.yaml @@ -0,0 +1,64 @@ +apiVersion: v1 +kind: Service +metadata: + name: mysql +spec: + ports: + - port: 3306 + selector: + app: mysql + clusterIP: None + +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: mysql + labels: + app: mysql +spec: + replicas: 1 + selector: + matchLabels: + app: mysql + template: + metadata: + labels: + app: mysql + spec: + containers: + - name: mysql + image: mysql:5.7 + ports: + - containerPort: 3306 + env: + - name: MYSQL_ROOT_PASSWORD + valueFrom: + secretKeyRef: + name: mysql-database-secret + key: mysql_root_password + volumeMounts: + - name: mysql-initdb + mountPath: /docker-entrypoint-initdb.d + - mountPath: "/var/lib/mysql" + subPath: "mysql" + name: mysql-data + volumes: + - name: mysql-initdb + configMap: + name: mysql-initdb-config + - name: mysql-data + persistentVolumeClaim: + claimName: mysql-data-disk + +--- +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + name: mysql-data-disk +spec: + accessModes: + - ReadWriteOnce + resources: + requests: + storage: 1Gi \ No newline at end of file diff --git a/cross_cutting/k8s/basic_example/openapi_helm_chart/values.yaml b/cross_cutting/k8s/basic_example/openapi_helm_chart/values.yaml new file mode 100644 index 0000000..e69de29 diff --git a/cross_cutting/k8s/basic_example/server/access_server/.gitignore b/cross_cutting/k8s/basic_example/server/access_server/.gitignore new file mode 100644 index 0000000..549e00a --- /dev/null +++ b/cross_cutting/k8s/basic_example/server/access_server/.gitignore @@ -0,0 +1,33 @@ +HELP.md +target/ +!.mvn/wrapper/maven-wrapper.jar +!**/src/main/**/target/ +!**/src/test/**/target/ + +### STS ### +.apt_generated +.classpath +.factorypath +.project +.settings +.springBeans +.sts4-cache + +### IntelliJ IDEA ### +.idea +*.iws +*.iml +*.ipr + +### NetBeans ### +/nbproject/private/ +/nbbuild/ +/dist/ +/nbdist/ +/.nb-gradle/ +build/ +!**/src/main/**/build/ +!**/src/test/**/build/ + +### VS Code ### +.vscode/ diff --git a/cross_cutting/k8s/basic_example/server/access_server/.mvn/wrapper/maven-wrapper.jar b/cross_cutting/k8s/basic_example/server/access_server/.mvn/wrapper/maven-wrapper.jar new file mode 100644 index 0000000..c1dd12f Binary files /dev/null and b/cross_cutting/k8s/basic_example/server/access_server/.mvn/wrapper/maven-wrapper.jar differ diff --git a/cross_cutting/k8s/basic_example/server/access_server/.mvn/wrapper/maven-wrapper.properties b/cross_cutting/k8s/basic_example/server/access_server/.mvn/wrapper/maven-wrapper.properties new file mode 100644 index 0000000..b74bf7f --- /dev/null +++ b/cross_cutting/k8s/basic_example/server/access_server/.mvn/wrapper/maven-wrapper.properties @@ -0,0 +1,2 @@ +distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.8.6/apache-maven-3.8.6-bin.zip +wrapperUrl=https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.1.0/maven-wrapper-3.1.0.jar diff --git a/cross_cutting/k8s/basic_example/server/access_server/Dockerfile b/cross_cutting/k8s/basic_example/server/access_server/Dockerfile new file mode 100644 index 0000000..3ff2b05 --- /dev/null +++ b/cross_cutting/k8s/basic_example/server/access_server/Dockerfile @@ -0,0 +1,6 @@ +FROM adoptopenjdk/openjdk11:alpine-jre +RUN mkdir /opt/app +WORKDIR /opt/app +ARG JAR_FILE=target/access_server-0.0.1-SNAPSHOT.jar +COPY ${JAR_FILE} app.jar +CMD ["java", "-jar", "/opt/app/app.jar"] \ No newline at end of file diff --git a/cross_cutting/k8s/basic_example/server/access_server/mvnw b/cross_cutting/k8s/basic_example/server/access_server/mvnw new file mode 100644 index 0000000..8a8fb22 --- /dev/null +++ b/cross_cutting/k8s/basic_example/server/access_server/mvnw @@ -0,0 +1,316 @@ +#!/bin/sh +# ---------------------------------------------------------------------------- +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# ---------------------------------------------------------------------------- + +# ---------------------------------------------------------------------------- +# Maven Start Up Batch script +# +# Required ENV vars: +# ------------------ +# JAVA_HOME - location of a JDK home dir +# +# Optional ENV vars +# ----------------- +# M2_HOME - location of maven2's installed home dir +# MAVEN_OPTS - parameters passed to the Java VM when running Maven +# e.g. to debug Maven itself, use +# set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 +# MAVEN_SKIP_RC - flag to disable loading of mavenrc files +# ---------------------------------------------------------------------------- + +if [ -z "$MAVEN_SKIP_RC" ] ; then + + if [ -f /usr/local/etc/mavenrc ] ; then + . /usr/local/etc/mavenrc + fi + + if [ -f /etc/mavenrc ] ; then + . /etc/mavenrc + fi + + if [ -f "$HOME/.mavenrc" ] ; then + . "$HOME/.mavenrc" + fi + +fi + +# OS specific support. $var _must_ be set to either true or false. +cygwin=false; +darwin=false; +mingw=false +case "`uname`" in + CYGWIN*) cygwin=true ;; + MINGW*) mingw=true;; + Darwin*) darwin=true + # Use /usr/libexec/java_home if available, otherwise fall back to /Library/Java/Home + # See https://developer.apple.com/library/mac/qa/qa1170/_index.html + if [ -z "$JAVA_HOME" ]; then + if [ -x "/usr/libexec/java_home" ]; then + export JAVA_HOME="`/usr/libexec/java_home`" + else + export JAVA_HOME="/Library/Java/Home" + fi + fi + ;; +esac + +if [ -z "$JAVA_HOME" ] ; then + if [ -r /etc/gentoo-release ] ; then + JAVA_HOME=`java-config --jre-home` + fi +fi + +if [ -z "$M2_HOME" ] ; then + ## resolve links - $0 may be a link to maven's home + PRG="$0" + + # need this for relative symlinks + while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG="`dirname "$PRG"`/$link" + fi + done + + saveddir=`pwd` + + M2_HOME=`dirname "$PRG"`/.. + + # make it fully qualified + M2_HOME=`cd "$M2_HOME" && pwd` + + cd "$saveddir" + # echo Using m2 at $M2_HOME +fi + +# For Cygwin, ensure paths are in UNIX format before anything is touched +if $cygwin ; then + [ -n "$M2_HOME" ] && + M2_HOME=`cygpath --unix "$M2_HOME"` + [ -n "$JAVA_HOME" ] && + JAVA_HOME=`cygpath --unix "$JAVA_HOME"` + [ -n "$CLASSPATH" ] && + CLASSPATH=`cygpath --path --unix "$CLASSPATH"` +fi + +# For Mingw, ensure paths are in UNIX format before anything is touched +if $mingw ; then + [ -n "$M2_HOME" ] && + M2_HOME="`(cd "$M2_HOME"; pwd)`" + [ -n "$JAVA_HOME" ] && + JAVA_HOME="`(cd "$JAVA_HOME"; pwd)`" +fi + +if [ -z "$JAVA_HOME" ]; then + javaExecutable="`which javac`" + if [ -n "$javaExecutable" ] && ! [ "`expr \"$javaExecutable\" : '\([^ ]*\)'`" = "no" ]; then + # readlink(1) is not available as standard on Solaris 10. + readLink=`which readlink` + if [ ! `expr "$readLink" : '\([^ ]*\)'` = "no" ]; then + if $darwin ; then + javaHome="`dirname \"$javaExecutable\"`" + javaExecutable="`cd \"$javaHome\" && pwd -P`/javac" + else + javaExecutable="`readlink -f \"$javaExecutable\"`" + fi + javaHome="`dirname \"$javaExecutable\"`" + javaHome=`expr "$javaHome" : '\(.*\)/bin'` + JAVA_HOME="$javaHome" + export JAVA_HOME + fi + fi +fi + +if [ -z "$JAVACMD" ] ; then + if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + else + JAVACMD="`\\unset -f command; \\command -v java`" + fi +fi + +if [ ! -x "$JAVACMD" ] ; then + echo "Error: JAVA_HOME is not defined correctly." >&2 + echo " We cannot execute $JAVACMD" >&2 + exit 1 +fi + +if [ -z "$JAVA_HOME" ] ; then + echo "Warning: JAVA_HOME environment variable is not set." +fi + +CLASSWORLDS_LAUNCHER=org.codehaus.plexus.classworlds.launcher.Launcher + +# traverses directory structure from process work directory to filesystem root +# first directory with .mvn subdirectory is considered project base directory +find_maven_basedir() { + + if [ -z "$1" ] + then + echo "Path not specified to find_maven_basedir" + return 1 + fi + + basedir="$1" + wdir="$1" + while [ "$wdir" != '/' ] ; do + if [ -d "$wdir"/.mvn ] ; then + basedir=$wdir + break + fi + # workaround for JBEAP-8937 (on Solaris 10/Sparc) + if [ -d "${wdir}" ]; then + wdir=`cd "$wdir/.."; pwd` + fi + # end of workaround + done + echo "${basedir}" +} + +# concatenates all lines of a file +concat_lines() { + if [ -f "$1" ]; then + echo "$(tr -s '\n' ' ' < "$1")" + fi +} + +BASE_DIR=`find_maven_basedir "$(pwd)"` +if [ -z "$BASE_DIR" ]; then + exit 1; +fi + +########################################################################################## +# Extension to allow automatically downloading the maven-wrapper.jar from Maven-central +# This allows using the maven wrapper in projects that prohibit checking in binary data. +########################################################################################## +if [ -r "$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" ]; then + if [ "$MVNW_VERBOSE" = true ]; then + echo "Found .mvn/wrapper/maven-wrapper.jar" + fi +else + if [ "$MVNW_VERBOSE" = true ]; then + echo "Couldn't find .mvn/wrapper/maven-wrapper.jar, downloading it ..." + fi + if [ -n "$MVNW_REPOURL" ]; then + jarUrl="$MVNW_REPOURL/org/apache/maven/wrapper/maven-wrapper/3.1.0/maven-wrapper-3.1.0.jar" + else + jarUrl="https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.1.0/maven-wrapper-3.1.0.jar" + fi + while IFS="=" read key value; do + case "$key" in (wrapperUrl) jarUrl="$value"; break ;; + esac + done < "$BASE_DIR/.mvn/wrapper/maven-wrapper.properties" + if [ "$MVNW_VERBOSE" = true ]; then + echo "Downloading from: $jarUrl" + fi + wrapperJarPath="$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" + if $cygwin; then + wrapperJarPath=`cygpath --path --windows "$wrapperJarPath"` + fi + + if command -v wget > /dev/null; then + if [ "$MVNW_VERBOSE" = true ]; then + echo "Found wget ... using wget" + fi + if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then + wget "$jarUrl" -O "$wrapperJarPath" || rm -f "$wrapperJarPath" + else + wget --http-user=$MVNW_USERNAME --http-password=$MVNW_PASSWORD "$jarUrl" -O "$wrapperJarPath" || rm -f "$wrapperJarPath" + fi + elif command -v curl > /dev/null; then + if [ "$MVNW_VERBOSE" = true ]; then + echo "Found curl ... using curl" + fi + if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then + curl -o "$wrapperJarPath" "$jarUrl" -f + else + curl --user $MVNW_USERNAME:$MVNW_PASSWORD -o "$wrapperJarPath" "$jarUrl" -f + fi + + else + if [ "$MVNW_VERBOSE" = true ]; then + echo "Falling back to using Java to download" + fi + javaClass="$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.java" + # For Cygwin, switch paths to Windows format before running javac + if $cygwin; then + javaClass=`cygpath --path --windows "$javaClass"` + fi + if [ -e "$javaClass" ]; then + if [ ! -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then + if [ "$MVNW_VERBOSE" = true ]; then + echo " - Compiling MavenWrapperDownloader.java ..." + fi + # Compiling the Java class + ("$JAVA_HOME/bin/javac" "$javaClass") + fi + if [ -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then + # Running the downloader + if [ "$MVNW_VERBOSE" = true ]; then + echo " - Running MavenWrapperDownloader.java ..." + fi + ("$JAVA_HOME/bin/java" -cp .mvn/wrapper MavenWrapperDownloader "$MAVEN_PROJECTBASEDIR") + fi + fi + fi +fi +########################################################################################## +# End of extension +########################################################################################## + +export MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-"$BASE_DIR"} +if [ "$MVNW_VERBOSE" = true ]; then + echo $MAVEN_PROJECTBASEDIR +fi +MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS" + +# For Cygwin, switch paths to Windows format before running java +if $cygwin; then + [ -n "$M2_HOME" ] && + M2_HOME=`cygpath --path --windows "$M2_HOME"` + [ -n "$JAVA_HOME" ] && + JAVA_HOME=`cygpath --path --windows "$JAVA_HOME"` + [ -n "$CLASSPATH" ] && + CLASSPATH=`cygpath --path --windows "$CLASSPATH"` + [ -n "$MAVEN_PROJECTBASEDIR" ] && + MAVEN_PROJECTBASEDIR=`cygpath --path --windows "$MAVEN_PROJECTBASEDIR"` +fi + +# Provide a "standardized" way to retrieve the CLI args that will +# work with both Windows and non-Windows executions. +MAVEN_CMD_LINE_ARGS="$MAVEN_CONFIG $@" +export MAVEN_CMD_LINE_ARGS + +WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain + +exec "$JAVACMD" \ + $MAVEN_OPTS \ + $MAVEN_DEBUG_OPTS \ + -classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \ + "-Dmaven.home=${M2_HOME}" \ + "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \ + ${WRAPPER_LAUNCHER} $MAVEN_CONFIG "$@" diff --git a/cross_cutting/k8s/basic_example/server/access_server/mvnw.cmd b/cross_cutting/k8s/basic_example/server/access_server/mvnw.cmd new file mode 100644 index 0000000..1d8ab01 --- /dev/null +++ b/cross_cutting/k8s/basic_example/server/access_server/mvnw.cmd @@ -0,0 +1,188 @@ +@REM ---------------------------------------------------------------------------- +@REM Licensed to the Apache Software Foundation (ASF) under one +@REM or more contributor license agreements. See the NOTICE file +@REM distributed with this work for additional information +@REM regarding copyright ownership. The ASF licenses this file +@REM to you under the Apache License, Version 2.0 (the +@REM "License"); you may not use this file except in compliance +@REM with the License. You may obtain a copy of the License at +@REM +@REM https://www.apache.org/licenses/LICENSE-2.0 +@REM +@REM Unless required by applicable law or agreed to in writing, +@REM software distributed under the License is distributed on an +@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +@REM KIND, either express or implied. See the License for the +@REM specific language governing permissions and limitations +@REM under the License. +@REM ---------------------------------------------------------------------------- + +@REM ---------------------------------------------------------------------------- +@REM Maven Start Up Batch script +@REM +@REM Required ENV vars: +@REM JAVA_HOME - location of a JDK home dir +@REM +@REM Optional ENV vars +@REM M2_HOME - location of maven2's installed home dir +@REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands +@REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a keystroke before ending +@REM MAVEN_OPTS - parameters passed to the Java VM when running Maven +@REM e.g. to debug Maven itself, use +@REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 +@REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files +@REM ---------------------------------------------------------------------------- + +@REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on' +@echo off +@REM set title of command window +title %0 +@REM enable echoing by setting MAVEN_BATCH_ECHO to 'on' +@if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO% + +@REM set %HOME% to equivalent of $HOME +if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%") + +@REM Execute a user defined script before this one +if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre +@REM check for pre script, once with legacy .bat ending and once with .cmd ending +if exist "%USERPROFILE%\mavenrc_pre.bat" call "%USERPROFILE%\mavenrc_pre.bat" %* +if exist "%USERPROFILE%\mavenrc_pre.cmd" call "%USERPROFILE%\mavenrc_pre.cmd" %* +:skipRcPre + +@setlocal + +set ERROR_CODE=0 + +@REM To isolate internal variables from possible post scripts, we use another setlocal +@setlocal + +@REM ==== START VALIDATION ==== +if not "%JAVA_HOME%" == "" goto OkJHome + +echo. +echo Error: JAVA_HOME not found in your environment. >&2 +echo Please set the JAVA_HOME variable in your environment to match the >&2 +echo location of your Java installation. >&2 +echo. +goto error + +:OkJHome +if exist "%JAVA_HOME%\bin\java.exe" goto init + +echo. +echo Error: JAVA_HOME is set to an invalid directory. >&2 +echo JAVA_HOME = "%JAVA_HOME%" >&2 +echo Please set the JAVA_HOME variable in your environment to match the >&2 +echo location of your Java installation. >&2 +echo. +goto error + +@REM ==== END VALIDATION ==== + +:init + +@REM Find the project base dir, i.e. the directory that contains the folder ".mvn". +@REM Fallback to current working directory if not found. + +set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR% +IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir + +set EXEC_DIR=%CD% +set WDIR=%EXEC_DIR% +:findBaseDir +IF EXIST "%WDIR%"\.mvn goto baseDirFound +cd .. +IF "%WDIR%"=="%CD%" goto baseDirNotFound +set WDIR=%CD% +goto findBaseDir + +:baseDirFound +set MAVEN_PROJECTBASEDIR=%WDIR% +cd "%EXEC_DIR%" +goto endDetectBaseDir + +:baseDirNotFound +set MAVEN_PROJECTBASEDIR=%EXEC_DIR% +cd "%EXEC_DIR%" + +:endDetectBaseDir + +IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig + +@setlocal EnableExtensions EnableDelayedExpansion +for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a +@endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS% + +:endReadAdditionalConfig + +SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe" +set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar" +set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain + +set DOWNLOAD_URL="https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.1.0/maven-wrapper-3.1.0.jar" + +FOR /F "usebackq tokens=1,2 delims==" %%A IN ("%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties") DO ( + IF "%%A"=="wrapperUrl" SET DOWNLOAD_URL=%%B +) + +@REM Extension to allow automatically downloading the maven-wrapper.jar from Maven-central +@REM This allows using the maven wrapper in projects that prohibit checking in binary data. +if exist %WRAPPER_JAR% ( + if "%MVNW_VERBOSE%" == "true" ( + echo Found %WRAPPER_JAR% + ) +) else ( + if not "%MVNW_REPOURL%" == "" ( + SET DOWNLOAD_URL="%MVNW_REPOURL%/org/apache/maven/wrapper/maven-wrapper/3.1.0/maven-wrapper-3.1.0.jar" + ) + if "%MVNW_VERBOSE%" == "true" ( + echo Couldn't find %WRAPPER_JAR%, downloading it ... + echo Downloading from: %DOWNLOAD_URL% + ) + + powershell -Command "&{"^ + "$webclient = new-object System.Net.WebClient;"^ + "if (-not ([string]::IsNullOrEmpty('%MVNW_USERNAME%') -and [string]::IsNullOrEmpty('%MVNW_PASSWORD%'))) {"^ + "$webclient.Credentials = new-object System.Net.NetworkCredential('%MVNW_USERNAME%', '%MVNW_PASSWORD%');"^ + "}"^ + "[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; $webclient.DownloadFile('%DOWNLOAD_URL%', '%WRAPPER_JAR%')"^ + "}" + if "%MVNW_VERBOSE%" == "true" ( + echo Finished downloading %WRAPPER_JAR% + ) +) +@REM End of extension + +@REM Provide a "standardized" way to retrieve the CLI args that will +@REM work with both Windows and non-Windows executions. +set MAVEN_CMD_LINE_ARGS=%* + +%MAVEN_JAVA_EXE% ^ + %JVM_CONFIG_MAVEN_PROPS% ^ + %MAVEN_OPTS% ^ + %MAVEN_DEBUG_OPTS% ^ + -classpath %WRAPPER_JAR% ^ + "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" ^ + %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %* +if ERRORLEVEL 1 goto error +goto end + +:error +set ERROR_CODE=1 + +:end +@endlocal & set ERROR_CODE=%ERROR_CODE% + +if not "%MAVEN_SKIP_RC%"=="" goto skipRcPost +@REM check for post script, once with legacy .bat ending and once with .cmd ending +if exist "%USERPROFILE%\mavenrc_post.bat" call "%USERPROFILE%\mavenrc_post.bat" +if exist "%USERPROFILE%\mavenrc_post.cmd" call "%USERPROFILE%\mavenrc_post.cmd" +:skipRcPost + +@REM pause the script if MAVEN_BATCH_PAUSE is set to 'on' +if "%MAVEN_BATCH_PAUSE%"=="on" pause + +if "%MAVEN_TERMINATE_CMD%"=="on" exit %ERROR_CODE% + +cmd /C exit /B %ERROR_CODE% diff --git a/cross_cutting/k8s/basic_example/server/access_server/pom.xml b/cross_cutting/k8s/basic_example/server/access_server/pom.xml new file mode 100644 index 0000000..4333f55 --- /dev/null +++ b/cross_cutting/k8s/basic_example/server/access_server/pom.xml @@ -0,0 +1,87 @@ + + + 4.0.0 + + org.springframework.boot + spring-boot-starter-parent + 2.7.4 + + + com.devonfw.devon4j.examples.cloud + access_server + 0.0.1-SNAPSHOT + access_server + Demo project for Spring Boot + + 11 + + + + org.springframework.boot + spring-boot-starter-web + + + + io.swagger.parser.v3 + swagger-parser + 2.1.2 + + + org.openapitools + jackson-databind-nullable + 0.2.3 + + + + + + com.github.tomakehurst + wiremock-jre8 + 2.34.0 + test + + + + org.springframework.boot + spring-boot-starter-test + test + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + org.openapitools + openapi-generator-maven-plugin + 6.1.0 + + + + generate + + + MyThaiApi-java-client + com.devonfw.devon4j.generated.client + com.devonfw.devon4j.generated.client.service + com.devonfw.devon4j.generated.client.model + com.devonfw.devon4j.generated.client.handler + ${project.basedir}/src/main/resources/MyThaiApi.yml + ${project.build.directory}/auto-generated + java + native + false + false + TO + + + + + + + + diff --git a/cross_cutting/k8s/basic_example/server/access_server/src/main/java/com/devonfw/devon4j/examples/cloud/access_server/AccessServerApplication.java b/cross_cutting/k8s/basic_example/server/access_server/src/main/java/com/devonfw/devon4j/examples/cloud/access_server/AccessServerApplication.java new file mode 100644 index 0000000..04bbe78 --- /dev/null +++ b/cross_cutting/k8s/basic_example/server/access_server/src/main/java/com/devonfw/devon4j/examples/cloud/access_server/AccessServerApplication.java @@ -0,0 +1,13 @@ +package com.devonfw.devon4j.examples.cloud.access_server; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +public class AccessServerApplication { + + public static void main(String[] args) { + SpringApplication.run(AccessServerApplication.class, args); + } + +} diff --git a/cross_cutting/k8s/basic_example/server/access_server/src/main/java/com/devonfw/devon4j/examples/cloud/access_server/invitedGuest/InvitedGuestConfig.java b/cross_cutting/k8s/basic_example/server/access_server/src/main/java/com/devonfw/devon4j/examples/cloud/access_server/invitedGuest/InvitedGuestConfig.java new file mode 100644 index 0000000..3dd69d7 --- /dev/null +++ b/cross_cutting/k8s/basic_example/server/access_server/src/main/java/com/devonfw/devon4j/examples/cloud/access_server/invitedGuest/InvitedGuestConfig.java @@ -0,0 +1,32 @@ +package com.devonfw.devon4j.examples.cloud.access_server.invitedGuest; + +import com.devonfw.devon4j.generated.client.handler.ApiClient; +import com.devonfw.devon4j.generated.client.service.InvitedGuestApi; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +@Configuration +public class InvitedGuestConfig { + + @Value("${MyThaiApi.host.uri:}") + public String hostInfoBasePath; + + @Bean + public ApiClient apiClient(){ + ApiClient client = new ApiClient(); + + if(!hostInfoBasePath.isEmpty()){ + client.updateBaseUri(hostInfoBasePath); + } + + return client; + } + + @Bean + public InvitedGuestApi invitedGuestApi(){ + return new InvitedGuestApi(apiClient()); + } + + +} \ No newline at end of file diff --git a/cross_cutting/k8s/basic_example/server/access_server/src/main/java/com/devonfw/devon4j/examples/cloud/access_server/invitedGuest/InvitedGuestController.java b/cross_cutting/k8s/basic_example/server/access_server/src/main/java/com/devonfw/devon4j/examples/cloud/access_server/invitedGuest/InvitedGuestController.java new file mode 100644 index 0000000..45ded8e --- /dev/null +++ b/cross_cutting/k8s/basic_example/server/access_server/src/main/java/com/devonfw/devon4j/examples/cloud/access_server/invitedGuest/InvitedGuestController.java @@ -0,0 +1,36 @@ +package com.devonfw.devon4j.examples.cloud.access_server.invitedGuest; + +import com.devonfw.devon4j.generated.client.handler.ApiException; +import com.devonfw.devon4j.generated.client.model.InvitedGuestTO; +import com.devonfw.devon4j.generated.client.service.InvitedGuestApi; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +@RestController +public class InvitedGuestController { + + InvitedGuestApi invitedGuestApi; + + public InvitedGuestController(InvitedGuestApi invitedGuestApi) { + this.invitedGuestApi = invitedGuestApi; + } + + @GetMapping("/guest") + public List getAllGuests() throws ApiException { + return invitedGuestApi.getInvitedGuestAll(); + } + + @GetMapping("/guest/{id}") + public InvitedGuestTO getAllGuests(@PathVariable Long id) throws ApiException { + return invitedGuestApi.getInvitedGuestById(id); + } + + + @PostMapping("/guest") + public InvitedGuestTO addGuests(@RequestBody InvitedGuestTO invitedGuestTO) throws ApiException { + return invitedGuestApi.createInvitedGuest(invitedGuestTO); + } + + +} diff --git a/cross_cutting/k8s/basic_example/server/access_server/src/main/resources/MyThaiApi.yml b/cross_cutting/k8s/basic_example/server/access_server/src/main/resources/MyThaiApi.yml new file mode 100644 index 0000000..c779573 --- /dev/null +++ b/cross_cutting/k8s/basic_example/server/access_server/src/main/resources/MyThaiApi.yml @@ -0,0 +1,181 @@ +openapi: '3.0.2' +info: + title: My Thai Star REST API + description: |- + This API file is just an example to show the options when creting an OpenAPI file. + This Api is a small example for the My Thai Star + version: '1.0' + contact: + email: contact@mail.de +servers: + - url: http://localhost:8080/api/v1 +paths: + + /booking: + post: + tags: + - "Booking" + summary: Create a new Booking + description: Creates and retruns a new Booking + operationId: createBooking + responses: + '201': + description: "Created" + content: + application/json: + schema: + $ref: '#/components/schemas/Booking' + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/Booking' + + get: + tags: + - "Booking" + summary: Get all Bookings + description: Returns a list of bookings + operationId: getBookingAll + responses: + '200': + description: OK + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/Booking' + + + /booking/{bookingId}: + get: + tags: + - "Booking" + summary: Find a single booking by Id + description: Returns a single booking + operationId: getBookingById + parameters: + - name: bookingId + in: path + description: ID of booking to return + required: true + schema: + type: integer + format: int64 + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/Booking' + + '400': + description: Invalid ID supplied + '404': + description: Booking not found + + /invitedGuest: + post: + tags: + - "InvitedGuest" + summary: Create a new invited guests + description: Creates and retruns a new invited guests + operationId: createInvitedGuest + responses: + '201': + description: "Created" + content: + application/json: + schema: + $ref: '#/components/schemas/InvitedGuest' + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/InvitedGuest' + + get: + tags: + - "InvitedGuest" + summary: Get all invited guests + description: Returns a list of invited guests + operationId: getInvitedGuestAll + responses: + '200': + description: OK + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/InvitedGuest' + + /invitedGuest/{guestId}: + get: + tags: + - "InvitedGuest" + summary: Find a single InvitedGuest by Id + description: Returns a single InvitedGuest + operationId: getInvitedGuestById + parameters: + - name: guestId + in: path + description: ID of guest to return + required: true + schema: + type: integer + format: int64 + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/InvitedGuest' + + '400': + description: Invalid ID supplied + '404': + description: InvitedGuest not found + +components: + schemas: + + InvitedGuest: + type: object + properties: + id: + type: integer + format: int64 + example: 1 + modificationCounter: + type: integer + format: int32 + default: 0 + example: 0 + email: + type: string + example: "guest.email@email.com" + + Booking: + type: object + properties: + id: + type: integer + format: int64 + example: 1 + modificationCounter: + type: integer + format: int32 + default: 0 + example: 0 + invitedGuests: + type: array + xml: + wrapped: true + items: + $ref: '#/components/schemas/InvitedGuest' + example: [] + default: [] \ No newline at end of file diff --git a/cross_cutting/k8s/basic_example/server/access_server/src/main/resources/application.properties b/cross_cutting/k8s/basic_example/server/access_server/src/main/resources/application.properties new file mode 100644 index 0000000..181afe5 --- /dev/null +++ b/cross_cutting/k8s/basic_example/server/access_server/src/main/resources/application.properties @@ -0,0 +1 @@ +MyThaiApi.host.uri=http://localhost:8080/api/v1 \ No newline at end of file diff --git a/cross_cutting/k8s/basic_example/server/access_server/src/main/resources/templates/test.html b/cross_cutting/k8s/basic_example/server/access_server/src/main/resources/templates/test.html new file mode 100644 index 0000000..03a7fb6 --- /dev/null +++ b/cross_cutting/k8s/basic_example/server/access_server/src/main/resources/templates/test.html @@ -0,0 +1,10 @@ + + + + + Title + + +

OKAY

+ + \ No newline at end of file diff --git a/cross_cutting/k8s/basic_example/server/access_server/src/test/java/com/devonfw/devon4j/examples/cloud/access_server/AccessServerApplicationTests.java b/cross_cutting/k8s/basic_example/server/access_server/src/test/java/com/devonfw/devon4j/examples/cloud/access_server/AccessServerApplicationTests.java new file mode 100644 index 0000000..9d0bc81 --- /dev/null +++ b/cross_cutting/k8s/basic_example/server/access_server/src/test/java/com/devonfw/devon4j/examples/cloud/access_server/AccessServerApplicationTests.java @@ -0,0 +1,13 @@ +package com.devonfw.devon4j.examples.cloud.access_server; + +import org.junit.jupiter.api.Test; +import org.springframework.boot.test.context.SpringBootTest; + +@SpringBootTest +class AccessServerApplicationTests { + + @Test + void contextLoads() { + } + +} diff --git a/cross_cutting/k8s/basic_example/server/original_server/.gitignore b/cross_cutting/k8s/basic_example/server/original_server/.gitignore new file mode 100644 index 0000000..549e00a --- /dev/null +++ b/cross_cutting/k8s/basic_example/server/original_server/.gitignore @@ -0,0 +1,33 @@ +HELP.md +target/ +!.mvn/wrapper/maven-wrapper.jar +!**/src/main/**/target/ +!**/src/test/**/target/ + +### STS ### +.apt_generated +.classpath +.factorypath +.project +.settings +.springBeans +.sts4-cache + +### IntelliJ IDEA ### +.idea +*.iws +*.iml +*.ipr + +### NetBeans ### +/nbproject/private/ +/nbbuild/ +/dist/ +/nbdist/ +/.nb-gradle/ +build/ +!**/src/main/**/build/ +!**/src/test/**/build/ + +### VS Code ### +.vscode/ diff --git a/cross_cutting/k8s/basic_example/server/original_server/.mvn/wrapper/maven-wrapper.jar b/cross_cutting/k8s/basic_example/server/original_server/.mvn/wrapper/maven-wrapper.jar new file mode 100644 index 0000000..c1dd12f Binary files /dev/null and b/cross_cutting/k8s/basic_example/server/original_server/.mvn/wrapper/maven-wrapper.jar differ diff --git a/cross_cutting/k8s/basic_example/server/original_server/.mvn/wrapper/maven-wrapper.properties b/cross_cutting/k8s/basic_example/server/original_server/.mvn/wrapper/maven-wrapper.properties new file mode 100644 index 0000000..b74bf7f --- /dev/null +++ b/cross_cutting/k8s/basic_example/server/original_server/.mvn/wrapper/maven-wrapper.properties @@ -0,0 +1,2 @@ +distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.8.6/apache-maven-3.8.6-bin.zip +wrapperUrl=https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.1.0/maven-wrapper-3.1.0.jar diff --git a/cross_cutting/k8s/basic_example/server/original_server/Dockerfile b/cross_cutting/k8s/basic_example/server/original_server/Dockerfile new file mode 100644 index 0000000..7a0da79 --- /dev/null +++ b/cross_cutting/k8s/basic_example/server/original_server/Dockerfile @@ -0,0 +1,6 @@ +FROM adoptopenjdk/openjdk11:alpine-jre +RUN mkdir /opt/app +WORKDIR /opt/app +ARG JAR_FILE=target/original_server-0.0.1-SNAPSHOT.jar +COPY ${JAR_FILE} app.jar +CMD ["java", "-jar", "/opt/app/app.jar"] \ No newline at end of file diff --git a/cross_cutting/k8s/basic_example/server/original_server/README.adoc b/cross_cutting/k8s/basic_example/server/original_server/README.adoc new file mode 100644 index 0000000..2631772 --- /dev/null +++ b/cross_cutting/k8s/basic_example/server/original_server/README.adoc @@ -0,0 +1,32 @@ += OpenApi Server Plugin + +This example will show how to generate server code with the OpenApi Generator plugin + +== How to run the example + +1. Run maven `clean` and `compile` +2. Start the application +3. Run tests + + +== Container + + +== Install Rancher Desktop + +== Select "containered" as container runtime + +=== Create local container imgage from project +---- +nerdctl build -t openapi-spring --namespace k8s.io . +---- + +=== Check if image is created +---- +nerdctl images --namespace k8s.io +---- + +=== Install components into the cluster with helm +---- +helm install openapi-serve ./openapi-example/ --namespace default +---- diff --git a/cross_cutting/k8s/basic_example/server/original_server/mvnw b/cross_cutting/k8s/basic_example/server/original_server/mvnw new file mode 100644 index 0000000..8a8fb22 --- /dev/null +++ b/cross_cutting/k8s/basic_example/server/original_server/mvnw @@ -0,0 +1,316 @@ +#!/bin/sh +# ---------------------------------------------------------------------------- +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# ---------------------------------------------------------------------------- + +# ---------------------------------------------------------------------------- +# Maven Start Up Batch script +# +# Required ENV vars: +# ------------------ +# JAVA_HOME - location of a JDK home dir +# +# Optional ENV vars +# ----------------- +# M2_HOME - location of maven2's installed home dir +# MAVEN_OPTS - parameters passed to the Java VM when running Maven +# e.g. to debug Maven itself, use +# set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 +# MAVEN_SKIP_RC - flag to disable loading of mavenrc files +# ---------------------------------------------------------------------------- + +if [ -z "$MAVEN_SKIP_RC" ] ; then + + if [ -f /usr/local/etc/mavenrc ] ; then + . /usr/local/etc/mavenrc + fi + + if [ -f /etc/mavenrc ] ; then + . /etc/mavenrc + fi + + if [ -f "$HOME/.mavenrc" ] ; then + . "$HOME/.mavenrc" + fi + +fi + +# OS specific support. $var _must_ be set to either true or false. +cygwin=false; +darwin=false; +mingw=false +case "`uname`" in + CYGWIN*) cygwin=true ;; + MINGW*) mingw=true;; + Darwin*) darwin=true + # Use /usr/libexec/java_home if available, otherwise fall back to /Library/Java/Home + # See https://developer.apple.com/library/mac/qa/qa1170/_index.html + if [ -z "$JAVA_HOME" ]; then + if [ -x "/usr/libexec/java_home" ]; then + export JAVA_HOME="`/usr/libexec/java_home`" + else + export JAVA_HOME="/Library/Java/Home" + fi + fi + ;; +esac + +if [ -z "$JAVA_HOME" ] ; then + if [ -r /etc/gentoo-release ] ; then + JAVA_HOME=`java-config --jre-home` + fi +fi + +if [ -z "$M2_HOME" ] ; then + ## resolve links - $0 may be a link to maven's home + PRG="$0" + + # need this for relative symlinks + while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG="`dirname "$PRG"`/$link" + fi + done + + saveddir=`pwd` + + M2_HOME=`dirname "$PRG"`/.. + + # make it fully qualified + M2_HOME=`cd "$M2_HOME" && pwd` + + cd "$saveddir" + # echo Using m2 at $M2_HOME +fi + +# For Cygwin, ensure paths are in UNIX format before anything is touched +if $cygwin ; then + [ -n "$M2_HOME" ] && + M2_HOME=`cygpath --unix "$M2_HOME"` + [ -n "$JAVA_HOME" ] && + JAVA_HOME=`cygpath --unix "$JAVA_HOME"` + [ -n "$CLASSPATH" ] && + CLASSPATH=`cygpath --path --unix "$CLASSPATH"` +fi + +# For Mingw, ensure paths are in UNIX format before anything is touched +if $mingw ; then + [ -n "$M2_HOME" ] && + M2_HOME="`(cd "$M2_HOME"; pwd)`" + [ -n "$JAVA_HOME" ] && + JAVA_HOME="`(cd "$JAVA_HOME"; pwd)`" +fi + +if [ -z "$JAVA_HOME" ]; then + javaExecutable="`which javac`" + if [ -n "$javaExecutable" ] && ! [ "`expr \"$javaExecutable\" : '\([^ ]*\)'`" = "no" ]; then + # readlink(1) is not available as standard on Solaris 10. + readLink=`which readlink` + if [ ! `expr "$readLink" : '\([^ ]*\)'` = "no" ]; then + if $darwin ; then + javaHome="`dirname \"$javaExecutable\"`" + javaExecutable="`cd \"$javaHome\" && pwd -P`/javac" + else + javaExecutable="`readlink -f \"$javaExecutable\"`" + fi + javaHome="`dirname \"$javaExecutable\"`" + javaHome=`expr "$javaHome" : '\(.*\)/bin'` + JAVA_HOME="$javaHome" + export JAVA_HOME + fi + fi +fi + +if [ -z "$JAVACMD" ] ; then + if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + else + JAVACMD="`\\unset -f command; \\command -v java`" + fi +fi + +if [ ! -x "$JAVACMD" ] ; then + echo "Error: JAVA_HOME is not defined correctly." >&2 + echo " We cannot execute $JAVACMD" >&2 + exit 1 +fi + +if [ -z "$JAVA_HOME" ] ; then + echo "Warning: JAVA_HOME environment variable is not set." +fi + +CLASSWORLDS_LAUNCHER=org.codehaus.plexus.classworlds.launcher.Launcher + +# traverses directory structure from process work directory to filesystem root +# first directory with .mvn subdirectory is considered project base directory +find_maven_basedir() { + + if [ -z "$1" ] + then + echo "Path not specified to find_maven_basedir" + return 1 + fi + + basedir="$1" + wdir="$1" + while [ "$wdir" != '/' ] ; do + if [ -d "$wdir"/.mvn ] ; then + basedir=$wdir + break + fi + # workaround for JBEAP-8937 (on Solaris 10/Sparc) + if [ -d "${wdir}" ]; then + wdir=`cd "$wdir/.."; pwd` + fi + # end of workaround + done + echo "${basedir}" +} + +# concatenates all lines of a file +concat_lines() { + if [ -f "$1" ]; then + echo "$(tr -s '\n' ' ' < "$1")" + fi +} + +BASE_DIR=`find_maven_basedir "$(pwd)"` +if [ -z "$BASE_DIR" ]; then + exit 1; +fi + +########################################################################################## +# Extension to allow automatically downloading the maven-wrapper.jar from Maven-central +# This allows using the maven wrapper in projects that prohibit checking in binary data. +########################################################################################## +if [ -r "$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" ]; then + if [ "$MVNW_VERBOSE" = true ]; then + echo "Found .mvn/wrapper/maven-wrapper.jar" + fi +else + if [ "$MVNW_VERBOSE" = true ]; then + echo "Couldn't find .mvn/wrapper/maven-wrapper.jar, downloading it ..." + fi + if [ -n "$MVNW_REPOURL" ]; then + jarUrl="$MVNW_REPOURL/org/apache/maven/wrapper/maven-wrapper/3.1.0/maven-wrapper-3.1.0.jar" + else + jarUrl="https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.1.0/maven-wrapper-3.1.0.jar" + fi + while IFS="=" read key value; do + case "$key" in (wrapperUrl) jarUrl="$value"; break ;; + esac + done < "$BASE_DIR/.mvn/wrapper/maven-wrapper.properties" + if [ "$MVNW_VERBOSE" = true ]; then + echo "Downloading from: $jarUrl" + fi + wrapperJarPath="$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" + if $cygwin; then + wrapperJarPath=`cygpath --path --windows "$wrapperJarPath"` + fi + + if command -v wget > /dev/null; then + if [ "$MVNW_VERBOSE" = true ]; then + echo "Found wget ... using wget" + fi + if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then + wget "$jarUrl" -O "$wrapperJarPath" || rm -f "$wrapperJarPath" + else + wget --http-user=$MVNW_USERNAME --http-password=$MVNW_PASSWORD "$jarUrl" -O "$wrapperJarPath" || rm -f "$wrapperJarPath" + fi + elif command -v curl > /dev/null; then + if [ "$MVNW_VERBOSE" = true ]; then + echo "Found curl ... using curl" + fi + if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then + curl -o "$wrapperJarPath" "$jarUrl" -f + else + curl --user $MVNW_USERNAME:$MVNW_PASSWORD -o "$wrapperJarPath" "$jarUrl" -f + fi + + else + if [ "$MVNW_VERBOSE" = true ]; then + echo "Falling back to using Java to download" + fi + javaClass="$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.java" + # For Cygwin, switch paths to Windows format before running javac + if $cygwin; then + javaClass=`cygpath --path --windows "$javaClass"` + fi + if [ -e "$javaClass" ]; then + if [ ! -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then + if [ "$MVNW_VERBOSE" = true ]; then + echo " - Compiling MavenWrapperDownloader.java ..." + fi + # Compiling the Java class + ("$JAVA_HOME/bin/javac" "$javaClass") + fi + if [ -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then + # Running the downloader + if [ "$MVNW_VERBOSE" = true ]; then + echo " - Running MavenWrapperDownloader.java ..." + fi + ("$JAVA_HOME/bin/java" -cp .mvn/wrapper MavenWrapperDownloader "$MAVEN_PROJECTBASEDIR") + fi + fi + fi +fi +########################################################################################## +# End of extension +########################################################################################## + +export MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-"$BASE_DIR"} +if [ "$MVNW_VERBOSE" = true ]; then + echo $MAVEN_PROJECTBASEDIR +fi +MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS" + +# For Cygwin, switch paths to Windows format before running java +if $cygwin; then + [ -n "$M2_HOME" ] && + M2_HOME=`cygpath --path --windows "$M2_HOME"` + [ -n "$JAVA_HOME" ] && + JAVA_HOME=`cygpath --path --windows "$JAVA_HOME"` + [ -n "$CLASSPATH" ] && + CLASSPATH=`cygpath --path --windows "$CLASSPATH"` + [ -n "$MAVEN_PROJECTBASEDIR" ] && + MAVEN_PROJECTBASEDIR=`cygpath --path --windows "$MAVEN_PROJECTBASEDIR"` +fi + +# Provide a "standardized" way to retrieve the CLI args that will +# work with both Windows and non-Windows executions. +MAVEN_CMD_LINE_ARGS="$MAVEN_CONFIG $@" +export MAVEN_CMD_LINE_ARGS + +WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain + +exec "$JAVACMD" \ + $MAVEN_OPTS \ + $MAVEN_DEBUG_OPTS \ + -classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \ + "-Dmaven.home=${M2_HOME}" \ + "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \ + ${WRAPPER_LAUNCHER} $MAVEN_CONFIG "$@" diff --git a/cross_cutting/k8s/basic_example/server/original_server/mvnw.cmd b/cross_cutting/k8s/basic_example/server/original_server/mvnw.cmd new file mode 100644 index 0000000..1d8ab01 --- /dev/null +++ b/cross_cutting/k8s/basic_example/server/original_server/mvnw.cmd @@ -0,0 +1,188 @@ +@REM ---------------------------------------------------------------------------- +@REM Licensed to the Apache Software Foundation (ASF) under one +@REM or more contributor license agreements. See the NOTICE file +@REM distributed with this work for additional information +@REM regarding copyright ownership. The ASF licenses this file +@REM to you under the Apache License, Version 2.0 (the +@REM "License"); you may not use this file except in compliance +@REM with the License. You may obtain a copy of the License at +@REM +@REM https://www.apache.org/licenses/LICENSE-2.0 +@REM +@REM Unless required by applicable law or agreed to in writing, +@REM software distributed under the License is distributed on an +@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +@REM KIND, either express or implied. See the License for the +@REM specific language governing permissions and limitations +@REM under the License. +@REM ---------------------------------------------------------------------------- + +@REM ---------------------------------------------------------------------------- +@REM Maven Start Up Batch script +@REM +@REM Required ENV vars: +@REM JAVA_HOME - location of a JDK home dir +@REM +@REM Optional ENV vars +@REM M2_HOME - location of maven2's installed home dir +@REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands +@REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a keystroke before ending +@REM MAVEN_OPTS - parameters passed to the Java VM when running Maven +@REM e.g. to debug Maven itself, use +@REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 +@REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files +@REM ---------------------------------------------------------------------------- + +@REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on' +@echo off +@REM set title of command window +title %0 +@REM enable echoing by setting MAVEN_BATCH_ECHO to 'on' +@if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO% + +@REM set %HOME% to equivalent of $HOME +if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%") + +@REM Execute a user defined script before this one +if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre +@REM check for pre script, once with legacy .bat ending and once with .cmd ending +if exist "%USERPROFILE%\mavenrc_pre.bat" call "%USERPROFILE%\mavenrc_pre.bat" %* +if exist "%USERPROFILE%\mavenrc_pre.cmd" call "%USERPROFILE%\mavenrc_pre.cmd" %* +:skipRcPre + +@setlocal + +set ERROR_CODE=0 + +@REM To isolate internal variables from possible post scripts, we use another setlocal +@setlocal + +@REM ==== START VALIDATION ==== +if not "%JAVA_HOME%" == "" goto OkJHome + +echo. +echo Error: JAVA_HOME not found in your environment. >&2 +echo Please set the JAVA_HOME variable in your environment to match the >&2 +echo location of your Java installation. >&2 +echo. +goto error + +:OkJHome +if exist "%JAVA_HOME%\bin\java.exe" goto init + +echo. +echo Error: JAVA_HOME is set to an invalid directory. >&2 +echo JAVA_HOME = "%JAVA_HOME%" >&2 +echo Please set the JAVA_HOME variable in your environment to match the >&2 +echo location of your Java installation. >&2 +echo. +goto error + +@REM ==== END VALIDATION ==== + +:init + +@REM Find the project base dir, i.e. the directory that contains the folder ".mvn". +@REM Fallback to current working directory if not found. + +set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR% +IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir + +set EXEC_DIR=%CD% +set WDIR=%EXEC_DIR% +:findBaseDir +IF EXIST "%WDIR%"\.mvn goto baseDirFound +cd .. +IF "%WDIR%"=="%CD%" goto baseDirNotFound +set WDIR=%CD% +goto findBaseDir + +:baseDirFound +set MAVEN_PROJECTBASEDIR=%WDIR% +cd "%EXEC_DIR%" +goto endDetectBaseDir + +:baseDirNotFound +set MAVEN_PROJECTBASEDIR=%EXEC_DIR% +cd "%EXEC_DIR%" + +:endDetectBaseDir + +IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig + +@setlocal EnableExtensions EnableDelayedExpansion +for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a +@endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS% + +:endReadAdditionalConfig + +SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe" +set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar" +set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain + +set DOWNLOAD_URL="https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.1.0/maven-wrapper-3.1.0.jar" + +FOR /F "usebackq tokens=1,2 delims==" %%A IN ("%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties") DO ( + IF "%%A"=="wrapperUrl" SET DOWNLOAD_URL=%%B +) + +@REM Extension to allow automatically downloading the maven-wrapper.jar from Maven-central +@REM This allows using the maven wrapper in projects that prohibit checking in binary data. +if exist %WRAPPER_JAR% ( + if "%MVNW_VERBOSE%" == "true" ( + echo Found %WRAPPER_JAR% + ) +) else ( + if not "%MVNW_REPOURL%" == "" ( + SET DOWNLOAD_URL="%MVNW_REPOURL%/org/apache/maven/wrapper/maven-wrapper/3.1.0/maven-wrapper-3.1.0.jar" + ) + if "%MVNW_VERBOSE%" == "true" ( + echo Couldn't find %WRAPPER_JAR%, downloading it ... + echo Downloading from: %DOWNLOAD_URL% + ) + + powershell -Command "&{"^ + "$webclient = new-object System.Net.WebClient;"^ + "if (-not ([string]::IsNullOrEmpty('%MVNW_USERNAME%') -and [string]::IsNullOrEmpty('%MVNW_PASSWORD%'))) {"^ + "$webclient.Credentials = new-object System.Net.NetworkCredential('%MVNW_USERNAME%', '%MVNW_PASSWORD%');"^ + "}"^ + "[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; $webclient.DownloadFile('%DOWNLOAD_URL%', '%WRAPPER_JAR%')"^ + "}" + if "%MVNW_VERBOSE%" == "true" ( + echo Finished downloading %WRAPPER_JAR% + ) +) +@REM End of extension + +@REM Provide a "standardized" way to retrieve the CLI args that will +@REM work with both Windows and non-Windows executions. +set MAVEN_CMD_LINE_ARGS=%* + +%MAVEN_JAVA_EXE% ^ + %JVM_CONFIG_MAVEN_PROPS% ^ + %MAVEN_OPTS% ^ + %MAVEN_DEBUG_OPTS% ^ + -classpath %WRAPPER_JAR% ^ + "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" ^ + %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %* +if ERRORLEVEL 1 goto error +goto end + +:error +set ERROR_CODE=1 + +:end +@endlocal & set ERROR_CODE=%ERROR_CODE% + +if not "%MAVEN_SKIP_RC%"=="" goto skipRcPost +@REM check for post script, once with legacy .bat ending and once with .cmd ending +if exist "%USERPROFILE%\mavenrc_post.bat" call "%USERPROFILE%\mavenrc_post.bat" +if exist "%USERPROFILE%\mavenrc_post.cmd" call "%USERPROFILE%\mavenrc_post.cmd" +:skipRcPost + +@REM pause the script if MAVEN_BATCH_PAUSE is set to 'on' +if "%MAVEN_BATCH_PAUSE%"=="on" pause + +if "%MAVEN_TERMINATE_CMD%"=="on" exit %ERROR_CODE% + +cmd /C exit /B %ERROR_CODE% diff --git a/cross_cutting/k8s/basic_example/server/original_server/pom.xml b/cross_cutting/k8s/basic_example/server/original_server/pom.xml new file mode 100644 index 0000000..81b4986 --- /dev/null +++ b/cross_cutting/k8s/basic_example/server/original_server/pom.xml @@ -0,0 +1,104 @@ + + + 4.0.0 + + org.springframework.boot + spring-boot-starter-parent + 2.7.3 + + + com.devonfw.devon4j.examples.cloud + original_server + 0.0.1-SNAPSHOT + openapidemo + The original server the data comes from + + 11 + + + + org.springframework.boot + spring-boot-starter-web + + + + + io.swagger.parser.v3 + swagger-parser + 2.1.2 + + + org.openapitools + jackson-databind-nullable + 0.2.3 + + + + + + com.github.tomakehurst + wiremock-jre8 + 2.34.0 + test + + + org.springframework.boot + spring-boot-starter-test + test + + + + + + + org.springframework.boot + spring-boot-starter-data-jpa + + + + mysql + mysql-connector-java + runtime + + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + org.openapitools + openapi-generator-maven-plugin + 6.1.0 + + + server-generator + + generate + + + ${project.basedir}/src/main/resources/MyThaiApi.yml + ${project.build.directory}/generated-sources/server + spring + spring-boot + TO + com.devonfw.devon4j.generated.api.service + com.devonfw.devon4j.generated.api.model + com.devonfw.devon4j.generated.api.handler + + src/java/main + true + true + true + + + + + + + + diff --git a/cross_cutting/k8s/basic_example/server/original_server/src/main/java/com/devonfw/devon4j/examples/service/openapidemo/OpenapidemoApplication.java b/cross_cutting/k8s/basic_example/server/original_server/src/main/java/com/devonfw/devon4j/examples/service/openapidemo/OpenapidemoApplication.java new file mode 100644 index 0000000..478f312 --- /dev/null +++ b/cross_cutting/k8s/basic_example/server/original_server/src/main/java/com/devonfw/devon4j/examples/service/openapidemo/OpenapidemoApplication.java @@ -0,0 +1,13 @@ +package com.devonfw.devon4j.examples.service.openapidemo; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +public class OpenapidemoApplication { + + public static void main(String[] args) { + SpringApplication.run(OpenapidemoApplication.class, args); + } + +} diff --git a/cross_cutting/k8s/basic_example/server/original_server/src/main/java/com/devonfw/devon4j/examples/service/openapidemo/invitedGuest/domain/InvitedGuest.java b/cross_cutting/k8s/basic_example/server/original_server/src/main/java/com/devonfw/devon4j/examples/service/openapidemo/invitedGuest/domain/InvitedGuest.java new file mode 100644 index 0000000..bccad2d --- /dev/null +++ b/cross_cutting/k8s/basic_example/server/original_server/src/main/java/com/devonfw/devon4j/examples/service/openapidemo/invitedGuest/domain/InvitedGuest.java @@ -0,0 +1,44 @@ +package com.devonfw.devon4j.examples.service.openapidemo.invitedGuest.domain; + +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.Id; +import javax.persistence.Version; + + +@Entity +public class InvitedGuest { + @Id + @GeneratedValue + private Long id; + + @Version + private Integer modificationCounter = 0; + + private String email; + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public Integer getModificationCounter() { + return modificationCounter; + } + + public void setModificationCounter(Integer modificationCounter) { + this.modificationCounter = modificationCounter; + } + + public String getEmail() { + return email; + } + + public void setEmail(String email) { + this.email = email; + } +} + diff --git a/cross_cutting/k8s/basic_example/server/original_server/src/main/java/com/devonfw/devon4j/examples/service/openapidemo/invitedGuest/domain/InvitedGuestRepository.java b/cross_cutting/k8s/basic_example/server/original_server/src/main/java/com/devonfw/devon4j/examples/service/openapidemo/invitedGuest/domain/InvitedGuestRepository.java new file mode 100644 index 0000000..c36d4f6 --- /dev/null +++ b/cross_cutting/k8s/basic_example/server/original_server/src/main/java/com/devonfw/devon4j/examples/service/openapidemo/invitedGuest/domain/InvitedGuestRepository.java @@ -0,0 +1,8 @@ +package com.devonfw.devon4j.examples.service.openapidemo.invitedGuest.domain; + +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +@Repository +public interface InvitedGuestRepository extends JpaRepository { +} diff --git a/cross_cutting/k8s/basic_example/server/original_server/src/main/java/com/devonfw/devon4j/examples/service/openapidemo/invitedGuest/logic/InvitedGuestManager.java b/cross_cutting/k8s/basic_example/server/original_server/src/main/java/com/devonfw/devon4j/examples/service/openapidemo/invitedGuest/logic/InvitedGuestManager.java new file mode 100644 index 0000000..fe2ef30 --- /dev/null +++ b/cross_cutting/k8s/basic_example/server/original_server/src/main/java/com/devonfw/devon4j/examples/service/openapidemo/invitedGuest/logic/InvitedGuestManager.java @@ -0,0 +1,51 @@ +package com.devonfw.devon4j.examples.service.openapidemo.invitedGuest.logic; + +import com.devonfw.devon4j.examples.service.openapidemo.invitedGuest.domain.InvitedGuest; +import com.devonfw.devon4j.examples.service.openapidemo.invitedGuest.domain.InvitedGuestRepository; +import com.devonfw.devon4j.generated.api.model.InvitedGuestTO; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.util.ArrayList; +import java.util.List; + +@Service +public class InvitedGuestManager { + + @Autowired + InvitedGuestRepository invitedGuestRepository; + + + public List getAllGuest() { + List foundGuests = invitedGuestRepository.findAll(); + List resultGuests = new ArrayList<>(); + foundGuests.forEach(guest -> { + InvitedGuestTO resultGuest = new InvitedGuestTO(); + resultGuest + .id(guest.getId()) + .email(guest.getEmail()) + .modificationCounter(guest.getModificationCounter()); + resultGuests.add(resultGuest); + }); + return resultGuests; + } + + public InvitedGuestTO getGuest(Long id) { + InvitedGuest foundGuest = invitedGuestRepository.getReferenceById(id); + InvitedGuestTO resultGuest = new InvitedGuestTO(); + resultGuest + .id(foundGuest.getId()) + .email(foundGuest.getEmail()) + .modificationCounter(foundGuest.getModificationCounter()); + return resultGuest; + } + + public InvitedGuestTO createInvitedGuest(InvitedGuestTO invitedGuestTO) { + InvitedGuest guest = new InvitedGuest(); + guest.setId(invitedGuestTO.getId()); + guest.setModificationCounter(invitedGuestTO.getModificationCounter()); + guest.setEmail(invitedGuestTO.getEmail()); + invitedGuestRepository.save(guest); + return invitedGuestTO; + } +} diff --git a/cross_cutting/k8s/basic_example/server/original_server/src/main/java/com/devonfw/devon4j/examples/service/openapidemo/invitedGuest/service/InvitedGuestController.java b/cross_cutting/k8s/basic_example/server/original_server/src/main/java/com/devonfw/devon4j/examples/service/openapidemo/invitedGuest/service/InvitedGuestController.java new file mode 100644 index 0000000..d02e767 --- /dev/null +++ b/cross_cutting/k8s/basic_example/server/original_server/src/main/java/com/devonfw/devon4j/examples/service/openapidemo/invitedGuest/service/InvitedGuestController.java @@ -0,0 +1,35 @@ +package com.devonfw.devon4j.examples.service.openapidemo.invitedGuest.service; + +import com.devonfw.devon4j.examples.service.openapidemo.invitedGuest.logic.InvitedGuestManager; +import com.devonfw.devon4j.generated.api.model.InvitedGuestTO; +import com.devonfw.devon4j.generated.api.service.InvitedGuestApi; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.RestController; + +import java.util.List; + +@RestController +public class InvitedGuestController implements InvitedGuestApi { + + InvitedGuestManager invitedGuestManager; + + public InvitedGuestController(InvitedGuestManager invitedGuestManager) { + this.invitedGuestManager = invitedGuestManager; + } + + @Override + public ResponseEntity createInvitedGuest(InvitedGuestTO invitedGuestTO) { + return new ResponseEntity<>(invitedGuestManager.createInvitedGuest(invitedGuestTO), HttpStatus.CREATED); + } + + @Override + public ResponseEntity> getInvitedGuestAll() { + return new ResponseEntity<>(invitedGuestManager.getAllGuest(), HttpStatus.OK); + } + + @Override + public ResponseEntity getInvitedGuestById(Long guestId) { + return new ResponseEntity<>(invitedGuestManager.getGuest(guestId), HttpStatus.OK); + } +} diff --git a/cross_cutting/k8s/basic_example/server/original_server/src/main/resources/MyThaiApi.yml b/cross_cutting/k8s/basic_example/server/original_server/src/main/resources/MyThaiApi.yml new file mode 100644 index 0000000..c779573 --- /dev/null +++ b/cross_cutting/k8s/basic_example/server/original_server/src/main/resources/MyThaiApi.yml @@ -0,0 +1,181 @@ +openapi: '3.0.2' +info: + title: My Thai Star REST API + description: |- + This API file is just an example to show the options when creting an OpenAPI file. + This Api is a small example for the My Thai Star + version: '1.0' + contact: + email: contact@mail.de +servers: + - url: http://localhost:8080/api/v1 +paths: + + /booking: + post: + tags: + - "Booking" + summary: Create a new Booking + description: Creates and retruns a new Booking + operationId: createBooking + responses: + '201': + description: "Created" + content: + application/json: + schema: + $ref: '#/components/schemas/Booking' + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/Booking' + + get: + tags: + - "Booking" + summary: Get all Bookings + description: Returns a list of bookings + operationId: getBookingAll + responses: + '200': + description: OK + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/Booking' + + + /booking/{bookingId}: + get: + tags: + - "Booking" + summary: Find a single booking by Id + description: Returns a single booking + operationId: getBookingById + parameters: + - name: bookingId + in: path + description: ID of booking to return + required: true + schema: + type: integer + format: int64 + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/Booking' + + '400': + description: Invalid ID supplied + '404': + description: Booking not found + + /invitedGuest: + post: + tags: + - "InvitedGuest" + summary: Create a new invited guests + description: Creates and retruns a new invited guests + operationId: createInvitedGuest + responses: + '201': + description: "Created" + content: + application/json: + schema: + $ref: '#/components/schemas/InvitedGuest' + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/InvitedGuest' + + get: + tags: + - "InvitedGuest" + summary: Get all invited guests + description: Returns a list of invited guests + operationId: getInvitedGuestAll + responses: + '200': + description: OK + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/InvitedGuest' + + /invitedGuest/{guestId}: + get: + tags: + - "InvitedGuest" + summary: Find a single InvitedGuest by Id + description: Returns a single InvitedGuest + operationId: getInvitedGuestById + parameters: + - name: guestId + in: path + description: ID of guest to return + required: true + schema: + type: integer + format: int64 + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/InvitedGuest' + + '400': + description: Invalid ID supplied + '404': + description: InvitedGuest not found + +components: + schemas: + + InvitedGuest: + type: object + properties: + id: + type: integer + format: int64 + example: 1 + modificationCounter: + type: integer + format: int32 + default: 0 + example: 0 + email: + type: string + example: "guest.email@email.com" + + Booking: + type: object + properties: + id: + type: integer + format: int64 + example: 1 + modificationCounter: + type: integer + format: int32 + default: 0 + example: 0 + invitedGuests: + type: array + xml: + wrapped: true + items: + $ref: '#/components/schemas/InvitedGuest' + example: [] + default: [] \ No newline at end of file diff --git a/cross_cutting/k8s/basic_example/server/original_server/src/main/resources/application.properties b/cross_cutting/k8s/basic_example/server/original_server/src/main/resources/application.properties new file mode 100644 index 0000000..ccf8b74 --- /dev/null +++ b/cross_cutting/k8s/basic_example/server/original_server/src/main/resources/application.properties @@ -0,0 +1,5 @@ +spring.jpa.hibernate.ddl-auto=update +spring.datasource.url=jdbc:mysql://${MYSQL_HOST:localhost}:3306/db_example +spring.datasource.username=${MYSQL_ROOT_USERNAME:root} +spring.datasource.password=${MYSQL_ROOT_PASSWORD:password} +spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver \ No newline at end of file diff --git a/cross_cutting/k8s/basic_example/server/original_server/src/test/resources/test.properties b/cross_cutting/k8s/basic_example/server/original_server/src/test/resources/test.properties new file mode 100644 index 0000000..7095742 --- /dev/null +++ b/cross_cutting/k8s/basic_example/server/original_server/src/test/resources/test.properties @@ -0,0 +1 @@ +spring.main.allow-bean-definition-overriding=true \ No newline at end of file diff --git a/cross_cutting/k8s/oauth2_proxy/ProxyTestServer/.gitignore b/cross_cutting/k8s/oauth2_proxy/ProxyTestServer/.gitignore new file mode 100644 index 0000000..549e00a --- /dev/null +++ b/cross_cutting/k8s/oauth2_proxy/ProxyTestServer/.gitignore @@ -0,0 +1,33 @@ +HELP.md +target/ +!.mvn/wrapper/maven-wrapper.jar +!**/src/main/**/target/ +!**/src/test/**/target/ + +### STS ### +.apt_generated +.classpath +.factorypath +.project +.settings +.springBeans +.sts4-cache + +### IntelliJ IDEA ### +.idea +*.iws +*.iml +*.ipr + +### NetBeans ### +/nbproject/private/ +/nbbuild/ +/dist/ +/nbdist/ +/.nb-gradle/ +build/ +!**/src/main/**/build/ +!**/src/test/**/build/ + +### VS Code ### +.vscode/ diff --git a/cross_cutting/k8s/oauth2_proxy/ProxyTestServer/.mvn/wrapper/maven-wrapper.jar b/cross_cutting/k8s/oauth2_proxy/ProxyTestServer/.mvn/wrapper/maven-wrapper.jar new file mode 100644 index 0000000..c1dd12f Binary files /dev/null and b/cross_cutting/k8s/oauth2_proxy/ProxyTestServer/.mvn/wrapper/maven-wrapper.jar differ diff --git a/cross_cutting/k8s/oauth2_proxy/ProxyTestServer/.mvn/wrapper/maven-wrapper.properties b/cross_cutting/k8s/oauth2_proxy/ProxyTestServer/.mvn/wrapper/maven-wrapper.properties new file mode 100644 index 0000000..b74bf7f --- /dev/null +++ b/cross_cutting/k8s/oauth2_proxy/ProxyTestServer/.mvn/wrapper/maven-wrapper.properties @@ -0,0 +1,2 @@ +distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.8.6/apache-maven-3.8.6-bin.zip +wrapperUrl=https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.1.0/maven-wrapper-3.1.0.jar diff --git a/cross_cutting/k8s/oauth2_proxy/ProxyTestServer/Dockerfile b/cross_cutting/k8s/oauth2_proxy/ProxyTestServer/Dockerfile new file mode 100644 index 0000000..24224bd --- /dev/null +++ b/cross_cutting/k8s/oauth2_proxy/ProxyTestServer/Dockerfile @@ -0,0 +1,6 @@ +FROM adoptopenjdk/openjdk11:alpine-jre +RUN mkdir /opt/app +WORKDIR /opt/app +ARG JAR_FILE=target/spring_server-0.0.1-SNAPSHOT.jar +COPY ${JAR_FILE} app.jar +CMD ["java", "-jar", "/opt/app/app.jar"] \ No newline at end of file diff --git a/cross_cutting/k8s/oauth2_proxy/ProxyTestServer/mvnw b/cross_cutting/k8s/oauth2_proxy/ProxyTestServer/mvnw new file mode 100644 index 0000000..8a8fb22 --- /dev/null +++ b/cross_cutting/k8s/oauth2_proxy/ProxyTestServer/mvnw @@ -0,0 +1,316 @@ +#!/bin/sh +# ---------------------------------------------------------------------------- +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# ---------------------------------------------------------------------------- + +# ---------------------------------------------------------------------------- +# Maven Start Up Batch script +# +# Required ENV vars: +# ------------------ +# JAVA_HOME - location of a JDK home dir +# +# Optional ENV vars +# ----------------- +# M2_HOME - location of maven2's installed home dir +# MAVEN_OPTS - parameters passed to the Java VM when running Maven +# e.g. to debug Maven itself, use +# set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 +# MAVEN_SKIP_RC - flag to disable loading of mavenrc files +# ---------------------------------------------------------------------------- + +if [ -z "$MAVEN_SKIP_RC" ] ; then + + if [ -f /usr/local/etc/mavenrc ] ; then + . /usr/local/etc/mavenrc + fi + + if [ -f /etc/mavenrc ] ; then + . /etc/mavenrc + fi + + if [ -f "$HOME/.mavenrc" ] ; then + . "$HOME/.mavenrc" + fi + +fi + +# OS specific support. $var _must_ be set to either true or false. +cygwin=false; +darwin=false; +mingw=false +case "`uname`" in + CYGWIN*) cygwin=true ;; + MINGW*) mingw=true;; + Darwin*) darwin=true + # Use /usr/libexec/java_home if available, otherwise fall back to /Library/Java/Home + # See https://developer.apple.com/library/mac/qa/qa1170/_index.html + if [ -z "$JAVA_HOME" ]; then + if [ -x "/usr/libexec/java_home" ]; then + export JAVA_HOME="`/usr/libexec/java_home`" + else + export JAVA_HOME="/Library/Java/Home" + fi + fi + ;; +esac + +if [ -z "$JAVA_HOME" ] ; then + if [ -r /etc/gentoo-release ] ; then + JAVA_HOME=`java-config --jre-home` + fi +fi + +if [ -z "$M2_HOME" ] ; then + ## resolve links - $0 may be a link to maven's home + PRG="$0" + + # need this for relative symlinks + while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG="`dirname "$PRG"`/$link" + fi + done + + saveddir=`pwd` + + M2_HOME=`dirname "$PRG"`/.. + + # make it fully qualified + M2_HOME=`cd "$M2_HOME" && pwd` + + cd "$saveddir" + # echo Using m2 at $M2_HOME +fi + +# For Cygwin, ensure paths are in UNIX format before anything is touched +if $cygwin ; then + [ -n "$M2_HOME" ] && + M2_HOME=`cygpath --unix "$M2_HOME"` + [ -n "$JAVA_HOME" ] && + JAVA_HOME=`cygpath --unix "$JAVA_HOME"` + [ -n "$CLASSPATH" ] && + CLASSPATH=`cygpath --path --unix "$CLASSPATH"` +fi + +# For Mingw, ensure paths are in UNIX format before anything is touched +if $mingw ; then + [ -n "$M2_HOME" ] && + M2_HOME="`(cd "$M2_HOME"; pwd)`" + [ -n "$JAVA_HOME" ] && + JAVA_HOME="`(cd "$JAVA_HOME"; pwd)`" +fi + +if [ -z "$JAVA_HOME" ]; then + javaExecutable="`which javac`" + if [ -n "$javaExecutable" ] && ! [ "`expr \"$javaExecutable\" : '\([^ ]*\)'`" = "no" ]; then + # readlink(1) is not available as standard on Solaris 10. + readLink=`which readlink` + if [ ! `expr "$readLink" : '\([^ ]*\)'` = "no" ]; then + if $darwin ; then + javaHome="`dirname \"$javaExecutable\"`" + javaExecutable="`cd \"$javaHome\" && pwd -P`/javac" + else + javaExecutable="`readlink -f \"$javaExecutable\"`" + fi + javaHome="`dirname \"$javaExecutable\"`" + javaHome=`expr "$javaHome" : '\(.*\)/bin'` + JAVA_HOME="$javaHome" + export JAVA_HOME + fi + fi +fi + +if [ -z "$JAVACMD" ] ; then + if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + else + JAVACMD="`\\unset -f command; \\command -v java`" + fi +fi + +if [ ! -x "$JAVACMD" ] ; then + echo "Error: JAVA_HOME is not defined correctly." >&2 + echo " We cannot execute $JAVACMD" >&2 + exit 1 +fi + +if [ -z "$JAVA_HOME" ] ; then + echo "Warning: JAVA_HOME environment variable is not set." +fi + +CLASSWORLDS_LAUNCHER=org.codehaus.plexus.classworlds.launcher.Launcher + +# traverses directory structure from process work directory to filesystem root +# first directory with .mvn subdirectory is considered project base directory +find_maven_basedir() { + + if [ -z "$1" ] + then + echo "Path not specified to find_maven_basedir" + return 1 + fi + + basedir="$1" + wdir="$1" + while [ "$wdir" != '/' ] ; do + if [ -d "$wdir"/.mvn ] ; then + basedir=$wdir + break + fi + # workaround for JBEAP-8937 (on Solaris 10/Sparc) + if [ -d "${wdir}" ]; then + wdir=`cd "$wdir/.."; pwd` + fi + # end of workaround + done + echo "${basedir}" +} + +# concatenates all lines of a file +concat_lines() { + if [ -f "$1" ]; then + echo "$(tr -s '\n' ' ' < "$1")" + fi +} + +BASE_DIR=`find_maven_basedir "$(pwd)"` +if [ -z "$BASE_DIR" ]; then + exit 1; +fi + +########################################################################################## +# Extension to allow automatically downloading the maven-wrapper.jar from Maven-central +# This allows using the maven wrapper in projects that prohibit checking in binary data. +########################################################################################## +if [ -r "$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" ]; then + if [ "$MVNW_VERBOSE" = true ]; then + echo "Found .mvn/wrapper/maven-wrapper.jar" + fi +else + if [ "$MVNW_VERBOSE" = true ]; then + echo "Couldn't find .mvn/wrapper/maven-wrapper.jar, downloading it ..." + fi + if [ -n "$MVNW_REPOURL" ]; then + jarUrl="$MVNW_REPOURL/org/apache/maven/wrapper/maven-wrapper/3.1.0/maven-wrapper-3.1.0.jar" + else + jarUrl="https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.1.0/maven-wrapper-3.1.0.jar" + fi + while IFS="=" read key value; do + case "$key" in (wrapperUrl) jarUrl="$value"; break ;; + esac + done < "$BASE_DIR/.mvn/wrapper/maven-wrapper.properties" + if [ "$MVNW_VERBOSE" = true ]; then + echo "Downloading from: $jarUrl" + fi + wrapperJarPath="$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" + if $cygwin; then + wrapperJarPath=`cygpath --path --windows "$wrapperJarPath"` + fi + + if command -v wget > /dev/null; then + if [ "$MVNW_VERBOSE" = true ]; then + echo "Found wget ... using wget" + fi + if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then + wget "$jarUrl" -O "$wrapperJarPath" || rm -f "$wrapperJarPath" + else + wget --http-user=$MVNW_USERNAME --http-password=$MVNW_PASSWORD "$jarUrl" -O "$wrapperJarPath" || rm -f "$wrapperJarPath" + fi + elif command -v curl > /dev/null; then + if [ "$MVNW_VERBOSE" = true ]; then + echo "Found curl ... using curl" + fi + if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then + curl -o "$wrapperJarPath" "$jarUrl" -f + else + curl --user $MVNW_USERNAME:$MVNW_PASSWORD -o "$wrapperJarPath" "$jarUrl" -f + fi + + else + if [ "$MVNW_VERBOSE" = true ]; then + echo "Falling back to using Java to download" + fi + javaClass="$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.java" + # For Cygwin, switch paths to Windows format before running javac + if $cygwin; then + javaClass=`cygpath --path --windows "$javaClass"` + fi + if [ -e "$javaClass" ]; then + if [ ! -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then + if [ "$MVNW_VERBOSE" = true ]; then + echo " - Compiling MavenWrapperDownloader.java ..." + fi + # Compiling the Java class + ("$JAVA_HOME/bin/javac" "$javaClass") + fi + if [ -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then + # Running the downloader + if [ "$MVNW_VERBOSE" = true ]; then + echo " - Running MavenWrapperDownloader.java ..." + fi + ("$JAVA_HOME/bin/java" -cp .mvn/wrapper MavenWrapperDownloader "$MAVEN_PROJECTBASEDIR") + fi + fi + fi +fi +########################################################################################## +# End of extension +########################################################################################## + +export MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-"$BASE_DIR"} +if [ "$MVNW_VERBOSE" = true ]; then + echo $MAVEN_PROJECTBASEDIR +fi +MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS" + +# For Cygwin, switch paths to Windows format before running java +if $cygwin; then + [ -n "$M2_HOME" ] && + M2_HOME=`cygpath --path --windows "$M2_HOME"` + [ -n "$JAVA_HOME" ] && + JAVA_HOME=`cygpath --path --windows "$JAVA_HOME"` + [ -n "$CLASSPATH" ] && + CLASSPATH=`cygpath --path --windows "$CLASSPATH"` + [ -n "$MAVEN_PROJECTBASEDIR" ] && + MAVEN_PROJECTBASEDIR=`cygpath --path --windows "$MAVEN_PROJECTBASEDIR"` +fi + +# Provide a "standardized" way to retrieve the CLI args that will +# work with both Windows and non-Windows executions. +MAVEN_CMD_LINE_ARGS="$MAVEN_CONFIG $@" +export MAVEN_CMD_LINE_ARGS + +WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain + +exec "$JAVACMD" \ + $MAVEN_OPTS \ + $MAVEN_DEBUG_OPTS \ + -classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \ + "-Dmaven.home=${M2_HOME}" \ + "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \ + ${WRAPPER_LAUNCHER} $MAVEN_CONFIG "$@" diff --git a/cross_cutting/k8s/oauth2_proxy/ProxyTestServer/mvnw.cmd b/cross_cutting/k8s/oauth2_proxy/ProxyTestServer/mvnw.cmd new file mode 100644 index 0000000..1d8ab01 --- /dev/null +++ b/cross_cutting/k8s/oauth2_proxy/ProxyTestServer/mvnw.cmd @@ -0,0 +1,188 @@ +@REM ---------------------------------------------------------------------------- +@REM Licensed to the Apache Software Foundation (ASF) under one +@REM or more contributor license agreements. See the NOTICE file +@REM distributed with this work for additional information +@REM regarding copyright ownership. The ASF licenses this file +@REM to you under the Apache License, Version 2.0 (the +@REM "License"); you may not use this file except in compliance +@REM with the License. You may obtain a copy of the License at +@REM +@REM https://www.apache.org/licenses/LICENSE-2.0 +@REM +@REM Unless required by applicable law or agreed to in writing, +@REM software distributed under the License is distributed on an +@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +@REM KIND, either express or implied. See the License for the +@REM specific language governing permissions and limitations +@REM under the License. +@REM ---------------------------------------------------------------------------- + +@REM ---------------------------------------------------------------------------- +@REM Maven Start Up Batch script +@REM +@REM Required ENV vars: +@REM JAVA_HOME - location of a JDK home dir +@REM +@REM Optional ENV vars +@REM M2_HOME - location of maven2's installed home dir +@REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands +@REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a keystroke before ending +@REM MAVEN_OPTS - parameters passed to the Java VM when running Maven +@REM e.g. to debug Maven itself, use +@REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 +@REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files +@REM ---------------------------------------------------------------------------- + +@REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on' +@echo off +@REM set title of command window +title %0 +@REM enable echoing by setting MAVEN_BATCH_ECHO to 'on' +@if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO% + +@REM set %HOME% to equivalent of $HOME +if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%") + +@REM Execute a user defined script before this one +if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre +@REM check for pre script, once with legacy .bat ending and once with .cmd ending +if exist "%USERPROFILE%\mavenrc_pre.bat" call "%USERPROFILE%\mavenrc_pre.bat" %* +if exist "%USERPROFILE%\mavenrc_pre.cmd" call "%USERPROFILE%\mavenrc_pre.cmd" %* +:skipRcPre + +@setlocal + +set ERROR_CODE=0 + +@REM To isolate internal variables from possible post scripts, we use another setlocal +@setlocal + +@REM ==== START VALIDATION ==== +if not "%JAVA_HOME%" == "" goto OkJHome + +echo. +echo Error: JAVA_HOME not found in your environment. >&2 +echo Please set the JAVA_HOME variable in your environment to match the >&2 +echo location of your Java installation. >&2 +echo. +goto error + +:OkJHome +if exist "%JAVA_HOME%\bin\java.exe" goto init + +echo. +echo Error: JAVA_HOME is set to an invalid directory. >&2 +echo JAVA_HOME = "%JAVA_HOME%" >&2 +echo Please set the JAVA_HOME variable in your environment to match the >&2 +echo location of your Java installation. >&2 +echo. +goto error + +@REM ==== END VALIDATION ==== + +:init + +@REM Find the project base dir, i.e. the directory that contains the folder ".mvn". +@REM Fallback to current working directory if not found. + +set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR% +IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir + +set EXEC_DIR=%CD% +set WDIR=%EXEC_DIR% +:findBaseDir +IF EXIST "%WDIR%"\.mvn goto baseDirFound +cd .. +IF "%WDIR%"=="%CD%" goto baseDirNotFound +set WDIR=%CD% +goto findBaseDir + +:baseDirFound +set MAVEN_PROJECTBASEDIR=%WDIR% +cd "%EXEC_DIR%" +goto endDetectBaseDir + +:baseDirNotFound +set MAVEN_PROJECTBASEDIR=%EXEC_DIR% +cd "%EXEC_DIR%" + +:endDetectBaseDir + +IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig + +@setlocal EnableExtensions EnableDelayedExpansion +for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a +@endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS% + +:endReadAdditionalConfig + +SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe" +set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar" +set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain + +set DOWNLOAD_URL="https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.1.0/maven-wrapper-3.1.0.jar" + +FOR /F "usebackq tokens=1,2 delims==" %%A IN ("%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties") DO ( + IF "%%A"=="wrapperUrl" SET DOWNLOAD_URL=%%B +) + +@REM Extension to allow automatically downloading the maven-wrapper.jar from Maven-central +@REM This allows using the maven wrapper in projects that prohibit checking in binary data. +if exist %WRAPPER_JAR% ( + if "%MVNW_VERBOSE%" == "true" ( + echo Found %WRAPPER_JAR% + ) +) else ( + if not "%MVNW_REPOURL%" == "" ( + SET DOWNLOAD_URL="%MVNW_REPOURL%/org/apache/maven/wrapper/maven-wrapper/3.1.0/maven-wrapper-3.1.0.jar" + ) + if "%MVNW_VERBOSE%" == "true" ( + echo Couldn't find %WRAPPER_JAR%, downloading it ... + echo Downloading from: %DOWNLOAD_URL% + ) + + powershell -Command "&{"^ + "$webclient = new-object System.Net.WebClient;"^ + "if (-not ([string]::IsNullOrEmpty('%MVNW_USERNAME%') -and [string]::IsNullOrEmpty('%MVNW_PASSWORD%'))) {"^ + "$webclient.Credentials = new-object System.Net.NetworkCredential('%MVNW_USERNAME%', '%MVNW_PASSWORD%');"^ + "}"^ + "[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; $webclient.DownloadFile('%DOWNLOAD_URL%', '%WRAPPER_JAR%')"^ + "}" + if "%MVNW_VERBOSE%" == "true" ( + echo Finished downloading %WRAPPER_JAR% + ) +) +@REM End of extension + +@REM Provide a "standardized" way to retrieve the CLI args that will +@REM work with both Windows and non-Windows executions. +set MAVEN_CMD_LINE_ARGS=%* + +%MAVEN_JAVA_EXE% ^ + %JVM_CONFIG_MAVEN_PROPS% ^ + %MAVEN_OPTS% ^ + %MAVEN_DEBUG_OPTS% ^ + -classpath %WRAPPER_JAR% ^ + "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" ^ + %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %* +if ERRORLEVEL 1 goto error +goto end + +:error +set ERROR_CODE=1 + +:end +@endlocal & set ERROR_CODE=%ERROR_CODE% + +if not "%MAVEN_SKIP_RC%"=="" goto skipRcPost +@REM check for post script, once with legacy .bat ending and once with .cmd ending +if exist "%USERPROFILE%\mavenrc_post.bat" call "%USERPROFILE%\mavenrc_post.bat" +if exist "%USERPROFILE%\mavenrc_post.cmd" call "%USERPROFILE%\mavenrc_post.cmd" +:skipRcPost + +@REM pause the script if MAVEN_BATCH_PAUSE is set to 'on' +if "%MAVEN_BATCH_PAUSE%"=="on" pause + +if "%MAVEN_TERMINATE_CMD%"=="on" exit %ERROR_CODE% + +cmd /C exit /B %ERROR_CODE% diff --git a/cross_cutting/k8s/oauth2_proxy/ProxyTestServer/pom.xml b/cross_cutting/k8s/oauth2_proxy/ProxyTestServer/pom.xml new file mode 100644 index 0000000..b856893 --- /dev/null +++ b/cross_cutting/k8s/oauth2_proxy/ProxyTestServer/pom.xml @@ -0,0 +1,41 @@ + + + 4.0.0 + + org.springframework.boot + spring-boot-starter-parent + 2.7.5 + + + com.devonfw.devon4j.examples.cloud + spring_server + 0.0.1-SNAPSHOT + demo + A basic spring server for testing + + 11 + + + + org.springframework.boot + spring-boot-starter-web + + + + org.springframework.boot + spring-boot-starter-test + test + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + + diff --git a/cross_cutting/k8s/oauth2_proxy/ProxyTestServer/src/main/java/com/example/demo/DemoApplication.java b/cross_cutting/k8s/oauth2_proxy/ProxyTestServer/src/main/java/com/example/demo/DemoApplication.java new file mode 100644 index 0000000..64b538a --- /dev/null +++ b/cross_cutting/k8s/oauth2_proxy/ProxyTestServer/src/main/java/com/example/demo/DemoApplication.java @@ -0,0 +1,13 @@ +package com.example.demo; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +public class DemoApplication { + + public static void main(String[] args) { + SpringApplication.run(DemoApplication.class, args); + } + +} diff --git a/cross_cutting/k8s/oauth2_proxy/ProxyTestServer/src/main/java/com/example/demo/test/service/ServiceAController.java b/cross_cutting/k8s/oauth2_proxy/ProxyTestServer/src/main/java/com/example/demo/test/service/ServiceAController.java new file mode 100644 index 0000000..b2a5c0d --- /dev/null +++ b/cross_cutting/k8s/oauth2_proxy/ProxyTestServer/src/main/java/com/example/demo/test/service/ServiceAController.java @@ -0,0 +1,28 @@ +package com.example.demo.test.service; + +import org.springframework.web.bind.annotation.*; + +@RestController +@RequestMapping("/api") +public class ServiceAController { + + @GetMapping("/serviceA") + public String getAllServiceA(){ + return "Success\nGET: /api/serviceA"; + } + + @GetMapping("/serviceA/{id}") + public String getByIdServiceA(@PathVariable Long id){ + return "Success\nGET: /api/serviceA/" + id; + } + + @PostMapping("/serviceA") + public String getAllServiceAByPost(){ + return "Success\nPOST: /api/serviceA"; + } + + @PostMapping("/serviceA/{id}") + public String getByIdServiceAByPost(@PathVariable Long id){ + return "Success\nPOST: /api/serviceA/" + id; + } +} diff --git a/cross_cutting/k8s/oauth2_proxy/ProxyTestServer/src/main/java/com/example/demo/test/service/ServiceBController.java b/cross_cutting/k8s/oauth2_proxy/ProxyTestServer/src/main/java/com/example/demo/test/service/ServiceBController.java new file mode 100644 index 0000000..ab30dc7 --- /dev/null +++ b/cross_cutting/k8s/oauth2_proxy/ProxyTestServer/src/main/java/com/example/demo/test/service/ServiceBController.java @@ -0,0 +1,28 @@ +package com.example.demo.test.service; + +import org.springframework.web.bind.annotation.*; + +@RestController +@RequestMapping("/api") +public class ServiceBController { + + @GetMapping("/serviceB") + public String getAllServiceB(){ + return "Success\nGET: /api/serviceB"; + } + + @GetMapping("/serviceB/{id}") + public String getByIdServiceB(@PathVariable Long id){ + return "Success\nGET: /api/serviceB/" + id; + } + + @PostMapping("/serviceB") + public String getAllServiceBByPost(){ + return "Success\nPOST: /api/serviceB"; + } + + @PostMapping("/serviceB/{id}") + public String getByIdServiceBByPost(@PathVariable Long id){ + return "Success\nPOST: /api/serviceB/" + id; + } +} diff --git a/cross_cutting/k8s/oauth2_proxy/ProxyTestServer/src/main/resources/application.properties b/cross_cutting/k8s/oauth2_proxy/ProxyTestServer/src/main/resources/application.properties new file mode 100644 index 0000000..e86bbd0 --- /dev/null +++ b/cross_cutting/k8s/oauth2_proxy/ProxyTestServer/src/main/resources/application.properties @@ -0,0 +1 @@ +server.port=8090 \ No newline at end of file diff --git a/cross_cutting/k8s/oauth2_proxy/ProxyTestServer/src/test/java/com/example/demo/DemoApplicationTests.java b/cross_cutting/k8s/oauth2_proxy/ProxyTestServer/src/test/java/com/example/demo/DemoApplicationTests.java new file mode 100644 index 0000000..2778a6a --- /dev/null +++ b/cross_cutting/k8s/oauth2_proxy/ProxyTestServer/src/test/java/com/example/demo/DemoApplicationTests.java @@ -0,0 +1,13 @@ +package com.example.demo; + +import org.junit.jupiter.api.Test; +import org.springframework.boot.test.context.SpringBootTest; + +@SpringBootTest +class DemoApplicationTests { + + @Test + void contextLoads() { + } + +} diff --git a/cross_cutting/k8s/oauth2_proxy/REAME.adoc b/cross_cutting/k8s/oauth2_proxy/REAME.adoc new file mode 100644 index 0000000..43f6f05 --- /dev/null +++ b/cross_cutting/k8s/oauth2_proxy/REAME.adoc @@ -0,0 +1,33 @@ += Keycloak + Proxy + +In this example a spring service is secured by an oauth2 Proxy with keycloak as identity provider. + +image::docs/img/flow.drawio.svg[] + +== Required +- Rancher Desktop +- Maven + +== Install +- Run `./install.sh` set the flag `--ignore-block` to ignore blocked ports when installing +- Create a keycloak user with the group `groups` +- Test by trying to access link:http://localtest.me/api/serviceA/ + +== Uninstall +- Run `./uninstall.sh` + +Set flag `--remove-db` to remove the everthing including percistent volume claims + +== Add users + +1. Login to the link:http://keycloak.localtest.me[keycloak ui] with username `admin` and +password `admin` +2. Select TestRealm from dropdown +3. Go to users +4. Click Create new user +5. Make sure to add a email and set verified to true +6. Let the user join the `groups` group +7. Click Save +8. Go to Credentials +9. Click Set Password +10. Create a new password with the option Temporary disabled \ No newline at end of file diff --git a/cross_cutting/k8s/oauth2_proxy/config/ingress.yaml b/cross_cutting/k8s/oauth2_proxy/config/ingress.yaml new file mode 100644 index 0000000..029debe --- /dev/null +++ b/cross_cutting/k8s/oauth2_proxy/config/ingress.yaml @@ -0,0 +1,19 @@ +#Deploy the main ingress to route all requests to the proxy +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: oauth2-proxy-ingress +spec: + ingressClassName: nginx #Map this ingress to the nginx controller + rules: + - host: localtest.me + http: + paths: + - path: / + pathType: Prefix + backend: + service: + name: oauth2-proxy + port: + number: 4180 + diff --git a/cross_cutting/k8s/oauth2_proxy/config/keyCloakValues.yaml b/cross_cutting/k8s/oauth2_proxy/config/keyCloakValues.yaml new file mode 100644 index 0000000..98363ae --- /dev/null +++ b/cross_cutting/k8s/oauth2_proxy/config/keyCloakValues.yaml @@ -0,0 +1,57 @@ +#Access to the keycloak ui +auth: + adminUser: admin + adminPassword: admin + + +#Import realm on startup +extraEnvVars: + - name: PROXY_ADDRESS_FORWARDING + value: "true" + - name: KEYCLOAK_EXTRA_ARGS + value: "-Dkeycloak.import=/config/realm.json" + +extraVolumes: +- name: config + configMap: + name: keycloak-realm + items: + - key: "realm.json" + path: "realm.json" + +extraVolumeMounts: +- name: config + mountPath: "/config" + readOnly: true + + +service: + type: ClusterIP + +#Setup routing +ingress: + enabled: true + ingressClassName: nginx + hostname: keycloak.localtest.me + path: / + servicePort: http + + +#Setup database +postgresql: + enabled: true + auth: + username: bn_keycloak + password: bn_keycloak_password + database: bitnami_keycloak + existingSecret: "" + architecture: standalone + +externalDatabase: + host: "" + port: 5432 + user: bn_keycloak + database: bitnami_keycloak + password: bn_keycloak_password + existingSecret: "" + existingSecretPasswordKey: "" \ No newline at end of file diff --git a/cross_cutting/k8s/oauth2_proxy/config/keycloak/realm.json b/cross_cutting/k8s/oauth2_proxy/config/keycloak/realm.json new file mode 100644 index 0000000..4ddcd60 --- /dev/null +++ b/cross_cutting/k8s/oauth2_proxy/config/keycloak/realm.json @@ -0,0 +1,2335 @@ +{ + "id": "8c81db02-6285-4a6d-94b5-8b3b9db6f9f5", + "realm": "TestRealm", + "notBefore": 0, + "defaultSignatureAlgorithm": "RS256", + "revokeRefreshToken": false, + "refreshTokenMaxReuse": 0, + "accessTokenLifespan": 300, + "accessTokenLifespanForImplicitFlow": 900, + "ssoSessionIdleTimeout": 1800, + "ssoSessionMaxLifespan": 36000, + "ssoSessionIdleTimeoutRememberMe": 0, + "ssoSessionMaxLifespanRememberMe": 0, + "offlineSessionIdleTimeout": 2592000, + "offlineSessionMaxLifespanEnabled": false, + "offlineSessionMaxLifespan": 5184000, + "clientSessionIdleTimeout": 0, + "clientSessionMaxLifespan": 0, + "clientOfflineSessionIdleTimeout": 0, + "clientOfflineSessionMaxLifespan": 0, + "accessCodeLifespan": 60, + "accessCodeLifespanUserAction": 300, + "accessCodeLifespanLogin": 1800, + "actionTokenGeneratedByAdminLifespan": 43200, + "actionTokenGeneratedByUserLifespan": 300, + "oauth2DeviceCodeLifespan": 600, + "oauth2DevicePollingInterval": 5, + "enabled": true, + "sslRequired": "external", + "registrationAllowed": false, + "registrationEmailAsUsername": false, + "rememberMe": false, + "verifyEmail": false, + "loginWithEmailAllowed": true, + "duplicateEmailsAllowed": false, + "resetPasswordAllowed": false, + "editUsernameAllowed": false, + "bruteForceProtected": false, + "permanentLockout": false, + "maxFailureWaitSeconds": 900, + "minimumQuickLoginWaitSeconds": 60, + "waitIncrementSeconds": 60, + "quickLoginCheckMilliSeconds": 1000, + "maxDeltaTimeSeconds": 43200, + "failureFactor": 30, + "roles": { + "realm": [ + { + "id": "853ffe63-3ec6-427f-89a3-27c2350d2c0d", + "name": "default-roles-testrealm", + "description": "${role_default-roles}", + "composite": true, + "composites": { + "realm": [ + "offline_access", + "uma_authorization" + ], + "client": { + "account": [ + "manage-account", + "view-profile" + ] + } + }, + "clientRole": false, + "containerId": "8c81db02-6285-4a6d-94b5-8b3b9db6f9f5", + "attributes": {} + }, + { + "id": "cf6b8fcf-7fe5-4ccb-9e34-25fff6087b8f", + "name": "offline_access", + "description": "${role_offline-access}", + "composite": false, + "clientRole": false, + "containerId": "8c81db02-6285-4a6d-94b5-8b3b9db6f9f5", + "attributes": {} + }, + { + "id": "ae237e42-1275-4e6b-8674-7f0004c9c23e", + "name": "uma_authorization", + "description": "${role_uma_authorization}", + "composite": false, + "clientRole": false, + "containerId": "8c81db02-6285-4a6d-94b5-8b3b9db6f9f5", + "attributes": {} + } + ], + "client": { + "realm-management": [ + { + "id": "3990a4f8-411d-471c-8831-ddf3a2cdd6e8", + "name": "query-users", + "description": "${role_query-users}", + "composite": false, + "clientRole": true, + "containerId": "32e4cf12-3b6f-4d5c-ae24-dbb577b7270e", + "attributes": {} + }, + { + "id": "7d3fbf2d-cccf-4483-a63b-56bd9ebbd96f", + "name": "manage-authorization", + "description": "${role_manage-authorization}", + "composite": false, + "clientRole": true, + "containerId": "32e4cf12-3b6f-4d5c-ae24-dbb577b7270e", + "attributes": {} + }, + { + "id": "19dcd3bc-0ce6-492b-a58d-c87c8a6e8096", + "name": "manage-clients", + "description": "${role_manage-clients}", + "composite": false, + "clientRole": true, + "containerId": "32e4cf12-3b6f-4d5c-ae24-dbb577b7270e", + "attributes": {} + }, + { + "id": "d1ff5fbb-0a0e-451d-8047-0fece24a0653", + "name": "manage-events", + "description": "${role_manage-events}", + "composite": false, + "clientRole": true, + "containerId": "32e4cf12-3b6f-4d5c-ae24-dbb577b7270e", + "attributes": {} + }, + { + "id": "73fa84c1-1670-4d9c-816d-0e7f5abf74b6", + "name": "query-clients", + "description": "${role_query-clients}", + "composite": false, + "clientRole": true, + "containerId": "32e4cf12-3b6f-4d5c-ae24-dbb577b7270e", + "attributes": {} + }, + { + "id": "1e485820-dd56-4721-b9f1-b1d2c258f770", + "name": "view-clients", + "description": "${role_view-clients}", + "composite": true, + "composites": { + "client": { + "realm-management": [ + "query-clients" + ] + } + }, + "clientRole": true, + "containerId": "32e4cf12-3b6f-4d5c-ae24-dbb577b7270e", + "attributes": {} + }, + { + "id": "e008946a-d69a-46e6-a3f7-0ae93cfc6a4b", + "name": "impersonation", + "description": "${role_impersonation}", + "composite": false, + "clientRole": true, + "containerId": "32e4cf12-3b6f-4d5c-ae24-dbb577b7270e", + "attributes": {} + }, + { + "id": "34ab4438-0c32-4ad5-9bf5-70e975014979", + "name": "view-authorization", + "description": "${role_view-authorization}", + "composite": false, + "clientRole": true, + "containerId": "32e4cf12-3b6f-4d5c-ae24-dbb577b7270e", + "attributes": {} + }, + { + "id": "72a97ffb-737a-49a1-ac0d-a1b8f6a5496a", + "name": "view-events", + "description": "${role_view-events}", + "composite": false, + "clientRole": true, + "containerId": "32e4cf12-3b6f-4d5c-ae24-dbb577b7270e", + "attributes": {} + }, + { + "id": "a5fa08a3-ae08-45ca-9522-9eb3b6b51f0a", + "name": "view-realm", + "description": "${role_view-realm}", + "composite": false, + "clientRole": true, + "containerId": "32e4cf12-3b6f-4d5c-ae24-dbb577b7270e", + "attributes": {} + }, + { + "id": "8b43091e-cf72-421e-803c-c5b7d5eb900d", + "name": "realm-admin", + "description": "${role_realm-admin}", + "composite": true, + "composites": { + "client": { + "realm-management": [ + "query-users", + "manage-authorization", + "manage-clients", + "manage-events", + "query-clients", + "view-clients", + "view-events", + "impersonation", + "view-authorization", + "view-realm", + "create-client", + "manage-identity-providers", + "view-identity-providers", + "manage-realm", + "query-groups", + "manage-users", + "view-users", + "query-realms" + ] + } + }, + "clientRole": true, + "containerId": "32e4cf12-3b6f-4d5c-ae24-dbb577b7270e", + "attributes": {} + }, + { + "id": "0befcf3f-2e66-4772-9334-a607f4b1dfed", + "name": "create-client", + "description": "${role_create-client}", + "composite": false, + "clientRole": true, + "containerId": "32e4cf12-3b6f-4d5c-ae24-dbb577b7270e", + "attributes": {} + }, + { + "id": "65b565f5-6ddb-4e17-80a8-08a25ef52410", + "name": "manage-identity-providers", + "description": "${role_manage-identity-providers}", + "composite": false, + "clientRole": true, + "containerId": "32e4cf12-3b6f-4d5c-ae24-dbb577b7270e", + "attributes": {} + }, + { + "id": "d5aa5d83-328f-4be4-b150-72de507949d6", + "name": "view-identity-providers", + "description": "${role_view-identity-providers}", + "composite": false, + "clientRole": true, + "containerId": "32e4cf12-3b6f-4d5c-ae24-dbb577b7270e", + "attributes": {} + }, + { + "id": "104599ee-ad63-4f0a-b578-55ef92834a6c", + "name": "manage-realm", + "description": "${role_manage-realm}", + "composite": false, + "clientRole": true, + "containerId": "32e4cf12-3b6f-4d5c-ae24-dbb577b7270e", + "attributes": {} + }, + { + "id": "fb8967d8-a758-4fba-8e23-1185afb696f2", + "name": "query-groups", + "description": "${role_query-groups}", + "composite": false, + "clientRole": true, + "containerId": "32e4cf12-3b6f-4d5c-ae24-dbb577b7270e", + "attributes": {} + }, + { + "id": "37a74ba1-939b-47d3-966e-5d0ade6a5bf1", + "name": "manage-users", + "description": "${role_manage-users}", + "composite": false, + "clientRole": true, + "containerId": "32e4cf12-3b6f-4d5c-ae24-dbb577b7270e", + "attributes": {} + }, + { + "id": "cabd42b5-99f7-46cd-9b91-ada7af053b49", + "name": "view-users", + "description": "${role_view-users}", + "composite": true, + "composites": { + "client": { + "realm-management": [ + "query-users", + "query-groups" + ] + } + }, + "clientRole": true, + "containerId": "32e4cf12-3b6f-4d5c-ae24-dbb577b7270e", + "attributes": {} + }, + { + "id": "484a510a-4b4f-4580-ae54-a29adaa18eba", + "name": "query-realms", + "description": "${role_query-realms}", + "composite": false, + "clientRole": true, + "containerId": "32e4cf12-3b6f-4d5c-ae24-dbb577b7270e", + "attributes": {} + } + ], + "security-admin-console": [], + "admin-cli": [], + "account-console": [], + "broker": [ + { + "id": "71657119-ef42-4fe9-b4c3-f2f405aa68ac", + "name": "read-token", + "description": "${role_read-token}", + "composite": false, + "clientRole": true, + "containerId": "49ffee80-b230-4418-b77b-1f9d2cc84976", + "attributes": {} + } + ], + "oauthproxy": [], + "account": [ + { + "id": "3e98d83a-9ada-49e6-ab4c-76d23985fb71", + "name": "view-consent", + "description": "${role_view-consent}", + "composite": false, + "clientRole": true, + "containerId": "682c8490-9cbc-40e9-9928-5dec10039565", + "attributes": {} + }, + { + "id": "2a25ad0d-9832-4e40-948f-65e05c5e8002", + "name": "manage-account", + "description": "${role_manage-account}", + "composite": true, + "composites": { + "client": { + "account": [ + "manage-account-links" + ] + } + }, + "clientRole": true, + "containerId": "682c8490-9cbc-40e9-9928-5dec10039565", + "attributes": {} + }, + { + "id": "c277936c-1c21-41a9-9893-2f1187e76594", + "name": "delete-account", + "description": "${role_delete-account}", + "composite": false, + "clientRole": true, + "containerId": "682c8490-9cbc-40e9-9928-5dec10039565", + "attributes": {} + }, + { + "id": "4d3fb1dc-81ac-44b6-9efa-271c17e48538", + "name": "manage-account-links", + "description": "${role_manage-account-links}", + "composite": false, + "clientRole": true, + "containerId": "682c8490-9cbc-40e9-9928-5dec10039565", + "attributes": {} + }, + { + "id": "1dd6d955-a767-4a44-aa77-a712d04af390", + "name": "manage-consent", + "description": "${role_manage-consent}", + "composite": true, + "composites": { + "client": { + "account": [ + "view-consent" + ] + } + }, + "clientRole": true, + "containerId": "682c8490-9cbc-40e9-9928-5dec10039565", + "attributes": {} + }, + { + "id": "cf60d1be-0343-47e7-b80e-92458e39524c", + "name": "view-profile", + "description": "${role_view-profile}", + "composite": false, + "clientRole": true, + "containerId": "682c8490-9cbc-40e9-9928-5dec10039565", + "attributes": {} + }, + { + "id": "f928e862-a6ad-481a-8c8f-77dd30413154", + "name": "view-applications", + "description": "${role_view-applications}", + "composite": false, + "clientRole": true, + "containerId": "682c8490-9cbc-40e9-9928-5dec10039565", + "attributes": {} + } + ] + } + }, + "groups": [ + { + "id": "f7aa90c0-381d-4b0d-a54e-1fdc06124bfe", + "name": "groups", + "path": "/groups", + "attributes": {}, + "realmRoles": [], + "clientRoles": {}, + "subGroups": [] + } + ], + "defaultRole": { + "id": "853ffe63-3ec6-427f-89a3-27c2350d2c0d", + "name": "default-roles-testrealm", + "description": "${role_default-roles}", + "composite": true, + "clientRole": false, + "containerId": "8c81db02-6285-4a6d-94b5-8b3b9db6f9f5" + }, + "requiredCredentials": [ + "password" + ], + "otpPolicyType": "totp", + "otpPolicyAlgorithm": "HmacSHA1", + "otpPolicyInitialCounter": 0, + "otpPolicyDigits": 6, + "otpPolicyLookAheadWindow": 1, + "otpPolicyPeriod": 30, + "otpSupportedApplications": [ + "FreeOTP", + "Google Authenticator" + ], + "webAuthnPolicyRpEntityName": "keycloak", + "webAuthnPolicySignatureAlgorithms": [ + "ES256" + ], + "webAuthnPolicyRpId": "", + "webAuthnPolicyAttestationConveyancePreference": "not specified", + "webAuthnPolicyAuthenticatorAttachment": "not specified", + "webAuthnPolicyRequireResidentKey": "not specified", + "webAuthnPolicyUserVerificationRequirement": "not specified", + "webAuthnPolicyCreateTimeout": 0, + "webAuthnPolicyAvoidSameAuthenticatorRegister": false, + "webAuthnPolicyAcceptableAaguids": [], + "webAuthnPolicyPasswordlessRpEntityName": "keycloak", + "webAuthnPolicyPasswordlessSignatureAlgorithms": [ + "ES256" + ], + "webAuthnPolicyPasswordlessRpId": "", + "webAuthnPolicyPasswordlessAttestationConveyancePreference": "not specified", + "webAuthnPolicyPasswordlessAuthenticatorAttachment": "not specified", + "webAuthnPolicyPasswordlessRequireResidentKey": "not specified", + "webAuthnPolicyPasswordlessUserVerificationRequirement": "not specified", + "webAuthnPolicyPasswordlessCreateTimeout": 0, + "webAuthnPolicyPasswordlessAvoidSameAuthenticatorRegister": false, + "webAuthnPolicyPasswordlessAcceptableAaguids": [], + "users": [ + { + "id": "ccfbe17f-216a-4185-bfa1-737ca0aa51b8", + "createdTimestamp": 1668609632804, + "username": "service-account-oauthproxy", + "enabled": true, + "totp": false, + "emailVerified": false, + "serviceAccountClientId": "oauthproxy", + "disableableCredentialTypes": [], + "requiredActions": [], + "realmRoles": [ + "default-roles-testrealm" + ], + "notBefore": 0, + "groups": [] + } + ], + "scopeMappings": [ + { + "clientScope": "offline_access", + "roles": [ + "offline_access" + ] + } + ], + "clientScopeMappings": { + "account": [ + { + "client": "account-console", + "roles": [ + "manage-account" + ] + } + ] + }, + "clients": [ + { + "id": "682c8490-9cbc-40e9-9928-5dec10039565", + "clientId": "account", + "name": "${client_account}", + "rootUrl": "${authBaseUrl}", + "baseUrl": "/realms/TestRealm/account/", + "surrogateAuthRequired": false, + "enabled": true, + "alwaysDisplayInConsole": false, + "clientAuthenticatorType": "client-secret", + "redirectUris": [ + "/realms/TestRealm/account/*" + ], + "webOrigins": [], + "notBefore": 0, + "bearerOnly": false, + "consentRequired": false, + "standardFlowEnabled": true, + "implicitFlowEnabled": false, + "directAccessGrantsEnabled": false, + "serviceAccountsEnabled": false, + "publicClient": true, + "frontchannelLogout": false, + "protocol": "openid-connect", + "attributes": { + "post.logout.redirect.uris": "+" + }, + "authenticationFlowBindingOverrides": {}, + "fullScopeAllowed": false, + "nodeReRegistrationTimeout": 0, + "defaultClientScopes": [ + "web-origins", + "acr", + "profile", + "roles", + "email" + ], + "optionalClientScopes": [ + "address", + "phone", + "offline_access", + "microprofile-jwt" + ] + }, + { + "id": "22bcb093-9d43-4518-9250-793a9bc3c75f", + "clientId": "account-console", + "name": "${client_account-console}", + "rootUrl": "${authBaseUrl}", + "baseUrl": "/realms/TestRealm/account/", + "surrogateAuthRequired": false, + "enabled": true, + "alwaysDisplayInConsole": false, + "clientAuthenticatorType": "client-secret", + "redirectUris": [ + "/realms/TestRealm/account/*" + ], + "webOrigins": [], + "notBefore": 0, + "bearerOnly": false, + "consentRequired": false, + "standardFlowEnabled": true, + "implicitFlowEnabled": false, + "directAccessGrantsEnabled": false, + "serviceAccountsEnabled": false, + "publicClient": true, + "frontchannelLogout": false, + "protocol": "openid-connect", + "attributes": { + "post.logout.redirect.uris": "+", + "pkce.code.challenge.method": "S256" + }, + "authenticationFlowBindingOverrides": {}, + "fullScopeAllowed": false, + "nodeReRegistrationTimeout": 0, + "protocolMappers": [ + { + "id": "2a505c18-93da-4050-89ae-c2ffb9ab8375", + "name": "audience resolve", + "protocol": "openid-connect", + "protocolMapper": "oidc-audience-resolve-mapper", + "consentRequired": false, + "config": {} + } + ], + "defaultClientScopes": [ + "web-origins", + "acr", + "profile", + "roles", + "email" + ], + "optionalClientScopes": [ + "address", + "phone", + "offline_access", + "microprofile-jwt" + ] + }, + { + "id": "73ee6477-5817-40ba-b602-fac20d1ed09d", + "clientId": "admin-cli", + "name": "${client_admin-cli}", + "surrogateAuthRequired": false, + "enabled": true, + "alwaysDisplayInConsole": false, + "clientAuthenticatorType": "client-secret", + "redirectUris": [], + "webOrigins": [], + "notBefore": 0, + "bearerOnly": false, + "consentRequired": false, + "standardFlowEnabled": false, + "implicitFlowEnabled": false, + "directAccessGrantsEnabled": true, + "serviceAccountsEnabled": false, + "publicClient": true, + "frontchannelLogout": false, + "protocol": "openid-connect", + "attributes": { + "post.logout.redirect.uris": "+" + }, + "authenticationFlowBindingOverrides": {}, + "fullScopeAllowed": false, + "nodeReRegistrationTimeout": 0, + "defaultClientScopes": [ + "web-origins", + "acr", + "profile", + "roles", + "email" + ], + "optionalClientScopes": [ + "address", + "phone", + "offline_access", + "microprofile-jwt" + ] + }, + { + "id": "49ffee80-b230-4418-b77b-1f9d2cc84976", + "clientId": "broker", + "name": "${client_broker}", + "surrogateAuthRequired": false, + "enabled": true, + "alwaysDisplayInConsole": false, + "clientAuthenticatorType": "client-secret", + "redirectUris": [], + "webOrigins": [], + "notBefore": 0, + "bearerOnly": true, + "consentRequired": false, + "standardFlowEnabled": true, + "implicitFlowEnabled": false, + "directAccessGrantsEnabled": false, + "serviceAccountsEnabled": false, + "publicClient": false, + "frontchannelLogout": false, + "protocol": "openid-connect", + "attributes": { + "post.logout.redirect.uris": "+" + }, + "authenticationFlowBindingOverrides": {}, + "fullScopeAllowed": false, + "nodeReRegistrationTimeout": 0, + "defaultClientScopes": [ + "web-origins", + "acr", + "profile", + "roles", + "email" + ], + "optionalClientScopes": [ + "address", + "phone", + "offline_access", + "microprofile-jwt" + ] + }, + { + "id": "6806b5db-bafc-47ac-b709-21e25180324b", + "clientId": "oauthproxy", + "name": "OAuth Proxy", + "description": "", + "rootUrl": "", + "adminUrl": "", + "baseUrl": "", + "surrogateAuthRequired": false, + "enabled": true, + "alwaysDisplayInConsole": false, + "clientAuthenticatorType": "client-secret", + "secret": "clientSecretCode", + "redirectUris": [ + "http://localtest.me/*", + "http://keycloak.localtest.me/*", + "http://localtest.me/oauth2/callback" + ], + "webOrigins": [], + "notBefore": 0, + "bearerOnly": false, + "consentRequired": false, + "standardFlowEnabled": true, + "implicitFlowEnabled": false, + "directAccessGrantsEnabled": true, + "serviceAccountsEnabled": true, + "publicClient": false, + "frontchannelLogout": true, + "protocol": "openid-connect", + "attributes": { + "client.secret.creation.time": "1668610502", + "post.logout.redirect.uris": "+", + "oauth2.device.authorization.grant.enabled": "false", + "backchannel.logout.revoke.offline.tokens": "false", + "use.refresh.tokens": "false", + "tls-client-certificate-bound-access-tokens": "false", + "oidc.ciba.grant.enabled": "false", + "backchannel.logout.session.required": "true", + "client_credentials.use_refresh_token": "false", + "acr.loa.map": "{}", + "require.pushed.authorization.requests": "false", + "display.on.consent.screen": "false", + "token.response.type.bearer.lower-case": "false" + }, + "authenticationFlowBindingOverrides": {}, + "fullScopeAllowed": true, + "nodeReRegistrationTimeout": -1, + "protocolMappers": [ + { + "id": "60c8c231-354e-4f06-bc66-8ecca3f12722", + "name": "groups", + "protocol": "openid-connect", + "protocolMapper": "oidc-group-membership-mapper", + "consentRequired": false, + "config": { + "full.path": "true", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "groups", + "userinfo.token.claim": "true" + } + }, + { + "id": "1eb68ebe-da6c-459d-a9c1-2887186d0e1c", + "name": "GroupsMapper", + "protocol": "openid-connect", + "protocolMapper": "oidc-audience-mapper", + "consentRequired": false, + "config": { + "included.client.audience": "oauthproxy", + "id.token.claim": "false", + "access.token.claim": "true", + "included.custom.audience": "oauthproxy", + "userinfo.token.claim": "false" + } + }, + { + "id": "41f8d071-3a44-4e54-bdd0-7ab745c7c383", + "name": "Client Host", + "protocol": "openid-connect", + "protocolMapper": "oidc-usersessionmodel-note-mapper", + "consentRequired": false, + "config": { + "user.session.note": "clientHost", + "userinfo.token.claim": "true", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "clientHost", + "jsonType.label": "String" + } + }, + { + "id": "9f029f33-e08a-4af9-8f6b-169e92c728e5", + "name": "Client IP Address", + "protocol": "openid-connect", + "protocolMapper": "oidc-usersessionmodel-note-mapper", + "consentRequired": false, + "config": { + "user.session.note": "clientAddress", + "userinfo.token.claim": "true", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "clientAddress", + "jsonType.label": "String" + } + }, + { + "id": "8fe78aa4-6221-4faa-9d6c-c52ed1533ece", + "name": "Client ID", + "protocol": "openid-connect", + "protocolMapper": "oidc-usersessionmodel-note-mapper", + "consentRequired": false, + "config": { + "user.session.note": "clientId", + "userinfo.token.claim": "true", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "clientId", + "jsonType.label": "String" + } + } + ], + "defaultClientScopes": [ + "web-origins", + "acr", + "profile", + "roles", + "email" + ], + "optionalClientScopes": [ + "address", + "phone", + "offline_access", + "microprofile-jwt" + ] + }, + { + "id": "32e4cf12-3b6f-4d5c-ae24-dbb577b7270e", + "clientId": "realm-management", + "name": "${client_realm-management}", + "surrogateAuthRequired": false, + "enabled": true, + "alwaysDisplayInConsole": false, + "clientAuthenticatorType": "client-secret", + "redirectUris": [], + "webOrigins": [], + "notBefore": 0, + "bearerOnly": true, + "consentRequired": false, + "standardFlowEnabled": true, + "implicitFlowEnabled": false, + "directAccessGrantsEnabled": false, + "serviceAccountsEnabled": false, + "publicClient": false, + "frontchannelLogout": false, + "protocol": "openid-connect", + "attributes": { + "post.logout.redirect.uris": "+" + }, + "authenticationFlowBindingOverrides": {}, + "fullScopeAllowed": false, + "nodeReRegistrationTimeout": 0, + "defaultClientScopes": [ + "web-origins", + "acr", + "profile", + "roles", + "email" + ], + "optionalClientScopes": [ + "address", + "phone", + "offline_access", + "microprofile-jwt" + ] + }, + { + "id": "0babb523-af5d-4ef1-8b46-144be1b72bc5", + "clientId": "security-admin-console", + "name": "${client_security-admin-console}", + "rootUrl": "${authAdminUrl}", + "baseUrl": "/admin/TestRealm/console/", + "surrogateAuthRequired": false, + "enabled": true, + "alwaysDisplayInConsole": false, + "clientAuthenticatorType": "client-secret", + "redirectUris": [ + "/admin/TestRealm/console/*" + ], + "webOrigins": [ + "+" + ], + "notBefore": 0, + "bearerOnly": false, + "consentRequired": false, + "standardFlowEnabled": true, + "implicitFlowEnabled": false, + "directAccessGrantsEnabled": false, + "serviceAccountsEnabled": false, + "publicClient": true, + "frontchannelLogout": false, + "protocol": "openid-connect", + "attributes": { + "post.logout.redirect.uris": "+", + "pkce.code.challenge.method": "S256" + }, + "authenticationFlowBindingOverrides": {}, + "fullScopeAllowed": false, + "nodeReRegistrationTimeout": 0, + "protocolMappers": [ + { + "id": "fb2ac6d8-7716-42ac-8e4f-c59e3f4738f2", + "name": "locale", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "userinfo.token.claim": "true", + "user.attribute": "locale", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "locale", + "jsonType.label": "String" + } + } + ], + "defaultClientScopes": [ + "web-origins", + "acr", + "profile", + "roles", + "email" + ], + "optionalClientScopes": [ + "address", + "phone", + "offline_access", + "microprofile-jwt" + ] + } + ], + "clientScopes": [ + { + "id": "22a5252e-3001-424b-aea6-695715282f81", + "name": "address", + "description": "OpenID Connect built-in scope: address", + "protocol": "openid-connect", + "attributes": { + "include.in.token.scope": "true", + "display.on.consent.screen": "true", + "consent.screen.text": "${addressScopeConsentText}" + }, + "protocolMappers": [ + { + "id": "102a7fcf-f91b-4240-9b7b-85c2152fb511", + "name": "address", + "protocol": "openid-connect", + "protocolMapper": "oidc-address-mapper", + "consentRequired": false, + "config": { + "user.attribute.formatted": "formatted", + "user.attribute.country": "country", + "user.attribute.postal_code": "postal_code", + "userinfo.token.claim": "true", + "user.attribute.street": "street", + "id.token.claim": "true", + "user.attribute.region": "region", + "access.token.claim": "true", + "user.attribute.locality": "locality" + } + } + ] + }, + { + "id": "3031c6c0-3fcd-4485-9190-a02b6854f3ce", + "name": "groups", + "description": "", + "protocol": "openid-connect", + "attributes": { + "include.in.token.scope": "true", + "display.on.consent.screen": "true", + "gui.order": "", + "consent.screen.text": "" + } + }, + { + "id": "a5c3d428-d007-4739-a53f-dbeecf49d27e", + "name": "phone", + "description": "OpenID Connect built-in scope: phone", + "protocol": "openid-connect", + "attributes": { + "include.in.token.scope": "true", + "display.on.consent.screen": "true", + "consent.screen.text": "${phoneScopeConsentText}" + }, + "protocolMappers": [ + { + "id": "2f4035d4-ab10-4ebe-b30d-dc0685420027", + "name": "phone number", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "userinfo.token.claim": "true", + "user.attribute": "phoneNumber", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "phone_number", + "jsonType.label": "String" + } + }, + { + "id": "ff9639b3-a7e8-45b8-97d6-4c157b24f7a3", + "name": "phone number verified", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "userinfo.token.claim": "true", + "user.attribute": "phoneNumberVerified", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "phone_number_verified", + "jsonType.label": "boolean" + } + } + ] + }, + { + "id": "4968e9df-a8d5-4846-b484-afe14b286391", + "name": "profile", + "description": "OpenID Connect built-in scope: profile", + "protocol": "openid-connect", + "attributes": { + "include.in.token.scope": "true", + "display.on.consent.screen": "true", + "consent.screen.text": "${profileScopeConsentText}" + }, + "protocolMappers": [ + { + "id": "33d7ef5b-d318-486c-bdb3-589546de425d", + "name": "given name", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-property-mapper", + "consentRequired": false, + "config": { + "userinfo.token.claim": "true", + "user.attribute": "firstName", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "given_name", + "jsonType.label": "String" + } + }, + { + "id": "4bbb666e-e3dd-467e-900c-7c641b98e256", + "name": "updated at", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "userinfo.token.claim": "true", + "user.attribute": "updatedAt", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "updated_at", + "jsonType.label": "long" + } + }, + { + "id": "1e83a053-de50-4a40-b6d1-9cea3a74846c", + "name": "profile", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "userinfo.token.claim": "true", + "user.attribute": "profile", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "profile", + "jsonType.label": "String" + } + }, + { + "id": "8e9552f6-8d35-49bf-bb9d-4f69753ad1ae", + "name": "username", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-property-mapper", + "consentRequired": false, + "config": { + "userinfo.token.claim": "true", + "user.attribute": "username", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "preferred_username", + "jsonType.label": "String" + } + }, + { + "id": "bb447261-8a22-446c-9391-967080bd9c89", + "name": "gender", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "userinfo.token.claim": "true", + "user.attribute": "gender", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "gender", + "jsonType.label": "String" + } + }, + { + "id": "1da1cd5d-f82e-4a03-bd2e-2a1b0194b8c8", + "name": "birthdate", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "userinfo.token.claim": "true", + "user.attribute": "birthdate", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "birthdate", + "jsonType.label": "String" + } + }, + { + "id": "8611fed8-1937-42c1-952c-38056e2701fa", + "name": "full name", + "protocol": "openid-connect", + "protocolMapper": "oidc-full-name-mapper", + "consentRequired": false, + "config": { + "id.token.claim": "true", + "access.token.claim": "true", + "userinfo.token.claim": "true" + } + }, + { + "id": "49529b20-e82f-4291-94f2-7295d6efd345", + "name": "family name", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-property-mapper", + "consentRequired": false, + "config": { + "userinfo.token.claim": "true", + "user.attribute": "lastName", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "family_name", + "jsonType.label": "String" + } + }, + { + "id": "2ee94565-fa6d-4b3d-a0b2-e5672794b7c4", + "name": "zoneinfo", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "userinfo.token.claim": "true", + "user.attribute": "zoneinfo", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "zoneinfo", + "jsonType.label": "String" + } + }, + { + "id": "43c80f8d-66ae-4ce5-9c1c-fc9b97695d78", + "name": "picture", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "userinfo.token.claim": "true", + "user.attribute": "picture", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "picture", + "jsonType.label": "String" + } + }, + { + "id": "c9ce9cac-402d-4179-9e11-e2779abfb366", + "name": "nickname", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "userinfo.token.claim": "true", + "user.attribute": "nickname", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "nickname", + "jsonType.label": "String" + } + }, + { + "id": "ec417563-9d20-43cc-b279-cf1684e68e00", + "name": "website", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "userinfo.token.claim": "true", + "user.attribute": "website", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "website", + "jsonType.label": "String" + } + }, + { + "id": "d0926157-d0ee-423a-9ce4-ba783dbc7e33", + "name": "locale", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "userinfo.token.claim": "true", + "user.attribute": "locale", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "locale", + "jsonType.label": "String" + } + }, + { + "id": "c3f58a5f-f8fc-4b08-afcc-c462c5df0d08", + "name": "middle name", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "userinfo.token.claim": "true", + "user.attribute": "middleName", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "middle_name", + "jsonType.label": "String" + } + } + ] + }, + { + "id": "e61d7aee-385b-401b-9ba6-206550bfcb64", + "name": "roles", + "description": "OpenID Connect scope for add user roles to the access token", + "protocol": "openid-connect", + "attributes": { + "include.in.token.scope": "false", + "display.on.consent.screen": "true", + "consent.screen.text": "${rolesScopeConsentText}" + }, + "protocolMappers": [ + { + "id": "9d0d0f36-fe0e-4615-a823-37db42af2918", + "name": "client roles", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-client-role-mapper", + "consentRequired": false, + "config": { + "user.attribute": "foo", + "access.token.claim": "true", + "claim.name": "resource_access.${client_id}.roles", + "jsonType.label": "String", + "multivalued": "true" + } + }, + { + "id": "3ffd17c0-a1ed-4f16-9c42-685838c82372", + "name": "audience resolve", + "protocol": "openid-connect", + "protocolMapper": "oidc-audience-resolve-mapper", + "consentRequired": false, + "config": {} + }, + { + "id": "bf6656f3-2a54-46a5-b0af-372e1a467345", + "name": "realm roles", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-realm-role-mapper", + "consentRequired": false, + "config": { + "user.attribute": "foo", + "access.token.claim": "true", + "claim.name": "realm_access.roles", + "jsonType.label": "String", + "multivalued": "true" + } + } + ] + }, + { + "id": "ef753be5-67eb-44f4-9742-5751c15f76a0", + "name": "offline_access", + "description": "OpenID Connect built-in scope: offline_access", + "protocol": "openid-connect", + "attributes": { + "consent.screen.text": "${offlineAccessScopeConsentText}", + "display.on.consent.screen": "true" + } + }, + { + "id": "83ec18df-2edb-4e13-ac62-6230775eb485", + "name": "role_list", + "description": "SAML role list", + "protocol": "saml", + "attributes": { + "consent.screen.text": "${samlRoleListScopeConsentText}", + "display.on.consent.screen": "true" + }, + "protocolMappers": [ + { + "id": "f31f7739-e683-4d2e-abfd-48581842c950", + "name": "role list", + "protocol": "saml", + "protocolMapper": "saml-role-list-mapper", + "consentRequired": false, + "config": { + "single": "false", + "attribute.nameformat": "Basic", + "attribute.name": "Role" + } + } + ] + }, + { + "id": "1be7477c-d4da-4a1c-ab3d-526d147f8ec4", + "name": "web-origins", + "description": "OpenID Connect scope for add allowed web origins to the access token", + "protocol": "openid-connect", + "attributes": { + "include.in.token.scope": "false", + "display.on.consent.screen": "false", + "consent.screen.text": "" + }, + "protocolMappers": [ + { + "id": "f3432113-1b3a-4783-9ee0-47c3b8e69576", + "name": "allowed web origins", + "protocol": "openid-connect", + "protocolMapper": "oidc-allowed-origins-mapper", + "consentRequired": false, + "config": {} + } + ] + }, + { + "id": "76db4598-8865-4c1d-b9ff-78f6ee8ee64d", + "name": "microprofile-jwt", + "description": "Microprofile - JWT built-in scope", + "protocol": "openid-connect", + "attributes": { + "include.in.token.scope": "true", + "display.on.consent.screen": "false" + }, + "protocolMappers": [ + { + "id": "ca8f2350-de72-472c-b60a-7081052f694c", + "name": "groups", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-realm-role-mapper", + "consentRequired": false, + "config": { + "multivalued": "true", + "userinfo.token.claim": "true", + "user.attribute": "foo", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "groups", + "jsonType.label": "String" + } + }, + { + "id": "80edf94d-e92a-4988-bcb6-91eb740a5346", + "name": "upn", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-property-mapper", + "consentRequired": false, + "config": { + "userinfo.token.claim": "true", + "user.attribute": "username", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "upn", + "jsonType.label": "String" + } + } + ] + }, + { + "id": "8fc0fd9b-5137-4771-8377-e9f76fcacfe2", + "name": "email", + "description": "OpenID Connect built-in scope: email", + "protocol": "openid-connect", + "attributes": { + "include.in.token.scope": "true", + "display.on.consent.screen": "true", + "consent.screen.text": "${emailScopeConsentText}" + }, + "protocolMappers": [ + { + "id": "f6f529fc-961b-4c0a-9092-2653a5d2eb6d", + "name": "email", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-property-mapper", + "consentRequired": false, + "config": { + "userinfo.token.claim": "true", + "user.attribute": "email", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "email", + "jsonType.label": "String" + } + }, + { + "id": "3d836e24-d5b0-4440-9b01-b10f9da7e301", + "name": "email verified", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-property-mapper", + "consentRequired": false, + "config": { + "userinfo.token.claim": "true", + "user.attribute": "emailVerified", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "email_verified", + "jsonType.label": "boolean" + } + } + ] + }, + { + "id": "806550a8-b409-4064-bb7c-aabb55ea82ca", + "name": "acr", + "description": "OpenID Connect scope for add acr (authentication context class reference) to the token", + "protocol": "openid-connect", + "attributes": { + "include.in.token.scope": "false", + "display.on.consent.screen": "false" + }, + "protocolMappers": [ + { + "id": "677c9981-4a73-4904-a704-377205f00b29", + "name": "acr loa level", + "protocol": "openid-connect", + "protocolMapper": "oidc-acr-mapper", + "consentRequired": false, + "config": { + "id.token.claim": "true", + "access.token.claim": "true", + "userinfo.token.claim": "true" + } + } + ] + } + ], + "defaultDefaultClientScopes": [ + "role_list", + "profile", + "email", + "roles", + "web-origins", + "acr" + ], + "defaultOptionalClientScopes": [ + "offline_access", + "address", + "phone", + "microprofile-jwt" + ], + "browserSecurityHeaders": { + "contentSecurityPolicyReportOnly": "", + "xContentTypeOptions": "nosniff", + "xRobotsTag": "none", + "xFrameOptions": "SAMEORIGIN", + "contentSecurityPolicy": "frame-src 'self'; frame-ancestors 'self'; object-src 'none';", + "xXSSProtection": "1; mode=block", + "strictTransportSecurity": "max-age=31536000; includeSubDomains" + }, + "smtpServer": {}, + "eventsEnabled": false, + "eventsListeners": [ + "jboss-logging" + ], + "enabledEventTypes": [], + "adminEventsEnabled": false, + "adminEventsDetailsEnabled": false, + "identityProviders": [], + "identityProviderMappers": [], + "components": { + "org.keycloak.services.clientregistration.policy.ClientRegistrationPolicy": [ + { + "id": "64c72d49-b65e-4f98-b7c7-a24a5f8965a0", + "name": "Trusted Hosts", + "providerId": "trusted-hosts", + "subType": "anonymous", + "subComponents": {}, + "config": { + "host-sending-registration-request-must-match": [ + "true" + ], + "client-uris-must-match": [ + "true" + ] + } + }, + { + "id": "90565766-caf0-4b36-bed2-28952262796d", + "name": "Allowed Protocol Mapper Types", + "providerId": "allowed-protocol-mappers", + "subType": "authenticated", + "subComponents": {}, + "config": { + "allowed-protocol-mapper-types": [ + "saml-user-attribute-mapper", + "oidc-usermodel-property-mapper", + "saml-role-list-mapper", + "saml-user-property-mapper", + "oidc-address-mapper", + "oidc-usermodel-attribute-mapper", + "oidc-sha256-pairwise-sub-mapper", + "oidc-full-name-mapper" + ] + } + }, + { + "id": "c21fbd1d-6665-4091-a9a0-cb96c1daafc3", + "name": "Full Scope Disabled", + "providerId": "scope", + "subType": "anonymous", + "subComponents": {}, + "config": {} + }, + { + "id": "d3de1368-f606-4e1d-a159-b764de447b57", + "name": "Max Clients Limit", + "providerId": "max-clients", + "subType": "anonymous", + "subComponents": {}, + "config": { + "max-clients": [ + "200" + ] + } + }, + { + "id": "193f7528-3786-4074-8421-b1db2dd427b3", + "name": "Allowed Client Scopes", + "providerId": "allowed-client-templates", + "subType": "anonymous", + "subComponents": {}, + "config": { + "allow-default-scopes": [ + "true" + ] + } + }, + { + "id": "33398ccc-1e5d-4bd4-afa5-02116ff726de", + "name": "Allowed Protocol Mapper Types", + "providerId": "allowed-protocol-mappers", + "subType": "anonymous", + "subComponents": {}, + "config": { + "allowed-protocol-mapper-types": [ + "saml-user-property-mapper", + "oidc-usermodel-attribute-mapper", + "oidc-full-name-mapper", + "oidc-sha256-pairwise-sub-mapper", + "oidc-address-mapper", + "oidc-usermodel-property-mapper", + "saml-user-attribute-mapper", + "saml-role-list-mapper" + ] + } + }, + { + "id": "284712ff-a083-4d10-b53b-76eaa3d39b87", + "name": "Allowed Client Scopes", + "providerId": "allowed-client-templates", + "subType": "authenticated", + "subComponents": {}, + "config": { + "allow-default-scopes": [ + "true" + ] + } + }, + { + "id": "250a6e70-254b-4513-a442-b3da2fc3505e", + "name": "Consent Required", + "providerId": "consent-required", + "subType": "anonymous", + "subComponents": {}, + "config": {} + } + ], + "org.keycloak.keys.KeyProvider": [ + { + "id": "bf58439a-d742-4869-94ff-b99d5922ae69", + "name": "rsa-enc-generated", + "providerId": "rsa-enc-generated", + "subComponents": {}, + "config": { + "priority": [ + "100" + ], + "algorithm": [ + "RSA-OAEP" + ] + } + }, + { + "id": "6b09570a-4c55-452f-8a79-bf77ecd19f35", + "name": "hmac-generated", + "providerId": "hmac-generated", + "subComponents": {}, + "config": { + "priority": [ + "100" + ], + "algorithm": [ + "HS256" + ] + } + }, + { + "id": "471e03f7-0209-43d9-8844-b7571356a37e", + "name": "aes-generated", + "providerId": "aes-generated", + "subComponents": {}, + "config": { + "priority": [ + "100" + ] + } + }, + { + "id": "09f4cea9-d83c-4c15-bb3b-3c8a44915453", + "name": "rsa-generated", + "providerId": "rsa-generated", + "subComponents": {}, + "config": { + "priority": [ + "100" + ] + } + } + ] + }, + "internationalizationEnabled": false, + "supportedLocales": [], + "authenticationFlows": [ + { + "id": "32036bfe-9c82-49af-97ad-768b1c05133e", + "alias": "Account verification options", + "description": "Method with which to verity the existing account", + "providerId": "basic-flow", + "topLevel": false, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "idp-email-verification", + "authenticatorFlow": false, + "requirement": "ALTERNATIVE", + "priority": 10, + "autheticatorFlow": false, + "userSetupAllowed": false + }, + { + "authenticatorFlow": true, + "requirement": "ALTERNATIVE", + "priority": 20, + "autheticatorFlow": true, + "flowAlias": "Verify Existing Account by Re-authentication", + "userSetupAllowed": false + } + ] + }, + { + "id": "aa068b6b-a467-42f1-b609-64a01f64522c", + "alias": "Authentication Options", + "description": "Authentication options.", + "providerId": "basic-flow", + "topLevel": false, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "basic-auth", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 10, + "autheticatorFlow": false, + "userSetupAllowed": false + }, + { + "authenticator": "basic-auth-otp", + "authenticatorFlow": false, + "requirement": "DISABLED", + "priority": 20, + "autheticatorFlow": false, + "userSetupAllowed": false + }, + { + "authenticator": "auth-spnego", + "authenticatorFlow": false, + "requirement": "DISABLED", + "priority": 30, + "autheticatorFlow": false, + "userSetupAllowed": false + } + ] + }, + { + "id": "bde7a69f-c6d5-48a3-a8e3-9be2fb9a6486", + "alias": "Browser - Conditional OTP", + "description": "Flow to determine if the OTP is required for the authentication", + "providerId": "basic-flow", + "topLevel": false, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "conditional-user-configured", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 10, + "autheticatorFlow": false, + "userSetupAllowed": false + }, + { + "authenticator": "auth-otp-form", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 20, + "autheticatorFlow": false, + "userSetupAllowed": false + } + ] + }, + { + "id": "021cfb4f-3b8c-4c61-bb85-ec305742a593", + "alias": "Direct Grant - Conditional OTP", + "description": "Flow to determine if the OTP is required for the authentication", + "providerId": "basic-flow", + "topLevel": false, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "conditional-user-configured", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 10, + "autheticatorFlow": false, + "userSetupAllowed": false + }, + { + "authenticator": "direct-grant-validate-otp", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 20, + "autheticatorFlow": false, + "userSetupAllowed": false + } + ] + }, + { + "id": "467f3382-3e12-44fb-9534-2b6cb5eae698", + "alias": "First broker login - Conditional OTP", + "description": "Flow to determine if the OTP is required for the authentication", + "providerId": "basic-flow", + "topLevel": false, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "conditional-user-configured", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 10, + "autheticatorFlow": false, + "userSetupAllowed": false + }, + { + "authenticator": "auth-otp-form", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 20, + "autheticatorFlow": false, + "userSetupAllowed": false + } + ] + }, + { + "id": "47783dfe-db70-4a88-a80d-1cbeea7be096", + "alias": "Handle Existing Account", + "description": "Handle what to do if there is existing account with same email/username like authenticated identity provider", + "providerId": "basic-flow", + "topLevel": false, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "idp-confirm-link", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 10, + "autheticatorFlow": false, + "userSetupAllowed": false + }, + { + "authenticatorFlow": true, + "requirement": "REQUIRED", + "priority": 20, + "autheticatorFlow": true, + "flowAlias": "Account verification options", + "userSetupAllowed": false + } + ] + }, + { + "id": "3bf41137-bc47-4455-9e51-f52b51e3832e", + "alias": "Reset - Conditional OTP", + "description": "Flow to determine if the OTP should be reset or not. Set to REQUIRED to force.", + "providerId": "basic-flow", + "topLevel": false, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "conditional-user-configured", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 10, + "autheticatorFlow": false, + "userSetupAllowed": false + }, + { + "authenticator": "reset-otp", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 20, + "autheticatorFlow": false, + "userSetupAllowed": false + } + ] + }, + { + "id": "6eee0911-1891-4b31-9c71-f0d68622470d", + "alias": "User creation or linking", + "description": "Flow for the existing/non-existing user alternatives", + "providerId": "basic-flow", + "topLevel": false, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticatorConfig": "create unique user config", + "authenticator": "idp-create-user-if-unique", + "authenticatorFlow": false, + "requirement": "ALTERNATIVE", + "priority": 10, + "autheticatorFlow": false, + "userSetupAllowed": false + }, + { + "authenticatorFlow": true, + "requirement": "ALTERNATIVE", + "priority": 20, + "autheticatorFlow": true, + "flowAlias": "Handle Existing Account", + "userSetupAllowed": false + } + ] + }, + { + "id": "4933896e-81d1-4db2-a75a-1edde4e8010a", + "alias": "Verify Existing Account by Re-authentication", + "description": "Reauthentication of existing account", + "providerId": "basic-flow", + "topLevel": false, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "idp-username-password-form", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 10, + "autheticatorFlow": false, + "userSetupAllowed": false + }, + { + "authenticatorFlow": true, + "requirement": "CONDITIONAL", + "priority": 20, + "autheticatorFlow": true, + "flowAlias": "First broker login - Conditional OTP", + "userSetupAllowed": false + } + ] + }, + { + "id": "cce1da95-a84c-472f-bd50-df3eb3aa58cf", + "alias": "browser", + "description": "browser based authentication", + "providerId": "basic-flow", + "topLevel": true, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "auth-cookie", + "authenticatorFlow": false, + "requirement": "ALTERNATIVE", + "priority": 10, + "autheticatorFlow": false, + "userSetupAllowed": false + }, + { + "authenticator": "auth-spnego", + "authenticatorFlow": false, + "requirement": "DISABLED", + "priority": 20, + "autheticatorFlow": false, + "userSetupAllowed": false + }, + { + "authenticator": "identity-provider-redirector", + "authenticatorFlow": false, + "requirement": "ALTERNATIVE", + "priority": 25, + "autheticatorFlow": false, + "userSetupAllowed": false + }, + { + "authenticatorFlow": true, + "requirement": "ALTERNATIVE", + "priority": 30, + "autheticatorFlow": true, + "flowAlias": "forms", + "userSetupAllowed": false + } + ] + }, + { + "id": "af146c6b-b689-4342-a44b-ffbc424b81b6", + "alias": "clients", + "description": "Base authentication for clients", + "providerId": "client-flow", + "topLevel": true, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "client-secret", + "authenticatorFlow": false, + "requirement": "ALTERNATIVE", + "priority": 10, + "autheticatorFlow": false, + "userSetupAllowed": false + }, + { + "authenticator": "client-jwt", + "authenticatorFlow": false, + "requirement": "ALTERNATIVE", + "priority": 20, + "autheticatorFlow": false, + "userSetupAllowed": false + }, + { + "authenticator": "client-secret-jwt", + "authenticatorFlow": false, + "requirement": "ALTERNATIVE", + "priority": 30, + "autheticatorFlow": false, + "userSetupAllowed": false + }, + { + "authenticator": "client-x509", + "authenticatorFlow": false, + "requirement": "ALTERNATIVE", + "priority": 40, + "autheticatorFlow": false, + "userSetupAllowed": false + } + ] + }, + { + "id": "c3c99b61-9fbb-4081-a576-75504a5d37bb", + "alias": "direct grant", + "description": "OpenID Connect Resource Owner Grant", + "providerId": "basic-flow", + "topLevel": true, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "direct-grant-validate-username", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 10, + "autheticatorFlow": false, + "userSetupAllowed": false + }, + { + "authenticator": "direct-grant-validate-password", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 20, + "autheticatorFlow": false, + "userSetupAllowed": false + }, + { + "authenticatorFlow": true, + "requirement": "CONDITIONAL", + "priority": 30, + "autheticatorFlow": true, + "flowAlias": "Direct Grant - Conditional OTP", + "userSetupAllowed": false + } + ] + }, + { + "id": "9fd357db-a590-456f-8e56-ad327d8f3db6", + "alias": "docker auth", + "description": "Used by Docker clients to authenticate against the IDP", + "providerId": "basic-flow", + "topLevel": true, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "docker-http-basic-authenticator", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 10, + "autheticatorFlow": false, + "userSetupAllowed": false + } + ] + }, + { + "id": "96d170af-7f83-459e-8873-ab104c980a2f", + "alias": "first broker login", + "description": "Actions taken after first broker login with identity provider account, which is not yet linked to any Keycloak account", + "providerId": "basic-flow", + "topLevel": true, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticatorConfig": "review profile config", + "authenticator": "idp-review-profile", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 10, + "autheticatorFlow": false, + "userSetupAllowed": false + }, + { + "authenticatorFlow": true, + "requirement": "REQUIRED", + "priority": 20, + "autheticatorFlow": true, + "flowAlias": "User creation or linking", + "userSetupAllowed": false + } + ] + }, + { + "id": "d52e121b-1034-4227-88d2-f5b7e0cd9772", + "alias": "forms", + "description": "Username, password, otp and other auth forms.", + "providerId": "basic-flow", + "topLevel": false, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "auth-username-password-form", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 10, + "autheticatorFlow": false, + "userSetupAllowed": false + }, + { + "authenticatorFlow": true, + "requirement": "CONDITIONAL", + "priority": 20, + "autheticatorFlow": true, + "flowAlias": "Browser - Conditional OTP", + "userSetupAllowed": false + } + ] + }, + { + "id": "0faf015b-5449-4308-ae5a-d79af61c0e04", + "alias": "http challenge", + "description": "An authentication flow based on challenge-response HTTP Authentication Schemes", + "providerId": "basic-flow", + "topLevel": true, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "no-cookie-redirect", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 10, + "autheticatorFlow": false, + "userSetupAllowed": false + }, + { + "authenticatorFlow": true, + "requirement": "REQUIRED", + "priority": 20, + "autheticatorFlow": true, + "flowAlias": "Authentication Options", + "userSetupAllowed": false + } + ] + }, + { + "id": "90850ddf-15a5-459f-a335-b368db97d9ac", + "alias": "registration", + "description": "registration flow", + "providerId": "basic-flow", + "topLevel": true, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "registration-page-form", + "authenticatorFlow": true, + "requirement": "REQUIRED", + "priority": 10, + "autheticatorFlow": true, + "flowAlias": "registration form", + "userSetupAllowed": false + } + ] + }, + { + "id": "5dccb5af-c7d5-4966-b520-59e898a8a075", + "alias": "registration form", + "description": "registration form", + "providerId": "form-flow", + "topLevel": false, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "registration-user-creation", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 20, + "autheticatorFlow": false, + "userSetupAllowed": false + }, + { + "authenticator": "registration-profile-action", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 40, + "autheticatorFlow": false, + "userSetupAllowed": false + }, + { + "authenticator": "registration-password-action", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 50, + "autheticatorFlow": false, + "userSetupAllowed": false + }, + { + "authenticator": "registration-recaptcha-action", + "authenticatorFlow": false, + "requirement": "DISABLED", + "priority": 60, + "autheticatorFlow": false, + "userSetupAllowed": false + } + ] + }, + { + "id": "64943d99-4b42-4ad5-9513-24426c87d55d", + "alias": "reset credentials", + "description": "Reset credentials for a user if they forgot their password or something", + "providerId": "basic-flow", + "topLevel": true, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "reset-credentials-choose-user", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 10, + "autheticatorFlow": false, + "userSetupAllowed": false + }, + { + "authenticator": "reset-credential-email", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 20, + "autheticatorFlow": false, + "userSetupAllowed": false + }, + { + "authenticator": "reset-password", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 30, + "autheticatorFlow": false, + "userSetupAllowed": false + }, + { + "authenticatorFlow": true, + "requirement": "CONDITIONAL", + "priority": 40, + "autheticatorFlow": true, + "flowAlias": "Reset - Conditional OTP", + "userSetupAllowed": false + } + ] + }, + { + "id": "e20e12b2-95b4-40db-812a-a6c84f6e91e3", + "alias": "saml ecp", + "description": "SAML ECP Profile Authentication Flow", + "providerId": "basic-flow", + "topLevel": true, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "http-basic-authenticator", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 10, + "autheticatorFlow": false, + "userSetupAllowed": false + } + ] + } + ], + "authenticatorConfig": [ + { + "id": "1cec2bd7-3e97-404b-bab8-a02f05b643f6", + "alias": "create unique user config", + "config": { + "require.password.update.after.registration": "false" + } + }, + { + "id": "e70d1edd-3778-4b60-9db3-f4c3e8fe8cf5", + "alias": "review profile config", + "config": { + "update.profile.on.first.login": "missing" + } + } + ], + "requiredActions": [ + { + "alias": "CONFIGURE_TOTP", + "name": "Configure OTP", + "providerId": "CONFIGURE_TOTP", + "enabled": true, + "defaultAction": false, + "priority": 10, + "config": {} + }, + { + "alias": "terms_and_conditions", + "name": "Terms and Conditions", + "providerId": "terms_and_conditions", + "enabled": false, + "defaultAction": false, + "priority": 20, + "config": {} + }, + { + "alias": "UPDATE_PASSWORD", + "name": "Update Password", + "providerId": "UPDATE_PASSWORD", + "enabled": true, + "defaultAction": false, + "priority": 30, + "config": {} + }, + { + "alias": "UPDATE_PROFILE", + "name": "Update Profile", + "providerId": "UPDATE_PROFILE", + "enabled": true, + "defaultAction": false, + "priority": 40, + "config": {} + }, + { + "alias": "VERIFY_EMAIL", + "name": "Verify Email", + "providerId": "VERIFY_EMAIL", + "enabled": true, + "defaultAction": false, + "priority": 50, + "config": {} + }, + { + "alias": "delete_account", + "name": "Delete Account", + "providerId": "delete_account", + "enabled": false, + "defaultAction": false, + "priority": 60, + "config": {} + }, + { + "alias": "webauthn-register", + "name": "Webauthn Register", + "providerId": "webauthn-register", + "enabled": true, + "defaultAction": false, + "priority": 70, + "config": {} + }, + { + "alias": "webauthn-register-passwordless", + "name": "Webauthn Register Passwordless", + "providerId": "webauthn-register-passwordless", + "enabled": true, + "defaultAction": false, + "priority": 80, + "config": {} + }, + { + "alias": "update_user_locale", + "name": "Update User Locale", + "providerId": "update_user_locale", + "enabled": true, + "defaultAction": false, + "priority": 1000, + "config": {} + } + ], + "browserFlow": "browser", + "registrationFlow": "registration", + "directGrantFlow": "direct grant", + "resetCredentialsFlow": "reset credentials", + "clientAuthenticationFlow": "clients", + "dockerAuthenticationFlow": "docker auth", + "attributes": { + "cibaBackchannelTokenDeliveryMode": "poll", + "cibaExpiresIn": "120", + "cibaAuthRequestedUserHint": "login_hint", + "oauth2DeviceCodeLifespan": "600", + "clientOfflineSessionMaxLifespan": "0", + "oauth2DevicePollingInterval": "5", + "clientSessionIdleTimeout": "0", + "parRequestUriLifespan": "60", + "clientSessionMaxLifespan": "0", + "clientOfflineSessionIdleTimeout": "0", + "cibaInterval": "5" + }, + "keycloakVersion": "19.0.3", + "userManagedAccessAllowed": false, + "clientProfiles": { + "profiles": [] + }, + "clientPolicies": { + "policies": [] + } +} \ No newline at end of file diff --git a/cross_cutting/k8s/oauth2_proxy/config/oauth2-proxy.yaml b/cross_cutting/k8s/oauth2_proxy/config/oauth2-proxy.yaml new file mode 100644 index 0000000..5edbedb --- /dev/null +++ b/cross_cutting/k8s/oauth2_proxy/config/oauth2-proxy.yaml @@ -0,0 +1,90 @@ +#This file configures the oauth proxy +#A list of arguments can be found here https://oauth2-proxy.github.io/oauth2-proxy/docs/configuration/overview + + +apiVersion: apps/v1 +kind: Deployment +metadata: + labels: + k8s-app: oauth2-proxy + name: oauth2-proxy +spec: + replicas: 1 + selector: + matchLabels: + k8s-app: oauth2-proxy + template: + metadata: + labels: + k8s-app: oauth2-proxy + spec: + containers: + - args: + # Set keycloak as identity provider + - --provider=keycloak-oidc + # Set the token issuer url to the localhost url that is accessible outside the cluster + - --oidc-issuer-url=http://keycloak.localtest.me/realms/TestRealm + # Deactivate oidc discovery to prevent the oidc-issuer-url to be used when starting the pod + # Rather set the next 3 urls manually + - --skip-oidc-discovery=true + # set to the kubernetes internal url to prevent the localhost url to resolve to the pods loopback + - --oidc-jwks-url=http://keycloak.default.svc.cluster.local/realms/TestRealm/protocol/openid-connect/certs + # set to the kubernetes internal url to prevent the localhost url to resolve to the pods loopback + - --redeem-url=http://keycloak.default.svc.cluster.local/realms/TestRealm/protocol/openid-connect/token + # set the login url to the external localhost address because the url is used to redirect the users browsers to the keycloak login screen that has to be accessible from outside the cluster + - --login-url=http://keycloak.localtest.me/realms/TestRealm/protocol/openid-connect/auth + + - --redirect-url=http://localtest.me/oauth2/callback + + # this url is the service that is called after the proxy authorized the request + - --upstream=http://spring-server-service.default.svc.cluster.local:8090 + + - --email-domain=* + - --scope=openid email + # Optional setting to allow the service behind the proxy to use the JWT + - --pass-authorization-header=true + # Optional setting to allow the service behind the proxy to use the JWT + - --pass-access-token=true + + - --http-address=0.0.0.0:4180 + - --cookie-refresh=1m + - --cookie-expire=30m + # Can only be true when using HTTPS + - --cookie-secure=false + + #Routes in the upstream with no protection + - --skip-auth-route=/api/serviceB/* + + #Limit access to allowed groups + - --allowed-group=/groups + - --show-debug-on-error=true #For testing + env: + - name: OAUTH2_PROXY_CLIENT_ID + value: oauthproxy + - name: OAUTH2_PROXY_CLIENT_SECRET + value: clientSecretCode + - name: OAUTH2_PROXY_COOKIE_SECRET + value: ODlxNVpUMWF1YmhuR0J1Vkl3cStxUT09 + image: quay.io/oauth2-proxy/oauth2-proxy:latest + imagePullPolicy: Always + name: oauth2-proxy + ports: + - containerPort: 4180 + protocol: TCP + + +--- +apiVersion: v1 +kind: Service +metadata: + labels: + k8s-app: oauth2-proxy + name: oauth2-proxy +spec: + ports: + - name: http + port: 4180 + protocol: TCP + targetPort: 4180 + selector: + k8s-app: oauth2-proxy \ No newline at end of file diff --git a/cross_cutting/k8s/oauth2_proxy/config/springServer.yaml b/cross_cutting/k8s/oauth2_proxy/config/springServer.yaml new file mode 100644 index 0000000..de015dc --- /dev/null +++ b/cross_cutting/k8s/oauth2_proxy/config/springServer.yaml @@ -0,0 +1,38 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: spring-server-deployment + labels: + app: spring-server +spec: + replicas: 1 + selector: + matchLabels: + app: spring-server + template: + metadata: + labels: + app: spring-server + spec: + containers: + - name: spring-spring + image: spring-server + imagePullPolicy: Never + ports: + - containerPort: 8090 + +--- +apiVersion: v1 +kind: Service +metadata: + labels: + name: spring-server-service +spec: + ports: + - name: http + protocol: TCP + port: 8090 + targetPort: 8090 + selector: + app: spring-server + diff --git a/cross_cutting/k8s/oauth2_proxy/docs/img/flow.drawio.svg b/cross_cutting/k8s/oauth2_proxy/docs/img/flow.drawio.svg new file mode 100644 index 0000000..ed4f926 --- /dev/null +++ b/cross_cutting/k8s/oauth2_proxy/docs/img/flow.drawio.svg @@ -0,0 +1,4 @@ + + + +
Spring
Spring
Keycloak
Keycloak
Proxy
Proxy
Actor
Actor
Keycloak Ingress
Keycloak Ingr...
Main
Ingress
Main...
1,7
1,7
2,8
2,8
3
3
4
4
5
5
6
6
7,9
7,9
K8s Cluster
K8s Cluster
Text is not SVG - cannot display
\ No newline at end of file diff --git a/cross_cutting/k8s/oauth2_proxy/install.sh b/cross_cutting/k8s/oauth2_proxy/install.sh new file mode 100644 index 0000000..1f5ae73 --- /dev/null +++ b/cross_cutting/k8s/oauth2_proxy/install.sh @@ -0,0 +1,107 @@ +#!/bin/bash + +#Display a small help page with details about the script and optional flags +if [[ $* == *--help* ]] +then + echo "This installer will generate an example deployment containing a spring server secured behind a oauth2 proxy using keyclaok as identity provider." + echo "To run this script successfully Rancher Desktop has to be installed." + echo "" + echo "If you run Kubernetes with a the containerd runtime add --containerd" + echo "" + echo "To skip the port check add --ignore-block" + exit +fi + + +#This is to prevent the scrip from running when port 80 is blocked +#Please make sure that nothing is blocking port 80 because if the port is blocked +#by something the ingress controller will not listen on localhost but on an external cluster ip +#We need to listen on localhost because the service localtest.me will resolve to 127.0.0.1 without any modification in host files +portcount=$(ss -lntu | grep ':80' -c) + +if [[ $* != *--ignore-block* ]] && [ "$portcount" -gt 1 ] +then + echo "Port 80 is currently blocked by another program." + echo "The setup will not work if the ingress controller is not listening on localhost:80." + echo "To skip this test run with --ignore-block" + exit +fi + +#List all docker images and check if the spring server image already got created +if [[ $* == *--containerd* ]] +then + dockerimageresult=$(nerdctl images --namespace k8s.io | grep "spring-server") +else + dockerimageresult=$(docker images | grep "spring-server") +fi + + +#Docker images doesn't exist so create it from the dockerfile +if [ -z "$dockerimageresult" ] +then + #Build server + mvn -f ./ProxyTestServer/pom.xml clean install + #Create image + if [[ $* == *--containerd* ]] + then + nerdctl build -t spring-server --namespace k8s.io ProxyTestServer/ + else + docker build -t spring-server ProxyTestServer/ + fi +fi + + +#Add the required helm repos for an easy keycloak and ingress controller deployment +helm repo add bitnami https://charts.bitnami.com/bitnami +helm repo add ingress-nginx https://kubernetes.github.io/ingress-nginx + + +#Install the nginx ingress controller from a helm chart to use ingress components inside the k8s cluster +#This helm chart can be customized with a values.yaml file +helm upgrade --install ingress-nginx ingress-nginx/ingress-nginx --namespace ingress-nginx --create-namespace + + +#Wait for ingress controller to be fully running before creating an ingress for it +#This is needed to prevent an error causing the ingress deployment to fail +kubectl wait deployment -n ingress-nginx ingress-nginx-controller --for condition=Available=True --timeout=-1s + +#Add an ingress to route all requests to the proxy +#The ingress is using the created nginx controller installed in the prior step +#To test the application locally we use the service localtest.me +#This services is a fully qualified domain name that gets resolved to 127.0.0.1 by public dns servers +kubectl apply -f config/ingress.yaml + + +#Move the exported realm file from keycloak into a configmap +#to access it when importing the realm back into keycloak during the deployment +#Currently there is no way to add users directly +#TODO: Richard Linde Add test users with and without groups +kubectl create configmap keycloak-realm --from-file=config/keycloak/realm.json + + +#Create keycloak from a helm chart +#Usernames and passwords for keycloak and it's database can be found in the keyCloakValues file +helm upgrade --install keycloak bitnami/keycloak -f config/keyCloakValues.yaml + + +#Deploy the spring service that is secured behind the proxy +kubectl apply -f config/springServer.yaml + + +#Add the keyclak proxy to route all requests to +#The proxy will check the request for the necessary permissions +#If the request is valid the proxy will redirect the request to the upstream(spring server) +#If not the browser of the user will redirect him to the oauth screen to login +kubectl apply -f config/oauth2-proxy.yaml + +#Wait for the keycloak pod to be fully set up +kubectl rollout status --watch --timeout=600s statefulset/keycloak + +#kubectl port-forward deployment.apps/ingress-nginx-controller 9947:80 -n ingress-nginx 2>&1 >/dev/null & + +kubectl get all + +echo "Everything set up and ready" +echo "If this is a first time install login to the keycloak ui to create a new user" +echo "Keycloak: http://keycloak.localtest.me" +echo "Spring : http://localtest.me" diff --git a/cross_cutting/k8s/oauth2_proxy/uninstall.sh b/cross_cutting/k8s/oauth2_proxy/uninstall.sh new file mode 100644 index 0000000..9fb9224 --- /dev/null +++ b/cross_cutting/k8s/oauth2_proxy/uninstall.sh @@ -0,0 +1,28 @@ +#!/bin/bash +#docker image rm spring-server -f + +helm uninstall keycloak + +helm uninstall ingress-nginx -n ingress-nginx + +helm repo remove bitnami +helm repo remove ingress-nginx + +kubectl delete -f config/ingress.yaml + +kubectl delete configmap keycloak-realm + +kubectl delete -f config/springServer.yaml + +kubectl delete -f config/oauth2-proxy.yaml + +if [[ $* == *--remove-db* ]] +then + kubectl delete pvc --all +fi + +#pkill -f "port-forward" + +kubectl get all + +echo "Setup got removed" \ No newline at end of file