diff --git a/.github/workflows/build-and-test-backend.yml b/.github/workflows/build-and-test-backend.yml index 4bfc3db4..bfef4098 100644 --- a/.github/workflows/build-and-test-backend.yml +++ b/.github/workflows/build-and-test-backend.yml @@ -1,38 +1,30 @@ -# This workflow will build a Java project with Maven, and cache/restore any dependencies to improve the workflow execution time -# For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-java-with-maven - -# This workflow uses actions that are not certified by GitHub. -# They are provided by a third-party and are governed by -# separate terms of service, privacy policy, and support -# documentation. - -name: Backend CI +name: Gradle Build on: push: - branches: [ "*" ] - pull_request: - branches: [ "*" ] jobs: - build-and-test: + build: runs-on: ubuntu-latest + permissions: + contents: read + packages: write steps: - - uses: actions/checkout@v4 - - name: Set up JDK 21 - uses: actions/setup-java@v4 - with: - java-version: '21' - distribution: 'temurin' - cache: maven - - name: Build and test backend with Maven - run: mvn -B --update-snapshots package --file ./backend/pom.xml - - name: Copy build artifact to staging directory - run: mkdir staging && cp ./backend/target/*.jar staging - - name: Upload build artifact to packages - uses: actions/upload-artifact@v4 - with: - name: agentmanager-backend-snapshot - path: staging + - uses: actions/checkout@v4 + - name: Set up JDK 21 + uses: actions/setup-java@v4 + with: + java-version: '21' + distribution: 'temurin' + + - name: Setup Gradle + uses: gradle/actions/setup-gradle@417ae3ccd767c252f5661f1ace9f835f9654f2b5 # v3.1.0 + with: + build-scan-publish: true + build-scan-terms-of-service-url: "https://gradle.com/terms-of-service" + build-scan-terms-of-service-agree: "yes" + + - name: Build with Gradle + run: ./gradlew build diff --git a/.gitignore b/.gitignore index af82301b..2931085f 100644 --- a/.gitignore +++ b/.gitignore @@ -1,7 +1,35 @@ -/out/ -.DS_Store -.idea -*.iml +# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider +# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 -/agentmanager/frontend/src/.nuxt/ -/agentmanager/frontend/src/node_modules/ +# User-specific stuff +.idea/ + +# File-based project format +*.iws + +# IntelliJ +out/ + +# mpeltonen/sbt-idea plugin +.idea_modules/ + +# JIRA plugin +atlassian-ide-plugin.xml + +# Crashlytics plugin (for Android Studio and IntelliJ) +com_crashlytics_export_strings.xml +crashlytics.properties +crashlytics-build.properties +fabric.properties + +# Gradle Files +.gradle +# build-Folder in root-dir +/build +# ui artifact build +/ui/build_packageClient/ + +**/.DS_Store + +**/node_modules +**/.nuxt diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 00000000..650f06f1 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,49 @@ +# Contributing + +## IDE + +We recommend using [IntelliJ](https://www.jetbrains.com/idea/download/#section=windows) as IDE for contributing. + +## Formatting + +We have [spotless](https://github.com/diffplug/spotless) configured to format the code. You can run the following commands: + +- `./gradlew spotlessCheck` to validate the formatting of the code. +- `./gradlew spotlessApply` to format the code. + +Be aware that the CI will fail if the code is not formatted correctly, as `spotlessCheck` is part of the build process. + + +## Building + +### Prerequisites +Please make sure you have the following tools installed on your machine: +- A big cup of coffee +- A Java Development Kit (JDK) with version 21 or higher +- A working internet connection (for downloading dependencies) +- Docker (optional, for running the application in a container) + +Thats it for now. + +### Backend +To build the backend, simply run the following command in the root directory of the project: +```shell +./gradlew +``` +This will generate the backend jar file in the `server/build/libs` directory. + +> [!TIP] +> You might have installed an auto-formatter in your IDE. It may break the installed spotless code-style. In this case, the build will not be successfull. Please run ```./gradlew potlessApply``` to fix this issue. + +### Frontend +To build the frontend, navigate to the `frontend/src` directory and run the following commands: +```shell + npm install + npm run build +``` +This will generate an entry point that launches a ready-to-run node server in the `frontend/src/.output` directory. + +You could also run the frontend in development mode by running: +```shell + npm run dev +``` \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 00000000..b5dbf32d --- /dev/null +++ b/README.md @@ -0,0 +1,17 @@ +# Welcome to the InspectIT Gepard Config-Server +InspectIT Gepard is an Extension for the OpenTelemetry Java agent, +enhancing your application with custom monitoring capabilities. +The agent is configured via a central configuration server, +which is responsible for providing instrumentation-rules, -scopes and -action along to configuration settings to the agent. +This repository contains the implementation of the configuration servers central component, +along with a User Interface for managing agents easily. + +InspectIT Gepard is developed by Novatec Consulting GmbH and backed by VHV Group. +It´s purpose is to replace the InspectIT Ocelot project with a more modern and flexible approach, utilizing the OpenTelemetry standard. + +## Getting Started +Currently, the project is in an early stage of development and not yet ready for production use. +However, if you want to try it out, you can follow the instructions in [CONTRIBUTING](./CONTRIBUTING.md). + +## Useful Links +- [SonarCloud](https://sonarcloud.io/) \ No newline at end of file diff --git a/agentmanager/backend/.mvn/wrapper/maven-wrapper.properties b/agentmanager/backend/.mvn/wrapper/maven-wrapper.properties deleted file mode 100644 index 8f96f52c..00000000 --- a/agentmanager/backend/.mvn/wrapper/maven-wrapper.properties +++ /dev/null @@ -1,19 +0,0 @@ -# Licensed to the Apache Software Foundation (ASF) under one -# or more contributor license agreements. See the NOTICE file -# distributed with this work for additional information -# regarding copyright ownership. The ASF licenses this file -# to you under the Apache License, Version 2.0 (the -# "License"); you may not use this file except in compliance -# with the License. You may obtain a copy of the License at -# -# https://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, -# software distributed under the License is distributed on an -# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -# KIND, either express or implied. See the License for the -# specific language governing permissions and limitations -# under the License. -wrapperVersion=3.3.2 -distributionType=only-script -distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.9.7/apache-maven-3.9.7-bin.zip diff --git a/agentmanager/backend/mvnw b/agentmanager/backend/mvnw deleted file mode 100755 index d7c358e5..00000000 --- a/agentmanager/backend/mvnw +++ /dev/null @@ -1,259 +0,0 @@ -#!/bin/sh -# ---------------------------------------------------------------------------- -# Licensed to the Apache Software Foundation (ASF) under one -# or more contributor license agreements. See the NOTICE file -# distributed with this work for additional information -# regarding copyright ownership. The ASF licenses this file -# to you under the Apache License, Version 2.0 (the -# "License"); you may not use this file except in compliance -# with the License. You may obtain a copy of the License at -# -# https://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, -# software distributed under the License is distributed on an -# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -# KIND, either express or implied. See the License for the -# specific language governing permissions and limitations -# under the License. -# ---------------------------------------------------------------------------- - -# ---------------------------------------------------------------------------- -# Apache Maven Wrapper startup batch script, version 3.3.2 -# -# Optional ENV vars -# ----------------- -# JAVA_HOME - location of a JDK home dir, required when download maven via java source -# MVNW_REPOURL - repo url base for downloading maven distribution -# MVNW_USERNAME/MVNW_PASSWORD - user and password for downloading maven -# MVNW_VERBOSE - true: enable verbose log; debug: trace the mvnw script; others: silence the output -# ---------------------------------------------------------------------------- - -set -euf -[ "${MVNW_VERBOSE-}" != debug ] || set -x - -# OS specific support. -native_path() { printf %s\\n "$1"; } -case "$(uname)" in -CYGWIN* | MINGW*) - [ -z "${JAVA_HOME-}" ] || JAVA_HOME="$(cygpath --unix "$JAVA_HOME")" - native_path() { cygpath --path --windows "$1"; } - ;; -esac - -# set JAVACMD and JAVACCMD -set_java_home() { - # For Cygwin and MinGW, ensure paths are in Unix format before anything is touched - 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" - JAVACCMD="$JAVA_HOME/jre/sh/javac" - else - JAVACMD="$JAVA_HOME/bin/java" - JAVACCMD="$JAVA_HOME/bin/javac" - - if [ ! -x "$JAVACMD" ] || [ ! -x "$JAVACCMD" ]; then - echo "The JAVA_HOME environment variable is not defined correctly, so mvnw cannot run." >&2 - echo "JAVA_HOME is set to \"$JAVA_HOME\", but \"\$JAVA_HOME/bin/java\" or \"\$JAVA_HOME/bin/javac\" does not exist." >&2 - return 1 - fi - fi - else - JAVACMD="$( - 'set' +e - 'unset' -f command 2>/dev/null - 'command' -v java - )" || : - JAVACCMD="$( - 'set' +e - 'unset' -f command 2>/dev/null - 'command' -v javac - )" || : - - if [ ! -x "${JAVACMD-}" ] || [ ! -x "${JAVACCMD-}" ]; then - echo "The java/javac command does not exist in PATH nor is JAVA_HOME set, so mvnw cannot run." >&2 - return 1 - fi - fi -} - -# hash string like Java String::hashCode -hash_string() { - str="${1:-}" h=0 - while [ -n "$str" ]; do - char="${str%"${str#?}"}" - h=$(((h * 31 + $(LC_CTYPE=C printf %d "'$char")) % 4294967296)) - str="${str#?}" - done - printf %x\\n $h -} - -verbose() { :; } -[ "${MVNW_VERBOSE-}" != true ] || verbose() { printf %s\\n "${1-}"; } - -die() { - printf %s\\n "$1" >&2 - exit 1 -} - -trim() { - # MWRAPPER-139: - # Trims trailing and leading whitespace, carriage returns, tabs, and linefeeds. - # Needed for removing poorly interpreted newline sequences when running in more - # exotic environments such as mingw bash on Windows. - printf "%s" "${1}" | tr -d '[:space:]' -} - -# parse distributionUrl and optional distributionSha256Sum, requires .mvn/wrapper/maven-wrapper.properties -while IFS="=" read -r key value; do - case "${key-}" in - distributionUrl) distributionUrl=$(trim "${value-}") ;; - distributionSha256Sum) distributionSha256Sum=$(trim "${value-}") ;; - esac -done <"${0%/*}/.mvn/wrapper/maven-wrapper.properties" -[ -n "${distributionUrl-}" ] || die "cannot read distributionUrl property in ${0%/*}/.mvn/wrapper/maven-wrapper.properties" - -case "${distributionUrl##*/}" in -maven-mvnd-*bin.*) - MVN_CMD=mvnd.sh _MVNW_REPO_PATTERN=/maven/mvnd/ - case "${PROCESSOR_ARCHITECTURE-}${PROCESSOR_ARCHITEW6432-}:$(uname -a)" in - *AMD64:CYGWIN* | *AMD64:MINGW*) distributionPlatform=windows-amd64 ;; - :Darwin*x86_64) distributionPlatform=darwin-amd64 ;; - :Darwin*arm64) distributionPlatform=darwin-aarch64 ;; - :Linux*x86_64*) distributionPlatform=linux-amd64 ;; - *) - echo "Cannot detect native platform for mvnd on $(uname)-$(uname -m), use pure java version" >&2 - distributionPlatform=linux-amd64 - ;; - esac - distributionUrl="${distributionUrl%-bin.*}-$distributionPlatform.zip" - ;; -maven-mvnd-*) MVN_CMD=mvnd.sh _MVNW_REPO_PATTERN=/maven/mvnd/ ;; -*) MVN_CMD="mvn${0##*/mvnw}" _MVNW_REPO_PATTERN=/org/apache/maven/ ;; -esac - -# apply MVNW_REPOURL and calculate MAVEN_HOME -# maven home pattern: ~/.m2/wrapper/dists/{apache-maven-,maven-mvnd--}/ -[ -z "${MVNW_REPOURL-}" ] || distributionUrl="$MVNW_REPOURL$_MVNW_REPO_PATTERN${distributionUrl#*"$_MVNW_REPO_PATTERN"}" -distributionUrlName="${distributionUrl##*/}" -distributionUrlNameMain="${distributionUrlName%.*}" -distributionUrlNameMain="${distributionUrlNameMain%-bin}" -MAVEN_USER_HOME="${MAVEN_USER_HOME:-${HOME}/.m2}" -MAVEN_HOME="${MAVEN_USER_HOME}/wrapper/dists/${distributionUrlNameMain-}/$(hash_string "$distributionUrl")" - -exec_maven() { - unset MVNW_VERBOSE MVNW_USERNAME MVNW_PASSWORD MVNW_REPOURL || : - exec "$MAVEN_HOME/bin/$MVN_CMD" "$@" || die "cannot exec $MAVEN_HOME/bin/$MVN_CMD" -} - -if [ -d "$MAVEN_HOME" ]; then - verbose "found existing MAVEN_HOME at $MAVEN_HOME" - exec_maven "$@" -fi - -case "${distributionUrl-}" in -*?-bin.zip | *?maven-mvnd-?*-?*.zip) ;; -*) die "distributionUrl is not valid, must match *-bin.zip or maven-mvnd-*.zip, but found '${distributionUrl-}'" ;; -esac - -# prepare tmp dir -if TMP_DOWNLOAD_DIR="$(mktemp -d)" && [ -d "$TMP_DOWNLOAD_DIR" ]; then - clean() { rm -rf -- "$TMP_DOWNLOAD_DIR"; } - trap clean HUP INT TERM EXIT -else - die "cannot create temp dir" -fi - -mkdir -p -- "${MAVEN_HOME%/*}" - -# Download and Install Apache Maven -verbose "Couldn't find MAVEN_HOME, downloading and installing it ..." -verbose "Downloading from: $distributionUrl" -verbose "Downloading to: $TMP_DOWNLOAD_DIR/$distributionUrlName" - -# select .zip or .tar.gz -if ! command -v unzip >/dev/null; then - distributionUrl="${distributionUrl%.zip}.tar.gz" - distributionUrlName="${distributionUrl##*/}" -fi - -# verbose opt -__MVNW_QUIET_WGET=--quiet __MVNW_QUIET_CURL=--silent __MVNW_QUIET_UNZIP=-q __MVNW_QUIET_TAR='' -[ "${MVNW_VERBOSE-}" != true ] || __MVNW_QUIET_WGET='' __MVNW_QUIET_CURL='' __MVNW_QUIET_UNZIP='' __MVNW_QUIET_TAR=v - -# normalize http auth -case "${MVNW_PASSWORD:+has-password}" in -'') MVNW_USERNAME='' MVNW_PASSWORD='' ;; -has-password) [ -n "${MVNW_USERNAME-}" ] || MVNW_USERNAME='' MVNW_PASSWORD='' ;; -esac - -if [ -z "${MVNW_USERNAME-}" ] && command -v wget >/dev/null; then - verbose "Found wget ... using wget" - wget ${__MVNW_QUIET_WGET:+"$__MVNW_QUIET_WGET"} "$distributionUrl" -O "$TMP_DOWNLOAD_DIR/$distributionUrlName" || die "wget: Failed to fetch $distributionUrl" -elif [ -z "${MVNW_USERNAME-}" ] && command -v curl >/dev/null; then - verbose "Found curl ... using curl" - curl ${__MVNW_QUIET_CURL:+"$__MVNW_QUIET_CURL"} -f -L -o "$TMP_DOWNLOAD_DIR/$distributionUrlName" "$distributionUrl" || die "curl: Failed to fetch $distributionUrl" -elif set_java_home; then - verbose "Falling back to use Java to download" - javaSource="$TMP_DOWNLOAD_DIR/Downloader.java" - targetZip="$TMP_DOWNLOAD_DIR/$distributionUrlName" - cat >"$javaSource" <<-END - public class Downloader extends java.net.Authenticator - { - protected java.net.PasswordAuthentication getPasswordAuthentication() - { - return new java.net.PasswordAuthentication( System.getenv( "MVNW_USERNAME" ), System.getenv( "MVNW_PASSWORD" ).toCharArray() ); - } - public static void main( String[] args ) throws Exception - { - setDefault( new Downloader() ); - java.nio.file.Files.copy( java.net.URI.create( args[0] ).toURL().openStream(), java.nio.file.Paths.get( args[1] ).toAbsolutePath().normalize() ); - } - } - END - # For Cygwin/MinGW, switch paths to Windows format before running javac and java - verbose " - Compiling Downloader.java ..." - "$(native_path "$JAVACCMD")" "$(native_path "$javaSource")" || die "Failed to compile Downloader.java" - verbose " - Running Downloader.java ..." - "$(native_path "$JAVACMD")" -cp "$(native_path "$TMP_DOWNLOAD_DIR")" Downloader "$distributionUrl" "$(native_path "$targetZip")" -fi - -# If specified, validate the SHA-256 sum of the Maven distribution zip file -if [ -n "${distributionSha256Sum-}" ]; then - distributionSha256Result=false - if [ "$MVN_CMD" = mvnd.sh ]; then - echo "Checksum validation is not supported for maven-mvnd." >&2 - echo "Please disable validation by removing 'distributionSha256Sum' from your maven-wrapper.properties." >&2 - exit 1 - elif command -v sha256sum >/dev/null; then - if echo "$distributionSha256Sum $TMP_DOWNLOAD_DIR/$distributionUrlName" | sha256sum -c >/dev/null 2>&1; then - distributionSha256Result=true - fi - elif command -v shasum >/dev/null; then - if echo "$distributionSha256Sum $TMP_DOWNLOAD_DIR/$distributionUrlName" | shasum -a 256 -c >/dev/null 2>&1; then - distributionSha256Result=true - fi - else - echo "Checksum validation was requested but neither 'sha256sum' or 'shasum' are available." >&2 - echo "Please install either command, or disable validation by removing 'distributionSha256Sum' from your maven-wrapper.properties." >&2 - exit 1 - fi - if [ $distributionSha256Result = false ]; then - echo "Error: Failed to validate Maven distribution SHA-256, your Maven distribution might be compromised." >&2 - echo "If you updated your Maven version, you need to update the specified distributionSha256Sum property." >&2 - exit 1 - fi -fi - -# unzip and move -if command -v unzip >/dev/null; then - unzip ${__MVNW_QUIET_UNZIP:+"$__MVNW_QUIET_UNZIP"} "$TMP_DOWNLOAD_DIR/$distributionUrlName" -d "$TMP_DOWNLOAD_DIR" || die "failed to unzip" -else - tar xzf${__MVNW_QUIET_TAR:+"$__MVNW_QUIET_TAR"} "$TMP_DOWNLOAD_DIR/$distributionUrlName" -C "$TMP_DOWNLOAD_DIR" || die "failed to untar" -fi -printf %s\\n "$distributionUrl" >"$TMP_DOWNLOAD_DIR/$distributionUrlNameMain/mvnw.url" -mv -- "$TMP_DOWNLOAD_DIR/$distributionUrlNameMain" "$MAVEN_HOME" || [ -d "$MAVEN_HOME" ] || die "fail to move MAVEN_HOME" - -clean || : -exec_maven "$@" diff --git a/agentmanager/backend/mvnw.cmd b/agentmanager/backend/mvnw.cmd deleted file mode 100644 index 6f779cff..00000000 --- a/agentmanager/backend/mvnw.cmd +++ /dev/null @@ -1,149 +0,0 @@ -<# : batch portion -@REM ---------------------------------------------------------------------------- -@REM Licensed to the Apache Software Foundation (ASF) under one -@REM or more contributor license agreements. See the NOTICE file -@REM distributed with this work for additional information -@REM regarding copyright ownership. The ASF licenses this file -@REM to you under the Apache License, Version 2.0 (the -@REM "License"); you may not use this file except in compliance -@REM with the License. You may obtain a copy of the License at -@REM -@REM https://www.apache.org/licenses/LICENSE-2.0 -@REM -@REM Unless required by applicable law or agreed to in writing, -@REM software distributed under the License is distributed on an -@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -@REM KIND, either express or implied. See the License for the -@REM specific language governing permissions and limitations -@REM under the License. -@REM ---------------------------------------------------------------------------- - -@REM ---------------------------------------------------------------------------- -@REM Apache Maven Wrapper startup batch script, version 3.3.2 -@REM -@REM Optional ENV vars -@REM MVNW_REPOURL - repo url base for downloading maven distribution -@REM MVNW_USERNAME/MVNW_PASSWORD - user and password for downloading maven -@REM MVNW_VERBOSE - true: enable verbose log; others: silence the output -@REM ---------------------------------------------------------------------------- - -@IF "%__MVNW_ARG0_NAME__%"=="" (SET __MVNW_ARG0_NAME__=%~nx0) -@SET __MVNW_CMD__= -@SET __MVNW_ERROR__= -@SET __MVNW_PSMODULEP_SAVE=%PSModulePath% -@SET PSModulePath= -@FOR /F "usebackq tokens=1* delims==" %%A IN (`powershell -noprofile "& {$scriptDir='%~dp0'; $script='%__MVNW_ARG0_NAME__%'; icm -ScriptBlock ([Scriptblock]::Create((Get-Content -Raw '%~f0'))) -NoNewScope}"`) DO @( - IF "%%A"=="MVN_CMD" (set __MVNW_CMD__=%%B) ELSE IF "%%B"=="" (echo %%A) ELSE (echo %%A=%%B) -) -@SET PSModulePath=%__MVNW_PSMODULEP_SAVE% -@SET __MVNW_PSMODULEP_SAVE= -@SET __MVNW_ARG0_NAME__= -@SET MVNW_USERNAME= -@SET MVNW_PASSWORD= -@IF NOT "%__MVNW_CMD__%"=="" (%__MVNW_CMD__% %*) -@echo Cannot start maven from wrapper >&2 && exit /b 1 -@GOTO :EOF -: end batch / begin powershell #> - -$ErrorActionPreference = "Stop" -if ($env:MVNW_VERBOSE -eq "true") { - $VerbosePreference = "Continue" -} - -# calculate distributionUrl, requires .mvn/wrapper/maven-wrapper.properties -$distributionUrl = (Get-Content -Raw "$scriptDir/.mvn/wrapper/maven-wrapper.properties" | ConvertFrom-StringData).distributionUrl -if (!$distributionUrl) { - Write-Error "cannot read distributionUrl property in $scriptDir/.mvn/wrapper/maven-wrapper.properties" -} - -switch -wildcard -casesensitive ( $($distributionUrl -replace '^.*/','') ) { - "maven-mvnd-*" { - $USE_MVND = $true - $distributionUrl = $distributionUrl -replace '-bin\.[^.]*$',"-windows-amd64.zip" - $MVN_CMD = "mvnd.cmd" - break - } - default { - $USE_MVND = $false - $MVN_CMD = $script -replace '^mvnw','mvn' - break - } -} - -# apply MVNW_REPOURL and calculate MAVEN_HOME -# maven home pattern: ~/.m2/wrapper/dists/{apache-maven-,maven-mvnd--}/ -if ($env:MVNW_REPOURL) { - $MVNW_REPO_PATTERN = if ($USE_MVND) { "/org/apache/maven/" } else { "/maven/mvnd/" } - $distributionUrl = "$env:MVNW_REPOURL$MVNW_REPO_PATTERN$($distributionUrl -replace '^.*'+$MVNW_REPO_PATTERN,'')" -} -$distributionUrlName = $distributionUrl -replace '^.*/','' -$distributionUrlNameMain = $distributionUrlName -replace '\.[^.]*$','' -replace '-bin$','' -$MAVEN_HOME_PARENT = "$HOME/.m2/wrapper/dists/$distributionUrlNameMain" -if ($env:MAVEN_USER_HOME) { - $MAVEN_HOME_PARENT = "$env:MAVEN_USER_HOME/wrapper/dists/$distributionUrlNameMain" -} -$MAVEN_HOME_NAME = ([System.Security.Cryptography.MD5]::Create().ComputeHash([byte[]][char[]]$distributionUrl) | ForEach-Object {$_.ToString("x2")}) -join '' -$MAVEN_HOME = "$MAVEN_HOME_PARENT/$MAVEN_HOME_NAME" - -if (Test-Path -Path "$MAVEN_HOME" -PathType Container) { - Write-Verbose "found existing MAVEN_HOME at $MAVEN_HOME" - Write-Output "MVN_CMD=$MAVEN_HOME/bin/$MVN_CMD" - exit $? -} - -if (! $distributionUrlNameMain -or ($distributionUrlName -eq $distributionUrlNameMain)) { - Write-Error "distributionUrl is not valid, must end with *-bin.zip, but found $distributionUrl" -} - -# prepare tmp dir -$TMP_DOWNLOAD_DIR_HOLDER = New-TemporaryFile -$TMP_DOWNLOAD_DIR = New-Item -Itemtype Directory -Path "$TMP_DOWNLOAD_DIR_HOLDER.dir" -$TMP_DOWNLOAD_DIR_HOLDER.Delete() | Out-Null -trap { - if ($TMP_DOWNLOAD_DIR.Exists) { - try { Remove-Item $TMP_DOWNLOAD_DIR -Recurse -Force | Out-Null } - catch { Write-Warning "Cannot remove $TMP_DOWNLOAD_DIR" } - } -} - -New-Item -Itemtype Directory -Path "$MAVEN_HOME_PARENT" -Force | Out-Null - -# Download and Install Apache Maven -Write-Verbose "Couldn't find MAVEN_HOME, downloading and installing it ..." -Write-Verbose "Downloading from: $distributionUrl" -Write-Verbose "Downloading to: $TMP_DOWNLOAD_DIR/$distributionUrlName" - -$webclient = New-Object System.Net.WebClient -if ($env:MVNW_USERNAME -and $env:MVNW_PASSWORD) { - $webclient.Credentials = New-Object System.Net.NetworkCredential($env:MVNW_USERNAME, $env:MVNW_PASSWORD) -} -[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12 -$webclient.DownloadFile($distributionUrl, "$TMP_DOWNLOAD_DIR/$distributionUrlName") | Out-Null - -# If specified, validate the SHA-256 sum of the Maven distribution zip file -$distributionSha256Sum = (Get-Content -Raw "$scriptDir/.mvn/wrapper/maven-wrapper.properties" | ConvertFrom-StringData).distributionSha256Sum -if ($distributionSha256Sum) { - if ($USE_MVND) { - Write-Error "Checksum validation is not supported for maven-mvnd. `nPlease disable validation by removing 'distributionSha256Sum' from your maven-wrapper.properties." - } - Import-Module $PSHOME\Modules\Microsoft.PowerShell.Utility -Function Get-FileHash - if ((Get-FileHash "$TMP_DOWNLOAD_DIR/$distributionUrlName" -Algorithm SHA256).Hash.ToLower() -ne $distributionSha256Sum) { - Write-Error "Error: Failed to validate Maven distribution SHA-256, your Maven distribution might be compromised. If you updated your Maven version, you need to update the specified distributionSha256Sum property." - } -} - -# unzip and move -Expand-Archive "$TMP_DOWNLOAD_DIR/$distributionUrlName" -DestinationPath "$TMP_DOWNLOAD_DIR" | Out-Null -Rename-Item -Path "$TMP_DOWNLOAD_DIR/$distributionUrlNameMain" -NewName $MAVEN_HOME_NAME | Out-Null -try { - Move-Item -Path "$TMP_DOWNLOAD_DIR/$MAVEN_HOME_NAME" -Destination $MAVEN_HOME_PARENT | Out-Null -} catch { - if (! (Test-Path -Path "$MAVEN_HOME" -PathType Container)) { - Write-Error "fail to move MAVEN_HOME" - } -} finally { - try { Remove-Item $TMP_DOWNLOAD_DIR -Recurse -Force | Out-Null } - catch { Write-Warning "Cannot remove $TMP_DOWNLOAD_DIR" } -} - -Write-Output "MVN_CMD=$MAVEN_HOME/bin/$MVN_CMD" diff --git a/agentmanager/backend/pom.xml b/agentmanager/backend/pom.xml deleted file mode 100644 index 9cf38faf..00000000 --- a/agentmanager/backend/pom.xml +++ /dev/null @@ -1,262 +0,0 @@ - - - 4.0.0 - - org.springframework.boot - spring-boot-starter-parent - 3.3.0 - - - rocks.gepard - backend - 0.0.1-SNAPSHOT - backend - backend - - 17 - 1.9.0 - - - - - - org.springframework.boot - spring-boot-starter-data-jpa - - - - org.springframework.boot - spring-boot-starter-web - - - - com.fasterxml.jackson.module - jackson-module-kotlin - - - - org.jetbrains.kotlin - kotlin-reflect - - - - org.jetbrains.kotlin - kotlin-stdlib-jdk8 - ${kotlin.version} - - - - org.springframework.boot - spring-boot-devtools - runtime - true - - - - com.h2database - h2 - runtime - - - - org.projectlombok - lombok - true - - - - org.springframework.boot - spring-boot-starter-test - test - - - - org.jetbrains.kotlin - kotlin-test-junit5 - test - - - - com.google.code.gson - gson - 2.10.1 - test - - - - io.swagger.core.v3 - swagger-annotations - 2.2.2 - - - - org.openapitools - jackson-databind-nullable - 0.2.2 - - - - javax.validation - validation-api - 2.0.1.Final - - - - javax.servlet - javax.servlet-api - 3.0.1 - compile - - - - javax.annotation - javax.annotation-api - 1.3.2 - - - - org.apache.commons - commons-lang3 - 3.14.0 - - - org.jetbrains.kotlin - kotlin-test - ${kotlin.version} - test - - - - - ${project.basedir}/src/main/kotlin - ${project.basedir}/src/test/kotlin - - - - - org.springframework.boot - spring-boot-maven-plugin - - - - org.projectlombok - lombok - - - - - - - org.jetbrains.kotlin - kotlin-maven-plugin - ${kotlin.version} - - - compile - compile - - compile - - - - src/main/kotlin - target/generated-sources/annotations - target/generated-sources/openapi/src/main/java - - - - - test-compile - test-compile - - test-compile - - - - src/test/kotlin - target/generated-test-sources/test-annotations - - - - - - - -Xjsr305=strict - - - spring - jpa - - 1.8 - - - - org.jetbrains.kotlin - kotlin-maven-allopen - ${kotlin.version} - - - org.jetbrains.kotlin - kotlin-maven-noarg - ${kotlin.version} - - - - - - org.openapitools - openapi-generator-maven-plugin - 6.1.0 - - - - generate - - - - ${project.basedir}/src/main/resources/openai/AgentController-1.0.0.yaml - - spring - rocks.gepard.backend.infrastructure.incoming.api - rocks.gepard.backend.infrastructure.incoming.model - - true - - - - - - - org.apache.maven.plugins - maven-compiler-plugin - - - default-compile - none - - - default-testCompile - none - - - compile - compile - - compile - - - - testCompile - test-compile - - testCompile - - - - - - - - - diff --git a/agentmanager/backend/src/main/kotlin/rocks/gepard/backend/AgentManagerConfiguration.kt b/agentmanager/backend/src/main/kotlin/rocks/gepard/backend/AgentManagerConfiguration.kt deleted file mode 100644 index 71b3c872..00000000 --- a/agentmanager/backend/src/main/kotlin/rocks/gepard/backend/AgentManagerConfiguration.kt +++ /dev/null @@ -1,45 +0,0 @@ -package rocks.gepard.backend - -import org.springframework.boot.ApplicationArguments -import org.springframework.boot.ApplicationRunner -import org.springframework.boot.context.properties.EnableConfigurationProperties -import org.springframework.boot.web.servlet.FilterRegistrationBean -import org.springframework.context.annotation.Bean -import org.springframework.context.annotation.Configuration -import org.springframework.core.Ordered -import org.springframework.web.cors.CorsConfiguration -import org.springframework.web.cors.UrlBasedCorsConfigurationSource -import org.springframework.web.filter.CorsFilter -import java.util.function.Consumer - -@Configuration -@EnableConfigurationProperties -class AgentManagerConfiguration { - // Bootstrap some test data into the in-memory database -// @Bean -// fun init(repository: AgentRepository): ApplicationRunner { -// return ApplicationRunner { _: ApplicationArguments? -> -// arrayOf("Affe", "Häschen", "Schildkröte", "Hund", "Katze").forEach { -// val agent = Agent(it) -// repository.save(agent) -// } -// repository.findAll().forEach(/* action = */ Consumer { x: Agent? -> println(x) }) -// } -// } - - // Fix the CORS errors - @Bean - fun simpleCorsFilter(): FilterRegistrationBean<*> { - val source = UrlBasedCorsConfigurationSource() - val config = CorsConfiguration() - config.allowCredentials = true - // *** URL below needs to match the Vue client URL and port *** - config.allowedOrigins = listOf("http://localhost:3000") - config.allowedMethods = listOf("*") - config.allowedHeaders = listOf("*") - source.registerCorsConfiguration("/**", config) - val bean: FilterRegistrationBean<*> = FilterRegistrationBean(CorsFilter(source)) - bean.order = Ordered.HIGHEST_PRECEDENCE - return bean - } -} \ No newline at end of file diff --git a/agentmanager/backend/src/main/kotlin/rocks/gepard/backend/BackendApplication.kt b/agentmanager/backend/src/main/kotlin/rocks/gepard/backend/BackendApplication.kt deleted file mode 100644 index 2e6f4ead..00000000 --- a/agentmanager/backend/src/main/kotlin/rocks/gepard/backend/BackendApplication.kt +++ /dev/null @@ -1,15 +0,0 @@ -package rocks.gepard.backend - -import org.springframework.boot.SpringApplication -import org.springframework.boot.autoconfigure.SpringBootApplication -import org.springframework.boot.context.properties.ConfigurationProperties - -@SpringBootApplication -class BackendApplication { - companion object { - @JvmStatic - fun main(args: Array) { - SpringApplication.run(BackendApplication::class.java, *args) - } - } -} \ No newline at end of file diff --git a/agentmanager/backend/src/main/kotlin/rocks/gepard/backend/application/AgentCache.kt b/agentmanager/backend/src/main/kotlin/rocks/gepard/backend/application/AgentCache.kt deleted file mode 100644 index 62b27fec..00000000 --- a/agentmanager/backend/src/main/kotlin/rocks/gepard/backend/application/AgentCache.kt +++ /dev/null @@ -1,26 +0,0 @@ -package rocks.gepard.backend.application - -import rocks.gepard.backend.infrastructure.incoming.model.AgentResponseDto - - -class AgentCache : GenericCache { - - private val cache = HashMap() - - override val size: Int - get() = cache.size - - override fun set(key: K, value: V) { - cache[key] = value - } - - override fun remove(key: K) = cache.remove(key) - - override fun get(key: K) = cache[key] - - override fun clear() = cache.clear() - - fun getAllAgents() : MutableList{ - return TODO() - } -} \ No newline at end of file diff --git a/agentmanager/backend/src/main/kotlin/rocks/gepard/backend/application/AgentService.kt b/agentmanager/backend/src/main/kotlin/rocks/gepard/backend/application/AgentService.kt deleted file mode 100644 index 21a31416..00000000 --- a/agentmanager/backend/src/main/kotlin/rocks/gepard/backend/application/AgentService.kt +++ /dev/null @@ -1,36 +0,0 @@ -package rocks.gepard.backend.application - -import org.springframework.stereotype.Service -import rocks.gepard.backend.domain.model.Agent -import rocks.gepard.backend.infrastructure.incoming.model.AgentDto -import rocks.gepard.backend.infrastructure.incoming.model.AgentResponseDto - -@Service -class AgentService { - - private val agentCache: AgentCache = AgentCache() - - fun storeAgent(agent: Agent?) { - agent?.let { - agentCache.set(it.name, it) - } - } - - fun getAllAgents(): MutableList { - - return agentCache.getAllAgents() - } - - fun getAgentByName(agentName: String?): AgentResponseDto? { - return agentName - ?.let { agentCache.get(it) } - ?.let(Agent::toResponseDto) - } -} - -fun Agent.toResponseDto(): AgentResponseDto { - return AgentResponseDto().name(this.name) - .javaversion(this.getJavaVersion()) - .otelversion(this.getOtelVersion()) - .config(this.configuration.toString()) -} diff --git a/agentmanager/backend/src/main/kotlin/rocks/gepard/backend/application/GenericCache.kt b/agentmanager/backend/src/main/kotlin/rocks/gepard/backend/application/GenericCache.kt deleted file mode 100644 index 09aae274..00000000 --- a/agentmanager/backend/src/main/kotlin/rocks/gepard/backend/application/GenericCache.kt +++ /dev/null @@ -1,29 +0,0 @@ -package rocks.gepard.backend.application - -interface GenericCache { - - /** - * The number of the items that are currently cached. - */ - val size: Int - - /** - * Cache a [value] with a given [key] - */ - operator fun set(key: K, value: V) - - /** - * Get the cached value of a given [key], or null if it's not cached or evicted. - */ - operator fun get(key: K): V? - - /** - * Remove the value of the [key] from the cache, and return the removed value, or null if it's not cached at all. - */ - fun remove(key: K): V? - - /** - * Remove all the items in the cache. - */ - fun clear() -} \ No newline at end of file diff --git a/agentmanager/backend/src/main/kotlin/rocks/gepard/backend/domain/model/Agent.kt b/agentmanager/backend/src/main/kotlin/rocks/gepard/backend/domain/model/Agent.kt deleted file mode 100644 index b469bdfa..00000000 --- a/agentmanager/backend/src/main/kotlin/rocks/gepard/backend/domain/model/Agent.kt +++ /dev/null @@ -1,36 +0,0 @@ -package rocks.gepard.backend.domain.model - -import jakarta.persistence.GeneratedValue -import jakarta.persistence.Id -import rocks.gepard.backend.domain.valueObjects.AgentMetaData -import rocks.gepard.backend.domain.valueObjects.Configuration -import java.time.LocalDateTime - -//@Entity -class Agent( - var name: String, - var registrationTime: LocalDateTime = LocalDateTime.now(), - var agentMetadata: AgentMetaData, -) { - - @Id - @GeneratedValue - var id: Long? = null - - val configuration: Configuration - get() = Configuration("configuration") - - fun getJavaVersion(): String { - return agentMetadata.javaVersion - } - - fun getOtelVersion(): String { - return agentMetadata.otelVersion - } - - override fun toString(): String { - return "Name='$name', Registrationtime='$registrationTime', " + - "agentMetaData='$agentMetadata'" - } - -} \ No newline at end of file diff --git a/agentmanager/backend/src/main/kotlin/rocks/gepard/backend/domain/valueObjects/AgentMetaData.kt b/agentmanager/backend/src/main/kotlin/rocks/gepard/backend/domain/valueObjects/AgentMetaData.kt deleted file mode 100644 index 4034b4ed..00000000 --- a/agentmanager/backend/src/main/kotlin/rocks/gepard/backend/domain/valueObjects/AgentMetaData.kt +++ /dev/null @@ -1,8 +0,0 @@ -package rocks.gepard.backend.domain.valueObjects - -class AgentMetaData(var otelVersion : String, var javaVersion : String) { - override fun toString(): String { - return "otelVersion='$otelVersion', " + - "javaVersion='$javaVersion'" - } -} \ No newline at end of file diff --git a/agentmanager/backend/src/main/kotlin/rocks/gepard/backend/domain/valueObjects/Configuration.kt b/agentmanager/backend/src/main/kotlin/rocks/gepard/backend/domain/valueObjects/Configuration.kt deleted file mode 100644 index c194260a..00000000 --- a/agentmanager/backend/src/main/kotlin/rocks/gepard/backend/domain/valueObjects/Configuration.kt +++ /dev/null @@ -1,5 +0,0 @@ -package rocks.gepard.backend.domain.valueObjects - -class Configuration(configuration: String) { - -} \ No newline at end of file diff --git a/agentmanager/backend/src/main/kotlin/rocks/gepard/backend/domain/valueObjects/RegistrationTime.kt b/agentmanager/backend/src/main/kotlin/rocks/gepard/backend/domain/valueObjects/RegistrationTime.kt deleted file mode 100644 index 31062bca..00000000 --- a/agentmanager/backend/src/main/kotlin/rocks/gepard/backend/domain/valueObjects/RegistrationTime.kt +++ /dev/null @@ -1,5 +0,0 @@ -package rocks.gepard.backend.domain.valueObjects - -class RegistrationTime(var value:Long) { - -} \ No newline at end of file diff --git a/agentmanager/backend/src/main/kotlin/rocks/gepard/backend/infrastructure/incoming/AgentController.kt b/agentmanager/backend/src/main/kotlin/rocks/gepard/backend/infrastructure/incoming/AgentController.kt deleted file mode 100644 index a8375a4f..00000000 --- a/agentmanager/backend/src/main/kotlin/rocks/gepard/backend/infrastructure/incoming/AgentController.kt +++ /dev/null @@ -1,92 +0,0 @@ -package rocks.gepard.backend.infrastructure.incoming - -import org.springframework.http.ResponseEntity -import org.springframework.web.bind.annotation.RestController -import rocks.gepard.backend.application.AgentService -import rocks.gepard.backend.domain.model.Agent -import rocks.gepard.backend.domain.valueObjects.AgentMetaData -import rocks.gepard.backend.infrastructure.incoming.api.AgentsApi -import rocks.gepard.backend.infrastructure.incoming.model.AgentDto -import rocks.gepard.backend.infrastructure.incoming.model.AgentResponseDto -import java.net.URI - -@RestController -class AgentController : AgentsApi { - - private val agentService = AgentService() - private val agentList: MutableList = mutableListOf() - - init { - createTestDate() - } - - override fun registerAgent(agentDto: AgentDto?): ResponseEntity { - val agentResponseDto = agentDto?.toResponse() - - // first without deeper logic -// val agent = agentDto?.toDomain() -// agentService.storeAgent(agent) - agentResponseDto?.let { - agentList.add(0, agentResponseDto) -// } else { -// agentResponseDto?.let { -// agentList.add(0, agentResponseDto) -// } - } - - return agentDto?.let { - ResponseEntity.created(URI.create("blubb")).build() - } ?: ResponseEntity.badRequest().build() - } - - override fun getAllAgents(limit: Int?): ResponseEntity> { -// val agents = agentService.getAllAgents() -// return ResponseEntity.ok(agents) - return ResponseEntity.ok(agentList) - } - - private fun createTestDate() { - agentList.clear() - val agent1 = AgentResponseDto() - agent1.name("Huhn") - agent1.healthState("alive") - agent1.javaversion("21") - agent1.otelversion("1.0.1") - agentList.add(agent1) - - val agent2 = AgentResponseDto() - agent2.name("Ente") - agent2.javaversion("8") - agent2.otelversion("1.0.5") - agentList.add(agent2) - - val agent3 = AgentResponseDto() - agent3.name("Rind") - agent3.healthState("alive") - agent3.config("config") - agent3.javaversion("17") - agent3.otelversion("1.0.4") - agentList.add(agent3) - } -} - -fun AgentDto.toDomain(): Agent { - return Agent( - name = this.name, - agentMetadata = AgentMetaData( - otelVersion = this.otelVersion, - javaVersion = this.javaVersion - ) - ) -} - -fun AgentDto.toResponse(): AgentResponseDto { - return AgentResponseDto().apply { - name = this@toResponse.name - healthState = "alive" - javaversion = this@toResponse.javaVersion - otelversion = this@toResponse.otelVersion - gepardVersion = this@toResponse.gepardVersion - } -} - diff --git a/agentmanager/backend/src/main/resources/application.properties b/agentmanager/backend/src/main/resources/application.properties deleted file mode 100644 index 3ca17a4e..00000000 --- a/agentmanager/backend/src/main/resources/application.properties +++ /dev/null @@ -1 +0,0 @@ -spring.application.name=backend diff --git a/agentmanager/backend/src/main/resources/openai/AgentController-1.0.0.yaml b/agentmanager/backend/src/main/resources/openai/AgentController-1.0.0.yaml deleted file mode 100644 index 0a682699..00000000 --- a/agentmanager/backend/src/main/resources/openai/AgentController-1.0.0.yaml +++ /dev/null @@ -1,127 +0,0 @@ -openapi: "3.0.0" -info: - title: AgentController API Description - description: API for managing agents - contact: - email: jwt@novatec-gmbh.de - license: - name: Apache 2.0 - url: http://www.apache.org/licenses/LICENSE-2.0.html - version: "1.0.0" -servers: - - url: "https://AgentController.swagger.io/api/v1" -tags: - - name: agent - description: Managing Agentlifecycle - -paths: - /agents: - post: - tags: - - agents - summary: Add a agent to the configserver - description: Add a agent to the configserver - operationId: registerAgent - requestBody: - content: - application/json: - schema: - $ref: '#/components/schemas/AgentDto' - required: true - responses: - '201': - description: Successfully created - content: - application/json: - schema: - $ref: '#/components/schemas/AgentResponseDto' - '415': - description: Invalid input - get: - summary: List all agents - operationId: getAllAgents - tags: - - agents - parameters: - - name: limit - in: query - description: How many items to return at one time (max 100) - required: false - schema: - type: integer - maximum: 100 - format: int32 - responses: - '200': - description: A paged array of agents - headers: - x-next: - description: A link to the next page of responses - schema: - type: string - content: - application/json: - schema: - $ref: "#/components/schemas/ResponseAgents" - default: - description: unexpected error - content: - application/json: - schema: - $ref: "#/components/schemas/Error" - -components: - schemas: - AgentDto: - type: object - properties: - name: - type: string - otelVersion: - type: string - gepardVersion: - type: string - javaVersion: - type: string - required: - - name - - AgentResponseDto: - type: object - properties: - name: - type: string - healthState: - type: string - otelversion: - type: string - javaversion: - type: string - config: - type: string - gepardVersion: - type: string - required: - - name - - healthState - - javaversion - - otelversion - - config - - gepardVersion - - ResponseAgents: - type: array - maxItems: 100 - items: - $ref: "#/components/schemas/AgentResponseDto" - Error: - type: object - required: - - code - - message - properties: - code: - type: integer - format: int32 - message: - type: string \ No newline at end of file diff --git a/agentmanager/backend/src/test/kotlin/rocks/gepard/backend/application/BackendApplicationTest.kt b/agentmanager/backend/src/test/kotlin/rocks/gepard/backend/application/BackendApplicationTest.kt deleted file mode 100644 index 3ccf1cde..00000000 --- a/agentmanager/backend/src/test/kotlin/rocks/gepard/backend/application/BackendApplicationTest.kt +++ /dev/null @@ -1,12 +0,0 @@ -package rocks.gepard.backend.application - -import org.junit.jupiter.api.Test - -import org.junit.jupiter.api.Assertions.* - -class BackendApplicationTest { - - @Test - fun main() { - } -} \ No newline at end of file diff --git a/agentmanager/backend/src/test/kotlin/rocks/gepard/backend/infrastructure/incoming/AgentControllerTest.kt b/agentmanager/backend/src/test/kotlin/rocks/gepard/backend/infrastructure/incoming/AgentControllerTest.kt deleted file mode 100644 index 4395338e..00000000 --- a/agentmanager/backend/src/test/kotlin/rocks/gepard/backend/infrastructure/incoming/AgentControllerTest.kt +++ /dev/null @@ -1,89 +0,0 @@ -package rocks.gepard.backend.infrastructure.incoming - -import com.google.gson.Gson -import org.apache.commons.lang3.StringUtils -import org.junit.jupiter.api.Test -import org.springframework.beans.factory.annotation.Autowired -import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest -import org.springframework.http.MediaType -import org.springframework.test.web.servlet.MockMvc -import org.springframework.test.web.servlet.request.MockMvcRequestBuilders -import org.springframework.test.web.servlet.result.MockMvcResultMatchers -import rocks.gepard.backend.infrastructure.incoming.model.AgentDto - -@WebMvcTest(AgentController::class) -class AgentControllerTest { - - @Autowired - private lateinit var mvc: MockMvc - - @Test - fun registerAgentWillReturns201IfAgentsWasCreated() { - - //GIVEN - val agentDto = AgentDto() - agentDto.name = "Hase" - agentDto.javaVersion = "17" - agentDto.otelVersion = "21" - - var gson = Gson() - val agentJson = gson.toJson(agentDto) - - //WHEN || THEN - mvc.perform( - MockMvcRequestBuilders.post("/api/v1/registerAgent") - .contentType(MediaType.APPLICATION_JSON) - .content(agentJson) - ) - .andExpect(MockMvcResultMatchers.status().isCreated) - } - - @Test - fun registerAgentWillReturns400WhenContentIsEmpty() { - - //GIVEN || WHEN || THEN - mvc.perform( - MockMvcRequestBuilders.post("/api/v1/registerAgent") - .contentType(MediaType.APPLICATION_JSON) - .content(StringUtils.EMPTY) - ) - .andExpect(MockMvcResultMatchers.status().isBadRequest) - } - - @Test - fun registerAgentWillReturns415WhenContentIsXML() { - - //GIVEN - val agentDto = AgentDto() - agentDto.name = "Hase" - agentDto.javaVersion = "17" - agentDto.otelVersion = "21" - - var gson = Gson() - val agentJson = gson.toJson(agentDto) - - //WHEN || THEN - mvc.perform( - MockMvcRequestBuilders.post("/api/v1/registerAgent") - .contentType(MediaType.APPLICATION_XML) - .content(agentJson) - ) - .andExpect(MockMvcResultMatchers.status().isUnsupportedMediaType) - } - - @Test - fun registerAgentWillReturns415WhenContentIsNull() { - - //GIVEN - var gson = Gson() - val agentJson = gson.toJson(null) - - //WHEN || THEN - mvc.perform( - MockMvcRequestBuilders.post("/api/v1/registerAgent") - .contentType(MediaType.APPLICATION_XML) - .content(agentJson) - ) - .andExpect(MockMvcResultMatchers.status().isUnsupportedMediaType) - } -} \ No newline at end of file diff --git a/agentmanager/frontend/pom.xml b/agentmanager/frontend/pom.xml deleted file mode 100644 index 7d42cbc5..00000000 --- a/agentmanager/frontend/pom.xml +++ /dev/null @@ -1,91 +0,0 @@ - - - 4.0.0 - - rocks.gepard - agentmanager - 1.0-SNAPSHOT - - - frontend - - - UTF-8 - official - 1.8 - - - - - mavenCentral - https://repo1.maven.org/maven2/ - - - - - blubb/main/kotlin - blubb/test/kotlin - - - org.jetbrains.kotlin - kotlin-maven-plugin - 1.9.21 - - - compile - compile - - compile - - - - test-compile - test-compile - - test-compile - - - - - - maven-surefire-plugin - 2.22.2 - - - maven-failsafe-plugin - 2.22.2 - - - org.codehaus.mojo - exec-maven-plugin - 1.6.0 - - MainKt - - - - - - - - org.jetbrains.kotlin - kotlin-test-junit5 - 1.9.21 - test - - - org.junit.jupiter - junit-jupiter-engine - 5.10.0 - test - - - org.jetbrains.kotlin - kotlin-stdlib - 1.9.21 - - - - \ No newline at end of file diff --git a/agentmanager/pom.xml b/agentmanager/pom.xml deleted file mode 100644 index 87e16a58..00000000 --- a/agentmanager/pom.xml +++ /dev/null @@ -1,49 +0,0 @@ - - - 4.0.0 - - rocks.gepard - agentmanager - 1.0-SNAPSHOT - pom - - frontend - backend - - - - UTF-8 - - - - - mavenCentral - https://repo1.maven.org/maven2/ - - - - - - - maven-surefire-plugin - 2.22.2 - - - maven-failsafe-plugin - 2.22.2 - - - - - - - org.junit.jupiter - junit-jupiter-engine - 5.10.0 - test - - - - \ No newline at end of file diff --git a/agentmanager/backend/.gitignore b/backend/.gitignore similarity index 100% rename from agentmanager/backend/.gitignore rename to backend/.gitignore diff --git a/backend/Dockerfile b/backend/Dockerfile new file mode 100644 index 00000000..a3080ffb --- /dev/null +++ b/backend/Dockerfile @@ -0,0 +1,36 @@ +# ---- Build Stage ---- +FROM gradle:jdk21 as build + +WORKDIR /home/gradle/project + +# Copy the Gradle wrapper files +COPY ../gradle /home/gradle/project/gradle +COPY ../gradlew /home/gradle/project/ +COPY ../build.gradle /home/gradle/project/ +COPY ../settings.gradle /home/gradle/project/ + +# Copy server source code +COPY backend /home/gradle/project/backend + +# Build the application +RUN ./gradlew build -x test + +# ---- Runtime Stage ---- +# We use Temurin as it is the default at VHV +FROM eclipse-temurin:21-jre-jammy + +# In order to use a postgres database, we need to set the active profile +# ENV SPRING_PROFILES_ACTIVE=postgres + +# Copy the build from the server subproject to the runtime image +# The jar already contains the static export from the ui subproject +COPY --from=build /home/gradle/project/backend/build/libs/*.jar app.jar + +# Expose the port on which the app will run +EXPOSE 8080 + +# Run the application +ENTRYPOINT ["java", "-jar", "/app.jar"] + + + diff --git a/backend/build.gradle b/backend/build.gradle new file mode 100644 index 00000000..3df9308e --- /dev/null +++ b/backend/build.gradle @@ -0,0 +1,68 @@ +plugins { + id 'java' + id 'org.springframework.boot' version '3.3.0' + id 'io.spring.dependency-management' version '1.1.5' + id("com.diffplug.spotless") version "6.25.0" + id 'jacoco' +} + +group = 'rocks.inspectit.gepard' +version = '0.0.1-SNAPSHOT' + +java { + sourceCompatibility = '17' +} + +configurations { + compileOnly { + extendsFrom annotationProcessor + } +} + +repositories { + mavenCentral() +} + +dependencies { + + // Starters + implementation 'org.springframework.boot:spring-boot-starter-web' + implementation 'org.springframework.boot:spring-boot-starter-actuator' + implementation 'org.springframework.boot:spring-boot-starter-validation' + annotationProcessor 'org.projectlombok:lombok' + + // Actuator - for management endpoints + implementation 'org.springframework.boot:spring-boot-starter-actuator' + // Swagger - for API documentation + implementation 'org.springdoc:springdoc-openapi-starter-webmvc-ui:2.6.0' + + // Test + testImplementation 'org.springframework.boot:spring-boot-starter-test' + testRuntimeOnly 'org.junit.platform:junit-platform-launcher' +} + +tasks.named('test') { + finalizedBy jacocoTestReport + useJUnitPlatform() +} + +jacocoTestReport { + dependsOn test +} + +spotless { + java { + importOrder() + removeUnusedImports() + + // Cleanthat will refactor code. Has to be configured. + // cleanthat() + + googleJavaFormat() // has its own section below + + formatAnnotations() // fixes formatting of type annotations, see below + + licenseHeader '/* (C) 2024 */' // or licenseHeaderFile + } +} + diff --git a/backend/lombok.config b/backend/lombok.config new file mode 100644 index 00000000..84bda564 --- /dev/null +++ b/backend/lombok.config @@ -0,0 +1,2 @@ +# Let static analysis tools know which classes are generated by Lombok +lombok.addLombokGeneratedAnnotation=true \ No newline at end of file diff --git a/backend/src/main/java/rocks/inspectit/gepard/agentmanager/Application.java b/backend/src/main/java/rocks/inspectit/gepard/agentmanager/Application.java new file mode 100644 index 00000000..9c03276a --- /dev/null +++ b/backend/src/main/java/rocks/inspectit/gepard/agentmanager/Application.java @@ -0,0 +1,13 @@ +/* (C) 2024 */ +package rocks.inspectit.gepard.agentmanager; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +public class Application { + + public static void main(String[] args) { + SpringApplication.run(Application.class, args); + } +} diff --git a/backend/src/main/java/rocks/inspectit/gepard/agentmanager/agent/model/Agent.java b/backend/src/main/java/rocks/inspectit/gepard/agentmanager/agent/model/Agent.java new file mode 100644 index 00000000..b98a9ea0 --- /dev/null +++ b/backend/src/main/java/rocks/inspectit/gepard/agentmanager/agent/model/Agent.java @@ -0,0 +1,30 @@ +/* (C) 2024 */ +package rocks.inspectit.gepard.agentmanager.agent.model; + +import jakarta.validation.constraints.NotNull; +import java.time.Instant; +import lombok.*; + +/** Represents an agent which is connected to the config server. */ +@AllArgsConstructor +@ToString +@Getter +public class Agent { + /** The name of the service which is running the agent. */ + @NotNull private String serviceName; + + /** The process id of the JVM which carries the agent. */ + @NotNull private Long pid; + + /** The Gepard-Version. */ + @NotNull private String gepardVersion; + + /** The OpenTelemetry-Java-Instrumentation-Version. */ + @NotNull private String otelVersion; + + /** The start time of the JVM which carries the agent. */ + @NotNull private Instant startTime; + + /** The Java version of the JVM which carries the agent. */ + @NotNull private String javaVersion; +} diff --git a/backend/src/main/java/rocks/inspectit/gepard/agentmanager/application/config/CorsConfiguration.java b/backend/src/main/java/rocks/inspectit/gepard/agentmanager/application/config/CorsConfiguration.java new file mode 100644 index 00000000..7f8d2355 --- /dev/null +++ b/backend/src/main/java/rocks/inspectit/gepard/agentmanager/application/config/CorsConfiguration.java @@ -0,0 +1,31 @@ +/* (C) 2024 */ +package rocks.inspectit.gepard.agentmanager.application.config; + +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Configuration; +import org.springframework.web.servlet.config.annotation.CorsRegistry; +import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; + +/** Web configuration for the application. */ +@Configuration +public class CorsConfiguration implements WebMvcConfigurer { + + @Value("${inspectit-config-server.security.cors.path-pattern}") + private String pathPattern; + + @Value("${inspectit-config-server.security.cors.allowed-origins}") + private String allowedOrigins; + + @Value("${inspectit-config-server.security.cors.allowed-methods}") + private String allowedMethods; + + /** + * Adds CORS mappings to the registry. Allow all origins and methods. + * + * @param registry The CorsRegistry to add mappings and allowed headers, methods and origins to. + */ + @Override + public void addCorsMappings(CorsRegistry registry) { + registry.addMapping(pathPattern).allowedOrigins(allowedOrigins).allowedMethods(allowedMethods); + } +} diff --git a/backend/src/main/java/rocks/inspectit/gepard/agentmanager/configuration/controller/ConfigurationController.java b/backend/src/main/java/rocks/inspectit/gepard/agentmanager/configuration/controller/ConfigurationController.java new file mode 100644 index 00000000..2c8b5e97 --- /dev/null +++ b/backend/src/main/java/rocks/inspectit/gepard/agentmanager/configuration/controller/ConfigurationController.java @@ -0,0 +1,40 @@ +/* (C) 2024 */ +package rocks.inspectit.gepard.agentmanager.configuration.controller; + +import io.swagger.v3.oas.annotations.Operation; +import jakarta.validation.Valid; +import java.util.Objects; +import lombok.RequiredArgsConstructor; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; +import rocks.inspectit.gepard.agentmanager.configuration.model.InspectitConfiguration; +import rocks.inspectit.gepard.agentmanager.configuration.service.ConfigurationService; + +@RestController +@RequestMapping("/api/v1/agent-configuration") +@RequiredArgsConstructor +public class ConfigurationController { + + private final ConfigurationService configurationService; + + @GetMapping + @Operation(summary = "Get the agent configuration.") + public ResponseEntity getAgentConfiguration() { + InspectitConfiguration configuration = configurationService.getConfiguration(); + + // No config available + if (Objects.isNull(configuration)) { + return ResponseEntity.noContent().build(); + } + + return ResponseEntity.ok().body(configuration); + } + + @PutMapping + @Operation(summary = "Update the agent configuration.") + public ResponseEntity updateAgentConfiguration( + @Valid @RequestBody InspectitConfiguration configuration) { + configurationService.updateConfiguration(configuration); + return ResponseEntity.ok().build(); + } +} diff --git a/backend/src/main/java/rocks/inspectit/gepard/agentmanager/configuration/model/InspectitConfiguration.java b/backend/src/main/java/rocks/inspectit/gepard/agentmanager/configuration/model/InspectitConfiguration.java new file mode 100644 index 00000000..521e34d2 --- /dev/null +++ b/backend/src/main/java/rocks/inspectit/gepard/agentmanager/configuration/model/InspectitConfiguration.java @@ -0,0 +1,17 @@ +/* (C) 2024 */ +package rocks.inspectit.gepard.agentmanager.configuration.model; + +import jakarta.validation.Valid; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import rocks.inspectit.gepard.agentmanager.configuration.model.instrumentation.InstrumentationConfiguration; + +/** Model of an inspectit gepard configuration. */ +@NoArgsConstructor +@AllArgsConstructor +@Getter +public class InspectitConfiguration { + + @Valid private InstrumentationConfiguration instrumentation = new InstrumentationConfiguration(); +} diff --git a/backend/src/main/java/rocks/inspectit/gepard/agentmanager/configuration/model/instrumentation/InstrumentationConfiguration.java b/backend/src/main/java/rocks/inspectit/gepard/agentmanager/configuration/model/instrumentation/InstrumentationConfiguration.java new file mode 100644 index 00000000..93962f88 --- /dev/null +++ b/backend/src/main/java/rocks/inspectit/gepard/agentmanager/configuration/model/instrumentation/InstrumentationConfiguration.java @@ -0,0 +1,21 @@ +/* (C) 2024 */ +package rocks.inspectit.gepard.agentmanager.configuration.model.instrumentation; + +import jakarta.validation.Valid; +import jakarta.validation.constraints.NotNull; +import java.util.List; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; + +/** + * The Instrumentation Configuration contains all configuration related to instrumentation. e.g + * scopes, rules, actions. + */ +@NoArgsConstructor +@AllArgsConstructor +@Getter +public class InstrumentationConfiguration { + + @Valid private List<@NotNull Scope> scopes = List.of(); +} diff --git a/backend/src/main/java/rocks/inspectit/gepard/agentmanager/configuration/model/instrumentation/Scope.java b/backend/src/main/java/rocks/inspectit/gepard/agentmanager/configuration/model/instrumentation/Scope.java new file mode 100644 index 00000000..ce1a6c9c --- /dev/null +++ b/backend/src/main/java/rocks/inspectit/gepard/agentmanager/configuration/model/instrumentation/Scope.java @@ -0,0 +1,24 @@ +/* (C) 2024 */ +package rocks.inspectit.gepard.agentmanager.configuration.model.instrumentation; + +import jakarta.validation.constraints.NotNull; +import java.util.List; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; + +/** + * Represents a scope in the instrumentation configuration. A scope defines a set of methods which + * should be instrumented. + */ +@NoArgsConstructor +@AllArgsConstructor +@Getter +public class Scope { + + @NotNull(message = "Fqn is missing.") private String fqn; + + private List<@NotNull String> methods = List.of(); + + private boolean enabled = false; +} diff --git a/backend/src/main/java/rocks/inspectit/gepard/agentmanager/configuration/service/ConfigurationService.java b/backend/src/main/java/rocks/inspectit/gepard/agentmanager/configuration/service/ConfigurationService.java new file mode 100644 index 00000000..80436a54 --- /dev/null +++ b/backend/src/main/java/rocks/inspectit/gepard/agentmanager/configuration/service/ConfigurationService.java @@ -0,0 +1,19 @@ +/* (C) 2024 */ +package rocks.inspectit.gepard.agentmanager.configuration.service; + +import org.springframework.stereotype.Service; +import rocks.inspectit.gepard.agentmanager.configuration.model.InspectitConfiguration; + +@Service +public class ConfigurationService { + + private volatile InspectitConfiguration inspectitConfiguration; + + public InspectitConfiguration getConfiguration() { + return inspectitConfiguration; + } + + public void updateConfiguration(InspectitConfiguration configuration) { + inspectitConfiguration = configuration; + } +} diff --git a/backend/src/main/java/rocks/inspectit/gepard/agentmanager/connection/controller/ConnectionController.java b/backend/src/main/java/rocks/inspectit/gepard/agentmanager/connection/controller/ConnectionController.java new file mode 100644 index 00000000..569085c7 --- /dev/null +++ b/backend/src/main/java/rocks/inspectit/gepard/agentmanager/connection/controller/ConnectionController.java @@ -0,0 +1,51 @@ +/* (C) 2024 */ +package rocks.inspectit.gepard.agentmanager.connection.controller; + +import io.swagger.v3.oas.annotations.Operation; +import jakarta.validation.Valid; +import java.util.List; +import java.util.UUID; +import lombok.RequiredArgsConstructor; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; +import org.springframework.web.servlet.support.ServletUriComponentsBuilder; +import rocks.inspectit.gepard.agentmanager.connection.model.Connection; +import rocks.inspectit.gepard.agentmanager.connection.model.dto.ConnectionDto; +import rocks.inspectit.gepard.agentmanager.connection.model.dto.CreateConnectionRequest; +import rocks.inspectit.gepard.agentmanager.connection.service.ConnectionService; + +/** + * Controller for handling agent connection requests. Holds the POST endpoint for handling + * connection requests from agents and the GET endpoints for fetching all or one connection by id. + */ +@RestController +@RequestMapping("/api/v1/connections") +@RequiredArgsConstructor +public class ConnectionController { + + private final ConnectionService connectionService; + + @PostMapping + @Operation(summary = "Connect an agent to the agent manager.") + public ResponseEntity connect(@Valid @RequestBody CreateConnectionRequest connectRequest) { + Connection connection = connectionService.handleConnectRequest(connectRequest); + return ResponseEntity.created( + ServletUriComponentsBuilder.fromCurrentRequest() + .path("/{id}") + .buildAndExpand(connection.getId()) + .toUri()) + .build(); + } + + @GetMapping + @Operation(summary = "Get all connections.") + public ResponseEntity> getConnections() { + return ResponseEntity.ok(connectionService.getConnections()); + } + + @GetMapping("/{id}") + @Operation(summary = "Get a connection by id.") + public ResponseEntity getConnection(@PathVariable UUID id) { + return ResponseEntity.ok(connectionService.getConnection(id)); + } +} diff --git a/backend/src/main/java/rocks/inspectit/gepard/agentmanager/connection/model/Connection.java b/backend/src/main/java/rocks/inspectit/gepard/agentmanager/connection/model/Connection.java new file mode 100644 index 00000000..3ca6b3a2 --- /dev/null +++ b/backend/src/main/java/rocks/inspectit/gepard/agentmanager/connection/model/Connection.java @@ -0,0 +1,28 @@ +/* (C) 2024 */ +package rocks.inspectit.gepard.agentmanager.connection.model; + +import java.time.LocalDateTime; +import java.util.UUID; +import lombok.*; +import rocks.inspectit.gepard.agentmanager.agent.model.Agent; + +/** + * Represents a connected agent. It is an internal data structure and not exposed to the API. Acts + * as Aggregate Root. + */ +@AllArgsConstructor +@NoArgsConstructor +@Getter +@Setter +@ToString +public class Connection { + + /** The id of the connection. */ + private UUID id; + + /** The registration time * */ + private LocalDateTime registrationTime; + + /** The agent which is connected. */ + private Agent agent; +} diff --git a/backend/src/main/java/rocks/inspectit/gepard/agentmanager/connection/model/dto/ConnectionDto.java b/backend/src/main/java/rocks/inspectit/gepard/agentmanager/connection/model/dto/ConnectionDto.java new file mode 100644 index 00000000..e50e6aa9 --- /dev/null +++ b/backend/src/main/java/rocks/inspectit/gepard/agentmanager/connection/model/dto/ConnectionDto.java @@ -0,0 +1,31 @@ +/* (C) 2024 */ +package rocks.inspectit.gepard.agentmanager.connection.model.dto; + +import jakarta.validation.constraints.NotNull; +import java.time.LocalDateTime; +import java.util.UUID; +import rocks.inspectit.gepard.agentmanager.connection.model.Connection; + +/** Represents a connection response. */ +public record ConnectionDto( + @NotNull(message = "ID missing.") UUID id, + @NotNull(message = "Registration Time missing.") LocalDateTime registrationTime, + @NotNull(message = "Service Name missing.") String serviceName, + @NotNull(message = "Gepard Version missing.") String gepardVersion, + @NotNull(message = "Open-Telemetry Version missing.") String otelVersion, + @NotNull(message = "Process ID is missing.") Long pid, + @NotNull(message = "Start-Time missing.") Long startTime, + @NotNull(message = "Java Version missing.") String javaVersion) { + + public static ConnectionDto fromConnection(Connection connection) { + return new ConnectionDto( + connection.getId(), + connection.getRegistrationTime(), + connection.getAgent().getServiceName(), + connection.getAgent().getGepardVersion(), + connection.getAgent().getOtelVersion(), + connection.getAgent().getPid(), + connection.getAgent().getStartTime().toEpochMilli(), + connection.getAgent().getJavaVersion()); + } +} diff --git a/backend/src/main/java/rocks/inspectit/gepard/agentmanager/connection/model/dto/CreateConnectionRequest.java b/backend/src/main/java/rocks/inspectit/gepard/agentmanager/connection/model/dto/CreateConnectionRequest.java new file mode 100644 index 00000000..b9f2f66d --- /dev/null +++ b/backend/src/main/java/rocks/inspectit/gepard/agentmanager/connection/model/dto/CreateConnectionRequest.java @@ -0,0 +1,32 @@ +/* (C) 2024 */ +package rocks.inspectit.gepard.agentmanager.connection.model.dto; + +import jakarta.validation.constraints.NotNull; +import java.time.Instant; +import java.time.LocalDateTime; +import java.util.UUID; +import rocks.inspectit.gepard.agentmanager.agent.model.Agent; +import rocks.inspectit.gepard.agentmanager.connection.model.Connection; + +/** Represents a connection request from an agent. */ +public record CreateConnectionRequest( + @NotNull(message = "Service Name missing.") String serviceName, + @NotNull(message = "Gepard Version missing.") String gepardVersion, + @NotNull(message = "Open-Telemetry Version missing.") String otelVersion, + @NotNull(message = "Process ID is missing.") Long pid, + @NotNull(message = "Start-Time missing.") Long startTime, + @NotNull(message = "Java Version missing.") String javaVersion) { + + public static Connection toConnection(CreateConnectionRequest createConnectionRequest) { + return new Connection( + UUID.randomUUID(), + LocalDateTime.now(), + new Agent( + createConnectionRequest.serviceName, + createConnectionRequest.pid, + createConnectionRequest.gepardVersion, + createConnectionRequest.otelVersion, + Instant.ofEpochMilli(createConnectionRequest.startTime), + createConnectionRequest.javaVersion)); + } +} diff --git a/backend/src/main/java/rocks/inspectit/gepard/agentmanager/connection/service/ConnectionService.java b/backend/src/main/java/rocks/inspectit/gepard/agentmanager/connection/service/ConnectionService.java new file mode 100644 index 00000000..697cf741 --- /dev/null +++ b/backend/src/main/java/rocks/inspectit/gepard/agentmanager/connection/service/ConnectionService.java @@ -0,0 +1,57 @@ +/* (C) 2024 */ +package rocks.inspectit.gepard.agentmanager.connection.service; + +import java.util.List; +import java.util.NoSuchElementException; +import java.util.UUID; +import java.util.concurrent.ConcurrentHashMap; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; +import rocks.inspectit.gepard.agentmanager.connection.model.Connection; +import rocks.inspectit.gepard.agentmanager.connection.model.dto.ConnectionDto; +import rocks.inspectit.gepard.agentmanager.connection.model.dto.CreateConnectionRequest; + +/** Service-Implementation for handling agent connection requests. */ +@Slf4j +@Service +@RequiredArgsConstructor +public class ConnectionService { + + private final ConcurrentHashMap connectionCache = new ConcurrentHashMap<>(); + + /** + * Handles a connection request from an agent. + * + * @param connectRequest The request for the new connection to be created. + * @return Connection The response containing all saved information. + */ + public Connection handleConnectRequest(CreateConnectionRequest connectRequest) { + Connection connection = CreateConnectionRequest.toConnection(connectRequest); + connectionCache.put(connection.getId(), connection); + + return connection; + } + + /** + * Returns all connections in the cache. + * + * @return List All connections from the cache. + */ + public List getConnections() { + return connectionCache.values().stream().map(ConnectionDto::fromConnection).toList(); + } + + /** + * Returns a connection from the cache by its id. + * + * @param id The id of the connection. + * @return ConnectionDto The connection. + */ + public ConnectionDto getConnection(UUID id) { + if (!connectionCache.containsKey(id)) { + throw new NoSuchElementException("No connection with id " + id + " found in cache."); + } + return ConnectionDto.fromConnection(connectionCache.get(id)); + } +} diff --git a/backend/src/main/java/rocks/inspectit/gepard/agentmanager/exception/ApiError.java b/backend/src/main/java/rocks/inspectit/gepard/agentmanager/exception/ApiError.java new file mode 100644 index 00000000..858cc036 --- /dev/null +++ b/backend/src/main/java/rocks/inspectit/gepard/agentmanager/exception/ApiError.java @@ -0,0 +1,8 @@ +/* (C) 2024 */ +package rocks.inspectit.gepard.agentmanager.exception; + +import java.time.LocalDateTime; +import java.util.List; + +/** Represents an error that occurred during an API request. */ +public record ApiError(String path, List errors, int statusCode, LocalDateTime timestamp) {} diff --git a/backend/src/main/java/rocks/inspectit/gepard/agentmanager/exception/GlobalExceptionHandler.java b/backend/src/main/java/rocks/inspectit/gepard/agentmanager/exception/GlobalExceptionHandler.java new file mode 100644 index 00000000..b122c50d --- /dev/null +++ b/backend/src/main/java/rocks/inspectit/gepard/agentmanager/exception/GlobalExceptionHandler.java @@ -0,0 +1,86 @@ +/* (C) 2024 */ +package rocks.inspectit.gepard.agentmanager.exception; + +import jakarta.servlet.http.HttpServletRequest; +import java.time.LocalDateTime; +import java.util.List; +import java.util.NoSuchElementException; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.http.converter.HttpMessageNotReadableException; +import org.springframework.validation.FieldError; +import org.springframework.web.bind.MethodArgumentNotValidException; +import org.springframework.web.bind.annotation.ControllerAdvice; +import org.springframework.web.bind.annotation.ExceptionHandler; +import org.springframework.web.method.annotation.MethodArgumentTypeMismatchException; + +/** Global exception handler for the application. */ +@ControllerAdvice +public class GlobalExceptionHandler { + + /** + * Handles MethodArgumentNotValidException (e.g. if a NotNull-Field isn´t present). + * + * @param ex the exception + * @return the response entity + */ + @ExceptionHandler(MethodArgumentNotValidException.class) + public ResponseEntity handleValidationErrors( + MethodArgumentNotValidException ex, HttpServletRequest request) { + List errors = + ex.getBindingResult().getFieldErrors().stream().map(FieldError::getDefaultMessage).toList(); + + ApiError apiError = + new ApiError( + request.getRequestURI(), errors, HttpStatus.BAD_REQUEST.value(), LocalDateTime.now()); + + return new ResponseEntity<>(apiError, HttpStatus.BAD_REQUEST); + } + + /** + * Handles HttpMessageNotReadableException (e.g. if no request body is provided). + * + * @param ex the exception + * @return the response entity + */ + @ExceptionHandler({HttpMessageNotReadableException.class}) + public ResponseEntity handleBadRequestError( + HttpMessageNotReadableException ex, HttpServletRequest request) { + ApiError apiError = + new ApiError( + request.getRequestURI(), + List.of(ex.getMessage()), + HttpStatus.BAD_REQUEST.value(), + LocalDateTime.now()); + return new ResponseEntity<>(apiError, HttpStatus.BAD_REQUEST); + } + + /** + * Handles MethodArgumentTypeMismatchException (e.g. if a path variable is not of the correct + * type). + */ + @ExceptionHandler(MethodArgumentTypeMismatchException.class) + public ResponseEntity handleMethodArgumentTypeMismatch( + MethodArgumentTypeMismatchException ex, HttpServletRequest request) { + ApiError apiError = + new ApiError( + request.getRequestURI(), + List.of(ex.getMessage()), + HttpStatus.BAD_REQUEST.value(), + LocalDateTime.now()); + return new ResponseEntity<>(apiError, HttpStatus.BAD_REQUEST); + } + + /** Handles NoSuchElementException (e.g. if an entity is not found). */ + @ExceptionHandler(NoSuchElementException.class) + public ResponseEntity handleNotFoundError( + NoSuchElementException ex, HttpServletRequest request) { + ApiError apiError = + new ApiError( + request.getRequestURI(), + List.of(ex.getMessage()), + HttpStatus.NOT_FOUND.value(), + LocalDateTime.now()); + return new ResponseEntity<>(apiError, HttpStatus.NOT_FOUND); + } +} diff --git a/backend/src/main/resources/application.yaml b/backend/src/main/resources/application.yaml new file mode 100644 index 00000000..732f5d4c --- /dev/null +++ b/backend/src/main/resources/application.yaml @@ -0,0 +1,38 @@ +spring: + application: + name: backend +# ssl: +# bundle: +# jks: +# server: +# key: +# alias: "igc-dev" +# keystore: +# location: "classpath:ssl/igc-dev.p12" +# password: "password" +# type: "PKCS12" + +server: + port: 8080 +# ssl: +# bundle: "server" +# enabled-protocols: "TLSv1.3" + +management: + server: + port: 9090 + endpoints: + web: + exposure: + include: "openapi, swagger-ui" + +springdoc: + show-actuator: true + use-management-port: true + +inspectit-config-server: + security: + cors: + path-pattern: "/**" + allowed-origins: "*" + allowed-methods: "*" \ No newline at end of file diff --git a/backend/src/main/resources/ssl/igc-dev.p12 b/backend/src/main/resources/ssl/igc-dev.p12 new file mode 100644 index 00000000..a69439e0 Binary files /dev/null and b/backend/src/main/resources/ssl/igc-dev.p12 differ diff --git a/backend/src/test/java/rocks/inspectit/gepard/agentmanager/configuration/controller/ConfigurationControllerTest.java b/backend/src/test/java/rocks/inspectit/gepard/agentmanager/configuration/controller/ConfigurationControllerTest.java new file mode 100644 index 00000000..dabc3b64 --- /dev/null +++ b/backend/src/test/java/rocks/inspectit/gepard/agentmanager/configuration/controller/ConfigurationControllerTest.java @@ -0,0 +1,71 @@ +/* (C) 2024 */ +package rocks.inspectit.gepard.agentmanager.configuration.controller; + +import static org.mockito.Mockito.when; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +import com.fasterxml.jackson.databind.ObjectMapper; +import java.util.List; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; +import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.http.MediaType; +import org.springframework.test.web.servlet.MockMvc; +import rocks.inspectit.gepard.agentmanager.configuration.model.InspectitConfiguration; +import rocks.inspectit.gepard.agentmanager.configuration.model.instrumentation.InstrumentationConfiguration; +import rocks.inspectit.gepard.agentmanager.configuration.model.instrumentation.Scope; +import rocks.inspectit.gepard.agentmanager.configuration.service.ConfigurationService; + +@WebMvcTest(controllers = ConfigurationController.class) +class ConfigurationControllerTest { + + @Autowired private MockMvc mockMvc; + + @Autowired private ObjectMapper objectMapper; + + @MockBean private ConfigurationService configurationService; + + @Test + void getConfiguration_whenNoConfigAvailable_shouldReturnNoContent() throws Exception { + + when(configurationService.getConfiguration()).thenReturn(null); + + mockMvc.perform(get("/api/v1/agent-configuration")).andExpect(status().isNoContent()); + } + + @Test + void getConfiguration_whenConfigAvailable_shouldReturnOk() throws Exception { + when(configurationService.getConfiguration()).thenReturn(new InspectitConfiguration()); + + mockMvc + .perform(get("/api/v1/agent-configuration")) + .andExpect(status().isOk()) + .andExpect(content().contentType(MediaType.APPLICATION_JSON)) + .andExpect(content().json(objectMapper.writeValueAsString(new InspectitConfiguration()))); + } + + @Test + void updateConfiguration_shouldReturnOkAndConfiguration() throws Exception { + + InspectitConfiguration configuration = createConfiguration(); + + when(configurationService.getConfiguration()).thenReturn(configuration); + + mockMvc + .perform( + get("/api/v1/agent-configuration") + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(configuration))) + .andExpect(status().isOk()); + } + + private InspectitConfiguration createConfiguration() { + Scope scope = new Scope("org.test.package", List.of("testMethod"), true); + InstrumentationConfiguration instrumentationConfiguration = + new InstrumentationConfiguration(List.of(scope)); + return new InspectitConfiguration(instrumentationConfiguration); + } +} diff --git a/backend/src/test/java/rocks/inspectit/gepard/agentmanager/connection/controller/ConnectionControllerTest.java b/backend/src/test/java/rocks/inspectit/gepard/agentmanager/connection/controller/ConnectionControllerTest.java new file mode 100644 index 00000000..a26a8da4 --- /dev/null +++ b/backend/src/test/java/rocks/inspectit/gepard/agentmanager/connection/controller/ConnectionControllerTest.java @@ -0,0 +1,96 @@ +/* (C) 2024 */ +package rocks.inspectit.gepard.agentmanager.connection.controller; + +import static org.mockito.Mockito.when; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; + +import com.fasterxml.jackson.databind.ObjectMapper; +import java.time.Instant; +import java.time.LocalDateTime; +import java.util.UUID; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; +import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.http.MediaType; +import org.springframework.test.web.servlet.MockMvc; +import rocks.inspectit.gepard.agentmanager.connection.model.Connection; +import rocks.inspectit.gepard.agentmanager.connection.model.dto.ConnectionDto; +import rocks.inspectit.gepard.agentmanager.connection.model.dto.CreateConnectionRequest; +import rocks.inspectit.gepard.agentmanager.connection.service.ConnectionService; + +@WebMvcTest(controllers = ConnectionController.class) +class ConnectionControllerTest { + + @Autowired private MockMvc mockMvc; + + @Autowired private ObjectMapper objectMapper; + + @MockBean private ConnectionService connectionService; + + @Test + void connect_whenFieldIsMissing_shouldReturnBadRequest() throws Exception { + String requestBody = + """ + { + "serviceName": "customer-service-e", + "gepardVersion: "0.0.1", + "otelVersion": "1.26.8" + + } + """; + + mockMvc + .perform( + post("/api/v1/connections") + .contentType(MediaType.APPLICATION_JSON) + .content(requestBody)) + .andExpect(status().isBadRequest()); + } + + @Test + void connect_whenEverythingIsValid_shouldReturnOk() throws Exception { + CreateConnectionRequest createConnectionRequest = + new CreateConnectionRequest( + "customer-service-e", "0.0.1", "1.26.8", 67887L, Instant.now().toEpochMilli(), "22"); + + Connection connection = CreateConnectionRequest.toConnection(createConnectionRequest); + when(connectionService.handleConnectRequest(createConnectionRequest)).thenReturn(connection); + + mockMvc + .perform( + post("/api/v1/connections") + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(createConnectionRequest))) + .andExpect(status().isCreated()) + .andExpect(header().exists("Location")) + .andExpect( + header() + .string("Location", "http://localhost/api/v1/connections/" + connection.getId())); + } + + @Test + void get_connections_whenEverythingIsValid_shouldReturnOk() throws Exception { + mockMvc + .perform(get("/api/v1/connections")) + .andExpect(status().isOk()) + .andExpect(content().contentType(MediaType.APPLICATION_JSON)); + } + + @Test + void get_connection_whenEverythingIsValid_shouldReturnOk() throws Exception { + UUID uuid = UUID.randomUUID(); + ConnectionDto connectionDto = + new ConnectionDto( + uuid, LocalDateTime.now(), "service name", "5", "7", 42L, 123456789L, "22"); + when(connectionService.getConnection(uuid)).thenReturn(connectionDto); + + mockMvc + .perform(get("/api/v1/connections/{id}", uuid)) + .andExpect(status().isOk()) + .andExpect(content().contentType(MediaType.APPLICATION_JSON)) + .andExpect(content().json(objectMapper.writeValueAsString(connectionDto))); + } +} diff --git a/backend/src/test/java/rocks/inspectit/gepard/agentmanager/connection/service/ConnectionServiceTest.java b/backend/src/test/java/rocks/inspectit/gepard/agentmanager/connection/service/ConnectionServiceTest.java new file mode 100644 index 00000000..8a029171 --- /dev/null +++ b/backend/src/test/java/rocks/inspectit/gepard/agentmanager/connection/service/ConnectionServiceTest.java @@ -0,0 +1,85 @@ +/* (C) 2024 */ +package rocks.inspectit.gepard.agentmanager.connection.service; + +import static org.junit.jupiter.api.Assertions.*; + +import java.time.Instant; +import java.util.List; +import java.util.NoSuchElementException; +import java.util.UUID; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.junit.jupiter.MockitoExtension; +import rocks.inspectit.gepard.agentmanager.connection.model.Connection; +import rocks.inspectit.gepard.agentmanager.connection.model.dto.ConnectionDto; +import rocks.inspectit.gepard.agentmanager.connection.model.dto.CreateConnectionRequest; + +@ExtendWith(MockitoExtension.class) +class ConnectionServiceTest { + + @InjectMocks private ConnectionService connectionService; + + @Test + void testHandleConnectRequest() { + CreateConnectionRequest createConnectionRequest = + new CreateConnectionRequest( + "customer-service-e", "0.0.1", "1.26.8", 67887L, Instant.now().toEpochMilli(), "22"); + + Connection response = connectionService.handleConnectRequest(createConnectionRequest); + + assertEquals( + createConnectionRequest.startTime(), response.getAgent().getStartTime().toEpochMilli()); + assertEquals(createConnectionRequest.javaVersion(), response.getAgent().getJavaVersion()); + assertEquals(createConnectionRequest.otelVersion(), response.getAgent().getOtelVersion()); + assertEquals(createConnectionRequest.gepardVersion(), response.getAgent().getGepardVersion()); + assertEquals(createConnectionRequest.pid(), response.getAgent().getPid()); + assertEquals(createConnectionRequest.serviceName(), response.getAgent().getServiceName()); + assertNotNull(response.getId()); + } + + @Test + void testGetConnections() { + CreateConnectionRequest createConnectionRequest = + new CreateConnectionRequest( + "customer-service-e", "0.0.1", "1.26.8", 67887L, Instant.now().toEpochMilli(), "22"); + connectionService.handleConnectRequest(createConnectionRequest); + connectionService.handleConnectRequest(createConnectionRequest); + + List response = connectionService.getConnections(); + + assertEquals(2, response.size()); + } + + @Test + void testGetConnectionsEmptyResult() { + List response = connectionService.getConnections(); + assertEquals(0, response.size()); + } + + @Test + void testGetConnection() { + CreateConnectionRequest createConnectionRequest = + new CreateConnectionRequest( + "customer-service-e", "0.0.1", "1.26.8", 67887L, Instant.now().toEpochMilli(), "22"); + Connection connection = connectionService.handleConnectRequest(createConnectionRequest); + + ConnectionDto connectionDto = connectionService.getConnection(connection.getId()); + + assertEquals(connection.getId(), connectionDto.id()); + assertEquals(createConnectionRequest.startTime(), connectionDto.startTime()); + assertEquals(createConnectionRequest.javaVersion(), connectionDto.javaVersion()); + assertEquals(createConnectionRequest.otelVersion(), connectionDto.otelVersion()); + assertEquals(createConnectionRequest.gepardVersion(), connectionDto.gepardVersion()); + assertEquals(createConnectionRequest.pid(), connectionDto.pid()); + assertEquals(createConnectionRequest.serviceName(), connectionDto.serviceName()); + } + + @Test + void testGetConnectionNotFound() { + UUID id = UUID.randomUUID(); + Exception exception = + assertThrows(NoSuchElementException.class, () -> connectionService.getConnection(id)); + assertEquals("No connection with id " + id + " found in cache.", exception.getMessage()); + } +} diff --git a/backend/src/test/java/rocks/inspectit/gepard/agentmanager/exception/GlobalExceptionHandlerTest.java b/backend/src/test/java/rocks/inspectit/gepard/agentmanager/exception/GlobalExceptionHandlerTest.java new file mode 100644 index 00000000..bbc29ccc --- /dev/null +++ b/backend/src/test/java/rocks/inspectit/gepard/agentmanager/exception/GlobalExceptionHandlerTest.java @@ -0,0 +1,88 @@ +/* (C) 2024 */ +package rocks.inspectit.gepard.agentmanager.exception; + +import static org.junit.jupiter.api.Assertions.*; + +import jakarta.servlet.http.HttpServletRequest; +import java.util.List; +import java.util.NoSuchElementException; +import java.util.Objects; +import org.junit.jupiter.api.Test; +import org.mockito.Mockito; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.http.converter.HttpMessageNotReadableException; +import org.springframework.validation.BindingResult; +import org.springframework.validation.FieldError; +import org.springframework.web.bind.MethodArgumentNotValidException; +import org.springframework.web.method.annotation.MethodArgumentTypeMismatchException; + +class GlobalExceptionHandlerTest { + + private final GlobalExceptionHandler globalExceptionHandler = new GlobalExceptionHandler(); + + @Test + void handleValidationErrors() { + MethodArgumentNotValidException exception = Mockito.mock(MethodArgumentNotValidException.class); + BindingResult bindingResult = Mockito.mock(BindingResult.class); + HttpServletRequest httpServletRequest = Mockito.mock(HttpServletRequest.class); + Mockito.when(httpServletRequest.getRequestURI()).thenReturn("requestURI"); + Mockito.when(exception.getBindingResult()).thenReturn(bindingResult); + + Mockito.when(bindingResult.getFieldErrors()) + .thenReturn(List.of(new FieldError("requestURI", "fieldError", "field error"))); + + ResponseEntity response = + globalExceptionHandler.handleValidationErrors(exception, httpServletRequest); + + assertEquals(List.of("field error"), Objects.requireNonNull(response.getBody()).errors()); + assertEquals("requestURI", response.getBody().path()); + assertEquals(HttpStatus.BAD_REQUEST, response.getStatusCode()); + } + + @Test + void handleBadRequestError() { + HttpMessageNotReadableException exception = Mockito.mock(HttpMessageNotReadableException.class); + HttpServletRequest httpServletRequest = Mockito.mock(HttpServletRequest.class); + Mockito.when(httpServletRequest.getRequestURI()).thenReturn("requestURI"); + Mockito.when(exception.getMessage()).thenReturn("exception message"); + + ResponseEntity response = + globalExceptionHandler.handleBadRequestError(exception, httpServletRequest); + + assertEquals(List.of("exception message"), Objects.requireNonNull(response.getBody()).errors()); + assertEquals("requestURI", response.getBody().path()); + assertEquals(HttpStatus.BAD_REQUEST, response.getStatusCode()); + } + + @Test + void handleMethodArgumentTypeMismatch() { + MethodArgumentTypeMismatchException exception = + Mockito.mock(MethodArgumentTypeMismatchException.class); + HttpServletRequest httpServletRequest = Mockito.mock(HttpServletRequest.class); + Mockito.when(httpServletRequest.getRequestURI()).thenReturn("requestURI"); + Mockito.when(exception.getMessage()).thenReturn("exception message"); + + ResponseEntity response = + globalExceptionHandler.handleMethodArgumentTypeMismatch(exception, httpServletRequest); + + assertEquals(List.of("exception message"), Objects.requireNonNull(response.getBody()).errors()); + assertEquals("requestURI", response.getBody().path()); + assertEquals(HttpStatus.BAD_REQUEST, response.getStatusCode()); + } + + @Test + void handleNotFoundError() { + NoSuchElementException exception = Mockito.mock(NoSuchElementException.class); + HttpServletRequest httpServletRequest = Mockito.mock(HttpServletRequest.class); + Mockito.when(httpServletRequest.getRequestURI()).thenReturn("requestURI"); + Mockito.when(exception.getMessage()).thenReturn("exception message"); + + ResponseEntity response = + globalExceptionHandler.handleNotFoundError(exception, httpServletRequest); + + assertEquals(List.of("exception message"), Objects.requireNonNull(response.getBody()).errors()); + assertEquals("requestURI", response.getBody().path()); + assertEquals(HttpStatus.NOT_FOUND, response.getStatusCode()); + } +} diff --git a/build.gradle b/build.gradle new file mode 100644 index 00000000..e4443129 --- /dev/null +++ b/build.gradle @@ -0,0 +1,7 @@ +defaultTasks 'build' + +wrapper { + description "Regenerates the Gradle Wrapper files" + gradleVersion = '8.7' + distributionUrl = "http://services.gradle.org/distributions/gradle-${gradleVersion}-all.zip" +} diff --git a/agentmanager/frontend/src/Api.js b/frontend/src/Api.js similarity index 85% rename from agentmanager/frontend/src/Api.js rename to frontend/src/Api.js index 601376ce..11a0eb9d 100644 --- a/agentmanager/frontend/src/Api.js +++ b/frontend/src/Api.js @@ -1,6 +1,6 @@ import axios from 'axios' -const SERVER_URL = 'http://localhost:8080/api/v1/'; +const SERVER_URL = 'https://localhost:8080/api/v1/'; const instance = axios.create({ baseURL: SERVER_URL, @@ -11,7 +11,7 @@ export default { // (C)reate // createNew: (text, completed) => instance.post('agents', {title: text, completed: completed}), // (R)ead - getAll: () => instance.get('agents', { + getAll: () => instance.get('connections', { transformResponse: [function (data) { return data? JSON.parse(response.data) : data; }] diff --git a/agentmanager/frontend/src/README.md b/frontend/src/README.md similarity index 100% rename from agentmanager/frontend/src/README.md rename to frontend/src/README.md diff --git a/agentmanager/frontend/src/app.vue b/frontend/src/app.vue similarity index 100% rename from agentmanager/frontend/src/app.vue rename to frontend/src/app.vue diff --git a/agentmanager/frontend/src/assets/css/main.css b/frontend/src/assets/css/main.css similarity index 100% rename from agentmanager/frontend/src/assets/css/main.css rename to frontend/src/assets/css/main.css diff --git a/agentmanager/frontend/src/components/AgentDashboard.vue b/frontend/src/components/AgentDashboard.vue similarity index 100% rename from agentmanager/frontend/src/components/AgentDashboard.vue rename to frontend/src/components/AgentDashboard.vue diff --git a/agentmanager/frontend/src/components/AgentDetailHeader.vue b/frontend/src/components/AgentDetailHeader.vue similarity index 87% rename from agentmanager/frontend/src/components/AgentDetailHeader.vue rename to frontend/src/components/AgentDetailHeader.vue index 2a1f3cce..45962866 100644 --- a/agentmanager/frontend/src/components/AgentDetailHeader.vue +++ b/frontend/src/components/AgentDetailHeader.vue @@ -6,7 +6,7 @@

Agent
-
{{ agent.name }}
+
{{ agent.serviceName }}

@@ -29,13 +29,15 @@ export default { }, setup() { const route = useRoute(); - const agentName = route.params.name; + const agentId = route.params.id; const agentsStore = useAgentsStore(); - const agent = agentsStore.getAgentByName(agentName); + console.log(agentId); + + const agent = agentsStore.getAgentById(agentId) return { - agentName, + agentId, agent, }; }, diff --git a/agentmanager/frontend/src/components/AgentList.vue b/frontend/src/components/AgentList.vue similarity index 87% rename from agentmanager/frontend/src/components/AgentList.vue rename to frontend/src/components/AgentList.vue index 484f708f..da3a11b8 100644 --- a/agentmanager/frontend/src/components/AgentList.vue +++ b/frontend/src/components/AgentList.vue @@ -25,9 +25,9 @@ - + {{ - agent.name + agent.serviceName }} @@ -36,13 +36,13 @@ }} {{ - agent.javaversion + agent.javaVersion }} - {{ agent.otelversion }} + {{ agent.otelVersion }} - Details > + Details > @@ -83,7 +83,7 @@ export default { store.clear() try { - const response = await fetch('http://localhost:8080/api/v1/agents'); + const response = await fetch('https://localhost:8080/api/v1/connections'); const data = await response.json(); data.forEach(agent => { store.addAgent(agent); @@ -93,11 +93,11 @@ export default { } this.agents = store.agents.map(agent => ({ - // id: agent.id, - name: agent.name, + id: agent.id, + serviceName: agent.serviceName, health: agent.healthState !== null ? agent.healthState : 'missed', - javaversion: agent.javaversion, - otelversion: agent.otelversion + javaVersion: agent.javaVersion, + otelVersion: agent.otelVersion })); }, }, diff --git a/agentmanager/frontend/src/components/AgentSummary.vue b/frontend/src/components/AgentSummary.vue similarity index 91% rename from agentmanager/frontend/src/components/AgentSummary.vue rename to frontend/src/components/AgentSummary.vue index 3a3bc7ef..4bf9a2c5 100644 --- a/agentmanager/frontend/src/components/AgentSummary.vue +++ b/frontend/src/components/AgentSummary.vue @@ -19,7 +19,7 @@ Otelversion: -
{{ agent.otelversion }}
+
{{ agent.otelVersion }}
@@ -28,7 +28,7 @@
- +
@@ -54,9 +54,9 @@ export default { setup() { const route = useRoute(); - const agentName = route.params.name; + const agentId = route.params.id; const agentsStore = useAgentsStore(); - const agent = agentsStore.getAgentByName(agentName); + const agent = agentsStore.getAgentById(agentId); const environments = { Dev: 'text-gray-400 bg-gray-400/10 ring-gray-400/20', @@ -70,7 +70,7 @@ export default { const deployment = agent ? (agent.deployment || defaultDeployment) : defaultDeployment; return { - agentName, + agentId, agent, environments, deployment, diff --git a/agentmanager/frontend/src/components/AgentUpdate.vue b/frontend/src/components/AgentUpdate.vue similarity index 100% rename from agentmanager/frontend/src/components/AgentUpdate.vue rename to frontend/src/components/AgentUpdate.vue diff --git a/agentmanager/frontend/src/components/ExternFileHandler.vue b/frontend/src/components/ExternFileHandler.vue similarity index 100% rename from agentmanager/frontend/src/components/ExternFileHandler.vue rename to frontend/src/components/ExternFileHandler.vue diff --git a/agentmanager/frontend/src/components/SidebarHeader.vue b/frontend/src/components/SidebarHeader.vue similarity index 100% rename from agentmanager/frontend/src/components/SidebarHeader.vue rename to frontend/src/components/SidebarHeader.vue diff --git a/agentmanager/frontend/src/components/Stats.vue b/frontend/src/components/Stats.vue similarity index 100% rename from agentmanager/frontend/src/components/Stats.vue rename to frontend/src/components/Stats.vue diff --git a/agentmanager/frontend/src/components/UpdateFeed.vue b/frontend/src/components/UpdateFeed.vue similarity index 100% rename from agentmanager/frontend/src/components/UpdateFeed.vue rename to frontend/src/components/UpdateFeed.vue diff --git a/agentmanager/frontend/src/components/YamlPreviewer.vue b/frontend/src/components/YamlPreviewer.vue similarity index 100% rename from agentmanager/frontend/src/components/YamlPreviewer.vue rename to frontend/src/components/YamlPreviewer.vue diff --git a/agentmanager/frontend/src/modals/EditModal.vue b/frontend/src/modals/EditModal.vue similarity index 100% rename from agentmanager/frontend/src/modals/EditModal.vue rename to frontend/src/modals/EditModal.vue diff --git a/agentmanager/frontend/src/nuxt.config.ts b/frontend/src/nuxt.config.ts similarity index 100% rename from agentmanager/frontend/src/nuxt.config.ts rename to frontend/src/nuxt.config.ts diff --git a/agentmanager/frontend/src/package-lock.json b/frontend/src/package-lock.json similarity index 100% rename from agentmanager/frontend/src/package-lock.json rename to frontend/src/package-lock.json diff --git a/agentmanager/frontend/src/package.json b/frontend/src/package.json similarity index 100% rename from agentmanager/frontend/src/package.json rename to frontend/src/package.json diff --git a/agentmanager/frontend/src/pages/agents/[name].vue b/frontend/src/pages/agents/[id].vue similarity index 90% rename from agentmanager/frontend/src/pages/agents/[name].vue rename to frontend/src/pages/agents/[id].vue index d8a42df3..3447b799 100644 --- a/agentmanager/frontend/src/pages/agents/[name].vue +++ b/frontend/src/pages/agents/[id].vue @@ -35,13 +35,13 @@ export default { }, setup() { const route = useRoute(); - const agentName = route.params.name; + const agentId = route.params.id; const agentsStore = useAgentsStore(); - const agent = agentsStore.getAgentByName(agentName); + const agent = agentsStore.getAgentById(agentId); return { - agentName, + agentId, agent, }; }, diff --git a/agentmanager/frontend/src/pages/index.vue b/frontend/src/pages/index.vue similarity index 100% rename from agentmanager/frontend/src/pages/index.vue rename to frontend/src/pages/index.vue diff --git a/agentmanager/frontend/src/public/gepardLogo.png b/frontend/src/public/gepardLogo.png similarity index 100% rename from agentmanager/frontend/src/public/gepardLogo.png rename to frontend/src/public/gepardLogo.png diff --git a/agentmanager/frontend/src/public/gepardLogo.webp b/frontend/src/public/gepardLogo.webp similarity index 100% rename from agentmanager/frontend/src/public/gepardLogo.webp rename to frontend/src/public/gepardLogo.webp diff --git a/agentmanager/frontend/src/server/tsconfig.json b/frontend/src/server/tsconfig.json similarity index 100% rename from agentmanager/frontend/src/server/tsconfig.json rename to frontend/src/server/tsconfig.json diff --git a/agentmanager/frontend/src/stores/Agentstore.js b/frontend/src/stores/Agentstore.js similarity index 77% rename from agentmanager/frontend/src/stores/Agentstore.js rename to frontend/src/stores/Agentstore.js index 22bff66e..d4d51fe9 100644 --- a/agentmanager/frontend/src/stores/Agentstore.js +++ b/frontend/src/stores/Agentstore.js @@ -13,8 +13,8 @@ export const useAgentsStore = defineStore({ clear() { this.agents = [] }, - getAgentByName(name) { - return this.agents.find(agent => agent.name === name); + getAgentById(id) { + return this.agents.find(agent => agent.id === id); }, } }); diff --git a/agentmanager/frontend/src/stores/FileStore.js b/frontend/src/stores/FileStore.js similarity index 100% rename from agentmanager/frontend/src/stores/FileStore.js rename to frontend/src/stores/FileStore.js diff --git a/agentmanager/frontend/src/tailwind.config.js b/frontend/src/tailwind.config.js similarity index 100% rename from agentmanager/frontend/src/tailwind.config.js rename to frontend/src/tailwind.config.js diff --git a/agentmanager/frontend/src/tsconfig.json b/frontend/src/tsconfig.json similarity index 100% rename from agentmanager/frontend/src/tsconfig.json rename to frontend/src/tsconfig.json diff --git a/agentmanager/frontend/src/yarn.lock b/frontend/src/yarn.lock similarity index 100% rename from agentmanager/frontend/src/yarn.lock rename to frontend/src/yarn.lock diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 00000000..d64cd491 Binary files /dev/null and b/gradle/wrapper/gradle-wrapper.jar differ diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 00000000..b82aa23a --- /dev/null +++ b/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,7 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip +networkTimeout=10000 +validateDistributionUrl=true +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/gradlew b/gradlew new file mode 100755 index 00000000..1aa94a42 --- /dev/null +++ b/gradlew @@ -0,0 +1,249 @@ +#!/bin/sh + +# +# Copyright © 2015-2021 the original authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +############################################################################## +# +# Gradle start up script for POSIX generated by Gradle. +# +# Important for running: +# +# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is +# noncompliant, but you have some other compliant shell such as ksh or +# bash, then to run this script, type that shell name before the whole +# command line, like: +# +# ksh Gradle +# +# Busybox and similar reduced shells will NOT work, because this script +# requires all of these POSIX shell features: +# * functions; +# * expansions «$var», «${var}», «${var:-default}», «${var+SET}», +# «${var#prefix}», «${var%suffix}», and «$( cmd )»; +# * compound commands having a testable exit status, especially «case»; +# * various built-in commands including «command», «set», and «ulimit». +# +# Important for patching: +# +# (2) This script targets any POSIX shell, so it avoids extensions provided +# by Bash, Ksh, etc; in particular arrays are avoided. +# +# The "traditional" practice of packing multiple parameters into a +# space-separated string is a well documented source of bugs and security +# problems, so this is (mostly) avoided, by progressively accumulating +# options in "$@", and eventually passing that to Java. +# +# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, +# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; +# see the in-line comments for details. +# +# There are tweaks for specific operating systems such as AIX, CygWin, +# Darwin, MinGW, and NonStop. +# +# (3) This script is generated from the Groovy template +# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# within the Gradle project. +# +# You can find Gradle at https://github.com/gradle/gradle/. +# +############################################################################## + +# Attempt to set APP_HOME + +# Resolve links: $0 may be a link +app_path=$0 + +# Need this for daisy-chained symlinks. +while + APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path + [ -h "$app_path" ] +do + ls=$( ls -ld "$app_path" ) + link=${ls#*' -> '} + case $link in #( + /*) app_path=$link ;; #( + *) app_path=$APP_HOME$link ;; + esac +done + +# This is normally unused +# shellcheck disable=SC2034 +APP_BASE_NAME=${0##*/} +# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036) +APP_HOME=$( cd "${APP_HOME:-./}" > /dev/null && pwd -P ) || exit + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD=maximum + +warn () { + echo "$*" +} >&2 + +die () { + echo + echo "$*" + echo + exit 1 +} >&2 + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "$( uname )" in #( + CYGWIN* ) cygwin=true ;; #( + Darwin* ) darwin=true ;; #( + MSYS* | MINGW* ) msys=true ;; #( + NONSTOP* ) nonstop=true ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + + +# Determine the Java command to use to start the JVM. +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 + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD=java + if ! command -v java >/dev/null 2>&1 + then + die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +fi + +# Increase the maximum file descriptors if we can. +if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then + case $MAX_FD in #( + max*) + # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC2039,SC3045 + MAX_FD=$( ulimit -H -n ) || + warn "Could not query maximum file descriptor limit" + esac + case $MAX_FD in #( + '' | soft) :;; #( + *) + # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC2039,SC3045 + ulimit -n "$MAX_FD" || + warn "Could not set maximum file descriptor limit to $MAX_FD" + esac +fi + +# Collect all arguments for the java command, stacking in reverse order: +# * args from the command line +# * the main class name +# * -classpath +# * -D...appname settings +# * --module-path (only if needed) +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. + +# For Cygwin or MSYS, switch paths to Windows format before running java +if "$cygwin" || "$msys" ; then + APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) + CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) + + JAVACMD=$( cygpath --unix "$JAVACMD" ) + + # Now convert the arguments - kludge to limit ourselves to /bin/sh + for arg do + if + case $arg in #( + -*) false ;; # don't mess with options #( + /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath + [ -e "$t" ] ;; #( + *) false ;; + esac + then + arg=$( cygpath --path --ignore --mixed "$arg" ) + fi + # Roll the args list around exactly as many times as the number of + # args, so each arg winds up back in the position where it started, but + # possibly modified. + # + # NB: a `for` loop captures its iteration list before it begins, so + # changing the positional parameters here affects neither the number of + # iterations, nor the values presented in `arg`. + shift # remove old arg + set -- "$@" "$arg" # push replacement arg + done +fi + + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Collect all arguments for the java command: +# * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments, +# and any embedded shellness will be escaped. +# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be +# treated as '${Hostname}' itself on the command line. + +set -- \ + "-Dorg.gradle.appname=$APP_BASE_NAME" \ + -classpath "$CLASSPATH" \ + org.gradle.wrapper.GradleWrapperMain \ + "$@" + +# Stop when "xargs" is not available. +if ! command -v xargs >/dev/null 2>&1 +then + die "xargs is not available" +fi + +# Use "xargs" to parse quoted args. +# +# With -n1 it outputs one arg per line, with the quotes and backslashes removed. +# +# In Bash we could simply go: +# +# readarray ARGS < <( xargs -n1 <<<"$var" ) && +# set -- "${ARGS[@]}" "$@" +# +# but POSIX shell has neither arrays nor command substitution, so instead we +# post-process each arg (as a line of input to sed) to backslash-escape any +# character that might be a shell metacharacter, then use eval to reverse +# that process (while maintaining the separation between arguments), and wrap +# the whole thing up as a single "set" statement. +# +# This will of course break if any of these variables contains a newline or +# an unmatched quote. +# + +eval "set -- $( + printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | + xargs -n1 | + sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | + tr '\n' ' ' + )" '"$@"' + +exec "$JAVACMD" "$@" diff --git a/gradlew.bat b/gradlew.bat new file mode 100644 index 00000000..6689b85b --- /dev/null +++ b/gradlew.bat @@ -0,0 +1,92 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + +@if "%DEBUG%"=="" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%"=="" set DIRNAME=. +@rem This is normally unused +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if %ERRORLEVEL% equ 0 goto execute + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto execute + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* + +:end +@rem End local scope for the variables with windows NT shell +if %ERRORLEVEL% equ 0 goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +set EXIT_CODE=%ERRORLEVEL% +if %EXIT_CODE% equ 0 set EXIT_CODE=1 +if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% +exit /b %EXIT_CODE% + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/http/connections.http b/http/connections.http new file mode 100644 index 00000000..9c36d779 --- /dev/null +++ b/http/connections.http @@ -0,0 +1,41 @@ +# This is a collection of Http-Requests to the connections ressource. +# It can be used with IntelliJs HttpClient to do End-to-End-Testing of the Config-Server Endpoints. +# Run the server and just start the collection. + +### GET request to retrieve all connections +GET https://localhost:8080/api/v1/connections + +### POST request to connections without fields + +POST https://127.0.0.1:8080/api/v1/connections + +### POST request to connections with missing fields + POST https://127.0.0.1:8080/api/v1/connections +Content-Type: application/json + +{ + "javaVersion": "17", + "gepardVersion": "0.0.1-SNAPSHOT", + "startTime": 1719850971, + "pid": 432423 + +} + +### POST request to connections with all fields + POST https://127.0.0.1:8080/api/v1/connections +Content-Type: application/json + +{ + "serviceName": "test-service", + "otelVersion": "1.2.5", + "javaVersion": "17", + "gepardVersion": "0.0.1-SNAPSHOT", + "startTime": 1719850971, + "pid": 432423 +} + +> {% client.global.set("response_redirect", response.headers.valueOf('Location')); %} + +### GET request to connections/{id} +GET {{response_redirect}} + diff --git a/settings.gradle b/settings.gradle new file mode 100644 index 00000000..10da18cb --- /dev/null +++ b/settings.gradle @@ -0,0 +1,5 @@ +rootProject.name = 'inspectit-gepard-agentmanager' +include 'backend' +// Switch between the two client side projects. +// include 'client' +include 'frontend' \ No newline at end of file