This is an automated email from the ASF dual-hosted git repository. marat pushed a commit to branch main in repository https://gitbox.apache.org/repos/asf/camel-karavan.git
commit d7d82f32618fef182cd9b46bc84c7f112e37e18d Author: Marat Gubaidullin <ma...@talismancloud.io> AuthorDate: Mon May 27 12:07:13 2024 -0400 Karavan status service --- .../.mvn/wrapper/maven-wrapper.properties | 18 ++ karavan-status/mvnw | 308 ++++++++++++++++++ karavan-status/mvnw.cmd | 205 ++++++++++++ karavan-status/pom.xml | 179 +++++++++++ .../camel/karavan/status/CamelStatusListener.java | 125 ++++++++ .../camel/karavan/status/CamelStatusService.java | 62 ++++ .../apache/camel/karavan/status/ConfigService.java | 78 +++++ .../karavan/status/ContainerStatusListener.java | 121 +++++++ .../camel/karavan/status/KaravanStatusCache.java | 188 +++++++++++ .../camel/karavan/status/KaravanStatusEvents.java | 33 ++ .../camel/karavan/status/KaravanStatusService.java | 67 ++++ .../camel/karavan/status/StatusConstants.java | 46 +++ .../camel/karavan/status/docker/DockerAPI.java | 102 ++++++ .../karavan/status/docker/DockerStatusService.java | 60 ++++ .../camel/karavan/status/docker/DockerUtils.java | 153 +++++++++ .../status/kubernetes/DeploymentEventHandler.java | 103 ++++++ .../kubernetes/DeploymentStatusListener.java | 47 +++ .../status/kubernetes/KubernetesStatusService.java | 150 +++++++++ .../karavan/status/kubernetes/PodEventHandler.java | 160 +++++++++ .../status/kubernetes/ServiceEventHandler.java | 98 ++++++ .../status/kubernetes/ServiceStatusListener.java | 46 +++ .../camel/karavan/status/model/CamelStatus.java | 67 ++++ .../karavan/status/model/CamelStatusRequest.java | 47 +++ .../karavan/status/model/CamelStatusValue.java | 57 ++++ .../camel/karavan/status/model/Configuration.java | 87 +++++ .../camel/karavan/status/model/ContainerPort.java | 55 ++++ .../karavan/status/model/ContainerStatus.java | 356 +++++++++++++++++++++ .../karavan/status/model/DeploymentStatus.java | 116 +++++++ .../camel/karavan/status/model/GroupedKey.java | 85 +++++ .../camel/karavan/status/model/ServiceStatus.java | 113 +++++++ .../src/main/resources/application.properties | 29 ++ 31 files changed, 3361 insertions(+) diff --git a/karavan-status/.mvn/wrapper/maven-wrapper.properties b/karavan-status/.mvn/wrapper/maven-wrapper.properties new file mode 100644 index 00000000..70f4f50f --- /dev/null +++ b/karavan-status/.mvn/wrapper/maven-wrapper.properties @@ -0,0 +1,18 @@ +# 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 +# +# http://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. +distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.8.8/apache-maven-3.8.8-bin.zip +wrapperUrl=https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.2.0/maven-wrapper-3.2.0.jar diff --git a/karavan-status/mvnw b/karavan-status/mvnw new file mode 100755 index 00000000..8d937f4c --- /dev/null +++ b/karavan-status/mvnw @@ -0,0 +1,308 @@ +#!/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 +# +# http://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. +# ---------------------------------------------------------------------------- + +# ---------------------------------------------------------------------------- +# Apache Maven Wrapper startup batch script, version 3.2.0 +# +# Required ENV vars: +# ------------------ +# JAVA_HOME - location of a JDK home dir +# +# Optional ENV vars +# ----------------- +# 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 + JAVA_HOME="$(/usr/libexec/java_home)"; export JAVA_HOME + else + JAVA_HOME="/Library/Java/Home"; export JAVA_HOME + fi + fi + ;; +esac + +if [ -z "$JAVA_HOME" ] ; then + if [ -r /etc/gentoo-release ] ; then + JAVA_HOME=$(java-config --jre-home) + fi +fi + +# For Cygwin, ensure paths are in UNIX format before anything is touched +if $cygwin ; then + [ -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 "$JAVA_HOME" ] && [ -d "$JAVA_HOME" ] && + JAVA_HOME="$(cd "$JAVA_HOME" || (echo "cannot cd into $JAVA_HOME."; exit 1); 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 2>/dev/null; \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 + +# 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/.." || exit 1; pwd) + fi + # end of workaround + done + printf '%s' "$(cd "$basedir" || exit 1; pwd)" +} + +# concatenates all lines of a file +concat_lines() { + if [ -f "$1" ]; then + # Remove \r in case we run on Windows within Git Bash + # and check out the repository with auto CRLF management + # enabled. Otherwise, we may read lines that are delimited with + # \r\n and produce $'-Xarg\r' rather than -Xarg due to word + # splitting rules. + tr -s '\r\n' ' ' < "$1" + fi +} + +log() { + if [ "$MVNW_VERBOSE" = true ]; then + printf '%s\n' "$1" + fi +} + +BASE_DIR=$(find_maven_basedir "$(dirname "$0")") +if [ -z "$BASE_DIR" ]; then + exit 1; +fi + +MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-"$BASE_DIR"}; export MAVEN_PROJECTBASEDIR +log "$MAVEN_PROJECTBASEDIR" + +########################################################################################## +# 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. +########################################################################################## +wrapperJarPath="$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" +if [ -r "$wrapperJarPath" ]; then + log "Found $wrapperJarPath" +else + log "Couldn't find $wrapperJarPath, downloading it ..." + + if [ -n "$MVNW_REPOURL" ]; then + wrapperUrl="$MVNW_REPOURL/org/apache/maven/wrapper/maven-wrapper/3.2.0/maven-wrapper-3.2.0.jar" + else + wrapperUrl="https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.2.0/maven-wrapper-3.2.0.jar" + fi + while IFS="=" read -r key value; do + # Remove '\r' from value to allow usage on windows as IFS does not consider '\r' as a separator ( considers space, tab, new line ('\n'), and custom '=' ) + safeValue=$(echo "$value" | tr -d '\r') + case "$key" in (wrapperUrl) wrapperUrl="$safeValue"; break ;; + esac + done < "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.properties" + log "Downloading from: $wrapperUrl" + + if $cygwin; then + wrapperJarPath=$(cygpath --path --windows "$wrapperJarPath") + fi + + if command -v wget > /dev/null; then + log "Found wget ... using wget" + [ "$MVNW_VERBOSE" = true ] && QUIET="" || QUIET="--quiet" + if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then + wget $QUIET "$wrapperUrl" -O "$wrapperJarPath" || rm -f "$wrapperJarPath" + else + wget $QUIET --http-user="$MVNW_USERNAME" --http-password="$MVNW_PASSWORD" "$wrapperUrl" -O "$wrapperJarPath" || rm -f "$wrapperJarPath" + fi + elif command -v curl > /dev/null; then + log "Found curl ... using curl" + [ "$MVNW_VERBOSE" = true ] && QUIET="" || QUIET="--silent" + if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then + curl $QUIET -o "$wrapperJarPath" "$wrapperUrl" -f -L || rm -f "$wrapperJarPath" + else + curl $QUIET --user "$MVNW_USERNAME:$MVNW_PASSWORD" -o "$wrapperJarPath" "$wrapperUrl" -f -L || rm -f "$wrapperJarPath" + fi + else + log "Falling back to using Java to download" + javaSource="$MAVEN_PROJECTBASEDIR/.mvn/wrapper/MavenWrapperDownloader.java" + javaClass="$MAVEN_PROJECTBASEDIR/.mvn/wrapper/MavenWrapperDownloader.class" + # For Cygwin, switch paths to Windows format before running javac + if $cygwin; then + javaSource=$(cygpath --path --windows "$javaSource") + javaClass=$(cygpath --path --windows "$javaClass") + fi + if [ -e "$javaSource" ]; then + if [ ! -e "$javaClass" ]; then + log " - Compiling MavenWrapperDownloader.java ..." + ("$JAVA_HOME/bin/javac" "$javaSource") + fi + if [ -e "$javaClass" ]; then + log " - Running MavenWrapperDownloader.java ..." + ("$JAVA_HOME/bin/java" -cp .mvn/wrapper MavenWrapperDownloader "$wrapperUrl" "$wrapperJarPath") || rm -f "$wrapperJarPath" + fi + fi + fi +fi +########################################################################################## +# End of extension +########################################################################################## + +# If specified, validate the SHA-256 sum of the Maven wrapper jar file +wrapperSha256Sum="" +while IFS="=" read -r key value; do + case "$key" in (wrapperSha256Sum) wrapperSha256Sum=$value; break ;; + esac +done < "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.properties" +if [ -n "$wrapperSha256Sum" ]; then + wrapperSha256Result=false + if command -v sha256sum > /dev/null; then + if echo "$wrapperSha256Sum $wrapperJarPath" | sha256sum -c > /dev/null 2>&1; then + wrapperSha256Result=true + fi + elif command -v shasum > /dev/null; then + if echo "$wrapperSha256Sum $wrapperJarPath" | shasum -a 256 -c > /dev/null 2>&1; then + wrapperSha256Result=true + fi + else + echo "Checksum validation was requested but neither 'sha256sum' or 'shasum' are available." + echo "Please install either command, or disable validation by removing 'wrapperSha256Sum' from your maven-wrapper.properties." + exit 1 + fi + if [ $wrapperSha256Result = false ]; then + echo "Error: Failed to validate Maven wrapper SHA-256, your Maven wrapper might be compromised." >&2 + echo "Investigate or delete $wrapperJarPath to attempt a clean download." >&2 + echo "If you updated your Maven version, you need to update the specified wrapperSha256Sum property." >&2 + exit 1 + fi +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 "$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 + +# shellcheck disable=SC2086 # safe args +exec "$JAVACMD" \ + $MAVEN_OPTS \ + $MAVEN_DEBUG_OPTS \ + -classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \ + "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \ + ${WRAPPER_LAUNCHER} $MAVEN_CONFIG "$@" diff --git a/karavan-status/mvnw.cmd b/karavan-status/mvnw.cmd new file mode 100644 index 00000000..f80fbad3 --- /dev/null +++ b/karavan-status/mvnw.cmd @@ -0,0 +1,205 @@ +@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 http://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 Apache Maven Wrapper startup batch script, version 3.2.0 +@REM +@REM Required ENV vars: +@REM JAVA_HOME - location of a JDK home dir +@REM +@REM Optional ENV vars +@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 WRAPPER_URL="https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.2.0/maven-wrapper-3.2.0.jar" + +FOR /F "usebackq tokens=1,2 delims==" %%A IN ("%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties") DO ( + IF "%%A"=="wrapperUrl" SET WRAPPER_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 WRAPPER_URL="%MVNW_REPOURL%/org/apache/maven/wrapper/maven-wrapper/3.2.0/maven-wrapper-3.2.0.jar" + ) + if "%MVNW_VERBOSE%" == "true" ( + echo Couldn't find %WRAPPER_JAR%, downloading it ... + echo Downloading from: %WRAPPER_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('%WRAPPER_URL%', '%WRAPPER_JAR%')"^ + "}" + if "%MVNW_VERBOSE%" == "true" ( + echo Finished downloading %WRAPPER_JAR% + ) +) +@REM End of extension + +@REM If specified, validate the SHA-256 sum of the Maven wrapper jar file +SET WRAPPER_SHA_256_SUM="" +FOR /F "usebackq tokens=1,2 delims==" %%A IN ("%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties") DO ( + IF "%%A"=="wrapperSha256Sum" SET WRAPPER_SHA_256_SUM=%%B +) +IF NOT %WRAPPER_SHA_256_SUM%=="" ( + powershell -Command "&{"^ + "$hash = (Get-FileHash \"%WRAPPER_JAR%\" -Algorithm SHA256).Hash.ToLower();"^ + "If('%WRAPPER_SHA_256_SUM%' -ne $hash){"^ + " Write-Output 'Error: Failed to validate Maven wrapper SHA-256, your Maven wrapper might be compromised.';"^ + " Write-Output 'Investigate or delete %WRAPPER_JAR% to attempt a clean download.';"^ + " Write-Output 'If you updated your Maven version, you need to update the specified wrapperSha256Sum property.';"^ + " exit 1;"^ + "}"^ + "}" + if ERRORLEVEL 1 goto error +) + +@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/karavan-status/pom.xml b/karavan-status/pom.xml new file mode 100644 index 00000000..4f734644 --- /dev/null +++ b/karavan-status/pom.xml @@ -0,0 +1,179 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + ~ 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 + ~ + ~ http://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. + --> +<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd" + xmlns="http://maven.apache.org/POM/4.0.0" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> + <modelVersion>4.0.0</modelVersion> + <groupId>org.apache.camel.karavan</groupId> + <artifactId>karavan-status</artifactId> + <version>4.7.0</version> + + <properties> + <compiler-plugin.version>3.11.0</compiler-plugin.version> + <maven.compiler.parameters>true</maven.compiler.parameters> + <maven.compiler.source>17</maven.compiler.source> + <jandex-maven-plugin-version>3.1.1</jandex-maven-plugin-version> + <maven.compiler.target>17</maven.compiler.target> + <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> + <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> + <quarkus.platform.artifact-id>quarkus-bom</quarkus.platform.artifact-id> + <quarkus.platform.group-id>io.quarkus.platform</quarkus.platform.group-id> + <quarkus.platform.version>3.8.2</quarkus.platform.version> + <surefire-plugin.version>3.1.0</surefire-plugin.version> + <resources-plugin.version>3.3.0</resources-plugin.version> + <docker-java.version>3.3.6</docker-java.version> + </properties> + + <dependencyManagement> + <dependencies> + <dependency> + <groupId>${quarkus.platform.group-id}</groupId> + <artifactId>${quarkus.platform.artifact-id}</artifactId> + <version>${quarkus.platform.version}</version> + <type>pom</type> + <scope>import</scope> + </dependency> + <dependency> + <groupId>${quarkus.platform.group-id}</groupId> + <artifactId>quarkus-camel-bom</artifactId> + <version>${quarkus.platform.version}</version> + <type>pom</type> + <scope>import</scope> + </dependency> + </dependencies> + </dependencyManagement> + + <dependencies> + <dependency> + <groupId>io.quarkus</groupId> + <artifactId>quarkus-smallrye-reactive-messaging</artifactId> + </dependency> + <dependency> + <groupId>io.quarkus</groupId> + <artifactId>quarkus-scheduler</artifactId> + </dependency> + <dependency> + <groupId>io.quarkus</groupId> + <artifactId>quarkus-resteasy-reactive-jackson</artifactId> + </dependency> + <dependency> + <groupId>io.smallrye.reactive</groupId> + <artifactId>smallrye-mutiny-vertx-web-client</artifactId> + </dependency> + <dependency> + <groupId>io.quarkus</groupId> + <artifactId>quarkus-smallrye-fault-tolerance</artifactId> + </dependency> + <dependency> + <groupId>io.quarkus</groupId> + <artifactId>quarkus-arc</artifactId> + </dependency> + <dependency> + <groupId>io.quarkus</groupId> + <artifactId>quarkus-smallrye-openapi</artifactId> + </dependency> + <dependency> + <groupId>io.quarkus</groupId> + <artifactId>quarkus-kubernetes-client</artifactId> + </dependency> + <!-- Docker --> + <dependency> + <groupId>com.github.docker-java</groupId> + <artifactId>docker-java-core</artifactId> + <version>${docker-java.version}</version> + <optional>false</optional> + </dependency> + <dependency> + <groupId>com.github.docker-java</groupId> + <artifactId>docker-java-transport-zerodep</artifactId> + <version>${docker-java.version}</version> + <optional>false</optional> + </dependency> + <dependency> + <groupId>io.quarkus</groupId> + <artifactId>quarkus-junit5</artifactId> + <scope>test</scope> + </dependency> + <dependency> + <groupId>io.rest-assured</groupId> + <artifactId>rest-assured</artifactId> + <scope>test</scope> + </dependency> + </dependencies> + <build> + <resources> + <resource> + <directory>src/main/resources</directory> + <filtering>true</filtering> + <includes> + <include>**/**</include> + </includes> + <excludes> + <exclude>**/.gitignore</exclude> + </excludes> + </resource> + </resources> + <plugins> + <plugin> + <groupId>${quarkus.platform.group-id}</groupId> + <artifactId>quarkus-maven-plugin</artifactId> + <version>${quarkus.platform.version}</version> + <extensions>true</extensions> + <executions> + <execution> + <goals> + <goal>build</goal> + <goal>generate-code</goal> + <goal>generate-code-tests</goal> + </goals> + </execution> + </executions> + </plugin> + <plugin> + <artifactId>maven-compiler-plugin</artifactId> + <version>${compiler-plugin.version}</version> + <configuration> + <parameters>${maven.compiler.parameters}</parameters> + </configuration> + </plugin> + <plugin> + <artifactId>maven-surefire-plugin</artifactId> + <version>${surefire-plugin.version}</version> + <configuration> + <systemPropertyVariables> + <java.util.logging.manager>org.jboss.logmanager.LogManager</java.util.logging.manager> + <maven.home>${maven.home}</maven.home> + </systemPropertyVariables> + </configuration> + </plugin> + </plugins> + </build> + <repositories> + <repository> + <id>apache.snapshots</id> + <name>Apache Development Snapshot Repository</name> + <url>https://repository.apache.org/content/repositories/snapshots/</url> + <releases> + <enabled>false</enabled> + </releases> + <snapshots> + <enabled>true</enabled> + </snapshots> + </repository> + </repositories> +</project> diff --git a/karavan-status/src/main/java/org/apache/camel/karavan/status/CamelStatusListener.java b/karavan-status/src/main/java/org/apache/camel/karavan/status/CamelStatusListener.java new file mode 100644 index 00000000..8fe12c72 --- /dev/null +++ b/karavan-status/src/main/java/org/apache/camel/karavan/status/CamelStatusListener.java @@ -0,0 +1,125 @@ +/* + * 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 + * + * http://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. + */ +package org.apache.camel.karavan.status; + +import io.quarkus.vertx.ConsumeEvent; +import io.vertx.core.json.JsonObject; +import io.vertx.mutiny.core.Vertx; +import io.vertx.mutiny.core.buffer.Buffer; +import io.vertx.mutiny.ext.web.client.HttpResponse; +import io.vertx.mutiny.ext.web.client.WebClient; +import jakarta.enterprise.context.ApplicationScoped; +import jakarta.inject.Inject; +import org.apache.camel.karavan.status.model.CamelStatus; +import org.apache.camel.karavan.status.model.CamelStatusRequest; +import org.apache.camel.karavan.status.model.CamelStatusValue; +import org.apache.camel.karavan.status.model.ContainerStatus; +import org.eclipse.microprofile.config.inject.ConfigProperty; +import org.eclipse.microprofile.faulttolerance.CircuitBreaker; +import org.jboss.logging.Logger; + +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.ExecutionException; + +import static org.apache.camel.karavan.status.KaravanStatusEvents.CMD_COLLECT_CAMEL_STATUS; + +@ApplicationScoped +public class CamelStatusListener { + + private static final Logger LOGGER = Logger.getLogger(CamelStatusListener.class.getName()); + + @Inject + KaravanStatusCache karavanStatusCache; + + @ConfigProperty(name = "karavan.environment") + String environment; + + @Inject + Vertx vertx; + + WebClient webClient; + + public WebClient getWebClient() { + if (webClient == null) { + webClient = WebClient.create(vertx); + } + return webClient; + } + + @ConsumeEvent(value = CMD_COLLECT_CAMEL_STATUS, blocking = true, ordered = true) + public void collectCamelStatuses(JsonObject data) { + try { + CamelStatusRequest dms = data.getJsonObject("camelStatusRequest").mapTo(CamelStatusRequest.class); + ContainerStatus containerStatus = data.getJsonObject("containerStatus").mapTo(ContainerStatus.class); + LOGGER.debug("Collect Camel Status for " + containerStatus.getContainerName()); + String projectId = dms.getProjectId(); + String containerName = dms.getContainerName(); + List<CamelStatusValue> statuses = new ArrayList<>(); + for (CamelStatusValue.Name statusName : CamelStatusValue.Name.values()) { + String status = getCamelStatus(containerStatus, statusName); + if (status != null) { + statuses.add(new CamelStatusValue(statusName, status)); + } + } + CamelStatus cs = new CamelStatus(projectId, containerName, statuses, environment); + karavanStatusCache.saveCamelStatus(cs); + } catch (Exception ex) { + LOGGER.error("collectCamelStatuses " + (ex.getCause() != null ? ex.getCause().getMessage() : ex.getMessage())); + } + } + + public String getContainerAddressForStatus(ContainerStatus containerStatus) throws Exception { + if (ConfigService.inKubernetes()) { + return "http://" + containerStatus.getPodIP() + ":8080"; + } else if (ConfigService.inDocker()) { + return "http://" + containerStatus.getContainerName() + ":8080"; + } else if (containerStatus.getPorts() != null && !containerStatus.getPorts().isEmpty()) { + Integer port = containerStatus.getPorts().get(0).getPublicPort(); + if (port != null) { + return "http://localhost:" + port; + } + } + throw new Exception("No port configured for project " + containerStatus.getContainerName()); + } + + public String getCamelStatus(ContainerStatus containerStatus, CamelStatusValue.Name statusName) throws Exception { + String url = getContainerAddressForStatus(containerStatus) + "/q/dev/" + statusName.name(); + try { + return getResult(url, 500); + } catch (InterruptedException | ExecutionException ex) { + LOGGER.error("getCamelStatus " + (ex.getCause() != null ? ex.getCause().getMessage() : ex.getMessage())); + } + return null; + } + + @CircuitBreaker(requestVolumeThreshold = 10, failureRatio = 0.5, delay = 1000) + public String getResult(String url, int timeout) throws InterruptedException, ExecutionException { + try { + HttpResponse<Buffer> result = getWebClient().getAbs(url).putHeader("Accept", "application/json") + .timeout(timeout).send().subscribeAsCompletionStage().toCompletableFuture().get(); + if (result.statusCode() == 200) { + JsonObject res = result.bodyAsJsonObject(); + return res.encodePrettily(); + } + } catch (Exception ex) { + LOGGER.error("getResult " + url); + LOGGER.error("getResult " + (ex.getCause() != null ? ex.getCause().getMessage() : ex.getMessage())); + } + return null; + } +} \ No newline at end of file diff --git a/karavan-status/src/main/java/org/apache/camel/karavan/status/CamelStatusService.java b/karavan-status/src/main/java/org/apache/camel/karavan/status/CamelStatusService.java new file mode 100644 index 00000000..9dd62852 --- /dev/null +++ b/karavan-status/src/main/java/org/apache/camel/karavan/status/CamelStatusService.java @@ -0,0 +1,62 @@ +/* + * 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 + * + * http://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. + */ +package org.apache.camel.karavan.status; + +import io.quarkus.scheduler.Scheduled; +import io.vertx.core.json.JsonObject; +import io.vertx.mutiny.core.eventbus.EventBus; +import jakarta.enterprise.context.ApplicationScoped; +import jakarta.inject.Inject; +import org.apache.camel.karavan.status.model.CamelStatusRequest; +import org.apache.camel.karavan.status.model.ContainerStatus; +import org.eclipse.microprofile.config.inject.ConfigProperty; +import org.jboss.logging.Logger; + +import java.util.*; + +import static org.apache.camel.karavan.status.KaravanStatusEvents.CMD_COLLECT_CAMEL_STATUS; + +@ApplicationScoped +public class CamelStatusService { + + private static final Logger LOGGER = Logger.getLogger(CamelStatusService.class.getName()); + + @Inject + KaravanStatusCache karavanStatusCache; + + @ConfigProperty(name = "karavan.environment") + String environment; + + @Inject + EventBus eventBus; + + @Scheduled(every = "{karavan.camel.status.interval}", concurrentExecution = Scheduled.ConcurrentExecution.SKIP) + public void collectCamelStatuses() { + LOGGER.debug("Collect Camel Statuses"); + karavanStatusCache.getContainerStatuses(environment).stream() + .filter(cs -> + cs.getType() == ContainerStatus.ContainerType.project + || cs.getType() == ContainerStatus.ContainerType.devmode + ).filter(cs -> Objects.equals(cs.getCamelRuntime(), StatusConstants.CamelRuntime.CAMEL_MAIN.getValue())) + .forEach(cs -> { + CamelStatusRequest csr = new CamelStatusRequest(cs.getProjectId(), cs.getContainerName()); + eventBus.publish(CMD_COLLECT_CAMEL_STATUS, + JsonObject.mapFrom(Map.of("containerStatus", cs, "camelStatusRequest", csr)) + ); + }); + } +} \ No newline at end of file diff --git a/karavan-status/src/main/java/org/apache/camel/karavan/status/ConfigService.java b/karavan-status/src/main/java/org/apache/camel/karavan/status/ConfigService.java new file mode 100644 index 00000000..d3d3cbc5 --- /dev/null +++ b/karavan-status/src/main/java/org/apache/camel/karavan/status/ConfigService.java @@ -0,0 +1,78 @@ +/* + * 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 + * + * http://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. + */ +package org.apache.camel.karavan.status; + +import io.quarkus.runtime.StartupEvent; +import jakarta.enterprise.context.ApplicationScoped; +import jakarta.enterprise.event.Observes; +import org.apache.camel.karavan.status.model.Configuration; +import org.eclipse.microprofile.config.inject.ConfigProperty; + +import java.nio.file.Files; +import java.nio.file.Paths; +import java.util.List; +import java.util.Objects; + +@ApplicationScoped +public class ConfigService { + + @ConfigProperty(name = "karavan.title") + String title; + + @ConfigProperty(name = "karavan.version") + String version; + + @ConfigProperty(name = "karavan.environment") + String environment; + + @ConfigProperty(name = "karavan.environments") + List<String> environments; + + + private Configuration configuration; + private static Boolean inKubernetes; + private static Boolean inDocker; + + void onStart(@Observes StartupEvent ev) { + configuration = new Configuration( + title, + version, + inKubernetes() ? "kubernetes" : "docker", + environment, + environments + ); + } + + public Configuration getConfiguration() { + return configuration; + } + + public static boolean inKubernetes() { + if (inKubernetes == null) { + inKubernetes = Objects.nonNull(System.getenv("KUBERNETES_SERVICE_HOST")); + } + return inKubernetes; + } + + public static boolean inDocker() { + if (inDocker == null) { + inDocker = !inKubernetes() && Files.exists(Paths.get(".dockerenv")); + } + return inDocker; + } + +} \ No newline at end of file diff --git a/karavan-status/src/main/java/org/apache/camel/karavan/status/ContainerStatusListener.java b/karavan-status/src/main/java/org/apache/camel/karavan/status/ContainerStatusListener.java new file mode 100644 index 00000000..c1e3c164 --- /dev/null +++ b/karavan-status/src/main/java/org/apache/camel/karavan/status/ContainerStatusListener.java @@ -0,0 +1,121 @@ +/* + * 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 + * + * http://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. + */ + +package org.apache.camel.karavan.status; + +import io.quarkus.vertx.ConsumeEvent; +import io.vertx.core.eventbus.EventBus; +import io.vertx.core.json.JsonArray; +import io.vertx.core.json.JsonObject; +import jakarta.enterprise.context.ApplicationScoped; +import jakarta.inject.Inject; +import org.apache.camel.karavan.status.model.ContainerStatus; +import org.apache.camel.karavan.status.docker.DockerAPI; +import org.eclipse.microprofile.config.inject.ConfigProperty; +import org.jboss.logging.Logger; + +import java.time.Instant; +import java.time.temporal.ChronoUnit; +import java.util.List; +import java.util.Objects; + +import static org.apache.camel.karavan.status.KaravanStatusEvents.*; + +@ApplicationScoped +public class ContainerStatusListener { + + private static final Logger LOGGER = Logger.getLogger(ContainerStatusListener.class.getName()); + @ConfigProperty(name = "karavan.environment") + String environment; + + @Inject + KaravanStatusCache karavanStatusCache; + + @Inject + DockerAPI dockerAPI; + + @Inject + EventBus eventBus; + + @ConsumeEvent(value = CMD_COLLECT_CONTAINER_STATISTIC, blocking = true) + void collectContainersStatistics(JsonObject data) { + ContainerStatus status = data.mapTo(ContainerStatus.class); + ContainerStatus newStatus = dockerAPI.collectContainerStatistics(status); + eventBus.publish(CONTAINER_UPDATED, JsonObject.mapFrom(newStatus)); + } + + @ConsumeEvent(value = CMD_CLEAN_STATUSES, blocking = true) + void cleanContainersStatuses(JsonArray list) { + List<ContainerStatus> statusesInDocker = list.stream().map(o -> ((JsonObject)o).mapTo(ContainerStatus.class)).toList(); + List<String> namesInDocker = statusesInDocker.stream().map(ContainerStatus::getContainerName).toList(); + List<ContainerStatus> statusesInCache = karavanStatusCache.getContainerStatuses(environment); + // clean deleted + statusesInCache.stream() + .filter(cs -> !checkTransit(cs)) + .filter(cs -> !namesInDocker.contains(cs.getContainerName())) + .forEach(containerStatus -> { + eventBus.publish(CONTAINER_DELETED, JsonObject.mapFrom(containerStatus)); + }); + } + + @ConsumeEvent(value = CONTAINER_DELETED, blocking = true, ordered = true) + public void cleanContainersStatus(JsonObject data) { + ContainerStatus containerStatus = data.mapTo(ContainerStatus.class); + karavanStatusCache.deleteContainerStatus(containerStatus); + karavanStatusCache.deleteCamelStatuses(containerStatus.getProjectId(), containerStatus.getEnv()); + } + + private boolean checkTransit(ContainerStatus cs) { + if (cs.getContainerId() == null && cs.getInTransit()) { + return Instant.parse(cs.getInitDate()).until(Instant.now(), ChronoUnit.SECONDS) < 10; + } + return false; + } + + @ConsumeEvent(value = CONTAINER_UPDATED, blocking = true, ordered = true) + public void saveContainerStatus(JsonObject data) { + ContainerStatus newStatus = data.mapTo(ContainerStatus.class); + ContainerStatus oldStatus = karavanStatusCache.getContainerStatus(newStatus.getProjectId(), newStatus.getEnv(), newStatus.getContainerName()); + + if (oldStatus == null) { + karavanStatusCache.saveContainerStatus(newStatus); + } else if (Objects.equals(oldStatus.getInTransit(), Boolean.FALSE)) { + saveContainerStatus(newStatus, oldStatus); + } else if (Objects.equals(oldStatus.getInTransit(), Boolean.TRUE)) { + if (!Objects.equals(oldStatus.getState(), newStatus.getState()) || newStatus.getCpuInfo() == null || newStatus.getCpuInfo().isEmpty()) { + saveContainerStatus(newStatus, oldStatus); + } + } + } + + private void saveContainerStatus(ContainerStatus newStatus, ContainerStatus oldStatus) { + if (Objects.equals("exited", newStatus.getState()) || Objects.equals("dead", newStatus.getState())) { + if (Objects.isNull(oldStatus.getFinished())) { + newStatus.setFinished(Instant.now().toString()); + newStatus.setMemoryInfo("0MiB/0MiB"); + newStatus.setCpuInfo("0%"); + } else if (Objects.nonNull(oldStatus.getFinished())) { + return; + } + } + if (newStatus.getCpuInfo() == null || newStatus.getCpuInfo().isBlank()) { + newStatus.setCpuInfo(oldStatus.getCpuInfo()); + newStatus.setMemoryInfo(oldStatus.getMemoryInfo()); + } + karavanStatusCache.saveContainerStatus(newStatus); + } +} \ No newline at end of file diff --git a/karavan-status/src/main/java/org/apache/camel/karavan/status/KaravanStatusCache.java b/karavan-status/src/main/java/org/apache/camel/karavan/status/KaravanStatusCache.java new file mode 100644 index 00000000..b53fbcc4 --- /dev/null +++ b/karavan-status/src/main/java/org/apache/camel/karavan/status/KaravanStatusCache.java @@ -0,0 +1,188 @@ +/* + * 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 + * + * http://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. + */ +package org.apache.camel.karavan.status; + +import jakarta.enterprise.inject.Default; +import jakarta.inject.Singleton; +import org.apache.camel.karavan.status.model.*; + +import java.util.*; +import java.util.concurrent.ConcurrentHashMap; + +@Default +@Singleton +public class KaravanStatusCache { + + private final Map<String, DeploymentStatus> deploymentStatuses = new ConcurrentHashMap<>(); + private final Map<String, ContainerStatus> containerStatuses = new ConcurrentHashMap<>(); + private final Map<String, Boolean> transits = new ConcurrentHashMap<>(); + private final Map<String, ServiceStatus> serviceStatuses = new ConcurrentHashMap<>(); + private final Map<String, CamelStatus> camelStatuses = new ConcurrentHashMap<>(); + + public DeploymentStatus getDeploymentStatus(String projectId, String environment) { + return deploymentStatuses.get(GroupedKey.create(projectId, environment, projectId)); + } + + public void saveDeploymentStatus(DeploymentStatus status) { + deploymentStatuses.put(GroupedKey.create(status.getProjectId(), status.getEnv(), status.getProjectId()), status); + } + + public void deleteDeploymentStatus(DeploymentStatus status) { + deploymentStatuses.remove(GroupedKey.create(status.getProjectId(), status.getEnv(), status.getProjectId())); + } + + public List<DeploymentStatus> getDeploymentStatuses() { + return new ArrayList<>(deploymentStatuses.values()); + } + + public List<DeploymentStatus> getDeploymentStatuses(String env) { + return deploymentStatuses.values().stream().filter(pf -> Objects.equals(pf.getEnv(), env)).toList(); + } + + public void deleteAllDeploymentsStatuses() { + deploymentStatuses.clear(); + } + + public void saveServiceStatus(ServiceStatus status) { + serviceStatuses.put(GroupedKey.create(status.getProjectId(), status.getEnv(), status.getProjectId()), status); + } + + public void deleteServiceStatus(ServiceStatus status) { + serviceStatuses.remove(GroupedKey.create(status.getProjectId(), status.getEnv(), status.getProjectId())); + } + + public List<ServiceStatus> getServiceStatuses() { + return new ArrayList<>(serviceStatuses.values()); + } + + public List<Boolean> getTransits() { + return new ArrayList<>(transits.values()); + } + + public Boolean getTransit(String projectId, String env, String containerName) { + return transits.get(GroupedKey.create(projectId, env, containerName)); + } + + public void setTransit(String projectId, String env, String containerName) { + transits.put(GroupedKey.create(projectId, env, containerName), true); + } + + public List<ContainerStatus> getContainerStatuses() { + return new ArrayList<>(containerStatuses.values()); + } + + public List<ContainerStatus> getContainerStatuses(String projectId, String env) { + return containerStatuses.values().stream().filter(el -> Objects.equals(el.getProjectId(), projectId) && Objects.equals(el.getEnv(), env)).toList(); + } + + public ContainerStatus getContainerStatus(String projectId, String env, String containerName) { + return getContainerStatus(GroupedKey.create(projectId, env, containerName)); + } + + public ContainerStatus getContainerStatus(String key) { + return containerStatuses.get(key); + } + + public ContainerStatus getDevModeContainerStatus(String projectId, String env) { + return containerStatuses.get(GroupedKey.create(projectId, env, projectId)); + } + + public List<ContainerStatus> getContainerStatuses(String env) { + return containerStatuses.values().stream().filter(el -> Objects.equals(el.getEnv(), env)).toList(); + } + + public List<ContainerStatus> getAllContainerStatuses() { + return new ArrayList<>(containerStatuses.values()); + } + + public void saveContainerStatus(ContainerStatus status) { + containerStatuses.put(GroupedKey.create(status.getProjectId(), status.getEnv(), status.getContainerName()), status); + } + + public void deleteContainerStatus(ContainerStatus status) { + containerStatuses.remove(GroupedKey.create(status.getProjectId(), status.getEnv(), status.getContainerName())); + } + + public void deleteAllContainersStatuses() { + containerStatuses.clear(); + } + + public void deleteContainerStatus(String projectId, String env, String containerName) { + containerStatuses.remove(GroupedKey.create(projectId, env, containerName)); + } + + public CamelStatus getCamelStatus(String projectId, String env, String containerName) { + var key = GroupedKey.create(projectId, env, containerName); + return camelStatuses.get(key); + } + + public CamelStatus getCamelStatus(String key) { + return camelStatuses.get(key); + } + + public List<CamelStatus> getCamelStatusesByEnv(CamelStatusValue.Name name) { + return camelStatuses.values().stream().map(cs -> { + var values = cs.getStatuses(); + cs.setStatuses(values.stream().filter(v -> Objects.equals(v.getName(), name)).toList()); + return cs; + }).toList(); + } + + public List<CamelStatus> getCamelStatusesByProjectAndEnv(String projectId, String env) { + return camelStatuses.values().stream().filter(el -> Objects.equals(el.getProjectId(), projectId) && Objects.equals(el.getEnv(), env)).toList(); + } + + public void saveCamelStatus(CamelStatus status) { + var key = GroupedKey.create(status.getProjectId(), status.getEnv(), status.getContainerName()); + camelStatuses.put(key, status); + } + + public void deleteCamelStatus(String projectId, String name, String env) { + var key = GroupedKey.create(projectId, env, name); + camelStatuses.remove(key); + } + + public void deleteCamelStatuses(String projectId, String env) { + camelStatuses.values().stream().filter(el -> Objects.equals(el.getProjectId(), projectId) && Objects.equals(el.getEnv(), env)) + .forEach(s -> { + var key = GroupedKey.create(projectId, env, s.getContainerName()); + camelStatuses.remove(key); + }); + } + + public void deleteAllCamelStatuses() { + camelStatuses.clear(); + } + + public List<ContainerStatus> getLoadedDevModeStatuses() { + return containerStatuses.values().stream().filter(el -> Objects.equals(el.getType(), ContainerStatus.ContainerType.devmode) && Objects.equals(el.getCodeLoaded(), true)).toList(); + } + + public List<ContainerStatus> getDevModeStatuses() { + return containerStatuses.values().stream().filter(el -> Objects.equals(el.getType(), ContainerStatus.ContainerType.devmode)).toList(); + } + + public List<ContainerStatus> getContainerStatusByEnv(String env) { + return containerStatuses.values().stream().filter(el -> Objects.equals(el.getEnv(), env)).toList(); + } + + public void clearAllStatuses() { + deploymentStatuses.clear(); + containerStatuses.clear(); + camelStatuses.clear(); + } +} diff --git a/karavan-status/src/main/java/org/apache/camel/karavan/status/KaravanStatusEvents.java b/karavan-status/src/main/java/org/apache/camel/karavan/status/KaravanStatusEvents.java new file mode 100644 index 00000000..ff425445 --- /dev/null +++ b/karavan-status/src/main/java/org/apache/camel/karavan/status/KaravanStatusEvents.java @@ -0,0 +1,33 @@ +/* + * 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 + * + * http://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. + */ +package org.apache.camel.karavan.status; + +public class KaravanStatusEvents { + + public static final String CMD_COLLECT_CAMEL_STATUS = "CMD_COLLECT_CAMEL_STATUS"; + public static final String CMD_COLLECT_CONTAINER_STATISTIC = "CMD_COLLECT_CONTAINER_STATISTIC"; + public static final String CMD_CLEAN_STATUSES = "CMD_CLEAN_STATUSES"; + + public static final String CONTAINER_UPDATED = "CONTAINER_UPDATED"; + public static final String CONTAINER_DELETED = "CONTAINER_DELETED"; + + public static final String DEPLOYMENT_UPDATED = "DEPLOYMENT_UPDATED"; + public static final String DEPLOYMENT_DELETED = "DEPLOYMENT_DELETED"; + + public static final String SERVICE_UPDATED = "SERVICE_UPDATED"; + public static final String SERVICE_DELETED = "SERVICE_DELETED"; +} \ No newline at end of file diff --git a/karavan-status/src/main/java/org/apache/camel/karavan/status/KaravanStatusService.java b/karavan-status/src/main/java/org/apache/camel/karavan/status/KaravanStatusService.java new file mode 100644 index 00000000..aa2c772a --- /dev/null +++ b/karavan-status/src/main/java/org/apache/camel/karavan/status/KaravanStatusService.java @@ -0,0 +1,67 @@ +/* + * 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 + * + * http://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. + */ +package org.apache.camel.karavan.status; + +import io.quarkus.runtime.ShutdownEvent; +import io.quarkus.runtime.Startup; +import io.quarkus.runtime.StartupEvent; +import jakarta.enterprise.event.Observes; +import jakarta.inject.Inject; +import jakarta.inject.Singleton; +import org.apache.camel.karavan.status.docker.DockerAPI; +import org.apache.camel.karavan.status.kubernetes.KubernetesStatusService; +import org.eclipse.microprofile.health.HealthCheck; +import org.eclipse.microprofile.health.HealthCheckResponse; +import org.eclipse.microprofile.health.Liveness; +import org.jboss.logging.Logger; + +import java.io.IOException; + +@Startup +@Liveness +@Singleton +public class KaravanStatusService implements HealthCheck { + + private static final Logger LOGGER = Logger.getLogger(KaravanStatusService.class.getName()); + + @Inject + KubernetesStatusService kubernetesStatusService; + + @Inject + DockerAPI dockerAPI; + + @Override + public HealthCheckResponse call() { + return HealthCheckResponse.up("Karavan"); + } + + void onStart(@Observes StartupEvent ev) throws Exception { + LOGGER.info("Status Listeners: starting..."); + if (ConfigService.inKubernetes()) { + kubernetesStatusService.startInformers(); + } + LOGGER.info("Status Listeners: started"); + } + + void onStop(@Observes ShutdownEvent ev) throws IOException { + LOGGER.info("Status Listeners: stopping..."); + if (ConfigService.inKubernetes()) { + kubernetesStatusService.stopInformers(); + } + LOGGER.info("Status Listeners: stopped"); + } +} diff --git a/karavan-status/src/main/java/org/apache/camel/karavan/status/StatusConstants.java b/karavan-status/src/main/java/org/apache/camel/karavan/status/StatusConstants.java new file mode 100644 index 00000000..981a9650 --- /dev/null +++ b/karavan-status/src/main/java/org/apache/camel/karavan/status/StatusConstants.java @@ -0,0 +1,46 @@ +/* + * 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 + * + * http://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. + */ +package org.apache.camel.karavan.status; + +public class StatusConstants { + + + public static final String LABEL_TYPE = "org.apache.camel.karavan/type"; + public static final String LABEL_PROJECT_ID = "org.apache.camel.karavan/projectId"; + public static final String LABEL_CAMEL_RUNTIME = "org.apache.camel.karavan/runtime"; + public static final String LABEL_KUBERNETES_RUNTIME = "app.kubernetes.io/runtime"; + public static final String ANNOTATION_COMMIT = "jkube.eclipse.org/git-commit"; + + public static final String CAMEL_PREFIX = "camel"; + + public enum CamelRuntime { + CAMEL_MAIN("camel-main"), + QUARKUS("quarkus"), + SPRING_BOOT("spring-boot"); + + private final String value; + + public String getValue() { + return value; + } + + CamelRuntime(String value) { + this.value = value; + } + } + +} diff --git a/karavan-status/src/main/java/org/apache/camel/karavan/status/docker/DockerAPI.java b/karavan-status/src/main/java/org/apache/camel/karavan/status/docker/DockerAPI.java new file mode 100644 index 00000000..72dcc145 --- /dev/null +++ b/karavan-status/src/main/java/org/apache/camel/karavan/status/docker/DockerAPI.java @@ -0,0 +1,102 @@ +/* + * 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 + * + * http://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. + */ +package org.apache.camel.karavan.status.docker; + +import com.github.dockerjava.api.DockerClient; +import com.github.dockerjava.api.model.*; +import com.github.dockerjava.core.DefaultDockerClientConfig; +import com.github.dockerjava.core.DockerClientConfig; +import com.github.dockerjava.core.DockerClientImpl; +import com.github.dockerjava.core.InvocationBuilder; +import com.github.dockerjava.transport.DockerHttpClient; +import com.github.dockerjava.zerodep.ZerodepDockerHttpClient; +import jakarta.enterprise.context.ApplicationScoped; +import org.apache.camel.karavan.status.model.ContainerStatus; +import org.eclipse.microprofile.config.inject.ConfigProperty; + +import java.io.IOException; +import java.util.*; + +@ApplicationScoped +public class DockerAPI { + + @ConfigProperty(name = "karavan.environment") + String environment; + + private DockerClient dockerClient; + + public List<ContainerStatus> collectContainersStatuses() { + List<ContainerStatus> result = new ArrayList<>(); + getDockerClient().listContainersCmd().withShowAll(true).exec().forEach(container -> { + ContainerStatus containerStatus = DockerUtils.getContainerStatus(container, environment); + result.add(containerStatus); + }); + return result; + } + + public ContainerStatus collectContainerStatistics(ContainerStatus containerStatus) { + Container container = getContainerByName(containerStatus.getContainerName()); + Statistics stats = getContainerStats(container.getId()); + DockerUtils.updateStatistics(containerStatus, stats); + return containerStatus; + } + + public Container getContainerByName(String name) { + List<Container> containers = findContainer(name); + return !containers.isEmpty() ? containers.get(0) : null; + } + + public Statistics getContainerStats(String containerId) { + InvocationBuilder.AsyncResultCallback<Statistics> callback = new InvocationBuilder.AsyncResultCallback<>(); + getDockerClient().statsCmd(containerId).withContainerId(containerId).withNoStream(true).exec(callback); + Statistics stats = null; + try { + stats = callback.awaitResult(); + callback.close(); + } catch (RuntimeException | IOException e) { + // you may want to throw an exception here + } + return stats; + } + + public List<Container> findContainer(String containerName) { + return getDockerClient().listContainersCmd().withShowAll(true).withNameFilter(List.of(containerName)).exec() + .stream().filter(c -> Objects.equals(c.getNames()[0].replaceFirst("/", ""), containerName)).toList(); + } + + private DockerClientConfig getDockerClientConfig() { + DefaultDockerClientConfig.Builder builder = DefaultDockerClientConfig.createDefaultConfigBuilder(); + return builder.build(); + } + + private DockerHttpClient getDockerHttpClient(DockerClientConfig config) { + return new ZerodepDockerHttpClient.Builder() + .dockerHost(config.getDockerHost()) + .sslConfig(config.getSSLConfig()) + .maxConnections(100) + .build(); + } + + public DockerClient getDockerClient() { + if (dockerClient == null) { + DockerClientConfig config = getDockerClientConfig(); + DockerHttpClient httpClient = getDockerHttpClient(config); + dockerClient = DockerClientImpl.getInstance(config, httpClient); + } + return dockerClient; + } +} diff --git a/karavan-status/src/main/java/org/apache/camel/karavan/status/docker/DockerStatusService.java b/karavan-status/src/main/java/org/apache/camel/karavan/status/docker/DockerStatusService.java new file mode 100644 index 00000000..ae46dc46 --- /dev/null +++ b/karavan-status/src/main/java/org/apache/camel/karavan/status/docker/DockerStatusService.java @@ -0,0 +1,60 @@ +/* + * 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 + * + * http://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. + */ + +package org.apache.camel.karavan.status.docker; + +import io.quarkus.scheduler.Scheduled; +import io.vertx.core.eventbus.EventBus; +import io.vertx.core.json.JsonArray; +import io.vertx.core.json.JsonObject; +import jakarta.enterprise.context.ApplicationScoped; +import jakarta.inject.Inject; +import org.apache.camel.karavan.status.ConfigService; +import org.apache.camel.karavan.status.model.ContainerStatus; + +import java.util.List; + +import static org.apache.camel.karavan.status.KaravanStatusEvents.*; + +@ApplicationScoped +public class DockerStatusService { + + @Inject + DockerAPI dockerAPI; + + @Inject + EventBus eventBus; + + @Scheduled(every = "{karavan.container.statistics.interval}", concurrentExecution = Scheduled.ConcurrentExecution.SKIP) + void collectContainersStatistics() { + List<ContainerStatus> statusesInDocker = dockerAPI.collectContainersStatuses(); + statusesInDocker.forEach(containerStatus -> { + eventBus.publish(CMD_COLLECT_CONTAINER_STATISTIC, JsonObject.mapFrom(containerStatus)); + }); + } + + @Scheduled(every = "{karavan.container.status.interval}", concurrentExecution = Scheduled.ConcurrentExecution.SKIP) + void collectContainersStatuses() { + if (!ConfigService.inKubernetes()) { + List<ContainerStatus> statusesInDocker = dockerAPI.collectContainersStatuses(); + statusesInDocker.forEach(containerStatus -> { + eventBus.publish(CONTAINER_UPDATED, JsonObject.mapFrom(containerStatus)); + }); + eventBus.publish(CMD_CLEAN_STATUSES, new JsonArray(statusesInDocker)); + } + } +} \ No newline at end of file diff --git a/karavan-status/src/main/java/org/apache/camel/karavan/status/docker/DockerUtils.java b/karavan-status/src/main/java/org/apache/camel/karavan/status/docker/DockerUtils.java new file mode 100644 index 00000000..c5eba1f7 --- /dev/null +++ b/karavan-status/src/main/java/org/apache/camel/karavan/status/docker/DockerUtils.java @@ -0,0 +1,153 @@ +/* + * 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 + * + * http://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. + */ +package org.apache.camel.karavan.status.docker; + +import com.github.dockerjava.api.model.*; +import io.smallrye.mutiny.tuples.Tuple2; +import org.apache.camel.karavan.status.model.ContainerPort; +import org.apache.camel.karavan.status.model.ContainerStatus; + +import java.text.DecimalFormat; +import java.time.Instant; +import java.util.*; +import java.util.concurrent.ConcurrentHashMap; +import java.util.stream.Collectors; + +import static org.apache.camel.karavan.status.StatusConstants.*; + +public class DockerUtils { + + protected static final DecimalFormat formatCpu = new DecimalFormat("0.00"); + protected static final DecimalFormat formatMiB = new DecimalFormat("0.0"); + protected static final DecimalFormat formatGiB = new DecimalFormat("0.00"); + protected static final Map<String, Tuple2<Long, Long>> previousStats = new ConcurrentHashMap<>(); + + private static final Map<String, Long> UNIT_MULTIPLIERS = new TreeMap<>(String.CASE_INSENSITIVE_ORDER);; + static { + UNIT_MULTIPLIERS.put("b", 1L); + UNIT_MULTIPLIERS.put("k", 1024L); + UNIT_MULTIPLIERS.put("m", 1024L * 1024); + UNIT_MULTIPLIERS.put("g", 1024L * 1024 * 1024); + // Add more units if needed + } + + static ContainerStatus getContainerStatus(Container container, String environment) { + String name = container.getNames()[0].replace("/", ""); + List<ContainerPort> ports = Arrays.stream(container.getPorts()) + .map(p -> new ContainerPort(p.getPrivatePort(), p.getPublicPort(), p.getType())) + .collect(Collectors.toList()); + List<ContainerStatus.Command> commands = getContainerCommand(container.getState()); + ContainerStatus.ContainerType type = getContainerType(container.getLabels()); + String created = Instant.ofEpochSecond(container.getCreated()).toString(); + String projectId = container.getLabels().getOrDefault(LABEL_PROJECT_ID, name); + String camelRuntime = container.getLabels().getOrDefault(LABEL_CAMEL_RUNTIME, ""); + return ContainerStatus.createWithId(projectId, name, environment, container.getId(), container.getImage(), + ports, type, commands, container.getState(), created, camelRuntime); + } + + static void updateStatistics(ContainerStatus containerStatus, Statistics stats) { + if (stats != null && stats.getMemoryStats() != null) { + String memoryUsageString = formatMemory(stats.getMemoryStats().getUsage()); + String memoryLimitString = formatMemory(stats.getMemoryStats().getLimit()); + containerStatus.setMemoryInfo(memoryUsageString + " / " + memoryLimitString); + containerStatus.setCpuInfo(formatCpu(containerStatus.getContainerName(), stats)); + } else { + containerStatus.setMemoryInfo("0MiB/0MiB"); + containerStatus.setCpuInfo("0%"); + } + } + + static String formatMemory(Long memory) { + try { + if (memory < (1073741824)) { + return formatMiB.format(memory.doubleValue() / 1048576) + "MiB"; + } else { + return formatGiB.format(memory.doubleValue() / 1073741824) + "GiB"; + } + } catch (Exception e) { + return ""; + } + } + + static ContainerStatus.ContainerType getContainerType(Map<String, String> labels) { + String type = labels.get(LABEL_TYPE); + if (Objects.equals(type, ContainerStatus.ContainerType.devmode.name())) { + return ContainerStatus.ContainerType.devmode; + } else if (Objects.equals(type, ContainerStatus.ContainerType.devservice.name())) { + return ContainerStatus.ContainerType.devservice; + } else if (Objects.equals(type, ContainerStatus.ContainerType.project.name())) { + return ContainerStatus.ContainerType.project; + } else if (Objects.equals(type, ContainerStatus.ContainerType.internal.name())) { + return ContainerStatus.ContainerType.internal; + } else if (Objects.equals(type, ContainerStatus.ContainerType.build.name())) { + return ContainerStatus.ContainerType.build; + } + return ContainerStatus.ContainerType.unknown; + } + + static List<ContainerStatus.Command> getContainerCommand(String state) { + List<ContainerStatus.Command> result = new ArrayList<>(); + if (Objects.equals(state, ContainerStatus.State.created.name())) { + result.add(ContainerStatus.Command.run); + result.add(ContainerStatus.Command.delete); + } else if (Objects.equals(state, ContainerStatus.State.exited.name())) { + result.add(ContainerStatus.Command.run); + result.add(ContainerStatus.Command.delete); + } else if (Objects.equals(state, ContainerStatus.State.running.name())) { + result.add(ContainerStatus.Command.pause); + result.add(ContainerStatus.Command.stop); + result.add(ContainerStatus.Command.delete); + } else if (Objects.equals(state, ContainerStatus.State.paused.name())) { + result.add(ContainerStatus.Command.run); + result.add(ContainerStatus.Command.stop); + result.add(ContainerStatus.Command.delete); + } else if (Objects.equals(state, ContainerStatus.State.dead.name())) { + result.add(ContainerStatus.Command.delete); + } + return result; + } + + static String formatCpu(String containerName, Statistics stats) { + try { + double cpuUsage = 0; + long previousCpu = previousStats.containsKey(containerName) ? previousStats.get(containerName).getItem1() + : -1; + long previousSystem = previousStats.containsKey(containerName) ? previousStats.get(containerName).getItem2() + : -1; + + CpuStatsConfig cpuStats = stats.getCpuStats(); + if (cpuStats != null) { + CpuUsageConfig cpuUsageConfig = cpuStats.getCpuUsage(); + long systemUsage = cpuStats.getSystemCpuUsage(); + long totalUsage = cpuUsageConfig.getTotalUsage(); + + if (previousCpu != -1 && previousSystem != -1) { + float cpuDelta = totalUsage - previousCpu; + float systemDelta = systemUsage - previousSystem; + + if (cpuDelta > 0 && systemDelta > 0) { + cpuUsage = cpuDelta / systemDelta * cpuStats.getOnlineCpus() * 100; + } + } + previousStats.put(containerName, Tuple2.of(totalUsage, systemUsage)); + } + return formatCpu.format(cpuUsage) + "%"; + } catch (Exception e) { + return ""; + } + } +} diff --git a/karavan-status/src/main/java/org/apache/camel/karavan/status/kubernetes/DeploymentEventHandler.java b/karavan-status/src/main/java/org/apache/camel/karavan/status/kubernetes/DeploymentEventHandler.java new file mode 100644 index 00000000..b28dfbb7 --- /dev/null +++ b/karavan-status/src/main/java/org/apache/camel/karavan/status/kubernetes/DeploymentEventHandler.java @@ -0,0 +1,103 @@ +/* + * 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 + * + * http://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. + */ + +package org.apache.camel.karavan.status.kubernetes; + +import io.fabric8.kubernetes.api.model.apps.Deployment; +import io.fabric8.kubernetes.client.informers.ResourceEventHandler; +import io.vertx.mutiny.core.eventbus.EventBus; +import io.vertx.core.json.JsonObject; +import org.apache.camel.karavan.status.model.DeploymentStatus; +import org.jboss.logging.Logger; + +import static org.apache.camel.karavan.status.KaravanStatusEvents.*; + +public class DeploymentEventHandler implements ResourceEventHandler<Deployment> { + + private static final Logger LOGGER = Logger.getLogger(DeploymentEventHandler.class.getName()); + private final KubernetesStatusService kubernetesStatusService; + private final EventBus eventBus; + + public DeploymentEventHandler(KubernetesStatusService kubernetesStatusService, EventBus eventBus) { + this.kubernetesStatusService = kubernetesStatusService; + this.eventBus = eventBus; + } + + @Override + public void onAdd(Deployment deployment) { + try { + LOGGER.info("onAdd " + deployment.getMetadata().getName()); + DeploymentStatus ds = getDeploymentStatus(deployment); + eventBus.publish(DEPLOYMENT_UPDATED, JsonObject.mapFrom(ds)); + } catch (Exception e){ + LOGGER.error(e.getMessage()); + } + } + + @Override + public void onUpdate(Deployment oldDeployment, Deployment newDeployment) { + try { + LOGGER.info("onUpdate " + newDeployment.getMetadata().getName()); + DeploymentStatus ds = getDeploymentStatus(newDeployment); + eventBus.publish(DEPLOYMENT_UPDATED, JsonObject.mapFrom(ds)); + } catch (Exception e){ + LOGGER.error(e.getMessage()); + } + } + + @Override + public void onDelete(Deployment deployment, boolean deletedFinalStateUnknown) { + try { + LOGGER.info("onDelete " + deployment.getMetadata().getName()); + DeploymentStatus ds = new DeploymentStatus( + deployment.getMetadata().getName(), + deployment.getMetadata().getNamespace(), + kubernetesStatusService.getCluster(), + kubernetesStatusService.environment); + eventBus.publish(DEPLOYMENT_DELETED, JsonObject.mapFrom(ds)); + } catch (Exception e){ + LOGGER.error(e.getMessage()); + } + } + + public DeploymentStatus getDeploymentStatus(Deployment deployment) { + try { + String dsImage = deployment.getSpec().getTemplate().getSpec().getContainers().get(0).getImage(); + String imageName = dsImage.startsWith("image-registry.openshift-image-registry.svc") + ? dsImage.replace("image-registry.openshift-image-registry.svc:5000/", "") + : dsImage; + + return new DeploymentStatus( + deployment.getMetadata().getName(), + deployment.getMetadata().getNamespace(), + kubernetesStatusService.getCluster(), + kubernetesStatusService.environment, + imageName, + deployment.getSpec().getReplicas(), + deployment.getStatus().getReadyReplicas(), + deployment.getStatus().getUnavailableReplicas() + ); + } catch (Exception ex) { + LOGGER.error(ex.getMessage()); + return new DeploymentStatus( + deployment.getMetadata().getName(), + deployment.getMetadata().getNamespace(), + kubernetesStatusService.getCluster(), + kubernetesStatusService.environment); + } + } +} \ No newline at end of file diff --git a/karavan-status/src/main/java/org/apache/camel/karavan/status/kubernetes/DeploymentStatusListener.java b/karavan-status/src/main/java/org/apache/camel/karavan/status/kubernetes/DeploymentStatusListener.java new file mode 100644 index 00000000..d8f0ae7d --- /dev/null +++ b/karavan-status/src/main/java/org/apache/camel/karavan/status/kubernetes/DeploymentStatusListener.java @@ -0,0 +1,47 @@ +/* + * 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 + * + * http://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. + */ + +package org.apache.camel.karavan.status.kubernetes; + +import io.quarkus.vertx.ConsumeEvent; +import io.vertx.core.json.JsonObject; +import jakarta.enterprise.context.ApplicationScoped; +import jakarta.inject.Inject; +import org.apache.camel.karavan.status.KaravanStatusCache; +import org.apache.camel.karavan.status.model.DeploymentStatus; + +import static org.apache.camel.karavan.status.KaravanStatusEvents.*; + +@ApplicationScoped +public class DeploymentStatusListener { + + @Inject + KaravanStatusCache karavanStatusCache; + + @ConsumeEvent(value = DEPLOYMENT_DELETED, blocking = true, ordered = true) + public void cleanDeploymentStatus(JsonObject data) { + DeploymentStatus ds = data.mapTo(DeploymentStatus.class); + karavanStatusCache.deleteDeploymentStatus(ds); + karavanStatusCache.deleteCamelStatuses(ds.getProjectId(), ds.getEnv()); + } + + @ConsumeEvent(value = DEPLOYMENT_UPDATED, blocking = true, ordered = true) + public void saveDeploymentStatus(JsonObject data) { + DeploymentStatus ds = data.mapTo(DeploymentStatus.class); + karavanStatusCache.saveDeploymentStatus(ds); + } +} \ No newline at end of file diff --git a/karavan-status/src/main/java/org/apache/camel/karavan/status/kubernetes/KubernetesStatusService.java b/karavan-status/src/main/java/org/apache/camel/karavan/status/kubernetes/KubernetesStatusService.java new file mode 100644 index 00000000..0b653bfa --- /dev/null +++ b/karavan-status/src/main/java/org/apache/camel/karavan/status/kubernetes/KubernetesStatusService.java @@ -0,0 +1,150 @@ +/* + * 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 + * + * http://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. + */ +package org.apache.camel.karavan.status.kubernetes; + +import io.fabric8.kubernetes.api.model.*; +import io.fabric8.kubernetes.api.model.apps.Deployment; +import io.fabric8.kubernetes.client.KubernetesClient; +import io.fabric8.kubernetes.client.KubernetesClientBuilder; +import io.fabric8.kubernetes.client.informers.SharedIndexInformer; +import io.quarkus.runtime.configuration.ProfileManager; +import io.vertx.mutiny.core.eventbus.EventBus; +import jakarta.enterprise.context.ApplicationScoped; +import jakarta.enterprise.inject.Default; +import jakarta.enterprise.inject.Produces; +import jakarta.inject.Inject; +import org.apache.camel.karavan.status.ConfigService; +import org.eclipse.microprofile.config.inject.ConfigProperty; +import org.eclipse.microprofile.health.HealthCheck; +import org.eclipse.microprofile.health.HealthCheckResponse; +import org.eclipse.microprofile.health.Readiness; +import org.jboss.logging.Logger; + +import java.util.*; + +import static org.apache.camel.karavan.status.StatusConstants.*; + +@Default +@Readiness +@ApplicationScoped +public class KubernetesStatusService implements HealthCheck { + + private static final Logger LOGGER = Logger.getLogger(KubernetesStatusService.class.getName()); + protected static final int INFORMERS = 3; + + @Inject + EventBus eventBus; + + private String namespace; + + @Produces + public KubernetesClient kubernetesClient() { + return new KubernetesClientBuilder().build(); + } + + @ConfigProperty(name = "karavan.environment") + public String environment; + + @ConfigProperty(name = "karavan.openshift") + Optional<Boolean> isOpenShift; + + List<SharedIndexInformer> informers = new ArrayList<>(INFORMERS); + + public void startInformers() { + try { + stopInformers(); + LOGGER.info("Starting Kubernetes Informers"); + + Map<String, String> labels = getRuntimeLabels(); + KubernetesClient client = kubernetesClient(); + + SharedIndexInformer<Deployment> deploymentInformer = client.apps().deployments().inNamespace(getNamespace()) + .withLabels(labels).inform(); + deploymentInformer.addEventHandlerWithResyncPeriod(new DeploymentEventHandler(this, eventBus), 30 * 1000L); + informers.add(deploymentInformer); + + SharedIndexInformer<Service> serviceInformer = client.services().inNamespace(getNamespace()) + .withLabels(labels).inform(); + serviceInformer.addEventHandlerWithResyncPeriod(new ServiceEventHandler(this, eventBus), 30 * 1000L); + informers.add(serviceInformer); + + SharedIndexInformer<Pod> podRunInformer = client.pods().inNamespace(getNamespace()) + .withLabels(labels).inform(); + podRunInformer.addEventHandlerWithResyncPeriod(new PodEventHandler( this, eventBus), 30 * 1000L); + informers.add(podRunInformer); + + LOGGER.info("Started Kubernetes Informers"); + } catch (Exception e) { + LOGGER.error("Error starting informers: " + e.getMessage()); + } + } + + + @Override + public HealthCheckResponse call() { + if (ConfigService.inKubernetes()) { + if (informers.size() == INFORMERS) { + return HealthCheckResponse.named("Kubernetes").up().build(); + } else { + return HealthCheckResponse.named("Kubernetes").down().build(); + } + } else { + return HealthCheckResponse.named("Kubernetesless").up().build(); + } + } + + public void stopInformers() { + LOGGER.info("Stop Kubernetes Informers"); + informers.forEach(SharedIndexInformer::close); + informers.clear(); + } + + + public String getNamespace() { + if (namespace == null) { + try (KubernetesClient client = kubernetesClient()) { + namespace = ProfileManager.getLaunchMode().isDevOrTest() ? "karavan" : client.getNamespace(); + } + } + return namespace; + } + + private Map<String, String> getRuntimeLabels() { + Map<String, String> labels = new HashMap<>(); + labels.put(isOpenshift() ? "app.openshift.io/runtime" : "app.kubernetes.io/runtime", CAMEL_PREFIX); + return labels; + } + + public boolean isOpenshift() { + return isOpenShift.isPresent() && isOpenShift.get(); + } + + public String getCluster() { + try (KubernetesClient client = kubernetesClient()) { + return client.getMasterUrl().getHost(); + } + } + + public ResourceRequirements getResourceRequirements(Map<String, String> containerResources) { + return new ResourceRequirementsBuilder() + .addToRequests("cpu", new Quantity(containerResources.get("requests.cpu"))) + .addToRequests("memory", new Quantity(containerResources.get("requests.memory"))) + .addToLimits("cpu", new Quantity(containerResources.get("limits.cpu"))) + .addToLimits("memory", new Quantity(containerResources.get("limits.memory"))) + .build(); + } +} diff --git a/karavan-status/src/main/java/org/apache/camel/karavan/status/kubernetes/PodEventHandler.java b/karavan-status/src/main/java/org/apache/camel/karavan/status/kubernetes/PodEventHandler.java new file mode 100644 index 00000000..448c9da1 --- /dev/null +++ b/karavan-status/src/main/java/org/apache/camel/karavan/status/kubernetes/PodEventHandler.java @@ -0,0 +1,160 @@ +/* + * 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 + * + * http://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. + */ + +package org.apache.camel.karavan.status.kubernetes; + +import io.fabric8.kubernetes.api.model.ContainerBuilder; +import io.fabric8.kubernetes.api.model.Pod; +import io.fabric8.kubernetes.api.model.Quantity; +import io.fabric8.kubernetes.api.model.ResourceRequirements; +import io.fabric8.kubernetes.client.informers.ResourceEventHandler; +import io.vertx.core.json.JsonObject; +import io.vertx.mutiny.core.eventbus.EventBus; +import org.apache.camel.karavan.status.model.ContainerStatus; +import org.jboss.logging.Logger; + +import java.util.List; +import java.util.Map; +import java.util.Objects; + +import static org.apache.camel.karavan.status.StatusConstants.*; +import static org.apache.camel.karavan.status.KaravanStatusEvents.CONTAINER_DELETED; +import static org.apache.camel.karavan.status.KaravanStatusEvents.CONTAINER_UPDATED; + +public class PodEventHandler implements ResourceEventHandler<Pod> { + + private static final Logger LOGGER = Logger.getLogger(PodEventHandler.class.getName()); + + private final KubernetesStatusService kubernetesStatusService; + private final EventBus eventBus; + + public static final Map<String, String> DEFAULT_CONTAINER_RESOURCES = Map.of( + "requests.memory", "256Mi", + "requests.cpu", "500m", + "limits.memory", "2048Mi", + "limits.cpu", "2000m" + ); + + public PodEventHandler(KubernetesStatusService kubernetesStatusService, EventBus eventBus) { + this.kubernetesStatusService = kubernetesStatusService; + this.eventBus = eventBus; + } + + @Override + public void onAdd(Pod pod) { + try { + LOGGER.info("onAdd " + pod.getMetadata().getName()); + ContainerStatus ps = getPodStatus(pod); + if (ps != null) { + eventBus.publish(CONTAINER_UPDATED, JsonObject.mapFrom(ps)); + } + } catch (Exception e) { + LOGGER.error(e.getMessage(), e.getCause()); + } + } + + @Override + public void onUpdate(Pod oldPod, Pod newPod) { + try { + LOGGER.info("onUpdate " + newPod.getMetadata().getName()); + if (!newPod.isMarkedForDeletion() && newPod.getMetadata().getDeletionTimestamp() == null) { + ContainerStatus ps = getPodStatus(newPod); + if (ps != null) { + eventBus.publish(CONTAINER_UPDATED, JsonObject.mapFrom(ps)); + } + } + } catch (Exception e) { + LOGGER.error(e.getMessage(), e.getCause()); + } + } + + @Override + public void onDelete(Pod pod, boolean deletedFinalStateUnknown) { + try { + LOGGER.info("onDelete " + pod.getMetadata().getName()); + String deployment = pod.getMetadata().getLabels().get("app"); + String projectId = deployment != null ? deployment : pod.getMetadata().getLabels().get(LABEL_PROJECT_ID); + + ContainerStatus cs = new ContainerStatus(); + cs.setProjectId(projectId); + cs.setContainerName(projectId); + cs.setEnv(kubernetesStatusService.environment); + + eventBus.publish(CONTAINER_DELETED, JsonObject.mapFrom(cs)); + } catch (Exception e) { + LOGGER.error(e.getMessage(), e.getCause()); + } + } + + + public ContainerStatus getPodStatus(Pod pod) { + String deployment = pod.getMetadata().getLabels().get("app"); + String projectId = deployment != null ? deployment : pod.getMetadata().getLabels().get(LABEL_PROJECT_ID); + String camel = deployment != null ? deployment : pod.getMetadata().getLabels().get(LABEL_KUBERNETES_RUNTIME); + String runtime = deployment != null ? deployment : pod.getMetadata().getLabels().get(LABEL_CAMEL_RUNTIME); + String type = pod.getMetadata().getLabels().get(LABEL_TYPE); + String commit = pod.getMetadata().getAnnotations().get(ANNOTATION_COMMIT); + ContainerStatus.ContainerType containerType = deployment != null + ? ContainerStatus.ContainerType.project + : (type != null ? ContainerStatus.ContainerType.valueOf(type) : ContainerStatus.ContainerType.unknown); + try { + boolean ready = pod.getStatus().getConditions().stream().anyMatch(c -> c.getType().equals("Ready") && c.getStatus().equals("True")); + boolean running = Objects.equals(pod.getStatus().getPhase(), "Running"); + boolean failed = Objects.equals(pod.getStatus().getPhase(), "Failed"); + boolean succeeded = Objects.equals(pod.getStatus().getPhase(), "Succeeded"); + String creationTimestamp = pod.getMetadata().getCreationTimestamp(); + + ResourceRequirements defaultRR = kubernetesStatusService.getResourceRequirements(DEFAULT_CONTAINER_RESOURCES); + ResourceRequirements resourceRequirements = pod.getSpec().getContainers().stream().findFirst() + .orElse(new ContainerBuilder().withResources(defaultRR).build()).getResources(); + + String requestMemory = resourceRequirements.getRequests().getOrDefault("memory", new Quantity()).toString(); + String requestCpu = resourceRequirements.getRequests().getOrDefault("cpu", new Quantity()).toString(); + String limitMemory = resourceRequirements.getLimits().getOrDefault("memory", new Quantity()).toString(); + String limitCpu = resourceRequirements.getLimits().getOrDefault("cpu", new Quantity()).toString(); + ContainerStatus status = new ContainerStatus( + pod.getMetadata().getName(), + List.of(ContainerStatus.Command.delete), + projectId, + kubernetesStatusService.environment, + containerType, + requestMemory + " / " + limitMemory, + requestCpu + " / " + limitCpu, + creationTimestamp); + status.setImage(pod.getSpec().getContainers().get(0).getImage()); + status.setCommit(commit); + status.setContainerId(pod.getMetadata().getName()); + status.setPhase(pod.getStatus().getPhase()); + status.setPodIP(pod.getStatus().getPodIP()); + status.setCamelRuntime(runtime != null ? runtime : (camel != null ? CamelRuntime.CAMEL_MAIN.getValue() : "")); + if (running) { + status.setState(ContainerStatus.State.running.name()); + } else if (failed) { + status.setState(ContainerStatus.State.dead.name()); + } else if (succeeded) { + status.setState(ContainerStatus.State.exited.name()); + } else { + status.setState(ContainerStatus.State.created.name()); + } + return status; + } catch (Exception ex) { + ex.printStackTrace(); + LOGGER.error(ex.getMessage(), ex.getCause()); + return null; + } + } +} \ No newline at end of file diff --git a/karavan-status/src/main/java/org/apache/camel/karavan/status/kubernetes/ServiceEventHandler.java b/karavan-status/src/main/java/org/apache/camel/karavan/status/kubernetes/ServiceEventHandler.java new file mode 100644 index 00000000..cf700cbe --- /dev/null +++ b/karavan-status/src/main/java/org/apache/camel/karavan/status/kubernetes/ServiceEventHandler.java @@ -0,0 +1,98 @@ +/* + * 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 + * + * http://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. + */ + +package org.apache.camel.karavan.status.kubernetes; + +import io.fabric8.kubernetes.api.model.Service; +import io.fabric8.kubernetes.client.informers.ResourceEventHandler; +import io.vertx.mutiny.core.eventbus.EventBus; +import io.vertx.core.json.JsonObject; +import org.apache.camel.karavan.status.model.ServiceStatus; +import org.jboss.logging.Logger; + +import static org.apache.camel.karavan.status.KaravanStatusEvents.*; + +public class ServiceEventHandler implements ResourceEventHandler<Service> { + + private static final Logger LOGGER = Logger.getLogger(ServiceEventHandler.class.getName()); + private KubernetesStatusService kubernetesStatusService; + private final EventBus eventBus; + + public ServiceEventHandler(KubernetesStatusService kubernetesStatusService, EventBus eventBus) { + this.kubernetesStatusService = kubernetesStatusService; + this.eventBus = eventBus; + } + + @Override + public void onAdd(Service service) { + try { + LOGGER.info("onAdd " + service.getMetadata().getName()); + ServiceStatus ds = getServiceStatus(service); + eventBus.publish(SERVICE_UPDATED, JsonObject.mapFrom(ds)); + } catch (Exception e){ + LOGGER.error(e.getMessage()); + } + } + + @Override + public void onUpdate(Service oldService, Service newService) { + try { + LOGGER.info("onUpdate " + newService.getMetadata().getName()); + ServiceStatus ds = getServiceStatus(newService); + eventBus.publish(SERVICE_UPDATED, JsonObject.mapFrom(ds)); + } catch (Exception e){ + LOGGER.error(e.getMessage()); + } + } + + @Override + public void onDelete(Service service, boolean deletedFinalStateUnknown) { + try { + LOGGER.info("onDelete " + service.getMetadata().getName()); + ServiceStatus ds = new ServiceStatus( + service.getMetadata().getName(), + service.getMetadata().getNamespace(), + kubernetesStatusService.getCluster(), + kubernetesStatusService.environment); + eventBus.publish(SERVICE_DELETED, JsonObject.mapFrom(ds)); + } catch (Exception e){ + LOGGER.error(e.getMessage()); + } + } + + public ServiceStatus getServiceStatus(Service service) { + try { + return new ServiceStatus( + service.getMetadata().getName(), + service.getMetadata().getNamespace(), + kubernetesStatusService.environment, + kubernetesStatusService.getCluster(), + service.getSpec().getPorts().get(0).getPort(), + service.getSpec().getPorts().get(0).getTargetPort().getIntVal(), + service.getSpec().getClusterIP(), + service.getSpec().getType() + ); + } catch (Exception ex) { + LOGGER.error(ex.getMessage()); + return new ServiceStatus( + service.getMetadata().getName(), + service.getMetadata().getNamespace(), + kubernetesStatusService.getCluster(), + kubernetesStatusService.environment); + } + } +} \ No newline at end of file diff --git a/karavan-status/src/main/java/org/apache/camel/karavan/status/kubernetes/ServiceStatusListener.java b/karavan-status/src/main/java/org/apache/camel/karavan/status/kubernetes/ServiceStatusListener.java new file mode 100644 index 00000000..9a827918 --- /dev/null +++ b/karavan-status/src/main/java/org/apache/camel/karavan/status/kubernetes/ServiceStatusListener.java @@ -0,0 +1,46 @@ +/* + * 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 + * + * http://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. + */ + +package org.apache.camel.karavan.status.kubernetes; + +import io.quarkus.vertx.ConsumeEvent; +import io.vertx.core.json.JsonObject; +import jakarta.enterprise.context.ApplicationScoped; +import jakarta.inject.Inject; +import org.apache.camel.karavan.status.KaravanStatusCache; +import org.apache.camel.karavan.status.model.ServiceStatus; + +import static org.apache.camel.karavan.status.KaravanStatusEvents.*; + +@ApplicationScoped +public class ServiceStatusListener { + + @Inject + KaravanStatusCache karavanStatusCache; + + @ConsumeEvent(value = SERVICE_DELETED, blocking = true, ordered = true) + public void cleanServiceStatus(JsonObject data) { + ServiceStatus ds = data.mapTo(ServiceStatus.class); + karavanStatusCache.deleteServiceStatus(ds); + } + + @ConsumeEvent(value = SERVICE_UPDATED, blocking = true, ordered = true) + public void saveServiceStatus(JsonObject data) { + ServiceStatus ds = data.mapTo(ServiceStatus.class); + karavanStatusCache.saveServiceStatus(ds); + } +} \ No newline at end of file diff --git a/karavan-status/src/main/java/org/apache/camel/karavan/status/model/CamelStatus.java b/karavan-status/src/main/java/org/apache/camel/karavan/status/model/CamelStatus.java new file mode 100644 index 00000000..c299952f --- /dev/null +++ b/karavan-status/src/main/java/org/apache/camel/karavan/status/model/CamelStatus.java @@ -0,0 +1,67 @@ +/* + * 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 + * + * http://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. + */ + +package org.apache.camel.karavan.status.model; + +import java.util.List; + +public class CamelStatus { + + String projectId; + String containerName; + List<CamelStatusValue> statuses; + String env; + + public CamelStatus(String projectId, String containerName, List<CamelStatusValue> statuses, String env) { + this.projectId = projectId; + this.containerName = containerName; + this.statuses = statuses; + this.env = env; + } + + public String getProjectId() { + return projectId; + } + + public void setProjectId(String projectId) { + this.projectId = projectId; + } + + public String getContainerName() { + return containerName; + } + + public void setContainerName(String containerName) { + this.containerName = containerName; + } + + public List<CamelStatusValue> getStatuses() { + return statuses; + } + + public void setStatuses(List<CamelStatusValue> statuses) { + this.statuses = statuses; + } + + public String getEnv() { + return env; + } + + public void setEnv(String env) { + this.env = env; + } +} diff --git a/karavan-status/src/main/java/org/apache/camel/karavan/status/model/CamelStatusRequest.java b/karavan-status/src/main/java/org/apache/camel/karavan/status/model/CamelStatusRequest.java new file mode 100644 index 00000000..9248a616 --- /dev/null +++ b/karavan-status/src/main/java/org/apache/camel/karavan/status/model/CamelStatusRequest.java @@ -0,0 +1,47 @@ +/* + * 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 + * + * http://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. + */ +package org.apache.camel.karavan.status.model; + +public class CamelStatusRequest { + + private String projectId; + private String containerName; + + public CamelStatusRequest() { + } + + public CamelStatusRequest(String projectId, String containerName) { + this.projectId = projectId; + this.containerName = containerName; + } + + public String getProjectId() { + return projectId; + } + + public void setProjectId(String projectId) { + this.projectId = projectId; + } + + public String getContainerName() { + return containerName; + } + + public void setContainerName(String containerName) { + this.containerName = containerName; + } +} diff --git a/karavan-status/src/main/java/org/apache/camel/karavan/status/model/CamelStatusValue.java b/karavan-status/src/main/java/org/apache/camel/karavan/status/model/CamelStatusValue.java new file mode 100644 index 00000000..a11f95fa --- /dev/null +++ b/karavan-status/src/main/java/org/apache/camel/karavan/status/model/CamelStatusValue.java @@ -0,0 +1,57 @@ +/* + * 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 + * + * http://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. + */ + +package org.apache.camel.karavan.status.model; + +public class CamelStatusValue { + + public enum Name { + + context, + inflight, + memory, + properties, + route, + trace, + jvm, + source + } + + Name name; + String status; + + public CamelStatusValue(Name name, String status) { + this.name = name; + this.status = status; + } + + public Name getName() { + return name; + } + + public void setName(Name name) { + this.name = name; + } + + public String getStatus() { + return status; + } + + public void setStatus(String status) { + this.status = status; + } +} diff --git a/karavan-status/src/main/java/org/apache/camel/karavan/status/model/Configuration.java b/karavan-status/src/main/java/org/apache/camel/karavan/status/model/Configuration.java new file mode 100644 index 00000000..421df7d6 --- /dev/null +++ b/karavan-status/src/main/java/org/apache/camel/karavan/status/model/Configuration.java @@ -0,0 +1,87 @@ +/* + * 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 + * + * http://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. + */ +package org.apache.camel.karavan.status.model; + +import java.util.List; + +public class Configuration { + private String title; + private String version; + private String infrastructure; + private String environment; + private List<String> environments; + private List<Object> status; + + public Configuration() { + } + + public Configuration(String title, String version, String infrastructure, String environment, List<String> environments) { + this.title = title; + this.version = version; + this.infrastructure = infrastructure; + this.environment = environment; + this.environments = environments; + } + + public String getTitle() { + return title; + } + + public void setTitle(String title) { + this.title = title; + } + + public String getVersion() { + return version; + } + + public void setVersion(String version) { + this.version = version; + } + + public String getInfrastructure() { + return infrastructure; + } + + public void setInfrastructure(String infrastructure) { + this.infrastructure = infrastructure; + } + + public String getEnvironment() { + return environment; + } + + public void setEnvironment(String environment) { + this.environment = environment; + } + + public List<String> getEnvironments() { + return environments; + } + + public void setEnvironments(List<String> environments) { + this.environments = environments; + } + + public List<Object> getStatus() { + return status; + } + + public void setStatus(List<Object> status) { + this.status = status; + } +} diff --git a/karavan-status/src/main/java/org/apache/camel/karavan/status/model/ContainerPort.java b/karavan-status/src/main/java/org/apache/camel/karavan/status/model/ContainerPort.java new file mode 100644 index 00000000..cfda5992 --- /dev/null +++ b/karavan-status/src/main/java/org/apache/camel/karavan/status/model/ContainerPort.java @@ -0,0 +1,55 @@ +/* + * 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 + * + * http://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. + */ + +package org.apache.camel.karavan.status.model; + +public class ContainerPort { + + Integer privatePort; + Integer publicPort; + String type; + + public ContainerPort(Integer privatePort, Integer publicPort, String type) { + this.privatePort = privatePort; + this.publicPort = publicPort; + this.type = type; + } + + public Integer getPrivatePort() { + return privatePort; + } + + public void setPrivatePort(Integer privatePort) { + this.privatePort = privatePort; + } + + public Integer getPublicPort() { + return publicPort; + } + + public void setPublicPort(Integer publicPort) { + this.publicPort = publicPort; + } + + public String getType() { + return type; + } + + public void setType(String type) { + this.type = type; + } +} \ No newline at end of file diff --git a/karavan-status/src/main/java/org/apache/camel/karavan/status/model/ContainerStatus.java b/karavan-status/src/main/java/org/apache/camel/karavan/status/model/ContainerStatus.java new file mode 100644 index 00000000..3877b453 --- /dev/null +++ b/karavan-status/src/main/java/org/apache/camel/karavan/status/model/ContainerStatus.java @@ -0,0 +1,356 @@ +/* + * 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 + * + * http://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. + */ + +package org.apache.camel.karavan.status.model; + +import java.time.Instant; +import java.util.List; + +public class ContainerStatus { + + + public enum State { + created, + running, + restarting, + paused, + exited, + dead + } + + public enum ContainerType { + internal, + devmode, + devservice, + project, + build, + unknown, + } + + public enum Command { + run, + pause, + stop, + delete, + } + + String projectId; + String containerName; + String containerId; + String image; + List<ContainerPort> ports; + String env; + ContainerType type; + String memoryInfo; + String cpuInfo; + String created; + String finished; + List<Command> commands; + String state; + String phase; + Boolean codeLoaded; + Boolean inTransit = false; + String initDate; + String podIP; + String camelRuntime; + String commit; + + public ContainerStatus(String projectId, String containerName, String containerId, String image, List<ContainerPort> ports, String env, ContainerType type, String memoryInfo, String cpuInfo, String created, String finished, List<Command> commands, String state, String phase, Boolean codeLoaded, Boolean inTransit, String initDate, String podIP, String camelRuntime) { + this.projectId = projectId; + this.containerName = containerName; + this.containerId = containerId; + this.image = image; + this.ports = ports; + this.env = env; + this.type = type; + this.memoryInfo = memoryInfo; + this.cpuInfo = cpuInfo; + this.created = created; + this.finished = finished; + this.commands = commands; + this.state = state; + this.phase = phase; + this.codeLoaded = codeLoaded; + this.inTransit = inTransit; + this.initDate = initDate; + this.podIP = podIP; + this.camelRuntime = camelRuntime; + } + + public ContainerStatus(String projectId, String containerName, String containerId, String image, List<ContainerPort> ports, String env, ContainerType type, String memoryInfo, String cpuInfo, String created, String finished, List<Command> commands, String state, String phase, Boolean codeLoaded, Boolean inTransit, String initDate) { + this.projectId = projectId; + this.containerName = containerName; + this.containerId = containerId; + this.image = image; + this.ports = ports; + this.env = env; + this.type = type; + this.memoryInfo = memoryInfo; + this.cpuInfo = cpuInfo; + this.created = created; + this.finished = finished; + this.commands = commands; + this.state = state; + this.phase = phase; + this.codeLoaded = codeLoaded; + this.inTransit = inTransit; + this.initDate = initDate; + } + + public ContainerStatus(String projectId, String containerName, String containerId, String image, List<ContainerPort> ports, String env, ContainerType type, String memoryInfo, String cpuInfo, String created, String finished, List<Command> commands, String state, Boolean codeLoaded, Boolean inTransit, String camelRuntime) { + this.projectId = projectId; + this.containerName = containerName; + this.containerId = containerId; + this.image = image; + this.ports = ports; + this.env = env; + this.type = type; + this.memoryInfo = memoryInfo; + this.cpuInfo = cpuInfo; + this.created = created; + this.finished = finished; + this.commands = commands; + this.state = state; + this.codeLoaded = codeLoaded; + this.camelRuntime = camelRuntime; + this.inTransit = inTransit; + this.initDate = Instant.now().toString(); + } + + public ContainerStatus(String containerName, List<Command> commands, String projectId, String env, ContainerType type, String memoryInfo, String cpuInfo, String created) { + this.containerName = containerName; + this.commands = commands; + this.projectId = projectId; + this.env = env; + this.type = type; + this.memoryInfo = memoryInfo; + this.cpuInfo = cpuInfo; + this.created = created; + this.initDate = Instant.now().toString(); + } + + public ContainerStatus(String containerName, List<Command> commands, String projectId, String env, ContainerType type, String created) { + this.containerName = containerName; + this.commands = commands; + this.projectId = projectId; + this.env = env; + this.created = created; + this.type = type; + this.initDate = Instant.now().toString(); + } + + public static ContainerStatus createDevMode(String projectId, String env) { + return new ContainerStatus(projectId, projectId, null, null, null, env, ContainerType.devmode, null, null, null, null, List.of(Command.run), null, false, false, ""); + } + + public static ContainerStatus createByType(String name, String env, ContainerType type) { + return new ContainerStatus(name, name, null, null, null, env, type, null, null, null, null, List.of(Command.run), null, false, false, ""); + } + + public static ContainerStatus createWithId(String projectId, String containerName, String env, String containerId, String image, List<ContainerPort> ports, ContainerType type, List<Command> commands, String status, String created, String camelRuntime) { + return new ContainerStatus(projectId, containerName, containerId, image, ports, env, type, + null, null, created, null, commands, status, false, false, camelRuntime); + } + + public ContainerStatus() { + } + + public String getPodIP() { + return podIP; + } + + public void setPodIP(String podIP) { + this.podIP = podIP; + } + + public String getProjectId() { + return projectId; + } + + public void setProjectId(String projectId) { + this.projectId = projectId; + } + + public String getContainerName() { + return containerName; + } + + public void setContainerName(String containerName) { + this.containerName = containerName; + } + + public String getContainerId() { + return containerId; + } + + public void setContainerId(String containerId) { + this.containerId = containerId; + } + + public String getImage() { + return image; + } + + public void setImage(String image) { + this.image = image; + } + + public List<ContainerPort> getPorts() { + return ports; + } + + public void setPorts(List<ContainerPort> ports) { + this.ports = ports; + } + + public String getEnv() { + return env; + } + + public void setEnv(String env) { + this.env = env; + } + + public ContainerType getType() { + return type; + } + + public void setType(ContainerType type) { + this.type = type; + } + + public String getMemoryInfo() { + return memoryInfo; + } + + public void setMemoryInfo(String memoryInfo) { + this.memoryInfo = memoryInfo; + } + + public String getCpuInfo() { + return cpuInfo; + } + + public void setCpuInfo(String cpuInfo) { + this.cpuInfo = cpuInfo; + } + + public String getCreated() { + return created; + } + + public void setCreated(String created) { + this.created = created; + } + + public List<Command> getCommands() { + return commands; + } + + public void setCommands(List<Command> commands) { + this.commands = commands; + } + + public String getState() { + return state; + } + + public void setState(String state) { + this.state = state; + } + + public Boolean getCodeLoaded() { + return codeLoaded; + } + + public void setCodeLoaded(Boolean codeLoaded) { + this.codeLoaded = codeLoaded; + } + + public Boolean getInTransit() { + return inTransit; + } + + public void setInTransit(Boolean inTransit) { + this.inTransit = inTransit; + } + + public String getFinished() { + return finished; + } + + public void setFinished(String finished) { + this.finished = finished; + } + + public String getInitDate() { + return initDate; + } + + public void setInitDate(String initDate) { + this.initDate = initDate; + } + + public String getPhase() { + return phase; + } + + public void setPhase(String phase) { + this.phase = phase; + } + + public String getCamelRuntime() { + return camelRuntime; + } + + public void setCamelRuntime(String camelRuntime) { + this.camelRuntime = camelRuntime; + } + + public String getCommit() { + return commit; + } + + public void setCommit(String commit) { + this.commit = commit; + } + + @Override + public String toString() { + return "ContainerStatus{" + + "projectId='" + projectId + '\'' + + ", containerName='" + containerName + '\'' + + ", containerId='" + containerId + '\'' + + ", image='" + image + '\'' + + ", ports=" + ports + + ", env='" + env + '\'' + + ", type=" + type + + ", memoryInfo='" + memoryInfo + '\'' + + ", cpuInfo='" + cpuInfo + '\'' + + ", created='" + created + '\'' + + ", finished='" + finished + '\'' + + ", commands=" + commands + + ", state='" + state + '\'' + + ", phase='" + phase + '\'' + + ", codeLoaded=" + codeLoaded + + ", inTransit=" + inTransit + + ", initDate='" + initDate + '\'' + + ", podIP='" + podIP + '\'' + + ", commit='" + commit + '\'' + + '}'; + } +} diff --git a/karavan-status/src/main/java/org/apache/camel/karavan/status/model/DeploymentStatus.java b/karavan-status/src/main/java/org/apache/camel/karavan/status/model/DeploymentStatus.java new file mode 100644 index 00000000..c25b9e08 --- /dev/null +++ b/karavan-status/src/main/java/org/apache/camel/karavan/status/model/DeploymentStatus.java @@ -0,0 +1,116 @@ +/* + * 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 + * + * http://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. + */ + +package org.apache.camel.karavan.status.model; + +public class DeploymentStatus { + + String projectId; + String namespace; + String env; + String cluster; + String image; + Integer replicas; + Integer readyReplicas; + Integer unavailableReplicas; + + public DeploymentStatus(String projectId, String namespace, String cluster, String env) { + this.projectId = projectId; + this.namespace = namespace; + this.cluster = cluster; + this.env = env; + this.image = ""; + this.replicas = 0; + this.readyReplicas = 0; + this.unavailableReplicas = 0; + } + + public DeploymentStatus(String projectId, String namespace, String cluster, String env, String image, Integer replicas, Integer readyReplicas, Integer unavailableReplicas) { + this.projectId = projectId; + this.namespace = namespace; + this.env = env; + this.cluster = cluster; + this.image = image; + this.replicas = replicas; + this.readyReplicas = readyReplicas; + this.unavailableReplicas = unavailableReplicas; + } + + public String getProjectId() { + return projectId; + } + + public void setProjectId(String projectId) { + this.projectId = projectId; + } + + public String getEnv() { + return env; + } + + public void setEnv(String env) { + this.env = env; + } + + public String getNamespace() { + return namespace; + } + + public void setNamespace(String namespace) { + this.namespace = namespace; + } + + public String getImage() { + return image; + } + + public void setImage(String image) { + this.image = image; + } + + public Integer getReplicas() { + return replicas; + } + + public void setReplicas(Integer replicas) { + this.replicas = replicas; + } + + public Integer getReadyReplicas() { + return readyReplicas; + } + + public void setReadyReplicas(Integer readyReplicas) { + this.readyReplicas = readyReplicas; + } + + public Integer getUnavailableReplicas() { + return unavailableReplicas; + } + + public void setUnavailableReplicas(Integer unavailableReplicas) { + this.unavailableReplicas = unavailableReplicas; + } + + public String getCluster() { + return cluster; + } + + public void setCluster(String cluster) { + this.cluster = cluster; + } +} diff --git a/karavan-status/src/main/java/org/apache/camel/karavan/status/model/GroupedKey.java b/karavan-status/src/main/java/org/apache/camel/karavan/status/model/GroupedKey.java new file mode 100644 index 00000000..0095d61a --- /dev/null +++ b/karavan-status/src/main/java/org/apache/camel/karavan/status/model/GroupedKey.java @@ -0,0 +1,85 @@ +/* + * 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 + * + * http://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. + */ + +package org.apache.camel.karavan.status.model; + +public class GroupedKey { + + private String projectId; + private String env; + private String key; + + public GroupedKey(String projectId, String env, String key) { + this.projectId = projectId; + this.env = env; + this.key = key; + } + + public static String create(String projectId, String env, String key) { + return new GroupedKey(projectId, env, key).getCacheKey(); + } + + public String getEnv() { + return env; + } + + public String getKey() { + return key; + } + + public String getProjectId() { + return projectId; + } + + public void setProjectId(String projectId) { + this.projectId = projectId; + } + + public void setEnv(String env) { + this.env = env; + } + + public void setKey(String key) { + this.key = key; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + GroupedKey that = (GroupedKey) o; + + if (!projectId.equals(that.projectId)) return false; + if (!env.equals(that.env)) return false; + return key.equals(that.key); + } + + public String getCacheKey() { + return projectId + ":" + env + ":" + key; + } + + @Override + public int hashCode() { + return getCacheKey().hashCode(); + } + + @Override + public String toString() { + return "GroupedKey{" + getCacheKey() + '}'; + } +} \ No newline at end of file diff --git a/karavan-status/src/main/java/org/apache/camel/karavan/status/model/ServiceStatus.java b/karavan-status/src/main/java/org/apache/camel/karavan/status/model/ServiceStatus.java new file mode 100644 index 00000000..a7f2cddb --- /dev/null +++ b/karavan-status/src/main/java/org/apache/camel/karavan/status/model/ServiceStatus.java @@ -0,0 +1,113 @@ +/* + * 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 + * + * http://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. + */ + +package org.apache.camel.karavan.status.model; + + +public class ServiceStatus { + + String projectId; + String namespace; + String env; + String cluster; + Integer port; + Integer targetPort; + String clusterIP; + String type; + + public ServiceStatus(String projectId, String namespace, String env, String cluster, Integer port, Integer targetPort, String clusterIP, String type) { + this.projectId = projectId; + this.namespace = namespace; + this.env = env; + this.cluster = cluster; + this.port = port; + this.targetPort = targetPort; + this.clusterIP = clusterIP; + this.type = type; + } + + public ServiceStatus(String projectId, String namespace, String cluster, String env) { + this.projectId = projectId; + this.namespace = namespace; + this.env = env; + this.cluster = cluster; + } + + public String getProjectId() { + return projectId; + } + + public void setProjectId(String projectId) { + this.projectId = projectId; + } + + public String getNamespace() { + return namespace; + } + + public void setNamespace(String namespace) { + this.namespace = namespace; + } + + public String getEnv() { + return env; + } + + public void setEnv(String env) { + this.env = env; + } + + public String getCluster() { + return cluster; + } + + public void setCluster(String cluster) { + this.cluster = cluster; + } + + public Integer getPort() { + return port; + } + + public void setPort(Integer port) { + this.port = port; + } + + public Integer getTargetPort() { + return targetPort; + } + + public void setTargetPort(Integer targetPort) { + this.targetPort = targetPort; + } + + public String getClusterIP() { + return clusterIP; + } + + public void setClusterIP(String clusterIP) { + this.clusterIP = clusterIP; + } + + public String getType() { + return type; + } + + public void setType(String type) { + this.type = type; + } +} diff --git a/karavan-status/src/main/resources/application.properties b/karavan-status/src/main/resources/application.properties new file mode 100644 index 00000000..66b26380 --- /dev/null +++ b/karavan-status/src/main/resources/application.properties @@ -0,0 +1,29 @@ +# +# 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 +# +# http://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. +# + +karavan.title=Apache Camel Karavan +karavan.version=4.7.0 +karavan.environment=dev +karavan.environments=dev + +karavan.camel.status.interval=2s +karavan.container.status.interval=2s +# karavan.container.status.interval should be off in kubernetes + +karavan.container.statistics.interval=10s +# karavan.container.statistics.interval should be off in kubernetes +