diff --git a/.gitignore b/.gitignore index 524f096..69a95bd 100644 --- a/.gitignore +++ b/.gitignore @@ -22,3 +22,7 @@ # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml hs_err_pid* replay_pid* + +# Idea + +.idea/ diff --git a/LICENSE b/LICENSE index b737d16..6689009 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2024 Yaroslav Svitlytskyi +Copyright (c) 2022 YarikRevich Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..4e699bd --- /dev/null +++ b/Makefile @@ -0,0 +1,135 @@ +dev := $(or $(dev), 'false') + +ifneq (,$(wildcard .env)) +include .env +export +endif + +.PHONY: help +.DEFAULT_GOAL := help +help: + @grep -h -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}' + +.PHONY: clean +clean: ## Clean project area + @mvn clean + +.PHONY: prepare +prepare: ## Install prerequisites + @mvn org.apache.maven.plugins:maven-dependency-plugin:3.6.0:tree -Dverbose=true + +.PHONY: test +test: clean ## Run both unit and integration tests + @mvn test + @mvn verify + +.PHONY: lint +lint: ## Run Apache Spotless linter + @mvn spotless:apply + +.PHONY: create-local-client +create-local-client: ## Create ResourceTracker local directory for client + @mkdir -p $(HOME)/.repoachiever/config + +.PHONY: create-local-api-server +create-local-api-server: ## Create ResourceTracker local directory for API Server + @mkdir -p $(HOME)/.repoachiever/config + @mkdir -p $(HOME)/.repoachiever/diagnostics/prometheus/internal + @mkdir -p $(HOME)/.repoachiever/diagnostics/prometheus/config + @mkdir -p $(HOME)/.repoachiever/diagnostics/grafana/internal + @mkdir -p $(HOME)/.repoachiever/diagnostics/grafana/config/dashboards + @mkdir -p $(HOME)/.repoachiever/diagnostics/grafana/config/datasources + @mkdir -p $(HOME)/.repoachiever/workspace + @mkdir -p $(HOME)/.repoachiever/internal/database + @mkdir -p $(HOME)/.repoachiever/internal/state + +.PHONY: clone-client-config +clone-client-config: ## Clone configuration files to local directory + @cp -r ./samples/config/client/user.yaml $(HOME)/.repoachiever/config + +.PHONY: clone-api-server-config +clone-api-server-config: ## Clone RepoAchiever API Server configuration files to local directory + @cp -r ./config/grafana/dashboards/dashboard.yml $(HOME)/.repoachiever/diagnostics/grafana/config/dashboards + @cp -r ./config/grafana/dashboards/diagnostics.tmpl $(HOME)/.repoachiever/diagnostics/grafana/config/dashboards + @cp -r ./config/grafana/datasources/datasource.tmpl $(HOME)/.repoachiever/diagnostics/grafana/config/datasources + @cp -r ./config/prometheus/prometheus.tmpl $(HOME)/.repoachiever/diagnostics/prometheus/config + @cp -r ./samples/config/api-server/api-server.yaml $(HOME)/.repoachiever/config + +.PHONY: clone-worker +clone-worker: ## Clone Worker JAR into a RepoAchiever local directory +ifeq (,$(wildcard $(HOME)/.repoachiever/bin/worker)) + @mkdir -p $(HOME)/.repoachiever/bin +endif + @cp -r ./bin/worker $(HOME)/.repoachiever/bin/ + +.PHONY: clone-cluster +clone-cluster: ## Clone Cluster JAR into a RepoAchiever local directory +ifeq (,$(wildcard $(HOME)/.repoachiever/bin/cluster)) + @mkdir -p $(HOME)/.repoachiever/bin +endif + @cp -r ./bin/cluster $(HOME)/.repoachiever/bin/ + +.PHONY: clone-api-server +clone-api-server: ## Clone API Server JAR into a RepoAchiever local directory +ifeq (,$(wildcard $(HOME)/.repoachiever/bin/api-server)) + @mkdir -p $(HOME)/.repoachiever/bin +endif + @cp -r ./bin/api-server $(HOME)/.repoachiever/bin/ + +.PHONY: build-worker +build-worker: clean ## Build Worker application +ifneq (,$(wildcard ./bin/worker)) + @rm -r ./bin/worker +endif +ifeq ($(dev), 'false') + @mvn -pl worker -T10 install +else + @mvn -P dev -pl worker -T10 install +endif + $(MAKE) clone-worker + +.PHONY: build-cluster +build-cluster: clean ## Build Cluster application +ifneq (,$(wildcard ./bin/cluster)) + @rm -r ./bin/cluster +endif +ifeq ($(dev), 'false') + @mvn -pl cluster -T10 install +else + @mvn -P dev -pl cluster -T10 install +endif + $(MAKE) clone-cluster + +.PHONY: build-api-server +build-api-server: clean create-local-api-server clone-api-server-config ## Build API Server application +ifneq (,$(wildcard ./bin/api-server)) + @rm -r ./bin/api-server +endif +ifeq ($(dev), 'false') + @mvn -pl api-server -T10 install +else + @mvn -P dev -pl api-server -T10 install +endif + $(MAKE) clone-api-server + +.PHONY: build-cli +build-cli: clean create-local-client clone-client-config ## Build CLI application +ifneq (,$(wildcard ./bin/cli)) + @rm -r ./bin/cli +endif +ifeq ($(dev), 'false') + @mvn -pl cli -T10 install +else + @mvn -P dev -pl cli -T10 install +endif + +.PHONY: build-gui +build-gui: clean create-local-client clone-client-config create-local-api-server build-api-server ## Build GUI application +ifneq (,$(wildcard ./bin/gui)) + @rm -r ./bin/gui +endif +ifeq ($(dev), 'false') + @mvn -pl gui -T10 install +else + @mvn -P dev -pl gui -T10 install +endif \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..2d3476b --- /dev/null +++ b/README.md @@ -0,0 +1,88 @@ +# RepoAchiever + +[![Build](https://github.com/YarikRevich/ResourceTracker/actions/workflows/build.yml/badge.svg)](https://github.com/YarikRevich/ResourceTracker/actions/workflows/build.yml) +![Linux](https://img.shields.io/badge/Linux-FCC624?style=for-the-badge&logo=linux&logoColor=black) +![MacOS](https://img.shields.io/badge/MacOS-8773f5?style=for-the-badge&logo=macos&logoColor=black) +[![StandWithUkraine](https://raw.githubusercontent.com/vshymanskyy/StandWithUkraine/main/badges/StandWithUkraine.svg)](https://github.com/vshymanskyy/StandWithUkraine/blob/main/docs/README.md) + +## General Information + +A tool used for remote Git repositories achieving operations. + +![](./docs/high-level-design.png) + +![](./docs/detailed-design.png) + +![](./docs/workspace-design.png) + +RepoAchiever uses server-side data validation. It means, that on client side no data transformation or further analysis are made. +Only API Server calls are on client side responsibility. + +## Setup + +All setup related operations are processed via **Makefile** placed in the root directory. + +### CLI + +In order to build CLI it's required to execute the following command. Initially it cleans the environment and builds Java project using **Maven** +```shell +make build-cli +``` + +After the execution of command given above the executable will be generated and placed into **bin** folder in the root directory of the project + +**CLI** build automatically places default **user.yaml** configuration file into ~/.resourcetracker/config directory. + +### GUI + +In order to build GUI it's required to execute the following command. Initially it cleans the environment and build Java project using **Maven** +```shell +make build-gui +``` + +After the execution of command given above the executable will be generated and placed into **bin** folder in the root directory of the project + +**GUI** build automatically compiles **API Server** and places both executable JAR and other dependencies into **~/.resourcetracker/bin/api-server** directory + +It's highly recommended not to move **API Server** files from the default local directory + +### API Server + +In order to build **API Server** it's required to execute the following command. Initially it cleans the environment and build Java project using **Maven** +```shell +make build-api-server +``` + +After the execution of command given above the executable will be generated and placed into **bin** folder in the root directory of the project + +## Use cases + +For both **CLI** and **GUI** examples, there was used the following user configuration file: +```yaml +requests: + - name: "first" + frequency: "10 * * * * *" + file: "/Volumes/Files/first.sh" +cloud: + provider: "aws" + credentials: + file: "/Volumes/Files/aws.csv" + region: "us-west-2" +api-server: + host: "http://localhost:8080" +``` + +And the following request script file: +```shell +#!/bin/bash + +echo "Hello world!" +``` + +### CLI + +![cli](./docs/example/cli.gif) + +### GUI + +![gui](./docs/example/gui.gif) \ No newline at end of file diff --git a/api-server/api-server.iml b/api-server/api-server.iml new file mode 100644 index 0000000..258b38a --- /dev/null +++ b/api-server/api-server.iml @@ -0,0 +1,9 @@ + + + + + + + + + \ No newline at end of file diff --git a/api-server/pom.xml b/api-server/pom.xml new file mode 100644 index 0000000..ee77cba --- /dev/null +++ b/api-server/pom.xml @@ -0,0 +1,317 @@ + + + 4.0.0 + api-server + 1.0-SNAPSHOT + api-server + API Server for RepoAchiever + + + com.repoachiever + base + 1.0-SNAPSHOT + + + + + + Shell-Command-Executor-Lib + Shell-Command-Executor-Lib + system + ${basedir}/../lib/Shell-Command-Executor-Lib-0.5.0-SNAPSHOT.jar + + + + + io.quarkus + quarkus-rest-client-jackson + + + io.quarkus + quarkus-rest-client + + + io.quarkus + quarkus-resteasy-reactive-jackson + + + io.quarkus + quarkus-micrometer-registry-prometheus + + + io.quarkus + quarkus-vertx-http + + + io.quarkus + quarkus-vertx-http-deployment + + + io.quarkus + quarkus-smallrye-openapi + + + io.quarkus + quarkus-smallrye-health + + + io.quarkus + quarkus-arc + + + io.quarkus + quarkus-junit5 + + + io.quarkus + quarkus-hibernate-validator + + + io.quarkus + quarkus-liquibase + + + io.quarkus + quarkus-hibernate-orm-panache + + + io.quarkus + quarkus-agroal + + + io.quarkiverse.jdbc + quarkus-jdbc-sqlite + + + + + org.springframework + spring-core + + + + + io.pebbletemplates + pebble + + + org.jboss + jandex + + + org.jboss.logging + jboss-logging + + + jakarta.json + jakarta.json-api + + + jakarta.activation + jakarta.activation-api + + + jakarta.transaction + jakarta.transaction-api + + + jakarta.servlet + jakarta.servlet-api + + + jakarta.annotation + jakarta.annotation-api + + + jakarta.validation + jakarta.validation-api + + + jakarta.ws.rs + jakarta.ws.rs-api + + + org.projectlombok + lombok + + + org.osgi + org.osgi.annotation + + + org.yaml + snakeyaml + + + com.fasterxml.jackson.dataformat + jackson-dataformat-yaml + + + commons-io + commons-io + + + com.opencsv + opencsv + + + org.jetbrains + annotations + + + + + org.freemarker + freemarker + + + + + org.apache.logging.log4j + log4j-api + + + org.apache.logging.log4j + log4j-core + + + org.hibernate.validator + hibernate-validator + + + + + api-server + + + io.quarkus.platform + quarkus-maven-plugin + + + org.apache.maven.plugins + maven-compiler-plugin + + + org.apache.maven.plugins + maven-surefire-plugin + + + org.apache.maven.plugins + maven-failsafe-plugin + + + com.diffplug.spotless + spotless-maven-plugin + + + org.openapitools + openapi-generator-maven-plugin + + + + generate + + + ${project.basedir}/../api-server/src/main/openapi/openapi.yml + jaxrs-spec + quarkus + ${default.package}.api + ${default.package}.model + true + false + false + false + false + false + true + false + + @lombok.Data @lombok.NoArgsConstructor @lombok.AllArgsConstructor(staticName = "of") + src/main/java + true + true + true + true + true + false + true + java8 + true + true + + + + + + + com.coderplus.maven.plugins + copy-rename-maven-plugin + + + + ${basedir}/target/quarkus-app/quarkus-run.jar + ${main.basedir}/../bin/api-server/api-server.jar + + + + + + pl.project13.maven + git-commit-id-plugin + + + org.codehaus.mojo + exec-maven-plugin + + + copy-quarkus-lib + package + + exec + + + cp + + -r + ${basedir}/target/quarkus-app/lib + ${main.basedir}/../bin/api-server/lib + + + + + copy-quarkus-quarkus + package + + exec + + + cp + + -r + ${basedir}/target/quarkus-app/quarkus + ${main.basedir}/../bin/api-server/quarkus + + + + + copy-quarkus-app + package + + exec + + + cp + + -r + ${basedir}/target/quarkus-app/app + ${main.basedir}/../bin/api-server/app + + + + + + + + diff --git a/api-server/src/main/java/com/repoachiever/converter/ClusterContextToJsonConverter.java b/api-server/src/main/java/com/repoachiever/converter/ClusterContextToJsonConverter.java new file mode 100644 index 0000000..dee85cc --- /dev/null +++ b/api-server/src/main/java/com/repoachiever/converter/ClusterContextToJsonConverter.java @@ -0,0 +1,31 @@ +package com.repoachiever.converter; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.DeserializationFeature; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.repoachiever.entity.common.ClusterContextEntity; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +/** + * Represents RepoAchiever Cluster context entity to JSON converter. + */ +public class ClusterContextToJsonConverter { + private static final Logger logger = LogManager.getLogger(ClusterContextToJsonConverter.class); + + public static String convert(ClusterContextEntity content) { + ObjectMapper mapper = + new ObjectMapper() + .configure(DeserializationFeature.FAIL_ON_NULL_CREATOR_PROPERTIES, true) + .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, true) + .configure(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY, true); + + try { + return mapper.writeValueAsString(content); + } catch (JsonProcessingException e) { + logger.fatal(e.getMessage()); + } + + return null; + } +} diff --git a/api-server/src/main/java/com/repoachiever/converter/ContentCredentialsToClusterContextCredentialsConverter.java b/api-server/src/main/java/com/repoachiever/converter/ContentCredentialsToClusterContextCredentialsConverter.java new file mode 100644 index 0000000..ced666d --- /dev/null +++ b/api-server/src/main/java/com/repoachiever/converter/ContentCredentialsToClusterContextCredentialsConverter.java @@ -0,0 +1,18 @@ +package com.repoachiever.converter; + +import com.repoachiever.model.Provider; +import com.repoachiever.model.CredentialsFieldsExternal; +import com.repoachiever.entity.common.ClusterContextEntity; + +/** + * Represents RepoAchiever Cluster content credentials to context credentials converter. + */ +public class ContentCredentialsToClusterContextCredentialsConverter { + public static ClusterContextEntity.Service.Credentials convert( + Provider provider, CredentialsFieldsExternal credentialsFieldsExternal) { + return switch (provider) { + case LOCAL -> null; + case GITHUB -> ClusterContextEntity.Service.Credentials.of(credentialsFieldsExternal.getToken()); + }; + } +} \ No newline at end of file diff --git a/api-server/src/main/java/com/repoachiever/converter/ContentProviderToClusterContextProviderConverter.java b/api-server/src/main/java/com/repoachiever/converter/ContentProviderToClusterContextProviderConverter.java new file mode 100644 index 0000000..cf7c977 --- /dev/null +++ b/api-server/src/main/java/com/repoachiever/converter/ContentProviderToClusterContextProviderConverter.java @@ -0,0 +1,24 @@ +package com.repoachiever.converter; + +import com.repoachiever.entity.common.ClusterContextEntity; +import com.repoachiever.model.Provider; + +import java.util.Arrays; +import java.util.Objects; + +/** + * Represents RepoAchiever Cluster content provider to context provider converter. + */ +public class ContentProviderToClusterContextProviderConverter { + public static ClusterContextEntity.Service.Provider convert(Provider contentProvider) { + return ClusterContextEntity.Service.Provider.valueOf( + Arrays.stream( + ClusterContextEntity.Service.Provider.values()) + .toList() + .stream() + .filter(element -> Objects.equals(element.toString(), contentProvider.toString())) + .map(Enum::name) + .toList() + .get(0)); + } +} diff --git a/api-server/src/main/java/com/repoachiever/converter/HealthCheckResponseToReadinessCheckResult.java b/api-server/src/main/java/com/repoachiever/converter/HealthCheckResponseToReadinessCheckResult.java new file mode 100644 index 0000000..5619e82 --- /dev/null +++ b/api-server/src/main/java/com/repoachiever/converter/HealthCheckResponseToReadinessCheckResult.java @@ -0,0 +1,44 @@ +package com.repoachiever.converter; + +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.ObjectReader; +import com.repoachiever.model.ReadinessCheckResult; +import java.io.IOException; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.eclipse.microprofile.health.HealthCheckResponse; + +/** + * Represents health check response to readiness result converter. + */ +public class HealthCheckResponseToReadinessCheckResult { + private static final Logger logger = + LogManager.getLogger(HealthCheckResponseToReadinessCheckResult.class); + + /** + * Converts given health check response to readiness check result entity. + * + * @param content given content to be converted. + * @return converted readiness check result entity. + */ + public static ReadinessCheckResult convert(HealthCheckResponse content) { + ObjectMapper mapper = new ObjectMapper(); + + String contentRaw = null; + try { + contentRaw = mapper.writeValueAsString(content); + } catch (IOException e) { + logger.fatal(e.getMessage()); + } + + ObjectReader reader = mapper.reader().forType(new TypeReference() {}); + try { + return reader.readValues(contentRaw).readAll().getFirst(); + } catch (IOException e) { + logger.fatal(e.getMessage()); + } + + return null; + } +} diff --git a/api-server/src/main/java/com/repoachiever/dto/ClusterAllocationDto.java b/api-server/src/main/java/com/repoachiever/dto/ClusterAllocationDto.java new file mode 100644 index 0000000..9fb898d --- /dev/null +++ b/api-server/src/main/java/com/repoachiever/dto/ClusterAllocationDto.java @@ -0,0 +1,31 @@ +package com.repoachiever.dto; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +/** + * Represents RepoAchiever Cluster allocation. + */ +@Getter +@AllArgsConstructor(staticName = "of") +public class ClusterAllocationDto { + /** + * Represents name of RepoAchiever Cluster allocation. + */ + private String name; + + /** + * Represents process identificator of RepoAchiever Cluster allocation. + */ + private Integer pid; + + /** + * Represents context used for RepoAchiever Cluster configuration. + */ + private String context; + + /** + * Represents workspace unit key used to target RepoAchiever Cluster results. + */ + private String workspaceUnitKey; +} diff --git a/api-server/src/main/java/com/repoachiever/dto/CommandExecutorOutputDto.java b/api-server/src/main/java/com/repoachiever/dto/CommandExecutorOutputDto.java new file mode 100644 index 0000000..870ca95 --- /dev/null +++ b/api-server/src/main/java/com/repoachiever/dto/CommandExecutorOutputDto.java @@ -0,0 +1,15 @@ +package com.repoachiever.dto; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +/** + * Represents command executor output. + */ +@Getter +@AllArgsConstructor(staticName = "of") +public class CommandExecutorOutputDto { + private String normalOutput; + + private String errorOutput; +} diff --git a/api-server/src/main/java/com/repoachiever/dto/RepositoryContentUnitDto.java b/api-server/src/main/java/com/repoachiever/dto/RepositoryContentUnitDto.java new file mode 100644 index 0000000..dd9633f --- /dev/null +++ b/api-server/src/main/java/com/repoachiever/dto/RepositoryContentUnitDto.java @@ -0,0 +1,20 @@ +package com.repoachiever.dto; + +import com.repoachiever.entity.common.ClusterContextEntity; +import com.repoachiever.model.CredentialsFieldsFull; +import com.repoachiever.model.Provider; +import lombok.AllArgsConstructor; +import lombok.Getter; + +/** + * Represents repository content unit. + */ +@Getter +@AllArgsConstructor(staticName = "of") +public class RepositoryContentUnitDto { + private String location; + + private Provider provider; + + private CredentialsFieldsFull credentials; +} \ No newline at end of file diff --git a/api-server/src/main/java/com/repoachiever/entity/common/ClusterContextEntity.java b/api-server/src/main/java/com/repoachiever/entity/common/ClusterContextEntity.java new file mode 100644 index 0000000..7a8d1aa --- /dev/null +++ b/api-server/src/main/java/com/repoachiever/entity/common/ClusterContextEntity.java @@ -0,0 +1,145 @@ +package com.repoachiever.entity.common; + +import java.util.List; + +import com.fasterxml.jackson.annotation.JsonProperty; +import jakarta.validation.constraints.NotNull; +import lombok.AllArgsConstructor; +import lombok.Getter; + +/** + * Represents context sent to RepoAchiever Cluster as a variable during startup operation. + */ +@Getter +@AllArgsConstructor(staticName = "of") +public class ClusterContextEntity { + /** + * Contains metadata for a specific RepoAchiever Cluster allocation. + */ + @AllArgsConstructor(staticName = "of") + public static class Metadata { + @JsonProperty("name") + public String name; + + @JsonProperty("workspace_unit_key") + public String workspaceUnitKey; + } + + @JsonProperty("metadata") + public Metadata metadata; + + /** + * Represents filter section elected for a specific RepoAchiever Cluster allocation. + */ + @AllArgsConstructor(staticName = "of") + public static class Filter { + @JsonProperty("locations") + public List locations; + } + + @JsonProperty("filter") + public Filter filter; + + /** + * Represents external service configurations for RepoAchiever Cluster allocation used to retrieve content. + */ + @AllArgsConstructor(staticName = "of") + public static class Service { + /** + * Represents all supported service providers, which can be used by RepoAchiever Cluster allocation. + */ + public enum Provider { + LOCAL("git-local"), + GITHUB("git-github"); + + private final String value; + + Provider(String value) { + this.value = value; + } + + public String toString() { + return value; + } + } + + @JsonProperty("provider") + public Provider provider; + + /** + * Represents credentials used for external service communication by RepoAchiever Cluster allocation. + */ + @AllArgsConstructor(staticName = "of") + public static class Credentials { + @JsonProperty("token") + public String token; + } + + @JsonProperty("credentials") + public Credentials credentials; + } + + @JsonProperty("service") + public Service service; + + /** + * Represents RepoAchiever Cluster configuration used for internal communication infrastructure setup. + */ + @AllArgsConstructor(staticName = "of") + public static class Communication { + @NotNull + @JsonProperty("api_server_name") + public String apiServerName; + + @JsonProperty("port") + public Integer port; + } + + @JsonProperty("communication") + public Communication communication; + + /** + * Represents RepoAchiever Cluster configuration used for content management. + */ + @AllArgsConstructor(staticName = "of") + public static class Content { + @JsonProperty("format") + public String format; + } + + @JsonProperty("content") + public Content content; + + /** + * Represents RepoAchiever API Server resources configuration section. + */ + @AllArgsConstructor(staticName = "of") + public static class Resource { + /** + * Represents RepoAchiever API Server configuration used for RepoAchiever Cluster. + */ + @AllArgsConstructor(staticName = "of") + public static class Cluster { + @JsonProperty("max-workers") + public Integer maxWorkers; + } + + @JsonProperty("cluster") + public Cluster cluster; + + /** + * Represents RepoAchiever API Server configuration used for RepoAchiever Worker. + */ + @AllArgsConstructor(staticName = "of") + public static class Worker { + @JsonProperty("frequency") + public String frequency; + } + + @JsonProperty("worker") + public Worker worker; + } + + @JsonProperty("resource") + public Resource resource; +} \ No newline at end of file diff --git a/api-server/src/main/java/com/repoachiever/entity/common/ConfigEntity.java b/api-server/src/main/java/com/repoachiever/entity/common/ConfigEntity.java new file mode 100644 index 0000000..2410b66 --- /dev/null +++ b/api-server/src/main/java/com/repoachiever/entity/common/ConfigEntity.java @@ -0,0 +1,197 @@ +package com.repoachiever.entity.common; + +import com.fasterxml.jackson.annotation.JsonProperty; +import jakarta.enterprise.context.ApplicationScoped; +import jakarta.validation.Valid; +import jakarta.validation.constraints.NotNull; +import jakarta.validation.constraints.Pattern; +import lombok.Getter; + +/** + * Represents configuration model used for RepoAchiever API Server operations. + */ +@Getter +@ApplicationScoped +public class ConfigEntity { + /** + * Represents RepoAchiever API Server configuration used for RepoAchiever API Server instance setup. + */ + @Getter + public static class Connection { + @NotNull + @JsonProperty("port") + public Integer port; + } + + @Valid + @NotNull + @JsonProperty("connection") + public Connection connection; + + /** + * Represents RepoAchiever API Server configuration used for internal communication infrastructure setup. + */ + @Getter + public static class Communication { + @NotNull + @JsonProperty("port") + public Integer port; + } + + @Valid + @NotNull + @JsonProperty("communication") + public Communication communication; + + /** + * Represents RepoAchiever API Server configuration used for content management. + */ + @Getter + public static class Content { + @NotNull + @Pattern(regexp = "(^zip$)|(^tar$)") + @JsonProperty("format") + public String format; + } + + @Valid + @NotNull + @JsonProperty("content") + public Content content; + + /** + * Represents RepoAchiever API Server configuration used for database setup. + */ + @Getter + public static class Database { + @NotNull + @JsonProperty("re-init") + public Boolean reInit; + } + + @Valid + @NotNull + @JsonProperty("database") + public Database database; + + /** + * Represents RepoAchiever API Server configuration used for diagnostics. + */ + @Getter + public static class Diagnostics { + @NotNull + @JsonProperty("enabled") + public Boolean enabled; + + /** + * Represents RepoAchiever API Server metrics configuration setup. + */ + @Getter + public static class Metrics { + @NotNull + @JsonProperty("port") + public Integer port; + } + + @Valid + @NotNull + @JsonProperty("metrics") + public Metrics metrics; + + /** + * Represents RepoAchiever API Server configuration used for Grafana instance setup. + */ + @Getter + public static class Grafana { + @NotNull + @JsonProperty("port") + public Integer port; + } + + @Valid + @NotNull + @JsonProperty("grafana") + public Grafana grafana; + + /** + * Represents RepoAchiever API Server configuration used for Prometheus instance setup. + */ + @Getter + public static class Prometheus { + @NotNull + @JsonProperty("port") + public Integer port; + } + + @Valid + @NotNull + @JsonProperty("prometheus") + public Prometheus prometheus; + + /** + * Represents RepoAchiever API Server configuration used for Prometheus Node Exporter instance setup. + */ + @Getter + public static class NodeExporter { + @NotNull + @JsonProperty("port") + public Integer port; + } + + @Valid + @NotNull + @JsonProperty("node-exporter") + public NodeExporter nodeExporter; + } + + @Valid + @NotNull + @JsonProperty("diagnostics") + public Diagnostics diagnostics; + + /** + * Represents RepoAchiever API Server resources configuration section. + */ + @Getter + public static class Resource { + /** + * Represents RepoAchiever API Server configuration used for RepoAchiever Cluster. + */ + @Getter + public static class Cluster { + @NotNull + @JsonProperty("max-workers") + public Integer maxWorkers; + } + + @Valid + @NotNull + @JsonProperty("cluster") + public Cluster cluster; + + /** + * Represents RepoAchiever API Server configuration used for RepoAchiever Worker. + */ + @Getter + public static class Worker { + @NotNull + @Pattern( + regexp = + "(((([0-9]|[0-5][0-9])(-([0-9]|[0-5][0-9]))?,)*([0-9]|[0-5][0-9])(-([0-9]|[0-5][0-9]))?)|(([\\*]|[0-9]|[0-5][0-9])/([0-9]|[0-5][0-9]))|([\\?])|([\\*]))[\\s](((([0-9]|[0-5][0-9])(-([0-9]|[0-5][0-9]))?,)*([0-9]|[0-5][0-9])(-([0-9]|[0-5][0-9]))?)|(([\\*]|[0-9]|[0-5][0-9])/([0-9]|[0-5][0-9]))|([\\?])|([\\*]))[\\s](((([0-9]|[0-1][0-9]|[2][0-3])(-([0-9]|[0-1][0-9]|[2][0-3]))?,)*([0-9]|[0-1][0-9]|[2][0-3])(-([0-9]|[0-1][0-9]|[2][0-3]))?)|(([\\*]|[0-9]|[0-1][0-9]|[2][0-3])/([0-9]|[0-1][0-9]|[2][0-3]))|([\\?])|([\\*]))[\\s](((([1-9]|[0][1-9]|[1-2][0-9]|[3][0-1])(-([1-9]|[0][1-9]|[1-2][0-9]|[3][0-1]))?,)*([1-9]|[0][1-9]|[1-2][0-9]|[3][0-1])(-([1-9]|[0][1-9]|[1-2][0-9]|[3][0-1]))?(C)?)|(([1-9]|[0][1-9]|[1-2][0-9]|[3][0-1])/([1-9]|[0][1-9]|[1-2][0-9]|[3][0-1])(C)?)|(L(-[0-9])?)|(L(-[1-2][0-9])?)|(L(-[3][0-1])?)|(LW)|([1-9]W)|([1-3][0-9]W)|([\\?])|([\\*]))[\\s](((([1-9]|0[1-9]|1[0-2])(-([1-9]|0[1-9]|1[0-2]))?,)*([1-9]|0[1-9]|1[0-2])(-([1-9]|0[1-9]|1[0-2]))?)|(([1-9]|0[1-9]|1[0-2])/([1-9]|0[1-9]|1[0-2]))|(((JAN|FEB|MAR|APR|MAY|JUN|JUL|AUG|SEP|OCT|NOV|DEC)(-(JAN|FEB|MAR|APR|MAY|JUN|JUL|AUG|SEP|OCT|NOV|DEC))?,)*(JAN|FEB|MAR|APR|MAY|JUN|JUL|AUG|SEP|OCT|NOV|DEC)(-(JAN|FEB|MAR|APR|MAY|JUN|JUL|AUG|SEP|OCT|NOV|DEC))?)|((JAN|FEB|MAR|APR|MAY|JUN|JUL|AUG|SEP|OCT|NOV|DEC)/(JAN|FEB|MAR|APR|MAY|JUN|JUL|AUG|SEP|OCT|NOV|DEC))|([\\?])|([\\*]))[\\s]((([1-7](-([1-7]))?,)*([1-7])(-([1-7]))?)|([1-7]/([1-7]))|(((MON|TUE|WED|THU|FRI|SAT|SUN)(-(MON|TUE|WED|THU|FRI|SAT|SUN))?,)*(MON|TUE|WED|THU|FRI|SAT|SUN)(-(MON|TUE|WED|THU|FRI|SAT|SUN))?(C)?)|((MON|TUE|WED|THU|FRI|SAT|SUN)/(MON|TUE|WED|THU|FRI|SAT|SUN)(C)?)|(([1-7]|(MON|TUE|WED|THU|FRI|SAT|SUN))?(L|LW)?)|(([1-7]|MON|TUE|WED|THU|FRI|SAT|SUN)#([1-7])?)|([\\?])|([\\*]))([\\s]?(([\\*])?|(19[7-9][0-9])|(20[0-9][0-9]))?|" + + " (((19[7-9][0-9])|(20[0-9][0-9]))/((19[7-9][0-9])|(20[0-9][0-9])))?|" + + " ((((19[7-9][0-9])|(20[0-9][0-9]))(-((19[7-9][0-9])|(20[0-9][0-9])))?,)*((19[7-9][0-9])|(20[0-9][0-9]))(-((19[7-9][0-9])|(20[0-9][0-9])))?)?)") + @JsonProperty("frequency") + public String frequency; + } + + @Valid + @NotNull + @JsonProperty("worker") + public Worker worker; + } + + @Valid + @NotNull + @JsonProperty("resource") + public Resource resource; +} \ No newline at end of file diff --git a/api-server/src/main/java/com/repoachiever/entity/common/MetadataFileEntity.java b/api-server/src/main/java/com/repoachiever/entity/common/MetadataFileEntity.java new file mode 100644 index 0000000..330715c --- /dev/null +++ b/api-server/src/main/java/com/repoachiever/entity/common/MetadataFileEntity.java @@ -0,0 +1,20 @@ +package com.repoachiever.entity.common; + +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; + +/** + * Represents metadata file, including pull requests, issues and releases, which are pulled from selected resource provider. + */ +@Getter +@NoArgsConstructor +@AllArgsConstructor(staticName = "of") +public class MetadataFileEntity { + @JsonProperty("data") + private String data; + + @JsonProperty("hash") + private String hash; +} \ No newline at end of file diff --git a/api-server/src/main/java/com/repoachiever/entity/common/PropertiesEntity.java b/api-server/src/main/java/com/repoachiever/entity/common/PropertiesEntity.java new file mode 100644 index 0000000..24bbb98 --- /dev/null +++ b/api-server/src/main/java/com/repoachiever/entity/common/PropertiesEntity.java @@ -0,0 +1,166 @@ +package com.repoachiever.entity.common; + +import jakarta.enterprise.context.ApplicationScoped; +import lombok.Getter; +import org.apache.commons.lang3.StringUtils; +import org.eclipse.microprofile.config.inject.ConfigProperty; + +/** + * Exposes access to properties setup to be used for further configuration. + */ +@Getter +@ApplicationScoped +public class PropertiesEntity { + @ConfigProperty(name = "quarkus.application.version") + String applicationVersion; + + @ConfigProperty(name = "quarkus.http.host") + String applicationHost; + + @ConfigProperty(name = "quarkus.http.port") + Integer applicationPort; + + @ConfigProperty(name = "state.location") + String stateLocation; + + @ConfigProperty(name = "state.running.name") + String stateRunningName; + + @ConfigProperty(name = "database.tables.config.name") + String databaseConfigTableName; + + @ConfigProperty(name = "database.tables.content.name") + String databaseContentTableName; + + @ConfigProperty(name = "database.tables.provider.name") + String databaseProviderTableName; + + @ConfigProperty(name = "database.tables.secret.name") + String databaseSecretTableName; + + @ConfigProperty(name = "database.statement.close-delay") + Integer databaseStatementCloseDelay; + + @ConfigProperty(name = "bin.directory") + String binDirectory; + + @ConfigProperty(name = "bin.cluster.location") + String binClusterLocation; + + @ConfigProperty(name = "config.directory") + String configDirectory; + + @ConfigProperty(name = "config.name") + String configName; + + @ConfigProperty(name = "workspace.directory") + String workspaceDirectory; + + @ConfigProperty(name = "workspace.content.directory") + String workspaceContentDirectory; + + @ConfigProperty(name = "workspace.content.version-amount") + Integer workspaceContentVersionAmount; + + @ConfigProperty(name = "workspace.metadata.directory") + String workspaceMetadataDirectory; + + @ConfigProperty(name = "workspace.prs-metadata-file.name") + String workspacePRsMetadataFileName; + + @ConfigProperty(name = "workspace.issues-metadata-file.name") + String workspaceIssuesMetadataFileName; + + @ConfigProperty(name = "workspace.releases-metadata-file.name") + String workspaceReleasesMetadataFileName; + + @ConfigProperty(name = "repoachiever-cluster.context.alias") + String clusterContextAlias; + + @ConfigProperty(name = "communication.api-server.name") + String communicationApiServerName; + + @ConfigProperty(name = "communication.cluster.base") + String communicationClusterBase; + + @ConfigProperty(name = "communication.cluster.startup-await-frequency") + Integer communicationClusterStartupAwaitFrequency; + + @ConfigProperty(name = "communication.cluster.startup-timeout") + Integer communicationClusterStartupTimeout; + + @ConfigProperty(name = "communication.cluster.health-check-frequency") + Integer communicationClusterHealthCheckFrequency; + + @ConfigProperty(name = "diagnostics.common.docker.network.name") + String diagnosticsCommonDockerNetworkName; + + @ConfigProperty(name = "diagnostics.grafana.config.location") + String diagnosticsGrafanaConfigLocation; + + @ConfigProperty(name = "diagnostics.grafana.datasources.location") + String diagnosticsGrafanaDatasourcesLocation; + + @ConfigProperty(name = "diagnostics.grafana.datasources.template") + String diagnosticsGrafanaDatasourcesTemplate; + + @ConfigProperty(name = "diagnostics.grafana.datasources.output") + String diagnosticsGrafanaDatasourcesOutput; + + @ConfigProperty(name = "diagnostics.grafana.dashboards.location") + String diagnosticsGrafanaDashboardsLocation; + + @ConfigProperty(name = "diagnostics.grafana.dashboards.diagnostics.template") + String diagnosticsGrafanaDashboardsDiagnosticsTemplate; + + @ConfigProperty(name = "diagnostics.grafana.dashboards.diagnostics.output") + String diagnosticsGrafanaDashboardsDiagnosticsOutput; + + @ConfigProperty(name = "diagnostics.grafana.internal.location") + String diagnosticsGrafanaInternalLocation; + + @ConfigProperty(name = "diagnostics.grafana.docker.name") + String diagnosticsGrafanaDockerName; + + @ConfigProperty(name = "diagnostics.grafana.docker.image") + String diagnosticsGrafanaDockerImage; + + @ConfigProperty(name = "diagnostics.prometheus.config.location") + String diagnosticsPrometheusConfigLocation; + + @ConfigProperty(name = "diagnostics.prometheus.config.template") + String diagnosticsPrometheusConfigTemplate; + + @ConfigProperty(name = "diagnostics.prometheus.config.output") + String diagnosticsPrometheusConfigOutput; + + @ConfigProperty(name = "diagnostics.prometheus.internal.location") + String diagnosticsPrometheusInternalLocation; + + @ConfigProperty(name = "diagnostics.prometheus.docker.name") + String diagnosticsPrometheusDockerName; + + @ConfigProperty(name = "diagnostics.prometheus.docker.image") + String diagnosticsPrometheusDockerImage; + + @ConfigProperty(name = "diagnostics.prometheus.node-exporter.docker.name") + String diagnosticsPrometheusNodeExporterDockerName; + + @ConfigProperty(name = "diagnostics.prometheus.node-exporter.docker.image") + String diagnosticsPrometheusNodeExporterDockerImage; + + @ConfigProperty(name = "diagnostics.metrics.connection.timeout") + Integer diagnosticsMetricsConnectionTimeout; + + @ConfigProperty(name = "git.commit.id.abbrev") + String gitCommitId; + + /** + * Removes the last symbol in git commit id of the repository. + * + * @return chopped repository git commit id. + */ + public String getGitCommitId() { + return StringUtils.chop(gitCommitId); + } +} diff --git a/api-server/src/main/java/com/repoachiever/entity/repository/ConfigEntity.java b/api-server/src/main/java/com/repoachiever/entity/repository/ConfigEntity.java new file mode 100644 index 0000000..f615fdb --- /dev/null +++ b/api-server/src/main/java/com/repoachiever/entity/repository/ConfigEntity.java @@ -0,0 +1,22 @@ +package com.repoachiever.entity.repository; + +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.Id; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.Setter; + +/** + * Represents entity used to describe added configuration. + */ +@Getter +@AllArgsConstructor(staticName = "of") +public class ConfigEntity { + private Integer id; + + private String name; + + private String hash; +} diff --git a/api-server/src/main/java/com/repoachiever/entity/repository/ContentEntity.java b/api-server/src/main/java/com/repoachiever/entity/repository/ContentEntity.java new file mode 100644 index 0000000..df8acf5 --- /dev/null +++ b/api-server/src/main/java/com/repoachiever/entity/repository/ContentEntity.java @@ -0,0 +1,20 @@ +package com.repoachiever.entity.repository; + +import jakarta.persistence.*; +import lombok.AllArgsConstructor; +import lombok.Getter; + +/** + * Represents entity used to describe registered locations. + */ +@Getter +@AllArgsConstructor(staticName = "of") +public class ContentEntity { + private Integer id; + + private String location; + + private Integer provider; + + private Integer secret; +} \ No newline at end of file diff --git a/api-server/src/main/java/com/repoachiever/entity/repository/ProviderEntity.java b/api-server/src/main/java/com/repoachiever/entity/repository/ProviderEntity.java new file mode 100644 index 0000000..45b69e1 --- /dev/null +++ b/api-server/src/main/java/com/repoachiever/entity/repository/ProviderEntity.java @@ -0,0 +1,15 @@ +package com.repoachiever.entity.repository; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +/** + * Represents entity used to describe available providers. + */ +@Getter +@AllArgsConstructor(staticName = "of") +public class ProviderEntity { + private Integer id; + + private String name; +} diff --git a/api-server/src/main/java/com/repoachiever/entity/repository/SecretEntity.java b/api-server/src/main/java/com/repoachiever/entity/repository/SecretEntity.java new file mode 100644 index 0000000..1848a3f --- /dev/null +++ b/api-server/src/main/java/com/repoachiever/entity/repository/SecretEntity.java @@ -0,0 +1,23 @@ +package com.repoachiever.entity.repository; + +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.Id; +import lombok.AllArgsConstructor; +import lombok.Getter; + +import java.util.Optional; + +/** + * Represents entity used to describe registered secrets. + */ +@Getter +@AllArgsConstructor(staticName = "of") +public class SecretEntity { + private Integer id; + + private Integer session; + + private Optional credentials; +} diff --git a/api-server/src/main/java/com/repoachiever/exception/ApiServerInstanceIsAlreadyRunningException.java b/api-server/src/main/java/com/repoachiever/exception/ApiServerInstanceIsAlreadyRunningException.java new file mode 100644 index 0000000..af511da --- /dev/null +++ b/api-server/src/main/java/com/repoachiever/exception/ApiServerInstanceIsAlreadyRunningException.java @@ -0,0 +1,21 @@ +package com.repoachiever.exception; + +import java.io.IOException; +import java.util.Arrays; +import java.util.Formatter; + +/** + * Represents exception used when RepoAchiever API Server is already running. + */ +public class ApiServerInstanceIsAlreadyRunningException extends IOException { + public ApiServerInstanceIsAlreadyRunningException() { + this(""); + } + + public ApiServerInstanceIsAlreadyRunningException(Object... message) { + super( + new Formatter() + .format("RepoAchiever API Server instance is already running: %s", Arrays.stream(message).toArray()) + .toString()); + } +} diff --git a/api-server/src/main/java/com/repoachiever/exception/ClusterApplicationFailureException.java b/api-server/src/main/java/com/repoachiever/exception/ClusterApplicationFailureException.java new file mode 100644 index 0000000..61026f4 --- /dev/null +++ b/api-server/src/main/java/com/repoachiever/exception/ClusterApplicationFailureException.java @@ -0,0 +1,21 @@ +package com.repoachiever.exception; + +import java.io.IOException; +import java.util.Arrays; +import java.util.Formatter; + +/** + * Represents exception used when RepoAchiever Cluster application fails. + */ +public class ClusterApplicationFailureException extends IOException { + public ClusterApplicationFailureException() { + this(""); + } + + public ClusterApplicationFailureException(Object... message) { + super( + new Formatter() + .format("RepoAchiever Cluster application failed: %s", Arrays.stream(message).toArray()) + .toString()); + } +} diff --git a/api-server/src/main/java/com/repoachiever/exception/ClusterApplicationTimeoutException.java b/api-server/src/main/java/com/repoachiever/exception/ClusterApplicationTimeoutException.java new file mode 100644 index 0000000..c85b75c --- /dev/null +++ b/api-server/src/main/java/com/repoachiever/exception/ClusterApplicationTimeoutException.java @@ -0,0 +1,21 @@ +package com.repoachiever.exception; + +import java.io.IOException; +import java.util.Arrays; +import java.util.Formatter; + +/** + * Represents exception used when RepoAchiever Cluster application received timeout. + */ +public class ClusterApplicationTimeoutException extends IOException { + public ClusterApplicationTimeoutException() { + this(""); + } + + public ClusterApplicationTimeoutException(Object... message) { + super( + new Formatter() + .format("RepoAchiever Cluster application received timeout: %s", Arrays.stream(message).toArray()) + .toString()); + } +} diff --git a/api-server/src/main/java/com/repoachiever/exception/ClusterDeploymentFailureException.java b/api-server/src/main/java/com/repoachiever/exception/ClusterDeploymentFailureException.java new file mode 100644 index 0000000..ed00608 --- /dev/null +++ b/api-server/src/main/java/com/repoachiever/exception/ClusterDeploymentFailureException.java @@ -0,0 +1,21 @@ +package com.repoachiever.exception; + +import java.io.IOException; +import java.util.Arrays; +import java.util.Formatter; + +/** + * Represents exception used when RepoAchiever Cluster deployment fails. + */ +public class ClusterDeploymentFailureException extends IOException { + public ClusterDeploymentFailureException() { + this(""); + } + + public ClusterDeploymentFailureException(Object... message) { + super( + new Formatter() + .format("RepoAchiever Cluster deployment failed: %s", Arrays.stream(message).toArray()) + .toString()); + } +} diff --git a/api-server/src/main/java/com/repoachiever/exception/ClusterDestructionFailureException.java b/api-server/src/main/java/com/repoachiever/exception/ClusterDestructionFailureException.java new file mode 100644 index 0000000..bb184e8 --- /dev/null +++ b/api-server/src/main/java/com/repoachiever/exception/ClusterDestructionFailureException.java @@ -0,0 +1,21 @@ +package com.repoachiever.exception; + +import java.io.IOException; +import java.util.Arrays; +import java.util.Formatter; + +/** + * Represents exception used when RepoAchiever Cluster destruction fails. + */ +public class ClusterDestructionFailureException extends IOException { + public ClusterDestructionFailureException() { + this(""); + } + + public ClusterDestructionFailureException(Object... message) { + super( + new Formatter() + .format("RepoAchiever Cluster destruction failed: %s", Arrays.stream(message).toArray()) + .toString()); + } +} diff --git a/api-server/src/main/java/com/repoachiever/exception/ClusterFullDestructionFailureException.java b/api-server/src/main/java/com/repoachiever/exception/ClusterFullDestructionFailureException.java new file mode 100644 index 0000000..6e0e55d --- /dev/null +++ b/api-server/src/main/java/com/repoachiever/exception/ClusterFullDestructionFailureException.java @@ -0,0 +1,21 @@ +package com.repoachiever.exception; + +import java.io.IOException; +import java.util.Arrays; +import java.util.Formatter; + +/** + * Represents exception used when RepoAchiever Cluster full destruction fails. + */ +public class ClusterFullDestructionFailureException extends IOException { + public ClusterFullDestructionFailureException() { + this(""); + } + + public ClusterFullDestructionFailureException(Object... message) { + super( + new Formatter() + .format("RepoAchiever Cluster full destruction failed: %s", Arrays.stream(message).toArray()) + .toString()); + } +} diff --git a/api-server/src/main/java/com/repoachiever/exception/ClusterOperationFailureException.java b/api-server/src/main/java/com/repoachiever/exception/ClusterOperationFailureException.java new file mode 100644 index 0000000..773ece1 --- /dev/null +++ b/api-server/src/main/java/com/repoachiever/exception/ClusterOperationFailureException.java @@ -0,0 +1,21 @@ +package com.repoachiever.exception; + +import java.io.IOException; +import java.util.Arrays; +import java.util.Formatter; + +/** + * Represents exception used when RepoAchiever Cluster operation fails. + */ +public class ClusterOperationFailureException extends IOException { + public ClusterOperationFailureException() { + this(""); + } + + public ClusterOperationFailureException(Object... message) { + super( + new Formatter() + .format("RepoAchiever Cluster operation failed: %s", Arrays.stream(message).toArray()) + .toString()); + } +} diff --git a/api-server/src/main/java/com/repoachiever/exception/ClusterRecreationFailureException.java b/api-server/src/main/java/com/repoachiever/exception/ClusterRecreationFailureException.java new file mode 100644 index 0000000..af0c319 --- /dev/null +++ b/api-server/src/main/java/com/repoachiever/exception/ClusterRecreationFailureException.java @@ -0,0 +1,21 @@ +package com.repoachiever.exception; + +import java.io.IOException; +import java.util.Arrays; +import java.util.Formatter; + +/** + * Represents exception used when RepoAchiever Cluster recreation operation fails. + */ +public class ClusterRecreationFailureException extends IOException { + public ClusterRecreationFailureException() { + this(""); + } + + public ClusterRecreationFailureException(Object... message) { + super( + new Formatter() + .format("RepoAchiever Cluster recreation operation failed: %s", Arrays.stream(message).toArray()) + .toString()); + } +} diff --git a/api-server/src/main/java/com/repoachiever/exception/ClusterUnhealthyReapplicationFailureException.java b/api-server/src/main/java/com/repoachiever/exception/ClusterUnhealthyReapplicationFailureException.java new file mode 100644 index 0000000..07556be --- /dev/null +++ b/api-server/src/main/java/com/repoachiever/exception/ClusterUnhealthyReapplicationFailureException.java @@ -0,0 +1,21 @@ +package com.repoachiever.exception; + +import java.io.IOException; +import java.util.Arrays; +import java.util.Formatter; + +/** + * Represents exception used when RepoAchiever Cluster unhealthy allocation reapplication fails. + */ +public class ClusterUnhealthyReapplicationFailureException extends IOException { + public ClusterUnhealthyReapplicationFailureException() { + this(""); + } + + public ClusterUnhealthyReapplicationFailureException(Object... message) { + super( + new Formatter() + .format("RepoAchiever Cluster unhealthy allocation reapplication failed: %s", Arrays.stream(message).toArray()) + .toString()); + } +} diff --git a/api-server/src/main/java/com/repoachiever/exception/ClusterWithdrawalFailureException.java b/api-server/src/main/java/com/repoachiever/exception/ClusterWithdrawalFailureException.java new file mode 100644 index 0000000..ce699d5 --- /dev/null +++ b/api-server/src/main/java/com/repoachiever/exception/ClusterWithdrawalFailureException.java @@ -0,0 +1,21 @@ +package com.repoachiever.exception; + +import java.io.IOException; +import java.util.Arrays; +import java.util.Formatter; + +/** + * Represents exception used when RepoAchiever Cluster withdrawal fails. + */ +public class ClusterWithdrawalFailureException extends IOException { + public ClusterWithdrawalFailureException() { + this(""); + } + + public ClusterWithdrawalFailureException(Object... message) { + super( + new Formatter() + .format("RepoAchiever Cluster withdrawal failed: %s", Arrays.stream(message).toArray()) + .toString()); + } +} diff --git a/api-server/src/main/java/com/repoachiever/exception/CommandExecutorException.java b/api-server/src/main/java/com/repoachiever/exception/CommandExecutorException.java new file mode 100644 index 0000000..6cf46ee --- /dev/null +++ b/api-server/src/main/java/com/repoachiever/exception/CommandExecutorException.java @@ -0,0 +1,17 @@ +package com.repoachiever.exception; + +import java.io.IOException; +import java.util.Arrays; +import java.util.Formatter; + +/** + * Represents exception used when command execution process fails. + */ +public class CommandExecutorException extends IOException { + public CommandExecutorException(Object... message) { + super( + new Formatter() + .format("Invalid command executor behaviour: %s", Arrays.stream(message).toArray()) + .toString()); + } +} diff --git a/api-server/src/main/java/com/repoachiever/exception/CommunicationConfigurationFailureException.java b/api-server/src/main/java/com/repoachiever/exception/CommunicationConfigurationFailureException.java new file mode 100644 index 0000000..1f09d8e --- /dev/null +++ b/api-server/src/main/java/com/repoachiever/exception/CommunicationConfigurationFailureException.java @@ -0,0 +1,21 @@ +package com.repoachiever.exception; + +import java.io.IOException; +import java.util.Arrays; +import java.util.Formatter; + +/** + * Represents exception used when communication configuration process fails. + */ +public class CommunicationConfigurationFailureException extends IOException { + public CommunicationConfigurationFailureException() { + this(""); + } + + public CommunicationConfigurationFailureException(Object... message) { + super( + new Formatter() + .format("Communication configuration operation failed: %s", Arrays.stream(message).toArray()) + .toString()); + } +} \ No newline at end of file diff --git a/api-server/src/main/java/com/repoachiever/exception/ConfigValidationException.java b/api-server/src/main/java/com/repoachiever/exception/ConfigValidationException.java new file mode 100644 index 0000000..a8ffa9d --- /dev/null +++ b/api-server/src/main/java/com/repoachiever/exception/ConfigValidationException.java @@ -0,0 +1,17 @@ +package com.repoachiever.exception; + +import java.io.IOException; +import java.util.Arrays; +import java.util.Formatter; + +/** + * Represents exception used when configuration file validation process fails. + */ +public class ConfigValidationException extends IOException { + public ConfigValidationException(Object... message) { + super( + new Formatter() + .format("Config file content is not valid: %s", Arrays.stream(message).toArray()) + .toString()); + } +} diff --git a/api-server/src/main/java/com/repoachiever/exception/ContentApplicationRetrievalFailureException.java b/api-server/src/main/java/com/repoachiever/exception/ContentApplicationRetrievalFailureException.java new file mode 100644 index 0000000..79961d4 --- /dev/null +++ b/api-server/src/main/java/com/repoachiever/exception/ContentApplicationRetrievalFailureException.java @@ -0,0 +1,21 @@ +package com.repoachiever.exception; + +import java.io.IOException; +import java.util.Arrays; +import java.util.Formatter; + +/** + * Represents exception used when content application retrieval process fails. + */ +public class ContentApplicationRetrievalFailureException extends IOException { + public ContentApplicationRetrievalFailureException() { + this(""); + } + + public ContentApplicationRetrievalFailureException(Object... message) { + super( + new Formatter() + .format("Content application retrieval failed: %s", Arrays.stream(message).toArray()) + .toString()); + } +} diff --git a/api-server/src/main/java/com/repoachiever/exception/ContentFileNotFoundException.java b/api-server/src/main/java/com/repoachiever/exception/ContentFileNotFoundException.java new file mode 100644 index 0000000..290cd6d --- /dev/null +++ b/api-server/src/main/java/com/repoachiever/exception/ContentFileNotFoundException.java @@ -0,0 +1,21 @@ +package com.repoachiever.exception; + +import java.io.IOException; +import java.util.Arrays; +import java.util.Formatter; + +/** + * Represents exception used when configuration file content was not found. + */ +public class ContentFileNotFoundException extends IOException { + public ContentFileNotFoundException() { + this(""); + } + + public ContentFileNotFoundException(Object... message) { + super( + new Formatter() + .format("Content file is not found: %s", Arrays.stream(message).toArray()) + .toString()); + } +} diff --git a/api-server/src/main/java/com/repoachiever/exception/ContentFileRemovalFailureException.java b/api-server/src/main/java/com/repoachiever/exception/ContentFileRemovalFailureException.java new file mode 100644 index 0000000..576f488 --- /dev/null +++ b/api-server/src/main/java/com/repoachiever/exception/ContentFileRemovalFailureException.java @@ -0,0 +1,21 @@ +package com.repoachiever.exception; + +import java.io.IOException; +import java.util.Arrays; +import java.util.Formatter; + +/** + * Represents exception used when configuration file content removal operation fails. + */ +public class ContentFileRemovalFailureException extends IOException { + public ContentFileRemovalFailureException() { + this(""); + } + + public ContentFileRemovalFailureException(Object... message) { + super( + new Formatter() + .format("Content file removal failed: %s", Arrays.stream(message).toArray()) + .toString()); + } +} \ No newline at end of file diff --git a/api-server/src/main/java/com/repoachiever/exception/ContentFileWriteFailureException.java b/api-server/src/main/java/com/repoachiever/exception/ContentFileWriteFailureException.java new file mode 100644 index 0000000..d447974 --- /dev/null +++ b/api-server/src/main/java/com/repoachiever/exception/ContentFileWriteFailureException.java @@ -0,0 +1,21 @@ +package com.repoachiever.exception; + +import java.io.IOException; +import java.util.Arrays; +import java.util.Formatter; + +/** + * Represents exception used when content file write operation fails. + */ +public class ContentFileWriteFailureException extends IOException { + public ContentFileWriteFailureException() { + this(""); + } + + public ContentFileWriteFailureException(Object... message) { + super( + new Formatter() + .format("Content file write failed: %s", Arrays.stream(message).toArray()) + .toString()); + } +} diff --git a/api-server/src/main/java/com/repoachiever/exception/CredentialsAreNotValidException.java b/api-server/src/main/java/com/repoachiever/exception/CredentialsAreNotValidException.java new file mode 100644 index 0000000..b414463 --- /dev/null +++ b/api-server/src/main/java/com/repoachiever/exception/CredentialsAreNotValidException.java @@ -0,0 +1,21 @@ +package com.repoachiever.exception; + +import java.io.IOException; +import java.util.Arrays; +import java.util.Formatter; + +/** + * Represents exception used when given credentials are not valid. + */ +public class CredentialsAreNotValidException extends IOException { + public CredentialsAreNotValidException() { + this(""); + } + + public CredentialsAreNotValidException(Object... message) { + super( + new Formatter() + .format("Credentials are not valid: %s", Arrays.stream(message).toArray()) + .toString()); + } +} diff --git a/api-server/src/main/java/com/repoachiever/exception/CredentialsConversionException.java b/api-server/src/main/java/com/repoachiever/exception/CredentialsConversionException.java new file mode 100644 index 0000000..791a3f1 --- /dev/null +++ b/api-server/src/main/java/com/repoachiever/exception/CredentialsConversionException.java @@ -0,0 +1,17 @@ +package com.repoachiever.exception; + +import java.io.IOException; +import java.util.Arrays; +import java.util.Formatter; + +/** + * Represents exception used when credentials conversion process fails. + */ +public class CredentialsConversionException extends IOException { + public CredentialsConversionException(Object... message) { + super( + new Formatter() + .format("Given credentials are invalid: %s", Arrays.stream(message).toArray()) + .toString()); + } +} diff --git a/api-server/src/main/java/com/repoachiever/exception/CredentialsFieldIsNotValidException.java b/api-server/src/main/java/com/repoachiever/exception/CredentialsFieldIsNotValidException.java new file mode 100644 index 0000000..84cef1e --- /dev/null +++ b/api-server/src/main/java/com/repoachiever/exception/CredentialsFieldIsNotValidException.java @@ -0,0 +1,21 @@ +package com.repoachiever.exception; + +import java.io.IOException; +import java.util.Arrays; +import java.util.Formatter; + +/** + * Represents exception used when credentials field is not valid. + */ +public class CredentialsFieldIsNotValidException extends IOException { + public CredentialsFieldIsNotValidException() { + this(""); + } + + public CredentialsFieldIsNotValidException(Object... message) { + super( + new Formatter() + .format("Credentials field is not valid: %s", Arrays.stream(message).toArray()) + .toString()); + } +} diff --git a/api-server/src/main/java/com/repoachiever/exception/DiagnosticsTemplateProcessingFailureException.java b/api-server/src/main/java/com/repoachiever/exception/DiagnosticsTemplateProcessingFailureException.java new file mode 100644 index 0000000..1691337 --- /dev/null +++ b/api-server/src/main/java/com/repoachiever/exception/DiagnosticsTemplateProcessingFailureException.java @@ -0,0 +1,21 @@ +package com.repoachiever.exception; + +import java.io.IOException; +import java.util.Arrays; +import java.util.Formatter; + +/** + * Represents exception used when diagnostics template processing fails. + */ +public class DiagnosticsTemplateProcessingFailureException extends IOException { + public DiagnosticsTemplateProcessingFailureException() { + this(""); + } + + public DiagnosticsTemplateProcessingFailureException(Object... message) { + super( + new Formatter() + .format("Diagnostics template processing failed: %s", Arrays.stream(message).toArray()) + .toString()); + } +} diff --git a/api-server/src/main/java/com/repoachiever/exception/DockerInspectRemovalFailureException.java b/api-server/src/main/java/com/repoachiever/exception/DockerInspectRemovalFailureException.java new file mode 100644 index 0000000..dda5a43 --- /dev/null +++ b/api-server/src/main/java/com/repoachiever/exception/DockerInspectRemovalFailureException.java @@ -0,0 +1,21 @@ +package com.repoachiever.exception; + +import java.io.IOException; +import java.util.Arrays; +import java.util.Formatter; + +/** + * Represents exception used when Docker inspection removal process fails. + */ +public class DockerInspectRemovalFailureException extends IOException { + public DockerInspectRemovalFailureException() { + this(""); + } + + public DockerInspectRemovalFailureException(Object... message) { + super( + new Formatter() + .format("Docker inspect container removal failed: %s", Arrays.stream(message).toArray()) + .toString()); + } +} diff --git a/api-server/src/main/java/com/repoachiever/exception/DockerIsNotAvailableException.java b/api-server/src/main/java/com/repoachiever/exception/DockerIsNotAvailableException.java new file mode 100644 index 0000000..c98a154 --- /dev/null +++ b/api-server/src/main/java/com/repoachiever/exception/DockerIsNotAvailableException.java @@ -0,0 +1,21 @@ +package com.repoachiever.exception; + +import java.io.IOException; +import java.util.Arrays; +import java.util.Formatter; + +/** + * Represents exception used when Docker availability check fails. + */ +public class DockerIsNotAvailableException extends IOException { + public DockerIsNotAvailableException() { + this(""); + } + + public DockerIsNotAvailableException(Object... message) { + super( + new Formatter() + .format("Docker instance is not available: %s", Arrays.stream(message).toArray()) + .toString()); + } +} diff --git a/api-server/src/main/java/com/repoachiever/exception/DockerNetworkCreateFailureException.java b/api-server/src/main/java/com/repoachiever/exception/DockerNetworkCreateFailureException.java new file mode 100644 index 0000000..21640c9 --- /dev/null +++ b/api-server/src/main/java/com/repoachiever/exception/DockerNetworkCreateFailureException.java @@ -0,0 +1,21 @@ +package com.repoachiever.exception; + +import java.io.IOException; +import java.util.Arrays; +import java.util.Formatter; + +/** + * Represents exception used when Docker network creation fails. + */ +public class DockerNetworkCreateFailureException extends IOException { + public DockerNetworkCreateFailureException() { + this(""); + } + + public DockerNetworkCreateFailureException(Object... message) { + super( + new Formatter() + .format("Docker network creation failed: %s", Arrays.stream(message).toArray()) + .toString()); + } +} diff --git a/api-server/src/main/java/com/repoachiever/exception/DockerNetworkRemoveFailureException.java b/api-server/src/main/java/com/repoachiever/exception/DockerNetworkRemoveFailureException.java new file mode 100644 index 0000000..cf55e3a --- /dev/null +++ b/api-server/src/main/java/com/repoachiever/exception/DockerNetworkRemoveFailureException.java @@ -0,0 +1,21 @@ +package com.repoachiever.exception; + +import java.io.IOException; +import java.util.Arrays; +import java.util.Formatter; + +/** + * Represents exception used when Docker network removal fails. + */ +public class DockerNetworkRemoveFailureException extends IOException { + public DockerNetworkRemoveFailureException() { + this(""); + } + + public DockerNetworkRemoveFailureException(Object... message) { + super( + new Formatter() + .format("Docker network removal failed: %s", Arrays.stream(message).toArray()) + .toString()); + } +} \ No newline at end of file diff --git a/api-server/src/main/java/com/repoachiever/exception/LocationsFieldIsNotValidException.java b/api-server/src/main/java/com/repoachiever/exception/LocationsFieldIsNotValidException.java new file mode 100644 index 0000000..1807859 --- /dev/null +++ b/api-server/src/main/java/com/repoachiever/exception/LocationsFieldIsNotValidException.java @@ -0,0 +1,21 @@ +package com.repoachiever.exception; + +import java.io.IOException; +import java.util.Arrays; +import java.util.Formatter; + +/** + * Represents exception used when locations field is not valid. + */ +public class LocationsFieldIsNotValidException extends IOException { + public LocationsFieldIsNotValidException() { + this(""); + } + + public LocationsFieldIsNotValidException(Object... message) { + super( + new Formatter() + .format("Locations field is not valid: %s", Arrays.stream(message).toArray()) + .toString()); + } +} \ No newline at end of file diff --git a/api-server/src/main/java/com/repoachiever/exception/MetadataFileNotFoundException.java b/api-server/src/main/java/com/repoachiever/exception/MetadataFileNotFoundException.java new file mode 100644 index 0000000..069b3fe --- /dev/null +++ b/api-server/src/main/java/com/repoachiever/exception/MetadataFileNotFoundException.java @@ -0,0 +1,21 @@ +package com.repoachiever.exception; + +import java.io.IOException; +import java.util.Arrays; +import java.util.Formatter; + +/** + * Represents exception used when metadata file was not found. + */ +public class MetadataFileNotFoundException extends IOException { + public MetadataFileNotFoundException() { + this(""); + } + + public MetadataFileNotFoundException(Object... message) { + super( + new Formatter() + .format("Metadata file is not found: %s", Arrays.stream(message).toArray()) + .toString()); + } +} diff --git a/api-server/src/main/java/com/repoachiever/exception/MetadataFileWriteFailureException.java b/api-server/src/main/java/com/repoachiever/exception/MetadataFileWriteFailureException.java new file mode 100644 index 0000000..4747254 --- /dev/null +++ b/api-server/src/main/java/com/repoachiever/exception/MetadataFileWriteFailureException.java @@ -0,0 +1,21 @@ +package com.repoachiever.exception; + +import java.io.IOException; +import java.util.Arrays; +import java.util.Formatter; + +/** + * Represents exception used when metadata file write operation fails. + */ +public class MetadataFileWriteFailureException extends IOException { + public MetadataFileWriteFailureException() { + this(""); + } + + public MetadataFileWriteFailureException(Object... message) { + super( + new Formatter() + .format("Metadata file write failed: %s", Arrays.stream(message).toArray()) + .toString()); + } +} diff --git a/api-server/src/main/java/com/repoachiever/exception/NodeExporterDeploymentFailureException.java b/api-server/src/main/java/com/repoachiever/exception/NodeExporterDeploymentFailureException.java new file mode 100644 index 0000000..d1c82cd --- /dev/null +++ b/api-server/src/main/java/com/repoachiever/exception/NodeExporterDeploymentFailureException.java @@ -0,0 +1,21 @@ +package com.repoachiever.exception; + +import java.io.IOException; +import java.util.Arrays; +import java.util.Formatter; + +/** + * Represents exception used when Prometheus Node Exporter deployment operation fails. + */ +public class NodeExporterDeploymentFailureException extends IOException { + public NodeExporterDeploymentFailureException() { + this(""); + } + + public NodeExporterDeploymentFailureException(Object... message) { + super( + new Formatter() + .format("Prometheus node exporter deployment failed: %s", Arrays.stream(message).toArray()) + .toString()); + } +} diff --git a/api-server/src/main/java/com/repoachiever/exception/PrometheusDeploymentFailureException.java b/api-server/src/main/java/com/repoachiever/exception/PrometheusDeploymentFailureException.java new file mode 100644 index 0000000..2e34f8c --- /dev/null +++ b/api-server/src/main/java/com/repoachiever/exception/PrometheusDeploymentFailureException.java @@ -0,0 +1,21 @@ +package com.repoachiever.exception; + +import java.io.IOException; +import java.util.Arrays; +import java.util.Formatter; + +/** + * Represents exception used when Prometheus deployment operation fails. + */ +public class PrometheusDeploymentFailureException extends IOException { + public PrometheusDeploymentFailureException() { + this(""); + } + + public PrometheusDeploymentFailureException(Object... message) { + super( + new Formatter() + .format("Prometheus deployment failed: %s", Arrays.stream(message).toArray()) + .toString()); + } +} \ No newline at end of file diff --git a/api-server/src/main/java/com/repoachiever/exception/QueryEmptyResultException.java b/api-server/src/main/java/com/repoachiever/exception/QueryEmptyResultException.java new file mode 100644 index 0000000..4f0bb31 --- /dev/null +++ b/api-server/src/main/java/com/repoachiever/exception/QueryEmptyResultException.java @@ -0,0 +1,21 @@ +package com.repoachiever.exception; + +import java.io.IOException; +import java.util.Arrays; +import java.util.Formatter; + +/** + * Represents exception used when database query returns empty result. + */ +public class QueryEmptyResultException extends IOException { + public QueryEmptyResultException() { + this(""); + } + + public QueryEmptyResultException(Object... message) { + super( + new Formatter() + .format("Query result is empty: %s", Arrays.stream(message).toArray()) + .toString()); + } +} diff --git a/api-server/src/main/java/com/repoachiever/exception/QueryExecutionFailureException.java b/api-server/src/main/java/com/repoachiever/exception/QueryExecutionFailureException.java new file mode 100644 index 0000000..7d90486 --- /dev/null +++ b/api-server/src/main/java/com/repoachiever/exception/QueryExecutionFailureException.java @@ -0,0 +1,21 @@ +package com.repoachiever.exception; + +import java.io.IOException; +import java.util.Arrays; +import java.util.Formatter; + +/** + * Represents exception used when database query execution operation fails. + */ +public class QueryExecutionFailureException extends IOException { + public QueryExecutionFailureException() { + this(""); + } + + public QueryExecutionFailureException(Object... message) { + super( + new Formatter() + .format("Query execution failed: %s", Arrays.stream(message).toArray()) + .toString()); + } +} diff --git a/api-server/src/main/java/com/repoachiever/exception/RepositoryContentApplicationFailureException.java b/api-server/src/main/java/com/repoachiever/exception/RepositoryContentApplicationFailureException.java new file mode 100644 index 0000000..fc73002 --- /dev/null +++ b/api-server/src/main/java/com/repoachiever/exception/RepositoryContentApplicationFailureException.java @@ -0,0 +1,21 @@ +package com.repoachiever.exception; + +import java.io.IOException; +import java.util.Arrays; +import java.util.Formatter; + +/** + * Represents exception used when repository content application operation fails. + */ +public class RepositoryContentApplicationFailureException extends IOException { + public RepositoryContentApplicationFailureException() { + this(""); + } + + public RepositoryContentApplicationFailureException(Object... message) { + super( + new Formatter() + .format("RepoAchiever Cluster repository content application failed: %s", Arrays.stream(message).toArray()) + .toString()); + } +} diff --git a/api-server/src/main/java/com/repoachiever/exception/RepositoryContentDestructionFailureException.java b/api-server/src/main/java/com/repoachiever/exception/RepositoryContentDestructionFailureException.java new file mode 100644 index 0000000..4987054 --- /dev/null +++ b/api-server/src/main/java/com/repoachiever/exception/RepositoryContentDestructionFailureException.java @@ -0,0 +1,21 @@ +package com.repoachiever.exception; + +import java.io.IOException; +import java.util.Arrays; +import java.util.Formatter; + +/** + * Represents exception used when repository content destruction operation fails. + */ +public class RepositoryContentDestructionFailureException extends IOException { + public RepositoryContentDestructionFailureException() { + this(""); + } + + public RepositoryContentDestructionFailureException(Object... message) { + super( + new Formatter() + .format("RepoAchiever Cluster repository content destruction failed: %s", Arrays.stream(message).toArray()) + .toString()); + } +} diff --git a/api-server/src/main/java/com/repoachiever/exception/RepositoryOperationFailureException.java b/api-server/src/main/java/com/repoachiever/exception/RepositoryOperationFailureException.java new file mode 100644 index 0000000..e43e3cf --- /dev/null +++ b/api-server/src/main/java/com/repoachiever/exception/RepositoryOperationFailureException.java @@ -0,0 +1,21 @@ +package com.repoachiever.exception; + +import java.io.IOException; +import java.util.Arrays; +import java.util.Formatter; + +/** + * Represents exception used when repository operation fails. + */ +public class RepositoryOperationFailureException extends IOException { + public RepositoryOperationFailureException() { + this(""); + } + + public RepositoryOperationFailureException(Object... message) { + super( + new Formatter() + .format("Repository operation failed: %s", Arrays.stream(message).toArray()) + .toString()); + } +} \ No newline at end of file diff --git a/api-server/src/main/java/com/repoachiever/exception/TelemetryOperationFailureException.java b/api-server/src/main/java/com/repoachiever/exception/TelemetryOperationFailureException.java new file mode 100644 index 0000000..3345723 --- /dev/null +++ b/api-server/src/main/java/com/repoachiever/exception/TelemetryOperationFailureException.java @@ -0,0 +1,21 @@ +package com.repoachiever.exception; + +import java.io.IOException; +import java.util.Arrays; +import java.util.Formatter; + +/** + * Represents exception used when telemetry operation fails. + */ +public class TelemetryOperationFailureException extends IOException { + public TelemetryOperationFailureException() { + this(""); + } + + public TelemetryOperationFailureException(Object... message) { + super( + new Formatter() + .format("Telemetry operation failed: %s", Arrays.stream(message).toArray()) + .toString()); + } +} \ No newline at end of file diff --git a/api-server/src/main/java/com/repoachiever/exception/WorkspaceContentDirectoryCreationFailureException.java b/api-server/src/main/java/com/repoachiever/exception/WorkspaceContentDirectoryCreationFailureException.java new file mode 100644 index 0000000..5cca97b --- /dev/null +++ b/api-server/src/main/java/com/repoachiever/exception/WorkspaceContentDirectoryCreationFailureException.java @@ -0,0 +1,21 @@ +package com.repoachiever.exception; + +import java.io.IOException; +import java.util.Arrays; +import java.util.Formatter; + +/** + * Represents exception used when workspace content directory creation operation fails. + */ +public class WorkspaceContentDirectoryCreationFailureException extends IOException { + public WorkspaceContentDirectoryCreationFailureException() { + this(""); + } + + public WorkspaceContentDirectoryCreationFailureException(Object... message) { + super( + new Formatter() + .format("Workspace content directory creation failed: %s", Arrays.stream(message).toArray()) + .toString()); + } +} \ No newline at end of file diff --git a/api-server/src/main/java/com/repoachiever/exception/WorkspaceUnitDirectoryCreationFailureException.java b/api-server/src/main/java/com/repoachiever/exception/WorkspaceUnitDirectoryCreationFailureException.java new file mode 100644 index 0000000..18f1c49 --- /dev/null +++ b/api-server/src/main/java/com/repoachiever/exception/WorkspaceUnitDirectoryCreationFailureException.java @@ -0,0 +1,21 @@ +package com.repoachiever.exception; + +import java.io.IOException; +import java.util.Arrays; +import java.util.Formatter; + +/** + * Represents exception used when workspace unit directory creation operation fails. + */ +public class WorkspaceUnitDirectoryCreationFailureException extends IOException { + public WorkspaceUnitDirectoryCreationFailureException() { + this(""); + } + + public WorkspaceUnitDirectoryCreationFailureException(Object... message) { + super( + new Formatter() + .format("Workspace unit directory creation failed: %s", Arrays.stream(message).toArray()) + .toString()); + } +} \ No newline at end of file diff --git a/api-server/src/main/java/com/repoachiever/exception/WorkspaceUnitDirectoryNotFoundException.java b/api-server/src/main/java/com/repoachiever/exception/WorkspaceUnitDirectoryNotFoundException.java new file mode 100644 index 0000000..e9502b5 --- /dev/null +++ b/api-server/src/main/java/com/repoachiever/exception/WorkspaceUnitDirectoryNotFoundException.java @@ -0,0 +1,21 @@ +package com.repoachiever.exception; + +import java.nio.file.NoSuchFileException; +import java.util.Arrays; +import java.util.Formatter; + +/** + * Represents exception used when workspace unit directory was not found. + */ +public class WorkspaceUnitDirectoryNotFoundException extends NoSuchFileException { + public WorkspaceUnitDirectoryNotFoundException() { + this(""); + } + + public WorkspaceUnitDirectoryNotFoundException(Object... message) { + super( + new Formatter() + .format("Workspace unit is not found: %s", Arrays.stream(message).toArray()) + .toString()); + } +} diff --git a/api-server/src/main/java/com/repoachiever/exception/WorkspaceUnitDirectoryPresentException.java b/api-server/src/main/java/com/repoachiever/exception/WorkspaceUnitDirectoryPresentException.java new file mode 100644 index 0000000..0a07207 --- /dev/null +++ b/api-server/src/main/java/com/repoachiever/exception/WorkspaceUnitDirectoryPresentException.java @@ -0,0 +1,23 @@ +package com.repoachiever.exception; + +import java.io.IOException; +import java.util.Arrays; +import java.util.Formatter; + +/** + * Represents exception used when workspace unit directory is already present. + */ +public class WorkspaceUnitDirectoryPresentException extends IOException { + public WorkspaceUnitDirectoryPresentException() { + this(""); + } + + public WorkspaceUnitDirectoryPresentException(Object... message) { + super( + new Formatter() + .format( + "Workspace unit is already present, please stop current deployment first: %s", + Arrays.stream(message).toArray()) + .toString()); + } +} diff --git a/api-server/src/main/java/com/repoachiever/exception/WorkspaceUnitDirectoryRemovalFailureException.java b/api-server/src/main/java/com/repoachiever/exception/WorkspaceUnitDirectoryRemovalFailureException.java new file mode 100644 index 0000000..6ee978a --- /dev/null +++ b/api-server/src/main/java/com/repoachiever/exception/WorkspaceUnitDirectoryRemovalFailureException.java @@ -0,0 +1,21 @@ +package com.repoachiever.exception; + +import java.io.IOException; +import java.util.Arrays; +import java.util.Formatter; + +/** + * Represents exception used when workspace unit directory removal operation fails. + */ +public class WorkspaceUnitDirectoryRemovalFailureException extends IOException { + public WorkspaceUnitDirectoryRemovalFailureException() { + this(""); + } + + public WorkspaceUnitDirectoryRemovalFailureException(Object... message) { + super( + new Formatter() + .format("Workspace unit directory removal failed: %s", Arrays.stream(message).toArray()) + .toString()); + } +} \ No newline at end of file diff --git a/api-server/src/main/java/com/repoachiever/logging/FatalAppender.java b/api-server/src/main/java/com/repoachiever/logging/FatalAppender.java new file mode 100644 index 0000000..a88883f --- /dev/null +++ b/api-server/src/main/java/com/repoachiever/logging/FatalAppender.java @@ -0,0 +1,36 @@ +package com.repoachiever.logging; + +import io.quarkus.runtime.Quarkus; +import org.apache.logging.log4j.Level; +import org.apache.logging.log4j.core.Appender; +import org.apache.logging.log4j.core.Core; +import org.apache.logging.log4j.core.Filter; +import org.apache.logging.log4j.core.LogEvent; +import org.apache.logging.log4j.core.appender.AbstractAppender; +import org.apache.logging.log4j.core.config.plugins.Plugin; +import org.apache.logging.log4j.core.config.plugins.PluginAttribute; +import org.apache.logging.log4j.core.config.plugins.PluginElement; +import org.apache.logging.log4j.core.config.plugins.PluginFactory; + +/** + * Service used for logging fatal level application state changes. + */ +@Plugin(name = "FatalAppender", category = Core.CATEGORY_NAME, elementType = Appender.ELEMENT_TYPE) +public class FatalAppender extends AbstractAppender { + protected FatalAppender(String name, Filter filter) { + super(name, filter, null, false, null); + } + + @PluginFactory + public static FatalAppender createAppender( + @PluginAttribute("name") String name, @PluginElement("Filter") Filter filter) { + return new FatalAppender(name, filter); + } + + @Override + public void append(LogEvent event) { + if (event.getLevel().equals(Level.FATAL)) { + Quarkus.asyncExit(1); + } + } +} diff --git a/api-server/src/main/java/com/repoachiever/mapping/ClusterApplicationFailureExceptionMapper.java b/api-server/src/main/java/com/repoachiever/mapping/ClusterApplicationFailureExceptionMapper.java new file mode 100644 index 0000000..bbebf78 --- /dev/null +++ b/api-server/src/main/java/com/repoachiever/mapping/ClusterApplicationFailureExceptionMapper.java @@ -0,0 +1,20 @@ +package com.repoachiever.mapping; + +import com.repoachiever.exception.ClusterApplicationFailureException; +import jakarta.ws.rs.core.Response; +import jakarta.ws.rs.ext.ExceptionMapper; +import jakarta.ws.rs.ext.Provider; + +/** + * Represents mapper for ClusterApplicationFailureException exception. + */ +@Provider +public class ClusterApplicationFailureExceptionMapper + implements ExceptionMapper { + @Override + public Response toResponse(ClusterApplicationFailureException e) { + return Response.status(Response.Status.BAD_REQUEST.getStatusCode()) + .entity(e.getMessage()) + .build(); + } +} diff --git a/api-server/src/main/java/com/repoachiever/mapping/ClusterWithdrawalFailureExceptionMapper.java b/api-server/src/main/java/com/repoachiever/mapping/ClusterWithdrawalFailureExceptionMapper.java new file mode 100644 index 0000000..616f84a --- /dev/null +++ b/api-server/src/main/java/com/repoachiever/mapping/ClusterWithdrawalFailureExceptionMapper.java @@ -0,0 +1,20 @@ +package com.repoachiever.mapping; + +import com.repoachiever.exception.ClusterWithdrawalFailureException; +import jakarta.ws.rs.core.Response; +import jakarta.ws.rs.ext.ExceptionMapper; +import jakarta.ws.rs.ext.Provider; + +/** + * Represents mapper for ClusterWithdrawalFailureExceptionMapper exception. + */ +@Provider +public class ClusterWithdrawalFailureExceptionMapper + implements ExceptionMapper { + @Override + public Response toResponse(ClusterWithdrawalFailureException e) { + return Response.status(Response.Status.BAD_REQUEST.getStatusCode()) + .entity(e.getMessage()) + .build(); + } +} diff --git a/api-server/src/main/java/com/repoachiever/mapping/CredentialsAreNotValidExceptionMapper.java b/api-server/src/main/java/com/repoachiever/mapping/CredentialsAreNotValidExceptionMapper.java new file mode 100644 index 0000000..20e15ae --- /dev/null +++ b/api-server/src/main/java/com/repoachiever/mapping/CredentialsAreNotValidExceptionMapper.java @@ -0,0 +1,18 @@ +package com.repoachiever.mapping; + +import com.repoachiever.exception.CredentialsAreNotValidException; +import jakarta.ws.rs.core.Response; +import jakarta.ws.rs.ext.ExceptionMapper; +import jakarta.ws.rs.ext.Provider; + +/** Represents mapper for CredentialsAreNotValidException exception. */ +@Provider +public class CredentialsAreNotValidExceptionMapper + implements ExceptionMapper { + @Override + public Response toResponse(CredentialsAreNotValidException e) { + return Response.status(Response.Status.BAD_REQUEST.getStatusCode()) + .entity(e.getMessage()) + .build(); + } +} diff --git a/api-server/src/main/java/com/repoachiever/mapping/CredentialsFieldIsNotValidExceptionMapper.java b/api-server/src/main/java/com/repoachiever/mapping/CredentialsFieldIsNotValidExceptionMapper.java new file mode 100644 index 0000000..ae816bd --- /dev/null +++ b/api-server/src/main/java/com/repoachiever/mapping/CredentialsFieldIsNotValidExceptionMapper.java @@ -0,0 +1,19 @@ +package com.repoachiever.mapping; + +import com.repoachiever.exception.CredentialsFieldIsNotValidException; +import com.repoachiever.exception.WorkspaceUnitDirectoryNotFoundException; +import jakarta.ws.rs.core.Response; +import jakarta.ws.rs.ext.ExceptionMapper; +import jakarta.ws.rs.ext.Provider; + +/** Represents mapper for CredentialsFieldIsNotValidException exception. */ +@Provider +public class CredentialsFieldIsNotValidExceptionMapper + implements ExceptionMapper { + @Override + public Response toResponse(CredentialsFieldIsNotValidException e) { + return Response.status(Response.Status.BAD_REQUEST.getStatusCode()) + .entity(e.getMessage()) + .build(); + } +} diff --git a/api-server/src/main/java/com/repoachiever/mapping/LocationsFieldIsNotValidExceptionMapper.java b/api-server/src/main/java/com/repoachiever/mapping/LocationsFieldIsNotValidExceptionMapper.java new file mode 100644 index 0000000..1e55c5f --- /dev/null +++ b/api-server/src/main/java/com/repoachiever/mapping/LocationsFieldIsNotValidExceptionMapper.java @@ -0,0 +1,18 @@ +package com.repoachiever.mapping; + +import com.repoachiever.exception.LocationsFieldIsNotValidException; +import jakarta.ws.rs.core.Response; +import jakarta.ws.rs.ext.ExceptionMapper; +import jakarta.ws.rs.ext.Provider; + +/** Represents mapper for LocationsFieldIsNotValidException exception. */ +@Provider +public class LocationsFieldIsNotValidExceptionMapper + implements ExceptionMapper { + @Override + public Response toResponse(LocationsFieldIsNotValidException e) { + return Response.status(Response.Status.BAD_REQUEST.getStatusCode()) + .entity(e.getMessage()) + .build(); + } +} diff --git a/api-server/src/main/java/com/repoachiever/mapping/RepositoryContentApplicationFailureExceptionMapper.java b/api-server/src/main/java/com/repoachiever/mapping/RepositoryContentApplicationFailureExceptionMapper.java new file mode 100644 index 0000000..abea333 --- /dev/null +++ b/api-server/src/main/java/com/repoachiever/mapping/RepositoryContentApplicationFailureExceptionMapper.java @@ -0,0 +1,20 @@ +package com.repoachiever.mapping; + +import com.repoachiever.exception.RepositoryContentApplicationFailureException; +import jakarta.ws.rs.core.Response; +import jakarta.ws.rs.ext.ExceptionMapper; +import jakarta.ws.rs.ext.Provider; + +/** + * Represents mapper for RepositoryContentApplicationFailureExceptionMapper exception. + */ +@Provider +public class RepositoryContentApplicationFailureExceptionMapper + implements ExceptionMapper { + @Override + public Response toResponse(RepositoryContentApplicationFailureException e) { + return Response.status(Response.Status.BAD_REQUEST.getStatusCode()) + .entity(e.getMessage()) + .build(); + } +} diff --git a/api-server/src/main/java/com/repoachiever/mapping/RepositoryContentDestructionFailureExceptionMapper.java b/api-server/src/main/java/com/repoachiever/mapping/RepositoryContentDestructionFailureExceptionMapper.java new file mode 100644 index 0000000..18911cd --- /dev/null +++ b/api-server/src/main/java/com/repoachiever/mapping/RepositoryContentDestructionFailureExceptionMapper.java @@ -0,0 +1,20 @@ +package com.repoachiever.mapping; + +import com.repoachiever.exception.RepositoryContentDestructionFailureException; +import jakarta.ws.rs.core.Response; +import jakarta.ws.rs.ext.ExceptionMapper; +import jakarta.ws.rs.ext.Provider; + +/** + * Represents mapper for RepositoryContentDestructionFailureExceptionMapper exception. + */ +@Provider +public class RepositoryContentDestructionFailureExceptionMapper + implements ExceptionMapper { + @Override + public Response toResponse(RepositoryContentDestructionFailureException e) { + return Response.status(Response.Status.BAD_REQUEST.getStatusCode()) + .entity(e.getMessage()) + .build(); + } +} diff --git a/api-server/src/main/java/com/repoachiever/mapping/WorkspaceUnitDirectoryNotFoundExceptionMapper.java b/api-server/src/main/java/com/repoachiever/mapping/WorkspaceUnitDirectoryNotFoundExceptionMapper.java new file mode 100644 index 0000000..36617a4 --- /dev/null +++ b/api-server/src/main/java/com/repoachiever/mapping/WorkspaceUnitDirectoryNotFoundExceptionMapper.java @@ -0,0 +1,18 @@ +package com.repoachiever.mapping; + +import com.repoachiever.exception.WorkspaceUnitDirectoryNotFoundException; +import jakarta.ws.rs.core.Response; +import jakarta.ws.rs.ext.ExceptionMapper; +import jakarta.ws.rs.ext.Provider; + +/** Represents mapper for WorkspaceUnitDirectoryNotFoundException exception. */ +@Provider +public class WorkspaceUnitDirectoryNotFoundExceptionMapper + implements ExceptionMapper { + @Override + public Response toResponse(WorkspaceUnitDirectoryNotFoundException e) { + return Response.status(Response.Status.BAD_REQUEST.getStatusCode()) + .entity(e.getMessage()) + .build(); + } +} diff --git a/api-server/src/main/java/com/repoachiever/repository/ConfigRepository.java b/api-server/src/main/java/com/repoachiever/repository/ConfigRepository.java new file mode 100644 index 0000000..cca8115 --- /dev/null +++ b/api-server/src/main/java/com/repoachiever/repository/ConfigRepository.java @@ -0,0 +1,123 @@ +package com.repoachiever.repository; + +import com.repoachiever.entity.common.PropertiesEntity; +import com.repoachiever.entity.repository.ConfigEntity; +import com.repoachiever.exception.QueryEmptyResultException; +import com.repoachiever.exception.QueryExecutionFailureException; +import com.repoachiever.exception.RepositoryOperationFailureException; +import com.repoachiever.repository.executor.RepositoryExecutor; +import jakarta.enterprise.context.ApplicationScoped; +import jakarta.inject.Inject; + +import javax.sql.DataSource; +import java.sql.ResultSet; +import java.sql.SQLException; + +/** + * Represents repository implementation to handle config table. + */ +@ApplicationScoped +public class ConfigRepository { + @Inject + PropertiesEntity properties; + + @Inject + RepositoryExecutor repositoryExecutor; + + /** + * Inserts given values into the config table. + * + * @param name given name of the configuration. + * @param hash given hash of the configuration. + * @throws RepositoryOperationFailureException if operation execution fails. + */ + public void insert(String name, String hash) throws RepositoryOperationFailureException { + try { + repositoryExecutor.performQuery( + String.format( + "INSERT INTO %s (name, hash) VALUES ('%s', '%s')", + properties.getDatabaseConfigTableName(), + name, + hash)); + + } catch (QueryExecutionFailureException | QueryEmptyResultException e) { + throw new RepositoryOperationFailureException(e.getMessage()); + } + } + + /** + * Checks if config entity with the given name is present. + * + * @param name given name of the configuration. + * @return result of the check. + * @throws RepositoryOperationFailureException if repository operation fails. + */ + public Boolean isPresentByName(String name) throws RepositoryOperationFailureException { + try { + ResultSet resultSet = repositoryExecutor.performQueryWithResult( + String.format( + "SELECT t.id, t.hash FROM %s as t WHERE t.name = '%s'", + properties.getDatabaseConfigTableName(), + name)); + + try { + resultSet.close(); + } catch (SQLException e) { + throw new RepositoryOperationFailureException(e.getMessage()); + } + } catch (QueryEmptyResultException e) { + return false; + } catch (QueryExecutionFailureException e) { + throw new RepositoryOperationFailureException(e.getMessage()); + } + + return true; + } + + /** + * Attempts to retrieve config entity by the given name. + * + * @param name given name of the configuration. + * @return retrieved config entity. + * @throws RepositoryOperationFailureException if repository operation fails. + */ + public ConfigEntity findByName(String name) throws RepositoryOperationFailureException { + ResultSet resultSet; + + try { + resultSet = + repositoryExecutor.performQueryWithResult( + String.format( + "SELECT t.id, t.hash FROM %s as t WHERE t.name = '%s'", + properties.getDatabaseConfigTableName(), + name)); + + } catch (QueryExecutionFailureException | QueryEmptyResultException e) { + throw new RepositoryOperationFailureException(e.getMessage()); + } + + Integer id; + + try { + id = resultSet.getInt("id"); + } catch (SQLException e) { + throw new RepositoryOperationFailureException(e.getMessage()); + } + + String hash; + + try { + hash = resultSet.getString("hash"); + } catch (SQLException e) { + throw new RepositoryOperationFailureException(e.getMessage()); + } + + try { + resultSet.close(); + } catch (SQLException e) { + throw new RepositoryOperationFailureException(e.getMessage()); + } + + return ConfigEntity.of(id, name, hash); + } +} diff --git a/api-server/src/main/java/com/repoachiever/repository/ContentRepository.java b/api-server/src/main/java/com/repoachiever/repository/ContentRepository.java new file mode 100644 index 0000000..ccd04d4 --- /dev/null +++ b/api-server/src/main/java/com/repoachiever/repository/ContentRepository.java @@ -0,0 +1,149 @@ +package com.repoachiever.repository; + +import com.repoachiever.entity.common.PropertiesEntity; +import com.repoachiever.entity.repository.ContentEntity; +import com.repoachiever.entity.repository.ProviderEntity; +import com.repoachiever.exception.QueryEmptyResultException; +import com.repoachiever.exception.QueryExecutionFailureException; +import com.repoachiever.exception.RepositoryOperationFailureException; +import com.repoachiever.repository.executor.RepositoryExecutor; +import jakarta.enterprise.context.ApplicationScoped; +import jakarta.inject.Inject; + +import javax.sql.DataSource; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.List; + +/** + * Represents repository implementation to handle content table. + */ +@ApplicationScoped +public class ContentRepository { + @Inject + PropertiesEntity properties; + + @Inject + RepositoryExecutor repositoryExecutor; + + /** + * Inserts given values into the content table. + * + * @param location given content location. + * @param provider given provider used for content retrieval. + * @param secret given secret, which allows content retrieval. + * @throws RepositoryOperationFailureException if operation execution fails. + */ + public void insert(String location, Integer provider, Integer secret) throws RepositoryOperationFailureException { + try { + repositoryExecutor.performQuery( + String.format( + "INSERT INTO %s (location, provider, secret) VALUES ('%s', %d, %d)", + properties.getDatabaseContentTableName(), + location, + provider, + secret)); + + } catch (QueryExecutionFailureException | QueryEmptyResultException e) { + throw new RepositoryOperationFailureException(e.getMessage()); + } + } + + /** + * Checks if content entity with the given location is present. + * + * @param location given location of the content. + * @return result of the check. + * @throws RepositoryOperationFailureException if repository operation fails. + */ + public Boolean isPresentByLocation(String location) throws RepositoryOperationFailureException { + try { + ResultSet resultSet = repositoryExecutor.performQueryWithResult( + String.format( + "SELECT t.id FROM %s as t WHERE t.location = '%s'", + properties.getDatabaseContentTableName(), + location)); + + try { + resultSet.close(); + } catch (SQLException e) { + throw new RepositoryOperationFailureException(e.getMessage()); + } + } catch (QueryEmptyResultException e) { + return false; + } catch (QueryExecutionFailureException e) { + throw new RepositoryOperationFailureException(e.getMessage()); + } + + return true; + } + + /** + * Retrieves all the persisted content entities. + * + * @return retrieved content entities. + * @throws RepositoryOperationFailureException if repository operation fails. + */ + public List findAll() throws RepositoryOperationFailureException { + ResultSet resultSet; + + try { + resultSet = + repositoryExecutor.performQueryWithResult( + String.format( + "SELECT t.id, t.location, t.provider, t.secret FROM %s as t", + properties.getDatabaseContentTableName())); + + } catch (QueryExecutionFailureException | QueryEmptyResultException e) { + throw new RepositoryOperationFailureException(e.getMessage()); + } + + List result = new ArrayList<>(); + + Integer id; + String location; + Integer provider; + Integer secret; + + try { + while (resultSet.next()) { + id = resultSet.getInt("id"); + location = resultSet.getString("location"); + provider = resultSet.getInt("provider"); + secret = resultSet.getInt("secret"); + + result.add(ContentEntity.of(id, location, provider, secret)); + } + } catch (SQLException e) { + throw new RepositoryOperationFailureException(e.getMessage()); + } + + try { + resultSet.close(); + } catch (SQLException e) { + throw new RepositoryOperationFailureException(e.getMessage()); + } + + return result; + } + + /** + * Deletes all entities with the given secret from content table. + * + * @param secret given secret, which allows content retrieval. + * @throws RepositoryOperationFailureException if operation execution fails. + */ + public void deleteBySecret(Integer secret) throws RepositoryOperationFailureException { + try { + repositoryExecutor.performQuery( + String.format( + "DELETE FROM %s as t WHERE t.secret = %d", + properties.getDatabaseContentTableName(), + secret)); + + } catch (QueryExecutionFailureException | QueryEmptyResultException e) { + throw new RepositoryOperationFailureException(e.getMessage()); + } + } +} diff --git a/api-server/src/main/java/com/repoachiever/repository/ProviderRepository.java b/api-server/src/main/java/com/repoachiever/repository/ProviderRepository.java new file mode 100644 index 0000000..8ab911c --- /dev/null +++ b/api-server/src/main/java/com/repoachiever/repository/ProviderRepository.java @@ -0,0 +1,153 @@ +package com.repoachiever.repository; + +import com.repoachiever.entity.common.PropertiesEntity; +import com.repoachiever.entity.repository.ConfigEntity; +import com.repoachiever.entity.repository.ProviderEntity; +import com.repoachiever.exception.QueryEmptyResultException; +import com.repoachiever.exception.QueryExecutionFailureException; +import com.repoachiever.exception.RepositoryOperationFailureException; +import com.repoachiever.repository.executor.RepositoryExecutor; +import jakarta.enterprise.context.ApplicationScoped; +import jakarta.inject.Inject; + +import javax.sql.DataSource; +import java.sql.ResultSet; +import java.sql.SQLException; + +/** + * Represents repository implementation to handle provider table. + */ +@ApplicationScoped +public class ProviderRepository { + @Inject + PropertiesEntity properties; + + @Inject + RepositoryExecutor repositoryExecutor; + + /** + * Inserts given values into the provider table. + * + * @param name given provider name. + * @throws RepositoryOperationFailureException if operation execution fails. + */ + public void insert(String name) throws RepositoryOperationFailureException { + try { + repositoryExecutor.performQuery( + String.format( + "INSERT INTO %s (name) VALUES ('%s')", + properties.getDatabaseProviderTableName(), + name)); + + } catch (QueryExecutionFailureException | QueryEmptyResultException e) { + throw new RepositoryOperationFailureException(e.getMessage()); + } + } + + /** + * Checks if provider entity with the given name is present. + * + * @param name given name of the provider. + * @return result of the check. + * @throws RepositoryOperationFailureException if repository operation fails. + */ + public Boolean isPresentByName(String name) throws RepositoryOperationFailureException { + try { + ResultSet resultSet = repositoryExecutor.performQueryWithResult( + String.format( + "SELECT t.id FROM %s as t WHERE t.name = '%s'", + properties.getDatabaseProviderTableName(), + name)); + + try { + resultSet.close(); + } catch (SQLException e) { + throw new RepositoryOperationFailureException(e.getMessage()); + } + } catch (QueryEmptyResultException e) { + return false; + } catch (QueryExecutionFailureException e) { + throw new RepositoryOperationFailureException(e.getMessage()); + } + + return true; + } + + /** + * Attempts to retrieve provider entity by the given name. + * + * @param name given name of the configuration. + * @return retrieved config entity. + * @throws RepositoryOperationFailureException if repository operation fails. + */ + public ProviderEntity findByName(String name) throws RepositoryOperationFailureException { + ResultSet resultSet; + + try { + resultSet = + repositoryExecutor.performQueryWithResult( + String.format( + "SELECT t.id FROM %s as t WHERE t.name = '%s'", + properties.getDatabaseProviderTableName(), + name)); + + } catch (QueryExecutionFailureException | QueryEmptyResultException e) { + throw new RepositoryOperationFailureException(e.getMessage()); + } + + Integer id; + + try { + id = resultSet.getInt("id"); + } catch (SQLException e) { + throw new RepositoryOperationFailureException(e.getMessage()); + } + + try { + resultSet.close(); + } catch (SQLException e) { + throw new RepositoryOperationFailureException(e.getMessage()); + } + + return ProviderEntity.of(id, name); + } + + /** + * Attempts to retrieve provider entity by the given identificator. + * + * @param id given identificator of the configuration. + * @return retrieved config entity. + * @throws RepositoryOperationFailureException if repository operation fails. + */ + public ProviderEntity findById(Integer id) throws RepositoryOperationFailureException { + ResultSet resultSet; + + try { + resultSet = + repositoryExecutor.performQueryWithResult( + String.format( + "SELECT t.name FROM %s as t WHERE t.id = '%s'", + properties.getDatabaseProviderTableName(), + id)); + + } catch (QueryExecutionFailureException | QueryEmptyResultException e) { + throw new RepositoryOperationFailureException(e.getMessage()); + } + + String name; + + try { + name = resultSet.getString("name"); + } catch (SQLException e) { + throw new RepositoryOperationFailureException(e.getMessage()); + } + + try { + resultSet.close(); + } catch (SQLException e) { + throw new RepositoryOperationFailureException(e.getMessage()); + } + + return ProviderEntity.of(id, name); + } +} diff --git a/api-server/src/main/java/com/repoachiever/repository/SecretRepository.java b/api-server/src/main/java/com/repoachiever/repository/SecretRepository.java new file mode 100644 index 0000000..9448bd9 --- /dev/null +++ b/api-server/src/main/java/com/repoachiever/repository/SecretRepository.java @@ -0,0 +1,194 @@ +package com.repoachiever.repository; + +import com.repoachiever.entity.common.PropertiesEntity; +import com.repoachiever.entity.repository.ProviderEntity; +import com.repoachiever.entity.repository.SecretEntity; +import com.repoachiever.exception.QueryEmptyResultException; +import com.repoachiever.exception.QueryExecutionFailureException; +import com.repoachiever.exception.RepositoryOperationFailureException; +import com.repoachiever.repository.executor.RepositoryExecutor; +import jakarta.enterprise.context.ApplicationScoped; +import jakarta.inject.Inject; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +import javax.sql.DataSource; +import java.sql.*; +import java.util.Optional; + +/** + * Represents repository implementation to handle secret table. + */ +@ApplicationScoped +public class SecretRepository { + @Inject + PropertiesEntity properties; + + @Inject + RepositoryExecutor repositoryExecutor; + + /** + * Inserts given values into the provider table. + * + * @param session given internal secret. + * @param credentials given optional external credentials. + * @throws RepositoryOperationFailureException if operation execution fails. + */ + public void insert(Integer session, Optional credentials) throws RepositoryOperationFailureException { + String query; + + if (credentials.isPresent()) { + query = String.format( + "INSERT INTO %s (session, credentials) VALUES (%d, '%s')", + properties.getDatabaseSecretTableName(), + session, + credentials.get()); + } else { + query = String.format( + "INSERT INTO %s (session) VALUES (%d)", + properties.getDatabaseSecretTableName(), + session); + } + + try { + repositoryExecutor.performQuery(query); + } catch (QueryExecutionFailureException | QueryEmptyResultException e) { + throw new RepositoryOperationFailureException(e.getMessage()); + } + } + + /** + * Checks if secret entity with the given session and credentials is present. + * + * @param session given session of the secrets set. + * @param credentials given optional external credentials. + * @return result of the check. + * @throws RepositoryOperationFailureException if repository operation fails. + */ + public Boolean isPresentBySessionAndCredentials(Integer session, Optional credentials) throws RepositoryOperationFailureException { + String query; + + if (credentials.isPresent()) { + query = String.format( + "SELECT t.id FROM %s as t WHERE t.session = %d AND t.credentials = '%s'", + properties.getDatabaseSecretTableName(), + session, + credentials.get()); + } else { + query = String.format( + "SELECT t.id FROM %s as t WHERE t.session = %d", + properties.getDatabaseSecretTableName(), + session); + } + + try { + ResultSet resultSet = repositoryExecutor.performQueryWithResult(query); + + try { + resultSet.close(); + } catch (SQLException e) { + throw new RepositoryOperationFailureException(e.getMessage()); + } + } catch (QueryEmptyResultException e) { + return false; + } catch (QueryExecutionFailureException e) { + throw new RepositoryOperationFailureException(e.getMessage()); + } + + return true; + } + + /** + * Attempts to retrieve secret entity by the given session and credentials. + * + * @param session given session of the secrets set. + * @param credentials given optional external credentials. + * @return retrieved secret entity. + * @throws RepositoryOperationFailureException if repository operation fails. + */ + public SecretEntity findBySessionAndCredentials(Integer session, Optional credentials) throws RepositoryOperationFailureException { + String query; + + if (credentials.isPresent()) { + query = String.format( + "SELECT t.id FROM %s as t WHERE t.session = %d AND t.credentials = '%s'", + properties.getDatabaseSecretTableName(), + session, + credentials.get()); + } else { + query = String.format( + "SELECT t.id FROM %s as t WHERE t.session = %d", + properties.getDatabaseSecretTableName(), + session); + } + + ResultSet resultSet; + + try { + resultSet = repositoryExecutor.performQueryWithResult(query); + } catch (QueryExecutionFailureException | QueryEmptyResultException e) { + throw new RepositoryOperationFailureException(e.getMessage()); + } + + Integer id; + + try { + id = resultSet.getInt("id"); + } catch (SQLException e) { + throw new RepositoryOperationFailureException(e.getMessage()); + } + + try { + resultSet.close(); + } catch (SQLException e) { + throw new RepositoryOperationFailureException(e.getMessage()); + } + + return SecretEntity.of(id, session, credentials); + } + + /** + * Attempts to retrieve secret entity by the given identificator. + * + * @param id given identificator of the secrets set. + * @return retrieved secret entity. + * @throws RepositoryOperationFailureException if repository operation fails. + */ + public SecretEntity findById(Integer id) throws RepositoryOperationFailureException { + ResultSet resultSet; + + try { + resultSet = repositoryExecutor.performQueryWithResult(String.format( + "SELECT t.session, t.credentials FROM %s as t WHERE t.id = %d", + properties.getDatabaseSecretTableName(), + id)); + + } catch (QueryExecutionFailureException | QueryEmptyResultException e) { + throw new RepositoryOperationFailureException(e.getMessage()); + } + + Integer session; + + try { + session = resultSet.getInt("session"); + } catch (SQLException e) { + throw new RepositoryOperationFailureException(e.getMessage()); + } + + String credentials; + + try { + credentials = resultSet.getString("credentials"); + } catch (SQLException e) { + throw new RepositoryOperationFailureException(e.getMessage()); + } + + try { + resultSet.close(); + } catch (SQLException e) { + throw new RepositoryOperationFailureException(e.getMessage()); + } + + return SecretEntity.of(id, session, Optional.ofNullable(credentials)); + } +} diff --git a/api-server/src/main/java/com/repoachiever/repository/common/RepositoryConfigurationHelper.java b/api-server/src/main/java/com/repoachiever/repository/common/RepositoryConfigurationHelper.java new file mode 100644 index 0000000..0112a8e --- /dev/null +++ b/api-server/src/main/java/com/repoachiever/repository/common/RepositoryConfigurationHelper.java @@ -0,0 +1,57 @@ +package com.repoachiever.repository.common; + +import com.repoachiever.model.CredentialsFieldsExternal; +import com.repoachiever.model.CredentialsFieldsFull; +import com.repoachiever.model.CredentialsFieldsInternal; +import com.repoachiever.model.Provider; +import java.util.Optional; + +/** + * Contains helpful tools used for repository configuration. + */ +public class RepositoryConfigurationHelper { + /** + * Extracts external credentials from the given credentials field as optional . + * + * @param provider given vendor provider. + * @param credentialsFieldExternal given credentials field. + * @return extracted external credentials as optional. + */ + public static Optional getExternalCredentials( + Provider provider, CredentialsFieldsExternal credentialsFieldExternal) { + return switch (provider) { + case LOCAL -> Optional.empty(); + case GITHUB -> Optional.ofNullable(credentialsFieldExternal.getToken()); + }; + } + + /** + * Converts given raw provider to content provider. + * + * @param value given raw provider. + * @return converted content provider. + */ + public static Provider convertRawProviderToContentProvider(String value) { + return Provider.fromString(value); + } + + /** + * Converts given raw secrets to common credentials according to the given provider. + * + * @param provider given provider. + * @param session given session identificator. + * @param credentials given raw credentials. + * @return converted common credentials. + */ + public static CredentialsFieldsFull convertRawSecretsToContentCredentials( + Provider provider, Integer session, Optional credentials) { + return switch (provider) { + case LOCAL -> CredentialsFieldsFull.of( + CredentialsFieldsInternal.of(session), + null); + case GITHUB -> CredentialsFieldsFull.of( + CredentialsFieldsInternal.of(session), + CredentialsFieldsExternal.of(credentials.get())); + }; + } +} diff --git a/api-server/src/main/java/com/repoachiever/repository/executor/RepositoryExecutor.java b/api-server/src/main/java/com/repoachiever/repository/executor/RepositoryExecutor.java new file mode 100644 index 0000000..d818419 --- /dev/null +++ b/api-server/src/main/java/com/repoachiever/repository/executor/RepositoryExecutor.java @@ -0,0 +1,154 @@ +package com.repoachiever.repository.executor; + +import com.repoachiever.entity.common.PropertiesEntity; +import com.repoachiever.exception.QueryEmptyResultException; +import com.repoachiever.exception.QueryExecutionFailureException; +import com.repoachiever.service.cluster.resource.ClusterCommunicationResource; +import jakarta.annotation.PostConstruct; +import jakarta.annotation.PreDestroy; +import jakarta.enterprise.context.ApplicationScoped; +import jakarta.inject.Inject; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +import javax.sql.DataSource; +import java.sql.Connection; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Statement; +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.TimeUnit; + +/** + * Service used to perform low-level database related operations. + */ +@ApplicationScoped +public class RepositoryExecutor { + private static final Logger logger = LogManager.getLogger(ClusterCommunicationResource.class); + + @Inject + PropertiesEntity properties; + + @Inject + DataSource dataSource; + + private Connection connection; + + private final List statements = new ArrayList<>(); + + private final ScheduledExecutorService scheduledExecutorService = + Executors.newSingleThreadScheduledExecutor(); + + @PostConstruct + public void configure() { + try { + this.connection = dataSource.getConnection(); + } catch (SQLException e) { + logger.fatal(new QueryExecutionFailureException(e.getMessage()).getMessage()); + } + } + + /** + * Performs given SQL query without result. + * + * @param query given SQL query to be executed. + * @throws QueryExecutionFailureException if query execution is interrupted by failure. + * @throws QueryEmptyResultException if result is empty. + */ + public void performQuery(String query) throws QueryExecutionFailureException, QueryEmptyResultException { + Statement statement; + + try { + statement = this.connection.createStatement(); + } catch (SQLException e) { + throw new QueryExecutionFailureException(e.getMessage()); + } + + try { + statement.executeUpdate(query); + } catch (SQLException e) { + throw new QueryExecutionFailureException(e.getMessage()); + } + + statements.add(statement); + + scheduledExecutorService.schedule(() -> { + try { + statement.close(); + } catch (SQLException e) { + logger.fatal(new QueryExecutionFailureException(e.getMessage()).getMessage()); + } + }, properties.getDatabaseStatementCloseDelay(), TimeUnit.MILLISECONDS); + } + + /** + * Performs given SQL query and returns raw result. + * + * @param query given SQL query to be executed. + * @return retrieved raw result. + * @throws QueryExecutionFailureException if query execution is interrupted by failure. + * @throws QueryEmptyResultException if result is empty. + */ + public ResultSet performQueryWithResult(String query) throws QueryExecutionFailureException, QueryEmptyResultException { + Statement statement; + + try { + statement = this.connection.createStatement(); + } catch (SQLException e) { + throw new QueryExecutionFailureException(e.getMessage()); + } + + ResultSet resultSet; + + try { + resultSet = statement.executeQuery(query); + } catch (SQLException e) { + throw new QueryExecutionFailureException(e.getMessage()); + } + + statements.add(statement); + + scheduledExecutorService.schedule(() -> { + try { + statement.close(); + } catch (SQLException e) { + logger.fatal(new QueryExecutionFailureException(e.getMessage()).getMessage()); + } + }, properties.getDatabaseStatementCloseDelay(), TimeUnit.MILLISECONDS); + + try { + if (!resultSet.isBeforeFirst()) { + throw new QueryEmptyResultException(); + } + } catch (SQLException e) { + throw new QueryExecutionFailureException(e.getMessage()); + } + + return resultSet; + } + + /** + * Closes opened database connection. + */ + @PreDestroy + private void close() { + statements.forEach(element -> { + try { + if (!element.isClosed()) { + element.close(); + } + } catch (SQLException e) { + logger.fatal(new QueryExecutionFailureException(e.getMessage()).getMessage()); + } + }); + + try { + this.connection.close(); + } catch (SQLException e) { + logger.fatal(e.getMessage()); + } + } +} diff --git a/api-server/src/main/java/com/repoachiever/repository/facade/RepositoryFacade.java b/api-server/src/main/java/com/repoachiever/repository/facade/RepositoryFacade.java new file mode 100644 index 0000000..a77ec68 --- /dev/null +++ b/api-server/src/main/java/com/repoachiever/repository/facade/RepositoryFacade.java @@ -0,0 +1,212 @@ +package com.repoachiever.repository.facade; + +import com.repoachiever.dto.RepositoryContentUnitDto; +import com.repoachiever.entity.repository.ContentEntity; +import com.repoachiever.entity.repository.ProviderEntity; +import com.repoachiever.entity.repository.SecretEntity; +import com.repoachiever.exception.ContentApplicationRetrievalFailureException; +import com.repoachiever.exception.RepositoryContentApplicationFailureException; +import com.repoachiever.exception.RepositoryContentDestructionFailureException; +import com.repoachiever.exception.RepositoryOperationFailureException; +import com.repoachiever.model.*; +import com.repoachiever.repository.ConfigRepository; +import com.repoachiever.repository.ContentRepository; +import com.repoachiever.repository.ProviderRepository; +import com.repoachiever.repository.SecretRepository; +import com.repoachiever.repository.common.RepositoryConfigurationHelper; +import jakarta.enterprise.context.ApplicationScoped; +import jakarta.inject.Inject; + +import java.util.*; +import java.util.stream.Collectors; + +import static java.util.stream.Collectors.groupingBy; + +/** + * Represents facade for repository implementations used to handle tables. + */ +@ApplicationScoped +public class RepositoryFacade { + @Inject + ConfigRepository configRepository; + + @Inject + ContentRepository contentRepository; + + @Inject + ProviderRepository providerRepository; + + @Inject + SecretRepository secretRepository; + + /** + * Retrieves content state for the given configuration properties. + * + * @param contentStateApplication given content state retrieve application. + * @return retrieve content state hash. + */ + public String retrieveContentState(ContentStateApplication contentStateApplication) { + return ""; + } + + /** + * Retrieves all the available locations for the given configuration properties. + * + * @param contentRetrievalApplication given content retrieval application. + * @return retrieved locations for the given configuration properties. + */ + public List retrieveLocations(ContentRetrievalApplication contentRetrievalApplication) { + return null; + } + + /** + * Retrieves all the data from content repository in a form of content applications. + * + * @return retrieved set of content applications. + * @throws ContentApplicationRetrievalFailureException if content application retrieval fails. + */ + public List retrieveContentApplication() throws ContentApplicationRetrievalFailureException { + List result = new ArrayList<>(); + + List units = new ArrayList<>(); + + List contents; + + try { + contents = contentRepository.findAll(); + } catch (RepositoryOperationFailureException e) { + throw new ContentApplicationRetrievalFailureException(e.getMessage()); + } + + for (ContentEntity content : contents) { + ProviderEntity rawProvider; + + try { + rawProvider = providerRepository.findById(content.getProvider()); + } catch (RepositoryOperationFailureException e) { + throw new ContentApplicationRetrievalFailureException(e.getMessage()); + } + + SecretEntity rawSecret; + + try { + rawSecret = secretRepository.findById(content.getSecret()); + } catch (RepositoryOperationFailureException e) { + throw new ContentApplicationRetrievalFailureException(e.getMessage()); + } + + Provider provider = + RepositoryConfigurationHelper.convertRawProviderToContentProvider( + rawProvider.getName()); + + CredentialsFieldsFull credentials = + RepositoryConfigurationHelper.convertRawSecretsToContentCredentials( + provider, rawSecret.getSession(), rawSecret.getCredentials()); + + units.add(RepositoryContentUnitDto.of( + content.getLocation(), + provider, + credentials)); + } + + Map>> groups = + units + .stream() + .collect( + groupingBy( + RepositoryContentUnitDto::getCredentials, + groupingBy(RepositoryContentUnitDto::getProvider))); + + groups + .forEach((key1, value1) -> { + value1 + .forEach((key2, value2) -> { + result.add( + ContentApplication.of(value2.stream().map(RepositoryContentUnitDto::getLocation).toList(), key2, key1)); + }); + }); + + return result; + } + + /** + * Applies given content application, updating previous state. + * + * @param contentApplication given content application used for topology configuration. + * @throws RepositoryContentApplicationFailureException if RepoAchiever Cluster repository content application failed. + */ + public void apply(ContentApplication contentApplication) throws RepositoryContentApplicationFailureException { + ProviderEntity provider; + + try { + provider = providerRepository.findByName(contentApplication.getProvider().toString()); + } catch (RepositoryOperationFailureException e) { + throw new RepositoryContentApplicationFailureException(e.getMessage()); + } + + Optional credentials = RepositoryConfigurationHelper.getExternalCredentials( + contentApplication.getProvider(), contentApplication.getCredentials().getExternal()); + + try { + if (!secretRepository.isPresentBySessionAndCredentials( + contentApplication.getCredentials().getInternal().getId(), credentials)) { + secretRepository.insert( + contentApplication.getCredentials().getInternal().getId(), + credentials); + } + } catch (RepositoryOperationFailureException e) { + throw new RepositoryContentApplicationFailureException(e.getMessage()); + } + + SecretEntity secret; + + try { + secret = secretRepository.findBySessionAndCredentials( + contentApplication.getCredentials().getInternal().getId(), + credentials); + } catch (RepositoryOperationFailureException e) { + throw new RepositoryContentApplicationFailureException(e.getMessage()); + } + + try { + contentRepository.deleteBySecret(secret.getId()); + } catch (RepositoryOperationFailureException e) { + throw new RepositoryContentApplicationFailureException(e.getMessage()); + } + + for (String location : contentApplication.getLocations()) { + try { + contentRepository.insert(location, provider.getId(), secret.getId()); + } catch (RepositoryOperationFailureException e) { + throw new RepositoryContentApplicationFailureException(e.getMessage()); + } + } + } + + /** + * Applies given content withdrawal, removing previous state. + * + * @param contentWithdrawal given content application used for topology configuration. + * @throws RepositoryContentDestructionFailureException if RepoAchiever Cluster repository content destruction failed. + */ + public void destroy(ContentWithdrawal contentWithdrawal) throws RepositoryContentDestructionFailureException { + Optional credentials = RepositoryConfigurationHelper.getExternalCredentials( + contentWithdrawal.getProvider(), contentWithdrawal.getCredentials().getExternal()); + + SecretEntity secret; + + try { + secret = secretRepository.findBySessionAndCredentials( + contentWithdrawal.getCredentials().getInternal().getId(), + credentials); + } catch (RepositoryOperationFailureException e) { + throw new RepositoryContentDestructionFailureException(e.getMessage()); + } + + try { + contentRepository.deleteBySecret(secret.getId()); + } catch (RepositoryOperationFailureException e) { + throw new RepositoryContentDestructionFailureException(e.getMessage()); + } + } +} diff --git a/api-server/src/main/java/com/repoachiever/resource/ContentResource.java b/api-server/src/main/java/com/repoachiever/resource/ContentResource.java new file mode 100644 index 0000000..01019b5 --- /dev/null +++ b/api-server/src/main/java/com/repoachiever/resource/ContentResource.java @@ -0,0 +1,128 @@ +package com.repoachiever.resource; + +import com.repoachiever.api.ContentResourceApi; +import com.repoachiever.exception.CredentialsAreNotValidException; +import com.repoachiever.exception.CredentialsFieldIsNotValidException; +import com.repoachiever.exception.LocationsFieldIsNotValidException; +import com.repoachiever.model.*; +import com.repoachiever.repository.facade.RepositoryFacade; +import com.repoachiever.resource.common.ResourceConfigurationHelper; +import com.repoachiever.service.cluster.facade.ClusterFacade; +import com.repoachiever.service.vendor.VendorFacade; +import jakarta.enterprise.context.ApplicationScoped; +import jakarta.inject.Inject; +import jakarta.transaction.Transactional; +import jakarta.ws.rs.BadRequestException; +import lombok.SneakyThrows; + +import java.io.File; +import java.util.Objects; + +/** Contains implementation of ContentResource. */ +@ApplicationScoped +public class ContentResource implements ContentResourceApi { + @Inject + RepositoryFacade repositoryFacade; + + @Inject + ClusterFacade clusterFacade; + + @Inject + VendorFacade vendorFacade; + + /** + * Implementation for declared in OpenAPI configuration v1ContentPost method. + * + * @param contentRetrievalApplication content retrieval application. + * @return retrieved content result. + */ + @Override + public ContentRetrievalResult v1ContentPost(ContentRetrievalApplication contentRetrievalApplication) { + if (Objects.isNull(contentRetrievalApplication)) { + throw new BadRequestException(); + } + + return ContentRetrievalResult.of( + repositoryFacade.retrieveLocations(contentRetrievalApplication)); + } + + /** + * Implementation for declared in OpenAPI configuration v1ContentApplyPost method. + * + * @param contentApplication content configuration application. + */ + @Override + @SneakyThrows + public void v1ContentApplyPost(ContentApplication contentApplication) { + if (Objects.isNull(contentApplication)) { + throw new BadRequestException(); + } + + if (!ResourceConfigurationHelper.isExternalCredentialsFieldValid( + contentApplication.getProvider(), contentApplication.getCredentials().getExternal())) { + throw new CredentialsFieldIsNotValidException(); + } + + if (!ResourceConfigurationHelper.isLocationsDuplicate(contentApplication.getLocations())) { + throw new LocationsFieldIsNotValidException(); + } + + if (!vendorFacade.isExternalCredentialsValid( + contentApplication.getProvider(), contentApplication.getCredentials().getExternal())) { + throw new CredentialsAreNotValidException(); + } + + clusterFacade.apply(contentApplication); + + repositoryFacade.apply(contentApplication); + } + + /** + * Implementation for declared in OpenAPI configuration v1ContentWithdrawDelete method. + * + * @param contentWithdrawal content withdrawal application. + */ + @Override + @SneakyThrows + public void v1ContentWithdrawDelete(ContentWithdrawal contentWithdrawal) { + if (Objects.isNull(contentWithdrawal)) { + throw new BadRequestException(); + } + + if (!ResourceConfigurationHelper.isExternalCredentialsFieldValid( + contentWithdrawal.getProvider(), contentWithdrawal.getCredentials().getExternal())) { + throw new CredentialsFieldIsNotValidException(); + } + + if (!vendorFacade.isExternalCredentialsValid( + contentWithdrawal.getProvider(), contentWithdrawal.getCredentials().getExternal())) { + throw new CredentialsAreNotValidException(); + } + + clusterFacade.destroy(contentWithdrawal); + + repositoryFacade.destroy(contentWithdrawal); + } + + /** + * Implementation for declared in OpenAPI configuration v1ContentDownloadGet method. + * + * @param location name of content location to be downloaded. + * @return downloaded content result. + */ + @Override + public File v1ContentDownloadGet(String location) { + + return null; + } + + /** + * Implementation for declared in OpenAPI configuration v1ContentCleanPost method. + * + * @param contentCleanup content cleanup application. + */ + @Override + public void v1ContentCleanPost(ContentCleanup contentCleanup) { + + } +} diff --git a/api-server/src/main/java/com/repoachiever/resource/HealthResource.java b/api-server/src/main/java/com/repoachiever/resource/HealthResource.java new file mode 100644 index 0000000..30b0666 --- /dev/null +++ b/api-server/src/main/java/com/repoachiever/resource/HealthResource.java @@ -0,0 +1,65 @@ +package com.repoachiever.resource; + +import com.repoachiever.api.HealthResourceApi; +import com.repoachiever.entity.common.PropertiesEntity; +import com.repoachiever.model.HealthCheckResult; +import com.repoachiever.model.ReadinessCheckApplication; +import com.repoachiever.model.ReadinessCheckResult; +import com.repoachiever.service.client.smallrye.ISmallRyeHealthCheckClientService; +import com.repoachiever.service.workspace.WorkspaceService; +import com.repoachiever.service.workspace.facade.WorkspaceFacade; +import jakarta.enterprise.context.ApplicationScoped; +import jakarta.inject.Inject; +import jakarta.ws.rs.BadRequestException; +import jakarta.ws.rs.WebApplicationException; +import org.eclipse.microprofile.rest.client.inject.RestClient; + +import java.util.Objects; + +/** + * Contains implementation of HealthResource. + */ +@ApplicationScoped +public class HealthResource implements HealthResourceApi { + @Inject + PropertiesEntity properties; + + @Inject + WorkspaceFacade workspaceFacade; + + @Inject + WorkspaceService workspaceService; + + @Inject + @RestClient + ISmallRyeHealthCheckClientService smallRyeHealthCheckClientService; + + /** + * Implementation for declared in OpenAPI configuration v1HealthGet method. + * + * @return health check result. + */ + @Override + public HealthCheckResult v1HealthGet() { + try { + return smallRyeHealthCheckClientService.qHealthGet(); + } catch (WebApplicationException e) { + return e.getResponse().readEntity(HealthCheckResult.class); + } + } + + /** + * Implementation for declared in OpenAPI configuration v1ReadinessPost method. + * + * @param readinessCheckApplication application used to perform application readiness check. + * @return readiness check result. + */ + @Override + public ReadinessCheckResult v1ReadinessPost(ReadinessCheckApplication readinessCheckApplication) { + if (Objects.isNull(readinessCheckApplication)) { + throw new BadRequestException(); + } + + return null; + } +} diff --git a/api-server/src/main/java/com/repoachiever/resource/InfoResource.java b/api-server/src/main/java/com/repoachiever/resource/InfoResource.java new file mode 100644 index 0000000..a648576 --- /dev/null +++ b/api-server/src/main/java/com/repoachiever/resource/InfoResource.java @@ -0,0 +1,56 @@ +package com.repoachiever.resource; + +import com.repoachiever.api.InfoResourceApi; +import com.repoachiever.entity.common.PropertiesEntity; +import com.repoachiever.model.ClusterInfoUnit; +import com.repoachiever.model.VersionExternalApiInfoResult; +import com.repoachiever.model.VersionInfoResult; +import jakarta.enterprise.context.ApplicationScoped; +import jakarta.inject.Inject; + +import java.util.List; + +/** + * Contains implementation of InfoResource. + */ +@ApplicationScoped +public class InfoResource implements InfoResourceApi { + @Inject + PropertiesEntity properties; + + /** + * Implementation for declared in OpenAPI configuration v1InfoVersionGet method. + * + * @return version information result. + */ + @Override + public VersionInfoResult v1InfoVersionGet() { + return VersionInfoResult.of( + VersionExternalApiInfoResult.of( + properties.getApplicationVersion(), properties.getGitCommitId())); + } + + /** + * Implementation for declared in OpenAPI configuration v1InfoClusterGet method. + * + * @return cluster information result. + */ + @Override + public List v1InfoClusterGet() { + // TODO: call cluster service to retrieve data from clusters. + + return null; + } + + /** + * Implementation for declared in OpenAPI configuration v1InfoTelemetryGet method. + * + * @return telemetry information result. + */ + @Override + public String v1InfoTelemetryGet() { + // TODO: call telemetry service to retrieve data. + + return null; + } +} diff --git a/api-server/src/main/java/com/repoachiever/resource/StateResource.java b/api-server/src/main/java/com/repoachiever/resource/StateResource.java new file mode 100644 index 0000000..eff6f61 --- /dev/null +++ b/api-server/src/main/java/com/repoachiever/resource/StateResource.java @@ -0,0 +1,28 @@ +package com.repoachiever.resource; + +import com.repoachiever.api.StateResourceApi; +import com.repoachiever.model.ContentStateApplication; +import com.repoachiever.model.ContentStateApplicationResult; +import jakarta.enterprise.context.ApplicationScoped; +import jakarta.ws.rs.BadRequestException; + +import java.util.Objects; + +/** Contains implementation of StateResource. */ +@ApplicationScoped +public class StateResource implements StateResourceApi { + /** + * Implementation for declared in OpenAPI configuration v1StateContentPost method. + * + * @param contentStateApplication application used to perform content state retrieval. + * @return retrieved state content hash. + */ + @Override + public ContentStateApplicationResult v1StateContentPost(ContentStateApplication contentStateApplication) { + if (Objects.isNull(contentStateApplication)) { + throw new BadRequestException(); + } + + return null; + } +} diff --git a/api-server/src/main/java/com/repoachiever/resource/common/ResourceConfigurationHelper.java b/api-server/src/main/java/com/repoachiever/resource/common/ResourceConfigurationHelper.java new file mode 100644 index 0000000..e5303a5 --- /dev/null +++ b/api-server/src/main/java/com/repoachiever/resource/common/ResourceConfigurationHelper.java @@ -0,0 +1,37 @@ +package com.repoachiever.resource.common; + +import com.repoachiever.model.CredentialsFieldsExternal; +import com.repoachiever.model.Provider; + +import java.util.List; +import java.util.Objects; + +/** + * Contains helpful tools used for resource configuration. + */ +public class ResourceConfigurationHelper { + /** + * Checks if the given external credentials field is valid according to the used provider. + * + * @param provider given vendor provider. + * @param credentialsFieldExternal given credentials field. + * @return result of the check. + */ + public static Boolean isExternalCredentialsFieldValid( + Provider provider, CredentialsFieldsExternal credentialsFieldExternal) { + return switch (provider) { + case LOCAL -> true; + case GITHUB -> Objects.nonNull(credentialsFieldExternal); + }; + } + + /** + * Checks if the given locations have duplicates. + * + * @param locations given locations. + * @return result of the check. + */ + public static Boolean isLocationsDuplicate(List locations) { + return locations.stream().distinct().count() == locations.size(); + } +} diff --git a/api-server/src/main/java/com/repoachiever/resource/communication/ApiServerCommunicationResource.java b/api-server/src/main/java/com/repoachiever/resource/communication/ApiServerCommunicationResource.java new file mode 100644 index 0000000..e044d4b --- /dev/null +++ b/api-server/src/main/java/com/repoachiever/resource/communication/ApiServerCommunicationResource.java @@ -0,0 +1,56 @@ +package com.repoachiever.resource.communication; + +import com.repoachiever.entity.common.PropertiesEntity; +import com.repoachiever.service.communication.apiserver.IApiServerCommunicationService; +import com.repoachiever.service.integration.diagnostics.DiagnosticsConfigService; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +import java.io.InputStream; +import java.rmi.RemoteException; +import java.rmi.server.UnicastRemoteObject; + +/** + * Contains implementation of communication provider for RepoAchiever API Server. + */ +public class ApiServerCommunicationResource extends UnicastRemoteObject implements IApiServerCommunicationService { + private static final Logger logger = LogManager.getLogger(ApiServerCommunicationResource.class); + + private final PropertiesEntity properties; + + public ApiServerCommunicationResource(PropertiesEntity properties) throws RemoteException { + this.properties = properties; + } + + /** + * @see IApiServerCommunicationService + */ + @Override + public void performRawContentUpload(String workspaceUnitKey, InputStream content) throws RemoteException { + + } + + /** + * @see IApiServerCommunicationService + */ + @Override + public void performAdditionalContentUpload(String workspaceUnitKey, String content) throws RemoteException { + + } + + /** + * @see IApiServerCommunicationService + */ + @Override + public void performLogsTransfer(String name, String message) throws RemoteException { + logger.info(String.format("Transferred logs(instance: %s): %s", name, message)); + } + + /** + * @see IApiServerCommunicationService + */ + @Override + public Boolean retrieveHealthCheck() throws RemoteException { + return true; + } +} diff --git a/api-server/src/main/java/com/repoachiever/service/client/github/IGitHubClientService.java b/api-server/src/main/java/com/repoachiever/service/client/github/IGitHubClientService.java new file mode 100644 index 0000000..d7f0afa --- /dev/null +++ b/api-server/src/main/java/com/repoachiever/service/client/github/IGitHubClientService.java @@ -0,0 +1,19 @@ +package com.repoachiever.service.client.github; + +import jakarta.ws.rs.GET; +import jakarta.ws.rs.Path; +import jakarta.ws.rs.Produces; +import jakarta.ws.rs.core.HttpHeaders; +import jakarta.ws.rs.core.MediaType; +import jakarta.ws.rs.core.Response; +import org.eclipse.microprofile.rest.client.inject.RegisterRestClient; +import org.jboss.resteasy.annotations.jaxrs.HeaderParam; + +/** Represents client for GitHub remote API. */ +@RegisterRestClient(configKey = "github") +public interface IGitHubClientService { + @GET + @Path("/octocat") + @Produces(MediaType.APPLICATION_JSON) + Response getOctocat(@HeaderParam(HttpHeaders.AUTHORIZATION) String token); +} diff --git a/api-server/src/main/java/com/repoachiever/service/client/smallrye/ISmallRyeHealthCheckClientService.java b/api-server/src/main/java/com/repoachiever/service/client/smallrye/ISmallRyeHealthCheckClientService.java new file mode 100644 index 0000000..ece7ff1 --- /dev/null +++ b/api-server/src/main/java/com/repoachiever/service/client/smallrye/ISmallRyeHealthCheckClientService.java @@ -0,0 +1,18 @@ +package com.repoachiever.service.client.smallrye; + +import com.repoachiever.model.HealthCheckResult; +import jakarta.ws.rs.GET; +import jakarta.ws.rs.Path; +import jakarta.ws.rs.Produces; +import jakarta.ws.rs.core.MediaType; +import org.eclipse.microprofile.rest.client.inject.RegisterRestClient; + +/** Represents client for SmallRye health check endpoints. */ +@Path("/q") +@RegisterRestClient(configKey = "small-rye-health-check") +public interface ISmallRyeHealthCheckClientService { + @GET + @Path("/health") + @Produces(MediaType.APPLICATION_JSON) + HealthCheckResult qHealthGet(); +} diff --git a/api-server/src/main/java/com/repoachiever/service/cluster/ClusterService.java b/api-server/src/main/java/com/repoachiever/service/cluster/ClusterService.java new file mode 100644 index 0000000..6d2005b --- /dev/null +++ b/api-server/src/main/java/com/repoachiever/service/cluster/ClusterService.java @@ -0,0 +1,174 @@ +package com.repoachiever.service.cluster; + +import com.repoachiever.dto.CommandExecutorOutputDto; +import com.repoachiever.entity.common.PropertiesEntity; +import com.repoachiever.exception.*; +import com.repoachiever.service.cluster.common.ClusterConfigurationHelper; +import com.repoachiever.service.cluster.resource.ClusterCommunicationResource; +import com.repoachiever.service.command.cluster.deploy.ClusterDeployCommandService; +import com.repoachiever.service.command.cluster.destroy.ClusterDestroyCommandService; +import com.repoachiever.service.executor.CommandExecutorService; +import jakarta.enterprise.context.ApplicationScoped; +import jakarta.inject.Inject; + +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; + +/** + * Service used for cluster deployment management, including distribution process. + */ +@ApplicationScoped +public class ClusterService { + @Inject + PropertiesEntity properties; + + @Inject + ClusterCommunicationResource clusterCommunicationResource; + + @Inject + CommandExecutorService commandExecutorService; + + /** + * Perform segregation of the given content locations according to the given segregation limitations. + * + * @param locations given content locations. + * @param separator given content location segregation separator. + * @return segregated content locations. + */ + public List> performContentLocationsSegregation(List locations, Integer separator) { + List> result = new ArrayList<>(); + + List temp = new ArrayList<>(); + + Integer counter = 0; + + for (Integer i = 0; i < locations.size(); i++) { + temp.add(locations.get(0)); + + if (counter.equals(separator - 1)) { + result.add(new ArrayList<>(temp)); + + temp.clear(); + + counter = 0; + } else { + counter++; + } + } + + if (!temp.isEmpty()) { + result.add(new ArrayList<>(temp)); + } + + return result; + } + + /** + * Performs deployment of RepoAchiever Cluster allocation. + * + * @param name given RepoAchiever Cluster allocation name. + * @param clusterContext given RepoAchiever Cluster context. + * @return process identificator of the deployed RepoAchiever Cluster instance. + * @throws ClusterDeploymentFailureException if deployment operation failed. + */ + public Integer deploy(String name, String clusterContext) throws ClusterDeploymentFailureException { + ClusterDeployCommandService clusterDeployCommandService = + new ClusterDeployCommandService( + clusterContext, + properties.getBinDirectory(), + properties.getBinClusterLocation()); + + CommandExecutorOutputDto clusterDeployCommandOutput; + + try { + clusterDeployCommandOutput = + commandExecutorService.executeCommand(clusterDeployCommandService); + } catch (CommandExecutorException e) { + throw new ClusterDeploymentFailureException(e.getMessage()); + } + + String clusterDeployCommandErrorOutput = clusterDeployCommandOutput.getErrorOutput(); + + if (Objects.nonNull(clusterDeployCommandErrorOutput) && !clusterDeployCommandErrorOutput.isEmpty()) { + throw new ClusterDeploymentFailureException(); + } + + Integer result = Integer.parseInt( + clusterDeployCommandOutput. + getNormalOutput(). + replaceAll("\n", "")); + + if (!ClusterConfigurationHelper.waitForStart(() -> { + try { + if (clusterCommunicationResource.retrieveHealthCheck(name)) { + return true; + } + } catch (ClusterOperationFailureException e) { + return false; + } + + return false; + }, + properties.getCommunicationClusterStartupAwaitFrequency(), + properties.getCommunicationClusterStartupTimeout())) { + throw new ClusterDeploymentFailureException(new ClusterApplicationTimeoutException().getMessage()); + } + + return result; + } + + /** + * Performs destruction of RepoAchiever Cluster allocation. + * + * @param pid given RepoAchiever Cluster allocation process id. + * @throws ClusterDestructionFailureException if destruction operation failed. + */ + public void destroy(Integer pid) throws ClusterDestructionFailureException { + ClusterDestroyCommandService clusterDestroyCommandService = new ClusterDestroyCommandService(pid); + + CommandExecutorOutputDto clusterDestroyCommandOutput; + + try { + clusterDestroyCommandOutput = + commandExecutorService.executeCommand(clusterDestroyCommandService); + } catch (CommandExecutorException e) { + throw new ClusterDestructionFailureException(e.getMessage()); + } + + String clusterDestroyCommandErrorOutput = clusterDestroyCommandOutput.getErrorOutput(); + + if (Objects.nonNull(clusterDestroyCommandErrorOutput) && !clusterDestroyCommandErrorOutput.isEmpty()) { + throw new ClusterDestructionFailureException(); + } + } + + /** + * Performs recreation of RepoAchiever Cluster allocation. + * + * @param pid given process identificator of the allocation RepoAchiever Cluster to be removed. + * @param name given RepoAchiever Cluster allocation name. + * @param clusterContext given RepoAchiever Cluster context used for the new allocation. + * @throws ClusterRecreationFailureException if recreation operation failed. + */ + public Integer recreate(Integer pid, String name, String clusterContext) throws ClusterRecreationFailureException { + try { + destroy(pid); + } catch (ClusterDestructionFailureException e) { + throw new ClusterRecreationFailureException(e.getMessage()); + } + + try { + return deploy(name, clusterContext); + } catch (ClusterDeploymentFailureException e) { + throw new ClusterRecreationFailureException(e.getMessage()); + } + } +} + + +// TODO: make assignment of random identificators + +// TODO: probably move this logic to ClusterService + +// TODO: should regenerate topology after each location added \ No newline at end of file diff --git a/api-server/src/main/java/com/repoachiever/service/cluster/common/ClusterConfigurationHelper.java b/api-server/src/main/java/com/repoachiever/service/cluster/common/ClusterConfigurationHelper.java new file mode 100644 index 0000000..08d86cd --- /dev/null +++ b/api-server/src/main/java/com/repoachiever/service/cluster/common/ClusterConfigurationHelper.java @@ -0,0 +1,70 @@ +package com.repoachiever.service.cluster.common; + +import java.util.UUID; +import java.util.concurrent.*; + +/** + * Contains helpful tools used for RepoAchiever Cluster configuration. + */ +public class ClusterConfigurationHelper { + private final static ScheduledExecutorService scheduledExecutorService = + Executors.newScheduledThreadPool(2); + + /** + * Composes name for RepoAchiever Cluster using pre-defined prefix and UUID. + * + * @param prefix given name prefix. + * @return composed RepoAchiever Cluster name. + */ + public static String getName(String prefix) { + return String.format("%s-%s", prefix, UUID.randomUUID()); + } + + /** + * Waits till the given callback execution succeeds. + * + * @param callback given callback. + * @param frequency given callback execution check frequency. + * @param timeout given callback execution timeout. + * @return result of the execution. + */ + public static Boolean waitForStart(Callable callback, Integer frequency, Integer timeout) { + CountDownLatch waiter = new CountDownLatch(1); + + ScheduledFuture awaitTask = scheduledExecutorService.scheduleAtFixedRate(() -> { + try { + if (callback.call()) { + waiter.countDown(); + } + } catch (Exception ignore) { + } + }, 0, frequency, TimeUnit.MILLISECONDS); + + ScheduledFuture timeoutTask = scheduledExecutorService.schedule(() -> { + if (!awaitTask.isCancelled()) { + awaitTask.cancel(true); + + waiter.countDown(); + } + }, timeout, TimeUnit.MILLISECONDS); + + + try { + waiter.await(); + } catch (InterruptedException e) { + return false; + } + + if (!awaitTask.isCancelled()) { + awaitTask.cancel(true); + } else { + return false; + } + + if (!timeoutTask.isDone()) { + timeoutTask.cancel(true); + } + + return true; + } +} diff --git a/api-server/src/main/java/com/repoachiever/service/cluster/facade/ClusterFacade.java b/api-server/src/main/java/com/repoachiever/service/cluster/facade/ClusterFacade.java new file mode 100644 index 0000000..0a28aae --- /dev/null +++ b/api-server/src/main/java/com/repoachiever/service/cluster/facade/ClusterFacade.java @@ -0,0 +1,288 @@ +package com.repoachiever.service.cluster.facade; + +import com.repoachiever.converter.ClusterContextToJsonConverter; +import com.repoachiever.converter.ContentCredentialsToClusterContextCredentialsConverter; +import com.repoachiever.converter.ContentProviderToClusterContextProviderConverter; +import com.repoachiever.dto.ClusterAllocationDto; +import com.repoachiever.entity.common.ClusterContextEntity; +import com.repoachiever.entity.common.PropertiesEntity; +import com.repoachiever.exception.*; +import com.repoachiever.model.ContentApplication; +import com.repoachiever.model.ContentWithdrawal; +import com.repoachiever.service.cluster.ClusterService; +import com.repoachiever.service.cluster.common.ClusterConfigurationHelper; +import com.repoachiever.service.cluster.resource.ClusterCommunicationResource; +import com.repoachiever.service.config.ConfigService; +import com.repoachiever.service.state.StateService; +import com.repoachiever.service.workspace.facade.WorkspaceFacade; +import jakarta.enterprise.context.ApplicationScoped; +import jakarta.inject.Inject; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +import java.util.ArrayList; +import java.util.List; + +/** + * Provides high-level access to RepoAchiever Cluster related operations. + */ +@ApplicationScoped +public class ClusterFacade { + private static final Logger logger = LogManager.getLogger(ClusterFacade.class); + + @Inject + PropertiesEntity properties; + + @Inject + WorkspaceFacade workspaceFacade; + + @Inject + ConfigService configService; + + @Inject + ClusterService clusterService; + + @Inject + ClusterCommunicationResource clusterCommunicationResource; + + /** + * Applies given content application, removing previous topology and deploying new one with up-to-date configuration. + * + * @param contentApplication given content application used for topology configuration. + * @throws ClusterApplicationFailureException if RepoAchiever Cluster application failed. + */ + public void apply(ContentApplication contentApplication) throws ClusterApplicationFailureException { + StateService.getTopologyStateGuard().lock(); + + String workspaceUnitKey = + workspaceFacade.createUnitKey( + contentApplication.getProvider(), contentApplication.getCredentials()); + + List suspends = new ArrayList<>(); + + for (ClusterAllocationDto clusterAllocation : StateService. + getClusterAllocationsByWorkspaceUnitKey(workspaceUnitKey)) { + logger.info( + String.format( + "Setting RepoAchiever Cluster allocation to suspend state: %s", + clusterAllocation.getName())); + + try { + clusterCommunicationResource.performSuspend(clusterAllocation.getName()); + + } catch (ClusterOperationFailureException e) { + logger.fatal(new ClusterApplicationFailureException(e.getMessage()).getMessage()); + return; + } + + suspends.add(clusterAllocation); + } + + List> segregation = clusterService.performContentLocationsSegregation( + contentApplication.getLocations(), + configService.getConfig().getResource().getCluster().getMaxWorkers()); + + List candidates = new ArrayList<>(); + + for (List locations : segregation) { + String name = ClusterConfigurationHelper.getName(properties.getCommunicationClusterBase()); + + String context = ClusterContextToJsonConverter.convert( + ClusterContextEntity.of( + ClusterContextEntity.Metadata.of(name, workspaceUnitKey), + ClusterContextEntity.Filter.of(locations), + ClusterContextEntity.Service.of( + ContentProviderToClusterContextProviderConverter.convert( + contentApplication.getProvider()), + ContentCredentialsToClusterContextCredentialsConverter.convert( + contentApplication.getProvider(), + contentApplication.getCredentials().getExternal())), + ClusterContextEntity.Communication.of( + properties.getCommunicationApiServerName(), + configService.getConfig().getCommunication().getPort()), + ClusterContextEntity.Content.of( + configService.getConfig().getContent().getFormat()), + ClusterContextEntity.Resource.of( + ClusterContextEntity.Resource.Cluster.of( + configService.getConfig().getResource().getCluster().getMaxWorkers()), + ClusterContextEntity.Resource.Worker.of( + configService.getConfig().getResource().getWorker().getFrequency())))); + + logger.info( + String.format("Deploying RepoAchiever Cluster new allocation: %s", name)); + + Integer pid; + + try { + pid = clusterService.deploy(name, context); + } catch (ClusterDeploymentFailureException e1) { + for (ClusterAllocationDto candidate : candidates) { + logger.info( + String.format("Removing RepoAchiever Cluster candidate allocation: %s", candidate.getName())); + + try { + clusterService.destroy(candidate.getPid()); + } catch (ClusterDestructionFailureException e2) { + throw new ClusterApplicationFailureException(e1.getMessage(), e2.getMessage()); + } + } + + for (ClusterAllocationDto suspended : suspends) { + logger.info( + String.format("Setting RepoAchiever Cluster suspended allocation to serve state: %s", suspended.getName())); + + try { + clusterCommunicationResource.performServe(suspended.getName()); + } catch (ClusterOperationFailureException e2) { + logger.fatal(new ClusterApplicationFailureException(e1.getMessage(), e2.getMessage()).getMessage()); + return; + } + } + + throw new ClusterApplicationFailureException(e1.getMessage()); + } + + candidates.add(ClusterAllocationDto.of(name, pid, context, workspaceUnitKey)); + } + + for (ClusterAllocationDto candidate : candidates) { + logger.info( + String.format( + "Setting RepoAchiever Cluster candidate allocation to serve state: %s", + candidate.getName())); + + try { + clusterCommunicationResource.performServe(candidate.getName()); + } catch (ClusterOperationFailureException e1) { + for (ClusterAllocationDto suspended : suspends) { + logger.info( + String.format( + "Setting RepoAchiever Cluster suspended allocation to serve state: %s", + suspended.getName())); + + try { + clusterCommunicationResource.performServe(suspended.getName()); + } catch (ClusterOperationFailureException e2) { + logger.fatal(new ClusterApplicationFailureException( + e1.getMessage(), e2.getMessage()).getMessage()); + return; + } + } + + throw new ClusterApplicationFailureException(e1.getMessage()); + } + } + + for (ClusterAllocationDto suspended : suspends) { + logger.info( + String.format("Removing RepoAchiever Cluster suspended allocation: %s", suspended.getName())); + + try { + clusterService.destroy(suspended.getPid()); + } catch (ClusterDestructionFailureException e) { + throw new ClusterApplicationFailureException(e.getMessage()); + } + } + + StateService.addClusterAllocations(candidates); + + StateService.removeClusterAllocationByNames( + suspends.stream().map(ClusterAllocationDto::getName).toList()); + + StateService.getTopologyStateGuard().unlock(); + } + + /** + * Applies given content withdrawal, removing existing content configuration with the given properties. + * + * @param contentWithdrawal given content application used for topology configuration. + * @throws ClusterWithdrawalFailureException if RepoAchiever Cluster withdrawal failed. + */ + public void destroy(ContentWithdrawal contentWithdrawal) throws ClusterWithdrawalFailureException { + StateService.getTopologyStateGuard().lock(); + + String workspaceUnitKey = + workspaceFacade.createUnitKey( + contentWithdrawal.getProvider(), contentWithdrawal.getCredentials()); + + List clusterAllocations = + StateService.getClusterAllocationsByWorkspaceUnitKey(workspaceUnitKey); + + for (ClusterAllocationDto clusterAllocation : clusterAllocations) { + logger.info( + String.format("Removing RepoAchiever Cluster allocation: %s", clusterAllocation.getName())); + + try { + clusterService.destroy(clusterAllocation.getPid()); + } catch (ClusterDestructionFailureException e) { + throw new ClusterWithdrawalFailureException(e.getMessage()); + } + } + + StateService.removeClusterAllocationByNames( + clusterAllocations.stream().map(ClusterAllocationDto::getName).toList()); + + StateService.getTopologyStateGuard().unlock(); + } + + /** + * Destroys all the created RepoAchiever Cluster allocations. + * + * @throws ClusterFullDestructionFailureException if RepoAchiever Cluster full destruction failed. + */ + public void destroyAll() throws ClusterFullDestructionFailureException { + StateService.getTopologyStateGuard().lock(); + + for (ClusterAllocationDto clusterAllocation : StateService.getClusterAllocations()) { + logger.info( + String.format("Removing RepoAchiever Cluster allocation: %s", clusterAllocation.getName())); + + try { + clusterService.destroy(clusterAllocation.getPid()); + } catch (ClusterDestructionFailureException e) { + throw new ClusterFullDestructionFailureException(e.getMessage()); + } + } + + StateService.getTopologyStateGuard().unlock(); + } + + /** + * Reapplies all unhealthy RepoAchiever Cluster allocations, which healthcheck operation failed for, recreating them. + * + * @throws ClusterUnhealthyReapplicationFailureException if RepoAchiever Cluster unhealthy allocation reapplication fails. + */ + public void reapplyUnhealthy() throws ClusterUnhealthyReapplicationFailureException { + StateService.getTopologyStateGuard().lock(); + + + List updates = new ArrayList<>(); + List removable = new ArrayList<>(); + + for (ClusterAllocationDto clusterAllocation : StateService.getClusterAllocations()) { + try { + clusterService.destroy(clusterAllocation.getPid()); + } catch (ClusterDestructionFailureException ignored) { + } + + Integer pid; + + try { + pid = clusterService.deploy(clusterAllocation.getName(), clusterAllocation.getContext()); + } catch (ClusterDeploymentFailureException e) { + throw new ClusterUnhealthyReapplicationFailureException(e.getMessage()); + } + + removable.add(clusterAllocation.getName()); +// +// updates.add(ClusterAllocationDto.of( +// clusterAllocation.getName(), pid, clusterAllocation.getContext())); + } + + StateService.removeClusterAllocationByNames(removable); + +// updates.forEach(StateService::addClusterAllocation); + + StateService.getTopologyStateGuard().unlock(); + } +} \ No newline at end of file diff --git a/api-server/src/main/java/com/repoachiever/service/cluster/resource/ClusterCommunicationResource.java b/api-server/src/main/java/com/repoachiever/service/cluster/resource/ClusterCommunicationResource.java new file mode 100644 index 0000000..86c8a13 --- /dev/null +++ b/api-server/src/main/java/com/repoachiever/service/cluster/resource/ClusterCommunicationResource.java @@ -0,0 +1,142 @@ +package com.repoachiever.service.cluster.resource; + +import com.repoachiever.exception.ClusterOperationFailureException; +import com.repoachiever.exception.CommunicationConfigurationFailureException; +import com.repoachiever.service.communication.common.CommunicationProviderConfigurationHelper; +import com.repoachiever.service.config.ConfigService; +import jakarta.annotation.PostConstruct; +import jakarta.enterprise.context.ApplicationScoped; +import com.repoachiever.service.communication.cluster.IClusterCommunicationService; +import jakarta.inject.Inject; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +import java.rmi.NotBoundException; +import java.rmi.RemoteException; +import java.rmi.registry.LocateRegistry; +import java.rmi.registry.Registry; + +/** + * Represents implementation for RepoAchiever Cluster remote API. + */ +@ApplicationScoped +public class ClusterCommunicationResource { + private static final Logger logger = LogManager.getLogger(ClusterCommunicationResource.class); + + @Inject + ConfigService configService; + + private Registry registry; + + @PostConstruct + private void configure() { + try { + this.registry = LocateRegistry.getRegistry( + configService.getConfig().getCommunication().getPort()); + } catch (RemoteException e) { + logger.fatal(new CommunicationConfigurationFailureException(e.getMessage()).getMessage()); + } + } + + /** + * Retrieves remote RepoAchiever Cluster allocation with the given name. + * + * @param name given RepoAchiever Cluster allocation name. + * @return retrieved RepoAchiever Cluster allocation. + * @throws ClusterOperationFailureException if RepoAchiever Cluster operation fails. + */ + private IClusterCommunicationService retrieveAllocation(String name) throws ClusterOperationFailureException { + try { + return (IClusterCommunicationService) registry.lookup( + CommunicationProviderConfigurationHelper.getBindName( + configService.getConfig().getCommunication().getPort(), + name)); + } catch (RemoteException | NotBoundException e) { + throw new ClusterOperationFailureException(e.getMessage()); + } + } + + /** + * Performs RepoAchiever Cluster suspend operation. Has no effect if RepoAchiever Cluster was already suspended + * previously. + * + * @param name given name of RepoAchiever Cluster. + * @throws ClusterOperationFailureException if RepoAchiever Cluster operation fails. + */ + public void performSuspend(String name) throws ClusterOperationFailureException { + IClusterCommunicationService allocation = retrieveAllocation(name); + + try { + allocation.performSuspend(); + } catch (RemoteException e) { + throw new ClusterOperationFailureException(e.getMessage()); + } + } + + /** + * Performs RepoAchiever Cluster serve operation. Has no effect if RepoAchiever Cluster was not suspended previously. + * + * @param name given name of RepoAchiever Cluster. + * @throws ClusterOperationFailureException if RepoAchiever Cluster operation fails. + */ + public void performServe(String name) throws ClusterOperationFailureException { + IClusterCommunicationService allocation = retrieveAllocation(name); + + try { + allocation.performServe(); + } catch (RemoteException e) { + throw new ClusterOperationFailureException(e.getMessage()); + } + } + + /** + * Retrieves health check status of the RepoAchiever Cluster with the given name. + * + * @param name given name of RepoAchiever Cluster. + * @return result of the check. + * @throws ClusterOperationFailureException if RepoAchiever Cluster operation fails. + */ + public Boolean retrieveHealthCheck(String name) throws ClusterOperationFailureException { + IClusterCommunicationService allocation = retrieveAllocation(name); + + try { + return allocation.retrieveHealthCheck(); + } catch (RemoteException e) { + throw new ClusterOperationFailureException(e.getMessage()); + } + } + + /** + * Retrieves version of the RepoAchiever Cluster with the given name. + * + * @param name given name of RepoAchiever Cluster. + * @return retrieved version of RepoAchiever Cluster. + * @throws ClusterOperationFailureException if RepoAchiever Cluster operation fails. + */ + public String retrieveVersion(String name) throws ClusterOperationFailureException { + IClusterCommunicationService allocation = retrieveAllocation(name); + + try { + return allocation.retrieveVersion(); + } catch (RemoteException e) { + throw new ClusterOperationFailureException(e.getMessage()); + } + } + + /** + * Retrieves amount of RepoAchiever Worker owned by RepoAchiever Cluster with the given name. + * + * @param name given name of RepoAchiever Cluster. + * @return retrieved amount of RepoAchiever Worker owned by RepoAchiever Cluster allocation. + * @throws ClusterOperationFailureException if RepoAchiever Cluster operation fails. + */ + public Integer retrieveWorkerAmount(String name) throws ClusterOperationFailureException { + IClusterCommunicationService allocation = retrieveAllocation(name); + + try { + return allocation.retrieveWorkerAmount(); + } catch (RemoteException e) { + throw new ClusterOperationFailureException(e.getMessage()); + } + } +} diff --git a/api-server/src/main/java/com/repoachiever/service/command/cluster/common/ClusterConfigurationHelper.java b/api-server/src/main/java/com/repoachiever/service/command/cluster/common/ClusterConfigurationHelper.java new file mode 100644 index 0000000..0f1fd0f --- /dev/null +++ b/api-server/src/main/java/com/repoachiever/service/command/cluster/common/ClusterConfigurationHelper.java @@ -0,0 +1,25 @@ +package com.repoachiever.service.command.cluster.common; + +import com.repoachiever.service.command.common.CommandConfigurationHelper; + +import java.util.HashMap; + +/** + * Contains helpful tools used for Grafana deployment configuration. + */ +public class ClusterConfigurationHelper { + /** + * Composes environment variables for Grafana deployment. + * + * @param clusterContext RepoAchiever Cluster context used for cluster configuration. + * @return composed environment variables. + */ + public static String getEnvironmentVariables(String clusterContext) { + return CommandConfigurationHelper.getEnvironmentVariables( + new HashMap<>() { + { + put("REPOACHIEVER_CLUSTER_CONTEXT", clusterContext); + } + }); + } +} \ No newline at end of file diff --git a/api-server/src/main/java/com/repoachiever/service/command/cluster/deploy/ClusterDeployCommandService.java b/api-server/src/main/java/com/repoachiever/service/command/cluster/deploy/ClusterDeployCommandService.java new file mode 100644 index 0000000..249c0a3 --- /dev/null +++ b/api-server/src/main/java/com/repoachiever/service/command/cluster/deploy/ClusterDeployCommandService.java @@ -0,0 +1,38 @@ +package com.repoachiever.service.command.cluster.deploy; + +import com.repoachiever.service.command.cluster.common.ClusterConfigurationHelper; +import process.SProcess; +import process.SProcessExecutor; + +import java.nio.file.Path; + +/** + * Represents RepoAchiever Cluster deployment command. + */ +public class ClusterDeployCommandService extends SProcess { + private final String command; + private final SProcessExecutor.OS osType; + + public ClusterDeployCommandService( + String clusterContext, String binDirectory, String binClusterLocation) { + this.osType = SProcessExecutor.getCommandExecutor().getOSType(); + + this.command = switch (osType) { + case WINDOWS -> null; + case UNIX, MAC, ANY -> String.format( + "%s java -jar %s & echo $!", + ClusterConfigurationHelper.getEnvironmentVariables(clusterContext), + Path.of(binDirectory, binClusterLocation)); + }; + } + + @Override + public String getCommand() { + return command; + } + + @Override + public SProcessExecutor.OS getOSType() { + return osType; + } +} diff --git a/api-server/src/main/java/com/repoachiever/service/command/cluster/destroy/ClusterDestroyCommandService.java b/api-server/src/main/java/com/repoachiever/service/command/cluster/destroy/ClusterDestroyCommandService.java new file mode 100644 index 0000000..fb45f93 --- /dev/null +++ b/api-server/src/main/java/com/repoachiever/service/command/cluster/destroy/ClusterDestroyCommandService.java @@ -0,0 +1,31 @@ +package com.repoachiever.service.command.cluster.destroy; + +import process.SProcess; +import process.SProcessExecutor; + +/** + * Represents RepoAchiever Cluster destruction command. + */ +public class ClusterDestroyCommandService extends SProcess { + private final String command; + private final SProcessExecutor.OS osType; + + public ClusterDestroyCommandService(Integer pid) { + this.osType = SProcessExecutor.getCommandExecutor().getOSType(); + + this.command = switch (osType) { + case WINDOWS -> null; + case UNIX, MAC, ANY -> String.format("kill -15 %d", pid); + }; + } + + @Override + public String getCommand() { + return command; + } + + @Override + public SProcessExecutor.OS getOSType() { + return osType; + } +} diff --git a/api-server/src/main/java/com/repoachiever/service/command/common/CommandConfigurationHelper.java b/api-server/src/main/java/com/repoachiever/service/command/common/CommandConfigurationHelper.java new file mode 100644 index 0000000..ae227ba --- /dev/null +++ b/api-server/src/main/java/com/repoachiever/service/command/common/CommandConfigurationHelper.java @@ -0,0 +1,71 @@ +package com.repoachiever.service.command.common; + +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +/** + * Contains helpful tools used for general command configuration. + */ +public class CommandConfigurationHelper { + /** + * Composes environment variables. + * + * @param attributes attributes to be included. + * @return composed environment variables. + */ + public static String getEnvironmentVariables(Map attributes) { + return attributes.entrySet().stream() + .map(element -> String.format("%s='%s'", element.getKey(), element.getValue())) + .collect(Collectors.joining(" ")); + } + + /** + * Composes Docker volumes declaration. + * + * @param attributes attributes to be included. + * @return composed Docker volumes declaration. + */ + public static String getDockerVolumes(Map attributes) { + return attributes.entrySet().stream() + .map(element -> String.format("-v %s:%s", element.getKey(), element.getValue())) + .collect(Collectors.joining(" ")); + } + + /** + * Composes Docker command arguments declaration. + * + * @param attributes attributes to be included. + * @return composed Docker command arguments declaration. + */ + public static String getDockerCommandArguments(Map attributes) { + return attributes.entrySet().stream() + .map(element -> String.format("--%s=%s", element.getKey(), element.getValue())) + .collect(Collectors.joining(" ")); + } + + /** + * Composes Docker command options declaration. + * + * @param attributes attributes to be included. + * @return composed Docker command options declaration. + */ + public static String getDockerCommandOptions(List attributes) { + return attributes. + stream(). + map(element -> String.format("--%s", element)) + .collect(Collectors.joining(" ")); + } + + /** + * Composes Docker port mappings. + * + * @param attributes attributes to be included. + * @return composed Docker port mappings. + */ + public static String getDockerPorts(Map attributes) { + return attributes.entrySet().stream() + .map(element -> String.format("-p 0.0.0.0:%d:%d", element.getKey(), element.getValue())) + .collect(Collectors.joining(" ")); + } +} diff --git a/api-server/src/main/java/com/repoachiever/service/command/docker/availability/DockerAvailabilityCheckCommandService.java b/api-server/src/main/java/com/repoachiever/service/command/docker/availability/DockerAvailabilityCheckCommandService.java new file mode 100644 index 0000000..a40d911 --- /dev/null +++ b/api-server/src/main/java/com/repoachiever/service/command/docker/availability/DockerAvailabilityCheckCommandService.java @@ -0,0 +1,33 @@ +package com.repoachiever.service.command.docker.availability; + +import jakarta.enterprise.context.ApplicationScoped; +import process.SProcess; +import process.SProcessExecutor; + +/** + * Represents Docker availability check command. + */ +@ApplicationScoped +public class DockerAvailabilityCheckCommandService extends SProcess { + private final String command; + private final SProcessExecutor.OS osType; + + public DockerAvailabilityCheckCommandService() { + this.osType = SProcessExecutor.getCommandExecutor().getOSType(); + + this.command = switch (osType) { + case WINDOWS -> null; + case UNIX, MAC, ANY -> "docker ps 2>/dev/null"; + }; + } + + @Override + public String getCommand() { + return command; + } + + @Override + public SProcessExecutor.OS getOSType() { + return osType; + } +} diff --git a/api-server/src/main/java/com/repoachiever/service/command/docker/inspect/remove/DockerInspectRemoveCommandService.java b/api-server/src/main/java/com/repoachiever/service/command/docker/inspect/remove/DockerInspectRemoveCommandService.java new file mode 100644 index 0000000..a0fc855 --- /dev/null +++ b/api-server/src/main/java/com/repoachiever/service/command/docker/inspect/remove/DockerInspectRemoveCommandService.java @@ -0,0 +1,32 @@ +package com.repoachiever.service.command.docker.inspect.remove; + +import jakarta.enterprise.context.ApplicationScoped; +import process.SProcess; +import process.SProcessExecutor; + +/** + * Represents Docker container removal command. Does nothing if given container does not exist. + */ +public class DockerInspectRemoveCommandService extends SProcess { + private final String command; + private final SProcessExecutor.OS osType; + + public DockerInspectRemoveCommandService(String name) { + this.osType = SProcessExecutor.getCommandExecutor().getOSType(); + + this.command = switch (osType) { + case WINDOWS -> null; + case UNIX, MAC, ANY -> String.format("docker rm -f %s 2>/dev/null", name); + }; + } + + @Override + public String getCommand() { + return command; + } + + @Override + public SProcessExecutor.OS getOSType() { + return osType; + } +} diff --git a/api-server/src/main/java/com/repoachiever/service/command/docker/network/create/DockerNetworkCreateCommandService.java b/api-server/src/main/java/com/repoachiever/service/command/docker/network/create/DockerNetworkCreateCommandService.java new file mode 100644 index 0000000..1d6c3cc --- /dev/null +++ b/api-server/src/main/java/com/repoachiever/service/command/docker/network/create/DockerNetworkCreateCommandService.java @@ -0,0 +1,34 @@ +package com.repoachiever.service.command.docker.network.create; + +import process.SProcess; +import process.SProcessExecutor; + +/** + * Represents Docker network creation command. + */ +public class DockerNetworkCreateCommandService extends SProcess { + private final String command; + private final SProcessExecutor.OS osType; + + public DockerNetworkCreateCommandService(String networkName) { + this.osType = SProcessExecutor.getCommandExecutor().getOSType(); + + this.command = switch (osType) { + case WINDOWS -> null; + case UNIX, MAC, ANY -> String.format( + "docker network inspect %s >/dev/null 2>&1 || docker network create -d bridge %s", + networkName, + networkName); + }; + } + + @Override + public String getCommand() { + return command; + } + + @Override + public SProcessExecutor.OS getOSType() { + return osType; + } +} diff --git a/api-server/src/main/java/com/repoachiever/service/command/docker/network/remove/DockerNetworkRemoveCommandService.java b/api-server/src/main/java/com/repoachiever/service/command/docker/network/remove/DockerNetworkRemoveCommandService.java new file mode 100644 index 0000000..0f94225 --- /dev/null +++ b/api-server/src/main/java/com/repoachiever/service/command/docker/network/remove/DockerNetworkRemoveCommandService.java @@ -0,0 +1,31 @@ +package com.repoachiever.service.command.docker.network.remove; + +import process.SProcess; +import process.SProcessExecutor; + +/** + * Represents Docker network removal command. + */ +public class DockerNetworkRemoveCommandService extends SProcess { + private final String command; + private final SProcessExecutor.OS osType; + + public DockerNetworkRemoveCommandService(String networkName) { + this.osType = SProcessExecutor.getCommandExecutor().getOSType(); + + this.command = switch (osType) { + case WINDOWS -> null; + case UNIX, MAC, ANY -> String.format("docker network rm %s 2> /dev/null", networkName); + }; + } + + @Override + public String getCommand() { + return command; + } + + @Override + public SProcessExecutor.OS getOSType() { + return osType; + } +} diff --git a/api-server/src/main/java/com/repoachiever/service/command/grafana/GrafanaDeployCommandService.java b/api-server/src/main/java/com/repoachiever/service/command/grafana/GrafanaDeployCommandService.java new file mode 100644 index 0000000..4498f9b --- /dev/null +++ b/api-server/src/main/java/com/repoachiever/service/command/grafana/GrafanaDeployCommandService.java @@ -0,0 +1,38 @@ +package com.repoachiever.service.command.grafana; + +import com.repoachiever.service.command.grafana.common.GrafanaConfigurationHelper; +import process.SProcess; +import process.SProcessExecutor; + +/** + * Represents Grafana deployment command. + */ +public class GrafanaDeployCommandService extends SProcess { + private final String command; + private final SProcessExecutor.OS osType; + + public GrafanaDeployCommandService( + String name, String image, Integer port, String configLocation, String internalLocation) { + this.osType = SProcessExecutor.getCommandExecutor().getOSType(); + + this.command = switch (osType) { + case WINDOWS -> null; + case UNIX, MAC, ANY -> String.format( + "docker run -d %s %s --name %s %s", + GrafanaConfigurationHelper.getDockerVolumes(configLocation, internalLocation), + GrafanaConfigurationHelper.getDockerPorts(port), + name, + image); + }; + } + + @Override + public String getCommand() { + return command; + } + + @Override + public SProcessExecutor.OS getOSType() { + return osType; + } +} diff --git a/api-server/src/main/java/com/repoachiever/service/command/grafana/common/GrafanaConfigurationHelper.java b/api-server/src/main/java/com/repoachiever/service/command/grafana/common/GrafanaConfigurationHelper.java new file mode 100644 index 0000000..0d203ae --- /dev/null +++ b/api-server/src/main/java/com/repoachiever/service/command/grafana/common/GrafanaConfigurationHelper.java @@ -0,0 +1,57 @@ +package com.repoachiever.service.command.grafana.common; + +import com.repoachiever.service.command.common.CommandConfigurationHelper; + +import java.util.HashMap; +import java.util.LinkedHashMap; +import java.util.List; + +/** Contains helpful tools used for Grafana deployment configuration. */ +public class GrafanaConfigurationHelper { + /** + * Composes environment variables for Grafana deployment. + * + * @return composed environment variables. + */ + public static String getEnvironmentVariables() { + return CommandConfigurationHelper.getEnvironmentVariables( + new HashMap<>() { + { + put("GF_SECURITY_ADMIN_PASSWORD", "repoachiever"); + put("GF_USERS_ALLOW_SIGN_UP", "false"); + } + }); + } + + /** + * Composes Grafana Docker volumes declaration. + * + * @param configLocation given Grafana local config directory location. + * @param internalLocation given Grafana local internal directory location. + * @return composed Grafana Docker volumes declaration. + */ + public static String getDockerVolumes(String configLocation, String internalLocation) { + return CommandConfigurationHelper.getDockerVolumes( + new HashMap<>() { + { + put(configLocation, "/etc/grafana/provisioning/"); + put(internalLocation, "/var/lib/grafana"); + } + }); + } + + /** + * Composes Prometheus Docker port mappings. + * + * @param port given Prometheus Docker port. + * @return composed Prometheus Docker port mappings. + */ + public static String getDockerPorts(Integer port) { + return CommandConfigurationHelper.getDockerPorts( + new HashMap<>() { + { + put(port, 3000); + } + }); + } +} \ No newline at end of file diff --git a/api-server/src/main/java/com/repoachiever/service/command/nodeexporter/NodeExporterDeployCommandService.java b/api-server/src/main/java/com/repoachiever/service/command/nodeexporter/NodeExporterDeployCommandService.java new file mode 100644 index 0000000..de20bc5 --- /dev/null +++ b/api-server/src/main/java/com/repoachiever/service/command/nodeexporter/NodeExporterDeployCommandService.java @@ -0,0 +1,38 @@ +package com.repoachiever.service.command.nodeexporter; + +import com.repoachiever.service.command.nodeexporter.common.NodeExporterConfigurationHelper; +import process.SProcess; +import process.SProcessExecutor; + +/** + * Represents Prometheus Node Exporter deployment command. + */ +public class NodeExporterDeployCommandService extends SProcess { + private final String command; + private final SProcessExecutor.OS osType; + + public NodeExporterDeployCommandService(String name, String image, Integer port) { + this.osType = SProcessExecutor.getCommandExecutor().getOSType(); + + this.command = switch (osType) { + case WINDOWS -> null; + case UNIX, MAC, ANY -> String.format( + "docker run -d %s %s --name %s %s %s", + NodeExporterConfigurationHelper.getDockerVolumes(), + NodeExporterConfigurationHelper.getDockerPorts(port), + name, + image, + NodeExporterConfigurationHelper.getDockerCommandArguments()); + }; + } + + @Override + public String getCommand() { + return command; + } + + @Override + public SProcessExecutor.OS getOSType() { + return osType; + } +} diff --git a/api-server/src/main/java/com/repoachiever/service/command/nodeexporter/common/NodeExporterConfigurationHelper.java b/api-server/src/main/java/com/repoachiever/service/command/nodeexporter/common/NodeExporterConfigurationHelper.java new file mode 100644 index 0000000..cbf60fc --- /dev/null +++ b/api-server/src/main/java/com/repoachiever/service/command/nodeexporter/common/NodeExporterConfigurationHelper.java @@ -0,0 +1,60 @@ +package com.repoachiever.service.command.nodeexporter.common; + +import com.repoachiever.service.command.common.CommandConfigurationHelper; + +import java.util.HashMap; +import java.util.LinkedHashMap; +import java.util.List; + +/** + * Contains helpful tools used for Prometheus NodeExporter deployment configuration. + */ +public class NodeExporterConfigurationHelper { + /** + * Composes Prometheus Node Exporter Docker volumes declaration. + * + * @return composed Prometheus Node Exporter Docker volumes declaration. + */ + public static String getDockerVolumes() { + return CommandConfigurationHelper.getDockerVolumes( + new HashMap<>() { + { + put("/proc", "/host/proc:ro"); + put("/sys", "/host/sys:ro"); + put("/", "/rootfs:ro"); + } + }); + } + + /** + * Composes Prometheus Node Exporter Docker command arguments declaration. + * + * @return composed Prometheus Node Exporter Docker command arguments declaration. + */ + public static String getDockerCommandArguments() { + return CommandConfigurationHelper.getDockerCommandArguments( + new LinkedHashMap<>() { + { + put("path.procfs", "/host/proc"); + put("path.sysfs", "/host/sys"); + put("collector.filesystem.ignored-mount-points", "\"^/(sys|proc|dev|host|etc|rootfs/var/lib/docker/containers|rootfs/var/lib/docker/overlay2|rootfs/run/docker/netns|rootfs/var/lib/docker/aufs)($$|/)\""); + } + }); + } + + + /** + * Composes Prometheus Node Exporter Docker port mappings. + * + * @param port given Prometheus Node Exporter Docker port. + * @return composed Prometheus Node Exporter Docker port mappings. + */ + public static String getDockerPorts(Integer port) { + return CommandConfigurationHelper.getDockerPorts( + new HashMap<>() { + { + put(port, port); + } + }); + } +} \ No newline at end of file diff --git a/api-server/src/main/java/com/repoachiever/service/command/prometheus/PrometheusDeployCommandService.java b/api-server/src/main/java/com/repoachiever/service/command/prometheus/PrometheusDeployCommandService.java new file mode 100644 index 0000000..c835878 --- /dev/null +++ b/api-server/src/main/java/com/repoachiever/service/command/prometheus/PrometheusDeployCommandService.java @@ -0,0 +1,56 @@ +package com.repoachiever.service.command.prometheus; + +import com.repoachiever.service.command.prometheus.common.PrometheusConfigurationHelper; +import process.SProcess; +import process.SProcessExecutor; + +/** + * Represents Prometheus deployment command. + */ +public class PrometheusDeployCommandService extends SProcess { + private final String command; + private final SProcessExecutor.OS osType; + + public PrometheusDeployCommandService( + String name, String image, Integer port, String configLocation, String internalLocation) { + this.osType = SProcessExecutor.getCommandExecutor().getOSType(); + + this.command = switch (osType) { + case WINDOWS -> null; + case UNIX, MAC, ANY -> String.format( + "docker run -d %s %s %s --name %s %s %s %s", + PrometheusConfigurationHelper.getDockerParameters(), + PrometheusConfigurationHelper.getDockerVolumes(configLocation, internalLocation), + PrometheusConfigurationHelper.getDockerPorts(port), + name, + image, + PrometheusConfigurationHelper.getDockerCommandArguments(), + PrometheusConfigurationHelper.getDockerCommandOptions()); + }; + } + + @Override + public String getCommand() { + return command; + } + + @Override + public SProcessExecutor.OS getOSType() { + return osType; + } +} + +//prometheus: +// image: prom/prometheus:v2.36.2 +// volumes: +// - ./prometheus/:/etc/prometheus/ +// - prometheus_data:/prometheus +// command: +// - '--config.file=/etc/prometheus/prometheus.yml' +// - '--storage.tsdb.path=/prometheus' +// - '--web.console.libraries=/usr/share/prometheus/console_libraries' +// - '--web.console.templates=/usr/share/prometheus/consoles' +// - '--web.enable-lifecycle' +// - '--web.enable-admin-api' +// ports: +// - 9090:9090 \ No newline at end of file diff --git a/api-server/src/main/java/com/repoachiever/service/command/prometheus/common/PrometheusConfigurationHelper.java b/api-server/src/main/java/com/repoachiever/service/command/prometheus/common/PrometheusConfigurationHelper.java new file mode 100644 index 0000000..85fb2c7 --- /dev/null +++ b/api-server/src/main/java/com/repoachiever/service/command/prometheus/common/PrometheusConfigurationHelper.java @@ -0,0 +1,86 @@ +package com.repoachiever.service.command.prometheus.common; + +import com.repoachiever.service.command.common.CommandConfigurationHelper; + +import java.util.HashMap; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.UUID; + + +/** + * Contains helpful tools used for Prometheus deployment configuration. + */ +public class PrometheusConfigurationHelper { + /** + * Composes Prometheus Docker additional parameters. + * + * @return composed Docker additional parameters. + */ + public static String getDockerParameters() { + return "--add-host=host.docker.internal:host-gateway"; + } + + /** + * Composes Prometheus Docker volumes declaration. + * + * @param configLocation given Prometheus local config directory location. + * @param internalLocation given Prometheus local internal directory location. + * @return composed Prometheus Docker volumes declaration. + */ + public static String getDockerVolumes(String configLocation, String internalLocation) { + return CommandConfigurationHelper.getDockerVolumes( + new HashMap<>() { + { + put(configLocation, "/etc/prometheus/"); + put(internalLocation, "/prometheus"); + } + }); + } + + /** + * Composes Prometheus Docker command arguments declaration. + * + * @return composed Prometheus Docker command arguments declaration. + */ + public static String getDockerCommandArguments() { + return CommandConfigurationHelper.getDockerCommandArguments( + new LinkedHashMap<>() { + { + put("config.file", "/etc/prometheus/prometheus.yml"); + put("storage.tsdb.path", "/prometheus"); + put("web.console.libraries", "/usr/share/prometheus/console_libraries"); + put("web.console.templates", "/usr/share/prometheus/consoles"); + } + }); + } + + /** + * Composes Prometheus Docker command options declaration. + * + * @return composed Prometheus Docker command options declaration. + */ + public static String getDockerCommandOptions() { + return CommandConfigurationHelper.getDockerCommandOptions( + List.of("web.enable-lifecycle", + "web.enable-admin-api")); + } + + /** + * Composes Prometheus Docker port mappings. + * + * @param port given Prometheus Docker port. + * @return composed Prometheus Docker port mappings. + */ + public static String getDockerPorts(Integer port) { + return CommandConfigurationHelper.getDockerPorts( + new HashMap<>() { + { + put(port, port); + } + }); + } +} + + + diff --git a/api-server/src/main/java/com/repoachiever/service/communication/apiserver/IApiServerCommunicationService.java b/api-server/src/main/java/com/repoachiever/service/communication/apiserver/IApiServerCommunicationService.java new file mode 100644 index 0000000..e7e7a15 --- /dev/null +++ b/api-server/src/main/java/com/repoachiever/service/communication/apiserver/IApiServerCommunicationService.java @@ -0,0 +1,45 @@ +package com.repoachiever.service.communication.apiserver; + +import java.io.InputStream; +import java.rmi.Remote; +import java.rmi.RemoteException; + +/** + * Represents communication provider for RepoAchiever API Server. + */ +public interface IApiServerCommunicationService extends Remote { + /** + * Performs raw content upload operation, initiated by RepoAchiever Cluster. + * + * @param workspaceUnitKey given user workspace unit key. + * @param content given content to be uploaded. + * @throws RemoteException if remote request fails. + */ + void performRawContentUpload(String workspaceUnitKey, InputStream content) throws RemoteException; + + /** + * Performs additional content(issues, prs, releases) upload operation, initiated by RepoAchiever Cluster. + * + * @param workspaceUnitKey given user workspace unit key. + * @param content given content to be uploaded. + * @throws RemoteException if remote request fails. + */ + void performAdditionalContentUpload(String workspaceUnitKey, String content) throws RemoteException; + + /** + * Handles incoming log messages related to the given RepoAchiever Cluster allocation. + * + * @param name given RepoAchiever Cluster allocation name. + * @param message given RepoAchiever Cluster log message. + * @throws RemoteException if remote request fails. + */ + void performLogsTransfer(String name, String message) throws RemoteException; + + /** + * Retrieves latest RepoAchiever API Server health check states. + * + * @return RepoAchiever API Server health check status. + * @throws RemoteException if remote request fails. + */ + Boolean retrieveHealthCheck() throws RemoteException; +} \ No newline at end of file diff --git a/api-server/src/main/java/com/repoachiever/service/communication/cluster/IClusterCommunicationService.java b/api-server/src/main/java/com/repoachiever/service/communication/cluster/IClusterCommunicationService.java new file mode 100644 index 0000000..92fc69b --- /dev/null +++ b/api-server/src/main/java/com/repoachiever/service/communication/cluster/IClusterCommunicationService.java @@ -0,0 +1,50 @@ +package com.repoachiever.service.communication.cluster; + +import java.rmi.Remote; +import java.rmi.RemoteException; + +/** Represents client for RepoAchiever Cluster remote API. */ +public interface IClusterCommunicationService extends Remote { + /** + * Performs RepoAchiever Cluster suspend operation. Has no effect if RepoAchiever Cluster was + * already suspended previously. + * + * @throws RemoteException if remote request fails. + */ + void performSuspend() throws RemoteException; + + /** + * Performs RepoAchiever Cluster serve operation. Has no effect if RepoAchiever Cluster was not + * suspended previously. + * + * @throws RemoteException if remote request fails. + */ + void performServe() throws RemoteException; + + /** + * Retrieves latest RepoAchiever Cluster health check states. + * + * @return RepoAchiever Cluster health check status. + * @throws RemoteException if remote request fails. + */ + Boolean retrieveHealthCheck() throws RemoteException; + + /** + * Retrieves version of the allocated RepoAchiever Cluster instance allowing to confirm API + * compatability. + * + * @return RepoAchiever Cluster version. + * @throws RemoteException if remote request fails. + */ + String retrieveVersion() throws RemoteException; + + /** + * Retrieves amount of allocated workers. + * + * @return amount of allocated workers. + * @throws RemoteException if remote request fails. + */ + Integer retrieveWorkerAmount() throws RemoteException; +} + +// TODO: LOCATE ALL RMI RELATED CLASSES AT THE SAME PATH \ No newline at end of file diff --git a/api-server/src/main/java/com/repoachiever/service/communication/common/CommunicationProviderConfigurationHelper.java b/api-server/src/main/java/com/repoachiever/service/communication/common/CommunicationProviderConfigurationHelper.java new file mode 100644 index 0000000..0a5462c --- /dev/null +++ b/api-server/src/main/java/com/repoachiever/service/communication/common/CommunicationProviderConfigurationHelper.java @@ -0,0 +1,15 @@ +package com.repoachiever.service.communication.common; + +/** Contains helpful tools used for communication provider configuration. */ +public class CommunicationProviderConfigurationHelper { + /** + * Composes binding URI declaration for RMI. + * + * @param registryPort given registry port. + * @param suffix given binding suffix. + * @return composed binding URI declaration for RMI. + */ + public static String getBindName(Integer registryPort, String suffix) { + return String.format("//localhost:%d/%s", registryPort, suffix); + } +} \ No newline at end of file diff --git a/api-server/src/main/java/com/repoachiever/service/config/ConfigService.java b/api-server/src/main/java/com/repoachiever/service/config/ConfigService.java new file mode 100644 index 0000000..eced9fe --- /dev/null +++ b/api-server/src/main/java/com/repoachiever/service/config/ConfigService.java @@ -0,0 +1,103 @@ +package com.repoachiever.service.config; + +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.DeserializationFeature; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.ObjectReader; +import com.fasterxml.jackson.dataformat.yaml.YAMLFactory; +import com.repoachiever.entity.common.ConfigEntity; +import com.repoachiever.entity.common.PropertiesEntity; +import com.repoachiever.exception.ConfigValidationException; +import io.quarkus.runtime.Startup; +import jakarta.annotation.PostConstruct; +import jakarta.enterprise.context.ApplicationScoped; +import jakarta.inject.Inject; +import jakarta.validation.ConstraintViolation; +import jakarta.validation.Validation; +import jakarta.validation.Validator; +import jakarta.validation.ValidatorFactory; + +import java.io.*; +import java.nio.file.Paths; +import java.util.List; +import java.util.Set; +import java.util.stream.Collectors; + +import lombok.Getter; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +/** + * Service used to perform RepoAchiever API Server configuration processing operation. + */ +@Startup +@ApplicationScoped +public class ConfigService { + private static final Logger logger = LogManager.getLogger(ConfigService.class); + + @Inject + PropertiesEntity properties; + + @Getter + private ConfigEntity config; + + /** + * Reads configuration from the opened configuration file using mapping with a configuration entity. + */ + @PostConstruct + private void configure() { + InputStream file = null; + + try { + try { + file = new FileInputStream( + Paths.get(properties.getConfigDirectory(), properties.getConfigName()).toString()); + } catch (FileNotFoundException e) { + logger.fatal(e.getMessage()); + return; + } + + ObjectMapper mapper = + new ObjectMapper(new YAMLFactory()) + .configure(DeserializationFeature.FAIL_ON_NULL_CREATOR_PROPERTIES, true) + .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, true) + .configure(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY, true); + mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL); + ObjectReader reader = mapper.reader().forType(new TypeReference() { + }); + + try { + List values = reader.readValues(file).readAll(); + if (values.isEmpty()) { + return; + } + + config = values.getFirst(); + } catch (IOException e) { + logger.fatal(e.getMessage()); + return; + } + + try (ValidatorFactory validatorFactory = Validation.buildDefaultValidatorFactory()) { + Validator validator = validatorFactory.getValidator(); + + Set> validationResult = + validator.validate(config); + + if (!validationResult.isEmpty()) { + logger.fatal(new ConfigValidationException( + validationResult.stream() + .map(ConstraintViolation::getMessage) + .collect(Collectors.joining(", "))).getMessage()); + } + } + } finally { + try { + file.close(); + } catch (IOException e) { + logger.fatal(e.getMessage()); + } + } + } +} diff --git a/api-server/src/main/java/com/repoachiever/service/executor/CommandExecutorService.java b/api-server/src/main/java/com/repoachiever/service/executor/CommandExecutorService.java new file mode 100644 index 0000000..7dbff48 --- /dev/null +++ b/api-server/src/main/java/com/repoachiever/service/executor/CommandExecutorService.java @@ -0,0 +1,62 @@ +package com.repoachiever.service.executor; + +import com.repoachiever.dto.CommandExecutorOutputDto; +import com.repoachiever.exception.CommandExecutorException; +import jakarta.enterprise.context.ApplicationScoped; +import process.SProcess; +import process.SProcessExecutor; +import process.exceptions.NonMatchingOSException; +import process.exceptions.SProcessNotYetStartedException; + +import java.io.IOException; + +/** + * Represents command executor service used to perform commands execution. + */ +@ApplicationScoped +public class CommandExecutorService { + private final SProcessExecutor processExecutor; + + CommandExecutorService() { + this.processExecutor = SProcessExecutor.getCommandExecutor(); + } + + /** + * Executes given command. + * + * @param command standalone command + * @return output result, which consists of stdout and stderr. + * @throws CommandExecutorException when any execution step failed. + */ + public CommandExecutorOutputDto executeCommand(SProcess command) throws CommandExecutorException { + try { + processExecutor.executeCommand(command); + } catch (IOException | NonMatchingOSException e) { + throw new CommandExecutorException(e.getMessage()); + } + + try { + command.waitForCompletion(); + } catch (SProcessNotYetStartedException | InterruptedException e) { + throw new CommandExecutorException(e.getMessage()); + } + + String commandErrorOutput; + + try { + commandErrorOutput = command.getErrorOutput(); + } catch (SProcessNotYetStartedException e) { + throw new CommandExecutorException(e.getMessage()); + } + + String commandNormalOutput; + + try { + commandNormalOutput = command.getNormalOutput(); + } catch (SProcessNotYetStartedException e) { + throw new CommandExecutorException(e.getMessage()); + } + + return CommandExecutorOutputDto.of(commandNormalOutput, commandErrorOutput); + } +} diff --git a/api-server/src/main/java/com/repoachiever/service/healthcheck/health/HealthCheckService.java b/api-server/src/main/java/com/repoachiever/service/healthcheck/health/HealthCheckService.java new file mode 100644 index 0000000..b368b16 --- /dev/null +++ b/api-server/src/main/java/com/repoachiever/service/healthcheck/health/HealthCheckService.java @@ -0,0 +1,20 @@ +package com.repoachiever.service.healthcheck.health; + +import jakarta.enterprise.context.ApplicationScoped; +import org.eclipse.microprofile.health.*; + +@Liveness +@ApplicationScoped +public class HealthCheckService implements HealthCheck { + @Override + public HealthCheckResponse call() { + HealthCheckResponseBuilder healthCheckResponse = + HealthCheckResponse.named("Terraform application availability"); + + healthCheckResponse.up(); + + // TODO: check if docker is installed + + return healthCheckResponse.build(); + } +} diff --git a/api-server/src/main/java/com/repoachiever/service/healthcheck/readiness/ReadinessCheckService.java b/api-server/src/main/java/com/repoachiever/service/healthcheck/readiness/ReadinessCheckService.java new file mode 100644 index 0000000..c399e23 --- /dev/null +++ b/api-server/src/main/java/com/repoachiever/service/healthcheck/readiness/ReadinessCheckService.java @@ -0,0 +1,23 @@ +package com.repoachiever.service.healthcheck.readiness; + +import com.repoachiever.entity.common.PropertiesEntity; +import org.eclipse.microprofile.health.HealthCheck; +import org.eclipse.microprofile.health.HealthCheckResponse; +import org.eclipse.microprofile.health.HealthCheckResponseBuilder; + +/** Checks if the Kafka service is available for the given workspace. */ +public class ReadinessCheckService implements HealthCheck { + + public ReadinessCheckService(PropertiesEntity properties) { + } + + @Override + public HealthCheckResponse call() { + HealthCheckResponseBuilder healthCheckResponse = + HealthCheckResponse.named("Connection availability"); + + healthCheckResponse.up(); + + return healthCheckResponse.build(); + } +} diff --git a/api-server/src/main/java/com/repoachiever/service/integration/communication/apiserver/ApiServerCommunicationConfigService.java b/api-server/src/main/java/com/repoachiever/service/integration/communication/apiserver/ApiServerCommunicationConfigService.java new file mode 100644 index 0000000..94f5bb4 --- /dev/null +++ b/api-server/src/main/java/com/repoachiever/service/integration/communication/apiserver/ApiServerCommunicationConfigService.java @@ -0,0 +1,60 @@ +package com.repoachiever.service.integration.communication.apiserver; + +import com.repoachiever.entity.common.PropertiesEntity; +import com.repoachiever.exception.CommunicationConfigurationFailureException; +import com.repoachiever.resource.communication.ApiServerCommunicationResource; +import com.repoachiever.service.config.ConfigService; +import com.repoachiever.service.communication.common.CommunicationProviderConfigurationHelper; +import io.quarkus.runtime.Startup; +import jakarta.annotation.PostConstruct; +import jakarta.enterprise.context.ApplicationScoped; +import jakarta.inject.Inject; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +import java.rmi.RemoteException; +import java.rmi.registry.LocateRegistry; +import java.rmi.registry.Registry; + +/** + * Service used to perform RepoAchiever API Server communication provider configuration. + */ +@Startup(value = 160) +@ApplicationScoped +public class ApiServerCommunicationConfigService { + private static final Logger logger = LogManager.getLogger(ApiServerCommunicationConfigService.class); + + @Inject + PropertiesEntity properties; + + @Inject + ConfigService configService; + + /** + * Performs setup of RepoAchiever API Server communication provider. + */ + @PostConstruct + private void process() { + Registry registry; + + try { + registry = LocateRegistry.getRegistry( + configService.getConfig().getCommunication().getPort()); + } catch (RemoteException e) { + logger.fatal(new CommunicationConfigurationFailureException(e.getMessage()).getMessage()); + return; + } + + Thread.ofPlatform().start(() -> { + try { + registry.rebind( + CommunicationProviderConfigurationHelper.getBindName( + configService.getConfig().getCommunication().getPort(), + properties.getCommunicationApiServerName()), + new ApiServerCommunicationResource(properties)); + } catch (RemoteException e) { + logger.fatal(e.getMessage()); + } + }); + } +} \ No newline at end of file diff --git a/api-server/src/main/java/com/repoachiever/service/integration/communication/cluster/healthcheck/ClusterHealthCheckCommunicationService.java b/api-server/src/main/java/com/repoachiever/service/integration/communication/cluster/healthcheck/ClusterHealthCheckCommunicationService.java new file mode 100644 index 0000000..02864e4 --- /dev/null +++ b/api-server/src/main/java/com/repoachiever/service/integration/communication/cluster/healthcheck/ClusterHealthCheckCommunicationService.java @@ -0,0 +1,58 @@ +package com.repoachiever.service.integration.communication.cluster.healthcheck; + +import com.repoachiever.dto.ClusterAllocationDto; +import com.repoachiever.entity.common.PropertiesEntity; +import com.repoachiever.exception.*; +import com.repoachiever.resource.communication.ApiServerCommunicationResource; +import com.repoachiever.service.cluster.ClusterService; +import com.repoachiever.service.cluster.facade.ClusterFacade; +import com.repoachiever.service.communication.common.CommunicationProviderConfigurationHelper; +import com.repoachiever.service.config.ConfigService; +import com.repoachiever.service.state.StateService; +import io.quarkus.runtime.Startup; +import jakarta.annotation.PostConstruct; +import jakarta.enterprise.context.ApplicationScoped; +import jakarta.inject.Inject; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +import java.rmi.RemoteException; +import java.rmi.registry.LocateRegistry; +import java.rmi.registry.Registry; +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.TimeUnit; + +/** + * Service used to perform RepoAchiever Cluster communication health check operations. + */ +@ApplicationScoped +public class ClusterHealthCheckCommunicationService { + private static final Logger logger = LogManager.getLogger(ClusterHealthCheckCommunicationService.class); + + @Inject + PropertiesEntity properties; + + @Inject + ClusterFacade clusterFacade; + + private final static ScheduledExecutorService scheduledExecutorService = + Executors.newSingleThreadScheduledExecutor(); + + /** + * Performs RepoAchiever Cluster communication health check operations. If RepoAchiever Cluster is not responding, + * then it will be redeployed. + */ + @PostConstruct + private void process() { + scheduledExecutorService.schedule(() -> { +// try { +// clusterFacade.reapplyUnhealthy(); +// } catch (ClusterUnhealthyReapplicationFailureException e) { +// logger.fatal(e.getMessage()); +// } + }, properties.getCommunicationClusterHealthCheckFrequency(), TimeUnit.MILLISECONDS); + } +} \ No newline at end of file diff --git a/api-server/src/main/java/com/repoachiever/service/integration/communication/cluster/topology/ClusterTopologyCommunicationConfigService.java b/api-server/src/main/java/com/repoachiever/service/integration/communication/cluster/topology/ClusterTopologyCommunicationConfigService.java new file mode 100644 index 0000000..fdcd129 --- /dev/null +++ b/api-server/src/main/java/com/repoachiever/service/integration/communication/cluster/topology/ClusterTopologyCommunicationConfigService.java @@ -0,0 +1,65 @@ +package com.repoachiever.service.integration.communication.cluster.topology; + +import com.repoachiever.exception.*; +import com.repoachiever.model.ContentApplication; +import com.repoachiever.repository.facade.RepositoryFacade; +import com.repoachiever.service.cluster.facade.ClusterFacade; +import io.quarkus.runtime.Startup; +import jakarta.annotation.PostConstruct; +import jakarta.enterprise.context.ApplicationScoped; +import jakarta.enterprise.event.Observes; +import jakarta.enterprise.event.Shutdown; +import jakarta.inject.Inject; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +import java.util.List; + +/** + * Service used to perform topology configuration. + */ +@Startup(value = 170) +@ApplicationScoped +public class ClusterTopologyCommunicationConfigService { + private static final Logger logger = LogManager.getLogger(ClusterTopologyCommunicationConfigService.class); + + @Inject + RepositoryFacade repositoryFacade; + + @Inject + ClusterFacade clusterFacade; + + /** + * Recreates previously created topology infrastructure if such existed before. + */ + @PostConstruct + private void process() { + List applications; + + try { + applications = repositoryFacade.retrieveContentApplication(); + } catch (ContentApplicationRetrievalFailureException ignored) { + return; + } + + for (ContentApplication application : applications) { + try { + clusterFacade.apply(application); + } catch (ClusterApplicationFailureException e) { + logger.fatal(e.getMessage()); + return; + } + } + } + + /** + * Gracefully stops all the created topology infrastructure. + */ + public void close(@Observes Shutdown event) { + try { + clusterFacade.destroyAll(); + } catch (ClusterFullDestructionFailureException e) { + logger.error(e.getMessage()); + } + } +} diff --git a/api-server/src/main/java/com/repoachiever/service/integration/communication/registry/RegistryCommunicationConfigService.java b/api-server/src/main/java/com/repoachiever/service/integration/communication/registry/RegistryCommunicationConfigService.java new file mode 100644 index 0000000..8332d70 --- /dev/null +++ b/api-server/src/main/java/com/repoachiever/service/integration/communication/registry/RegistryCommunicationConfigService.java @@ -0,0 +1,48 @@ +package com.repoachiever.service.integration.communication.registry; + +import com.repoachiever.exception.CommunicationConfigurationFailureException; +import com.repoachiever.service.config.ConfigService; +import io.quarkus.runtime.Startup; +import jakarta.annotation.PostConstruct; +import jakarta.enterprise.context.ApplicationScoped; +import jakarta.inject.Inject; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +import java.rmi.RemoteException; +import java.rmi.registry.LocateRegistry; +import java.rmi.registry.Registry; + +/** + * Service used to perform initial communication infrastructure configuration. + */ +@Startup(value = 150) +@ApplicationScoped +public class RegistryCommunicationConfigService { + private static final Logger logger = LogManager.getLogger(RegistryCommunicationConfigService.class); + + @Inject + ConfigService configService; + + /** + * Performs initial communication infrastructure configuration. + */ + @PostConstruct + private void process() { + Registry registry; + + try { + registry = LocateRegistry.createRegistry( + configService.getConfig().getCommunication().getPort()); + } catch (RemoteException e) { + logger.fatal(new CommunicationConfigurationFailureException(e.getMessage()).getMessage()); + return; + } + + try { + registry.list(); + } catch (RemoteException e) { + logger.fatal(new CommunicationConfigurationFailureException(e.getMessage()).getMessage()); + } + } +} diff --git a/api-server/src/main/java/com/repoachiever/service/integration/diagnostics/DiagnosticsConfigService.java b/api-server/src/main/java/com/repoachiever/service/integration/diagnostics/DiagnosticsConfigService.java new file mode 100644 index 0000000..ba9f04d --- /dev/null +++ b/api-server/src/main/java/com/repoachiever/service/integration/diagnostics/DiagnosticsConfigService.java @@ -0,0 +1,335 @@ +package com.repoachiever.service.integration.diagnostics; + +import com.repoachiever.dto.CommandExecutorOutputDto; +import com.repoachiever.entity.common.PropertiesEntity; +import com.repoachiever.exception.*; +import com.repoachiever.service.command.docker.availability.DockerAvailabilityCheckCommandService; +import com.repoachiever.service.command.docker.inspect.remove.DockerInspectRemoveCommandService; +import com.repoachiever.service.command.docker.network.create.DockerNetworkCreateCommandService; +import com.repoachiever.service.command.docker.network.remove.DockerNetworkRemoveCommandService; +import com.repoachiever.service.command.grafana.GrafanaDeployCommandService; +import com.repoachiever.service.command.nodeexporter.NodeExporterDeployCommandService; +import com.repoachiever.service.command.prometheus.PrometheusDeployCommandService; +import com.repoachiever.service.config.ConfigService; +import com.repoachiever.service.executor.CommandExecutorService; +import com.repoachiever.service.telemetry.TelemetryService; +import io.quarkus.runtime.Startup; +import jakarta.annotation.PostConstruct; +import jakarta.annotation.PreDestroy; +import jakarta.annotation.Priority; +import jakarta.enterprise.context.ApplicationScoped; +import jakarta.inject.Inject; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +import java.util.Objects; + +/** + * Service used to perform diagnostics infrastructure configuration operations. + */ +@Startup +@Priority(value = 190) +@ApplicationScoped +public class DiagnosticsConfigService { + private static final Logger logger = LogManager.getLogger(DiagnosticsConfigService.class); + + @Inject + PropertiesEntity properties; + + @Inject + ConfigService configService; + + @Inject + CommandExecutorService commandExecutorService; + + @Inject + DockerAvailabilityCheckCommandService dockerAvailabilityCheckCommandService; + + /** + * Creates Docker diagnostics network and deploys diagnostics infrastructure instances with pre-defined configurations. + */ + @PostConstruct + private void process() { + if (configService.getConfig().getDiagnostics().getEnabled()) { + CommandExecutorOutputDto dockerAvailabilityCommandOutput; + + try { + dockerAvailabilityCommandOutput = + commandExecutorService.executeCommand(dockerAvailabilityCheckCommandService); + } catch (CommandExecutorException e) { + logger.fatal(new DockerIsNotAvailableException(e.getMessage()).getMessage()); + return; + } + + String dockerAvailabilityCommandErrorOutput = dockerAvailabilityCommandOutput.getErrorOutput(); + + if ((Objects.nonNull(dockerAvailabilityCommandErrorOutput) && + !dockerAvailabilityCommandErrorOutput.isEmpty()) || + dockerAvailabilityCommandOutput.getNormalOutput().isEmpty()) { + logger.fatal(new DockerIsNotAvailableException(dockerAvailabilityCommandErrorOutput).getMessage()); + return; + } + + DockerInspectRemoveCommandService dockerInspectRemoveCommandService = + new DockerInspectRemoveCommandService(properties.getDiagnosticsPrometheusDockerName()); + + CommandExecutorOutputDto dockerInspectRemoveCommandOutput; + + try { + dockerInspectRemoveCommandOutput = + commandExecutorService.executeCommand(dockerInspectRemoveCommandService); + } catch (CommandExecutorException e) { + logger.fatal(new DockerInspectRemovalFailureException(e.getMessage()).getMessage()); + return; + } + + String dockerInspectRemoveCommandErrorOutput = dockerInspectRemoveCommandOutput.getErrorOutput(); + + if (Objects.nonNull(dockerInspectRemoveCommandErrorOutput) && + !dockerInspectRemoveCommandErrorOutput.isEmpty()) { + logger.fatal(new DockerInspectRemovalFailureException( + dockerInspectRemoveCommandErrorOutput).getMessage()); + } + + + dockerInspectRemoveCommandService = + new DockerInspectRemoveCommandService(properties.getDiagnosticsPrometheusNodeExporterDockerName()); + + try { + dockerInspectRemoveCommandOutput = + commandExecutorService.executeCommand(dockerInspectRemoveCommandService); + } catch (CommandExecutorException e) { + logger.fatal(new DockerInspectRemovalFailureException(e.getMessage()).getMessage()); + return; + } + + dockerInspectRemoveCommandErrorOutput = dockerInspectRemoveCommandOutput.getErrorOutput(); + + if (Objects.nonNull(dockerInspectRemoveCommandErrorOutput) && + !dockerInspectRemoveCommandErrorOutput.isEmpty()) { + logger.fatal(new DockerInspectRemovalFailureException( + dockerInspectRemoveCommandErrorOutput).getMessage()); + } + + dockerInspectRemoveCommandService = + new DockerInspectRemoveCommandService(properties.getDiagnosticsGrafanaDockerName()); + + try { + dockerInspectRemoveCommandOutput = + commandExecutorService.executeCommand(dockerInspectRemoveCommandService); + } catch (CommandExecutorException e) { + logger.fatal(new DockerInspectRemovalFailureException(e.getMessage()).getMessage()); + return; + } + + dockerInspectRemoveCommandErrorOutput = dockerInspectRemoveCommandOutput.getErrorOutput(); + + if (Objects.nonNull(dockerInspectRemoveCommandErrorOutput) && + !dockerInspectRemoveCommandErrorOutput.isEmpty()) { + logger.fatal(new DockerInspectRemovalFailureException( + dockerInspectRemoveCommandErrorOutput).getMessage()); + } + + DockerNetworkCreateCommandService dockerNetworkCreateCommandService = + new DockerNetworkCreateCommandService(properties.getDiagnosticsCommonDockerNetworkName()); + + CommandExecutorOutputDto dockerNetworkCreateCommandOutput; + + try { + dockerNetworkCreateCommandOutput = + commandExecutorService.executeCommand(dockerNetworkCreateCommandService); + } catch (CommandExecutorException e) { + logger.fatal(new DockerNetworkCreateFailureException(e.getMessage()).getMessage()); + return; + } + + String dockerNetworkCreateCommandErrorOutput = dockerNetworkCreateCommandOutput.getErrorOutput(); + + if (Objects.nonNull(dockerNetworkCreateCommandErrorOutput) && + !dockerNetworkCreateCommandErrorOutput.isEmpty()) { + logger.fatal(new DockerNetworkCreateFailureException( + dockerNetworkCreateCommandErrorOutput).getMessage()); + } + + NodeExporterDeployCommandService nodeExporterDeployCommandService = + new NodeExporterDeployCommandService( + properties.getDiagnosticsPrometheusNodeExporterDockerName(), + properties.getDiagnosticsPrometheusNodeExporterDockerImage(), + configService.getConfig().getDiagnostics().getNodeExporter().getPort()); + + CommandExecutorOutputDto nodeExporterDeployCommandOutput; + + try { + nodeExporterDeployCommandOutput = + commandExecutorService.executeCommand(nodeExporterDeployCommandService); + } catch (CommandExecutorException e) { + logger.fatal(new NodeExporterDeploymentFailureException(e.getMessage()).getMessage()); + return; + } + + String nodeExporterDeployCommandErrorOutput = nodeExporterDeployCommandOutput.getErrorOutput(); + + if (Objects.nonNull(nodeExporterDeployCommandErrorOutput) && + !nodeExporterDeployCommandErrorOutput.isEmpty()) { + logger.fatal(new NodeExporterDeploymentFailureException( + nodeExporterDeployCommandErrorOutput).getMessage()); + } + + PrometheusDeployCommandService prometheusDeployCommandService = + new PrometheusDeployCommandService( + properties.getDiagnosticsPrometheusDockerName(), + properties.getDiagnosticsPrometheusDockerImage(), + configService.getConfig().getDiagnostics().getPrometheus().getPort(), + properties.getDiagnosticsPrometheusConfigLocation(), + properties.getDiagnosticsPrometheusInternalLocation()); + + CommandExecutorOutputDto prometheusDeployCommandOutput; + + try { + prometheusDeployCommandOutput = + commandExecutorService.executeCommand(prometheusDeployCommandService); + } catch (CommandExecutorException e) { + logger.fatal(new PrometheusDeploymentFailureException(e.getMessage()).getMessage()); + return; + } + + String prometheusDeployCommandErrorOutput = prometheusDeployCommandOutput.getErrorOutput(); + + if (Objects.nonNull(prometheusDeployCommandErrorOutput) && + !prometheusDeployCommandErrorOutput.isEmpty()) { + logger.fatal(new PrometheusDeploymentFailureException( + prometheusDeployCommandErrorOutput).getMessage()); + } + + GrafanaDeployCommandService grafanaDeployCommandService = + new GrafanaDeployCommandService( + properties.getDiagnosticsGrafanaDockerName(), + properties.getDiagnosticsGrafanaDockerImage(), + configService.getConfig().getDiagnostics().getGrafana().getPort(), + properties.getDiagnosticsGrafanaConfigLocation(), + properties.getDiagnosticsGrafanaInternalLocation()); + + CommandExecutorOutputDto grafanaDeployCommandOutput; + + try { + grafanaDeployCommandOutput = + commandExecutorService.executeCommand(grafanaDeployCommandService); + } catch (CommandExecutorException e) { + logger.fatal(new PrometheusDeploymentFailureException(e.getMessage()).getMessage()); + return; + } + + String grafanaDeployCommandErrorOutput = grafanaDeployCommandOutput.getErrorOutput(); + + if (Objects.nonNull(grafanaDeployCommandErrorOutput) && + !grafanaDeployCommandErrorOutput.isEmpty()) { + logger.fatal(new PrometheusDeploymentFailureException( + grafanaDeployCommandErrorOutput).getMessage()); + } + } + } + + /** + * Removes created Docker networks and stops started diagnostics containers. + */ + @PreDestroy + private void close() { + if (configService.getConfig().getDiagnostics().getEnabled()) { + CommandExecutorOutputDto dockerAvailabilityCommandOutput; + + try { + dockerAvailabilityCommandOutput = + commandExecutorService.executeCommand(dockerAvailabilityCheckCommandService); + } catch (CommandExecutorException e) { + return; + } + + String dockerAvailabilityCommandErrorOutput = dockerAvailabilityCommandOutput.getErrorOutput(); + + if ((Objects.nonNull(dockerAvailabilityCommandErrorOutput) && + !dockerAvailabilityCommandErrorOutput.isEmpty()) || + dockerAvailabilityCommandOutput.getNormalOutput().isEmpty()) { + return; + } + + DockerNetworkRemoveCommandService dockerNetworkRemoveCommandService = + new DockerNetworkRemoveCommandService(properties.getDiagnosticsCommonDockerNetworkName()); + + CommandExecutorOutputDto dockerNetworkRemoveCommandOutput; + + try { + dockerNetworkRemoveCommandOutput = + commandExecutorService.executeCommand(dockerNetworkRemoveCommandService); + } catch (CommandExecutorException e) { + logger.fatal(new DockerNetworkRemoveFailureException(e.getMessage()).getMessage()); + return; + } + + String dockerNetworkRemoveCommandErrorOutput = dockerNetworkRemoveCommandOutput.getErrorOutput(); + + if (Objects.nonNull(dockerNetworkRemoveCommandErrorOutput) && + !dockerNetworkRemoveCommandErrorOutput.isEmpty()) { + logger.fatal(new DockerNetworkRemoveFailureException(dockerNetworkRemoveCommandErrorOutput).getMessage()); + } + + DockerInspectRemoveCommandService dockerInspectRemoveCommandService = + new DockerInspectRemoveCommandService(properties.getDiagnosticsPrometheusDockerName()); + + CommandExecutorOutputDto dockerInspectRemoveCommandOutput; + + try { + dockerInspectRemoveCommandOutput = + commandExecutorService.executeCommand(dockerInspectRemoveCommandService); + } catch (CommandExecutorException e) { + logger.fatal(new DockerInspectRemovalFailureException(e.getMessage()).getMessage()); + return; + } + + String dockerInspectRemoveCommandErrorOutput = dockerInspectRemoveCommandOutput.getErrorOutput(); + + if (Objects.nonNull(dockerInspectRemoveCommandErrorOutput) && + !dockerInspectRemoveCommandErrorOutput.isEmpty()) { + logger.fatal(new DockerInspectRemovalFailureException( + dockerInspectRemoveCommandErrorOutput).getMessage()); + } + + dockerInspectRemoveCommandService = + new DockerInspectRemoveCommandService(properties.getDiagnosticsPrometheusNodeExporterDockerName()); + + try { + dockerInspectRemoveCommandOutput = + commandExecutorService.executeCommand(dockerInspectRemoveCommandService); + } catch (CommandExecutorException e) { + logger.fatal(new DockerInspectRemovalFailureException(e.getMessage()).getMessage()); + return; + } + + dockerInspectRemoveCommandErrorOutput = dockerInspectRemoveCommandOutput.getErrorOutput(); + + if (Objects.nonNull(dockerInspectRemoveCommandErrorOutput) && + !dockerInspectRemoveCommandErrorOutput.isEmpty()) { + logger.fatal(new DockerInspectRemovalFailureException( + dockerInspectRemoveCommandErrorOutput).getMessage()); + } + + dockerInspectRemoveCommandService = + new DockerInspectRemoveCommandService(properties.getDiagnosticsGrafanaDockerName()); + + try { + dockerInspectRemoveCommandOutput = + commandExecutorService.executeCommand(dockerInspectRemoveCommandService); + } catch (CommandExecutorException e) { + logger.fatal(new DockerInspectRemovalFailureException(e.getMessage()).getMessage()); + return; + } + + dockerInspectRemoveCommandErrorOutput = dockerInspectRemoveCommandOutput.getErrorOutput(); + + if (Objects.nonNull(dockerInspectRemoveCommandErrorOutput) && + !dockerInspectRemoveCommandErrorOutput.isEmpty()) { + logger.fatal(new DockerInspectRemovalFailureException( + dockerInspectRemoveCommandErrorOutput).getMessage()); + } + } + } +} diff --git a/api-server/src/main/java/com/repoachiever/service/integration/diagnostics/telemetry/TelemetryConfigService.java b/api-server/src/main/java/com/repoachiever/service/integration/diagnostics/telemetry/TelemetryConfigService.java new file mode 100644 index 0000000..a4b0a48 --- /dev/null +++ b/api-server/src/main/java/com/repoachiever/service/integration/diagnostics/telemetry/TelemetryConfigService.java @@ -0,0 +1,168 @@ +package com.repoachiever.service.integration.diagnostics.telemetry; + +import com.repoachiever.entity.common.PropertiesEntity; +import com.repoachiever.exception.TelemetryOperationFailureException; +import com.repoachiever.service.config.ConfigService; +import com.repoachiever.service.telemetry.binding.TelemetryBinding; +import io.micrometer.core.instrument.binder.jvm.JvmMemoryMetrics; +import io.micrometer.core.instrument.binder.jvm.JvmThreadMetrics; +import io.micrometer.core.instrument.binder.system.DiskSpaceMetrics; +import io.micrometer.core.instrument.binder.system.ProcessorMetrics; +import io.micrometer.core.instrument.binder.system.UptimeMetrics; +import io.micrometer.prometheus.PrometheusConfig; +import io.micrometer.prometheus.PrometheusMeterRegistry; +import io.quarkus.runtime.Startup; +import jakarta.annotation.PostConstruct; +import jakarta.annotation.PreDestroy; +import jakarta.annotation.Priority; +import jakarta.enterprise.context.ApplicationScoped; +import jakarta.inject.Inject; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +import java.io.*; +import java.net.ServerSocket; +import java.net.Socket; +import java.net.SocketException; +import java.nio.charset.StandardCharsets; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; + +/** + * Service used to perform diagnostics telemetry configuration operations. + */ +@Startup +@Priority(value = 200) +@ApplicationScoped +public class TelemetryConfigService { + private static final Logger logger = LogManager.getLogger(TelemetryConfigService.class); + + @Inject + PropertiesEntity properties; + + @Inject + ConfigService configService; + + @Inject + TelemetryBinding telemetryBinding; + + private ServerSocket connector; + + private final ExecutorService executorService = Executors.newVirtualThreadPerTaskExecutor(); + + /** + * Performs telemetry metrics server configuration, registering bindings provided by external provider. + */ + @PostConstruct + private void configure() { + try { + connector = new ServerSocket( + configService.getConfig().getDiagnostics().getMetrics().getPort()); + } catch (IOException e) { + logger.fatal(new TelemetryOperationFailureException(e.getMessage()).getMessage()); + return; + } + + PrometheusMeterRegistry prometheusRegistry = new PrometheusMeterRegistry(PrometheusConfig.DEFAULT); + + new JvmThreadMetrics().bindTo(prometheusRegistry); + new JvmMemoryMetrics().bindTo(prometheusRegistry); + new DiskSpaceMetrics(new File(properties.getWorkspaceDirectory())).bindTo(prometheusRegistry); + new ProcessorMetrics().bindTo(prometheusRegistry); + new UptimeMetrics().bindTo(prometheusRegistry); + + telemetryBinding.bindTo(prometheusRegistry); + + Thread.ofPlatform().start(() -> { + while (!connector.isClosed()) { + Socket connection; + + try { + connection = connector.accept(); + } catch (IOException ignored) { + continue; + } + + try { + connection.setSoTimeout(properties.getDiagnosticsMetricsConnectionTimeout()); + } catch (SocketException e) { + logger.error(new TelemetryOperationFailureException(e.getMessage()).getMessage()); + return; + } + + executorService.execute(() -> { + OutputStreamWriter outputStreamWriter; + + try { + outputStreamWriter = + new OutputStreamWriter(connection.getOutputStream(), StandardCharsets.UTF_8); + } catch (IOException e) { + logger.error(new TelemetryOperationFailureException(e.getMessage()).getMessage()); + return; + } + + BufferedReader inputStreamReader; + try { + inputStreamReader = new BufferedReader( + new InputStreamReader(connection.getInputStream())); + } catch (IOException e) { + logger.error(new TelemetryOperationFailureException(e.getMessage()).getMessage()); + return; + } + + try { + outputStreamWriter.write( + String.format( + "HTTP/1.1 200 OK\r\nContent-Type: text/plain\r\nConnection: close\r\n\r\n%s", + prometheusRegistry.scrape())); + } catch (IOException ignored) { + return; + } + + try { + outputStreamWriter.flush(); + } catch (IOException e) { + logger.error(new TelemetryOperationFailureException(e.getMessage()).getMessage()); + return; + } + + try { + inputStreamReader.readLine(); + } catch (IOException e) { + logger.error(new TelemetryOperationFailureException(e.getMessage()).getMessage()); + return; + } + + try { + inputStreamReader.close(); + } catch (IOException e) { + logger.error(new TelemetryOperationFailureException(e.getMessage()).getMessage()); + return; + } + + try { + outputStreamWriter.close(); + } catch (IOException e) { + logger.error(new TelemetryOperationFailureException(e.getMessage()).getMessage()); + return; + } + + try { + connection.close(); + } catch (IOException e) { + logger.error(new TelemetryOperationFailureException(e.getMessage()).getMessage()); + } + }); + } + }); + } + + @PreDestroy + private void close() { + try { + connector.close(); + } catch (IOException e) { + logger.error(new TelemetryOperationFailureException(e.getMessage()).getMessage()); + } + } +} diff --git a/api-server/src/main/java/com/repoachiever/service/integration/diagnostics/template/TemplateConfigService.java b/api-server/src/main/java/com/repoachiever/service/integration/diagnostics/template/TemplateConfigService.java new file mode 100644 index 0000000..b3fd358 --- /dev/null +++ b/api-server/src/main/java/com/repoachiever/service/integration/diagnostics/template/TemplateConfigService.java @@ -0,0 +1,214 @@ +package com.repoachiever.service.integration.diagnostics.template; + +import com.repoachiever.entity.common.PropertiesEntity; +import com.repoachiever.exception.DiagnosticsTemplateProcessingFailureException; +import com.repoachiever.service.config.ConfigService; +import freemarker.cache.FileTemplateLoader; +import freemarker.template.*; +import io.quarkus.runtime.Startup; +import jakarta.annotation.PostConstruct; +import jakarta.annotation.Priority; +import jakarta.enterprise.context.ApplicationScoped; + +import jakarta.inject.Inject; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +import java.io.File; +import java.io.FileWriter; +import java.io.IOException; +import java.io.Writer; +import java.nio.file.Paths; +import java.util.Arrays; +import java.util.HashMap; +import java.util.Map; +import java.util.Objects; + +import static freemarker.template.Configuration.VERSION_2_3_32; + +/** + * Service used to perform diagnostics template configuration operations. + */ +@Startup +@Priority(value = 180) +@ApplicationScoped +public class TemplateConfigService { + private static final Logger logger = LogManager.getLogger(TemplateConfigService.class); + + @Inject + PropertiesEntity properties; + + @Inject + ConfigService configService; + + /** + * Performs diagnostics infrastructure configuration templates parsing operations. + */ + @PostConstruct + private void process() { + if (configService.getConfig().getDiagnostics().getEnabled()) { + Configuration cfg = new Configuration(VERSION_2_3_32); + try { + cfg.setTemplateLoader(new FileTemplateLoader(new File(properties.getDiagnosticsPrometheusConfigLocation()))); + } catch (IOException e) { + logger.fatal(new DiagnosticsTemplateProcessingFailureException(e.getMessage()).getMessage()); + return; + } + cfg.setDefaultEncoding("UTF-8"); + + Template template; + + try { + template = cfg.getTemplate(properties.getDiagnosticsPrometheusConfigTemplate()); + } catch (IOException e) { + logger.fatal(new DiagnosticsTemplateProcessingFailureException(e.getMessage()).getMessage()); + return; + } + + Writer fileWriter; + + try { + fileWriter = new FileWriter( + Paths.get( + properties.getDiagnosticsPrometheusConfigLocation(), + properties.getDiagnosticsPrometheusConfigOutput()). + toFile()); + } catch (IOException e) { + logger.fatal(new DiagnosticsTemplateProcessingFailureException(e.getMessage()).getMessage()); + return; + } + + Map input = new HashMap<>() { + { + put("metrics", new HashMap() { + { + put("host", "host.docker.internal"); + put("port", String.valueOf(configService.getConfig().getDiagnostics().getMetrics().getPort())); + } + }); + put("nodeexporter", new HashMap() { + { + put("host", properties.getDiagnosticsPrometheusNodeExporterDockerName()); + put("port", String.valueOf(configService.getConfig().getDiagnostics().getNodeExporter().getPort())); + } + }); + } + }; + + try { + template.process(input, fileWriter); + } catch (TemplateException | IOException e) { + logger.fatal(new DiagnosticsTemplateProcessingFailureException(e.getMessage()).getMessage()); + } finally { + try { + fileWriter.close(); + } catch (IOException e) { + logger.fatal(new DiagnosticsTemplateProcessingFailureException(e.getMessage()).getMessage()); + } + } + + try { + cfg.setTemplateLoader(new FileTemplateLoader( + new File(properties.getDiagnosticsGrafanaDatasourcesLocation()))); + } catch (IOException e) { + logger.fatal(new DiagnosticsTemplateProcessingFailureException(e.getMessage()).getMessage()); + return; + } + + try { + template = cfg.getTemplate(properties.getDiagnosticsGrafanaDatasourcesTemplate()); + } catch (IOException e) { + logger.fatal(new DiagnosticsTemplateProcessingFailureException(e.getMessage()).getMessage()); + return; + } + + try { + fileWriter = new FileWriter( + Paths.get( + properties.getDiagnosticsGrafanaDatasourcesLocation(), + properties.getDiagnosticsGrafanaDatasourcesOutput()). + toFile()); + } catch (IOException e) { + logger.fatal(new DiagnosticsTemplateProcessingFailureException(e.getMessage()).getMessage()); + return; + } + + input = new HashMap<>() { + { + put("prometheus", new HashMap() { + { + put("host", properties.getDiagnosticsPrometheusDockerName()); + put("port", String.valueOf(configService.getConfig().getDiagnostics().getPrometheus().getPort())); + } + }); + } + }; + + try { + template.process(input, fileWriter); + } catch (TemplateException | IOException e) { + logger.fatal(new DiagnosticsTemplateProcessingFailureException(e.getMessage()).getMessage()); + } finally { + try { + fileWriter.close(); + } catch (IOException e) { + logger.fatal(new DiagnosticsTemplateProcessingFailureException(e.getMessage()).getMessage()); + } + } + + try { + cfg.setTemplateLoader(new FileTemplateLoader( + new File(properties.getDiagnosticsGrafanaDashboardsLocation()))); + } catch (IOException e) { + logger.fatal(new DiagnosticsTemplateProcessingFailureException(e.getMessage()).getMessage()); + return; + } + + try { + template = cfg.getTemplate(properties.getDiagnosticsGrafanaDashboardsDiagnosticsTemplate()); + } catch (IOException e) { + logger.fatal(new DiagnosticsTemplateProcessingFailureException(e.getMessage()).getMessage()); + return; + } + + try { + fileWriter = new FileWriter( + Paths.get( + properties.getDiagnosticsGrafanaDashboardsLocation(), + properties.getDiagnosticsGrafanaDashboardsDiagnosticsOutput()). + toFile()); + } catch (IOException e) { + logger.fatal(new DiagnosticsTemplateProcessingFailureException(e.getMessage()).getMessage()); + return; + } + + input = new HashMap<>() { + { + put("info", new HashMap() { + { + put("version", properties.getGitCommitId()); + } + }); + put("nodeexporter", new HashMap() { + { + put("port", String.valueOf( + configService.getConfig().getDiagnostics().getNodeExporter().getPort())); + } + }); + } + }; + + try { + template.process(input, fileWriter); + } catch (TemplateException | IOException e) { + logger.fatal(new DiagnosticsTemplateProcessingFailureException(e.getMessage()).getMessage()); + } finally { + try { + fileWriter.close(); + } catch (IOException e) { + logger.fatal(new DiagnosticsTemplateProcessingFailureException(e.getMessage()).getMessage()); + } + } + } + } +} diff --git a/api-server/src/main/java/com/repoachiever/service/integration/http/HttpServerConfigService.java b/api-server/src/main/java/com/repoachiever/service/integration/http/HttpServerConfigService.java new file mode 100644 index 0000000..341f4f2 --- /dev/null +++ b/api-server/src/main/java/com/repoachiever/service/integration/http/HttpServerConfigService.java @@ -0,0 +1,28 @@ +package com.repoachiever.service.integration.http; + +import com.repoachiever.entity.common.ConfigEntity; +import com.repoachiever.service.config.ConfigService; +import io.quarkus.vertx.http.HttpServerOptionsCustomizer; +import io.vertx.core.http.HttpServerOptions; +import jakarta.enterprise.context.ApplicationScoped; +import jakarta.inject.Inject; + +/** + * Provides http server configuration used as a source of properties for all defined resources. + */ +@ApplicationScoped +public class HttpServerConfigService implements HttpServerOptionsCustomizer { + @Inject + ConfigService configService; + + /** + * @see HttpServerOptionsCustomizer + */ + @Override + public void customizeHttpServer(HttpServerOptions options) { + ConfigEntity.Connection connection = configService.getConfig().getConnection(); + + options.setPort(connection.getPort()); + } +} + diff --git a/api-server/src/main/java/com/repoachiever/service/integration/properties/general/GeneralPropertiesConfigService.java b/api-server/src/main/java/com/repoachiever/service/integration/properties/general/GeneralPropertiesConfigService.java new file mode 100644 index 0000000..782db0e --- /dev/null +++ b/api-server/src/main/java/com/repoachiever/service/integration/properties/general/GeneralPropertiesConfigService.java @@ -0,0 +1,17 @@ +package com.repoachiever.service.integration.properties.general; + +import io.quarkus.runtime.Startup; +import jakarta.annotation.PostConstruct; +import jakarta.enterprise.context.ApplicationScoped; + +/** + * Service used to perform general properties configuration operations. + */ +@Startup +@ApplicationScoped +public class GeneralPropertiesConfigService { + @PostConstruct + private void process() { + + } +} \ No newline at end of file diff --git a/api-server/src/main/java/com/repoachiever/service/integration/properties/git/GitPropertiesConfigService.java b/api-server/src/main/java/com/repoachiever/service/integration/properties/git/GitPropertiesConfigService.java new file mode 100644 index 0000000..88653cc --- /dev/null +++ b/api-server/src/main/java/com/repoachiever/service/integration/properties/git/GitPropertiesConfigService.java @@ -0,0 +1,75 @@ +package com.repoachiever.service.integration.properties.git; + +import io.quarkus.runtime.annotations.StaticInitSafe; +import java.io.IOException; +import java.io.InputStream; +import java.util.Map; +import java.util.Properties; +import java.util.Set; +import java.util.stream.Collectors; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.eclipse.microprofile.config.spi.ConfigSource; + +/** Provides access to external config source used as a source of Git info. */ +@StaticInitSafe +public class GitPropertiesConfigService implements ConfigSource { + private static final Logger logger = LogManager.getLogger(GitPropertiesConfigService.class); + + private static final String GIT_CONFIG_PROPERTIES_FILE = "git.properties"; + + private final Properties config; + + public GitPropertiesConfigService() { + this.config = new Properties(); + + ClassLoader classLoader = getClass().getClassLoader(); + InputStream gitBuildPropertiesStream = + classLoader.getResourceAsStream(GIT_CONFIG_PROPERTIES_FILE); + try { + config.load(gitBuildPropertiesStream); + } catch (IOException e) { + logger.fatal(e.getMessage()); + } + } + + /** + * @see ConfigSource + */ + @Override + public Map getProperties() { + return ConfigSource.super.getProperties(); + } + + /** + * @see ConfigSource + */ + @Override + public Set getPropertyNames() { + return config.keySet().stream().map(element -> (String) element).collect(Collectors.toSet()); + } + + /** + * @see ConfigSource + */ + @Override + public int getOrdinal() { + return ConfigSource.super.getOrdinal(); + } + + /** + * @see ConfigSource + */ + @Override + public String getValue(String key) { + return (String) config.get(key); + } + + /** + * @see ConfigSource + */ + @Override + public String getName() { + return GitPropertiesConfigService.class.getSimpleName(); + } +} diff --git a/api-server/src/main/java/com/repoachiever/service/integration/state/StateConfigService.java b/api-server/src/main/java/com/repoachiever/service/integration/state/StateConfigService.java new file mode 100644 index 0000000..6a35e33 --- /dev/null +++ b/api-server/src/main/java/com/repoachiever/service/integration/state/StateConfigService.java @@ -0,0 +1,66 @@ +package com.repoachiever.service.integration.state; + +import com.repoachiever.entity.common.PropertiesEntity; +import com.repoachiever.exception.ApiServerInstanceIsAlreadyRunningException; +import com.repoachiever.service.state.StateService; +import io.quarkus.runtime.Startup; +import jakarta.annotation.PostConstruct; +import jakarta.annotation.PreDestroy; +import jakarta.annotation.Priority; +import jakarta.enterprise.context.ApplicationScoped; +import jakarta.inject.Inject; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; + +/** + * Service used to perform application critical state configuration. + */ +@Startup +@Priority(value = 140) +@ApplicationScoped +public class StateConfigService { + private static final Logger logger = LogManager.getLogger(StateConfigService.class); + + @Inject + PropertiesEntity properties; + + /** + * Performs application state initialization operations. + */ + @PostConstruct + private void process() { + Path running = Paths.get(properties.getStateLocation(), properties.getStateRunningName()); + + if (Files.exists(running)) { + logger.fatal(new ApiServerInstanceIsAlreadyRunningException().getMessage()); + return; + } + + try { + Files.createFile(running); + } catch (IOException e) { + logger.fatal(e.getMessage()); + } + + StateService.setStarted(true); + } + + /** + * Performs graceful application state cleanup after execution is finished. + */ + @PreDestroy + private void close() { + if (StateService.getStarted()) { + try { + Files.delete(Paths.get(properties.getStateLocation(), properties.getStateRunningName())); + } catch (IOException e) { + logger.error(e.getMessage()); + } + } + } +} diff --git a/api-server/src/main/java/com/repoachiever/service/state/StateService.java b/api-server/src/main/java/com/repoachiever/service/state/StateService.java new file mode 100644 index 0000000..95d1579 --- /dev/null +++ b/api-server/src/main/java/com/repoachiever/service/state/StateService.java @@ -0,0 +1,80 @@ +package com.repoachiever.service.state; + +import com.repoachiever.dto.ClusterAllocationDto; +import lombok.Getter; +import lombok.Setter; + +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; +import java.util.concurrent.locks.ReentrantLock; +import java.util.stream.Collectors; + +/** + * Service used to operate as a collection of application state properties. + */ +public class StateService { + /** + * Represents if RepoAchiever API Server application has been started. + */ + @Getter + @Setter + private static Boolean started = false; + + /** + * Represents RepoAchiever Cluster topology state guard. + */ + @Getter + private final static ReentrantLock topologyStateGuard = new ReentrantLock(); + + + /** + * Represents a set of all available RepoAchiever Cluster allocations. + */ + @Getter + private final static List clusterAllocations = new ArrayList<>(); + + /** + * Retrieves RepoAchiever allocations with the given workspace unit key. + * + * @param workspaceUnitKey given workspace unit key. + * @return filtered RepoAchiever allocations according to the given workspace unit key. + */ + public static List getClusterAllocationsByWorkspaceUnitKey(String workspaceUnitKey) { + return clusterAllocations. + stream(). + filter(element -> Objects.equals(element.getWorkspaceUnitKey(), workspaceUnitKey)). + collect(Collectors.toList()); + } + + /** + * Adds new RepoAchiever Cluster allocations. + * + * @param allocations given RepoAchiever Cluster allocations. + */ + public static void addClusterAllocations(List allocations) { + clusterAllocations.addAll(allocations); + } + + /** + * Checks if RepoAchiever Cluster allocations with the given name exists. + * + * @param name given RepoAchiever Cluster allocation. + * @return result of the check. + */ + public static Boolean isClusterAllocationPresentByName(String name) { + return clusterAllocations + .stream() + .anyMatch(element -> Objects.equals(element.getName(), name)); + } + + /** + * Removes RepoAchiever Cluster allocations with the given names. + * + * @param names given RepoAchiever Cluster allocation names. + */ + public static void removeClusterAllocationByNames(List names) { + names.forEach( + element1 -> clusterAllocations.removeIf(element2 -> Objects.equals(element2.getName(), element1))); + } +} diff --git a/api-server/src/main/java/com/repoachiever/service/telemetry/TelemetryService.java b/api-server/src/main/java/com/repoachiever/service/telemetry/TelemetryService.java new file mode 100644 index 0000000..35706da --- /dev/null +++ b/api-server/src/main/java/com/repoachiever/service/telemetry/TelemetryService.java @@ -0,0 +1,57 @@ +package com.repoachiever.service.telemetry; + +import com.repoachiever.service.telemetry.binding.TelemetryBinding; +import jakarta.enterprise.context.ApplicationScoped; +import jakarta.inject.Inject; + +/** + * Provides access to gather information and expose it to telemetry representation tool. + */ +@ApplicationScoped +public class TelemetryService { + @Inject + TelemetryBinding telemetryBinding; + + /** + * Increases allocated workers amount counter. + * + * @param value given increment value. + */ + public void increaseWorkersAmountBy(Integer value) { + telemetryBinding.getWorkerAmount().set(telemetryBinding.getWorkerAmount().get() + value); + } + + /** + * Decreases allocated workers amount counter. + * + * @param value given increment value. + */ + public void decreaseWorkersAmountBy(Integer value) { + telemetryBinding.getWorkerAmount().set(telemetryBinding.getWorkerAmount().get() - value); + } + + /** + * Increases allocated clusters amount counter. + * + * @param value given increment value. + */ + public void increaseClustersAmountBy(Integer value) { + telemetryBinding.getWorkerAmount().set(telemetryBinding.getWorkerAmount().get() + value); + } + + /** + * Decreases allocated clusters amount counter. + * + * @param value given increment value. + */ + public void decreaseClustersAmountBy(Integer value) { + telemetryBinding.getWorkerAmount().set(telemetryBinding.getWorkerAmount().get() - value); + } + + /** + * Records latest cluster allocation health check. + */ + public void recordClusterHealthCheck() { + + } +} diff --git a/api-server/src/main/java/com/repoachiever/service/telemetry/binding/TelemetryBinding.java b/api-server/src/main/java/com/repoachiever/service/telemetry/binding/TelemetryBinding.java new file mode 100644 index 0000000..239e829 --- /dev/null +++ b/api-server/src/main/java/com/repoachiever/service/telemetry/binding/TelemetryBinding.java @@ -0,0 +1,44 @@ +package com.repoachiever.service.telemetry.binding; + +import io.micrometer.core.instrument.Timer; +import io.micrometer.core.instrument.Gauge; +import io.micrometer.core.instrument.MeterRegistry; +import io.micrometer.core.instrument.binder.MeterBinder; +import jakarta.enterprise.context.ApplicationScoped; +import lombok.Getter; +import org.jetbrains.annotations.NotNull; + +import java.util.concurrent.atomic.AtomicInteger; + +/** + * Service used to create custom telemetry bindings used to distribute application metrics. + */ +@ApplicationScoped +public class TelemetryBinding implements MeterBinder { + @Getter + private final AtomicInteger workerAmount = new AtomicInteger(); + + @Getter + private final AtomicInteger clusterAmount = new AtomicInteger(); + + @Getter + private Timer clusterHealthCheck; + + /** + * @see MeterBinder + */ + @Override + public void bindTo(@NotNull MeterRegistry meterRegistry) { + Gauge.builder("general.worker_amount", workerAmount, AtomicInteger::get) + .description("Represents amount of allocated workers") + .register(meterRegistry); + + Gauge.builder("general.cluster_amount", clusterAmount, AtomicInteger::get) + .description("Represents amount of allocated clusters") + .register(meterRegistry); + + clusterHealthCheck = Timer.builder("general.cluster_health_check") + .description("Represents all the performed health check requests for allocated clusters") + .register(meterRegistry); + } +} diff --git a/api-server/src/main/java/com/repoachiever/service/vendor/VendorFacade.java b/api-server/src/main/java/com/repoachiever/service/vendor/VendorFacade.java new file mode 100644 index 0000000..5c2c4b5 --- /dev/null +++ b/api-server/src/main/java/com/repoachiever/service/vendor/VendorFacade.java @@ -0,0 +1,28 @@ +package com.repoachiever.service.vendor; + +import com.repoachiever.model.CredentialsFieldsExternal; +import com.repoachiever.model.Provider; +import com.repoachiever.service.vendor.git.github.GitGitHubVendorService; +import jakarta.enterprise.context.ApplicationScoped; +import jakarta.inject.Inject; + +/** Provides high-level access to VCS vendor operations. */ +@ApplicationScoped +public class VendorFacade { + @Inject + GitGitHubVendorService gitGitHubVendorService; + + /** + * Checks if the given external credentials are valid, according to the given provider name. + * + * @param provider given external provider name. + * @param credentialsFieldExternal given external credentials. + * @return result of the check. + */ + public Boolean isExternalCredentialsValid(Provider provider, CredentialsFieldsExternal credentialsFieldExternal) { + return switch (provider) { + case LOCAL -> true; + case GITHUB -> gitGitHubVendorService.isTokenValid(credentialsFieldExternal.getToken()); + }; + } +} diff --git a/api-server/src/main/java/com/repoachiever/service/vendor/common/VendorConfigurationHelper.java b/api-server/src/main/java/com/repoachiever/service/vendor/common/VendorConfigurationHelper.java new file mode 100644 index 0000000..eab22c9 --- /dev/null +++ b/api-server/src/main/java/com/repoachiever/service/vendor/common/VendorConfigurationHelper.java @@ -0,0 +1,16 @@ +package com.repoachiever.service.vendor.common; + +/** + * Contains helpful tools used for vendor configuration. + */ +public class VendorConfigurationHelper { + /** + * Converts given raw token value to a wrapped format. + * + * @param token given raw token value. + * @return wrapped token. + */ + public static String getWrappedToken(String token) { + return String.format("Bearer %s", token); + } +} diff --git a/api-server/src/main/java/com/repoachiever/service/vendor/git/github/GitGitHubVendorService.java b/api-server/src/main/java/com/repoachiever/service/vendor/git/github/GitGitHubVendorService.java new file mode 100644 index 0000000..dbe5dd7 --- /dev/null +++ b/api-server/src/main/java/com/repoachiever/service/vendor/git/github/GitGitHubVendorService.java @@ -0,0 +1,33 @@ +package com.repoachiever.service.vendor.git.github; + +import com.repoachiever.service.client.github.IGitHubClientService; +import com.repoachiever.service.vendor.common.VendorConfigurationHelper; +import jakarta.enterprise.context.ApplicationScoped; +import jakarta.inject.Inject; +import jakarta.ws.rs.WebApplicationException; +import jakarta.ws.rs.core.Response; +import org.apache.http.HttpStatus; +import org.eclipse.microprofile.rest.client.inject.RestClient; + +@ApplicationScoped +public class GitGitHubVendorService { + @Inject + @RestClient + IGitHubClientService gitHubClientService; + + /** + * Checks if the given token is valid. + * + * @return result of the check. + */ + public boolean isTokenValid(String token) { + try { + Response response = gitHubClientService + .getOctocat(VendorConfigurationHelper.getWrappedToken(token)); + + return response.getStatus() == HttpStatus.SC_OK; + } catch (WebApplicationException e) { + return false; + } + } +} diff --git a/api-server/src/main/java/com/repoachiever/service/workspace/WorkspaceService.java b/api-server/src/main/java/com/repoachiever/service/workspace/WorkspaceService.java new file mode 100644 index 0000000..f2aee94 --- /dev/null +++ b/api-server/src/main/java/com/repoachiever/service/workspace/WorkspaceService.java @@ -0,0 +1,362 @@ +package com.repoachiever.service.workspace; + +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.DeserializationFeature; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.ObjectReader; +import com.repoachiever.entity.common.MetadataFileEntity; +import com.repoachiever.entity.common.PropertiesEntity; +import com.repoachiever.exception.*; +import jakarta.enterprise.context.ApplicationScoped; +import jakarta.inject.Inject; +import jakarta.xml.bind.DatatypeConverter; + +import java.io.*; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.security.MessageDigest; + +import lombok.SneakyThrows; +import org.apache.commons.io.FileUtils; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.springframework.util.FileSystemUtils; + +/** + * Represents local content workspace for different users. + */ +@ApplicationScoped +public class WorkspaceService { + private static final Logger logger = LogManager.getLogger(WorkspaceService.class); + + @Inject + PropertiesEntity properties; + + /** + * Creates unit key from the given segments. + * + * @param segments given segments to be used for unit key creation. + * @return created unit key from the given segments. + */ + @SneakyThrows + public String createUnitKey(String... segments) { + MessageDigest md = MessageDigest.getInstance("SHA3-256"); + return DatatypeConverter.printHexBinary(md.digest(String.join(".", segments).getBytes())); + } + + /** + * Creates content directory in the given workspace unit directory + * + * @param workspaceUnitDirectory given workspace unit directory + * @throws WorkspaceContentDirectoryCreationFailureException if workspace content directory creation operation failed. + */ + public void createContentDirectory(String workspaceUnitDirectory) throws + WorkspaceContentDirectoryCreationFailureException { + Path unitDirectoryPath = Path.of(workspaceUnitDirectory, properties.getWorkspaceContentDirectory()); + + if (Files.notExists(unitDirectoryPath)) { + try { + Files.createDirectory(unitDirectoryPath); + } catch (IOException e) { + throw new WorkspaceContentDirectoryCreationFailureException(e.getMessage()); + } + } + } + + /** + * Creates workspace unit with the help of the given key. + * + * @param key given workspace unit key. + * @throws WorkspaceUnitDirectoryCreationFailureException if workspace unit directory creation operation failed. + */ + public void createUnitDirectory(String key) throws + WorkspaceUnitDirectoryCreationFailureException { + Path unitDirectoryPath = Path.of(properties.getWorkspaceDirectory(), key); + + if (Files.notExists(unitDirectoryPath)) { + try { + Files.createDirectory(unitDirectoryPath); + } catch (IOException e) { + throw new WorkspaceUnitDirectoryCreationFailureException(e.getMessage()); + } + } + } + + /** + * Removes workspace unit with the help of the given key. + * + * @param key given workspace unit key. + * @throws WorkspaceUnitDirectoryRemovalFailureException if IO operation failed. + */ + public void removeUnitDirectory(String key) throws WorkspaceUnitDirectoryRemovalFailureException { + try { + FileSystemUtils.deleteRecursively(Path.of(properties.getWorkspaceDirectory(), key)); + } catch (IOException e) { + throw new WorkspaceUnitDirectoryRemovalFailureException(e.getMessage()); + } + } + + /** + * Checks if workspace unit directory with the help of the given key. + * + * @param key given workspace unit key. + * @return result if workspace unit directory exists with the help of the given key. + */ + public boolean isUnitDirectoryExist(String key) { + return Files.exists(Paths.get(properties.getWorkspaceDirectory(), key)); + } + + /** + * Retrieves path for the workspace unit with the help of the given key. + * + * @param key given workspace unit key. + * @throws WorkspaceUnitDirectoryNotFoundException if workspace unit with the given name does not + * exist. + */ + public String getUnitDirectory(String key) throws WorkspaceUnitDirectoryNotFoundException { + Path unitDirectoryPath = Path.of(properties.getWorkspaceDirectory(), key); + + if (Files.notExists(unitDirectoryPath)) { + throw new WorkspaceUnitDirectoryNotFoundException(); + } + + return unitDirectoryPath.toString(); + } + + /** + * Writes given content repository to the given workspace unit directory. + * + * @param workspaceUnitDirectory given workspace unit directory. + * @param name given content repository name. + * @param input given content repository input. + * @throws ContentFileWriteFailureException if content file cannot be created. + */ + public void createContentFile(String workspaceUnitDirectory, String name, InputStream input) throws + ContentFileWriteFailureException { + Path contentDirectoryPath = Path.of(workspaceUnitDirectory, properties.getWorkspaceContentDirectory(), name); + + File file = new File(contentDirectoryPath.toString()); + + try { + FileUtils.copyInputStreamToFile(input, file); + } catch (IOException e) { + throw new ContentFileWriteFailureException(e.getMessage()); + } + } + + /** + * Removes content file in the given workspace unit. + * + * @param workspaceUnitDirectory given workspace unit directory. + * @param name given content repository name. + * @throws ContentFileRemovalFailureException if content file cannot be created. + */ + public void removeContentFile(String workspaceUnitDirectory, String name) throws + ContentFileRemovalFailureException { + try { + FileSystemUtils.deleteRecursively( + Path.of(workspaceUnitDirectory, properties.getWorkspaceContentDirectory(), name)); + } catch (IOException e) { + throw new ContentFileRemovalFailureException(e); + } + } + + /** + * Retrieves content file of the given name with the help of the given workspace unit directory. + * + * @param workspaceUnitDirectory given workspace unit directory. + * @param name given name of the content file. + * @return content file entity. + * @throws ContentFileNotFoundException if the content file not found. + */ + public OutputStream getContentFile(String workspaceUnitDirectory, String name) throws + ContentFileNotFoundException { + Path contentDirectoryPath = Path.of(workspaceUnitDirectory, properties.getWorkspaceContentDirectory(), name); + + File file = new File(contentDirectoryPath.toString()); + + try { + return new FileOutputStream(file); + } catch (FileNotFoundException e) { + throw new ContentFileNotFoundException(e); + } + } + + /** + * Writes metadata file input of the given type to the given workspace unit directory. + * + * @param workspaceUnitDirectory given workspace unit directory. + * @param type given type of the metadata file. + * @param input given metadata file entity input. + * @throws MetadataFileWriteFailureException if metadata file cannot be created. + */ + private void createMetadataFile(String workspaceUnitDirectory, String type, MetadataFileEntity input) + throws MetadataFileWriteFailureException { + ObjectMapper mapper = new ObjectMapper(); + + File variableFile = + new File( + Paths.get(workspaceUnitDirectory, properties.getWorkspaceMetadataDirectory(), type) + .toString()); + + try { + mapper.writeValue(variableFile, input); + } catch (IOException e) { + throw new MetadataFileWriteFailureException(e.getMessage()); + } + } + + /** + * Writes metadata file input of the prs type to the given workspace unit directory. + * + * @param workspaceUnitDirectory given workspace unit directory. + * @param input given metadata file entity input. + * @throws MetadataFileWriteFailureException if metadata file cannot be created. + */ + public void createPRsMetadataFile(String workspaceUnitDirectory, MetadataFileEntity input) + throws MetadataFileWriteFailureException { + createMetadataFile(workspaceUnitDirectory, properties.getWorkspacePRsMetadataFileName(), input); + } + + /** + * Writes metadata file input of the issues type to the given workspace unit directory. + * + * @param workspaceUnitDirectory given workspace unit directory. + * @param input given metadata file entity input. + * @throws MetadataFileWriteFailureException if metadata file cannot be created. + */ + public void createIssuesMetadataFile(String workspaceUnitDirectory, MetadataFileEntity input) + throws MetadataFileWriteFailureException { + createMetadataFile(workspaceUnitDirectory, properties.getWorkspaceIssuesMetadataFileName(), input); + } + + /** + * Writes metadata file input of the releases type to the given workspace unit directory. + * + * @param workspaceUnitDirectory given workspace unit directory. + * @param input given metadata file entity input. + * @throws MetadataFileWriteFailureException if metadata file cannot be created. + */ + public void createReleasesMetadataFile(String workspaceUnitDirectory, MetadataFileEntity input) + throws MetadataFileWriteFailureException { + createMetadataFile(workspaceUnitDirectory, properties.getWorkspaceReleasesMetadataFileName(), input); + } + + /** + * Checks if metadata file of the given type exists in the given workspace unit directory. + * + * @param workspaceUnitDirectory given workspace unit directory. + * @param type given type of the metadata file. + * @return result if metadata file exists in the given workspace unit directory. + */ + private boolean isMetadataFileExist(String workspaceUnitDirectory, String type) { + return Files.exists( + Paths.get(workspaceUnitDirectory, properties.getWorkspaceMetadataDirectory(), type)); + } + + /** + * Checks if metadata file of prs type exists in the given workspace unit directory. + * + * @param workspaceUnitDirectory given workspace unit directory. + * @return result if metadata file exists in the given workspace unit directory. + */ + private boolean isPRsMetadataFileExist(String workspaceUnitDirectory) { + return isMetadataFileExist(workspaceUnitDirectory, properties.getWorkspacePRsMetadataFileName()); + } + + /** + * Checks if metadata file of issues type exists in the given workspace unit directory. + * + * @param workspaceUnitDirectory given workspace unit directory. + * @return result if metadata file exists in the given workspace unit directory. + */ + private boolean isIssuesMetadataFileExist(String workspaceUnitDirectory) { + return isMetadataFileExist(workspaceUnitDirectory, properties.getWorkspaceIssuesMetadataFileName()); + } + + /** + * Checks if metadata file of releases type exists in the given workspace unit directory. + * + * @param workspaceUnitDirectory given workspace unit directory. + * @return result if metadata file exists in the given workspace unit directory. + */ + private boolean isReleasesMetadataFileExist(String workspaceUnitDirectory) { + return isMetadataFileExist(workspaceUnitDirectory, properties.getWorkspaceReleasesMetadataFileName()); + } + + /** + * Retrieves metadata file content of the given type with the help of the given workspace unit directory. + * + * @param workspaceUnitDirectory given workspace unit directory. + * @param type given type of the metadata file. + * @return metadata file entity. + * @throws MetadataFileNotFoundException if the metadata file not found. + */ + public MetadataFileEntity getMetadataFileContent(String workspaceUnitDirectory, String type) + throws MetadataFileNotFoundException { + ObjectMapper mapper = + new ObjectMapper() + .configure(DeserializationFeature.FAIL_ON_NULL_CREATOR_PROPERTIES, true) + .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, true) + .configure(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY, true); + mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL); + ObjectReader reader = mapper.reader().forType(new TypeReference() { + }); + + InputStream variableFile; + try { + variableFile = + new FileInputStream( + Paths.get(workspaceUnitDirectory, properties.getWorkspaceMetadataDirectory(), type) + .toString()); + } catch (FileNotFoundException e) { + throw new MetadataFileNotFoundException(e.getMessage()); + } + + try { + return reader.readValues(variableFile).readAll().getFirst(); + } catch (IOException e) { + logger.fatal(e.getMessage()); + } + + return null; + } + + /** + * Retrieves metadata file content of prs type with the help of the given workspace unit directory. + * + * @param workspaceUnitDirectory given workspace unit directory. + * @return metadata file entity. + * @throws MetadataFileNotFoundException if the metadata variable file not found. + */ + public MetadataFileEntity getPRsMetadataFileContent(String workspaceUnitDirectory) + throws MetadataFileNotFoundException { + return getMetadataFileContent(workspaceUnitDirectory, properties.getWorkspacePRsMetadataFileName()); + } + + /** + * Retrieves metadata file content of issues type with the help of the given workspace unit directory. + * + * @param workspaceUnitDirectory given workspace unit directory. + * @return metadata file entity. + * @throws MetadataFileNotFoundException if the metadata variable file not found. + */ + public MetadataFileEntity getIssuesMetadataFileContent(String workspaceUnitDirectory) + throws MetadataFileNotFoundException { + return getMetadataFileContent(workspaceUnitDirectory, properties.getWorkspaceIssuesMetadataFileName()); + } + + /** + * Retrieves metadata file content of releases type with the help of the given workspace unit directory. + * + * @param workspaceUnitDirectory given workspace unit directory. + * @return metadata file entity. + * @throws MetadataFileNotFoundException if the metadata variable file not found. + */ + public MetadataFileEntity getReleasesMetadataFileContent(String workspaceUnitDirectory) + throws MetadataFileNotFoundException { + return getMetadataFileContent(workspaceUnitDirectory, properties.getWorkspaceReleasesMetadataFileName()); + } +} diff --git a/api-server/src/main/java/com/repoachiever/service/workspace/facade/WorkspaceFacade.java b/api-server/src/main/java/com/repoachiever/service/workspace/facade/WorkspaceFacade.java new file mode 100644 index 0000000..45a17ff --- /dev/null +++ b/api-server/src/main/java/com/repoachiever/service/workspace/facade/WorkspaceFacade.java @@ -0,0 +1,114 @@ +package com.repoachiever.service.workspace.facade; + +import com.repoachiever.entity.common.PropertiesEntity; +import com.repoachiever.model.CredentialsFieldsFull; +import com.repoachiever.model.Provider; +import com.repoachiever.service.workspace.WorkspaceService; +import jakarta.enterprise.context.ApplicationScoped; +import jakarta.inject.Inject; + +import java.io.InputStream; + +/** + * Provides high-level access to workspace operations. + */ +@ApplicationScoped +public class WorkspaceFacade { + @Inject + PropertiesEntity properties; + + @Inject + WorkspaceService workspaceService; + + /** + * Creates unit key with the help of the given readiness check application. + * + * @param provider given provider. + * @param credentialsFields given credentials. + * @return result of the readiness check for the given configuration. + */ + public String createUnitKey(Provider provider, CredentialsFieldsFull credentialsFields) { + return switch (provider) { + case LOCAL -> workspaceService.createUnitKey(String.valueOf(credentialsFields.getInternal().getId())); + case GITHUB -> workspaceService.createUnitKey( + String.valueOf(credentialsFields.getInternal().getId()), credentialsFields.getExternal().getToken()); + }; + } + + /** + * + * @param input + * @param hash + * @param id + * @param provider + * @param credentialsFields + */ + public void addContent( + InputStream input, String hash, String id, Provider provider, CredentialsFieldsFull credentialsFields) { + + } + + /** + * + * @param id + * @param provider + * @param credentialsFields + */ + public void updatePRsMetadataFile( + String id, Provider provider, CredentialsFieldsFull credentialsFields) { + + } + + /** + * + * @param id + * @param provider + * @param credentialsFields + */ + public void updateIssuesMetadataFile( + String id, Provider provider, CredentialsFieldsFull credentialsFields) { + } + + /** + * + * @param id + * @param provider + * @param credentialsFields + */ + public void updateReleasesMetadataFile( + String id, Provider provider, CredentialsFieldsFull credentialsFields) { + } + +// /** +// * Update Kafka host in internal config file. +// * +// * @param provider given provider. +// * @param credentialsFields given credentials. +// * @throws WorkspaceUnitDirectoryNotFoundException if workspace unit directory was not found. +// * @throws InternalConfigNotFoundException if internal config file was not found. +// * @throws InternalConfigWriteFailureException if internal config file cannot be created. +// */ +// public void updateKafkaHost( +// String machineAdWorkspaceUnitDirectoryNotFoundExceptiondress, Provider provider, CredentialsFields credentialsFields) +// throws, +// InternalConfigNotFoundException, +// InternalConfigWriteFailureException { +// String workspaceUnitKey = createUnitKey(provider, credentialsFields); +// +// String workspaceUnitDirectory = workspaceService.getUnitDirectory(workspaceUnitKey); +// +// InternalConfigEntity internalConfig = null; +// +// if (workspaceService.isInternalConfigFileExist(workspaceUnitDirectory)) { +// internalConfig = workspaceService.getInternalConfigFileContent(workspaceUnitDirectory); +// } +// +// if (Objects.isNull(internalConfig)) { +// internalConfig = new InternalConfigEntity(); +// } +// +// internalConfig.getKafka().setHost(machineAddress); +// +// workspaceService.createInternalConfigFile(workspaceUnitDirectory, internalConfig); +// } +} diff --git a/api-server/src/main/openapi/openapi.yml b/api-server/src/main/openapi/openapi.yml new file mode 100644 index 0000000..240916a --- /dev/null +++ b/api-server/src/main/openapi/openapi.yml @@ -0,0 +1,352 @@ +openapi: 3.0.1 +info: + title: OpenAPI document of RepoAchiever API Server + description: RepoAchiever API Server Open API documentation + version: "1.0" + +tags: + - name: ContentResource + description: Contains all endpoints related to operations on processed content. + - name: StateResource + description: Contains all endpoints related to state processing. + - name: InfoResource + description: Contains all endpoints related to general info of API Server. + - name: HealthResource + description: Contains all endpoints related to general API Server health information. + +paths: + /v1/content: + post: + tags: + - ContentResource + requestBody: + required: true + description: Content retrieval application + content: + application/json: + schema: + $ref: "#/components/schemas/ContentRetrievalApplication" + responses: + 204: + description: A list of all available content + content: + application/json: + schema: + $ref: "#/components/schemas/ContentRetrievalResult" + /v1/content/apply: + post: + tags: + - ContentResource + requestBody: + required: true + description: Content configuration application + content: + application/json: + schema: + $ref: "#/components/schemas/ContentApplication" + responses: + 204: + description: Given content configuration was successfully applied + 400: + description: Given content configuration was not applied + /v1/content/withdraw: + delete: + tags: + - ContentResource + requestBody: + required: true + description: Content withdraw application. Does not remove persisted content. + content: + application/json: + schema: + $ref: "#/components/schemas/ContentWithdrawal" + responses: + 204: + description: Given content configuration was successfully withdrawn + 400: + description: Given content configuration was not withdrawn + /v1/content/download: + get: + tags: + - ContentResource + parameters: + - in: query + name: location + schema: + type: string + description: Name of content location to be downloaded + responses: + 200: + description: A content was successfully retrieved + content: + application/octet-stream: + schema: + type: string + format: binary + /v1/content/clean: + post: + tags: + - ContentResource + requestBody: + required: true + description: Content configuration application + content: + application/json: + schema: + $ref: "#/components/schemas/ContentCleanup" + responses: + 201: + description: Content with the given configuration was successfully deleted + 400: + description: Content with the given configuration was not deleted + /v1/state/content: + post: + tags: + - StateResource + requestBody: + required: true + description: Given content state key + content: + application/json: + schema: + $ref: "#/components/schemas/ContentStateApplication" + responses: + 201: + description: Content state hash is retrieved successfully + content: + application/json: + schema: + $ref: "#/components/schemas/ContentStateApplicationResult" + /v1/info/version: + get: + tags: + - InfoResource + responses: + 200: + description: General information about running API Server + content: + application/json: + schema: + $ref: "#/components/schemas/VersionInfoResult" + /v1/info/cluster: + get: + tags: + - InfoResource + responses: + 200: + description: General information about running clusters + content: + application/json: + schema: + $ref: "#/components/schemas/ClusterInfoResult" + /v1/info/telemetry: + get: + tags: + - InfoResource + responses: + 200: + description: A set of Prometheus samples used by Grafana instance + content: + text/plain: + schema: + type: string + /v1/health: + get: + tags: + - HealthResource + responses: + 200: + description: General health information about running API Server + content: + application/json: + schema: + $ref: "#/components/schemas/HealthCheckResult" + /v1/readiness: + post: + tags: + - HealthResource + requestBody: + required: true + description: Check if API Server is ready to serve for the given user + content: + application/json: + schema: + $ref: "#/components/schemas/ReadinessCheckApplication" + responses: + 200: + description: General health information about running API Server + content: + application/json: + schema: + $ref: "#/components/schemas/ReadinessCheckResult" +components: + schemas: + Provider: + type: string + enum: + - git-local + - git-github + CredentialsFieldsFull: + required: + - internal + properties: + internal: + $ref: "#/components/schemas/CredentialsFieldsInternal" + external: + $ref: "#/components/schemas/CredentialsFieldsExternal" + CredentialsFieldsInternal: + properties: + id: + type: integer + CredentialsFieldsExternal: + anyOf: + - $ref: "#/components/schemas/GitGitHubCredentials" + GitGitHubCredentials: + required: + - token + properties: + token: + type: string + ContentRetrievalApplication: + required: + - provider + - credentials + properties: + provider: + $ref: "#/components/schemas/Provider" + credentials: + $ref: "#/components/schemas/CredentialsFieldsFull" + ContentRetrievalResult: + required: + - locations + properties: + locations: + type: array + items: + type: string + ContentApplication: + required: + - locations + - provider + - credentials + properties: + locations: + type: array + items: + type: string + provider: + $ref: "#/components/schemas/Provider" + credentials: + $ref: "#/components/schemas/CredentialsFieldsFull" + ContentWithdrawal: + required: + - credentials + - provider + properties: + provider: + $ref: "#/components/schemas/Provider" + credentials: + $ref: "#/components/schemas/CredentialsFieldsFull" + ContentCleanup: + required: + - credentials + properties: + credentials: + $ref: "#/components/schemas/CredentialsFieldsFull" + ContentStateApplication: + required: + - provider + - credentials + properties: + provider: + $ref: "#/components/schemas/Provider" + credentials: + $ref: "#/components/schemas/CredentialsFieldsFull" + ContentStateApplicationResult: + required: + - hash + properties: + hash: + type: string + VersionInfoResult: + properties: + externalApi: + $ref: "#/components/schemas/VersionExternalApiInfoResult" + VersionExternalApiInfoResult: + required: + - version + - hash + properties: + version: + type: string + hash: + type: string + ClusterInfoResult: + type: array + items: + $ref: "#/components/schemas/ClusterInfoUnit" + ClusterInfoUnit: + required: + - name + properties: + name: + type: string + health: + type: boolean + workers: + type: integer + HealthCheckResult: + required: + - status + - checks + properties: + status: + $ref: "#/components/schemas/HealthCheckStatus" + checks: + type: array + items: + $ref: "#/components/schemas/HealthCheckUnit" + HealthCheckUnit: + required: + - name + - status + properties: + name: + type: string + status: + $ref: "#/components/schemas/HealthCheckStatus" + HealthCheckStatus: + type: string + enum: + - UP + - DOWN + ReadinessCheckApplication: + properties: + test: + type: object + ReadinessCheckResult: + required: + - name + - status + - data + properties: + name: + type: string + status: + $ref: "#/components/schemas/ReadinessCheckStatus" + data: + type: object + ReadinessCheckUnit: + required: + - name + - status + properties: + name: + type: string + status: + $ref: "#/components/schemas/ReadinessCheckStatus" + ReadinessCheckStatus: + type: string + enum: + - UP + - DOWN diff --git a/api-server/src/main/resources/META-INF/services/org.eclipse.microprofile.config.spi.ConfigSource b/api-server/src/main/resources/META-INF/services/org.eclipse.microprofile.config.spi.ConfigSource new file mode 100644 index 0000000..1803d67 --- /dev/null +++ b/api-server/src/main/resources/META-INF/services/org.eclipse.microprofile.config.spi.ConfigSource @@ -0,0 +1 @@ +com.repoachiever.service.integration.properties.git.GitPropertiesConfigService \ No newline at end of file diff --git a/api-server/src/main/resources/application.properties b/api-server/src/main/resources/application.properties new file mode 100644 index 0000000..f8772cd --- /dev/null +++ b/api-server/src/main/resources/application.properties @@ -0,0 +1,155 @@ +# Describes general internal Quarkus configuration. +quarkus.http.cors=false +quarkus.smallrye-health.ui.always-include=true +quarkus.swagger-ui.always-include=true +quarkus.native.builder-image=graalvm + +# Describes database Quarkus configuration. +quarkus.datasource.jdbc.driver=org.sqlite.JDBC +quarkus.datasource.db-kind=other +quarkus.datasource.jdbc.url=jdbc:sqlite:${user.home}/.repoachiever/internal/database/data.db +quarkus.datasource.username=repoachiever_user +quarkus.datasource.password=repoachiever_password + +# Describes LiquiBase Quarkus configuration. +quarkus.liquibase.change-log=liquibase/config.yaml +quarkus.liquibase.migrate-at-start=true + +# Describes external Quarkus clients configuration. +quarkus.rest-client.github.url=https://api.github.com +quarkus.rest-client.small-rye-health-check.url=http://${quarkus.http.host}:${quarkus.http.port} + +# Describes location of RepoAchiever API Server states. +state.location=${user.home}/.repoachiever/internal/state + +# Describes name of RepoAchiever API Server running state. +state.running.name=.running + +# Describes database config table name. +database.tables.config.name=config + +# Describes database content table name. +database.tables.content.name=content + +# Describes database provider table name. +database.tables.provider.name=provider + +# Describes database secrets table name. +database.tables.secret.name=secret + +# Describes database statement close delay duration. +database.statement.close-delay=10000 + +# Describes git configuration properties file. +git.config.location=git.properties + +# Describes location of RepoAchiever executable files. +bin.directory=${user.home}/.repoachiever/bin + +# Describes location of RepoAchiever Cluster executable file. +bin.cluster.location=cluster/cluster.jar + +# Describes location of application configurations. +config.directory=${user.home}/.repoachiever/config + +# Describes name of the application configuration file. +config.name=api-server.yaml + +# Describes location of local workspace. +workspace.directory=${user.home}/.repoachiever/workspace + +# Describes location of content directory. +workspace.content.directory=content + +# Describes max amount of versions for each content repository. +workspace.content.version-amount=5 + +# Describes location of metadata directory. +workspace.metadata.directory=metadata + +# Describes location of pull requests metadata file. +workspace.prs-metadata-file.name=prs.json + +# Describes location of issue metadata file. +workspace.issues-metadata-file.name=issues.json + +# Describes location of releases metadata file. +workspace.releases-metadata-file.name=releases.json + +# Describes RepoAchiever Cluster context environment variable name. +repoachiever-cluster.context.alias=REPOACHIEVER_CLUSTER_CONTEXT + +# Describes RepoAchiever API Server communication provider name. +communication.api-server.name=repoachiever-api-server + +# Describes RepoAchiever Cluster allocation base prefix. +communication.cluster.base=repoachiever-cluster + +# Describes RepoAchiever Cluster startup await frequency duration. +communication.cluster.startup-await-frequency=1000 + +# Describes RepoAchiever Cluster startup timeout duration. +communication.cluster.startup-timeout=10000 + +# Describes RepoAchiever Cluster health check operation frequency duration. +communication.cluster.health-check-frequency=1000 + +# Describes name of the Docker network used to install diagnostics infrastructure. +diagnostics.common.docker.network.name=repoachiever-cluster + +# Describes location of Grafana configuration files. +diagnostics.grafana.config.location=${user.home}/.repoachiever/diagnostics/grafana/config + +# Describes location of Grafana datasources configuration files. +diagnostics.grafana.datasources.location=${user.home}/.repoachiever/diagnostics/grafana/config/datasources + +# Describes name of Grafana configuration template file. +diagnostics.grafana.datasources.template=datasource.tmpl + +# Describes name of Grafana configuration template processing output file. +diagnostics.grafana.datasources.output=datasource.yml + +# Describes location of Grafana dashboards configuration files. +diagnostics.grafana.dashboards.location=${user.home}/.repoachiever/diagnostics/grafana/config/dashboards + +# Describes location of Grafana diagnostics dashboards configuration files. +diagnostics.grafana.dashboards.diagnostics.template=diagnostics.tmpl + +# Describes location of Grafana diagnostics dashboards configuration files. +diagnostics.grafana.dashboards.diagnostics.output=diagnostics.json + +# Describes location of Grafana internal files. +diagnostics.grafana.internal.location=${user.home}/.repoachiever/diagnostics/grafana/internal + +# Describes name of the Docker container used for Grafana instance deployment. +diagnostics.grafana.docker.name=repoachiever-diagnostics-grafana + +# Describes image name of the Docker container used for Grafana instance deployment. +diagnostics.grafana.docker.image=grafana/grafana + +# Describes location of Prometheus configuration files. +diagnostics.prometheus.config.location=${user.home}/.repoachiever/diagnostics/prometheus/config + +# Describes name of Prometheus configuration template file. +diagnostics.prometheus.config.template=prometheus.tmpl + +# Describes name of Prometheus configuration template processing output file. +diagnostics.prometheus.config.output=prometheus.yml + +# Describes location of Prometheus internal files. +diagnostics.prometheus.internal.location=${user.home}/.repoachiever/diagnostics/prometheus/internal + +# Describes name of the Docker container used for Prometheus instance deployment. +diagnostics.prometheus.docker.name=repoachiever-diagnostics-prometheus + +# Describes image name of the Docker container used for Prometheus instance deployment. +diagnostics.prometheus.docker.image=prom/prometheus:v2.36.2 + +# Describes name of the Docker container used for Prometheus Node Exporter instance deployment. +diagnostics.prometheus.node-exporter.docker.name=repoachiever-diagnostics-prometheus-node-exporter + +# Describes image name of the Docker container used for Prometheus Node Exporter instance deployment. +diagnostics.prometheus.node-exporter.docker.image=quay.io/prometheus/node-exporter:latest + +# Describes connection timeout used by metrics service. +diagnostics.metrics.connection.timeout=3000 \ No newline at end of file diff --git a/api-server/src/main/resources/liquibase/config.yaml b/api-server/src/main/resources/liquibase/config.yaml new file mode 100644 index 0000000..ef09d45 --- /dev/null +++ b/api-server/src/main/resources/liquibase/config.yaml @@ -0,0 +1,104 @@ +databaseChangeLog: + - changeSet: + id: 1 + author: YarikRevich + changes: + - createTable: + tableName: config + columns: + - column: + name: id + type: INT + autoIncrement: true + constraints: + primaryKey: true + nullable: false + - column: + name: name + type: VARCHAR + constraints: + unique: true + nullable: false + - column: + name: hash + type: VARCHAR + constraints: + nullable: false + - createTable: + tableName: secret + columns: + - column: + name: id + type: INT + autoIncrement: true + constraints: + primaryKey: true + nullable: false + - column: + name: session + type: INT + constraints: + nullable: false + - column: + name: credentials + type: VARCHAR + constraints: + nullable: true + - createTable: + tableName: provider + columns: + - column: + name: id + type: INT + autoIncrement: true + constraints: + primaryKey: true + nullable: false + - column: + name: name + type: VARCHAR + constraints: + unique: true + nullable: false + - createTable: + tableName: content + columns: + - column: + name: id + type: INT + autoIncrement: true + constraints: + primaryKey: true + nullable: false + - column: + name: location + type: VARCHAR + constraints: + nullable: false + - column: + name: provider + type: INT + constraints: + foreignKeyName: provider_fk + references: provider(id) + nullable: false + - column: + name: secret + type: INT + constraints: + foreignKeyName: secret_fk + references: secret(id) + nullable: false + - loadData: + tableName: provider + usePreparedStatements: false + separator: ; + relativeToChangelogFile: true + file: data/data.csv + encoding: UTF-8 + quotchar: '''' + columns: + - column: + header: Name + name: name + type: STRING \ No newline at end of file diff --git a/api-server/src/main/resources/liquibase/data/data.csv b/api-server/src/main/resources/liquibase/data/data.csv new file mode 100644 index 0000000..5b0462b --- /dev/null +++ b/api-server/src/main/resources/liquibase/data/data.csv @@ -0,0 +1,3 @@ +Name +git-local +git-github \ No newline at end of file diff --git a/api-server/src/main/resources/log4j2.xml b/api-server/src/main/resources/log4j2.xml new file mode 100644 index 0000000..e47463a --- /dev/null +++ b/api-server/src/main/resources/log4j2.xml @@ -0,0 +1,60 @@ + + + + + + + + + + + + + %d %p %c{1.} [%t] %m%n + + + + + + + + %d %p %c{1.} [%t] %m%n + + + + + + + + %d %p %c{1.} [%t] %m%n + + + + + + + + %d %p %c{1.} [%t] %m%n + + + + + + + + %d %p %c{1.} [%t] %m%n + + + + + + + + + + + + + + + diff --git a/api-server/target/classes/META-INF/org/apache/logging/log4j/core/config/plugins/Log4j2Plugins.dat b/api-server/target/classes/META-INF/org/apache/logging/log4j/core/config/plugins/Log4j2Plugins.dat new file mode 100644 index 0000000..1a80445 Binary files /dev/null and b/api-server/target/classes/META-INF/org/apache/logging/log4j/core/config/plugins/Log4j2Plugins.dat differ diff --git a/api-server/target/classes/META-INF/panache-archive.marker b/api-server/target/classes/META-INF/panache-archive.marker new file mode 100644 index 0000000..eb166da --- /dev/null +++ b/api-server/target/classes/META-INF/panache-archive.marker @@ -0,0 +1 @@ +This file is a marker, it exists to tell Quarkus that this archive has a dependency on Panache, and may need to be transformed at build time \ No newline at end of file diff --git a/api-server/target/classes/META-INF/services/org.eclipse.microprofile.config.spi.ConfigSource b/api-server/target/classes/META-INF/services/org.eclipse.microprofile.config.spi.ConfigSource new file mode 100644 index 0000000..1803d67 --- /dev/null +++ b/api-server/target/classes/META-INF/services/org.eclipse.microprofile.config.spi.ConfigSource @@ -0,0 +1 @@ +com.repoachiever.service.integration.properties.git.GitPropertiesConfigService \ No newline at end of file diff --git a/api-server/target/classes/application.properties b/api-server/target/classes/application.properties new file mode 100644 index 0000000..f8772cd --- /dev/null +++ b/api-server/target/classes/application.properties @@ -0,0 +1,155 @@ +# Describes general internal Quarkus configuration. +quarkus.http.cors=false +quarkus.smallrye-health.ui.always-include=true +quarkus.swagger-ui.always-include=true +quarkus.native.builder-image=graalvm + +# Describes database Quarkus configuration. +quarkus.datasource.jdbc.driver=org.sqlite.JDBC +quarkus.datasource.db-kind=other +quarkus.datasource.jdbc.url=jdbc:sqlite:${user.home}/.repoachiever/internal/database/data.db +quarkus.datasource.username=repoachiever_user +quarkus.datasource.password=repoachiever_password + +# Describes LiquiBase Quarkus configuration. +quarkus.liquibase.change-log=liquibase/config.yaml +quarkus.liquibase.migrate-at-start=true + +# Describes external Quarkus clients configuration. +quarkus.rest-client.github.url=https://api.github.com +quarkus.rest-client.small-rye-health-check.url=http://${quarkus.http.host}:${quarkus.http.port} + +# Describes location of RepoAchiever API Server states. +state.location=${user.home}/.repoachiever/internal/state + +# Describes name of RepoAchiever API Server running state. +state.running.name=.running + +# Describes database config table name. +database.tables.config.name=config + +# Describes database content table name. +database.tables.content.name=content + +# Describes database provider table name. +database.tables.provider.name=provider + +# Describes database secrets table name. +database.tables.secret.name=secret + +# Describes database statement close delay duration. +database.statement.close-delay=10000 + +# Describes git configuration properties file. +git.config.location=git.properties + +# Describes location of RepoAchiever executable files. +bin.directory=${user.home}/.repoachiever/bin + +# Describes location of RepoAchiever Cluster executable file. +bin.cluster.location=cluster/cluster.jar + +# Describes location of application configurations. +config.directory=${user.home}/.repoachiever/config + +# Describes name of the application configuration file. +config.name=api-server.yaml + +# Describes location of local workspace. +workspace.directory=${user.home}/.repoachiever/workspace + +# Describes location of content directory. +workspace.content.directory=content + +# Describes max amount of versions for each content repository. +workspace.content.version-amount=5 + +# Describes location of metadata directory. +workspace.metadata.directory=metadata + +# Describes location of pull requests metadata file. +workspace.prs-metadata-file.name=prs.json + +# Describes location of issue metadata file. +workspace.issues-metadata-file.name=issues.json + +# Describes location of releases metadata file. +workspace.releases-metadata-file.name=releases.json + +# Describes RepoAchiever Cluster context environment variable name. +repoachiever-cluster.context.alias=REPOACHIEVER_CLUSTER_CONTEXT + +# Describes RepoAchiever API Server communication provider name. +communication.api-server.name=repoachiever-api-server + +# Describes RepoAchiever Cluster allocation base prefix. +communication.cluster.base=repoachiever-cluster + +# Describes RepoAchiever Cluster startup await frequency duration. +communication.cluster.startup-await-frequency=1000 + +# Describes RepoAchiever Cluster startup timeout duration. +communication.cluster.startup-timeout=10000 + +# Describes RepoAchiever Cluster health check operation frequency duration. +communication.cluster.health-check-frequency=1000 + +# Describes name of the Docker network used to install diagnostics infrastructure. +diagnostics.common.docker.network.name=repoachiever-cluster + +# Describes location of Grafana configuration files. +diagnostics.grafana.config.location=${user.home}/.repoachiever/diagnostics/grafana/config + +# Describes location of Grafana datasources configuration files. +diagnostics.grafana.datasources.location=${user.home}/.repoachiever/diagnostics/grafana/config/datasources + +# Describes name of Grafana configuration template file. +diagnostics.grafana.datasources.template=datasource.tmpl + +# Describes name of Grafana configuration template processing output file. +diagnostics.grafana.datasources.output=datasource.yml + +# Describes location of Grafana dashboards configuration files. +diagnostics.grafana.dashboards.location=${user.home}/.repoachiever/diagnostics/grafana/config/dashboards + +# Describes location of Grafana diagnostics dashboards configuration files. +diagnostics.grafana.dashboards.diagnostics.template=diagnostics.tmpl + +# Describes location of Grafana diagnostics dashboards configuration files. +diagnostics.grafana.dashboards.diagnostics.output=diagnostics.json + +# Describes location of Grafana internal files. +diagnostics.grafana.internal.location=${user.home}/.repoachiever/diagnostics/grafana/internal + +# Describes name of the Docker container used for Grafana instance deployment. +diagnostics.grafana.docker.name=repoachiever-diagnostics-grafana + +# Describes image name of the Docker container used for Grafana instance deployment. +diagnostics.grafana.docker.image=grafana/grafana + +# Describes location of Prometheus configuration files. +diagnostics.prometheus.config.location=${user.home}/.repoachiever/diagnostics/prometheus/config + +# Describes name of Prometheus configuration template file. +diagnostics.prometheus.config.template=prometheus.tmpl + +# Describes name of Prometheus configuration template processing output file. +diagnostics.prometheus.config.output=prometheus.yml + +# Describes location of Prometheus internal files. +diagnostics.prometheus.internal.location=${user.home}/.repoachiever/diagnostics/prometheus/internal + +# Describes name of the Docker container used for Prometheus instance deployment. +diagnostics.prometheus.docker.name=repoachiever-diagnostics-prometheus + +# Describes image name of the Docker container used for Prometheus instance deployment. +diagnostics.prometheus.docker.image=prom/prometheus:v2.36.2 + +# Describes name of the Docker container used for Prometheus Node Exporter instance deployment. +diagnostics.prometheus.node-exporter.docker.name=repoachiever-diagnostics-prometheus-node-exporter + +# Describes image name of the Docker container used for Prometheus Node Exporter instance deployment. +diagnostics.prometheus.node-exporter.docker.image=quay.io/prometheus/node-exporter:latest + +# Describes connection timeout used by metrics service. +diagnostics.metrics.connection.timeout=3000 \ No newline at end of file diff --git a/api-server/target/classes/git.properties b/api-server/target/classes/git.properties new file mode 100644 index 0000000..8625b3b --- /dev/null +++ b/api-server/target/classes/git.properties @@ -0,0 +1,22 @@ +#Generated by Git-Commit-Id-Plugin +#Sun May 12 16:25:41 CEST 2024 +git.branch=feature/base +git.build.host=Yaroslavs-MacBook-Pro.local +git.build.time=05/12/2024 16\:25\:41 +0200 +git.build.user.email=yariksvitlitskiy81@gmail.com +git.build.user.name=Yaroslav Svitlytskyi +git.build.version=1.0-SNAPSHOT +git.closest.tag.commit.count= +git.closest.tag.name= +git.commit.id=7af25884b7fd03964465f3e8e205c3aff2bff78b +git.commit.id.abbrev=7af25884 +git.commit.id.describe=7af2588-dirty +git.commit.id.describe-short=7af2588-dirty +git.commit.message.full=fix\: fixed bugs +git.commit.message.short=fix\: fixed bugs +git.commit.time=05/12/2024 15\:00\:09 +0200 +git.commit.user.email=yariksvitlitskiy81@gmail.com +git.commit.user.name=Yaroslav Svitlytskyi +git.dirty=true +git.remote.origin.url=git@github.com\:YarikRevich/RepoArchiever.git +git.tags= diff --git a/api-server/target/classes/liquibase/config.yaml b/api-server/target/classes/liquibase/config.yaml new file mode 100644 index 0000000..ef09d45 --- /dev/null +++ b/api-server/target/classes/liquibase/config.yaml @@ -0,0 +1,104 @@ +databaseChangeLog: + - changeSet: + id: 1 + author: YarikRevich + changes: + - createTable: + tableName: config + columns: + - column: + name: id + type: INT + autoIncrement: true + constraints: + primaryKey: true + nullable: false + - column: + name: name + type: VARCHAR + constraints: + unique: true + nullable: false + - column: + name: hash + type: VARCHAR + constraints: + nullable: false + - createTable: + tableName: secret + columns: + - column: + name: id + type: INT + autoIncrement: true + constraints: + primaryKey: true + nullable: false + - column: + name: session + type: INT + constraints: + nullable: false + - column: + name: credentials + type: VARCHAR + constraints: + nullable: true + - createTable: + tableName: provider + columns: + - column: + name: id + type: INT + autoIncrement: true + constraints: + primaryKey: true + nullable: false + - column: + name: name + type: VARCHAR + constraints: + unique: true + nullable: false + - createTable: + tableName: content + columns: + - column: + name: id + type: INT + autoIncrement: true + constraints: + primaryKey: true + nullable: false + - column: + name: location + type: VARCHAR + constraints: + nullable: false + - column: + name: provider + type: INT + constraints: + foreignKeyName: provider_fk + references: provider(id) + nullable: false + - column: + name: secret + type: INT + constraints: + foreignKeyName: secret_fk + references: secret(id) + nullable: false + - loadData: + tableName: provider + usePreparedStatements: false + separator: ; + relativeToChangelogFile: true + file: data/data.csv + encoding: UTF-8 + quotchar: '''' + columns: + - column: + header: Name + name: name + type: STRING \ No newline at end of file diff --git a/api-server/target/classes/liquibase/data/data.csv b/api-server/target/classes/liquibase/data/data.csv new file mode 100644 index 0000000..5b0462b --- /dev/null +++ b/api-server/target/classes/liquibase/data/data.csv @@ -0,0 +1,3 @@ +Name +git-local +git-github \ No newline at end of file diff --git a/api-server/target/classes/log4j2.xml b/api-server/target/classes/log4j2.xml new file mode 100644 index 0000000..e47463a --- /dev/null +++ b/api-server/target/classes/log4j2.xml @@ -0,0 +1,60 @@ + + + + + + + + + + + + + %d %p %c{1.} [%t] %m%n + + + + + + + + %d %p %c{1.} [%t] %m%n + + + + + + + + %d %p %c{1.} [%t] %m%n + + + + + + + + %d %p %c{1.} [%t] %m%n + + + + + + + + %d %p %c{1.} [%t] %m%n + + + + + + + + + + + + + + + diff --git a/api-server/target/failsafe-reports/failsafe-summary.xml b/api-server/target/failsafe-reports/failsafe-summary.xml new file mode 100644 index 0000000..fffbf43 --- /dev/null +++ b/api-server/target/failsafe-reports/failsafe-summary.xml @@ -0,0 +1,8 @@ + + + 0 + 0 + 0 + 0 + + \ No newline at end of file diff --git a/api-server/target/generated-sources/openapi/.dockerignore b/api-server/target/generated-sources/openapi/.dockerignore new file mode 100644 index 0000000..b86c7ac --- /dev/null +++ b/api-server/target/generated-sources/openapi/.dockerignore @@ -0,0 +1,4 @@ +* +!target/*-runner +!target/*-runner.jar +!target/lib/* \ No newline at end of file diff --git a/api-server/target/generated-sources/openapi/.openapi-generator-ignore b/api-server/target/generated-sources/openapi/.openapi-generator-ignore new file mode 100644 index 0000000..7484ee5 --- /dev/null +++ b/api-server/target/generated-sources/openapi/.openapi-generator-ignore @@ -0,0 +1,23 @@ +# OpenAPI Generator Ignore +# Generated by openapi-generator https://github.com/openapitools/openapi-generator + +# Use this file to prevent files from being overwritten by the generator. +# The patterns follow closely to .gitignore or .dockerignore. + +# As an example, the C# client generator defines ApiClient.cs. +# You can make changes and tell OpenAPI Generator to ignore just this file by uncommenting the following line: +#ApiClient.cs + +# You can match any string of characters against a directory, file or extension with a single asterisk (*): +#foo/*/qux +# The above matches foo/bar/qux and foo/baz/qux, but not foo/bar/baz/qux + +# You can recursively match patterns against a directory, file or extension with a double asterisk (**): +#foo/**/qux +# This matches foo/bar/qux, foo/baz/qux, and foo/bar/baz/qux + +# You can also negate patterns with an exclamation (!). +# For example, you can ignore all files in a docs folder with the file extension .md: +#docs/*.md +# Then explicitly reverse the ignore rule for a single file: +#!docs/README.md diff --git a/api-server/target/generated-sources/openapi/.openapi-generator/FILES b/api-server/target/generated-sources/openapi/.openapi-generator/FILES new file mode 100644 index 0000000..ced31dc --- /dev/null +++ b/api-server/target/generated-sources/openapi/.openapi-generator/FILES @@ -0,0 +1,35 @@ +.dockerignore +.openapi-generator-ignore +README.md +pom.xml +src/main/docker/Dockerfile.jvm +src/main/docker/Dockerfile.native +src/main/java/com/repoachiever/RestResourceRoot.java +src/main/java/com/repoachiever/api/ContentResourceApi.java +src/main/java/com/repoachiever/api/HealthResourceApi.java +src/main/java/com/repoachiever/api/InfoResourceApi.java +src/main/java/com/repoachiever/api/StateResourceApi.java +src/main/java/com/repoachiever/model/ClusterInfoUnit.java +src/main/java/com/repoachiever/model/ContentApplication.java +src/main/java/com/repoachiever/model/ContentCleanup.java +src/main/java/com/repoachiever/model/ContentRetrievalApplication.java +src/main/java/com/repoachiever/model/ContentRetrievalResult.java +src/main/java/com/repoachiever/model/ContentStateApplication.java +src/main/java/com/repoachiever/model/ContentStateApplicationResult.java +src/main/java/com/repoachiever/model/ContentWithdrawal.java +src/main/java/com/repoachiever/model/CredentialsFieldsExternal.java +src/main/java/com/repoachiever/model/CredentialsFieldsFull.java +src/main/java/com/repoachiever/model/CredentialsFieldsInternal.java +src/main/java/com/repoachiever/model/GitGitHubCredentials.java +src/main/java/com/repoachiever/model/HealthCheckResult.java +src/main/java/com/repoachiever/model/HealthCheckStatus.java +src/main/java/com/repoachiever/model/HealthCheckUnit.java +src/main/java/com/repoachiever/model/Provider.java +src/main/java/com/repoachiever/model/ReadinessCheckApplication.java +src/main/java/com/repoachiever/model/ReadinessCheckResult.java +src/main/java/com/repoachiever/model/ReadinessCheckStatus.java +src/main/java/com/repoachiever/model/ReadinessCheckUnit.java +src/main/java/com/repoachiever/model/VersionExternalApiInfoResult.java +src/main/java/com/repoachiever/model/VersionInfoResult.java +src/main/resources/META-INF/openapi.yaml +src/main/resources/application.properties diff --git a/api-server/target/generated-sources/openapi/.openapi-generator/VERSION b/api-server/target/generated-sources/openapi/.openapi-generator/VERSION new file mode 100644 index 0000000..c0be8a7 --- /dev/null +++ b/api-server/target/generated-sources/openapi/.openapi-generator/VERSION @@ -0,0 +1 @@ +6.4.0 \ No newline at end of file diff --git a/api-server/target/generated-sources/openapi/.openapi-generator/openapi.yml-default.sha256 b/api-server/target/generated-sources/openapi/.openapi-generator/openapi.yml-default.sha256 new file mode 100644 index 0000000..229ea46 --- /dev/null +++ b/api-server/target/generated-sources/openapi/.openapi-generator/openapi.yml-default.sha256 @@ -0,0 +1 @@ +23dd5ef7368f378589d86135d0c9e3602c82eeb0dd992c1c2efe429c7811b94e \ No newline at end of file diff --git a/api-server/target/generated-sources/openapi/README.md b/api-server/target/generated-sources/openapi/README.md new file mode 100644 index 0000000..578b4eb --- /dev/null +++ b/api-server/target/generated-sources/openapi/README.md @@ -0,0 +1,15 @@ +# JAX-RS server with OpenAPI using Quarkus + +## Overview +This server was generated by the [OpenAPI Generator](https://openapi-generator.tech) project. By using an +[OpenAPI-Spec](https://openapis.org), you can easily generate a server stub. + +This is an example of building a OpenAPI-enabled JAX-RS server. +This example uses the [JAX-RS](https://jax-rs-spec.java.net/) framework and +the [Eclipse-MicroProfile-OpenAPI](https://github.com/eclipse/microprofile-open-api) addition. + +The pom file is configured to use [Quarkus](https://quarkus.io/) as application server. + +This project produces a jar that defines some interfaces. +The jar can be used in combination with another project providing the implementation. + diff --git a/api-server/target/generated-sources/openapi/pom.xml b/api-server/target/generated-sources/openapi/pom.xml new file mode 100644 index 0000000..b1e2d58 --- /dev/null +++ b/api-server/target/generated-sources/openapi/pom.xml @@ -0,0 +1,137 @@ + + + 4.0.0 + org.openapitools + openapi-jaxrs-client + openapi-jaxrs-client + 1.0 + + + + 3.8.1 + true + 1.8 + 1.8 + UTF-8 + UTF-8 + 1.1.1.Final + quarkus-universe-bom + io.quarkus + 1.1.1.Final + 2.22.1 + + + + + ${quarkus.platform.group-id} + ${quarkus.platform.artifact-id} + ${quarkus.platform.version} + pom + import + + + + + + io.quarkus + quarkus-resteasy + + + io.quarkus + quarkus-junit5 + test + + + io.rest-assured + rest-assured + test + + + io.quarkus + quarkus-smallrye-openapi + + + + + + org.codehaus.mojo + build-helper-maven-plugin + 1.9.1 + + + add-source + generate-sources + + add-source + + + + src/gen/java + + + + + + + io.quarkus + quarkus-maven-plugin + ${quarkus-plugin.version} + + + + build + + + + + + maven-compiler-plugin + ${compiler-plugin.version} + + + maven-surefire-plugin + ${surefire-plugin.version} + + + org.jboss.logmanager.LogManager + + + + + + + + native + + + native + + + + + + maven-failsafe-plugin + ${surefire-plugin.version} + + + + integration-test + verify + + + + ${project.build.directory}/${project.build.finalName}-runner + + + + + + + + + native + + + + diff --git a/api-server/target/generated-sources/openapi/src/main/docker/Dockerfile.jvm b/api-server/target/generated-sources/openapi/src/main/docker/Dockerfile.jvm new file mode 100644 index 0000000..87730c9 --- /dev/null +++ b/api-server/target/generated-sources/openapi/src/main/docker/Dockerfile.jvm @@ -0,0 +1,34 @@ +#### +# This Dockerfile is used in order to build a container that runs the Quarkus application in JVM mode +# +# Before building the docker image run: +# +# mvn package +# +# Then, build the image with: +# +# docker build -f src/main/docker/Dockerfile.jvm -t quarkus/openapi-jaxrs-client-jvm . +# +# Then run the container using: +# +# docker run -i --rm -p 8080:8080 quarkus/openapi-jaxrs-client-jvm +# +### +FROM fabric8/java-alpine-openjdk8-jre:1.6.5 +ENV JAVA_OPTIONS="-Dquarkus.http.host=0.0.0.0 -Djava.util.logging.manager=org.jboss.logmanager.LogManager" +ENV AB_ENABLED=jmx_exporter + +# Be prepared for running in OpenShift too +RUN adduser -G root --no-create-home --disabled-password 1001 \ + && chown -R 1001 /deployments \ + && chmod -R "g+rwX" /deployments \ + && chown -R 1001:root /deployments + +COPY target/lib/* /deployments/lib/ +COPY target/*-runner.jar /deployments/app.jar +EXPOSE 8080 + +# run with user 1001 +USER 1001 + +ENTRYPOINT [ "/deployments/run-java.sh" ] \ No newline at end of file diff --git a/api-server/target/generated-sources/openapi/src/main/docker/Dockerfile.native b/api-server/target/generated-sources/openapi/src/main/docker/Dockerfile.native new file mode 100644 index 0000000..1a46ca0 --- /dev/null +++ b/api-server/target/generated-sources/openapi/src/main/docker/Dockerfile.native @@ -0,0 +1,22 @@ +#### +# This Dockerfile is used in order to build a container that runs the Quarkus application in native (no JVM) mode +# +# Before building the docker image run: +# +# mvn package -Pnative -Dquarkus.native.container-build=true +# +# Then, build the image with: +# +# docker build -f src/main/docker/Dockerfile.native -t quarkus/openapi-jaxrs-client . +# +# Then run the container using: +# +# docker run -i --rm -p 8080:8080 quarkus/openapi-jaxrs-client +# +### +FROM registry.access.redhat.com/ubi8/ubi-minimal +WORKDIR /work/ +COPY target/*-runner /work/application +RUN chmod 775 /work +EXPOSE 8080 +CMD ["./application", "-Dquarkus.http.host=0.0.0.0"] \ No newline at end of file diff --git a/api-server/target/generated-sources/openapi/src/main/java/com/repoachiever/RestResourceRoot.java b/api-server/target/generated-sources/openapi/src/main/java/com/repoachiever/RestResourceRoot.java new file mode 100644 index 0000000..d213a41 --- /dev/null +++ b/api-server/target/generated-sources/openapi/src/main/java/com/repoachiever/RestResourceRoot.java @@ -0,0 +1,5 @@ +package com.repoachiever; + +public class RestResourceRoot { + public static final String APPLICATION_PATH = ""; +} diff --git a/api-server/target/generated-sources/openapi/src/main/java/com/repoachiever/api/ContentResourceApi.java b/api-server/target/generated-sources/openapi/src/main/java/com/repoachiever/api/ContentResourceApi.java new file mode 100644 index 0000000..d67cf06 --- /dev/null +++ b/api-server/target/generated-sources/openapi/src/main/java/com/repoachiever/api/ContentResourceApi.java @@ -0,0 +1,49 @@ +package com.repoachiever.api; + +import com.repoachiever.model.ContentApplication; +import com.repoachiever.model.ContentCleanup; +import com.repoachiever.model.ContentRetrievalApplication; +import com.repoachiever.model.ContentRetrievalResult; +import com.repoachiever.model.ContentWithdrawal; +import java.io.File; + +import jakarta.ws.rs.*; +import jakarta.ws.rs.core.Response; + + + +import java.io.InputStream; +import java.util.Map; +import java.util.List; +import jakarta.validation.constraints.*; +import jakarta.validation.Valid; + +@Path("/v1/content") +@jakarta.annotation.Generated(value = "org.openapitools.codegen.languages.JavaJAXRSSpecServerCodegen", date = "2024-05-12T16:25:41.890330+02:00[Europe/Warsaw]") +public interface ContentResourceApi { + + @POST + @Path("/apply") + @Consumes({ "application/json" }) + void v1ContentApplyPost(@Valid @NotNull ContentApplication contentApplication); + + @POST + @Path("/clean") + @Consumes({ "application/json" }) + void v1ContentCleanPost(@Valid @NotNull ContentCleanup contentCleanup); + + @GET + @Path("/download") + @Produces({ "application/octet-stream" }) + File v1ContentDownloadGet(@QueryParam("location") String location); + + @POST + @Consumes({ "application/json" }) + @Produces({ "application/json" }) + ContentRetrievalResult v1ContentPost(@Valid @NotNull ContentRetrievalApplication contentRetrievalApplication); + + @DELETE + @Path("/withdraw") + @Consumes({ "application/json" }) + void v1ContentWithdrawDelete(@Valid @NotNull ContentWithdrawal contentWithdrawal); +} diff --git a/api-server/target/generated-sources/openapi/src/main/java/com/repoachiever/api/HealthResourceApi.java b/api-server/target/generated-sources/openapi/src/main/java/com/repoachiever/api/HealthResourceApi.java new file mode 100644 index 0000000..803db5f --- /dev/null +++ b/api-server/target/generated-sources/openapi/src/main/java/com/repoachiever/api/HealthResourceApi.java @@ -0,0 +1,32 @@ +package com.repoachiever.api; + +import com.repoachiever.model.HealthCheckResult; +import com.repoachiever.model.ReadinessCheckApplication; +import com.repoachiever.model.ReadinessCheckResult; + +import jakarta.ws.rs.*; +import jakarta.ws.rs.core.Response; + + + +import java.io.InputStream; +import java.util.Map; +import java.util.List; +import jakarta.validation.constraints.*; +import jakarta.validation.Valid; + +@Path("/v1") +@jakarta.annotation.Generated(value = "org.openapitools.codegen.languages.JavaJAXRSSpecServerCodegen", date = "2024-05-12T16:25:41.890330+02:00[Europe/Warsaw]") +public interface HealthResourceApi { + + @GET + @Path("/health") + @Produces({ "application/json" }) + HealthCheckResult v1HealthGet(); + + @POST + @Path("/readiness") + @Consumes({ "application/json" }) + @Produces({ "application/json" }) + ReadinessCheckResult v1ReadinessPost(@Valid @NotNull ReadinessCheckApplication readinessCheckApplication); +} diff --git a/api-server/target/generated-sources/openapi/src/main/java/com/repoachiever/api/InfoResourceApi.java b/api-server/target/generated-sources/openapi/src/main/java/com/repoachiever/api/InfoResourceApi.java new file mode 100644 index 0000000..14be2b6 --- /dev/null +++ b/api-server/target/generated-sources/openapi/src/main/java/com/repoachiever/api/InfoResourceApi.java @@ -0,0 +1,35 @@ +package com.repoachiever.api; + +import com.repoachiever.model.ClusterInfoUnit; +import com.repoachiever.model.VersionInfoResult; + +import jakarta.ws.rs.*; +import jakarta.ws.rs.core.Response; + + + +import java.io.InputStream; +import java.util.Map; +import java.util.List; +import jakarta.validation.constraints.*; +import jakarta.validation.Valid; + +@Path("/v1/info") +@jakarta.annotation.Generated(value = "org.openapitools.codegen.languages.JavaJAXRSSpecServerCodegen", date = "2024-05-12T16:25:41.890330+02:00[Europe/Warsaw]") +public interface InfoResourceApi { + + @GET + @Path("/cluster") + @Produces({ "application/json" }) + List v1InfoClusterGet(); + + @GET + @Path("/telemetry") + @Produces({ "text/plain" }) + String v1InfoTelemetryGet(); + + @GET + @Path("/version") + @Produces({ "application/json" }) + VersionInfoResult v1InfoVersionGet(); +} diff --git a/api-server/target/generated-sources/openapi/src/main/java/com/repoachiever/api/StateResourceApi.java b/api-server/target/generated-sources/openapi/src/main/java/com/repoachiever/api/StateResourceApi.java new file mode 100644 index 0000000..ed43a92 --- /dev/null +++ b/api-server/target/generated-sources/openapi/src/main/java/com/repoachiever/api/StateResourceApi.java @@ -0,0 +1,25 @@ +package com.repoachiever.api; + +import com.repoachiever.model.ContentStateApplication; +import com.repoachiever.model.ContentStateApplicationResult; + +import jakarta.ws.rs.*; +import jakarta.ws.rs.core.Response; + + + +import java.io.InputStream; +import java.util.Map; +import java.util.List; +import jakarta.validation.constraints.*; +import jakarta.validation.Valid; + +@Path("/v1/state/content") +@jakarta.annotation.Generated(value = "org.openapitools.codegen.languages.JavaJAXRSSpecServerCodegen", date = "2024-05-12T16:25:41.890330+02:00[Europe/Warsaw]") +public interface StateResourceApi { + + @POST + @Consumes({ "application/json" }) + @Produces({ "application/json" }) + ContentStateApplicationResult v1StateContentPost(@Valid @NotNull ContentStateApplication contentStateApplication); +} diff --git a/api-server/target/generated-sources/openapi/src/main/java/com/repoachiever/model/ClusterInfoUnit.java b/api-server/target/generated-sources/openapi/src/main/java/com/repoachiever/model/ClusterInfoUnit.java new file mode 100644 index 0000000..da62edc --- /dev/null +++ b/api-server/target/generated-sources/openapi/src/main/java/com/repoachiever/model/ClusterInfoUnit.java @@ -0,0 +1,123 @@ +package com.repoachiever.model; + +import java.io.Serializable; +import jakarta.validation.constraints.*; +import jakarta.validation.Valid; + +import java.util.Objects; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonValue; +import com.fasterxml.jackson.annotation.JsonTypeName; + + + +@JsonTypeName("ClusterInfoUnit") +@jakarta.annotation.Generated(value = "org.openapitools.codegen.languages.JavaJAXRSSpecServerCodegen", date = "2024-05-12T16:25:41.890330+02:00[Europe/Warsaw]")@lombok.Data @lombok.NoArgsConstructor @lombok.AllArgsConstructor(staticName = "of") + +public class ClusterInfoUnit implements Serializable { + private @Valid String name; + private @Valid Boolean health; + private @Valid Integer workers; + + /** + **/ + public ClusterInfoUnit name(String name) { + this.name = name; + return this; + } + + + @JsonProperty("name") + @NotNull + public String getName() { + return name; + } + + @JsonProperty("name") + public void setName(String name) { + this.name = name; + } + + /** + **/ + public ClusterInfoUnit health(Boolean health) { + this.health = health; + return this; + } + + + @JsonProperty("health") + public Boolean getHealth() { + return health; + } + + @JsonProperty("health") + public void setHealth(Boolean health) { + this.health = health; + } + + /** + **/ + public ClusterInfoUnit workers(Integer workers) { + this.workers = workers; + return this; + } + + + @JsonProperty("workers") + public Integer getWorkers() { + return workers; + } + + @JsonProperty("workers") + public void setWorkers(Integer workers) { + this.workers = workers; + } + + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + ClusterInfoUnit clusterInfoUnit = (ClusterInfoUnit) o; + return Objects.equals(this.name, clusterInfoUnit.name) && + Objects.equals(this.health, clusterInfoUnit.health) && + Objects.equals(this.workers, clusterInfoUnit.workers); + } + + @Override + public int hashCode() { + return Objects.hash(name, health, workers); + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append("class ClusterInfoUnit {\n"); + + sb.append(" name: ").append(toIndentedString(name)).append("\n"); + sb.append(" health: ").append(toIndentedString(health)).append("\n"); + sb.append(" workers: ").append(toIndentedString(workers)).append("\n"); + sb.append("}"); + return sb.toString(); + } + + /** + * Convert the given object to string with each line indented by 4 spaces + * (except the first line). + */ + private String toIndentedString(Object o) { + if (o == null) { + return "null"; + } + return o.toString().replace("\n", "\n "); + } + + +} + diff --git a/api-server/target/generated-sources/openapi/src/main/java/com/repoachiever/model/ContentApplication.java b/api-server/target/generated-sources/openapi/src/main/java/com/repoachiever/model/ContentApplication.java new file mode 100644 index 0000000..dc4b33e --- /dev/null +++ b/api-server/target/generated-sources/openapi/src/main/java/com/repoachiever/model/ContentApplication.java @@ -0,0 +1,145 @@ +package com.repoachiever.model; + +import com.repoachiever.model.CredentialsFieldsFull; +import com.repoachiever.model.Provider; +import java.util.ArrayList; +import java.util.List; +import java.io.Serializable; +import jakarta.validation.constraints.*; +import jakarta.validation.Valid; + +import java.util.Objects; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonValue; +import com.fasterxml.jackson.annotation.JsonTypeName; + + + +@JsonTypeName("ContentApplication") +@jakarta.annotation.Generated(value = "org.openapitools.codegen.languages.JavaJAXRSSpecServerCodegen", date = "2024-05-12T16:25:41.890330+02:00[Europe/Warsaw]")@lombok.Data @lombok.NoArgsConstructor @lombok.AllArgsConstructor(staticName = "of") + +public class ContentApplication implements Serializable { + private @Valid List locations = new ArrayList<>(); + private @Valid Provider provider; + private @Valid CredentialsFieldsFull credentials; + + /** + **/ + public ContentApplication locations(List locations) { + this.locations = locations; + return this; + } + + + @JsonProperty("locations") + @NotNull + public List getLocations() { + return locations; + } + + @JsonProperty("locations") + public void setLocations(List locations) { + this.locations = locations; + } + + public ContentApplication addLocationsItem(String locationsItem) { + if (this.locations == null) { + this.locations = new ArrayList<>(); + } + + this.locations.add(locationsItem); + return this; + } + + public ContentApplication removeLocationsItem(String locationsItem) { + if (locationsItem != null && this.locations != null) { + this.locations.remove(locationsItem); + } + + return this; + } + /** + **/ + public ContentApplication provider(Provider provider) { + this.provider = provider; + return this; + } + + + @JsonProperty("provider") + @NotNull + public Provider getProvider() { + return provider; + } + + @JsonProperty("provider") + public void setProvider(Provider provider) { + this.provider = provider; + } + + /** + **/ + public ContentApplication credentials(CredentialsFieldsFull credentials) { + this.credentials = credentials; + return this; + } + + + @JsonProperty("credentials") + @NotNull + public CredentialsFieldsFull getCredentials() { + return credentials; + } + + @JsonProperty("credentials") + public void setCredentials(CredentialsFieldsFull credentials) { + this.credentials = credentials; + } + + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + ContentApplication contentApplication = (ContentApplication) o; + return Objects.equals(this.locations, contentApplication.locations) && + Objects.equals(this.provider, contentApplication.provider) && + Objects.equals(this.credentials, contentApplication.credentials); + } + + @Override + public int hashCode() { + return Objects.hash(locations, provider, credentials); + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append("class ContentApplication {\n"); + + sb.append(" locations: ").append(toIndentedString(locations)).append("\n"); + sb.append(" provider: ").append(toIndentedString(provider)).append("\n"); + sb.append(" credentials: ").append(toIndentedString(credentials)).append("\n"); + sb.append("}"); + return sb.toString(); + } + + /** + * Convert the given object to string with each line indented by 4 spaces + * (except the first line). + */ + private String toIndentedString(Object o) { + if (o == null) { + return "null"; + } + return o.toString().replace("\n", "\n "); + } + + +} + diff --git a/api-server/target/generated-sources/openapi/src/main/java/com/repoachiever/model/ContentCleanup.java b/api-server/target/generated-sources/openapi/src/main/java/com/repoachiever/model/ContentCleanup.java new file mode 100644 index 0000000..2970568 --- /dev/null +++ b/api-server/target/generated-sources/openapi/src/main/java/com/repoachiever/model/ContentCleanup.java @@ -0,0 +1,82 @@ +package com.repoachiever.model; + +import com.repoachiever.model.CredentialsFieldsFull; +import java.io.Serializable; +import jakarta.validation.constraints.*; +import jakarta.validation.Valid; + +import java.util.Objects; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonValue; +import com.fasterxml.jackson.annotation.JsonTypeName; + + + +@JsonTypeName("ContentCleanup") +@jakarta.annotation.Generated(value = "org.openapitools.codegen.languages.JavaJAXRSSpecServerCodegen", date = "2024-05-12T16:25:41.890330+02:00[Europe/Warsaw]")@lombok.Data @lombok.NoArgsConstructor @lombok.AllArgsConstructor(staticName = "of") + +public class ContentCleanup implements Serializable { + private @Valid CredentialsFieldsFull credentials; + + /** + **/ + public ContentCleanup credentials(CredentialsFieldsFull credentials) { + this.credentials = credentials; + return this; + } + + + @JsonProperty("credentials") + @NotNull + public CredentialsFieldsFull getCredentials() { + return credentials; + } + + @JsonProperty("credentials") + public void setCredentials(CredentialsFieldsFull credentials) { + this.credentials = credentials; + } + + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + ContentCleanup contentCleanup = (ContentCleanup) o; + return Objects.equals(this.credentials, contentCleanup.credentials); + } + + @Override + public int hashCode() { + return Objects.hash(credentials); + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append("class ContentCleanup {\n"); + + sb.append(" credentials: ").append(toIndentedString(credentials)).append("\n"); + sb.append("}"); + return sb.toString(); + } + + /** + * Convert the given object to string with each line indented by 4 spaces + * (except the first line). + */ + private String toIndentedString(Object o) { + if (o == null) { + return "null"; + } + return o.toString().replace("\n", "\n "); + } + + +} + diff --git a/api-server/target/generated-sources/openapi/src/main/java/com/repoachiever/model/ContentRetrievalApplication.java b/api-server/target/generated-sources/openapi/src/main/java/com/repoachiever/model/ContentRetrievalApplication.java new file mode 100644 index 0000000..1c1dcfa --- /dev/null +++ b/api-server/target/generated-sources/openapi/src/main/java/com/repoachiever/model/ContentRetrievalApplication.java @@ -0,0 +1,105 @@ +package com.repoachiever.model; + +import com.repoachiever.model.CredentialsFieldsFull; +import com.repoachiever.model.Provider; +import java.io.Serializable; +import jakarta.validation.constraints.*; +import jakarta.validation.Valid; + +import java.util.Objects; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonValue; +import com.fasterxml.jackson.annotation.JsonTypeName; + + + +@JsonTypeName("ContentRetrievalApplication") +@jakarta.annotation.Generated(value = "org.openapitools.codegen.languages.JavaJAXRSSpecServerCodegen", date = "2024-05-12T16:25:41.890330+02:00[Europe/Warsaw]")@lombok.Data @lombok.NoArgsConstructor @lombok.AllArgsConstructor(staticName = "of") + +public class ContentRetrievalApplication implements Serializable { + private @Valid Provider provider; + private @Valid CredentialsFieldsFull credentials; + + /** + **/ + public ContentRetrievalApplication provider(Provider provider) { + this.provider = provider; + return this; + } + + + @JsonProperty("provider") + @NotNull + public Provider getProvider() { + return provider; + } + + @JsonProperty("provider") + public void setProvider(Provider provider) { + this.provider = provider; + } + + /** + **/ + public ContentRetrievalApplication credentials(CredentialsFieldsFull credentials) { + this.credentials = credentials; + return this; + } + + + @JsonProperty("credentials") + @NotNull + public CredentialsFieldsFull getCredentials() { + return credentials; + } + + @JsonProperty("credentials") + public void setCredentials(CredentialsFieldsFull credentials) { + this.credentials = credentials; + } + + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + ContentRetrievalApplication contentRetrievalApplication = (ContentRetrievalApplication) o; + return Objects.equals(this.provider, contentRetrievalApplication.provider) && + Objects.equals(this.credentials, contentRetrievalApplication.credentials); + } + + @Override + public int hashCode() { + return Objects.hash(provider, credentials); + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append("class ContentRetrievalApplication {\n"); + + sb.append(" provider: ").append(toIndentedString(provider)).append("\n"); + sb.append(" credentials: ").append(toIndentedString(credentials)).append("\n"); + sb.append("}"); + return sb.toString(); + } + + /** + * Convert the given object to string with each line indented by 4 spaces + * (except the first line). + */ + private String toIndentedString(Object o) { + if (o == null) { + return "null"; + } + return o.toString().replace("\n", "\n "); + } + + +} + diff --git a/api-server/target/generated-sources/openapi/src/main/java/com/repoachiever/model/ContentRetrievalResult.java b/api-server/target/generated-sources/openapi/src/main/java/com/repoachiever/model/ContentRetrievalResult.java new file mode 100644 index 0000000..17cf3d5 --- /dev/null +++ b/api-server/target/generated-sources/openapi/src/main/java/com/repoachiever/model/ContentRetrievalResult.java @@ -0,0 +1,99 @@ +package com.repoachiever.model; + +import java.util.ArrayList; +import java.util.List; +import java.io.Serializable; +import jakarta.validation.constraints.*; +import jakarta.validation.Valid; + +import java.util.Objects; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonValue; +import com.fasterxml.jackson.annotation.JsonTypeName; + + + +@JsonTypeName("ContentRetrievalResult") +@jakarta.annotation.Generated(value = "org.openapitools.codegen.languages.JavaJAXRSSpecServerCodegen", date = "2024-05-12T16:25:41.890330+02:00[Europe/Warsaw]")@lombok.Data @lombok.NoArgsConstructor @lombok.AllArgsConstructor(staticName = "of") + +public class ContentRetrievalResult implements Serializable { + private @Valid List locations = new ArrayList<>(); + + /** + **/ + public ContentRetrievalResult locations(List locations) { + this.locations = locations; + return this; + } + + + @JsonProperty("locations") + @NotNull + public List getLocations() { + return locations; + } + + @JsonProperty("locations") + public void setLocations(List locations) { + this.locations = locations; + } + + public ContentRetrievalResult addLocationsItem(String locationsItem) { + if (this.locations == null) { + this.locations = new ArrayList<>(); + } + + this.locations.add(locationsItem); + return this; + } + + public ContentRetrievalResult removeLocationsItem(String locationsItem) { + if (locationsItem != null && this.locations != null) { + this.locations.remove(locationsItem); + } + + return this; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + ContentRetrievalResult contentRetrievalResult = (ContentRetrievalResult) o; + return Objects.equals(this.locations, contentRetrievalResult.locations); + } + + @Override + public int hashCode() { + return Objects.hash(locations); + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append("class ContentRetrievalResult {\n"); + + sb.append(" locations: ").append(toIndentedString(locations)).append("\n"); + sb.append("}"); + return sb.toString(); + } + + /** + * Convert the given object to string with each line indented by 4 spaces + * (except the first line). + */ + private String toIndentedString(Object o) { + if (o == null) { + return "null"; + } + return o.toString().replace("\n", "\n "); + } + + +} + diff --git a/api-server/target/generated-sources/openapi/src/main/java/com/repoachiever/model/ContentStateApplication.java b/api-server/target/generated-sources/openapi/src/main/java/com/repoachiever/model/ContentStateApplication.java new file mode 100644 index 0000000..5e5cbc0 --- /dev/null +++ b/api-server/target/generated-sources/openapi/src/main/java/com/repoachiever/model/ContentStateApplication.java @@ -0,0 +1,105 @@ +package com.repoachiever.model; + +import com.repoachiever.model.CredentialsFieldsFull; +import com.repoachiever.model.Provider; +import java.io.Serializable; +import jakarta.validation.constraints.*; +import jakarta.validation.Valid; + +import java.util.Objects; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonValue; +import com.fasterxml.jackson.annotation.JsonTypeName; + + + +@JsonTypeName("ContentStateApplication") +@jakarta.annotation.Generated(value = "org.openapitools.codegen.languages.JavaJAXRSSpecServerCodegen", date = "2024-05-12T16:25:41.890330+02:00[Europe/Warsaw]")@lombok.Data @lombok.NoArgsConstructor @lombok.AllArgsConstructor(staticName = "of") + +public class ContentStateApplication implements Serializable { + private @Valid Provider provider; + private @Valid CredentialsFieldsFull credentials; + + /** + **/ + public ContentStateApplication provider(Provider provider) { + this.provider = provider; + return this; + } + + + @JsonProperty("provider") + @NotNull + public Provider getProvider() { + return provider; + } + + @JsonProperty("provider") + public void setProvider(Provider provider) { + this.provider = provider; + } + + /** + **/ + public ContentStateApplication credentials(CredentialsFieldsFull credentials) { + this.credentials = credentials; + return this; + } + + + @JsonProperty("credentials") + @NotNull + public CredentialsFieldsFull getCredentials() { + return credentials; + } + + @JsonProperty("credentials") + public void setCredentials(CredentialsFieldsFull credentials) { + this.credentials = credentials; + } + + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + ContentStateApplication contentStateApplication = (ContentStateApplication) o; + return Objects.equals(this.provider, contentStateApplication.provider) && + Objects.equals(this.credentials, contentStateApplication.credentials); + } + + @Override + public int hashCode() { + return Objects.hash(provider, credentials); + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append("class ContentStateApplication {\n"); + + sb.append(" provider: ").append(toIndentedString(provider)).append("\n"); + sb.append(" credentials: ").append(toIndentedString(credentials)).append("\n"); + sb.append("}"); + return sb.toString(); + } + + /** + * Convert the given object to string with each line indented by 4 spaces + * (except the first line). + */ + private String toIndentedString(Object o) { + if (o == null) { + return "null"; + } + return o.toString().replace("\n", "\n "); + } + + +} + diff --git a/api-server/target/generated-sources/openapi/src/main/java/com/repoachiever/model/ContentStateApplicationResult.java b/api-server/target/generated-sources/openapi/src/main/java/com/repoachiever/model/ContentStateApplicationResult.java new file mode 100644 index 0000000..94f9f7d --- /dev/null +++ b/api-server/target/generated-sources/openapi/src/main/java/com/repoachiever/model/ContentStateApplicationResult.java @@ -0,0 +1,81 @@ +package com.repoachiever.model; + +import java.io.Serializable; +import jakarta.validation.constraints.*; +import jakarta.validation.Valid; + +import java.util.Objects; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonValue; +import com.fasterxml.jackson.annotation.JsonTypeName; + + + +@JsonTypeName("ContentStateApplicationResult") +@jakarta.annotation.Generated(value = "org.openapitools.codegen.languages.JavaJAXRSSpecServerCodegen", date = "2024-05-12T16:25:41.890330+02:00[Europe/Warsaw]")@lombok.Data @lombok.NoArgsConstructor @lombok.AllArgsConstructor(staticName = "of") + +public class ContentStateApplicationResult implements Serializable { + private @Valid String hash; + + /** + **/ + public ContentStateApplicationResult hash(String hash) { + this.hash = hash; + return this; + } + + + @JsonProperty("hash") + @NotNull + public String getHash() { + return hash; + } + + @JsonProperty("hash") + public void setHash(String hash) { + this.hash = hash; + } + + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + ContentStateApplicationResult contentStateApplicationResult = (ContentStateApplicationResult) o; + return Objects.equals(this.hash, contentStateApplicationResult.hash); + } + + @Override + public int hashCode() { + return Objects.hash(hash); + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append("class ContentStateApplicationResult {\n"); + + sb.append(" hash: ").append(toIndentedString(hash)).append("\n"); + sb.append("}"); + return sb.toString(); + } + + /** + * Convert the given object to string with each line indented by 4 spaces + * (except the first line). + */ + private String toIndentedString(Object o) { + if (o == null) { + return "null"; + } + return o.toString().replace("\n", "\n "); + } + + +} + diff --git a/api-server/target/generated-sources/openapi/src/main/java/com/repoachiever/model/ContentWithdrawal.java b/api-server/target/generated-sources/openapi/src/main/java/com/repoachiever/model/ContentWithdrawal.java new file mode 100644 index 0000000..af1c0a7 --- /dev/null +++ b/api-server/target/generated-sources/openapi/src/main/java/com/repoachiever/model/ContentWithdrawal.java @@ -0,0 +1,105 @@ +package com.repoachiever.model; + +import com.repoachiever.model.CredentialsFieldsFull; +import com.repoachiever.model.Provider; +import java.io.Serializable; +import jakarta.validation.constraints.*; +import jakarta.validation.Valid; + +import java.util.Objects; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonValue; +import com.fasterxml.jackson.annotation.JsonTypeName; + + + +@JsonTypeName("ContentWithdrawal") +@jakarta.annotation.Generated(value = "org.openapitools.codegen.languages.JavaJAXRSSpecServerCodegen", date = "2024-05-12T16:25:41.890330+02:00[Europe/Warsaw]")@lombok.Data @lombok.NoArgsConstructor @lombok.AllArgsConstructor(staticName = "of") + +public class ContentWithdrawal implements Serializable { + private @Valid Provider provider; + private @Valid CredentialsFieldsFull credentials; + + /** + **/ + public ContentWithdrawal provider(Provider provider) { + this.provider = provider; + return this; + } + + + @JsonProperty("provider") + @NotNull + public Provider getProvider() { + return provider; + } + + @JsonProperty("provider") + public void setProvider(Provider provider) { + this.provider = provider; + } + + /** + **/ + public ContentWithdrawal credentials(CredentialsFieldsFull credentials) { + this.credentials = credentials; + return this; + } + + + @JsonProperty("credentials") + @NotNull + public CredentialsFieldsFull getCredentials() { + return credentials; + } + + @JsonProperty("credentials") + public void setCredentials(CredentialsFieldsFull credentials) { + this.credentials = credentials; + } + + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + ContentWithdrawal contentWithdrawal = (ContentWithdrawal) o; + return Objects.equals(this.provider, contentWithdrawal.provider) && + Objects.equals(this.credentials, contentWithdrawal.credentials); + } + + @Override + public int hashCode() { + return Objects.hash(provider, credentials); + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append("class ContentWithdrawal {\n"); + + sb.append(" provider: ").append(toIndentedString(provider)).append("\n"); + sb.append(" credentials: ").append(toIndentedString(credentials)).append("\n"); + sb.append("}"); + return sb.toString(); + } + + /** + * Convert the given object to string with each line indented by 4 spaces + * (except the first line). + */ + private String toIndentedString(Object o) { + if (o == null) { + return "null"; + } + return o.toString().replace("\n", "\n "); + } + + +} + diff --git a/api-server/target/generated-sources/openapi/src/main/java/com/repoachiever/model/CredentialsFieldsExternal.java b/api-server/target/generated-sources/openapi/src/main/java/com/repoachiever/model/CredentialsFieldsExternal.java new file mode 100644 index 0000000..45dbb53 --- /dev/null +++ b/api-server/target/generated-sources/openapi/src/main/java/com/repoachiever/model/CredentialsFieldsExternal.java @@ -0,0 +1,82 @@ +package com.repoachiever.model; + +import com.repoachiever.model.GitGitHubCredentials; +import java.io.Serializable; +import jakarta.validation.constraints.*; +import jakarta.validation.Valid; + +import java.util.Objects; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonValue; +import com.fasterxml.jackson.annotation.JsonTypeName; + + + +@JsonTypeName("CredentialsFieldsExternal") +@jakarta.annotation.Generated(value = "org.openapitools.codegen.languages.JavaJAXRSSpecServerCodegen", date = "2024-05-12T16:25:41.890330+02:00[Europe/Warsaw]")@lombok.Data @lombok.NoArgsConstructor @lombok.AllArgsConstructor(staticName = "of") + +public class CredentialsFieldsExternal implements Serializable { + private @Valid String token; + + /** + **/ + public CredentialsFieldsExternal token(String token) { + this.token = token; + return this; + } + + + @JsonProperty("token") + @NotNull + public String getToken() { + return token; + } + + @JsonProperty("token") + public void setToken(String token) { + this.token = token; + } + + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + CredentialsFieldsExternal credentialsFieldsExternal = (CredentialsFieldsExternal) o; + return Objects.equals(this.token, credentialsFieldsExternal.token); + } + + @Override + public int hashCode() { + return Objects.hash(token); + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append("class CredentialsFieldsExternal {\n"); + + sb.append(" token: ").append(toIndentedString(token)).append("\n"); + sb.append("}"); + return sb.toString(); + } + + /** + * Convert the given object to string with each line indented by 4 spaces + * (except the first line). + */ + private String toIndentedString(Object o) { + if (o == null) { + return "null"; + } + return o.toString().replace("\n", "\n "); + } + + +} + diff --git a/api-server/target/generated-sources/openapi/src/main/java/com/repoachiever/model/CredentialsFieldsFull.java b/api-server/target/generated-sources/openapi/src/main/java/com/repoachiever/model/CredentialsFieldsFull.java new file mode 100644 index 0000000..b054b75 --- /dev/null +++ b/api-server/target/generated-sources/openapi/src/main/java/com/repoachiever/model/CredentialsFieldsFull.java @@ -0,0 +1,104 @@ +package com.repoachiever.model; + +import com.repoachiever.model.CredentialsFieldsExternal; +import com.repoachiever.model.CredentialsFieldsInternal; +import java.io.Serializable; +import jakarta.validation.constraints.*; +import jakarta.validation.Valid; + +import java.util.Objects; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonValue; +import com.fasterxml.jackson.annotation.JsonTypeName; + + + +@JsonTypeName("CredentialsFieldsFull") +@jakarta.annotation.Generated(value = "org.openapitools.codegen.languages.JavaJAXRSSpecServerCodegen", date = "2024-05-12T16:25:41.890330+02:00[Europe/Warsaw]")@lombok.Data @lombok.NoArgsConstructor @lombok.AllArgsConstructor(staticName = "of") + +public class CredentialsFieldsFull implements Serializable { + private @Valid CredentialsFieldsInternal internal; + private @Valid CredentialsFieldsExternal external; + + /** + **/ + public CredentialsFieldsFull internal(CredentialsFieldsInternal internal) { + this.internal = internal; + return this; + } + + + @JsonProperty("internal") + @NotNull + public CredentialsFieldsInternal getInternal() { + return internal; + } + + @JsonProperty("internal") + public void setInternal(CredentialsFieldsInternal internal) { + this.internal = internal; + } + + /** + **/ + public CredentialsFieldsFull external(CredentialsFieldsExternal external) { + this.external = external; + return this; + } + + + @JsonProperty("external") + public CredentialsFieldsExternal getExternal() { + return external; + } + + @JsonProperty("external") + public void setExternal(CredentialsFieldsExternal external) { + this.external = external; + } + + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + CredentialsFieldsFull credentialsFieldsFull = (CredentialsFieldsFull) o; + return Objects.equals(this.internal, credentialsFieldsFull.internal) && + Objects.equals(this.external, credentialsFieldsFull.external); + } + + @Override + public int hashCode() { + return Objects.hash(internal, external); + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append("class CredentialsFieldsFull {\n"); + + sb.append(" internal: ").append(toIndentedString(internal)).append("\n"); + sb.append(" external: ").append(toIndentedString(external)).append("\n"); + sb.append("}"); + return sb.toString(); + } + + /** + * Convert the given object to string with each line indented by 4 spaces + * (except the first line). + */ + private String toIndentedString(Object o) { + if (o == null) { + return "null"; + } + return o.toString().replace("\n", "\n "); + } + + +} + diff --git a/api-server/target/generated-sources/openapi/src/main/java/com/repoachiever/model/CredentialsFieldsInternal.java b/api-server/target/generated-sources/openapi/src/main/java/com/repoachiever/model/CredentialsFieldsInternal.java new file mode 100644 index 0000000..d49c4a7 --- /dev/null +++ b/api-server/target/generated-sources/openapi/src/main/java/com/repoachiever/model/CredentialsFieldsInternal.java @@ -0,0 +1,80 @@ +package com.repoachiever.model; + +import java.io.Serializable; +import jakarta.validation.constraints.*; +import jakarta.validation.Valid; + +import java.util.Objects; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonValue; +import com.fasterxml.jackson.annotation.JsonTypeName; + + + +@JsonTypeName("CredentialsFieldsInternal") +@jakarta.annotation.Generated(value = "org.openapitools.codegen.languages.JavaJAXRSSpecServerCodegen", date = "2024-05-12T16:25:41.890330+02:00[Europe/Warsaw]")@lombok.Data @lombok.NoArgsConstructor @lombok.AllArgsConstructor(staticName = "of") + +public class CredentialsFieldsInternal implements Serializable { + private @Valid Integer id; + + /** + **/ + public CredentialsFieldsInternal id(Integer id) { + this.id = id; + return this; + } + + + @JsonProperty("id") + public Integer getId() { + return id; + } + + @JsonProperty("id") + public void setId(Integer id) { + this.id = id; + } + + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + CredentialsFieldsInternal credentialsFieldsInternal = (CredentialsFieldsInternal) o; + return Objects.equals(this.id, credentialsFieldsInternal.id); + } + + @Override + public int hashCode() { + return Objects.hash(id); + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append("class CredentialsFieldsInternal {\n"); + + sb.append(" id: ").append(toIndentedString(id)).append("\n"); + sb.append("}"); + return sb.toString(); + } + + /** + * Convert the given object to string with each line indented by 4 spaces + * (except the first line). + */ + private String toIndentedString(Object o) { + if (o == null) { + return "null"; + } + return o.toString().replace("\n", "\n "); + } + + +} + diff --git a/api-server/target/generated-sources/openapi/src/main/java/com/repoachiever/model/GitGitHubCredentials.java b/api-server/target/generated-sources/openapi/src/main/java/com/repoachiever/model/GitGitHubCredentials.java new file mode 100644 index 0000000..ea5b5d0 --- /dev/null +++ b/api-server/target/generated-sources/openapi/src/main/java/com/repoachiever/model/GitGitHubCredentials.java @@ -0,0 +1,81 @@ +package com.repoachiever.model; + +import java.io.Serializable; +import jakarta.validation.constraints.*; +import jakarta.validation.Valid; + +import java.util.Objects; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonValue; +import com.fasterxml.jackson.annotation.JsonTypeName; + + + +@JsonTypeName("GitGitHubCredentials") +@jakarta.annotation.Generated(value = "org.openapitools.codegen.languages.JavaJAXRSSpecServerCodegen", date = "2024-05-12T16:25:41.890330+02:00[Europe/Warsaw]")@lombok.Data @lombok.NoArgsConstructor @lombok.AllArgsConstructor(staticName = "of") + +public class GitGitHubCredentials implements Serializable { + private @Valid String token; + + /** + **/ + public GitGitHubCredentials token(String token) { + this.token = token; + return this; + } + + + @JsonProperty("token") + @NotNull + public String getToken() { + return token; + } + + @JsonProperty("token") + public void setToken(String token) { + this.token = token; + } + + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + GitGitHubCredentials gitGitHubCredentials = (GitGitHubCredentials) o; + return Objects.equals(this.token, gitGitHubCredentials.token); + } + + @Override + public int hashCode() { + return Objects.hash(token); + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append("class GitGitHubCredentials {\n"); + + sb.append(" token: ").append(toIndentedString(token)).append("\n"); + sb.append("}"); + return sb.toString(); + } + + /** + * Convert the given object to string with each line indented by 4 spaces + * (except the first line). + */ + private String toIndentedString(Object o) { + if (o == null) { + return "null"; + } + return o.toString().replace("\n", "\n "); + } + + +} + diff --git a/api-server/target/generated-sources/openapi/src/main/java/com/repoachiever/model/HealthCheckResult.java b/api-server/target/generated-sources/openapi/src/main/java/com/repoachiever/model/HealthCheckResult.java new file mode 100644 index 0000000..5092b48 --- /dev/null +++ b/api-server/target/generated-sources/openapi/src/main/java/com/repoachiever/model/HealthCheckResult.java @@ -0,0 +1,123 @@ +package com.repoachiever.model; + +import com.repoachiever.model.HealthCheckStatus; +import com.repoachiever.model.HealthCheckUnit; +import java.util.ArrayList; +import java.util.List; +import java.io.Serializable; +import jakarta.validation.constraints.*; +import jakarta.validation.Valid; + +import java.util.Objects; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonValue; +import com.fasterxml.jackson.annotation.JsonTypeName; + + + +@JsonTypeName("HealthCheckResult") +@jakarta.annotation.Generated(value = "org.openapitools.codegen.languages.JavaJAXRSSpecServerCodegen", date = "2024-05-12T16:25:41.890330+02:00[Europe/Warsaw]")@lombok.Data @lombok.NoArgsConstructor @lombok.AllArgsConstructor(staticName = "of") + +public class HealthCheckResult implements Serializable { + private @Valid HealthCheckStatus status; + private @Valid List checks = new ArrayList<>(); + + /** + **/ + public HealthCheckResult status(HealthCheckStatus status) { + this.status = status; + return this; + } + + + @JsonProperty("status") + @NotNull + public HealthCheckStatus getStatus() { + return status; + } + + @JsonProperty("status") + public void setStatus(HealthCheckStatus status) { + this.status = status; + } + + /** + **/ + public HealthCheckResult checks(List checks) { + this.checks = checks; + return this; + } + + + @JsonProperty("checks") + @NotNull + public List getChecks() { + return checks; + } + + @JsonProperty("checks") + public void setChecks(List checks) { + this.checks = checks; + } + + public HealthCheckResult addChecksItem(HealthCheckUnit checksItem) { + if (this.checks == null) { + this.checks = new ArrayList<>(); + } + + this.checks.add(checksItem); + return this; + } + + public HealthCheckResult removeChecksItem(HealthCheckUnit checksItem) { + if (checksItem != null && this.checks != null) { + this.checks.remove(checksItem); + } + + return this; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + HealthCheckResult healthCheckResult = (HealthCheckResult) o; + return Objects.equals(this.status, healthCheckResult.status) && + Objects.equals(this.checks, healthCheckResult.checks); + } + + @Override + public int hashCode() { + return Objects.hash(status, checks); + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append("class HealthCheckResult {\n"); + + sb.append(" status: ").append(toIndentedString(status)).append("\n"); + sb.append(" checks: ").append(toIndentedString(checks)).append("\n"); + sb.append("}"); + return sb.toString(); + } + + /** + * Convert the given object to string with each line indented by 4 spaces + * (except the first line). + */ + private String toIndentedString(Object o) { + if (o == null) { + return "null"; + } + return o.toString().replace("\n", "\n "); + } + + +} + diff --git a/api-server/target/generated-sources/openapi/src/main/java/com/repoachiever/model/HealthCheckStatus.java b/api-server/target/generated-sources/openapi/src/main/java/com/repoachiever/model/HealthCheckStatus.java new file mode 100644 index 0000000..c4f7833 --- /dev/null +++ b/api-server/target/generated-sources/openapi/src/main/java/com/repoachiever/model/HealthCheckStatus.java @@ -0,0 +1,57 @@ +package com.repoachiever.model; + +import java.io.Serializable; +import jakarta.validation.constraints.*; +import jakarta.validation.Valid; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonValue; + +/** + * Gets or Sets HealthCheckStatus + */ +public enum HealthCheckStatus { + + UP("UP"), + + DOWN("DOWN"); + + private String value; + + HealthCheckStatus(String value) { + this.value = value; + } + + /** + * Convert a String into String, as specified in the + * See JAX RS 2.0 Specification, section 3.2, p. 12 + */ + public static HealthCheckStatus fromString(String s) { + for (HealthCheckStatus b : HealthCheckStatus.values()) { + // using Objects.toString() to be safe if value type non-object type + // because types like 'int' etc. will be auto-boxed + if (java.util.Objects.toString(b.value).equals(s)) { + return b; + } + } + throw new IllegalArgumentException("Unexpected string value '" + s + "'"); + } + + @Override + @JsonValue + public String toString() { + return String.valueOf(value); + } + + @JsonCreator + public static HealthCheckStatus fromValue(String value) { + for (HealthCheckStatus b : HealthCheckStatus.values()) { + if (b.value.equals(value)) { + return b; + } + } + throw new IllegalArgumentException("Unexpected value '" + value + "'"); + } +} + + diff --git a/api-server/target/generated-sources/openapi/src/main/java/com/repoachiever/model/HealthCheckUnit.java b/api-server/target/generated-sources/openapi/src/main/java/com/repoachiever/model/HealthCheckUnit.java new file mode 100644 index 0000000..0188c87 --- /dev/null +++ b/api-server/target/generated-sources/openapi/src/main/java/com/repoachiever/model/HealthCheckUnit.java @@ -0,0 +1,104 @@ +package com.repoachiever.model; + +import com.repoachiever.model.HealthCheckStatus; +import java.io.Serializable; +import jakarta.validation.constraints.*; +import jakarta.validation.Valid; + +import java.util.Objects; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonValue; +import com.fasterxml.jackson.annotation.JsonTypeName; + + + +@JsonTypeName("HealthCheckUnit") +@jakarta.annotation.Generated(value = "org.openapitools.codegen.languages.JavaJAXRSSpecServerCodegen", date = "2024-05-12T16:25:41.890330+02:00[Europe/Warsaw]")@lombok.Data @lombok.NoArgsConstructor @lombok.AllArgsConstructor(staticName = "of") + +public class HealthCheckUnit implements Serializable { + private @Valid String name; + private @Valid HealthCheckStatus status; + + /** + **/ + public HealthCheckUnit name(String name) { + this.name = name; + return this; + } + + + @JsonProperty("name") + @NotNull + public String getName() { + return name; + } + + @JsonProperty("name") + public void setName(String name) { + this.name = name; + } + + /** + **/ + public HealthCheckUnit status(HealthCheckStatus status) { + this.status = status; + return this; + } + + + @JsonProperty("status") + @NotNull + public HealthCheckStatus getStatus() { + return status; + } + + @JsonProperty("status") + public void setStatus(HealthCheckStatus status) { + this.status = status; + } + + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + HealthCheckUnit healthCheckUnit = (HealthCheckUnit) o; + return Objects.equals(this.name, healthCheckUnit.name) && + Objects.equals(this.status, healthCheckUnit.status); + } + + @Override + public int hashCode() { + return Objects.hash(name, status); + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append("class HealthCheckUnit {\n"); + + sb.append(" name: ").append(toIndentedString(name)).append("\n"); + sb.append(" status: ").append(toIndentedString(status)).append("\n"); + sb.append("}"); + return sb.toString(); + } + + /** + * Convert the given object to string with each line indented by 4 spaces + * (except the first line). + */ + private String toIndentedString(Object o) { + if (o == null) { + return "null"; + } + return o.toString().replace("\n", "\n "); + } + + +} + diff --git a/api-server/target/generated-sources/openapi/src/main/java/com/repoachiever/model/Provider.java b/api-server/target/generated-sources/openapi/src/main/java/com/repoachiever/model/Provider.java new file mode 100644 index 0000000..4f6b4a0 --- /dev/null +++ b/api-server/target/generated-sources/openapi/src/main/java/com/repoachiever/model/Provider.java @@ -0,0 +1,57 @@ +package com.repoachiever.model; + +import java.io.Serializable; +import jakarta.validation.constraints.*; +import jakarta.validation.Valid; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonValue; + +/** + * Gets or Sets Provider + */ +public enum Provider { + + LOCAL("git-local"), + + GITHUB("git-github"); + + private String value; + + Provider(String value) { + this.value = value; + } + + /** + * Convert a String into String, as specified in the + * See JAX RS 2.0 Specification, section 3.2, p. 12 + */ + public static Provider fromString(String s) { + for (Provider b : Provider.values()) { + // using Objects.toString() to be safe if value type non-object type + // because types like 'int' etc. will be auto-boxed + if (java.util.Objects.toString(b.value).equals(s)) { + return b; + } + } + throw new IllegalArgumentException("Unexpected string value '" + s + "'"); + } + + @Override + @JsonValue + public String toString() { + return String.valueOf(value); + } + + @JsonCreator + public static Provider fromValue(String value) { + for (Provider b : Provider.values()) { + if (b.value.equals(value)) { + return b; + } + } + throw new IllegalArgumentException("Unexpected value '" + value + "'"); + } +} + + diff --git a/api-server/target/generated-sources/openapi/src/main/java/com/repoachiever/model/ReadinessCheckApplication.java b/api-server/target/generated-sources/openapi/src/main/java/com/repoachiever/model/ReadinessCheckApplication.java new file mode 100644 index 0000000..6fd2623 --- /dev/null +++ b/api-server/target/generated-sources/openapi/src/main/java/com/repoachiever/model/ReadinessCheckApplication.java @@ -0,0 +1,80 @@ +package com.repoachiever.model; + +import java.io.Serializable; +import jakarta.validation.constraints.*; +import jakarta.validation.Valid; + +import java.util.Objects; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonValue; +import com.fasterxml.jackson.annotation.JsonTypeName; + + + +@JsonTypeName("ReadinessCheckApplication") +@jakarta.annotation.Generated(value = "org.openapitools.codegen.languages.JavaJAXRSSpecServerCodegen", date = "2024-05-12T16:25:41.890330+02:00[Europe/Warsaw]")@lombok.Data @lombok.NoArgsConstructor @lombok.AllArgsConstructor(staticName = "of") + +public class ReadinessCheckApplication implements Serializable { + private @Valid Object test; + + /** + **/ + public ReadinessCheckApplication test(Object test) { + this.test = test; + return this; + } + + + @JsonProperty("test") + public Object getTest() { + return test; + } + + @JsonProperty("test") + public void setTest(Object test) { + this.test = test; + } + + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + ReadinessCheckApplication readinessCheckApplication = (ReadinessCheckApplication) o; + return Objects.equals(this.test, readinessCheckApplication.test); + } + + @Override + public int hashCode() { + return Objects.hash(test); + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append("class ReadinessCheckApplication {\n"); + + sb.append(" test: ").append(toIndentedString(test)).append("\n"); + sb.append("}"); + return sb.toString(); + } + + /** + * Convert the given object to string with each line indented by 4 spaces + * (except the first line). + */ + private String toIndentedString(Object o) { + if (o == null) { + return "null"; + } + return o.toString().replace("\n", "\n "); + } + + +} + diff --git a/api-server/target/generated-sources/openapi/src/main/java/com/repoachiever/model/ReadinessCheckResult.java b/api-server/target/generated-sources/openapi/src/main/java/com/repoachiever/model/ReadinessCheckResult.java new file mode 100644 index 0000000..4f35056 --- /dev/null +++ b/api-server/target/generated-sources/openapi/src/main/java/com/repoachiever/model/ReadinessCheckResult.java @@ -0,0 +1,126 @@ +package com.repoachiever.model; + +import com.repoachiever.model.ReadinessCheckStatus; +import java.io.Serializable; +import jakarta.validation.constraints.*; +import jakarta.validation.Valid; + +import java.util.Objects; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonValue; +import com.fasterxml.jackson.annotation.JsonTypeName; + + + +@JsonTypeName("ReadinessCheckResult") +@jakarta.annotation.Generated(value = "org.openapitools.codegen.languages.JavaJAXRSSpecServerCodegen", date = "2024-05-12T16:25:41.890330+02:00[Europe/Warsaw]")@lombok.Data @lombok.NoArgsConstructor @lombok.AllArgsConstructor(staticName = "of") + +public class ReadinessCheckResult implements Serializable { + private @Valid String name; + private @Valid ReadinessCheckStatus status; + private @Valid Object data; + + /** + **/ + public ReadinessCheckResult name(String name) { + this.name = name; + return this; + } + + + @JsonProperty("name") + @NotNull + public String getName() { + return name; + } + + @JsonProperty("name") + public void setName(String name) { + this.name = name; + } + + /** + **/ + public ReadinessCheckResult status(ReadinessCheckStatus status) { + this.status = status; + return this; + } + + + @JsonProperty("status") + @NotNull + public ReadinessCheckStatus getStatus() { + return status; + } + + @JsonProperty("status") + public void setStatus(ReadinessCheckStatus status) { + this.status = status; + } + + /** + **/ + public ReadinessCheckResult data(Object data) { + this.data = data; + return this; + } + + + @JsonProperty("data") + @NotNull + public Object getData() { + return data; + } + + @JsonProperty("data") + public void setData(Object data) { + this.data = data; + } + + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + ReadinessCheckResult readinessCheckResult = (ReadinessCheckResult) o; + return Objects.equals(this.name, readinessCheckResult.name) && + Objects.equals(this.status, readinessCheckResult.status) && + Objects.equals(this.data, readinessCheckResult.data); + } + + @Override + public int hashCode() { + return Objects.hash(name, status, data); + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append("class ReadinessCheckResult {\n"); + + sb.append(" name: ").append(toIndentedString(name)).append("\n"); + sb.append(" status: ").append(toIndentedString(status)).append("\n"); + sb.append(" data: ").append(toIndentedString(data)).append("\n"); + sb.append("}"); + return sb.toString(); + } + + /** + * Convert the given object to string with each line indented by 4 spaces + * (except the first line). + */ + private String toIndentedString(Object o) { + if (o == null) { + return "null"; + } + return o.toString().replace("\n", "\n "); + } + + +} + diff --git a/api-server/target/generated-sources/openapi/src/main/java/com/repoachiever/model/ReadinessCheckStatus.java b/api-server/target/generated-sources/openapi/src/main/java/com/repoachiever/model/ReadinessCheckStatus.java new file mode 100644 index 0000000..7770414 --- /dev/null +++ b/api-server/target/generated-sources/openapi/src/main/java/com/repoachiever/model/ReadinessCheckStatus.java @@ -0,0 +1,57 @@ +package com.repoachiever.model; + +import java.io.Serializable; +import jakarta.validation.constraints.*; +import jakarta.validation.Valid; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonValue; + +/** + * Gets or Sets ReadinessCheckStatus + */ +public enum ReadinessCheckStatus { + + UP("UP"), + + DOWN("DOWN"); + + private String value; + + ReadinessCheckStatus(String value) { + this.value = value; + } + + /** + * Convert a String into String, as specified in the + * See JAX RS 2.0 Specification, section 3.2, p. 12 + */ + public static ReadinessCheckStatus fromString(String s) { + for (ReadinessCheckStatus b : ReadinessCheckStatus.values()) { + // using Objects.toString() to be safe if value type non-object type + // because types like 'int' etc. will be auto-boxed + if (java.util.Objects.toString(b.value).equals(s)) { + return b; + } + } + throw new IllegalArgumentException("Unexpected string value '" + s + "'"); + } + + @Override + @JsonValue + public String toString() { + return String.valueOf(value); + } + + @JsonCreator + public static ReadinessCheckStatus fromValue(String value) { + for (ReadinessCheckStatus b : ReadinessCheckStatus.values()) { + if (b.value.equals(value)) { + return b; + } + } + throw new IllegalArgumentException("Unexpected value '" + value + "'"); + } +} + + diff --git a/api-server/target/generated-sources/openapi/src/main/java/com/repoachiever/model/ReadinessCheckUnit.java b/api-server/target/generated-sources/openapi/src/main/java/com/repoachiever/model/ReadinessCheckUnit.java new file mode 100644 index 0000000..6c94db2 --- /dev/null +++ b/api-server/target/generated-sources/openapi/src/main/java/com/repoachiever/model/ReadinessCheckUnit.java @@ -0,0 +1,104 @@ +package com.repoachiever.model; + +import com.repoachiever.model.ReadinessCheckStatus; +import java.io.Serializable; +import jakarta.validation.constraints.*; +import jakarta.validation.Valid; + +import java.util.Objects; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonValue; +import com.fasterxml.jackson.annotation.JsonTypeName; + + + +@JsonTypeName("ReadinessCheckUnit") +@jakarta.annotation.Generated(value = "org.openapitools.codegen.languages.JavaJAXRSSpecServerCodegen", date = "2024-05-12T16:25:41.890330+02:00[Europe/Warsaw]")@lombok.Data @lombok.NoArgsConstructor @lombok.AllArgsConstructor(staticName = "of") + +public class ReadinessCheckUnit implements Serializable { + private @Valid String name; + private @Valid ReadinessCheckStatus status; + + /** + **/ + public ReadinessCheckUnit name(String name) { + this.name = name; + return this; + } + + + @JsonProperty("name") + @NotNull + public String getName() { + return name; + } + + @JsonProperty("name") + public void setName(String name) { + this.name = name; + } + + /** + **/ + public ReadinessCheckUnit status(ReadinessCheckStatus status) { + this.status = status; + return this; + } + + + @JsonProperty("status") + @NotNull + public ReadinessCheckStatus getStatus() { + return status; + } + + @JsonProperty("status") + public void setStatus(ReadinessCheckStatus status) { + this.status = status; + } + + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + ReadinessCheckUnit readinessCheckUnit = (ReadinessCheckUnit) o; + return Objects.equals(this.name, readinessCheckUnit.name) && + Objects.equals(this.status, readinessCheckUnit.status); + } + + @Override + public int hashCode() { + return Objects.hash(name, status); + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append("class ReadinessCheckUnit {\n"); + + sb.append(" name: ").append(toIndentedString(name)).append("\n"); + sb.append(" status: ").append(toIndentedString(status)).append("\n"); + sb.append("}"); + return sb.toString(); + } + + /** + * Convert the given object to string with each line indented by 4 spaces + * (except the first line). + */ + private String toIndentedString(Object o) { + if (o == null) { + return "null"; + } + return o.toString().replace("\n", "\n "); + } + + +} + diff --git a/api-server/target/generated-sources/openapi/src/main/java/com/repoachiever/model/VersionExternalApiInfoResult.java b/api-server/target/generated-sources/openapi/src/main/java/com/repoachiever/model/VersionExternalApiInfoResult.java new file mode 100644 index 0000000..428db14 --- /dev/null +++ b/api-server/target/generated-sources/openapi/src/main/java/com/repoachiever/model/VersionExternalApiInfoResult.java @@ -0,0 +1,103 @@ +package com.repoachiever.model; + +import java.io.Serializable; +import jakarta.validation.constraints.*; +import jakarta.validation.Valid; + +import java.util.Objects; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonValue; +import com.fasterxml.jackson.annotation.JsonTypeName; + + + +@JsonTypeName("VersionExternalApiInfoResult") +@jakarta.annotation.Generated(value = "org.openapitools.codegen.languages.JavaJAXRSSpecServerCodegen", date = "2024-05-12T16:25:41.890330+02:00[Europe/Warsaw]")@lombok.Data @lombok.NoArgsConstructor @lombok.AllArgsConstructor(staticName = "of") + +public class VersionExternalApiInfoResult implements Serializable { + private @Valid String version; + private @Valid String hash; + + /** + **/ + public VersionExternalApiInfoResult version(String version) { + this.version = version; + return this; + } + + + @JsonProperty("version") + @NotNull + public String getVersion() { + return version; + } + + @JsonProperty("version") + public void setVersion(String version) { + this.version = version; + } + + /** + **/ + public VersionExternalApiInfoResult hash(String hash) { + this.hash = hash; + return this; + } + + + @JsonProperty("hash") + @NotNull + public String getHash() { + return hash; + } + + @JsonProperty("hash") + public void setHash(String hash) { + this.hash = hash; + } + + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + VersionExternalApiInfoResult versionExternalApiInfoResult = (VersionExternalApiInfoResult) o; + return Objects.equals(this.version, versionExternalApiInfoResult.version) && + Objects.equals(this.hash, versionExternalApiInfoResult.hash); + } + + @Override + public int hashCode() { + return Objects.hash(version, hash); + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append("class VersionExternalApiInfoResult {\n"); + + sb.append(" version: ").append(toIndentedString(version)).append("\n"); + sb.append(" hash: ").append(toIndentedString(hash)).append("\n"); + sb.append("}"); + return sb.toString(); + } + + /** + * Convert the given object to string with each line indented by 4 spaces + * (except the first line). + */ + private String toIndentedString(Object o) { + if (o == null) { + return "null"; + } + return o.toString().replace("\n", "\n "); + } + + +} + diff --git a/api-server/target/generated-sources/openapi/src/main/java/com/repoachiever/model/VersionInfoResult.java b/api-server/target/generated-sources/openapi/src/main/java/com/repoachiever/model/VersionInfoResult.java new file mode 100644 index 0000000..ce5829e --- /dev/null +++ b/api-server/target/generated-sources/openapi/src/main/java/com/repoachiever/model/VersionInfoResult.java @@ -0,0 +1,81 @@ +package com.repoachiever.model; + +import com.repoachiever.model.VersionExternalApiInfoResult; +import java.io.Serializable; +import jakarta.validation.constraints.*; +import jakarta.validation.Valid; + +import java.util.Objects; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonValue; +import com.fasterxml.jackson.annotation.JsonTypeName; + + + +@JsonTypeName("VersionInfoResult") +@jakarta.annotation.Generated(value = "org.openapitools.codegen.languages.JavaJAXRSSpecServerCodegen", date = "2024-05-12T16:25:41.890330+02:00[Europe/Warsaw]")@lombok.Data @lombok.NoArgsConstructor @lombok.AllArgsConstructor(staticName = "of") + +public class VersionInfoResult implements Serializable { + private @Valid VersionExternalApiInfoResult externalApi; + + /** + **/ + public VersionInfoResult externalApi(VersionExternalApiInfoResult externalApi) { + this.externalApi = externalApi; + return this; + } + + + @JsonProperty("externalApi") + public VersionExternalApiInfoResult getExternalApi() { + return externalApi; + } + + @JsonProperty("externalApi") + public void setExternalApi(VersionExternalApiInfoResult externalApi) { + this.externalApi = externalApi; + } + + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + VersionInfoResult versionInfoResult = (VersionInfoResult) o; + return Objects.equals(this.externalApi, versionInfoResult.externalApi); + } + + @Override + public int hashCode() { + return Objects.hash(externalApi); + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append("class VersionInfoResult {\n"); + + sb.append(" externalApi: ").append(toIndentedString(externalApi)).append("\n"); + sb.append("}"); + return sb.toString(); + } + + /** + * Convert the given object to string with each line indented by 4 spaces + * (except the first line). + */ + private String toIndentedString(Object o) { + if (o == null) { + return "null"; + } + return o.toString().replace("\n", "\n "); + } + + +} + diff --git a/api-server/target/generated-sources/openapi/src/main/resources/META-INF/openapi.yaml b/api-server/target/generated-sources/openapi/src/main/resources/META-INF/openapi.yaml new file mode 100644 index 0000000..1e89526 --- /dev/null +++ b/api-server/target/generated-sources/openapi/src/main/resources/META-INF/openapi.yaml @@ -0,0 +1,465 @@ +openapi: 3.0.1 +info: + description: RepoAchiever API Server Open API documentation + title: OpenAPI document of RepoAchiever API Server + version: "1.0" +servers: +- url: / +tags: +- description: Contains all endpoints related to operations on processed content. + name: ContentResource +- description: Contains all endpoints related to state processing. + name: StateResource +- description: Contains all endpoints related to general info of API Server. + name: InfoResource +- description: Contains all endpoints related to general API Server health information. + name: HealthResource +paths: + /v1/content: + post: + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/ContentRetrievalApplication' + description: Content retrieval application + required: true + responses: + "204": + content: + application/json: + schema: + $ref: '#/components/schemas/ContentRetrievalResult' + description: A list of all available content + tags: + - ContentResource + x-content-type: application/json + x-accepts: application/json + x-tags: + - tag: ContentResource + /v1/content/apply: + post: + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/ContentApplication' + description: Content configuration application + required: true + responses: + "204": + description: Given content configuration was successfully applied + "400": + description: Given content configuration was not applied + tags: + - ContentResource + x-content-type: application/json + x-accepts: application/json + x-tags: + - tag: ContentResource + /v1/content/withdraw: + delete: + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/ContentWithdrawal' + description: Content withdraw application. Does not remove persisted content. + required: true + responses: + "204": + description: Given content configuration was successfully withdrawn + "400": + description: Given content configuration was not withdrawn + tags: + - ContentResource + x-content-type: application/json + x-accepts: application/json + x-tags: + - tag: ContentResource + /v1/content/download: + get: + parameters: + - description: Name of content location to be downloaded + explode: true + in: query + name: location + required: false + schema: + type: string + style: form + responses: + "200": + content: + application/octet-stream: + schema: + format: binary + type: string + description: A content was successfully retrieved + tags: + - ContentResource + x-accepts: application/octet-stream + x-tags: + - tag: ContentResource + /v1/content/clean: + post: + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/ContentCleanup' + description: Content configuration application + required: true + responses: + "201": + description: Content with the given configuration was successfully deleted + "400": + description: Content with the given configuration was not deleted + tags: + - ContentResource + x-content-type: application/json + x-accepts: application/json + x-tags: + - tag: ContentResource + /v1/state/content: + post: + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/ContentStateApplication' + description: Given content state key + required: true + responses: + "201": + content: + application/json: + schema: + $ref: '#/components/schemas/ContentStateApplicationResult' + description: Content state hash is retrieved successfully + tags: + - StateResource + x-content-type: application/json + x-accepts: application/json + x-tags: + - tag: StateResource + /v1/info/version: + get: + responses: + "200": + content: + application/json: + schema: + $ref: '#/components/schemas/VersionInfoResult' + description: General information about running API Server + tags: + - InfoResource + x-accepts: application/json + x-tags: + - tag: InfoResource + /v1/info/cluster: + get: + responses: + "200": + content: + application/json: + schema: + $ref: '#/components/schemas/ClusterInfoResult' + description: General information about running clusters + tags: + - InfoResource + x-accepts: application/json + x-tags: + - tag: InfoResource + /v1/info/telemetry: + get: + responses: + "200": + content: + text/plain: + schema: + type: string + description: A set of Prometheus samples used by Grafana instance + tags: + - InfoResource + x-accepts: text/plain + x-tags: + - tag: InfoResource + /v1/health: + get: + responses: + "200": + content: + application/json: + schema: + $ref: '#/components/schemas/HealthCheckResult' + description: General health information about running API Server + tags: + - HealthResource + x-accepts: application/json + x-tags: + - tag: HealthResource + /v1/readiness: + post: + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/ReadinessCheckApplication' + description: Check if API Server is ready to serve for the given user + required: true + responses: + "200": + content: + application/json: + schema: + $ref: '#/components/schemas/ReadinessCheckResult' + description: General health information about running API Server + tags: + - HealthResource + x-content-type: application/json + x-accepts: application/json + x-tags: + - tag: HealthResource +components: + schemas: + Provider: + enum: + - git-local + - git-github + type: string + CredentialsFieldsFull: + example: + internal: + id: 0 + external: null + properties: + internal: + $ref: '#/components/schemas/CredentialsFieldsInternal' + external: + $ref: '#/components/schemas/CredentialsFieldsExternal' + required: + - internal + CredentialsFieldsInternal: + example: + id: 0 + properties: + id: + type: integer + CredentialsFieldsExternal: + anyOf: + - $ref: '#/components/schemas/GitGitHubCredentials' + GitGitHubCredentials: + properties: + token: + type: string + required: + - token + ContentRetrievalApplication: + example: + provider: null + credentials: + internal: + id: 0 + external: null + properties: + provider: + $ref: '#/components/schemas/Provider' + credentials: + $ref: '#/components/schemas/CredentialsFieldsFull' + required: + - credentials + - provider + ContentRetrievalResult: + example: + locations: + - locations + - locations + properties: + locations: + items: + type: string + type: array + required: + - locations + ContentApplication: + example: + provider: null + credentials: + internal: + id: 0 + external: null + locations: + - locations + - locations + properties: + locations: + items: + type: string + type: array + provider: + $ref: '#/components/schemas/Provider' + credentials: + $ref: '#/components/schemas/CredentialsFieldsFull' + required: + - credentials + - locations + - provider + ContentWithdrawal: + example: + provider: null + credentials: + internal: + id: 0 + external: null + properties: + provider: + $ref: '#/components/schemas/Provider' + credentials: + $ref: '#/components/schemas/CredentialsFieldsFull' + required: + - credentials + - provider + ContentCleanup: + example: + credentials: + internal: + id: 0 + external: null + properties: + credentials: + $ref: '#/components/schemas/CredentialsFieldsFull' + required: + - credentials + ContentStateApplication: + example: + provider: null + credentials: + internal: + id: 0 + external: null + properties: + provider: + $ref: '#/components/schemas/Provider' + credentials: + $ref: '#/components/schemas/CredentialsFieldsFull' + required: + - credentials + - provider + ContentStateApplicationResult: + example: + hash: hash + properties: + hash: + type: string + required: + - hash + VersionInfoResult: + example: + externalApi: + version: version + hash: hash + properties: + externalApi: + $ref: '#/components/schemas/VersionExternalApiInfoResult' + VersionExternalApiInfoResult: + example: + version: version + hash: hash + properties: + version: + type: string + hash: + type: string + required: + - hash + - version + ClusterInfoResult: + items: + $ref: '#/components/schemas/ClusterInfoUnit' + type: array + ClusterInfoUnit: + example: + name: name + health: true + workers: 0 + properties: + name: + type: string + health: + type: boolean + workers: + type: integer + required: + - name + HealthCheckResult: + example: + checks: + - name: name + status: null + - name: name + status: null + status: null + properties: + status: + $ref: '#/components/schemas/HealthCheckStatus' + checks: + items: + $ref: '#/components/schemas/HealthCheckUnit' + type: array + required: + - checks + - status + HealthCheckUnit: + example: + name: name + status: null + properties: + name: + type: string + status: + $ref: '#/components/schemas/HealthCheckStatus' + required: + - name + - status + HealthCheckStatus: + enum: + - UP + - DOWN + type: string + ReadinessCheckApplication: + example: + test: "{}" + properties: + test: + type: object + ReadinessCheckResult: + example: + data: "{}" + name: name + status: null + properties: + name: + type: string + status: + $ref: '#/components/schemas/ReadinessCheckStatus' + data: + type: object + required: + - data + - name + - status + ReadinessCheckUnit: + properties: + name: + type: string + status: + $ref: '#/components/schemas/ReadinessCheckStatus' + required: + - name + - status + ReadinessCheckStatus: + enum: + - UP + - DOWN + type: string diff --git a/api-server/target/generated-sources/openapi/src/main/resources/application.properties b/api-server/target/generated-sources/openapi/src/main/resources/application.properties new file mode 100644 index 0000000..83b16e9 --- /dev/null +++ b/api-server/target/generated-sources/openapi/src/main/resources/application.properties @@ -0,0 +1,5 @@ +# Configuration file +# key = value + +mp.openapi.scan.disable=true + diff --git a/api-server/target/maven-archiver/pom.properties b/api-server/target/maven-archiver/pom.properties new file mode 100644 index 0000000..b2fcf44 --- /dev/null +++ b/api-server/target/maven-archiver/pom.properties @@ -0,0 +1,4 @@ +#Created by Apache Maven 3.9.6 +artifactId=api-server +groupId=com.repoachiever +version=1.0-SNAPSHOT diff --git a/api-server/target/maven-status/maven-compiler-plugin/compile/default-compile/createdFiles.lst b/api-server/target/maven-status/maven-compiler-plugin/compile/default-compile/createdFiles.lst new file mode 100644 index 0000000..63ea8a0 --- /dev/null +++ b/api-server/target/maven-status/maven-compiler-plugin/compile/default-compile/createdFiles.lst @@ -0,0 +1,210 @@ +com/repoachiever/model/GitGitHubCredentials.class +com/repoachiever/service/command/cluster/deploy/ClusterDeployCommandService.class +com/repoachiever/repository/facade/RepositoryFacade.class +com/repoachiever/model/ContentCleanup.class +com/repoachiever/exception/ContentFileNotFoundException.class +com/repoachiever/dto/RepositoryContentUnitDto.class +com/repoachiever/entity/common/ClusterContextEntity$Metadata.class +com/repoachiever/api/InfoResourceApi.class +com/repoachiever/service/integration/communication/cluster/healthcheck/ClusterHealthCheckCommunicationService.class +com/repoachiever/service/workspace/WorkspaceService$1.class +com/repoachiever/exception/RepositoryContentApplicationFailureException.class +com/repoachiever/model/HealthCheckUnit.class +com/repoachiever/dto/CommandExecutorOutputDto.class +com/repoachiever/model/HealthCheckResult.class +com/repoachiever/exception/CommandExecutorException.class +com/repoachiever/exception/QueryExecutionFailureException.class +com/repoachiever/entity/common/ClusterContextEntity$Resource$Cluster.class +com/repoachiever/mapping/CredentialsFieldIsNotValidExceptionMapper.class +com/repoachiever/service/cluster/resource/ClusterCommunicationResource.class +com/repoachiever/model/ContentStateApplication.class +com/repoachiever/exception/NodeExporterDeploymentFailureException.class +com/repoachiever/resource/ContentResource.class +com/repoachiever/exception/WorkspaceUnitDirectoryNotFoundException.class +com/repoachiever/resource/HealthResource.class +com/repoachiever/entity/common/ClusterContextEntity$Filter.class +com/repoachiever/entity/common/ConfigEntity$Connection.class +com/repoachiever/service/command/prometheus/common/PrometheusConfigurationHelper$3.class +com/repoachiever/exception/DockerNetworkRemoveFailureException.class +com/repoachiever/service/command/cluster/destroy/ClusterDestroyCommandService.class +com/repoachiever/service/command/grafana/common/GrafanaConfigurationHelper$1.class +com/repoachiever/entity/common/ConfigEntity$Diagnostics.class +com/repoachiever/entity/repository/ProviderEntity.class +com/repoachiever/entity/common/ConfigEntity.class +com/repoachiever/service/integration/diagnostics/template/TemplateConfigService$3.class +com/repoachiever/service/command/cluster/common/ClusterConfigurationHelper.class +com/repoachiever/exception/ClusterApplicationTimeoutException.class +com/repoachiever/converter/ContentCredentialsToClusterContextCredentialsConverter$1.class +com/repoachiever/service/command/prometheus/common/PrometheusConfigurationHelper$2.class +com/repoachiever/entity/common/ClusterContextEntity$Communication.class +com/repoachiever/model/CredentialsFieldsFull.class +com/repoachiever/exception/RepositoryOperationFailureException.class +com/repoachiever/model/ReadinessCheckUnit.class +com/repoachiever/entity/common/ClusterContextEntity$Content.class +com/repoachiever/service/integration/diagnostics/template/TemplateConfigService$3$1.class +com/repoachiever/service/integration/diagnostics/template/TemplateConfigService$2.class +com/repoachiever/model/ContentRetrievalApplication.class +com/repoachiever/api/ContentResourceApi.class +com/repoachiever/exception/ContentFileWriteFailureException.class +com/repoachiever/exception/QueryEmptyResultException.class +com/repoachiever/entity/common/ClusterContextEntity$Resource.class +com/repoachiever/service/integration/diagnostics/template/TemplateConfigService$1.class +com/repoachiever/entity/common/ClusterContextEntity$Resource$Worker.class +com/repoachiever/repository/common/RepositoryConfigurationHelper.class +com/repoachiever/service/command/cluster/common/ClusterConfigurationHelper$1.class +com/repoachiever/entity/common/ClusterContextEntity$Service$Provider.class +com/repoachiever/entity/common/ConfigEntity$Diagnostics$Grafana.class +com/repoachiever/exception/WorkspaceContentDirectoryCreationFailureException.class +com/repoachiever/service/command/docker/network/remove/DockerNetworkRemoveCommandService.class +com/repoachiever/model/HealthCheckStatus.class +com/repoachiever/service/command/prometheus/PrometheusDeployCommandService$1.class +com/repoachiever/service/config/ConfigService.class +com/repoachiever/model/ReadinessCheckApplication.class +com/repoachiever/model/ContentApplication.class +com/repoachiever/converter/HealthCheckResponseToReadinessCheckResult.class +com/repoachiever/exception/ClusterOperationFailureException.class +com/repoachiever/service/command/docker/availability/DockerAvailabilityCheckCommandService.class +com/repoachiever/service/integration/diagnostics/template/TemplateConfigService$3$2.class +com/repoachiever/service/command/nodeexporter/common/NodeExporterConfigurationHelper$1.class +com/repoachiever/model/Provider.class +com/repoachiever/exception/MetadataFileNotFoundException.class +com/repoachiever/service/integration/diagnostics/template/TemplateConfigService$2$1.class +com/repoachiever/service/integration/communication/cluster/topology/ClusterTopologyCommunicationConfigService.class +com/repoachiever/mapping/CredentialsAreNotValidExceptionMapper.class +com/repoachiever/mapping/WorkspaceUnitDirectoryNotFoundExceptionMapper.class +com/repoachiever/entity/common/ConfigEntity$Resource$Cluster.class +com/repoachiever/entity/common/ConfigEntity$Database.class +com/repoachiever/model/VersionExternalApiInfoResult.class +com/repoachiever/exception/ClusterDestructionFailureException.class +com/repoachiever/api/HealthResourceApi.class +com/repoachiever/dto/ClusterAllocationDto.class +com/repoachiever/converter/ClusterContextToJsonConverter.class +com/repoachiever/service/vendor/VendorFacade$1.class +com/repoachiever/exception/ContentApplicationRetrievalFailureException.class +com/repoachiever/service/workspace/facade/WorkspaceFacade.class +com/repoachiever/exception/CommunicationConfigurationFailureException.class +com/repoachiever/entity/common/ClusterContextEntity$Service.class +com/repoachiever/service/telemetry/TelemetryService.class +com/repoachiever/service/command/docker/network/create/DockerNetworkCreateCommandService.class +com/repoachiever/entity/common/ConfigEntity$Content.class +com/repoachiever/service/integration/state/StateConfigService.class +com/repoachiever/service/command/cluster/destroy/ClusterDestroyCommandService$1.class +com/repoachiever/service/integration/diagnostics/template/TemplateConfigService.class +com/repoachiever/exception/ClusterUnhealthyReapplicationFailureException.class +com/repoachiever/service/command/grafana/GrafanaDeployCommandService$1.class +com/repoachiever/exception/ConfigValidationException.class +com/repoachiever/exception/CredentialsFieldIsNotValidException.class +com/repoachiever/service/healthcheck/readiness/ReadinessCheckService.class +com/repoachiever/exception/ApiServerInstanceIsAlreadyRunningException.class +com/repoachiever/exception/RepositoryContentDestructionFailureException.class +com/repoachiever/service/telemetry/binding/TelemetryBinding.class +com/repoachiever/exception/DockerInspectRemovalFailureException.class +com/repoachiever/mapping/ClusterApplicationFailureExceptionMapper.class +com/repoachiever/resource/StateResource.class +com/repoachiever/service/workspace/WorkspaceService.class +com/repoachiever/service/command/grafana/common/GrafanaConfigurationHelper$3.class +com/repoachiever/entity/common/MetadataFileEntity.class +com/repoachiever/service/workspace/facade/WorkspaceFacade$1.class +com/repoachiever/entity/common/ConfigEntity$Resource$Worker.class +com/repoachiever/service/vendor/common/VendorConfigurationHelper.class +com/repoachiever/service/integration/http/HttpServerConfigService.class +com/repoachiever/exception/ClusterWithdrawalFailureException.class +com/repoachiever/service/integration/properties/git/GitPropertiesConfigService.class +com/repoachiever/converter/HealthCheckResponseToReadinessCheckResult$1.class +com/repoachiever/repository/common/RepositoryConfigurationHelper$1.class +com/repoachiever/exception/WorkspaceUnitDirectoryRemovalFailureException.class +com/repoachiever/entity/common/ClusterContextEntity$Service$Credentials.class +com/repoachiever/service/client/smallrye/ISmallRyeHealthCheckClientService.class +com/repoachiever/exception/DiagnosticsTemplateProcessingFailureException.class +com/repoachiever/exception/LocationsFieldIsNotValidException.class +com/repoachiever/service/command/common/CommandConfigurationHelper.class +com/repoachiever/mapping/ClusterWithdrawalFailureExceptionMapper.class +com/repoachiever/service/command/docker/inspect/remove/DockerInspectRemoveCommandService$1.class +com/repoachiever/resource/common/ResourceConfigurationHelper$1.class +META-INF/panache-archive.marker +com/repoachiever/model/ReadinessCheckResult.class +com/repoachiever/service/config/ConfigService$1.class +com/repoachiever/service/healthcheck/health/HealthCheckService.class +com/repoachiever/service/integration/properties/general/GeneralPropertiesConfigService.class +com/repoachiever/exception/CredentialsAreNotValidException.class +com/repoachiever/entity/common/ConfigEntity$Communication.class +com/repoachiever/service/command/docker/network/create/DockerNetworkCreateCommandService$1.class +com/repoachiever/model/CredentialsFieldsInternal.class +com/repoachiever/service/command/nodeexporter/common/NodeExporterConfigurationHelper.class +META-INF/org/apache/logging/log4j/core/config/plugins/Log4j2Plugins.dat +com/repoachiever/RestResourceRoot.class +com/repoachiever/service/command/nodeexporter/NodeExporterDeployCommandService.class +com/repoachiever/service/integration/communication/apiserver/ApiServerCommunicationConfigService.class +com/repoachiever/entity/repository/ConfigEntity.class +com/repoachiever/exception/DockerIsNotAvailableException.class +com/repoachiever/service/command/nodeexporter/common/NodeExporterConfigurationHelper$2.class +com/repoachiever/service/vendor/VendorFacade.class +com/repoachiever/service/command/grafana/common/GrafanaConfigurationHelper.class +com/repoachiever/exception/PrometheusDeploymentFailureException.class +com/repoachiever/model/ClusterInfoUnit.class +com/repoachiever/entity/repository/ContentEntity.class +com/repoachiever/service/command/grafana/common/GrafanaConfigurationHelper$2.class +com/repoachiever/service/command/grafana/GrafanaDeployCommandService.class +com/repoachiever/mapping/RepositoryContentDestructionFailureExceptionMapper.class +com/repoachiever/entity/common/PropertiesEntity.class +com/repoachiever/model/ContentWithdrawal.class +com/repoachiever/service/command/prometheus/common/PrometheusConfigurationHelper$1.class +com/repoachiever/exception/WorkspaceUnitDirectoryPresentException.class +com/repoachiever/service/integration/diagnostics/DiagnosticsConfigService.class +com/repoachiever/entity/common/ConfigEntity$Resource.class +com/repoachiever/exception/DockerNetworkCreateFailureException.class +com/repoachiever/entity/common/ConfigEntity$Diagnostics$Prometheus.class +com/repoachiever/service/command/nodeexporter/common/NodeExporterConfigurationHelper$3.class +com/repoachiever/repository/ContentRepository.class +com/repoachiever/model/ReadinessCheckStatus.class +com/repoachiever/converter/ContentProviderToClusterContextProviderConverter.class +com/repoachiever/service/cluster/common/ClusterConfigurationHelper.class +com/repoachiever/repository/executor/RepositoryExecutor.class +com/repoachiever/service/command/nodeexporter/NodeExporterDeployCommandService$1.class +com/repoachiever/service/command/prometheus/common/PrometheusConfigurationHelper.class +com/repoachiever/service/vendor/git/github/GitGitHubVendorService.class +com/repoachiever/repository/ConfigRepository.class +com/repoachiever/entity/repository/SecretEntity.class +com/repoachiever/service/state/StateService.class +com/repoachiever/model/VersionInfoResult.class +com/repoachiever/mapping/RepositoryContentApplicationFailureExceptionMapper.class +com/repoachiever/service/executor/CommandExecutorService.class +com/repoachiever/converter/ContentCredentialsToClusterContextCredentialsConverter.class +com/repoachiever/api/StateResourceApi.class +com/repoachiever/service/integration/communication/registry/RegistryCommunicationConfigService.class +com/repoachiever/model/CredentialsFieldsExternal.class +com/repoachiever/exception/ContentFileRemovalFailureException.class +com/repoachiever/exception/ClusterApplicationFailureException.class +com/repoachiever/model/ContentStateApplicationResult.class +com/repoachiever/service/command/docker/availability/DockerAvailabilityCheckCommandService$1.class +com/repoachiever/entity/common/ConfigEntity$Diagnostics$Metrics.class +com/repoachiever/model/ContentRetrievalResult.class +com/repoachiever/service/command/prometheus/PrometheusDeployCommandService.class +com/repoachiever/resource/common/ResourceConfigurationHelper.class +com/repoachiever/resource/InfoResource.class +com/repoachiever/exception/TelemetryOperationFailureException.class +com/repoachiever/service/communication/cluster/IClusterCommunicationService.class +com/repoachiever/resource/communication/ApiServerCommunicationResource.class +com/repoachiever/logging/FatalAppender.class +com/repoachiever/service/command/docker/network/remove/DockerNetworkRemoveCommandService$1.class +com/repoachiever/exception/ClusterRecreationFailureException.class +com/repoachiever/service/communication/common/CommunicationProviderConfigurationHelper.class +com/repoachiever/repository/ProviderRepository.class +com/repoachiever/service/integration/diagnostics/telemetry/TelemetryConfigService.class +com/repoachiever/service/integration/diagnostics/template/TemplateConfigService$1$2.class +com/repoachiever/exception/ClusterDeploymentFailureException.class +com/repoachiever/exception/MetadataFileWriteFailureException.class +com/repoachiever/exception/CredentialsConversionException.class +com/repoachiever/service/cluster/ClusterService.class +com/repoachiever/service/command/cluster/deploy/ClusterDeployCommandService$1.class +com/repoachiever/exception/ClusterFullDestructionFailureException.class +com/repoachiever/entity/common/ConfigEntity$Diagnostics$NodeExporter.class +com/repoachiever/mapping/LocationsFieldIsNotValidExceptionMapper.class +com/repoachiever/service/communication/apiserver/IApiServerCommunicationService.class +com/repoachiever/service/client/github/IGitHubClientService.class +com/repoachiever/exception/WorkspaceUnitDirectoryCreationFailureException.class +com/repoachiever/service/cluster/facade/ClusterFacade.class +com/repoachiever/service/command/docker/inspect/remove/DockerInspectRemoveCommandService.class +com/repoachiever/entity/common/ClusterContextEntity.class +com/repoachiever/repository/SecretRepository.class +com/repoachiever/service/integration/diagnostics/template/TemplateConfigService$1$1.class diff --git a/api-server/target/maven-status/maven-compiler-plugin/compile/default-compile/inputFiles.lst b/api-server/target/maven-status/maven-compiler-plugin/compile/default-compile/inputFiles.lst new file mode 100644 index 0000000..12ced00 --- /dev/null +++ b/api-server/target/maven-status/maven-compiler-plugin/compile/default-compile/inputFiles.lst @@ -0,0 +1,151 @@ +/Volumes/Files/java/RepoAchiever/api-server/src/main/java/com/repoachiever/converter/HealthCheckResponseToReadinessCheckResult.java +/Volumes/Files/java/RepoAchiever/api-server/src/main/java/com/repoachiever/exception/DockerInspectRemovalFailureException.java +/Volumes/Files/java/RepoAchiever/api-server/src/main/java/com/repoachiever/service/communication/apiserver/IApiServerCommunicationService.java +/Volumes/Files/java/RepoAchiever/api-server/target/generated-sources/openapi/src/main/java/com/repoachiever/model/CredentialsFieldsExternal.java +/Volumes/Files/java/RepoAchiever/api-server/target/generated-sources/openapi/src/main/java/com/repoachiever/model/ContentWithdrawal.java +/Volumes/Files/java/RepoAchiever/api-server/src/main/java/com/repoachiever/entity/repository/SecretEntity.java +/Volumes/Files/java/RepoAchiever/api-server/src/main/java/com/repoachiever/service/command/prometheus/PrometheusDeployCommandService.java +/Volumes/Files/java/RepoAchiever/api-server/src/main/java/com/repoachiever/exception/CredentialsConversionException.java +/Volumes/Files/java/RepoAchiever/api-server/target/generated-sources/openapi/src/main/java/com/repoachiever/model/ReadinessCheckUnit.java +/Volumes/Files/java/RepoAchiever/api-server/src/main/java/com/repoachiever/service/client/github/IGitHubClientService.java +/Volumes/Files/java/RepoAchiever/api-server/src/main/java/com/repoachiever/exception/ClusterDeploymentFailureException.java +/Volumes/Files/java/RepoAchiever/api-server/src/main/java/com/repoachiever/repository/ConfigRepository.java +/Volumes/Files/java/RepoAchiever/api-server/target/generated-sources/openapi/src/main/java/com/repoachiever/model/HealthCheckStatus.java +/Volumes/Files/java/RepoAchiever/api-server/src/main/java/com/repoachiever/mapping/RepositoryContentDestructionFailureExceptionMapper.java +/Volumes/Files/java/RepoAchiever/api-server/src/main/java/com/repoachiever/service/integration/diagnostics/telemetry/TelemetryConfigService.java +/Volumes/Files/java/RepoAchiever/api-server/src/main/java/com/repoachiever/entity/repository/ConfigEntity.java +/Volumes/Files/java/RepoAchiever/api-server/src/main/java/com/repoachiever/service/integration/communication/cluster/healthcheck/ClusterHealthCheckCommunicationService.java +/Volumes/Files/java/RepoAchiever/api-server/target/generated-sources/openapi/src/main/java/com/repoachiever/model/ContentCleanup.java +/Volumes/Files/java/RepoAchiever/api-server/src/main/java/com/repoachiever/exception/RepositoryOperationFailureException.java +/Volumes/Files/java/RepoAchiever/api-server/target/generated-sources/openapi/src/main/java/com/repoachiever/model/ContentApplication.java +/Volumes/Files/java/RepoAchiever/api-server/target/generated-sources/openapi/src/main/java/com/repoachiever/api/ContentResourceApi.java +/Volumes/Files/java/RepoAchiever/api-server/target/generated-sources/openapi/src/main/java/com/repoachiever/model/HealthCheckUnit.java +/Volumes/Files/java/RepoAchiever/api-server/src/main/java/com/repoachiever/entity/repository/ProviderEntity.java +/Volumes/Files/java/RepoAchiever/api-server/src/main/java/com/repoachiever/repository/ContentRepository.java +/Volumes/Files/java/RepoAchiever/api-server/src/main/java/com/repoachiever/exception/RepositoryContentDestructionFailureException.java +/Volumes/Files/java/RepoAchiever/api-server/src/main/java/com/repoachiever/exception/ContentApplicationRetrievalFailureException.java +/Volumes/Files/java/RepoAchiever/api-server/src/main/java/com/repoachiever/service/executor/CommandExecutorService.java +/Volumes/Files/java/RepoAchiever/api-server/src/main/java/com/repoachiever/service/integration/properties/general/GeneralPropertiesConfigService.java +/Volumes/Files/java/RepoAchiever/api-server/src/main/java/com/repoachiever/exception/QueryEmptyResultException.java +/Volumes/Files/java/RepoAchiever/api-server/src/main/java/com/repoachiever/service/integration/communication/apiserver/ApiServerCommunicationConfigService.java +/Volumes/Files/java/RepoAchiever/api-server/src/main/java/com/repoachiever/service/cluster/common/ClusterConfigurationHelper.java +/Volumes/Files/java/RepoAchiever/api-server/target/generated-sources/openapi/src/main/java/com/repoachiever/model/ClusterInfoUnit.java +/Volumes/Files/java/RepoAchiever/api-server/src/main/java/com/repoachiever/exception/QueryExecutionFailureException.java +/Volumes/Files/java/RepoAchiever/api-server/target/generated-sources/openapi/src/main/java/com/repoachiever/model/ContentRetrievalResult.java +/Volumes/Files/java/RepoAchiever/api-server/src/main/java/com/repoachiever/resource/InfoResource.java +/Volumes/Files/java/RepoAchiever/api-server/target/generated-sources/openapi/src/main/java/com/repoachiever/model/Provider.java +/Volumes/Files/java/RepoAchiever/api-server/src/main/java/com/repoachiever/exception/WorkspaceContentDirectoryCreationFailureException.java +/Volumes/Files/java/RepoAchiever/api-server/src/main/java/com/repoachiever/service/state/StateService.java +/Volumes/Files/java/RepoAchiever/api-server/src/main/java/com/repoachiever/service/command/common/CommandConfigurationHelper.java +/Volumes/Files/java/RepoAchiever/api-server/src/main/java/com/repoachiever/service/integration/properties/git/GitPropertiesConfigService.java +/Volumes/Files/java/RepoAchiever/api-server/src/main/java/com/repoachiever/exception/PrometheusDeploymentFailureException.java +/Volumes/Files/java/RepoAchiever/api-server/src/main/java/com/repoachiever/repository/executor/RepositoryExecutor.java +/Volumes/Files/java/RepoAchiever/api-server/src/main/java/com/repoachiever/service/integration/diagnostics/DiagnosticsConfigService.java +/Volumes/Files/java/RepoAchiever/api-server/target/generated-sources/openapi/src/main/java/com/repoachiever/api/InfoResourceApi.java +/Volumes/Files/java/RepoAchiever/api-server/src/main/java/com/repoachiever/resource/communication/ApiServerCommunicationResource.java +/Volumes/Files/java/RepoAchiever/api-server/src/main/java/com/repoachiever/entity/common/ConfigEntity.java +/Volumes/Files/java/RepoAchiever/api-server/src/main/java/com/repoachiever/exception/WorkspaceUnitDirectoryCreationFailureException.java +/Volumes/Files/java/RepoAchiever/api-server/src/main/java/com/repoachiever/entity/common/MetadataFileEntity.java +/Volumes/Files/java/RepoAchiever/api-server/target/generated-sources/openapi/src/main/java/com/repoachiever/RestResourceRoot.java +/Volumes/Files/java/RepoAchiever/api-server/src/main/java/com/repoachiever/entity/repository/ContentEntity.java +/Volumes/Files/java/RepoAchiever/api-server/src/main/java/com/repoachiever/repository/ProviderRepository.java +/Volumes/Files/java/RepoAchiever/api-server/src/main/java/com/repoachiever/service/integration/communication/registry/RegistryCommunicationConfigService.java +/Volumes/Files/java/RepoAchiever/api-server/src/main/java/com/repoachiever/converter/ContentProviderToClusterContextProviderConverter.java +/Volumes/Files/java/RepoAchiever/api-server/src/main/java/com/repoachiever/service/workspace/WorkspaceService.java +/Volumes/Files/java/RepoAchiever/api-server/src/main/java/com/repoachiever/exception/WorkspaceUnitDirectoryNotFoundException.java +/Volumes/Files/java/RepoAchiever/api-server/src/main/java/com/repoachiever/exception/NodeExporterDeploymentFailureException.java +/Volumes/Files/java/RepoAchiever/api-server/src/main/java/com/repoachiever/exception/DockerIsNotAvailableException.java +/Volumes/Files/java/RepoAchiever/api-server/target/generated-sources/openapi/src/main/java/com/repoachiever/model/ContentRetrievalApplication.java +/Volumes/Files/java/RepoAchiever/api-server/src/main/java/com/repoachiever/exception/CommunicationConfigurationFailureException.java +/Volumes/Files/java/RepoAchiever/api-server/src/main/java/com/repoachiever/exception/ContentFileNotFoundException.java +/Volumes/Files/java/RepoAchiever/api-server/target/generated-sources/openapi/src/main/java/com/repoachiever/model/GitGitHubCredentials.java +/Volumes/Files/java/RepoAchiever/api-server/src/main/java/com/repoachiever/resource/HealthResource.java +/Volumes/Files/java/RepoAchiever/api-server/src/main/java/com/repoachiever/service/cluster/ClusterService.java +/Volumes/Files/java/RepoAchiever/api-server/src/main/java/com/repoachiever/exception/WorkspaceUnitDirectoryRemovalFailureException.java +/Volumes/Files/java/RepoAchiever/api-server/src/main/java/com/repoachiever/entity/common/PropertiesEntity.java +/Volumes/Files/java/RepoAchiever/api-server/src/main/java/com/repoachiever/service/telemetry/TelemetryService.java +/Volumes/Files/java/RepoAchiever/api-server/src/main/java/com/repoachiever/exception/ConfigValidationException.java +/Volumes/Files/java/RepoAchiever/api-server/src/main/java/com/repoachiever/exception/ClusterUnhealthyReapplicationFailureException.java +/Volumes/Files/java/RepoAchiever/api-server/src/main/java/com/repoachiever/resource/common/ResourceConfigurationHelper.java +/Volumes/Files/java/RepoAchiever/api-server/src/main/java/com/repoachiever/mapping/CredentialsFieldIsNotValidExceptionMapper.java +/Volumes/Files/java/RepoAchiever/api-server/target/generated-sources/openapi/src/main/java/com/repoachiever/model/ReadinessCheckApplication.java +/Volumes/Files/java/RepoAchiever/api-server/src/main/java/com/repoachiever/service/command/docker/availability/DockerAvailabilityCheckCommandService.java +/Volumes/Files/java/RepoAchiever/api-server/src/main/java/com/repoachiever/exception/RepositoryContentApplicationFailureException.java +/Volumes/Files/java/RepoAchiever/api-server/src/main/java/com/repoachiever/converter/ClusterContextToJsonConverter.java +/Volumes/Files/java/RepoAchiever/api-server/src/main/java/com/repoachiever/exception/ClusterDestructionFailureException.java +/Volumes/Files/java/RepoAchiever/api-server/src/main/java/com/repoachiever/service/vendor/common/VendorConfigurationHelper.java +/Volumes/Files/java/RepoAchiever/api-server/src/main/java/com/repoachiever/exception/MetadataFileWriteFailureException.java +/Volumes/Files/java/RepoAchiever/api-server/src/main/java/com/repoachiever/mapping/LocationsFieldIsNotValidExceptionMapper.java +/Volumes/Files/java/RepoAchiever/api-server/src/main/java/com/repoachiever/exception/DiagnosticsTemplateProcessingFailureException.java +/Volumes/Files/java/RepoAchiever/api-server/src/main/java/com/repoachiever/service/cluster/resource/ClusterCommunicationResource.java +/Volumes/Files/java/RepoAchiever/api-server/src/main/java/com/repoachiever/mapping/RepositoryContentApplicationFailureExceptionMapper.java +/Volumes/Files/java/RepoAchiever/api-server/src/main/java/com/repoachiever/service/integration/communication/cluster/topology/ClusterTopologyCommunicationConfigService.java +/Volumes/Files/java/RepoAchiever/api-server/src/main/java/com/repoachiever/exception/WorkspaceUnitDirectoryPresentException.java +/Volumes/Files/java/RepoAchiever/api-server/src/main/java/com/repoachiever/repository/common/RepositoryConfigurationHelper.java +/Volumes/Files/java/RepoAchiever/api-server/src/main/java/com/repoachiever/repository/facade/RepositoryFacade.java +/Volumes/Files/java/RepoAchiever/api-server/src/main/java/com/repoachiever/service/integration/http/HttpServerConfigService.java +/Volumes/Files/java/RepoAchiever/api-server/src/main/java/com/repoachiever/service/command/cluster/deploy/ClusterDeployCommandService.java +/Volumes/Files/java/RepoAchiever/api-server/target/generated-sources/openapi/src/main/java/com/repoachiever/api/HealthResourceApi.java +/Volumes/Files/java/RepoAchiever/api-server/src/main/java/com/repoachiever/service/workspace/facade/WorkspaceFacade.java +/Volumes/Files/java/RepoAchiever/api-server/src/main/java/com/repoachiever/exception/CommandExecutorException.java +/Volumes/Files/java/RepoAchiever/api-server/src/main/java/com/repoachiever/service/communication/cluster/IClusterCommunicationService.java +/Volumes/Files/java/RepoAchiever/api-server/src/main/java/com/repoachiever/service/healthcheck/readiness/ReadinessCheckService.java +/Volumes/Files/java/RepoAchiever/api-server/src/main/java/com/repoachiever/service/command/docker/inspect/remove/DockerInspectRemoveCommandService.java +/Volumes/Files/java/RepoAchiever/api-server/src/main/java/com/repoachiever/mapping/ClusterWithdrawalFailureExceptionMapper.java +/Volumes/Files/java/RepoAchiever/api-server/src/main/java/com/repoachiever/exception/ClusterApplicationTimeoutException.java +/Volumes/Files/java/RepoAchiever/api-server/target/generated-sources/openapi/src/main/java/com/repoachiever/model/ContentStateApplication.java +/Volumes/Files/java/RepoAchiever/api-server/src/main/java/com/repoachiever/repository/SecretRepository.java +/Volumes/Files/java/RepoAchiever/api-server/src/main/java/com/repoachiever/service/integration/diagnostics/template/TemplateConfigService.java +/Volumes/Files/java/RepoAchiever/api-server/src/main/java/com/repoachiever/service/communication/common/CommunicationProviderConfigurationHelper.java +/Volumes/Files/java/RepoAchiever/api-server/src/main/java/com/repoachiever/exception/TelemetryOperationFailureException.java +/Volumes/Files/java/RepoAchiever/api-server/src/main/java/com/repoachiever/exception/ClusterRecreationFailureException.java +/Volumes/Files/java/RepoAchiever/api-server/target/generated-sources/openapi/src/main/java/com/repoachiever/model/VersionInfoResult.java +/Volumes/Files/java/RepoAchiever/api-server/src/main/java/com/repoachiever/exception/LocationsFieldIsNotValidException.java +/Volumes/Files/java/RepoAchiever/api-server/src/main/java/com/repoachiever/exception/ClusterWithdrawalFailureException.java +/Volumes/Files/java/RepoAchiever/api-server/src/main/java/com/repoachiever/resource/StateResource.java +/Volumes/Files/java/RepoAchiever/api-server/src/main/java/com/repoachiever/exception/ContentFileRemovalFailureException.java +/Volumes/Files/java/RepoAchiever/api-server/src/main/java/com/repoachiever/service/command/nodeexporter/common/NodeExporterConfigurationHelper.java +/Volumes/Files/java/RepoAchiever/api-server/src/main/java/com/repoachiever/mapping/ClusterApplicationFailureExceptionMapper.java +/Volumes/Files/java/RepoAchiever/api-server/src/main/java/com/repoachiever/dto/ClusterAllocationDto.java +/Volumes/Files/java/RepoAchiever/api-server/src/main/java/com/repoachiever/service/client/smallrye/ISmallRyeHealthCheckClientService.java +/Volumes/Files/java/RepoAchiever/api-server/src/main/java/com/repoachiever/service/command/prometheus/common/PrometheusConfigurationHelper.java +/Volumes/Files/java/RepoAchiever/api-server/src/main/java/com/repoachiever/service/cluster/facade/ClusterFacade.java +/Volumes/Files/java/RepoAchiever/api-server/target/generated-sources/openapi/src/main/java/com/repoachiever/model/CredentialsFieldsFull.java +/Volumes/Files/java/RepoAchiever/api-server/src/main/java/com/repoachiever/service/command/nodeexporter/NodeExporterDeployCommandService.java +/Volumes/Files/java/RepoAchiever/api-server/src/main/java/com/repoachiever/service/command/cluster/destroy/ClusterDestroyCommandService.java +/Volumes/Files/java/RepoAchiever/api-server/src/main/java/com/repoachiever/service/vendor/VendorFacade.java +/Volumes/Files/java/RepoAchiever/api-server/src/main/java/com/repoachiever/exception/CredentialsFieldIsNotValidException.java +/Volumes/Files/java/RepoAchiever/api-server/src/main/java/com/repoachiever/exception/MetadataFileNotFoundException.java +/Volumes/Files/java/RepoAchiever/api-server/target/generated-sources/openapi/src/main/java/com/repoachiever/model/VersionExternalApiInfoResult.java +/Volumes/Files/java/RepoAchiever/api-server/src/main/java/com/repoachiever/dto/RepositoryContentUnitDto.java +/Volumes/Files/java/RepoAchiever/api-server/src/main/java/com/repoachiever/service/healthcheck/health/HealthCheckService.java +/Volumes/Files/java/RepoAchiever/api-server/src/main/java/com/repoachiever/service/command/docker/network/create/DockerNetworkCreateCommandService.java +/Volumes/Files/java/RepoAchiever/api-server/target/generated-sources/openapi/src/main/java/com/repoachiever/model/ReadinessCheckStatus.java +/Volumes/Files/java/RepoAchiever/api-server/src/main/java/com/repoachiever/service/command/docker/network/remove/DockerNetworkRemoveCommandService.java +/Volumes/Files/java/RepoAchiever/api-server/src/main/java/com/repoachiever/service/command/grafana/GrafanaDeployCommandService.java +/Volumes/Files/java/RepoAchiever/api-server/src/main/java/com/repoachiever/exception/ContentFileWriteFailureException.java +/Volumes/Files/java/RepoAchiever/api-server/src/main/java/com/repoachiever/service/command/cluster/common/ClusterConfigurationHelper.java +/Volumes/Files/java/RepoAchiever/api-server/src/main/java/com/repoachiever/mapping/WorkspaceUnitDirectoryNotFoundExceptionMapper.java +/Volumes/Files/java/RepoAchiever/api-server/src/main/java/com/repoachiever/entity/common/ClusterContextEntity.java +/Volumes/Files/java/RepoAchiever/api-server/src/main/java/com/repoachiever/service/config/ConfigService.java +/Volumes/Files/java/RepoAchiever/api-server/src/main/java/com/repoachiever/service/telemetry/binding/TelemetryBinding.java +/Volumes/Files/java/RepoAchiever/api-server/src/main/java/com/repoachiever/exception/ClusterFullDestructionFailureException.java +/Volumes/Files/java/RepoAchiever/api-server/target/generated-sources/openapi/src/main/java/com/repoachiever/model/CredentialsFieldsInternal.java +/Volumes/Files/java/RepoAchiever/api-server/src/main/java/com/repoachiever/exception/ClusterOperationFailureException.java +/Volumes/Files/java/RepoAchiever/api-server/src/main/java/com/repoachiever/exception/DockerNetworkRemoveFailureException.java +/Volumes/Files/java/RepoAchiever/api-server/src/main/java/com/repoachiever/dto/CommandExecutorOutputDto.java +/Volumes/Files/java/RepoAchiever/api-server/src/main/java/com/repoachiever/service/integration/state/StateConfigService.java +/Volumes/Files/java/RepoAchiever/api-server/src/main/java/com/repoachiever/service/vendor/git/github/GitGitHubVendorService.java +/Volumes/Files/java/RepoAchiever/api-server/src/main/java/com/repoachiever/exception/DockerNetworkCreateFailureException.java +/Volumes/Files/java/RepoAchiever/api-server/src/main/java/com/repoachiever/logging/FatalAppender.java +/Volumes/Files/java/RepoAchiever/api-server/target/generated-sources/openapi/src/main/java/com/repoachiever/api/StateResourceApi.java +/Volumes/Files/java/RepoAchiever/api-server/src/main/java/com/repoachiever/service/command/grafana/common/GrafanaConfigurationHelper.java +/Volumes/Files/java/RepoAchiever/api-server/src/main/java/com/repoachiever/exception/ApiServerInstanceIsAlreadyRunningException.java +/Volumes/Files/java/RepoAchiever/api-server/src/main/java/com/repoachiever/exception/CredentialsAreNotValidException.java +/Volumes/Files/java/RepoAchiever/api-server/target/generated-sources/openapi/src/main/java/com/repoachiever/model/ReadinessCheckResult.java +/Volumes/Files/java/RepoAchiever/api-server/target/generated-sources/openapi/src/main/java/com/repoachiever/model/ContentStateApplicationResult.java +/Volumes/Files/java/RepoAchiever/api-server/src/main/java/com/repoachiever/exception/ClusterApplicationFailureException.java +/Volumes/Files/java/RepoAchiever/api-server/src/main/java/com/repoachiever/converter/ContentCredentialsToClusterContextCredentialsConverter.java +/Volumes/Files/java/RepoAchiever/api-server/src/main/java/com/repoachiever/resource/ContentResource.java +/Volumes/Files/java/RepoAchiever/api-server/target/generated-sources/openapi/src/main/java/com/repoachiever/model/HealthCheckResult.java +/Volumes/Files/java/RepoAchiever/api-server/src/main/java/com/repoachiever/mapping/CredentialsAreNotValidExceptionMapper.java diff --git a/api-server/target/quarkus-app/quarkus-app-dependencies.txt b/api-server/target/quarkus-app/quarkus-app-dependencies.txt new file mode 100644 index 0000000..036c801 --- /dev/null +++ b/api-server/target/quarkus-app/quarkus-app-dependencies.txt @@ -0,0 +1,293 @@ +Shell-Command-Executor-Lib:Shell-Command-Executor-Lib::jar:0.5.0-SNAPSHOST +com.aayushatharva.brotli4j:brotli4j::jar:1.7.1 +com.fasterxml.jackson.core:jackson-annotations::jar:2.15.3 +com.fasterxml.jackson.core:jackson-core::jar:2.15.3 +com.fasterxml.jackson.core:jackson-databind::jar:2.15.3 +com.fasterxml.jackson.dataformat:jackson-dataformat-yaml::jar:2.15.3 +com.fasterxml.jackson.datatype:jackson-datatype-jdk8::jar:2.15.3 +com.fasterxml.jackson.datatype:jackson-datatype-jsr310::jar:2.15.3 +com.fasterxml.jackson.jakarta.rs:jackson-jakarta-rs-base::jar:2.15.3 +com.fasterxml.jackson.jakarta.rs:jackson-jakarta-rs-json-provider::jar:2.15.3 +com.fasterxml.jackson.module:jackson-module-jakarta-xmlbind-annotations::jar:2.15.3 +com.fasterxml.jackson.module:jackson-module-parameter-names::jar:2.15.3 +com.fasterxml:classmate::jar:1.5.1 +com.github.ben-manes.caffeine:caffeine::jar:3.1.8 +com.github.java-json-tools:btf::jar:1.3 +com.github.java-json-tools:jackson-coreutils::jar:2.0 +com.github.java-json-tools:json-patch::jar:1.13 +com.github.java-json-tools:msg-simple::jar:1.2 +com.google.errorprone:error_prone_annotations::jar:2.21.1 +com.ibm.async:asyncutil::jar:0.1.0 +com.opencsv:opencsv::jar:5.6 +com.sun.istack:istack-commons-runtime::jar:4.1.2 +commons-beanutils:commons-beanutils::jar:1.9.4 +commons-codec:commons-codec::jar:1.15 +commons-collections:commons-collections::jar:3.2.2 +commons-io:commons-io::jar:2.11.0 +commons-logging:commons-logging::jar:1.2 +io.agroal:agroal-api::jar:2.1 +io.agroal:agroal-narayana::jar:2.1 +io.agroal:agroal-pool::jar:2.1 +io.github.crac:org-crac::jar:0.1.3 +io.micrometer:micrometer-commons::jar:1.11.5 +io.micrometer:micrometer-core::jar:1.11.5 +io.micrometer:micrometer-observation::jar:1.11.5 +io.micrometer:micrometer-registry-prometheus::jar:1.11.5 +io.mvnpm:importmap::jar:1.0.10 +io.netty:netty-buffer::jar:4.1.100.Final +io.netty:netty-codec-dns::jar:4.1.100.Final +io.netty:netty-codec-haproxy::jar:4.1.100.Final +io.netty:netty-codec-http2::jar:4.1.100.Final +io.netty:netty-codec-http::jar:4.1.100.Final +io.netty:netty-codec-socks::jar:4.1.100.Final +io.netty:netty-codec::jar:4.1.100.Final +io.netty:netty-common::jar:4.1.100.Final +io.netty:netty-handler-proxy::jar:4.1.100.Final +io.netty:netty-handler::jar:4.1.100.Final +io.netty:netty-resolver-dns::jar:4.1.100.Final +io.netty:netty-resolver::jar:4.1.100.Final +io.netty:netty-transport-native-unix-common::jar:4.1.100.Final +io.netty:netty-transport::jar:4.1.100.Final +io.pebbletemplates:pebble::jar:3.2.2 +io.prometheus:simpleclient::jar:0.16.0 +io.prometheus:simpleclient_common::jar:0.16.0 +io.prometheus:simpleclient_tracer_common::jar:0.16.0 +io.prometheus:simpleclient_tracer_otel::jar:0.16.0 +io.prometheus:simpleclient_tracer_otel_agent::jar:0.16.0 +io.quarkiverse.jdbc:quarkus-jdbc-sqlite::jar:3.0.7 +io.quarkus.arc:arc-processor::jar:3.4.3 +io.quarkus.arc:arc::jar:3.4.3 +io.quarkus.gizmo:gizmo::jar:1.6.1.Final +io.quarkus.qute:qute-core::jar:3.4.3 +io.quarkus.resteasy.reactive:resteasy-reactive-common-types::jar:3.4.3 +io.quarkus.resteasy.reactive:resteasy-reactive-common::jar:3.4.3 +io.quarkus.resteasy.reactive:resteasy-reactive-jackson::jar:3.4.3 +io.quarkus.resteasy.reactive:resteasy-reactive-vertx::jar:3.4.3 +io.quarkus.resteasy.reactive:resteasy-reactive::jar:3.4.3 +io.quarkus.security:quarkus-security::jar:2.0.2.Final +io.quarkus:quarkus-agroal::jar:3.4.3 +io.quarkus:quarkus-apache-httpclient::jar:3.4.3 +io.quarkus:quarkus-arc-deployment::jar:3.4.3 +io.quarkus:quarkus-arc::jar:3.4.3 +io.quarkus:quarkus-bootstrap-app-model::jar:3.4.3 +io.quarkus:quarkus-bootstrap-core::jar:3.4.3 +io.quarkus:quarkus-bootstrap-runner::jar:3.4.3 +io.quarkus:quarkus-builder::jar:3.4.3 +io.quarkus:quarkus-caffeine::jar:3.4.3 +io.quarkus:quarkus-class-change-agent::jar:3.4.3 +io.quarkus:quarkus-core-deployment::jar:3.4.3 +io.quarkus:quarkus-core::jar:3.4.3 +io.quarkus:quarkus-credentials::jar:3.4.3 +io.quarkus:quarkus-datasource-common::jar:3.4.3 +io.quarkus:quarkus-datasource::jar:3.4.3 +io.quarkus:quarkus-development-mode-spi::jar:3.4.3 +io.quarkus:quarkus-devtools-utilities::jar:3.4.3 +io.quarkus:quarkus-fs-util::jar:0.0.9 +io.quarkus:quarkus-hibernate-orm-panache-common::jar:3.4.3 +io.quarkus:quarkus-hibernate-orm-panache::jar:3.4.3 +io.quarkus:quarkus-hibernate-orm::jar:3.4.3 +io.quarkus:quarkus-hibernate-validator::jar:3.4.3 +io.quarkus:quarkus-jackson::jar:3.4.3 +io.quarkus:quarkus-jaxb::jar:3.4.3 +io.quarkus:quarkus-jaxp::jar:3.4.3 +io.quarkus:quarkus-jsonp::jar:3.4.3 +io.quarkus:quarkus-kubernetes-spi::jar:3.4.3 +io.quarkus:quarkus-liquibase::jar:3.4.3 +io.quarkus:quarkus-micrometer-registry-prometheus::jar:3.4.3 +io.quarkus:quarkus-micrometer::jar:3.4.3 +io.quarkus:quarkus-mutiny-deployment::jar:3.4.3 +io.quarkus:quarkus-mutiny::jar:3.4.3 +io.quarkus:quarkus-narayana-jta::jar:3.4.3 +io.quarkus:quarkus-netty-deployment::jar:3.4.3 +io.quarkus:quarkus-netty::jar:3.4.3 +io.quarkus:quarkus-panache-common::jar:3.4.3 +io.quarkus:quarkus-panache-hibernate-common::jar:3.4.3 +io.quarkus:quarkus-rest-client-config::jar:3.4.3 +io.quarkus:quarkus-rest-client-jackson::jar:3.4.3 +io.quarkus:quarkus-rest-client::jar:3.4.3 +io.quarkus:quarkus-resteasy-common::jar:3.4.3 +io.quarkus:quarkus-resteasy-reactive-common::jar:3.4.3 +io.quarkus:quarkus-resteasy-reactive-jackson-common::jar:3.4.3 +io.quarkus:quarkus-resteasy-reactive-jackson::jar:3.4.3 +io.quarkus:quarkus-resteasy-reactive::jar:3.4.3 +io.quarkus:quarkus-security-runtime-spi::jar:3.4.3 +io.quarkus:quarkus-smallrye-context-propagation-deployment::jar:3.4.3 +io.quarkus:quarkus-smallrye-context-propagation-spi::jar:3.4.3 +io.quarkus:quarkus-smallrye-context-propagation::jar:3.4.3 +io.quarkus:quarkus-smallrye-health::jar:3.4.3 +io.quarkus:quarkus-smallrye-openapi::jar:3.4.3 +io.quarkus:quarkus-swagger-ui::jar:3.4.3 +io.quarkus:quarkus-transaction-annotations::jar:3.4.3 +io.quarkus:quarkus-vertx-deployment::jar:3.4.3 +io.quarkus:quarkus-vertx-http-deployment-spi::jar:3.4.3 +io.quarkus:quarkus-vertx-http-deployment::jar:3.4.3 +io.quarkus:quarkus-vertx-http-dev-console-runtime-spi::jar:3.4.3 +io.quarkus:quarkus-vertx-http-dev-console-spi::jar:3.4.3 +io.quarkus:quarkus-vertx-http-dev-ui-resources::jar:3.4.3 +io.quarkus:quarkus-vertx-http-dev-ui-spi::jar:3.4.3 +io.quarkus:quarkus-vertx-http::jar:3.4.3 +io.quarkus:quarkus-vertx-latebound-mdc-provider::jar:3.4.3 +io.quarkus:quarkus-vertx::jar:3.4.3 +io.quarkus:quarkus-virtual-threads-deployment::jar:3.4.3 +io.quarkus:quarkus-virtual-threads::jar:3.4.3 +io.smallrye.common:smallrye-common-annotation::jar:2.1.2 +io.smallrye.common:smallrye-common-classloader::jar:2.1.0 +io.smallrye.common:smallrye-common-constraint::jar:2.1.2 +io.smallrye.common:smallrye-common-cpu::jar:2.1.0 +io.smallrye.common:smallrye-common-expression::jar:2.1.0 +io.smallrye.common:smallrye-common-function::jar:2.1.0 +io.smallrye.common:smallrye-common-io::jar:2.1.2 +io.smallrye.common:smallrye-common-net::jar:2.1.0 +io.smallrye.common:smallrye-common-os::jar:2.1.2 +io.smallrye.common:smallrye-common-ref::jar:2.1.0 +io.smallrye.common:smallrye-common-vertx-context::jar:2.1.2 +io.smallrye.config:smallrye-config-common::jar:3.3.4 +io.smallrye.config:smallrye-config-core::jar:3.3.4 +io.smallrye.config:smallrye-config-validator::jar:3.3.4 +io.smallrye.config:smallrye-config::jar:3.3.4 +io.smallrye.reactive:mutiny-smallrye-context-propagation::jar:2.3.1 +io.smallrye.reactive:mutiny-zero-flow-adapters::jar:1.0.0 +io.smallrye.reactive:mutiny::jar:2.3.1 +io.smallrye.reactive:smallrye-mutiny-vertx-auth-common::jar:3.5.0 +io.smallrye.reactive:smallrye-mutiny-vertx-bridge-common::jar:3.5.0 +io.smallrye.reactive:smallrye-mutiny-vertx-core::jar:3.5.0 +io.smallrye.reactive:smallrye-mutiny-vertx-runtime::jar:3.5.0 +io.smallrye.reactive:smallrye-mutiny-vertx-uri-template::jar:3.5.0 +io.smallrye.reactive:smallrye-mutiny-vertx-web-common::jar:3.5.0 +io.smallrye.reactive:smallrye-mutiny-vertx-web::jar:3.5.0 +io.smallrye.reactive:smallrye-reactive-converter-api::jar:3.0.0 +io.smallrye.reactive:smallrye-reactive-converter-mutiny::jar:3.0.0 +io.smallrye.reactive:vertx-mutiny-generator::jar:3.5.0 +io.smallrye:jandex::jar:3.1.5 +io.smallrye:smallrye-context-propagation-api::jar:2.1.0 +io.smallrye:smallrye-context-propagation-jta::jar:2.1.0 +io.smallrye:smallrye-context-propagation-storage::jar:2.1.0 +io.smallrye:smallrye-context-propagation::jar:2.1.0 +io.smallrye:smallrye-fault-tolerance-vertx::jar:6.2.6 +io.smallrye:smallrye-health-api::jar:4.0.4 +io.smallrye:smallrye-health-provided-checks::jar:4.0.4 +io.smallrye:smallrye-health::jar:4.0.4 +io.smallrye:smallrye-open-api-core::jar:3.5.2 +io.vertx:vertx-auth-common::jar:4.4.5 +io.vertx:vertx-bridge-common::jar:4.4.5 +io.vertx:vertx-codegen::jar:4.4.4 +io.vertx:vertx-core::jar:4.4.5 +io.vertx:vertx-uri-template::jar:4.4.4 +io.vertx:vertx-web-common::jar:4.4.5 +io.vertx:vertx-web::jar:4.4.5 +jakarta.activation:jakarta.activation-api::jar:2.1.2 +jakarta.annotation:jakarta.annotation-api::jar:2.1.1 +jakarta.ejb:jakarta.ejb-api::jar:4.0.1 +jakarta.el:jakarta.el-api::jar:5.0.0 +jakarta.enterprise:jakarta.enterprise.cdi-api::jar:4.0.1 +jakarta.enterprise:jakarta.enterprise.lang-model::jar:4.0.1 +jakarta.inject:jakarta.inject-api::jar:2.0.1 +jakarta.interceptor:jakarta.interceptor-api::jar:2.1.0 +jakarta.json:jakarta.json-api::jar:2.1.3 +jakarta.persistence:jakarta.persistence-api::jar:3.1.0 +jakarta.resource:jakarta.resource-api::jar:2.0.0 +jakarta.servlet:jakarta.servlet-api::jar:6.0.0 +jakarta.transaction:jakarta.transaction-api::jar:2.0.1 +jakarta.validation:jakarta.validation-api::jar:3.0.2 +jakarta.ws.rs:jakarta.ws.rs-api::jar:3.1.0 +jakarta.xml.bind:jakarta.xml.bind-api::jar:4.0.1 +net.bytebuddy:byte-buddy::jar:1.14.9 +org.aesh:aesh::jar:2.7 +org.aesh:readline::jar:2.4 +org.antlr:antlr4-runtime::jar:4.10.1 +org.apache.commons:commons-collections4::jar:4.4 +org.apache.commons:commons-lang3::jar:3.14.0 +org.apache.commons:commons-text::jar:1.9 +org.apache.httpcomponents:httpasyncclient::jar:4.1.5 +org.apache.httpcomponents:httpclient::jar:4.5.14 +org.apache.httpcomponents:httpcore-nio::jar:4.4.16 +org.apache.httpcomponents:httpcore::jar:4.4.16 +org.apache.logging.log4j:log4j-api::jar:2.23.1 +org.apache.logging.log4j:log4j-core::jar:2.23.1 +org.apiguardian:apiguardian-api::jar:1.1.2 +org.eclipse.angus:angus-activation::jar:2.0.1 +org.eclipse.microprofile.config:microprofile-config-api::jar:3.0.2 +org.eclipse.microprofile.context-propagation:microprofile-context-propagation-api::jar:1.3 +org.eclipse.microprofile.health:microprofile-health-api::jar:4.0.1 +org.eclipse.microprofile.openapi:microprofile-openapi-api::jar:3.1 +org.eclipse.microprofile.reactive-streams-operators:microprofile-reactive-streams-operators-api::jar:3.0 +org.eclipse.microprofile.rest.client:microprofile-rest-client-api::jar:3.0.1 +org.eclipse.parsson:parsson::jar:1.1.4 +org.eclipse.sisu:org.eclipse.sisu.inject::jar:0.3.5 +org.freemarker:freemarker::jar:2.3.32 +org.fusesource.jansi:jansi::jar:2.4.0 +org.glassfish.expressly:expressly::jar:5.0.0 +org.glassfish.jaxb:jaxb-core::jar:4.0.3 +org.glassfish.jaxb:jaxb-runtime::jar:4.0.3 +org.glassfish.jaxb:txw2::jar:4.0.3 +org.graalvm.sdk:graal-sdk::jar:23.0.1 +org.hdrhistogram:HdrHistogram::jar:2.1.12 +org.hibernate.common:hibernate-commons-annotations::jar:6.0.6.Final +org.hibernate.orm:hibernate-community-dialects::jar:6.2.13.Final +org.hibernate.orm:hibernate-core::jar:6.2.13.Final +org.hibernate.orm:hibernate-graalvm::jar:6.2.13.Final +org.hibernate.validator:hibernate-validator::jar:8.0.1.Final +org.hibernate:quarkus-local-cache::jar:0.2.1 +org.jboss.invocation:jboss-invocation::jar:2.0.0.Final +org.jboss.logging:commons-logging-jboss-logging::jar:1.0.0.Final +org.jboss.logging:jboss-logging-annotations::jar:2.2.1.Final +org.jboss.logging:jboss-logging::jar:3.4.3.Final +org.jboss.logmanager:jboss-logmanager::jar:3.0.2.Final +org.jboss.narayana.jta:narayana-jta::jar:7.0.0.Final +org.jboss.narayana.jts:narayana-jts-integration::jar:7.0.0.Final +org.jboss.resteasy.microprofile:microprofile-config::jar:2.1.4.Final +org.jboss.resteasy.microprofile:microprofile-rest-client-base::jar:2.1.4.Final +org.jboss.resteasy.microprofile:microprofile-rest-client::jar:2.1.4.Final +org.jboss.resteasy:resteasy-client-api::jar:6.2.5.Final +org.jboss.resteasy:resteasy-client::jar:6.2.5.Final +org.jboss.resteasy:resteasy-core-spi::jar:6.2.5.Final +org.jboss.resteasy:resteasy-core::jar:6.2.5.Final +org.jboss.resteasy:resteasy-jackson2-provider::jar:6.2.5.Final +org.jboss.slf4j:slf4j-jboss-logmanager::jar:2.0.0.Final +org.jboss.threads:jboss-threads::jar:3.5.0.Final +org.jboss:jboss-transaction-spi::jar:8.0.0.Final +org.jetbrains:annotations::jar:24.1.0 +org.junit.jupiter:junit-jupiter-api::jar:5.9.3 +org.junit.jupiter:junit-jupiter-engine::jar:5.9.3 +org.junit.jupiter:junit-jupiter-params::jar:5.9.3 +org.junit.jupiter:junit-jupiter::jar:5.9.3 +org.junit.platform:junit-platform-commons::jar:1.9.3 +org.junit.platform:junit-platform-engine::jar:1.9.3 +org.junit.platform:junit-platform-launcher::jar:1.9.3 +org.latencyutils:LatencyUtils::jar:2.0.3 +org.liquibase:liquibase-core::jar:4.20.0 +org.mvnpm.at.lit-labs:ssr-dom-shim::jar:1.1.1 +org.mvnpm.at.lit:reactive-element::jar:1.6.3 +org.mvnpm.at.mvnpm:vaadin-webcomponents::jar:24.1.6 +org.mvnpm.at.open-wc:dedupe-mixin::jar:1.4.0 +org.mvnpm.at.polymer:polymer::jar:3.5.1 +org.mvnpm.at.types:trusted-types::jar:2.0.3 +org.mvnpm.at.vaadin:router::jar:1.7.5 +org.mvnpm.at.vaadin:vaadin-development-mode-detector::jar:2.0.6 +org.mvnpm.at.vaadin:vaadin-usage-statistics::jar:2.1.2 +org.mvnpm.at.vanillawc:wc-codemirror::jar:2.1.0 +org.mvnpm.at.webcomponents:shadycss::jar:1.11.2 +org.mvnpm:echarts::jar:5.4.3 +org.mvnpm:es-module-shims::jar:1.8.0 +org.mvnpm:lit-element-state::jar:1.7.0 +org.mvnpm:lit-element::jar:3.3.3 +org.mvnpm:lit-html::jar:2.8.0 +org.mvnpm:lit::jar:2.8.0 +org.mvnpm:path-to-regexp::jar:2.4.0 +org.mvnpm:tslib::jar:2.3.0 +org.mvnpm:zrender::jar:5.4.4 +org.opentest4j:opentest4j::jar:1.2.0 +org.osgi:osgi.core::jar:6.0.0 +org.ow2.asm:asm-analysis::jar:9.5 +org.ow2.asm:asm-commons::jar:9.5 +org.ow2.asm:asm-tree::jar:9.5 +org.ow2.asm:asm-util::jar:9.5 +org.ow2.asm:asm::jar:9.5 +org.reactivestreams:reactive-streams::jar:1.0.4 +org.slf4j:slf4j-api::jar:2.0.9 +org.springframework:spring-core::jar:6.1.6 +org.springframework:spring-jcl::jar:6.0.13 +org.unbescape:unbescape::jar:1.1.6.RELEASE +org.wildfly.common:wildfly-common::jar:1.5.4.Final-format-001 +org.xerial:sqlite-jdbc::jar:3.41.2.2 +org.yaml:snakeyaml::jar:1.33 diff --git a/api-server/target/quarkus-app/quarkus/quarkus-application.dat b/api-server/target/quarkus-app/quarkus/quarkus-application.dat new file mode 100644 index 0000000..2a72b4a Binary files /dev/null and b/api-server/target/quarkus-app/quarkus/quarkus-application.dat differ diff --git a/api-server/target/quarkus-artifact.properties b/api-server/target/quarkus-artifact.properties new file mode 100644 index 0000000..5331494 --- /dev/null +++ b/api-server/target/quarkus-artifact.properties @@ -0,0 +1,4 @@ +#Generated by Quarkus - Do not edit manually +#Sun May 12 16:25:48 CEST 2024 +path=quarkus-app/quarkus-run.jar +type=jar diff --git a/cli/.DS_Store b/cli/.DS_Store new file mode 100644 index 0000000..4924e66 Binary files /dev/null and b/cli/.DS_Store differ diff --git a/cli/cli.iml b/cli/cli.iml new file mode 100644 index 0000000..448dd89 --- /dev/null +++ b/cli/cli.iml @@ -0,0 +1,10 @@ + + + + + + + + + + \ No newline at end of file diff --git a/cli/pom.xml b/cli/pom.xml new file mode 100644 index 0000000..c22453f --- /dev/null +++ b/cli/pom.xml @@ -0,0 +1,224 @@ + + 4.0.0 + cli + 1.0-SNAPSHOT + cli + CLI for ResourceTracker + + + com.repoachiever + base + 1.0-SNAPSHOT + + + + + com.repoachiever.CLI + + + + + + + org.springframework.boot + spring-boot-starter-validation + + + org.springframework.boot + spring-boot-starter-webflux + + + + + io.netty + netty-resolver-dns-native-macos + osx-aarch_64 + + + + + io.swagger.core.v3 + swagger-annotations + + + io.swagger.core.v3 + swagger-models + + + org.openapitools + jackson-databind-nullable + + + + + info.picocli + picocli + + + info.picocli + picocli-spring-boot-starter + + + + + jakarta.annotation + jakarta.annotation-api + + + jakarta.validation + jakarta.validation-api + + + jakarta.servlet + jakarta.servlet-api + + + jakarta.ws.rs + jakarta.ws.rs-api + + + org.projectlombok + lombok + + + org.yaml + snakeyaml + + + com.fasterxml.jackson.dataformat + jackson-dataformat-yaml + + + com.opencsv + opencsv + + + commons-io + commons-io + + + me.tongfei + progressbar + + + + + org.apache.logging.log4j + log4j-api + + + org.apache.logging.log4j + log4j-core + + + + + junit + junit + + + + + cli + + + org.springframework.boot + spring-boot-maven-plugin + + + org.apache.maven.plugins + maven-compiler-plugin + + + com.diffplug.spotless + spotless-maven-plugin + + + com.coderplus.maven.plugins + copy-rename-maven-plugin + + + + ${basedir}/target/cli.jar + ${main.basedir}/../bin/cli/cli.jar + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + build-info + + + + + + + + org.openapitools + openapi-generator-maven-plugin + + + + generate + + + ${project.basedir}/../api-server/src/main/openapi/openapi.yml + java + webclient + ${default.package}.api + ${default.package}.model + true + false + false + false + false + false + true + false + + @lombok.Data @lombok.AllArgsConstructor(staticName = "of") + src/main/java + true + false + true + true + false + true + java8 + true + true + + + + + + + pl.project13.maven + git-commit-id-plugin + + + + + + + dev + + true + + + dev + + + + prod + + prod + + + + diff --git a/cli/src/.DS_Store b/cli/src/.DS_Store new file mode 100644 index 0000000..d1aacd2 Binary files /dev/null and b/cli/src/.DS_Store differ diff --git a/cli/src/main/java/com/repoachiever/App.java b/cli/src/main/java/com/repoachiever/App.java new file mode 100644 index 0000000..b762dd6 --- /dev/null +++ b/cli/src/main/java/com/repoachiever/App.java @@ -0,0 +1,94 @@ +package com.repoachiever; + +import com.repoachiever.entity.PropertiesEntity; +import com.repoachiever.service.client.command.*; +import com.repoachiever.service.command.BaseCommandService; +// import com.repoachiever.service.KafkaConsumerWrapper; +import com.repoachiever.service.command.external.start.StartExternalCommandService; +import com.repoachiever.service.command.external.start.provider.aws.AWSStartExternalCommandService; +import com.repoachiever.service.command.external.state.StateExternalCommandService; +import com.repoachiever.service.command.external.state.provider.aws.AWSStateExternalCommandService; +import com.repoachiever.service.command.external.stop.StopExternalCommandService; +import com.repoachiever.service.command.external.stop.provider.aws.AWSStopExternalCommandService; +import com.repoachiever.service.command.external.version.VersionExternalCommandService; +import com.repoachiever.service.command.internal.health.HealthCheckInternalCommandService; +import com.repoachiever.service.command.internal.readiness.ReadinessCheckInternalCommandService; +import com.repoachiever.service.command.internal.readiness.provider.aws.AWSReadinessCheckInternalCommandService; +import com.repoachiever.service.config.ConfigService; +import com.repoachiever.service.config.common.ValidConfigService; +import com.repoachiever.service.visualization.VisualizationService; +import com.repoachiever.service.visualization.common.label.StartCommandVisualizationLabel; +import com.repoachiever.service.visualization.common.label.StateCommandVisualizationLabel; +import com.repoachiever.service.visualization.common.label.StopCommandVisualizationLabel; +import com.repoachiever.service.visualization.common.label.VersionCommandVisualizationLabel; +import com.repoachiever.service.visualization.state.VisualizationState; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.ApplicationArguments; +import org.springframework.boot.ApplicationRunner; +import org.springframework.boot.ExitCodeGenerator; +import org.springframework.boot.info.BuildProperties; +import org.springframework.context.annotation.Import; +import org.springframework.stereotype.Component; +import picocli.CommandLine; + +@Component +@Import({ + BaseCommandService.class, + StartExternalCommandService.class, + AWSStateExternalCommandService.class, + StateExternalCommandService.class, + StopExternalCommandService.class, + VersionExternalCommandService.class, + HealthCheckInternalCommandService.class, + ReadinessCheckInternalCommandService.class, + AWSReadinessCheckInternalCommandService.class, + ApplyClientCommandService.class, + DestroyClientCommandService.class, + HealthCheckClientCommandService.class, + ReadinessCheckClientCommandService.class, + LogsClientCommandService.class, + ScriptAcquireClientCommandService.class, + SecretsAcquireClientCommandService.class, + VersionClientCommandService.class, + AWSStartExternalCommandService.class, + AWSStopExternalCommandService.class, + ConfigService.class, + ValidConfigService.class, + BuildProperties.class, + PropertiesEntity.class, + StartCommandVisualizationLabel.class, + StopCommandVisualizationLabel.class, + StateCommandVisualizationLabel.class, + VersionCommandVisualizationLabel.class, + VisualizationService.class, + VisualizationState.class +}) +public class App implements ApplicationRunner, ExitCodeGenerator { + private static final Logger logger = LogManager.getLogger(App.class); + + private int exitCode; + + @Autowired private ValidConfigService validConfigService; + + @Autowired private BaseCommandService baseCommandService; + + @Override + public void run(ApplicationArguments args) { + try { + validConfigService.validate(); + } catch (Exception e) { + logger.fatal(e.getMessage()); + return; + } + + CommandLine cmd = new CommandLine(baseCommandService); + exitCode = cmd.execute(args.getSourceArgs()); + } + + @Override + public int getExitCode() { + return exitCode; + } +} diff --git a/cli/src/main/java/com/repoachiever/CLI.java b/cli/src/main/java/com/repoachiever/CLI.java new file mode 100644 index 0000000..02ee6ef --- /dev/null +++ b/cli/src/main/java/com/repoachiever/CLI.java @@ -0,0 +1,13 @@ +package com.repoachiever; + +import com.repoachiever.service.client.command.*; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +public class CLI { + public static void main(String[] args) { + SpringApplication application = new SpringApplication(App.class); + System.exit(SpringApplication.exit(application.run(args))); + } +} diff --git a/cli/src/main/java/com/repoachiever/converter/CredentialsConverter.java b/cli/src/main/java/com/repoachiever/converter/CredentialsConverter.java new file mode 100644 index 0000000..e6abb88 --- /dev/null +++ b/cli/src/main/java/com/repoachiever/converter/CredentialsConverter.java @@ -0,0 +1,11 @@ +package com.repoachiever.converter; + +import com.fasterxml.jackson.databind.ObjectMapper; + +public class CredentialsConverter { + @SuppressWarnings("unchecked") + public static T convert(Object input, Class stub) { + ObjectMapper mapper = new ObjectMapper(); + return mapper.convertValue(input, stub); + } +} diff --git a/cli/src/main/java/com/repoachiever/dto/ValidationScriptApplicationDto.java b/cli/src/main/java/com/repoachiever/dto/ValidationScriptApplicationDto.java new file mode 100644 index 0000000..1749e85 --- /dev/null +++ b/cli/src/main/java/com/repoachiever/dto/ValidationScriptApplicationDto.java @@ -0,0 +1,12 @@ +package com.repoachiever.dto; + +import java.util.List; +import lombok.AllArgsConstructor; +import lombok.Getter; + +/** Represents script validation application used for script acquiring process. */ +@Getter +@AllArgsConstructor(staticName = "of") +public class ValidationScriptApplicationDto { + private List fileContent; +} diff --git a/cli/src/main/java/com/repoachiever/dto/ValidationSecretsApplicationDto.java b/cli/src/main/java/com/repoachiever/dto/ValidationSecretsApplicationDto.java new file mode 100644 index 0000000..288b23c --- /dev/null +++ b/cli/src/main/java/com/repoachiever/dto/ValidationSecretsApplicationDto.java @@ -0,0 +1,14 @@ +package com.repoachiever.dto; + +import com.repoachiever.model.Provider; +import lombok.AllArgsConstructor; +import lombok.Getter; + +/** Represents secrets validation application used for secrets acquiring process. */ +@Getter +@AllArgsConstructor(staticName = "of") +public class ValidationSecretsApplicationDto { + private Provider provider; + + private String filePath; +} diff --git a/cli/src/main/java/com/repoachiever/dto/VisualizationLabelDto.java b/cli/src/main/java/com/repoachiever/dto/VisualizationLabelDto.java new file mode 100644 index 0000000..b971900 --- /dev/null +++ b/cli/src/main/java/com/repoachiever/dto/VisualizationLabelDto.java @@ -0,0 +1,26 @@ +package com.repoachiever.dto; + +import lombok.AllArgsConstructor; + +/** */ +@AllArgsConstructor(staticName = "of") +public class VisualizationLabelDto { + private final String message; + + private final Integer percentage; + + @Override + public String toString() { + int filledLength = (int) Math.round((double) percentage / 100 * 20); + int emptyLength = 20 - filledLength; + + return "[" + + "#".repeat(Math.max(0, filledLength)) + + "-".repeat(Math.max(0, emptyLength)) + + "] " + + percentage + + "%" + + " " + + message; + } +} diff --git a/cli/src/main/java/com/repoachiever/entity/ConfigEntity.java b/cli/src/main/java/com/repoachiever/entity/ConfigEntity.java new file mode 100644 index 0000000..ee87ff5 --- /dev/null +++ b/cli/src/main/java/com/repoachiever/entity/ConfigEntity.java @@ -0,0 +1,72 @@ +package com.repoachiever.entity; + +import com.fasterxml.jackson.annotation.JsonFormat; +import com.fasterxml.jackson.annotation.JsonProperty; +import jakarta.validation.Valid; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import jakarta.validation.constraints.Pattern; +import java.util.List; +import lombok.Getter; +import lombok.NoArgsConstructor; + +/** Represents configuration model used for ResourceTracker deployment operation. */ +@Getter +public class ConfigEntity { + /** Represents request to be executed in remote environment. */ + @Getter + public static class Request { + @NotBlank public String name; + + @Pattern(regexp = "^(((./)?)|((~/.)?)|((/?))?)([a-zA-Z/]*)((\\.([a-z]+))?)$") + public String file; + + @Pattern( + regexp = + "(((([0-9]|[0-5][0-9])(-([0-9]|[0-5][0-9]))?,)*([0-9]|[0-5][0-9])(-([0-9]|[0-5][0-9]))?)|(([\\*]|[0-9]|[0-5][0-9])/([0-9]|[0-5][0-9]))|([\\?])|([\\*]))[\\s](((([0-9]|[0-5][0-9])(-([0-9]|[0-5][0-9]))?,)*([0-9]|[0-5][0-9])(-([0-9]|[0-5][0-9]))?)|(([\\*]|[0-9]|[0-5][0-9])/([0-9]|[0-5][0-9]))|([\\?])|([\\*]))[\\s](((([0-9]|[0-1][0-9]|[2][0-3])(-([0-9]|[0-1][0-9]|[2][0-3]))?,)*([0-9]|[0-1][0-9]|[2][0-3])(-([0-9]|[0-1][0-9]|[2][0-3]))?)|(([\\*]|[0-9]|[0-1][0-9]|[2][0-3])/([0-9]|[0-1][0-9]|[2][0-3]))|([\\?])|([\\*]))[\\s](((([1-9]|[0][1-9]|[1-2][0-9]|[3][0-1])(-([1-9]|[0][1-9]|[1-2][0-9]|[3][0-1]))?,)*([1-9]|[0][1-9]|[1-2][0-9]|[3][0-1])(-([1-9]|[0][1-9]|[1-2][0-9]|[3][0-1]))?(C)?)|(([1-9]|[0][1-9]|[1-2][0-9]|[3][0-1])/([1-9]|[0][1-9]|[1-2][0-9]|[3][0-1])(C)?)|(L(-[0-9])?)|(L(-[1-2][0-9])?)|(L(-[3][0-1])?)|(LW)|([1-9]W)|([1-3][0-9]W)|([\\?])|([\\*]))[\\s](((([1-9]|0[1-9]|1[0-2])(-([1-9]|0[1-9]|1[0-2]))?,)*([1-9]|0[1-9]|1[0-2])(-([1-9]|0[1-9]|1[0-2]))?)|(([1-9]|0[1-9]|1[0-2])/([1-9]|0[1-9]|1[0-2]))|(((JAN|FEB|MAR|APR|MAY|JUN|JUL|AUG|SEP|OCT|NOV|DEC)(-(JAN|FEB|MAR|APR|MAY|JUN|JUL|AUG|SEP|OCT|NOV|DEC))?,)*(JAN|FEB|MAR|APR|MAY|JUN|JUL|AUG|SEP|OCT|NOV|DEC)(-(JAN|FEB|MAR|APR|MAY|JUN|JUL|AUG|SEP|OCT|NOV|DEC))?)|((JAN|FEB|MAR|APR|MAY|JUN|JUL|AUG|SEP|OCT|NOV|DEC)/(JAN|FEB|MAR|APR|MAY|JUN|JUL|AUG|SEP|OCT|NOV|DEC))|([\\?])|([\\*]))[\\s]((([1-7](-([1-7]))?,)*([1-7])(-([1-7]))?)|([1-7]/([1-7]))|(((MON|TUE|WED|THU|FRI|SAT|SUN)(-(MON|TUE|WED|THU|FRI|SAT|SUN))?,)*(MON|TUE|WED|THU|FRI|SAT|SUN)(-(MON|TUE|WED|THU|FRI|SAT|SUN))?(C)?)|((MON|TUE|WED|THU|FRI|SAT|SUN)/(MON|TUE|WED|THU|FRI|SAT|SUN)(C)?)|(([1-7]|(MON|TUE|WED|THU|FRI|SAT|SUN))?(L|LW)?)|(([1-7]|MON|TUE|WED|THU|FRI|SAT|SUN)#([1-7])?)|([\\?])|([\\*]))([\\s]?(([\\*])?|(19[7-9][0-9])|(20[0-9][0-9]))?|" + + " (((19[7-9][0-9])|(20[0-9][0-9]))/((19[7-9][0-9])|(20[0-9][0-9])))?|" + + " ((((19[7-9][0-9])|(20[0-9][0-9]))(-((19[7-9][0-9])|(20[0-9][0-9])))?,)*((19[7-9][0-9])|(20[0-9][0-9]))(-((19[7-9][0-9])|(20[0-9][0-9])))?)?)") + public String frequency; + } + + @Valid @NotNull public List requests; + + /** + * Represents remove cloud infrastructure configuration properties used for further deployment + * related operations. + */ + @Getter + public static class Cloud { + @JsonFormat(shape = JsonFormat.Shape.OBJECT) + public enum Provider { + @JsonProperty("aws") + AWS, + } + + @NotNull public Provider provider; + + @Getter + @NoArgsConstructor + public static class AWSCredentials { + @Pattern(regexp = "^(((./)?)|((~/.)?)|((/?))?)([a-zA-Z/]*)((\\.([a-z]+))?)$") + public String file; + + @NotBlank public String region; + } + + @NotNull public Object credentials; + } + + @Valid @NotNull public Cloud cloud; + + /** Represents API Server configuration used for further connection establishment. */ + @Getter + public static class APIServer { + @NotBlank public String host; + } + + @Valid + @NotNull + @JsonProperty("api-server") + public APIServer apiServer; +} diff --git a/cli/src/main/java/com/repoachiever/entity/PropertiesEntity.java b/cli/src/main/java/com/repoachiever/entity/PropertiesEntity.java new file mode 100644 index 0000000..772f766 --- /dev/null +++ b/cli/src/main/java/com/repoachiever/entity/PropertiesEntity.java @@ -0,0 +1,79 @@ +package com.repoachiever.entity; + +import lombok.Getter; +import org.apache.commons.lang3.StringUtils; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.support.PropertySourcesPlaceholderConfigurer; +import org.springframework.core.io.ClassPathResource; + +/** Represents application properties used for application configuration. */ +@Getter +@Configuration +public class PropertiesEntity { + private static final String GIT_CONFIG_PROPERTIES_FILE = "git.properties"; + + @Value(value = "${git.commit.id.abbrev}") + private String gitCommitId; + + @Value(value = "${config.root}") + private String configRootPath; + + @Value(value = "${config.user.file}") + private String configUserFilePath; + + @Value(value = "${progress.visualization.period}") + private Integer progressVisualizationPeriod; + + @Value(value = "${progress.visualization.secrets-acquire-request}") + private String progressVisualizationSecretsAcquireRequestLabel; + + @Value(value = "${progress.visualization.script-acquire-request}") + private String progressVisualizationScriptAcquireRequestLabel; + + @Value(value = "${progress.visualization.apply-request}") + private String progressVisualizationApplyRequestLabel; + + @Value(value = "${progress.visualization.apply-response}") + private String progressVisualizationApplyResponseLabel; + + @Value(value = "${progress.visualization.destroy-request}") + private String progressVisualizationDestroyRequestLabel; + + @Value(value = "${progress.visualization.destroy-response}") + private String progressVisualizationDestroyResponseLabel; + + @Value(value = "${progress.visualization.state-request}") + private String progressVisualizationStateRequestLabel; + + @Value(value = "${progress.visualization.state-response}") + private String progressVisualizationStateResponseLabel; + + @Value(value = "${progress.visualization.version-info-request}") + private String progressVisualizationVersionInfoRequestLabel; + + @Value(value = "${progress.visualization.health-check-request}") + private String progressVisualizationHealthCheckRequestLabel; + + @Value(value = "${progress.visualization.readiness-check-request}") + private String progressVisualizationReadinessCheckRequestLabel; + + @Bean + private static PropertySourcesPlaceholderConfigurer placeholderConfigurer() { + PropertySourcesPlaceholderConfigurer propsConfig = new PropertySourcesPlaceholderConfigurer(); + propsConfig.setLocation(new ClassPathResource(GIT_CONFIG_PROPERTIES_FILE)); + propsConfig.setIgnoreResourceNotFound(true); + propsConfig.setIgnoreUnresolvablePlaceholders(true); + return propsConfig; + } + + /** + * Removes the last symbol in git commit id of the repository. + * + * @return chopped repository git commit id. + */ + public String getGitCommitId() { + return StringUtils.chop(gitCommitId); + } +} diff --git a/cli/src/main/java/com/repoachiever/exception/ApiServerException.java b/cli/src/main/java/com/repoachiever/exception/ApiServerException.java new file mode 100644 index 0000000..d89b655 --- /dev/null +++ b/cli/src/main/java/com/repoachiever/exception/ApiServerException.java @@ -0,0 +1,18 @@ +package com.repoachiever.exception; + +import java.io.IOException; +import java.util.Arrays; +import java.util.Formatter; + +public class ApiServerException extends IOException { + public ApiServerException() { + this(""); + } + + public ApiServerException(Object... message) { + super( + new Formatter() + .format("API Server exception: %s", Arrays.stream(message).toArray()) + .toString()); + } +} diff --git a/cli/src/main/java/com/repoachiever/exception/ApiServerNotAvailableException.java b/cli/src/main/java/com/repoachiever/exception/ApiServerNotAvailableException.java new file mode 100644 index 0000000..471bacb --- /dev/null +++ b/cli/src/main/java/com/repoachiever/exception/ApiServerNotAvailableException.java @@ -0,0 +1,18 @@ +package com.repoachiever.exception; + +import java.io.IOException; +import java.util.Arrays; +import java.util.Formatter; + +public class ApiServerNotAvailableException extends IOException { + public ApiServerNotAvailableException() { + this(""); + } + + public ApiServerNotAvailableException(Object... message) { + super( + new Formatter() + .format("API Server is not available: %s", Arrays.stream(message).toArray()) + .toString()); + } +} diff --git a/cli/src/main/java/com/repoachiever/exception/CloudCredentialsFileNotFoundException.java b/cli/src/main/java/com/repoachiever/exception/CloudCredentialsFileNotFoundException.java new file mode 100644 index 0000000..43961c6 --- /dev/null +++ b/cli/src/main/java/com/repoachiever/exception/CloudCredentialsFileNotFoundException.java @@ -0,0 +1,19 @@ +package com.repoachiever.exception; + +import java.io.IOException; +import java.util.Arrays; +import java.util.Formatter; + +public class CloudCredentialsFileNotFoundException extends IOException { + public CloudCredentialsFileNotFoundException() { + this(""); + } + + public CloudCredentialsFileNotFoundException(Object... message) { + super( + new Formatter() + .format( + "Given cloud credentials file is not found: %s", Arrays.stream(message).toArray()) + .toString()); + } +} diff --git a/cli/src/main/java/com/repoachiever/exception/CloudCredentialsValidationException.java b/cli/src/main/java/com/repoachiever/exception/CloudCredentialsValidationException.java new file mode 100644 index 0000000..713b34f --- /dev/null +++ b/cli/src/main/java/com/repoachiever/exception/CloudCredentialsValidationException.java @@ -0,0 +1,18 @@ +package com.repoachiever.exception; + +import java.io.IOException; +import java.util.Arrays; +import java.util.Formatter; + +public class CloudCredentialsValidationException extends IOException { + public CloudCredentialsValidationException() { + this(""); + } + + public CloudCredentialsValidationException(Object... message) { + super( + new Formatter() + .format("Given cloud credentials are not valid!: %s", Arrays.stream(message).toArray()) + .toString()); + } +} diff --git a/cli/src/main/java/com/repoachiever/exception/ConfigValidationException.java b/cli/src/main/java/com/repoachiever/exception/ConfigValidationException.java new file mode 100644 index 0000000..9b78871 --- /dev/null +++ b/cli/src/main/java/com/repoachiever/exception/ConfigValidationException.java @@ -0,0 +1,14 @@ +package com.repoachiever.exception; + +import java.io.IOException; +import java.util.Arrays; +import java.util.Formatter; + +public class ConfigValidationException extends IOException { + public ConfigValidationException(Object... message) { + super( + new Formatter() + .format("Config file content is not valid: %s", Arrays.stream(message).toArray()) + .toString()); + } +} diff --git a/cli/src/main/java/com/repoachiever/exception/ScriptDataFileNotFoundException.java b/cli/src/main/java/com/repoachiever/exception/ScriptDataFileNotFoundException.java new file mode 100644 index 0000000..753feee --- /dev/null +++ b/cli/src/main/java/com/repoachiever/exception/ScriptDataFileNotFoundException.java @@ -0,0 +1,18 @@ +package com.repoachiever.exception; + +import java.io.IOException; +import java.util.Arrays; +import java.util.Formatter; + +public class ScriptDataFileNotFoundException extends IOException { + public ScriptDataFileNotFoundException() { + this(""); + } + + public ScriptDataFileNotFoundException(Object... message) { + super( + new Formatter() + .format("Given explicit script file is not found: %s", Arrays.stream(message).toArray()) + .toString()); + } +} diff --git a/cli/src/main/java/com/repoachiever/exception/ScriptDataValidationException.java b/cli/src/main/java/com/repoachiever/exception/ScriptDataValidationException.java new file mode 100644 index 0000000..17250fb --- /dev/null +++ b/cli/src/main/java/com/repoachiever/exception/ScriptDataValidationException.java @@ -0,0 +1,19 @@ +package com.repoachiever.exception; + +import java.io.IOException; +import java.util.Arrays; +import java.util.Formatter; + +/** Represents exception, when given file is not valid. */ +public class ScriptDataValidationException extends IOException { + public ScriptDataValidationException() { + this(""); + } + + public ScriptDataValidationException(Object... message) { + super( + new Formatter() + .format("Given explicit script file is not valid: %s", Arrays.stream(message).toArray()) + .toString()); + } +} diff --git a/cli/src/main/java/com/repoachiever/exception/VersionMismatchException.java b/cli/src/main/java/com/repoachiever/exception/VersionMismatchException.java new file mode 100644 index 0000000..b8ffe86 --- /dev/null +++ b/cli/src/main/java/com/repoachiever/exception/VersionMismatchException.java @@ -0,0 +1,21 @@ +package com.repoachiever.exception; + +import java.io.IOException; +import java.util.Arrays; +import java.util.Formatter; + +/** Represents exception, when API Server version is different from the version of the client. */ +public class VersionMismatchException extends IOException { + public VersionMismatchException() { + this(""); + } + + public VersionMismatchException(Object... message) { + super( + new Formatter() + .format( + "API Server version is different from the version of the client: %s", + Arrays.stream(message).toArray()) + .toString()); + } +} diff --git a/cli/src/main/java/com/repoachiever/logging/FatalAppender.java b/cli/src/main/java/com/repoachiever/logging/FatalAppender.java new file mode 100644 index 0000000..53ff3e2 --- /dev/null +++ b/cli/src/main/java/com/repoachiever/logging/FatalAppender.java @@ -0,0 +1,48 @@ +package com.repoachiever.logging; + +import java.time.Instant; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; +import org.apache.logging.log4j.Level; +import org.apache.logging.log4j.core.Appender; +import org.apache.logging.log4j.core.Core; +import org.apache.logging.log4j.core.Filter; +import org.apache.logging.log4j.core.LogEvent; +import org.apache.logging.log4j.core.appender.AbstractAppender; +import org.apache.logging.log4j.core.config.plugins.Plugin; +import org.apache.logging.log4j.core.config.plugins.PluginAttribute; +import org.apache.logging.log4j.core.config.plugins.PluginElement; +import org.apache.logging.log4j.core.config.plugins.PluginFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.SpringApplication; +import org.springframework.context.ApplicationContext; +import org.springframework.stereotype.Component; + +@Component +@Plugin(name = "FatalAppender", category = Core.CATEGORY_NAME, elementType = Appender.ELEMENT_TYPE) +public class FatalAppender extends AbstractAppender { + @Autowired private ApplicationContext context; + + private ConcurrentMap eventMap = new ConcurrentHashMap<>(); + + @SuppressWarnings("deprecation") + protected FatalAppender(String name, Filter filter) { + super(name, filter, null); + } + + @PluginFactory + public static FatalAppender createAppender( + @PluginAttribute("name") String name, @PluginElement("Filter") Filter filter) { + return new FatalAppender(name, filter); + } + + @Override + public void append(LogEvent event) { + if (event.getLevel().equals(Level.FATAL)) { + SpringApplication.exit(context, () -> 1); + System.exit(1); + } + + eventMap.put(Instant.now().toString(), event); + } +} diff --git a/cli/src/main/java/com/repoachiever/service/client/IClientCommand.java b/cli/src/main/java/com/repoachiever/service/client/IClientCommand.java new file mode 100644 index 0000000..a81043d --- /dev/null +++ b/cli/src/main/java/com/repoachiever/service/client/IClientCommand.java @@ -0,0 +1,19 @@ +package com.repoachiever.service.client; + +import com.repoachiever.exception.ApiServerException; + +/** + * Represents external resource command interface. + * + * @param type of the command response. + * @param type of the command request. + */ +public interface IClientCommand { + /** + * Processes certain request for an external command. + * + * @param input input to be given as request body. + * @return command response. + */ + T process(K input) throws ApiServerException; +} diff --git a/cli/src/main/java/com/repoachiever/service/client/command/ApplyClientCommandService.java b/cli/src/main/java/com/repoachiever/service/client/command/ApplyClientCommandService.java new file mode 100644 index 0000000..02db9c4 --- /dev/null +++ b/cli/src/main/java/com/repoachiever/service/client/command/ApplyClientCommandService.java @@ -0,0 +1,44 @@ +package com.repoachiever.service.client.command; + +import com.repoachiever.ApiClient; +import com.repoachiever.api.TerraformResourceApi; +import com.repoachiever.exception.ApiServerException; +import com.repoachiever.exception.ApiServerNotAvailableException; +import com.repoachiever.model.TerraformDeploymentApplication; +import com.repoachiever.model.TerraformDeploymentApplicationResult; +import com.repoachiever.service.client.IClientCommand; +import com.repoachiever.service.config.ConfigService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.web.reactive.function.client.WebClientRequestException; +import org.springframework.web.reactive.function.client.WebClientResponseException; + +/** Represents apply client command service. */ +@Service +public class ApplyClientCommandService + implements IClientCommand< + TerraformDeploymentApplicationResult, TerraformDeploymentApplication> { + private final TerraformResourceApi terraformResourceApi; + + public ApplyClientCommandService(@Autowired ConfigService configService) { + ApiClient apiClient = + new ApiClient().setBasePath(configService.getConfig().getApiServer().getHost()); + + this.terraformResourceApi = new TerraformResourceApi(apiClient); + } + + /** + * @see IClientCommand + */ + @Override + public TerraformDeploymentApplicationResult process(TerraformDeploymentApplication input) + throws ApiServerException { + try { + return terraformResourceApi.v1TerraformApplyPost(input).block(); + } catch (WebClientResponseException e) { + throw new ApiServerException(e.getResponseBodyAsString()); + } catch (WebClientRequestException e) { + throw new ApiServerException(new ApiServerNotAvailableException(e.getMessage()).getMessage()); + } + } +} diff --git a/cli/src/main/java/com/repoachiever/service/client/command/DestroyClientCommandService.java b/cli/src/main/java/com/repoachiever/service/client/command/DestroyClientCommandService.java new file mode 100644 index 0000000..4a1d297 --- /dev/null +++ b/cli/src/main/java/com/repoachiever/service/client/command/DestroyClientCommandService.java @@ -0,0 +1,40 @@ +package com.repoachiever.service.client.command; + +import com.repoachiever.ApiClient; +import com.repoachiever.api.TerraformResourceApi; +import com.repoachiever.exception.ApiServerException; +import com.repoachiever.exception.ApiServerNotAvailableException; +import com.repoachiever.model.TerraformDestructionApplication; +import com.repoachiever.service.client.IClientCommand; +import com.repoachiever.service.config.ConfigService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.web.reactive.function.client.WebClientRequestException; +import org.springframework.web.reactive.function.client.WebClientResponseException; + +/** Represents destroy client command service. */ +@Service +public class DestroyClientCommandService + implements IClientCommand { + private final TerraformResourceApi terraformResourceApi; + + public DestroyClientCommandService(@Autowired ConfigService configService) { + ApiClient apiClient = + new ApiClient().setBasePath(configService.getConfig().getApiServer().getHost()); + + this.terraformResourceApi = new TerraformResourceApi(apiClient); + } + + /** + * @see IClientCommand + */ + public Void process(TerraformDestructionApplication input) throws ApiServerException { + try { + return terraformResourceApi.v1TerraformDestroyPost(input).block(); + } catch (WebClientResponseException e) { + throw new ApiServerException(e.getResponseBodyAsString()); + } catch (WebClientRequestException e) { + throw new ApiServerException(new ApiServerNotAvailableException(e.getMessage()).getMessage()); + } + } +} diff --git a/cli/src/main/java/com/repoachiever/service/client/command/HealthCheckClientCommandService.java b/cli/src/main/java/com/repoachiever/service/client/command/HealthCheckClientCommandService.java new file mode 100644 index 0000000..99ec67e --- /dev/null +++ b/cli/src/main/java/com/repoachiever/service/client/command/HealthCheckClientCommandService.java @@ -0,0 +1,39 @@ +package com.repoachiever.service.client.command; + +import com.repoachiever.ApiClient; +import com.repoachiever.api.HealthResourceApi; +import com.repoachiever.exception.ApiServerException; +import com.repoachiever.exception.ApiServerNotAvailableException; +import com.repoachiever.model.HealthCheckResult; +import com.repoachiever.service.client.IClientCommand; +import com.repoachiever.service.config.ConfigService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.web.reactive.function.client.WebClientRequestException; +import org.springframework.web.reactive.function.client.WebClientResponseException; + +/** Represents health check client command service. */ +@Service +public class HealthCheckClientCommandService implements IClientCommand { + private final HealthResourceApi healthResourceApi; + + public HealthCheckClientCommandService(@Autowired ConfigService configService) { + ApiClient apiClient = + new ApiClient().setBasePath(configService.getConfig().getApiServer().getHost()); + + this.healthResourceApi = new HealthResourceApi(apiClient); + } + + /** + * @see IClientCommand + */ + public HealthCheckResult process(Void input) throws ApiServerException { + try { + return healthResourceApi.v1HealthGet().block(); + } catch (WebClientResponseException e) { + throw new ApiServerException(e.getResponseBodyAsString()); + } catch (WebClientRequestException e) { + throw new ApiServerException(new ApiServerNotAvailableException(e.getMessage()).getMessage()); + } + } +} diff --git a/cli/src/main/java/com/repoachiever/service/client/command/LogsClientCommandService.java b/cli/src/main/java/com/repoachiever/service/client/command/LogsClientCommandService.java new file mode 100644 index 0000000..57dd5e8 --- /dev/null +++ b/cli/src/main/java/com/repoachiever/service/client/command/LogsClientCommandService.java @@ -0,0 +1,42 @@ +package com.repoachiever.service.client.command; + +import com.repoachiever.ApiClient; +import com.repoachiever.api.TopicResourceApi; +import com.repoachiever.exception.ApiServerException; +import com.repoachiever.exception.ApiServerNotAvailableException; +import com.repoachiever.model.TopicLogsApplication; +import com.repoachiever.model.TopicLogsResult; +import com.repoachiever.service.client.IClientCommand; +import com.repoachiever.service.config.ConfigService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.web.reactive.function.client.WebClientRequestException; +import org.springframework.web.reactive.function.client.WebClientResponseException; + +/** Represents logs topic client command service. */ +@Service +public class LogsClientCommandService + implements IClientCommand { + private final TopicResourceApi topicResourceApi; + + public LogsClientCommandService(@Autowired ConfigService configService) { + ApiClient apiClient = + new ApiClient().setBasePath(configService.getConfig().getApiServer().getHost()); + + this.topicResourceApi = new TopicResourceApi(apiClient); + } + + /** + * @see IClientCommand + */ + @Override + public TopicLogsResult process(TopicLogsApplication input) throws ApiServerException { + try { + return topicResourceApi.v1TopicLogsPost(input).block(); + } catch (WebClientResponseException e) { + throw new ApiServerException(e.getResponseBodyAsString()); + } catch (WebClientRequestException e) { + throw new ApiServerException(new ApiServerNotAvailableException(e.getMessage()).getMessage()); + } + } +} diff --git a/cli/src/main/java/com/repoachiever/service/client/command/ReadinessCheckClientCommandService.java b/cli/src/main/java/com/repoachiever/service/client/command/ReadinessCheckClientCommandService.java new file mode 100644 index 0000000..4ab1ea2 --- /dev/null +++ b/cli/src/main/java/com/repoachiever/service/client/command/ReadinessCheckClientCommandService.java @@ -0,0 +1,41 @@ +package com.repoachiever.service.client.command; + +import com.repoachiever.ApiClient; +import com.repoachiever.api.HealthResourceApi; +import com.repoachiever.exception.ApiServerException; +import com.repoachiever.exception.ApiServerNotAvailableException; +import com.repoachiever.model.ReadinessCheckApplication; +import com.repoachiever.model.ReadinessCheckResult; +import com.repoachiever.service.client.IClientCommand; +import com.repoachiever.service.config.ConfigService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.web.reactive.function.client.WebClientRequestException; +import org.springframework.web.reactive.function.client.WebClientResponseException; + +/** Represents readiness check client command service. */ +@Service +public class ReadinessCheckClientCommandService + implements IClientCommand { + private final HealthResourceApi healthResourceApi; + + public ReadinessCheckClientCommandService(@Autowired ConfigService configService) { + ApiClient apiClient = + new ApiClient().setBasePath(configService.getConfig().getApiServer().getHost()); + + this.healthResourceApi = new HealthResourceApi(apiClient); + } + + /** + * @see IClientCommand + */ + public ReadinessCheckResult process(ReadinessCheckApplication input) throws ApiServerException { + try { + return healthResourceApi.v1ReadinessPost(input).block(); + } catch (WebClientResponseException e) { + throw new ApiServerException(e.getResponseBodyAsString()); + } catch (WebClientRequestException e) { + throw new ApiServerException(new ApiServerNotAvailableException(e.getMessage()).getMessage()); + } + } +} diff --git a/cli/src/main/java/com/repoachiever/service/client/command/ScriptAcquireClientCommandService.java b/cli/src/main/java/com/repoachiever/service/client/command/ScriptAcquireClientCommandService.java new file mode 100644 index 0000000..2907b28 --- /dev/null +++ b/cli/src/main/java/com/repoachiever/service/client/command/ScriptAcquireClientCommandService.java @@ -0,0 +1,46 @@ +package com.repoachiever.service.client.command; + +import com.repoachiever.ApiClient; +import com.repoachiever.api.ValidationResourceApi; +import com.repoachiever.dto.ValidationScriptApplicationDto; +import com.repoachiever.exception.ApiServerException; +import com.repoachiever.exception.ApiServerNotAvailableException; +import com.repoachiever.model.ValidationScriptApplication; +import com.repoachiever.model.ValidationScriptApplicationResult; +import com.repoachiever.service.client.IClientCommand; +import com.repoachiever.service.config.ConfigService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.web.reactive.function.client.WebClientRequestException; +import org.springframework.web.reactive.function.client.WebClientResponseException; + +/** Represents script validation client command service. */ +@Service +public class ScriptAcquireClientCommandService + implements IClientCommand { + private final ValidationResourceApi validationResourceApi; + + public ScriptAcquireClientCommandService(@Autowired ConfigService configService) { + ApiClient apiClient = + new ApiClient().setBasePath(configService.getConfig().getApiServer().getHost()); + + this.validationResourceApi = new ValidationResourceApi(apiClient); + } + + /** + * @see IClientCommand + */ + @Override + public ValidationScriptApplicationResult process(ValidationScriptApplicationDto input) + throws ApiServerException { + try { + return validationResourceApi + .v1ScriptAcquirePost(ValidationScriptApplication.of(input.getFileContent())) + .block(); + } catch (WebClientResponseException e) { + throw new ApiServerException(e.getResponseBodyAsString()); + } catch (WebClientRequestException e) { + throw new ApiServerException(new ApiServerNotAvailableException(e.getMessage()).getMessage()); + } + } +} diff --git a/cli/src/main/java/com/repoachiever/service/client/command/SecretsAcquireClientCommandService.java b/cli/src/main/java/com/repoachiever/service/client/command/SecretsAcquireClientCommandService.java new file mode 100644 index 0000000..cc93841 --- /dev/null +++ b/cli/src/main/java/com/repoachiever/service/client/command/SecretsAcquireClientCommandService.java @@ -0,0 +1,66 @@ +package com.repoachiever.service.client.command; + +import com.repoachiever.ApiClient; +import com.repoachiever.api.ValidationResourceApi; +import com.repoachiever.dto.ValidationSecretsApplicationDto; +import com.repoachiever.exception.ApiServerException; +import com.repoachiever.exception.ApiServerNotAvailableException; +import com.repoachiever.exception.CloudCredentialsFileNotFoundException; +import com.repoachiever.model.Provider; +import com.repoachiever.model.ValidationSecretsApplication; +import com.repoachiever.model.ValidationSecretsApplicationResult; +import com.repoachiever.service.client.IClientCommand; +import com.repoachiever.service.config.ConfigService; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.web.reactive.function.client.WebClientRequestException; +import org.springframework.web.reactive.function.client.WebClientResponseException; + +/** Represents secrets validation client command service. */ +@Service +public class SecretsAcquireClientCommandService + implements IClientCommand { + private final ValidationResourceApi validationResourceApi; + + public SecretsAcquireClientCommandService(@Autowired ConfigService configService) { + ApiClient apiClient = + new ApiClient().setBasePath(configService.getConfig().getApiServer().getHost()); + + this.validationResourceApi = new ValidationResourceApi(apiClient); + } + + /** + * @see IClientCommand + */ + @Override + public ValidationSecretsApplicationResult process(ValidationSecretsApplicationDto input) + throws ApiServerException { + Path filePath = Paths.get(input.getFilePath()); + + if (Files.notExists(filePath)) { + throw new ApiServerException(new CloudCredentialsFileNotFoundException().getMessage()); + } + + String content; + + try { + content = Files.readString(filePath); + } catch (IOException e) { + throw new ApiServerException(e.getMessage()); + } + + try { + return validationResourceApi + .v1SecretsAcquirePost(ValidationSecretsApplication.of(Provider.AWS, content)) + .block(); + } catch (WebClientResponseException e) { + throw new ApiServerException(e.getResponseBodyAsString()); + } catch (WebClientRequestException e) { + throw new ApiServerException(new ApiServerNotAvailableException(e.getHeaders()).getMessage()); + } + } +} diff --git a/cli/src/main/java/com/repoachiever/service/client/command/VersionClientCommandService.java b/cli/src/main/java/com/repoachiever/service/client/command/VersionClientCommandService.java new file mode 100644 index 0000000..e80dec6 --- /dev/null +++ b/cli/src/main/java/com/repoachiever/service/client/command/VersionClientCommandService.java @@ -0,0 +1,39 @@ +package com.repoachiever.service.client.command; + +import com.repoachiever.ApiClient; +import com.repoachiever.api.InfoResourceApi; +import com.repoachiever.exception.ApiServerException; +import com.repoachiever.exception.ApiServerNotAvailableException; +import com.repoachiever.model.ApplicationInfoResult; +import com.repoachiever.service.client.IClientCommand; +import com.repoachiever.service.config.ConfigService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.web.reactive.function.client.WebClientRequestException; +import org.springframework.web.reactive.function.client.WebClientResponseException; + +/** Represents version information client command service. */ +@Service +public class VersionClientCommandService implements IClientCommand { + private final InfoResourceApi infoResourceApi; + + public VersionClientCommandService(@Autowired ConfigService configService) { + ApiClient apiClient = + new ApiClient().setBasePath(configService.getConfig().getApiServer().getHost()); + + this.infoResourceApi = new InfoResourceApi(apiClient); + } + + /** + * @see IClientCommand + */ + public ApplicationInfoResult process(Void input) throws ApiServerException { + try { + return infoResourceApi.v1InfoVersionGet().block(); + } catch (WebClientResponseException e) { + throw new ApiServerException(e.getResponseBodyAsString()); + } catch (WebClientRequestException e) { + throw new ApiServerException(new ApiServerNotAvailableException(e.getMessage()).getMessage()); + } + } +} diff --git a/cli/src/main/java/com/repoachiever/service/command/BaseCommandService.java b/cli/src/main/java/com/repoachiever/service/command/BaseCommandService.java new file mode 100644 index 0000000..85dcc37 --- /dev/null +++ b/cli/src/main/java/com/repoachiever/service/command/BaseCommandService.java @@ -0,0 +1,134 @@ +package com.repoachiever.service.command; + +import com.repoachiever.exception.ApiServerException; +import com.repoachiever.service.command.external.start.StartExternalCommandService; +import com.repoachiever.service.command.external.state.StateExternalCommandService; +import com.repoachiever.service.command.external.stop.StopExternalCommandService; +import com.repoachiever.service.command.external.version.VersionExternalCommandService; +import com.repoachiever.service.command.internal.health.HealthCheckInternalCommandService; +import com.repoachiever.service.command.internal.readiness.ReadinessCheckInternalCommandService; +import com.repoachiever.service.visualization.VisualizationService; +import com.repoachiever.service.visualization.common.label.StartCommandVisualizationLabel; +import com.repoachiever.service.visualization.common.label.StateCommandVisualizationLabel; +import com.repoachiever.service.visualization.common.label.StopCommandVisualizationLabel; +import com.repoachiever.service.visualization.common.label.VersionCommandVisualizationLabel; +import com.repoachiever.service.visualization.state.VisualizationState; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import picocli.CommandLine.Command; + +/** Represents general command management service. */ +@Service +@Command( + name = "help", + mixinStandardHelpOptions = true, + description = "Cloud-based remote resource tracker", + version = "1.0") +public class BaseCommandService { + private static final Logger logger = LogManager.getLogger(BaseCommandService.class); + + @Autowired private StartExternalCommandService startCommandService; + + @Autowired private StateExternalCommandService stateCommandService; + + @Autowired private StopExternalCommandService stopCommandService; + + @Autowired private VersionExternalCommandService versionCommandService; + + @Autowired private HealthCheckInternalCommandService healthCheckInternalCommandService; + + @Autowired private ReadinessCheckInternalCommandService readinessCheckInternalCommandService; + + @Autowired private StartCommandVisualizationLabel startCommandVisualizationLabel; + + @Autowired private StopCommandVisualizationLabel stopCommandVisualizationLabel; + + @Autowired private StateCommandVisualizationLabel stateCommandVisualizationLabel; + + @Autowired private VersionCommandVisualizationLabel versionCommandVisualizationLabel; + + @Autowired private VisualizationService visualizationService; + + @Autowired private VisualizationState visualizationState; + + /** Provides access to start command service. */ + @Command(description = "Start remote requests execution") + private void start() { + visualizationState.setLabel(startCommandVisualizationLabel); + + visualizationService.process(); + + try { + healthCheckInternalCommandService.process(); + + startCommandService.process(); + } catch (ApiServerException e) { + logger.fatal(e.getMessage()); + return; + } + + visualizationService.await(); + } + + /** Provides access to state command service. */ + @Command(description = "Retrieve state of remote requests executions") + private void state() { + visualizationState.setLabel(stateCommandVisualizationLabel); + + visualizationService.process(); + + try { + healthCheckInternalCommandService.process(); + readinessCheckInternalCommandService.process(); + + stateCommandService.process(); + } catch (ApiServerException e) { + logger.fatal(e.getMessage()); + return; + } + + visualizationService.await(); + } + + /** Provides access to stop command service. */ + @Command(description = "Stop remote requests execution") + private void stop() { + visualizationState.setLabel(stopCommandVisualizationLabel); + + visualizationService.process(); + + try { + healthCheckInternalCommandService.process(); + + stopCommandService.process(); + } catch (ApiServerException e) { + logger.fatal(e.getMessage()); + return; + } + + visualizationService.await(); + } + + /** Provides access to version command service. */ + @Command( + description = + "Retrieve version of ResourceTracker CLI and ResourceTracker API Server(if available)") + private void version() { + visualizationState.setLabel(versionCommandVisualizationLabel); + + visualizationService.process(); + + try { + healthCheckInternalCommandService.process(); + + versionCommandService.process(); + } catch (ApiServerException e) { + logger.fatal(e.getMessage()); + return; + } + + visualizationService.await(); + } +} diff --git a/cli/src/main/java/com/repoachiever/service/command/common/ICommand.java b/cli/src/main/java/com/repoachiever/service/command/common/ICommand.java new file mode 100644 index 0000000..0fc0e70 --- /dev/null +++ b/cli/src/main/java/com/repoachiever/service/command/common/ICommand.java @@ -0,0 +1,9 @@ +package com.repoachiever.service.command.common; + +import com.repoachiever.exception.ApiServerException; + +/** Represents common command interface. */ +public interface ICommand { + /** Processes certain request for an external command. */ + void process() throws ApiServerException; +} diff --git a/cli/src/main/java/com/repoachiever/service/command/external/start/StartExternalCommandService.java b/cli/src/main/java/com/repoachiever/service/command/external/start/StartExternalCommandService.java new file mode 100644 index 0000000..7e64cea --- /dev/null +++ b/cli/src/main/java/com/repoachiever/service/command/external/start/StartExternalCommandService.java @@ -0,0 +1,26 @@ +package com.repoachiever.service.command.external.start; + +import com.repoachiever.exception.ApiServerException; +import com.repoachiever.model.*; +import com.repoachiever.service.command.common.ICommand; +import com.repoachiever.service.command.external.start.provider.aws.AWSStartExternalCommandService; +import com.repoachiever.service.config.ConfigService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +/** Represents start external command service. */ +@Service +public class StartExternalCommandService implements ICommand { + @Autowired private ConfigService configService; + + @Autowired private AWSStartExternalCommandService awsStartExternalCommandService; + + /** + * @see ICommand + */ + public void process() throws ApiServerException { + switch (configService.getConfig().getCloud().getProvider()) { + case AWS -> awsStartExternalCommandService.process(); + } + } +} diff --git a/cli/src/main/java/com/repoachiever/service/command/external/start/provider/aws/AWSStartExternalCommandService.java b/cli/src/main/java/com/repoachiever/service/command/external/start/provider/aws/AWSStartExternalCommandService.java new file mode 100644 index 0000000..2926fdc --- /dev/null +++ b/cli/src/main/java/com/repoachiever/service/command/external/start/provider/aws/AWSStartExternalCommandService.java @@ -0,0 +1,120 @@ +package com.repoachiever.service.command.external.start.provider.aws; + +import com.repoachiever.converter.CredentialsConverter; +import com.repoachiever.dto.ValidationScriptApplicationDto; +import com.repoachiever.dto.ValidationSecretsApplicationDto; +import com.repoachiever.entity.ConfigEntity; +import com.repoachiever.entity.PropertiesEntity; +import com.repoachiever.exception.ApiServerException; +import com.repoachiever.exception.CloudCredentialsValidationException; +import com.repoachiever.exception.ScriptDataValidationException; +import com.repoachiever.exception.VersionMismatchException; +import com.repoachiever.model.*; +import com.repoachiever.service.client.command.ApplyClientCommandService; +import com.repoachiever.service.client.command.ScriptAcquireClientCommandService; +import com.repoachiever.service.client.command.SecretsAcquireClientCommandService; +import com.repoachiever.service.client.command.VersionClientCommandService; +import com.repoachiever.service.command.common.ICommand; +import com.repoachiever.service.config.ConfigService; +import com.repoachiever.service.visualization.state.VisualizationState; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Paths; +import java.util.List; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +/** Represents start external command service for AWS provider. */ +@Service +public class AWSStartExternalCommandService implements ICommand { + private static final Logger logger = LogManager.getLogger(AWSStartExternalCommandService.class); + + @Autowired private ConfigService configService; + + @Autowired private PropertiesEntity properties; + + @Autowired private ApplyClientCommandService applyClientCommandService; + + @Autowired private VersionClientCommandService versionClientCommandService; + + @Autowired private SecretsAcquireClientCommandService secretsAcquireClientCommandService; + + @Autowired private ScriptAcquireClientCommandService scriptAcquireClientCommandService; + + @Autowired private VisualizationState visualizationState; + + /** + * @see ICommand + */ + @Override + public void process() throws ApiServerException { + visualizationState.getLabel().pushNext(); + + ConfigEntity.Cloud.AWSCredentials credentials = + CredentialsConverter.convert( + configService.getConfig().getCloud().getCredentials(), + ConfigEntity.Cloud.AWSCredentials.class); + + ValidationSecretsApplicationDto validationSecretsApplicationDto = + ValidationSecretsApplicationDto.of(Provider.AWS, credentials.getFile()); + + ValidationSecretsApplicationResult validationSecretsApplicationResult = + secretsAcquireClientCommandService.process(validationSecretsApplicationDto); + + if (validationSecretsApplicationResult.getValid()) { + visualizationState.getLabel().pushNext(); + + ApplicationInfoResult applicationInfoResult = versionClientCommandService.process(null); + + if (!applicationInfoResult.getExternalApi().getHash().equals(properties.getGitCommitId())) { + throw new ApiServerException(new VersionMismatchException().getMessage()); + } + + List requests = + configService.getConfig().getRequests().stream() + .map( + element -> { + try { + return DeploymentRequest.of( + element.getName(), + Files.readString(Paths.get(element.getFile())), + element.getFrequency()); + } catch (IOException e) { + throw new RuntimeException(e); + } + }) + .toList(); + + ValidationScriptApplicationDto validationScriptApplicationDto = + ValidationScriptApplicationDto.of( + requests.stream().map(DeploymentRequest::getScript).toList()); + + ValidationScriptApplicationResult validationScriptApplicationResult = + scriptAcquireClientCommandService.process(validationScriptApplicationDto); + + if (validationScriptApplicationResult.getValid()) { + visualizationState.getLabel().pushNext(); + + CredentialsFields credentialsFields = + CredentialsFields.of( + AWSSecrets.of( + validationSecretsApplicationResult.getSecrets().getAccessKey(), + validationSecretsApplicationResult.getSecrets().getSecretKey()), + credentials.getRegion()); + + TerraformDeploymentApplication terraformDeploymentApplication = + TerraformDeploymentApplication.of(requests, Provider.AWS, credentialsFields); + + applyClientCommandService.process(terraformDeploymentApplication); + + visualizationState.getLabel().pushNext(); + } else { + logger.fatal(new ScriptDataValidationException().getMessage()); + } + } else { + logger.fatal(new CloudCredentialsValidationException().getMessage()); + } + } +} diff --git a/cli/src/main/java/com/repoachiever/service/command/external/state/StateExternalCommandService.java b/cli/src/main/java/com/repoachiever/service/command/external/state/StateExternalCommandService.java new file mode 100644 index 0000000..233e319 --- /dev/null +++ b/cli/src/main/java/com/repoachiever/service/command/external/state/StateExternalCommandService.java @@ -0,0 +1,25 @@ +package com.repoachiever.service.command.external.state; + +import com.repoachiever.exception.ApiServerException; +import com.repoachiever.service.command.common.ICommand; +import com.repoachiever.service.command.external.state.provider.aws.AWSStateExternalCommandService; +import com.repoachiever.service.config.ConfigService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +/** Represents state external command service. */ +@Service +public class StateExternalCommandService implements ICommand { + @Autowired private ConfigService configService; + + @Autowired private AWSStateExternalCommandService awsStateExternalCommandService; + + /** + * @see ICommand + */ + public void process() throws ApiServerException { + switch (configService.getConfig().getCloud().getProvider()) { + case AWS -> awsStateExternalCommandService.process(); + } + } +} diff --git a/cli/src/main/java/com/repoachiever/service/command/external/state/provider/aws/AWSStateExternalCommandService.java b/cli/src/main/java/com/repoachiever/service/command/external/state/provider/aws/AWSStateExternalCommandService.java new file mode 100644 index 0000000..f01cc5f --- /dev/null +++ b/cli/src/main/java/com/repoachiever/service/command/external/state/provider/aws/AWSStateExternalCommandService.java @@ -0,0 +1,86 @@ +package com.repoachiever.service.command.external.state.provider.aws; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.repoachiever.converter.CredentialsConverter; +import com.repoachiever.dto.ValidationSecretsApplicationDto; +import com.repoachiever.entity.ConfigEntity; +import com.repoachiever.exception.ApiServerException; +import com.repoachiever.exception.CloudCredentialsValidationException; +import com.repoachiever.model.*; +import com.repoachiever.service.client.command.LogsClientCommandService; +import com.repoachiever.service.client.command.SecretsAcquireClientCommandService; +import com.repoachiever.service.command.common.ICommand; +import com.repoachiever.service.config.ConfigService; +import com.repoachiever.service.visualization.state.VisualizationState; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +/** Represents start external command service for AWS provider. */ +@Service +public class AWSStateExternalCommandService implements ICommand { + private static final Logger logger = LogManager.getLogger(AWSStateExternalCommandService.class); + + @Autowired private ConfigService configService; + + @Autowired private SecretsAcquireClientCommandService secretsAcquireClientCommandService; + + @Autowired private LogsClientCommandService logsClientCommandService; + + @Autowired private VisualizationState visualizationState; + + /** + * @see ICommand + */ + @Override + public void process() throws ApiServerException { + visualizationState.getLabel().pushNext(); + + ConfigEntity.Cloud.AWSCredentials credentials = + CredentialsConverter.convert( + configService.getConfig().getCloud().getCredentials(), + ConfigEntity.Cloud.AWSCredentials.class); + + ValidationSecretsApplicationDto validationSecretsApplicationDto = + ValidationSecretsApplicationDto.of(Provider.AWS, credentials.getFile()); + + ValidationSecretsApplicationResult validationSecretsApplicationResult = + secretsAcquireClientCommandService.process(validationSecretsApplicationDto); + + if (validationSecretsApplicationResult.getValid()) { + visualizationState.getLabel().pushNext(); + + CredentialsFields credentialsFields = + CredentialsFields.of( + AWSSecrets.of( + validationSecretsApplicationResult.getSecrets().getAccessKey(), + validationSecretsApplicationResult.getSecrets().getSecretKey()), + credentials.getRegion()); + + TopicLogsResult topicLogsResult; + + TopicLogsApplication topicLogsApplication = + TopicLogsApplication.of(Provider.AWS, credentialsFields); + + topicLogsResult = logsClientCommandService.process(topicLogsApplication); + + visualizationState.getLabel().pushNext(); + + ObjectMapper mapper = new ObjectMapper(); + topicLogsResult + .getResult() + .forEach( + element -> { + try { + visualizationState.addResult(mapper.writeValueAsString(element)); + } catch (JsonProcessingException e) { + throw new RuntimeException(e); + } + }); + } else { + logger.fatal(new CloudCredentialsValidationException().getMessage()); + } + } +} diff --git a/cli/src/main/java/com/repoachiever/service/command/external/stop/StopExternalCommandService.java b/cli/src/main/java/com/repoachiever/service/command/external/stop/StopExternalCommandService.java new file mode 100644 index 0000000..1a393c7 --- /dev/null +++ b/cli/src/main/java/com/repoachiever/service/command/external/stop/StopExternalCommandService.java @@ -0,0 +1,26 @@ +package com.repoachiever.service.command.external.stop; + +import com.repoachiever.exception.ApiServerException; +import com.repoachiever.model.*; +import com.repoachiever.service.command.common.ICommand; +import com.repoachiever.service.command.external.stop.provider.aws.AWSStopExternalCommandService; +import com.repoachiever.service.config.ConfigService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +/** Represents stop external command service. */ +@Service +public class StopExternalCommandService implements ICommand { + @Autowired private ConfigService configService; + + @Autowired private AWSStopExternalCommandService stopExternalCommandService; + + /** + * @see ICommand + */ + public void process() throws ApiServerException { + switch (configService.getConfig().getCloud().getProvider()) { + case AWS -> stopExternalCommandService.process(); + } + } +} diff --git a/cli/src/main/java/com/repoachiever/service/command/external/stop/provider/aws/AWSStopExternalCommandService.java b/cli/src/main/java/com/repoachiever/service/command/external/stop/provider/aws/AWSStopExternalCommandService.java new file mode 100644 index 0000000..7677ed5 --- /dev/null +++ b/cli/src/main/java/com/repoachiever/service/command/external/stop/provider/aws/AWSStopExternalCommandService.java @@ -0,0 +1,80 @@ +package com.repoachiever.service.command.external.stop.provider.aws; + +import com.repoachiever.converter.CredentialsConverter; +import com.repoachiever.dto.ValidationSecretsApplicationDto; +import com.repoachiever.entity.ConfigEntity; +import com.repoachiever.exception.ApiServerException; +import com.repoachiever.exception.CloudCredentialsValidationException; +import com.repoachiever.model.AWSSecrets; +import com.repoachiever.model.CredentialsFields; +import com.repoachiever.model.DestructionRequest; +import com.repoachiever.model.Provider; +import com.repoachiever.model.TerraformDestructionApplication; +import com.repoachiever.model.ValidationSecretsApplicationResult; +import com.repoachiever.service.client.command.DestroyClientCommandService; +import com.repoachiever.service.client.command.SecretsAcquireClientCommandService; +import com.repoachiever.service.command.common.ICommand; +import com.repoachiever.service.config.ConfigService; +import com.repoachiever.service.visualization.state.VisualizationState; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +/** */ +@Service +public class AWSStopExternalCommandService implements ICommand { + private static final Logger logger = LogManager.getLogger(AWSStopExternalCommandService.class); + + @Autowired private ConfigService configService; + + @Autowired private DestroyClientCommandService destroyClientCommandService; + + @Autowired private SecretsAcquireClientCommandService secretsAcquireClientCommandService; + + @Autowired private VisualizationState visualizationState; + + /** + * @see ICommand + */ + @Override + public void process() throws ApiServerException { + visualizationState.getLabel().pushNext(); + + ConfigEntity.Cloud.AWSCredentials credentials = + CredentialsConverter.convert( + configService.getConfig().getCloud().getCredentials(), + ConfigEntity.Cloud.AWSCredentials.class); + + ValidationSecretsApplicationDto validationSecretsApplicationDto = + ValidationSecretsApplicationDto.of(Provider.AWS, credentials.getFile()); + + ValidationSecretsApplicationResult validationSecretsApplicationResult = + secretsAcquireClientCommandService.process(validationSecretsApplicationDto); + + if (validationSecretsApplicationResult.getValid()) { + visualizationState.getLabel().pushNext(); + + CredentialsFields credentialsFields = + CredentialsFields.of( + AWSSecrets.of( + validationSecretsApplicationResult.getSecrets().getAccessKey(), + validationSecretsApplicationResult.getSecrets().getSecretKey()), + credentials.getRegion()); + + TerraformDestructionApplication terraformDestructionApplication = + TerraformDestructionApplication.of( + configService.getConfig().getRequests().stream() + .map(element -> DestructionRequest.of(element.getName())) + .toList(), + Provider.AWS, + credentialsFields); + + destroyClientCommandService.process(terraformDestructionApplication); + + visualizationState.getLabel().pushNext(); + } else { + logger.fatal(new CloudCredentialsValidationException().getMessage()); + } + } +} diff --git a/cli/src/main/java/com/repoachiever/service/command/external/version/VersionExternalCommandService.java b/cli/src/main/java/com/repoachiever/service/command/external/version/VersionExternalCommandService.java new file mode 100644 index 0000000..54c7bb6 --- /dev/null +++ b/cli/src/main/java/com/repoachiever/service/command/external/version/VersionExternalCommandService.java @@ -0,0 +1,41 @@ +package com.repoachiever.service.command.external.version; + +import com.repoachiever.entity.PropertiesEntity; +import com.repoachiever.exception.ApiServerException; +import com.repoachiever.model.ApplicationInfoResult; +import com.repoachiever.service.client.command.HealthCheckClientCommandService; +import com.repoachiever.service.client.command.VersionClientCommandService; +import com.repoachiever.service.command.common.ICommand; +import com.repoachiever.service.visualization.state.VisualizationState; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +/** Represents version external command service. */ +@Service +public class VersionExternalCommandService implements ICommand { + @Autowired PropertiesEntity properties; + + @Autowired private VersionClientCommandService versionClientCommandService; + + @Autowired private HealthCheckClientCommandService healthCheckClientCommandService; + + @Autowired private VisualizationState visualizationState; + + /** + * @see ICommand + */ + public void process() throws ApiServerException { + visualizationState.getLabel().pushNext(); + + try { + ApplicationInfoResult applicationInfoResult = versionClientCommandService.process(null); + + visualizationState.addResult( + String.format( + "API Server version: %s", applicationInfoResult.getExternalApi().getHash())); + } finally { + visualizationState.addResult( + String.format("Client version: %s", properties.getGitCommitId())); + } + } +} diff --git a/cli/src/main/java/com/repoachiever/service/command/internal/health/HealthCheckInternalCommandService.java b/cli/src/main/java/com/repoachiever/service/command/internal/health/HealthCheckInternalCommandService.java new file mode 100644 index 0000000..90b6b2d --- /dev/null +++ b/cli/src/main/java/com/repoachiever/service/command/internal/health/HealthCheckInternalCommandService.java @@ -0,0 +1,34 @@ +package com.repoachiever.service.command.internal.health; + +import com.repoachiever.exception.ApiServerException; +import com.repoachiever.exception.ApiServerNotAvailableException; +import com.repoachiever.model.HealthCheckResult; +import com.repoachiever.model.HealthCheckStatus; +import com.repoachiever.service.client.command.HealthCheckClientCommandService; +import com.repoachiever.service.command.common.ICommand; +import com.repoachiever.service.visualization.state.VisualizationState; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +@Service +public class HealthCheckInternalCommandService implements ICommand { + @Autowired private HealthCheckClientCommandService healthCheckClientCommandService; + + @Autowired private VisualizationState visualizationState; + + /** + * @see ICommand + */ + @Override + public void process() throws ApiServerException { + visualizationState.getLabel().pushNext(); + + HealthCheckResult healthCheckResult = healthCheckClientCommandService.process(null); + + if (healthCheckResult.getStatus() == HealthCheckStatus.DOWN) { + throw new ApiServerException( + new ApiServerNotAvailableException(healthCheckResult.getChecks().toString()) + .getMessage()); + } + } +} diff --git a/cli/src/main/java/com/repoachiever/service/command/internal/readiness/ReadinessCheckInternalCommandService.java b/cli/src/main/java/com/repoachiever/service/command/internal/readiness/ReadinessCheckInternalCommandService.java new file mode 100644 index 0000000..b164049 --- /dev/null +++ b/cli/src/main/java/com/repoachiever/service/command/internal/readiness/ReadinessCheckInternalCommandService.java @@ -0,0 +1,28 @@ +package com.repoachiever.service.command.internal.readiness; + +import com.repoachiever.exception.ApiServerException; +import com.repoachiever.model.*; +import com.repoachiever.service.command.common.ICommand; +import com.repoachiever.service.command.internal.readiness.provider.aws.AWSReadinessCheckInternalCommandService; +import com.repoachiever.service.config.ConfigService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +/** Represents readiness check internal command service. */ +@Service +public class ReadinessCheckInternalCommandService implements ICommand { + @Autowired private ConfigService configService; + + @Autowired + private AWSReadinessCheckInternalCommandService awsReadinessCheckInternalCommandService; + + /** + * @see ICommand + */ + @Override + public void process() throws ApiServerException { + switch (configService.getConfig().getCloud().getProvider()) { + case AWS -> awsReadinessCheckInternalCommandService.process(); + } + } +} diff --git a/cli/src/main/java/com/repoachiever/service/command/internal/readiness/provider/aws/AWSReadinessCheckInternalCommandService.java b/cli/src/main/java/com/repoachiever/service/command/internal/readiness/provider/aws/AWSReadinessCheckInternalCommandService.java new file mode 100644 index 0000000..114cb86 --- /dev/null +++ b/cli/src/main/java/com/repoachiever/service/command/internal/readiness/provider/aws/AWSReadinessCheckInternalCommandService.java @@ -0,0 +1,74 @@ +package com.repoachiever.service.command.internal.readiness.provider.aws; + +import com.repoachiever.converter.CredentialsConverter; +import com.repoachiever.dto.ValidationSecretsApplicationDto; +import com.repoachiever.entity.ConfigEntity; +import com.repoachiever.exception.ApiServerException; +import com.repoachiever.exception.ApiServerNotAvailableException; +import com.repoachiever.exception.CloudCredentialsValidationException; +import com.repoachiever.model.*; +import com.repoachiever.service.client.command.ReadinessCheckClientCommandService; +import com.repoachiever.service.client.command.SecretsAcquireClientCommandService; +import com.repoachiever.service.command.common.ICommand; +import com.repoachiever.service.config.ConfigService; +import com.repoachiever.service.visualization.state.VisualizationState; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +@Service +public class AWSReadinessCheckInternalCommandService implements ICommand { + private static final Logger logger = + LogManager.getLogger(AWSReadinessCheckInternalCommandService.class); + + @Autowired private ConfigService configService; + + @Autowired private SecretsAcquireClientCommandService secretsAcquireClientCommandService; + + @Autowired private ReadinessCheckClientCommandService readinessCheckClientCommandService; + + @Autowired private VisualizationState visualizationState; + + /** + * @see ICommand + */ + @Override + public void process() throws ApiServerException { + visualizationState.getLabel().pushNext(); + + ConfigEntity.Cloud.AWSCredentials credentials = + CredentialsConverter.convert( + configService.getConfig().getCloud().getCredentials(), + ConfigEntity.Cloud.AWSCredentials.class); + + ValidationSecretsApplicationDto validationSecretsApplicationDto = + ValidationSecretsApplicationDto.of(Provider.AWS, credentials.getFile()); + + ValidationSecretsApplicationResult validationSecretsApplicationResult = + secretsAcquireClientCommandService.process(validationSecretsApplicationDto); + + if (validationSecretsApplicationResult.getValid()) { + CredentialsFields credentialsFields = + CredentialsFields.of( + AWSSecrets.of( + validationSecretsApplicationResult.getSecrets().getAccessKey(), + validationSecretsApplicationResult.getSecrets().getSecretKey()), + credentials.getRegion()); + + ReadinessCheckApplication readinessCheckApplication = + ReadinessCheckApplication.of(Provider.AWS, credentialsFields); + + ReadinessCheckResult readinessCheckResult = + readinessCheckClientCommandService.process(readinessCheckApplication); + + if (readinessCheckResult.getStatus() == ReadinessCheckStatus.DOWN) { + throw new ApiServerException( + new ApiServerNotAvailableException(readinessCheckResult.getData().toString()) + .getMessage()); + } + } else { + logger.fatal(new CloudCredentialsValidationException().getMessage()); + } + } +} diff --git a/cli/src/main/java/com/repoachiever/service/config/ConfigService.java b/cli/src/main/java/com/repoachiever/service/config/ConfigService.java new file mode 100644 index 0000000..941d806 --- /dev/null +++ b/cli/src/main/java/com/repoachiever/service/config/ConfigService.java @@ -0,0 +1,89 @@ +package com.repoachiever.service.config; + +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.DeserializationFeature; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.ObjectReader; +import com.fasterxml.jackson.dataformat.yaml.YAMLFactory; +import com.repoachiever.entity.ConfigEntity; +import com.repoachiever.entity.PropertiesEntity; +import jakarta.annotation.PostConstruct; +import jakarta.annotation.PreDestroy; + +import java.io.*; +import java.nio.file.Paths; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +/** + * Service for processing configuration file. + */ +@Service +public class ConfigService { + private static final Logger logger = LogManager.getLogger(ConfigService.class); + + private InputStream configFile; + + private ConfigEntity parsedConfigFile; + + /** + * Default constructor, which opens configuration file at the given path. + * + * @param properties common application properties + */ + public ConfigService(@Autowired PropertiesEntity properties) { + try { + configFile = + new FileInputStream( + Paths.get( + System.getProperty("user.home"), + properties.getConfigRootPath(), + properties.getConfigUserFilePath()) + .toString()); + } catch (FileNotFoundException e) { + logger.fatal(e.getMessage()); + } + } + + /** + * Reads configuration from the opened configuration file using mapping with a configuration + * entity. + */ + @PostConstruct + private void configure() { + ObjectMapper mapper = + new ObjectMapper(new YAMLFactory()) + .configure(DeserializationFeature.FAIL_ON_NULL_CREATOR_PROPERTIES, true) + .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, true) + .configure(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY, true); + mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL); + ObjectReader reader = mapper.reader().forType(new TypeReference() { + }); + + try { + parsedConfigFile = reader.readValues(configFile).readAll().getFirst(); + } catch (IOException e) { + logger.fatal(e.getMessage()); + } + } + + /** + * @return Parsed configuration entity + */ + public ConfigEntity getConfig() { + return parsedConfigFile; + } + + @PreDestroy + private void close() { + try { + configFile.close(); + } catch (IOException e) { + logger.fatal(e.getMessage()); + } + } +} diff --git a/cli/src/main/java/com/repoachiever/service/config/common/ValidConfigService.java b/cli/src/main/java/com/repoachiever/service/config/common/ValidConfigService.java new file mode 100644 index 0000000..0f146a2 --- /dev/null +++ b/cli/src/main/java/com/repoachiever/service/config/common/ValidConfigService.java @@ -0,0 +1,40 @@ +package com.repoachiever.service.config.common; + +import com.repoachiever.entity.ConfigEntity; +import com.repoachiever.exception.ConfigValidationException; +import com.repoachiever.service.config.ConfigService; +import jakarta.validation.ConstraintViolation; +import jakarta.validation.Validation; +import jakarta.validation.Validator; +import jakarta.validation.ValidatorFactory; +import java.util.Set; +import java.util.stream.Collectors; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +/** Represents service for config validation. */ +@Service +public class ValidConfigService { + @Autowired private ConfigService configService; + + /** + * Validates parsed local configuration file. + * + * @throws ConfigValidationException if the configuration validation is not passed. + */ + public void validate() throws ConfigValidationException { + try (ValidatorFactory validatorFactory = Validation.buildDefaultValidatorFactory()) { + Validator validator = validatorFactory.getValidator(); + + Set> validationResult = + validator.validate(configService.getConfig()); + + if (!validationResult.isEmpty()) { + throw new ConfigValidationException( + validationResult.stream() + .map(ConstraintViolation::getMessage) + .collect(Collectors.joining(", "))); + } + } + } +} diff --git a/cli/src/main/java/com/repoachiever/service/visualization/VisualizationService.java b/cli/src/main/java/com/repoachiever/service/visualization/VisualizationService.java new file mode 100644 index 0000000..242e3c5 --- /dev/null +++ b/cli/src/main/java/com/repoachiever/service/visualization/VisualizationService.java @@ -0,0 +1,50 @@ +package com.repoachiever.service.visualization; + +import com.repoachiever.entity.PropertiesEntity; +import com.repoachiever.service.visualization.state.VisualizationState; +import java.util.concurrent.*; +import lombok.SneakyThrows; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +/** Represents visualization service used to indicate current execution steps. */ +@Service +public class VisualizationService { + @Autowired private PropertiesEntity properties; + + @Autowired private VisualizationState visualizationState; + + private final ScheduledExecutorService scheduledExecutorService = + Executors.newScheduledThreadPool(2); + + private final CountDownLatch latch = new CountDownLatch(1); + + /** Starts progress visualization processor. */ + public void process() { + scheduledExecutorService.scheduleAtFixedRate( + () -> { + if (visualizationState.getLabel().isNext()) { + System.out.println(visualizationState.getLabel().getCurrent()); + } + + if (visualizationState.getLabel().isEmpty() && !visualizationState.getLabel().isNext()) { + latch.countDown(); + } + }, + 0, + properties.getProgressVisualizationPeriod(), + TimeUnit.MILLISECONDS); + } + + /** Awaits for visualization service to end its processes. */ + @SneakyThrows + public void await() { + latch.await(); + + if (!visualizationState.getResult().isEmpty()) { + System.out.print("\n"); + } + + visualizationState.getResult().forEach(System.out::println); + } +} diff --git a/cli/src/main/java/com/repoachiever/service/visualization/common/IVisualizationLabel.java b/cli/src/main/java/com/repoachiever/service/visualization/common/IVisualizationLabel.java new file mode 100644 index 0000000..3b56108 --- /dev/null +++ b/cli/src/main/java/com/repoachiever/service/visualization/common/IVisualizationLabel.java @@ -0,0 +1,28 @@ +package com.repoachiever.service.visualization.common; + +/** Represents iterative interface for visualization label. */ +public interface IVisualizationLabel { + /** + * Checks if there are steps available in the storage. + * + * @return result of the check. + */ + boolean isEmpty(); + + /** + * Checks if there is next step in the specified label. + * + * @return result of the check. + */ + boolean isNext(); + + /** Pushes next step in the specified label. */ + void pushNext(); + + /** + * Returns string interpretation of the current step. + * + * @return string interpretation of the current step. + */ + String getCurrent(); +} diff --git a/cli/src/main/java/com/repoachiever/service/visualization/common/label/StartCommandVisualizationLabel.java b/cli/src/main/java/com/repoachiever/service/visualization/common/label/StartCommandVisualizationLabel.java new file mode 100644 index 0000000..dd0ed06 --- /dev/null +++ b/cli/src/main/java/com/repoachiever/service/visualization/common/label/StartCommandVisualizationLabel.java @@ -0,0 +1,82 @@ +package com.repoachiever.service.visualization.common.label; + +import com.repoachiever.dto.VisualizationLabelDto; +import com.repoachiever.entity.PropertiesEntity; +import com.repoachiever.service.visualization.common.IVisualizationLabel; +import java.util.ArrayDeque; +import java.util.List; +import java.util.concurrent.locks.ReentrantLock; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +/** Represents label set used for apply command service. */ +@Service +public class StartCommandVisualizationLabel implements IVisualizationLabel { + private final ArrayDeque stepsQueue = new ArrayDeque<>(); + + private final ArrayDeque batchQueue = new ArrayDeque<>(); + + private final ReentrantLock mutex = new ReentrantLock(); + + public StartCommandVisualizationLabel(@Autowired PropertiesEntity properties) { + stepsQueue.addAll( + List.of( + VisualizationLabelDto.of( + properties.getProgressVisualizationHealthCheckRequestLabel(), 10), + VisualizationLabelDto.of( + properties.getProgressVisualizationSecretsAcquireRequestLabel(), 30), + VisualizationLabelDto.of( + properties.getProgressVisualizationScriptAcquireRequestLabel(), 60), + VisualizationLabelDto.of(properties.getProgressVisualizationApplyRequestLabel(), 90), + VisualizationLabelDto.of( + properties.getProgressVisualizationApplyResponseLabel(), 100))); + } + + /** + * @see IVisualizationLabel + */ + @Override + public boolean isEmpty() { + return stepsQueue.isEmpty(); + } + + /** + * @see IVisualizationLabel + */ + @Override + public boolean isNext() { + mutex.lock(); + + try { + return !batchQueue.isEmpty(); + } finally { + mutex.unlock(); + } + } + + /** + * @see IVisualizationLabel + */ + @Override + public void pushNext() { + mutex.lock(); + + batchQueue.push(stepsQueue.pop().toString()); + + mutex.unlock(); + } + + /** + * @see IVisualizationLabel + */ + @Override + public String getCurrent() { + mutex.lock(); + + try { + return batchQueue.pollLast(); + } finally { + mutex.unlock(); + } + } +} diff --git a/cli/src/main/java/com/repoachiever/service/visualization/common/label/StateCommandVisualizationLabel.java b/cli/src/main/java/com/repoachiever/service/visualization/common/label/StateCommandVisualizationLabel.java new file mode 100644 index 0000000..fff547a --- /dev/null +++ b/cli/src/main/java/com/repoachiever/service/visualization/common/label/StateCommandVisualizationLabel.java @@ -0,0 +1,82 @@ +package com.repoachiever.service.visualization.common.label; + +import com.repoachiever.dto.VisualizationLabelDto; +import com.repoachiever.entity.PropertiesEntity; +import com.repoachiever.service.visualization.common.IVisualizationLabel; +import java.util.ArrayDeque; +import java.util.List; +import java.util.concurrent.locks.ReentrantLock; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +/** Represents label set used for apply command service. */ +@Service +public class StateCommandVisualizationLabel implements IVisualizationLabel { + private final ArrayDeque stepsQueue = new ArrayDeque<>(); + + private final ArrayDeque batchQueue = new ArrayDeque<>(); + + private final ReentrantLock mutex = new ReentrantLock(); + + public StateCommandVisualizationLabel(@Autowired PropertiesEntity properties) { + stepsQueue.addAll( + List.of( + VisualizationLabelDto.of( + properties.getProgressVisualizationHealthCheckRequestLabel(), 10), + VisualizationLabelDto.of( + properties.getProgressVisualizationReadinessCheckRequestLabel(), 30), + VisualizationLabelDto.of( + properties.getProgressVisualizationSecretsAcquireRequestLabel(), 50), + VisualizationLabelDto.of(properties.getProgressVisualizationStateRequestLabel(), 90), + VisualizationLabelDto.of( + properties.getProgressVisualizationStateResponseLabel(), 100))); + } + + /** + * @see IVisualizationLabel + */ + @Override + public boolean isEmpty() { + return stepsQueue.isEmpty(); + } + + /** + * @see IVisualizationLabel + */ + @Override + public boolean isNext() { + mutex.lock(); + + try { + return !batchQueue.isEmpty(); + } finally { + mutex.unlock(); + } + } + + /** + * @see IVisualizationLabel + */ + @Override + public void pushNext() { + mutex.lock(); + + batchQueue.push(stepsQueue.pop().toString()); + + mutex.unlock(); + } + + /** + * @see IVisualizationLabel + */ + @Override + public String getCurrent() { + mutex.lock(); + + try { + return batchQueue.pollLast(); + } finally { + mutex.unlock(); + } + } +} diff --git a/cli/src/main/java/com/repoachiever/service/visualization/common/label/StopCommandVisualizationLabel.java b/cli/src/main/java/com/repoachiever/service/visualization/common/label/StopCommandVisualizationLabel.java new file mode 100644 index 0000000..136b59d --- /dev/null +++ b/cli/src/main/java/com/repoachiever/service/visualization/common/label/StopCommandVisualizationLabel.java @@ -0,0 +1,80 @@ +package com.repoachiever.service.visualization.common.label; + +import com.repoachiever.dto.VisualizationLabelDto; +import com.repoachiever.entity.PropertiesEntity; +import com.repoachiever.service.visualization.common.IVisualizationLabel; +import java.util.ArrayDeque; +import java.util.List; +import java.util.concurrent.locks.ReentrantLock; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +/** Represents label set used for destroy command service. */ +@Service +public class StopCommandVisualizationLabel implements IVisualizationLabel { + private final ArrayDeque stepsQueue = new ArrayDeque<>(); + + private final ArrayDeque batchQueue = new ArrayDeque<>(); + + private final ReentrantLock mutex = new ReentrantLock(); + + public StopCommandVisualizationLabel(@Autowired PropertiesEntity properties) { + stepsQueue.addAll( + List.of( + VisualizationLabelDto.of( + properties.getProgressVisualizationHealthCheckRequestLabel(), 10), + VisualizationLabelDto.of( + properties.getProgressVisualizationSecretsAcquireRequestLabel(), 50), + VisualizationLabelDto.of(properties.getProgressVisualizationDestroyRequestLabel(), 90), + VisualizationLabelDto.of( + properties.getProgressVisualizationDestroyResponseLabel(), 100))); + } + + /** + * @see IVisualizationLabel + */ + @Override + public boolean isEmpty() { + return stepsQueue.isEmpty(); + } + + /** + * @see IVisualizationLabel + */ + @Override + public boolean isNext() { + mutex.lock(); + + try { + return !batchQueue.isEmpty(); + } finally { + mutex.unlock(); + } + } + + /** + * @see IVisualizationLabel + */ + @Override + public void pushNext() { + mutex.lock(); + + batchQueue.push(stepsQueue.pop().toString()); + + mutex.unlock(); + } + + /** + * @see IVisualizationLabel + */ + @Override + public String getCurrent() { + mutex.lock(); + + try { + return batchQueue.pollLast(); + } finally { + mutex.unlock(); + } + } +} diff --git a/cli/src/main/java/com/repoachiever/service/visualization/common/label/VersionCommandVisualizationLabel.java b/cli/src/main/java/com/repoachiever/service/visualization/common/label/VersionCommandVisualizationLabel.java new file mode 100644 index 0000000..3b88448 --- /dev/null +++ b/cli/src/main/java/com/repoachiever/service/visualization/common/label/VersionCommandVisualizationLabel.java @@ -0,0 +1,77 @@ +package com.repoachiever.service.visualization.common.label; + +import com.repoachiever.dto.VisualizationLabelDto; +import com.repoachiever.entity.PropertiesEntity; +import com.repoachiever.service.visualization.common.IVisualizationLabel; +import java.util.ArrayDeque; +import java.util.List; +import java.util.concurrent.locks.ReentrantLock; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +/** Represents label set used for version command service. */ +@Service +public class VersionCommandVisualizationLabel implements IVisualizationLabel { + private final ArrayDeque stepsQueue = new ArrayDeque<>(); + + private final ArrayDeque batchQueue = new ArrayDeque<>(); + + private final ReentrantLock mutex = new ReentrantLock(); + + public VersionCommandVisualizationLabel(@Autowired PropertiesEntity properties) { + stepsQueue.addAll( + List.of( + VisualizationLabelDto.of( + properties.getProgressVisualizationHealthCheckRequestLabel(), 10), + VisualizationLabelDto.of( + properties.getProgressVisualizationVersionInfoRequestLabel(), 100))); + } + + /** + * @see IVisualizationLabel + */ + @Override + public boolean isEmpty() { + return stepsQueue.isEmpty(); + } + + /** + * @see IVisualizationLabel + */ + @Override + public boolean isNext() { + mutex.lock(); + + try { + return !batchQueue.isEmpty(); + } finally { + mutex.unlock(); + } + } + + /** + * @see IVisualizationLabel + */ + @Override + public void pushNext() { + mutex.lock(); + + batchQueue.push(stepsQueue.pop().toString()); + + mutex.unlock(); + } + + /** + * @see IVisualizationLabel + */ + @Override + public String getCurrent() { + mutex.lock(); + + try { + return batchQueue.pollLast(); + } finally { + mutex.unlock(); + } + } +} diff --git a/cli/src/main/java/com/repoachiever/service/visualization/state/VisualizationState.java b/cli/src/main/java/com/repoachiever/service/visualization/state/VisualizationState.java new file mode 100644 index 0000000..a8b6b0f --- /dev/null +++ b/cli/src/main/java/com/repoachiever/service/visualization/state/VisualizationState.java @@ -0,0 +1,28 @@ +package com.repoachiever.service.visualization.state; + +import com.repoachiever.service.visualization.common.IVisualizationLabel; +import java.util.*; +import lombok.Getter; +import lombok.Setter; +import org.springframework.stereotype.Service; + +/** + * Represents general visualization state used to gather output values to be processed by + * visualization service. + */ +@Getter +@Service +public class VisualizationState { + @Setter private IVisualizationLabel label; + + private final List result = new ArrayList<>(); + + /** + * Adds given message to the result array. + * + * @param message given message to be added to result array. + */ + public void addResult(String message) { + result.add(message); + } +} diff --git a/cli/src/main/resources/application.properties b/cli/src/main/resources/application.properties new file mode 100644 index 0000000..07e434b --- /dev/null +++ b/cli/src/main/resources/application.properties @@ -0,0 +1,43 @@ +# Describes Spring related properties. +spring.main.banner-mode=off +spring.main.web-application-type=NONE + +# Describes the path and name of a configuration file. +config.root=.resourcetracker/config +config.user.file=user.yaml + +# Describes visualizer state update period +progress.visualization.period=1000 + +# Describes visualization label used for secrets validation request process. +progress.visualization.secrets-acquire-request=Checking if the given cloud credentials are valid + +# Describes visualization label used for script validation request process. +progress.visualization.script-acquire-request=Checking if the given script is allowed to be used + +# Describes visualization label used for infrastructure deployment request process. +progress.visualization.apply-request=Sending infrastructure deployment request + +# Describes visualization label used for infrastructure deployment response. +progress.visualization.apply-response=Application of deployment infrastructure has been completed + +# Describes visualization label used for infrastructure destruction request process. +progress.visualization.destroy-request=Sending infrastructure destruction request + +# Describes visualization label used for infrastructure destruction response. +progress.visualization.destroy-response=Destruction of deployed infrastructure has been completed + +# Describes visualization label used for general state retrieval request process. +progress.visualization.state-request=Sending general state retrieval request + +# Describes visualization label used for general state retrieval response. +progress.visualization.state-response=General state has been retrieved successfully + +# Describes visualization label used for version request process. +progress.visualization.version-info-request=Sending request to API Server to retrieve its version information + +# Describes visualization label used for health check request. +progress.visualization.health-check-request=Sending health check request + +# Describes visualization label used for readiness check request. +progress.visualization.readiness-check-request=Sending readiness check request \ No newline at end of file diff --git a/cli/src/main/resources/log4j2.xml b/cli/src/main/resources/log4j2.xml new file mode 100644 index 0000000..e47463a --- /dev/null +++ b/cli/src/main/resources/log4j2.xml @@ -0,0 +1,60 @@ + + + + + + + + + + + + + %d %p %c{1.} [%t] %m%n + + + + + + + + %d %p %c{1.} [%t] %m%n + + + + + + + + %d %p %c{1.} [%t] %m%n + + + + + + + + %d %p %c{1.} [%t] %m%n + + + + + + + + %d %p %c{1.} [%t] %m%n + + + + + + + + + + + + + + + diff --git a/cluster/pom.xml b/cluster/pom.xml new file mode 100644 index 0000000..c8c484a --- /dev/null +++ b/cluster/pom.xml @@ -0,0 +1,176 @@ + + 4.0.0 + cluster + 1.0-SNAPSHOT + cluster + Worker for RepoAchiever + + + com.repoachiever + base + 1.0-SNAPSHOT + + + + true + allow + + com.repoachiever.Cluster + + + + + + + Shell-Command-Executor-Lib + Shell-Command-Executor-Lib + system + ${basedir}/../lib/Shell-Command-Executor-Lib-0.5.0-SNAPSHOT.jar + + + + + org.springframework.boot + spring-boot-starter-web + + + org.springframework.boot + spring-boot-starter-logging + + + + + org.springframework.boot + spring-boot-starter-validation + + + org.springframework.boot + spring-boot-starter-log4j2 + + + org.springframework.integration + spring-integration-rmi + + + org.springframework + spring-remoting + + + + + jakarta.annotation + jakarta.annotation-api + + + jakarta.validation + jakarta.validation-api + + + jakarta.servlet + jakarta.servlet-api + + + jakarta.ws.rs + jakarta.ws.rs-api + + + org.projectlombok + lombok + + + org.yaml + snakeyaml + + + com.fasterxml.jackson.dataformat + jackson-dataformat-yaml + + + commons-io + commons-io + + + com.google.guava + guava + + + org.apache.commons + commons-lang3 + + + + + org.apache.logging.log4j + log4j-api + + + org.apache.logging.log4j + log4j-core + + + + + junit + junit + + + + + cluster + + + org.springframework.boot + spring-boot-maven-plugin + + true + + + + com.google.cloud.tools + jib-maven-plugin + + + org.apache.maven.plugins + maven-compiler-plugin + + 19 + 19 + + + + com.coderplus.maven.plugins + copy-rename-maven-plugin + + + + ${basedir}/target/cluster.jar + ${main.basedir}/../bin/cluster/cluster.jar + + + + + + com.diffplug.spotless + spotless-maven-plugin + + + + + + + dev + + true + + + dev + + + + prod + + prod + + + + diff --git a/cluster/src/main/java/com/repoachiever/App.java b/cluster/src/main/java/com/repoachiever/App.java new file mode 100644 index 0000000..6a011e4 --- /dev/null +++ b/cluster/src/main/java/com/repoachiever/App.java @@ -0,0 +1,37 @@ +package com.repoachiever; + +import com.repoachiever.entity.PropertiesEntity; +import com.repoachiever.service.apiserver.resource.ApiServerCommunicationResource; +import com.repoachiever.service.config.ConfigService; +import com.repoachiever.service.integration.communication.cluster.ClusterCommunicationConfigService; +import com.repoachiever.service.integration.logging.state.LoggingStateService; +import com.repoachiever.service.executor.CommandExecutorService; +import com.repoachiever.service.integration.logging.transfer.LoggingTransferService; +import com.repoachiever.service.waiter.WaiterHelper; +import org.springframework.boot.ApplicationArguments; +import org.springframework.boot.ApplicationRunner; +import org.springframework.context.annotation.Import; +import org.springframework.stereotype.Component; + +/** + * Represents initialization point for the RepoAchiever Cluster application. + */ +@Component +@Import({ + ConfigService.class, + CommandExecutorService.class, + PropertiesEntity.class, + ClusterCommunicationConfigService.class, + ApiServerCommunicationResource.class, + LoggingStateService.class, + LoggingTransferService.class +}) +public class App implements ApplicationRunner { + /** + * @see ApplicationRunner + */ + @Override + public void run(ApplicationArguments args) { + WaiterHelper.waitForExit(); + } +} diff --git a/cluster/src/main/java/com/repoachiever/Cluster.java b/cluster/src/main/java/com/repoachiever/Cluster.java new file mode 100644 index 0000000..ef757f8 --- /dev/null +++ b/cluster/src/main/java/com/repoachiever/Cluster.java @@ -0,0 +1,15 @@ +package com.repoachiever; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +/** + * Represents entry point for the RepoAchiever Cluster application. + */ +@SpringBootApplication +public class Cluster { + public static void main(String[] args) { + SpringApplication application = new SpringApplication(App.class); + System.exit(SpringApplication.exit(application.run(args))); + } +} diff --git a/cluster/src/main/java/com/repoachiever/converter/CronExpressionConverter.java b/cluster/src/main/java/com/repoachiever/converter/CronExpressionConverter.java new file mode 100644 index 0000000..24b4451 --- /dev/null +++ b/cluster/src/main/java/com/repoachiever/converter/CronExpressionConverter.java @@ -0,0 +1,28 @@ +package com.repoachiever.converter; + +import com.repoachiever.exception.CronExpressionException; +import java.time.Duration; +import java.time.LocalDateTime; +import java.util.Objects; +import org.springframework.scheduling.support.CronExpression; + +/** + * Represents converter used for cron expression parsing operation. + */ +public class CronExpressionConverter { + /** + * Converts frequency from cron expression to milliseconds. + * + * @param src cron expression to be converted + * @return frequency in milliseconds + */ + public static Long convert(String src) throws CronExpressionException { + CronExpression cronExpression = CronExpression.parse(src); + LocalDateTime nextExecutionTime = cronExpression.next(LocalDateTime.now()); + if (Objects.isNull(nextExecutionTime)) { + throw new CronExpressionException(); + } + LocalDateTime afterNextExecutionTime = cronExpression.next(nextExecutionTime); + return Duration.between(nextExecutionTime, afterNextExecutionTime).toMillis(); + } +} diff --git a/cluster/src/main/java/com/repoachiever/dto/CommandExecutorOutputDto.java b/cluster/src/main/java/com/repoachiever/dto/CommandExecutorOutputDto.java new file mode 100644 index 0000000..0051755 --- /dev/null +++ b/cluster/src/main/java/com/repoachiever/dto/CommandExecutorOutputDto.java @@ -0,0 +1,13 @@ +package com.repoachiever.dto; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +/** Represents gathered output of the executed command. */ +@Getter +@AllArgsConstructor(staticName = "of") +public class CommandExecutorOutputDto { + private String normalOutput; + + private String errorOutput; +} diff --git a/cluster/src/main/java/com/repoachiever/entity/ConfigEntity.java b/cluster/src/main/java/com/repoachiever/entity/ConfigEntity.java new file mode 100644 index 0000000..790ce5f --- /dev/null +++ b/cluster/src/main/java/com/repoachiever/entity/ConfigEntity.java @@ -0,0 +1,170 @@ +package com.repoachiever.entity; + +import com.fasterxml.jackson.annotation.JsonProperty; +import jakarta.validation.Valid; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; + +import java.util.List; + +import jakarta.validation.constraints.Pattern; +import lombok.AllArgsConstructor; +import lombok.Getter; + +/** + * Service used to perform RepoAchiever Cluster processing operation. + */ +@Getter +public class ConfigEntity { + /** + * Contains metadata for a specific RepoAchiever Cluster allocation. + */ + @Getter + public static class Metadata { + @JsonProperty("name") + public String name; + + @JsonProperty("workspace_unit_key") + public String workspaceUnitKey; + } + + @Valid + @NotNull + @JsonProperty("metadata") + public Metadata metadata; + + /** + * Represents filter section elected for a specific RepoAchiever Cluster allocation. + */ + @Getter + public static class Filter { + @JsonProperty("locations") + public List locations; + } + + @Valid + @NotNull + @JsonProperty("filter") + public Filter filter; + + /** + * Represents external service configurations for RepoAchiever Cluster allocation used to retrieve content. + */ + @Getter + public static class Service { + /** + * Represents all supported service providers, which can be used by RepoAchiever Cluster allocation. + */ + public enum Provider { + LOCAL("git-local"), + GITHUB("git-github"); + + private final String value; + + Provider(String value) { + this.value = value; + } + + public String toString() { + return value; + } + } + + @JsonProperty("provider") + public Provider provider; + + /** + * Represents credentials used for external service communication by RepoAchiever Cluster allocation. + */ + @Getter + public static class Credentials { + @JsonProperty("token") + public String token; + } + + @JsonProperty("credentials") + public Credentials credentials; + } + + @Valid + @NotNull + @JsonProperty("service") + public Service service; + + /** Represents RepoAchiever Cluster configuration used for internal communication infrastructure setup. */ + @Getter + public static class Communication { + @NotNull + @JsonProperty("api_server_name") + public String apiServerName; + + @NotNull + @JsonProperty("port") + public Integer port; + } + + @Valid + @NotNull + @JsonProperty("communication") + public Communication communication; + + /** Represents RepoAchiever Cluster configuration used for content management. */ + @Getter + public static class Content { + @NotNull + @Pattern(regexp = "(^zip$)|(^tar$)") + @JsonProperty("format") + public String format; + } + + @Valid + @NotNull + @JsonProperty("content") + public Content content; + + /** + * Represents RepoAchiever API Server resources configuration section. + */ + @Getter + public static class Resource { + /** + * Represents RepoAchiever API Server configuration used for RepoAchiever Cluster. + */ + @Getter + public static class Cluster { + @NotNull + @JsonProperty("max-workers") + public Integer maxWorkers; + } + + @Valid + @NotNull + @JsonProperty("cluster") + public Cluster cluster; + + /** + * Represents RepoAchiever API Server configuration used for RepoAchiever Worker. + */ + @Getter + public static class Worker { + @NotNull + @Pattern( + regexp = + "(((([0-9]|[0-5][0-9])(-([0-9]|[0-5][0-9]))?,)*([0-9]|[0-5][0-9])(-([0-9]|[0-5][0-9]))?)|(([\\*]|[0-9]|[0-5][0-9])/([0-9]|[0-5][0-9]))|([\\?])|([\\*]))[\\s](((([0-9]|[0-5][0-9])(-([0-9]|[0-5][0-9]))?,)*([0-9]|[0-5][0-9])(-([0-9]|[0-5][0-9]))?)|(([\\*]|[0-9]|[0-5][0-9])/([0-9]|[0-5][0-9]))|([\\?])|([\\*]))[\\s](((([0-9]|[0-1][0-9]|[2][0-3])(-([0-9]|[0-1][0-9]|[2][0-3]))?,)*([0-9]|[0-1][0-9]|[2][0-3])(-([0-9]|[0-1][0-9]|[2][0-3]))?)|(([\\*]|[0-9]|[0-1][0-9]|[2][0-3])/([0-9]|[0-1][0-9]|[2][0-3]))|([\\?])|([\\*]))[\\s](((([1-9]|[0][1-9]|[1-2][0-9]|[3][0-1])(-([1-9]|[0][1-9]|[1-2][0-9]|[3][0-1]))?,)*([1-9]|[0][1-9]|[1-2][0-9]|[3][0-1])(-([1-9]|[0][1-9]|[1-2][0-9]|[3][0-1]))?(C)?)|(([1-9]|[0][1-9]|[1-2][0-9]|[3][0-1])/([1-9]|[0][1-9]|[1-2][0-9]|[3][0-1])(C)?)|(L(-[0-9])?)|(L(-[1-2][0-9])?)|(L(-[3][0-1])?)|(LW)|([1-9]W)|([1-3][0-9]W)|([\\?])|([\\*]))[\\s](((([1-9]|0[1-9]|1[0-2])(-([1-9]|0[1-9]|1[0-2]))?,)*([1-9]|0[1-9]|1[0-2])(-([1-9]|0[1-9]|1[0-2]))?)|(([1-9]|0[1-9]|1[0-2])/([1-9]|0[1-9]|1[0-2]))|(((JAN|FEB|MAR|APR|MAY|JUN|JUL|AUG|SEP|OCT|NOV|DEC)(-(JAN|FEB|MAR|APR|MAY|JUN|JUL|AUG|SEP|OCT|NOV|DEC))?,)*(JAN|FEB|MAR|APR|MAY|JUN|JUL|AUG|SEP|OCT|NOV|DEC)(-(JAN|FEB|MAR|APR|MAY|JUN|JUL|AUG|SEP|OCT|NOV|DEC))?)|((JAN|FEB|MAR|APR|MAY|JUN|JUL|AUG|SEP|OCT|NOV|DEC)/(JAN|FEB|MAR|APR|MAY|JUN|JUL|AUG|SEP|OCT|NOV|DEC))|([\\?])|([\\*]))[\\s]((([1-7](-([1-7]))?,)*([1-7])(-([1-7]))?)|([1-7]/([1-7]))|(((MON|TUE|WED|THU|FRI|SAT|SUN)(-(MON|TUE|WED|THU|FRI|SAT|SUN))?,)*(MON|TUE|WED|THU|FRI|SAT|SUN)(-(MON|TUE|WED|THU|FRI|SAT|SUN))?(C)?)|((MON|TUE|WED|THU|FRI|SAT|SUN)/(MON|TUE|WED|THU|FRI|SAT|SUN)(C)?)|(([1-7]|(MON|TUE|WED|THU|FRI|SAT|SUN))?(L|LW)?)|(([1-7]|MON|TUE|WED|THU|FRI|SAT|SUN)#([1-7])?)|([\\?])|([\\*]))([\\s]?(([\\*])?|(19[7-9][0-9])|(20[0-9][0-9]))?|" + + " (((19[7-9][0-9])|(20[0-9][0-9]))/((19[7-9][0-9])|(20[0-9][0-9])))?|" + + " ((((19[7-9][0-9])|(20[0-9][0-9]))(-((19[7-9][0-9])|(20[0-9][0-9])))?,)*((19[7-9][0-9])|(20[0-9][0-9]))(-((19[7-9][0-9])|(20[0-9][0-9])))?)?)") + @JsonProperty("frequency") + public String frequency; + } + + @Valid + @NotNull + @JsonProperty("worker") + public Worker worker; + } + + @Valid + @NotNull + @JsonProperty("resource") + public Resource resource; +} diff --git a/cluster/src/main/java/com/repoachiever/entity/PropertiesEntity.java b/cluster/src/main/java/com/repoachiever/entity/PropertiesEntity.java new file mode 100644 index 0000000..223d3af --- /dev/null +++ b/cluster/src/main/java/com/repoachiever/entity/PropertiesEntity.java @@ -0,0 +1,56 @@ +package com.repoachiever.entity; + +import lombok.Getter; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.support.PropertySourcesPlaceholderConfigurer; +import org.springframework.core.io.ClassPathResource; +import org.apache.commons.lang3.StringUtils; + +/** + * Exposes access to properties setup to be used for further configuration. + */ +@Getter +@Configuration +public class PropertiesEntity { + private static final String GIT_CONFIG_PROPERTIES_FILE = "git.properties"; + + @Value(value = "${REPOACHIEVER_CLUSTER_CONTEXT:null}") + private String clusterContext; + + @Value(value = "${logging.transfer.frequency}") + private Integer loggingTransferFrequency; + + @Value(value = "${logging.state.frequency}") + private Integer loggingStateFrequency; + + @Value(value = "${logging.state-finalizer.frequency}") + private Integer loggingStateFinalizerFrequency; + + @Value(value = "${git.commit.id.abbrev}") + private String gitCommitId; + + /** + * Adds custom properties to resource configurations. + * + * @return modified property sources configurer. + */ + @Bean + private static PropertySourcesPlaceholderConfigurer placeholderConfigurer() { + PropertySourcesPlaceholderConfigurer propsConfig = new PropertySourcesPlaceholderConfigurer(); + propsConfig.setLocation(new ClassPathResource(GIT_CONFIG_PROPERTIES_FILE)); + propsConfig.setIgnoreResourceNotFound(true); + propsConfig.setIgnoreUnresolvablePlaceholders(true); + return propsConfig; + } + + /** + * Removes the last symbol in git commit id of the repository. + * + * @return chopped repository git commit id. + */ + public String getGitCommitId() { + return StringUtils.chop(gitCommitId); + } +} diff --git a/cluster/src/main/java/com/repoachiever/exception/ApiServerOperationFailureException.java b/cluster/src/main/java/com/repoachiever/exception/ApiServerOperationFailureException.java new file mode 100644 index 0000000..3376590 --- /dev/null +++ b/cluster/src/main/java/com/repoachiever/exception/ApiServerOperationFailureException.java @@ -0,0 +1,21 @@ +package com.repoachiever.exception; + +import java.io.IOException; +import java.util.Arrays; +import java.util.Formatter; + +/** + * Represents exception used when RepoAchiever API Server operation fails. + */ +public class ApiServerOperationFailureException extends IOException { + public ApiServerOperationFailureException() { + this(""); + } + + public ApiServerOperationFailureException(Object... message) { + super( + new Formatter() + .format("RepoAchiever API Server operation failed: %s", Arrays.stream(message).toArray()) + .toString()); + } +} diff --git a/cluster/src/main/java/com/repoachiever/exception/CommandExecutorException.java b/cluster/src/main/java/com/repoachiever/exception/CommandExecutorException.java new file mode 100644 index 0000000..1033e07 --- /dev/null +++ b/cluster/src/main/java/com/repoachiever/exception/CommandExecutorException.java @@ -0,0 +1,17 @@ +package com.repoachiever.exception; + +import java.io.IOException; +import java.util.Arrays; +import java.util.Formatter; + +/** + * Represents exception used to indicate command executor failure. + */ +public class CommandExecutorException extends IOException { + public CommandExecutorException(Object... message) { + super( + new Formatter() + .format("Invalid command executor behaviour: %s", Arrays.stream(message).toArray()) + .toString()); + } +} diff --git a/cluster/src/main/java/com/repoachiever/exception/CommunicationConfigurationFailureException.java b/cluster/src/main/java/com/repoachiever/exception/CommunicationConfigurationFailureException.java new file mode 100644 index 0000000..a6672f1 --- /dev/null +++ b/cluster/src/main/java/com/repoachiever/exception/CommunicationConfigurationFailureException.java @@ -0,0 +1,21 @@ +package com.repoachiever.exception; + +import java.io.IOException; +import java.util.Arrays; +import java.util.Formatter; + +/** + * Represents exception used when communication configuration fails. + */ +public class CommunicationConfigurationFailureException extends IOException { + public CommunicationConfigurationFailureException() { + this(""); + } + + public CommunicationConfigurationFailureException(Object... message) { + super( + new Formatter() + .format("Communication configuration operation failed: %s", Arrays.stream(message).toArray()) + .toString()); + } +} diff --git a/cluster/src/main/java/com/repoachiever/exception/ConfigNotGivenException.java b/cluster/src/main/java/com/repoachiever/exception/ConfigNotGivenException.java new file mode 100644 index 0000000..8ad7455 --- /dev/null +++ b/cluster/src/main/java/com/repoachiever/exception/ConfigNotGivenException.java @@ -0,0 +1,21 @@ +package com.repoachiever.exception; + +import java.io.IOException; +import java.util.Arrays; +import java.util.Formatter; + +/** + * Represents exception used when config file is not provided. + */ +public class ConfigNotGivenException extends IOException { + public ConfigNotGivenException() { + this(""); + } + + public ConfigNotGivenException(Object... message) { + super( + new Formatter() + .format("Config file is not given: %s", Arrays.stream(message).toArray()) + .toString()); + } +} \ No newline at end of file diff --git a/cluster/src/main/java/com/repoachiever/exception/ConfigValidationException.java b/cluster/src/main/java/com/repoachiever/exception/ConfigValidationException.java new file mode 100644 index 0000000..aa2f396 --- /dev/null +++ b/cluster/src/main/java/com/repoachiever/exception/ConfigValidationException.java @@ -0,0 +1,21 @@ +package com.repoachiever.exception; + +import java.io.IOException; +import java.util.Arrays; +import java.util.Formatter; + +/** + * Represents exception used when config file is not valid. + */ +public class ConfigValidationException extends IOException { + public ConfigValidationException() { + this(""); + } + + public ConfigValidationException(Object... message) { + super( + new Formatter() + .format("Config file content is not valid: %s", Arrays.stream(message).toArray()) + .toString()); + } +} diff --git a/cluster/src/main/java/com/repoachiever/exception/CronExpressionException.java b/cluster/src/main/java/com/repoachiever/exception/CronExpressionException.java new file mode 100644 index 0000000..d2befeb --- /dev/null +++ b/cluster/src/main/java/com/repoachiever/exception/CronExpressionException.java @@ -0,0 +1,17 @@ +package com.repoachiever.exception; + +import java.io.IOException; +import java.util.Arrays; +import java.util.Formatter; + +/** + * Represents exception used to indicate cron expression conversion failure. + */ +public class CronExpressionException extends IOException { + public CronExpressionException(Object... message) { + super( + new Formatter() + .format("Invalid cron exception: %s", Arrays.stream(message).toArray()) + .toString()); + } +} diff --git a/cluster/src/main/java/com/repoachiever/logging/FatalAppender.java b/cluster/src/main/java/com/repoachiever/logging/FatalAppender.java new file mode 100644 index 0000000..e7a1df3 --- /dev/null +++ b/cluster/src/main/java/com/repoachiever/logging/FatalAppender.java @@ -0,0 +1,36 @@ +package com.repoachiever.logging; + +import com.repoachiever.service.state.StateService; +import org.apache.logging.log4j.Level; +import org.apache.logging.log4j.core.Appender; +import org.apache.logging.log4j.core.Core; +import org.apache.logging.log4j.core.Filter; +import org.apache.logging.log4j.core.LogEvent; +import org.apache.logging.log4j.core.appender.AbstractAppender; +import org.apache.logging.log4j.core.config.plugins.Plugin; +import org.apache.logging.log4j.core.config.plugins.PluginAttribute; +import org.apache.logging.log4j.core.config.plugins.PluginElement; +import org.apache.logging.log4j.core.config.plugins.PluginFactory; + +/** + * Service used for logging fatal level application state changes. + */ +@Plugin(name = "fatalappender", category = Core.CATEGORY_NAME, elementType = Appender.ELEMENT_TYPE) +public class FatalAppender extends AbstractAppender { + protected FatalAppender(String name, Filter filter) { + super(name, filter, null, false, null); + } + + @PluginFactory + public static FatalAppender createAppender( + @PluginAttribute("name") String name, @PluginElement("Filter") Filter filter) { + return new FatalAppender(name, filter); + } + + @Override + public void append(LogEvent event) { + if (event.getLevel().equals(Level.FATAL)) { + StateService.setExit(true); + } + } +} diff --git a/cluster/src/main/java/com/repoachiever/logging/TransferAppender.java b/cluster/src/main/java/com/repoachiever/logging/TransferAppender.java new file mode 100644 index 0000000..393c5d2 --- /dev/null +++ b/cluster/src/main/java/com/repoachiever/logging/TransferAppender.java @@ -0,0 +1,39 @@ +package com.repoachiever.logging; + +import com.repoachiever.logging.common.LoggingConfigurationHelper; +import com.repoachiever.service.state.StateService; +import org.apache.logging.log4j.core.Appender; +import org.apache.logging.log4j.core.Core; +import org.apache.logging.log4j.core.Filter; +import org.apache.logging.log4j.core.LogEvent; +import org.apache.logging.log4j.core.appender.AbstractAppender; +import org.apache.logging.log4j.core.config.plugins.Plugin; +import org.apache.logging.log4j.core.config.plugins.PluginAttribute; +import org.apache.logging.log4j.core.config.plugins.PluginElement; +import org.apache.logging.log4j.core.config.plugins.PluginFactory; + +/** + * Service used for logging message transfer to RepoAchiever API Server allocation. + */ +@Plugin(name = "transferappender", category = Core.CATEGORY_NAME, elementType = Appender.ELEMENT_TYPE) +public class TransferAppender extends AbstractAppender { + protected TransferAppender(String name, Filter filter) { + super(name, filter, null, false, null); + } + + @PluginFactory + public static TransferAppender createAppender( + @PluginAttribute("name") String name, @PluginElement("Filter") Filter filter) { + return new TransferAppender(name, filter); + } + + @Override + public void append(LogEvent event) { + String message = event.getMessage().getFormattedMessage(); + + if (LoggingConfigurationHelper.isMessageTransferable(message)) { + StateService.addLogMessage( + LoggingConfigurationHelper.extractTransferableMessage(message)); + } + } +} diff --git a/cluster/src/main/java/com/repoachiever/logging/common/LoggingConfigurationHelper.java b/cluster/src/main/java/com/repoachiever/logging/common/LoggingConfigurationHelper.java new file mode 100644 index 0000000..a6d1f00 --- /dev/null +++ b/cluster/src/main/java/com/repoachiever/logging/common/LoggingConfigurationHelper.java @@ -0,0 +1,38 @@ +package com.repoachiever.logging.common; + +/** + * Contains helpful tools used for logging configuration. + */ +public class LoggingConfigurationHelper { + private static final String TRANSFERABLE_MESSAGE_PREFIX = "!transferable!"; + + /** + * Checks if the given log message contains given prefix. + * + * @param message given log message. + * @return result of the check. + */ + public static Boolean isMessageTransferable(String message) { + return message.contains(TRANSFERABLE_MESSAGE_PREFIX); + } + + /** + * Formats transferable message with the given prefix. + * + * @param message given formatted transferable log message. + * @return formatted transferable message. + */ + public static String extractTransferableMessage(String message) { + return message.replaceAll(TRANSFERABLE_MESSAGE_PREFIX, ""); + } + + /** + * Formats transferable message with the given prefix. + * + * @param message given log message. + * @return formatted transferable message. + */ + public static String getTransferableMessage(String message) { + return String.format("%s %s", TRANSFERABLE_MESSAGE_PREFIX, message); + } +} diff --git a/cluster/src/main/java/com/repoachiever/resource/communication/ClusterCommunicationResource.java b/cluster/src/main/java/com/repoachiever/resource/communication/ClusterCommunicationResource.java new file mode 100644 index 0000000..7408b2c --- /dev/null +++ b/cluster/src/main/java/com/repoachiever/resource/communication/ClusterCommunicationResource.java @@ -0,0 +1,56 @@ +package com.repoachiever.resource.communication; + +import com.repoachiever.entity.PropertiesEntity; +import com.repoachiever.service.communication.cluster.IClusterCommunicationService; +import com.repoachiever.service.state.StateService; + +import java.rmi.RemoteException; +import java.rmi.server.UnicastRemoteObject; + +/** + * Contains implementation of communication provider for RepoAchiever Cluster. + */ +public class ClusterCommunicationResource extends UnicastRemoteObject implements IClusterCommunicationService { + private final PropertiesEntity properties; + + public ClusterCommunicationResource(PropertiesEntity properties) throws RemoteException { + this.properties = properties; + } + + /** + * @see IClusterCommunicationService + */ + @Override + public void performSuspend() throws RemoteException { + StateService.setSuspended(true); + } + + /** + * @see IClusterCommunicationService + */ + @Override + public void performServe() throws RemoteException { + StateService.setSuspended(false); + } + + /** + * @see IClusterCommunicationService + */ + @Override + public Boolean retrieveHealthCheck() throws RemoteException { + return true; + } + + /** + * @see IClusterCommunicationService + */ + @Override + public String retrieveVersion() throws RemoteException { + return properties.getGitCommitId(); + } + + @Override + public Integer retrieveWorkerAmount() throws RemoteException { + return 10; + } +} \ No newline at end of file diff --git a/cluster/src/main/java/com/repoachiever/service/apiserver/resource/ApiServerCommunicationResource.java b/cluster/src/main/java/com/repoachiever/service/apiserver/resource/ApiServerCommunicationResource.java new file mode 100644 index 0000000..d2ab784 --- /dev/null +++ b/cluster/src/main/java/com/repoachiever/service/apiserver/resource/ApiServerCommunicationResource.java @@ -0,0 +1,128 @@ +package com.repoachiever.service.apiserver.resource; + +import com.repoachiever.exception.ApiServerOperationFailureException; +import com.repoachiever.exception.CommunicationConfigurationFailureException; +import com.repoachiever.service.communication.apiserver.IApiServerCommunicationService; +import com.repoachiever.service.communication.common.CommunicationProviderConfigurationHelper; +import com.repoachiever.service.config.ConfigService; +import jakarta.annotation.PostConstruct; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.io.InputStream; +import java.rmi.NotBoundException; +import java.rmi.RemoteException; +import java.rmi.registry.LocateRegistry; +import java.rmi.registry.Registry; +import java.util.Arrays; + +/** + * Represents implementation for RepoAchiever API Server remote API. + */ +@Service +public class ApiServerCommunicationResource { + private static final Logger logger = LogManager.getLogger(ApiServerCommunicationResource.class); + + @Autowired + private ConfigService configService; + + private Registry registry; + + @PostConstruct + private void configure() { + try { + this.registry = LocateRegistry.getRegistry( + configService.getConfig().getCommunication().getPort()); + } catch (RemoteException e) { + logger.fatal(new CommunicationConfigurationFailureException(e.getMessage()).getMessage()); + } + } + + /** + * Retrieves remote RepoAchiever API Server allocation. + * + * @return retrieved RepoAchiever API Server allocation. + * @throws ApiServerOperationFailureException if RepoAchiever API Server operation fails. + */ + private IApiServerCommunicationService retrieveAllocation() throws ApiServerOperationFailureException { + try { + return (IApiServerCommunicationService) registry.lookup( + CommunicationProviderConfigurationHelper.getBindName( + configService.getConfig().getCommunication().getPort(), + configService.getConfig().getCommunication().getApiServerName())); + } catch (RemoteException | NotBoundException e) { + throw new ApiServerOperationFailureException(e.getMessage()); + } + } + + /** + * Performs raw content upload operation. + * + * @param content given content to be uploaded. + * @throws ApiServerOperationFailureException if RepoAchiever API Server operation fails. + */ + public void performRawContentUpload(InputStream content) throws ApiServerOperationFailureException { + IApiServerCommunicationService allocation = retrieveAllocation(); + + try { + allocation.performRawContentUpload( + configService.getConfig().getMetadata().getWorkspaceUnitKey(), + content); + } catch (RemoteException e) { + throw new ApiServerOperationFailureException(e.getMessage()); + } + } + + /** + * Performs additional content(issues, prs, releases) upload operation, initiated by RepoAchiever Cluster. + * + * @param content given content to be uploaded. + * @throws ApiServerOperationFailureException if RepoAchiever API Server operation fails. + */ + public void performAdditionalContentUpload(String content) throws ApiServerOperationFailureException { + IApiServerCommunicationService allocation = retrieveAllocation(); + + try { + allocation.performAdditionalContentUpload( + configService.getConfig().getMetadata().getWorkspaceUnitKey(), + content); + } catch (RemoteException e) { + throw new ApiServerOperationFailureException(e.getMessage()); + } + } + + /** + * Handles incoming log messages related to the RepoAchiever Cluster allocation. + * + * @param message given RepoAchiever Cluster log message. + * @throws ApiServerOperationFailureException if RepoAchiever API Server operation fails. + */ + public void performLogsTransfer(String message) throws ApiServerOperationFailureException { + IApiServerCommunicationService allocation = retrieveAllocation(); + + try { + allocation.performLogsTransfer( + configService.getConfig().getMetadata().getName(), message); + } catch (RemoteException e) { + throw new ApiServerOperationFailureException(e.getMessage()); + } + } + + /** + * Retrieves health check status of RepoAchiever API Server allocation. + * + * @return result of the check. + * @throws ApiServerOperationFailureException if RepoAchiever API Server operation fails. + */ + public Boolean retrieveHealthCheck() throws ApiServerOperationFailureException { + IApiServerCommunicationService allocation = retrieveAllocation(); + + try { + return allocation.retrieveHealthCheck(); + } catch (RemoteException e) { + throw new ApiServerOperationFailureException(e.getMessage()); + } + } +} diff --git a/cluster/src/main/java/com/repoachiever/service/communication/apiserver/IApiServerCommunicationService.java b/cluster/src/main/java/com/repoachiever/service/communication/apiserver/IApiServerCommunicationService.java new file mode 100644 index 0000000..e7e7a15 --- /dev/null +++ b/cluster/src/main/java/com/repoachiever/service/communication/apiserver/IApiServerCommunicationService.java @@ -0,0 +1,45 @@ +package com.repoachiever.service.communication.apiserver; + +import java.io.InputStream; +import java.rmi.Remote; +import java.rmi.RemoteException; + +/** + * Represents communication provider for RepoAchiever API Server. + */ +public interface IApiServerCommunicationService extends Remote { + /** + * Performs raw content upload operation, initiated by RepoAchiever Cluster. + * + * @param workspaceUnitKey given user workspace unit key. + * @param content given content to be uploaded. + * @throws RemoteException if remote request fails. + */ + void performRawContentUpload(String workspaceUnitKey, InputStream content) throws RemoteException; + + /** + * Performs additional content(issues, prs, releases) upload operation, initiated by RepoAchiever Cluster. + * + * @param workspaceUnitKey given user workspace unit key. + * @param content given content to be uploaded. + * @throws RemoteException if remote request fails. + */ + void performAdditionalContentUpload(String workspaceUnitKey, String content) throws RemoteException; + + /** + * Handles incoming log messages related to the given RepoAchiever Cluster allocation. + * + * @param name given RepoAchiever Cluster allocation name. + * @param message given RepoAchiever Cluster log message. + * @throws RemoteException if remote request fails. + */ + void performLogsTransfer(String name, String message) throws RemoteException; + + /** + * Retrieves latest RepoAchiever API Server health check states. + * + * @return RepoAchiever API Server health check status. + * @throws RemoteException if remote request fails. + */ + Boolean retrieveHealthCheck() throws RemoteException; +} \ No newline at end of file diff --git a/cluster/src/main/java/com/repoachiever/service/communication/cluster/IClusterCommunicationService.java b/cluster/src/main/java/com/repoachiever/service/communication/cluster/IClusterCommunicationService.java new file mode 100644 index 0000000..92fc69b --- /dev/null +++ b/cluster/src/main/java/com/repoachiever/service/communication/cluster/IClusterCommunicationService.java @@ -0,0 +1,50 @@ +package com.repoachiever.service.communication.cluster; + +import java.rmi.Remote; +import java.rmi.RemoteException; + +/** Represents client for RepoAchiever Cluster remote API. */ +public interface IClusterCommunicationService extends Remote { + /** + * Performs RepoAchiever Cluster suspend operation. Has no effect if RepoAchiever Cluster was + * already suspended previously. + * + * @throws RemoteException if remote request fails. + */ + void performSuspend() throws RemoteException; + + /** + * Performs RepoAchiever Cluster serve operation. Has no effect if RepoAchiever Cluster was not + * suspended previously. + * + * @throws RemoteException if remote request fails. + */ + void performServe() throws RemoteException; + + /** + * Retrieves latest RepoAchiever Cluster health check states. + * + * @return RepoAchiever Cluster health check status. + * @throws RemoteException if remote request fails. + */ + Boolean retrieveHealthCheck() throws RemoteException; + + /** + * Retrieves version of the allocated RepoAchiever Cluster instance allowing to confirm API + * compatability. + * + * @return RepoAchiever Cluster version. + * @throws RemoteException if remote request fails. + */ + String retrieveVersion() throws RemoteException; + + /** + * Retrieves amount of allocated workers. + * + * @return amount of allocated workers. + * @throws RemoteException if remote request fails. + */ + Integer retrieveWorkerAmount() throws RemoteException; +} + +// TODO: LOCATE ALL RMI RELATED CLASSES AT THE SAME PATH \ No newline at end of file diff --git a/cluster/src/main/java/com/repoachiever/service/communication/common/CommunicationProviderConfigurationHelper.java b/cluster/src/main/java/com/repoachiever/service/communication/common/CommunicationProviderConfigurationHelper.java new file mode 100644 index 0000000..0a5462c --- /dev/null +++ b/cluster/src/main/java/com/repoachiever/service/communication/common/CommunicationProviderConfigurationHelper.java @@ -0,0 +1,15 @@ +package com.repoachiever.service.communication.common; + +/** Contains helpful tools used for communication provider configuration. */ +public class CommunicationProviderConfigurationHelper { + /** + * Composes binding URI declaration for RMI. + * + * @param registryPort given registry port. + * @param suffix given binding suffix. + * @return composed binding URI declaration for RMI. + */ + public static String getBindName(Integer registryPort, String suffix) { + return String.format("//localhost:%d/%s", registryPort, suffix); + } +} \ No newline at end of file diff --git a/cluster/src/main/java/com/repoachiever/service/config/ConfigService.java b/cluster/src/main/java/com/repoachiever/service/config/ConfigService.java new file mode 100644 index 0000000..3f94c23 --- /dev/null +++ b/cluster/src/main/java/com/repoachiever/service/config/ConfigService.java @@ -0,0 +1,106 @@ +package com.repoachiever.service.config; + +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.core.JsonFactory; +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.DeserializationFeature; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.ObjectReader; +import com.repoachiever.entity.ConfigEntity; +import com.repoachiever.entity.PropertiesEntity; +import com.repoachiever.exception.ConfigNotGivenException; +import com.repoachiever.exception.ConfigValidationException; +import jakarta.annotation.PostConstruct; + +import java.io.IOException; +import java.io.InputStream; +import java.util.Objects; +import java.util.Set; +import java.util.stream.Collectors; + +import jakarta.validation.ConstraintViolation; +import jakarta.validation.Validation; +import jakarta.validation.Validator; +import jakarta.validation.ValidatorFactory; +import org.apache.commons.io.IOUtils; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +/** + * Service used to perform RepoAchiever Cluster configuration processing operation. + */ +@Component +public class ConfigService { + private static final Logger logger = LogManager.getLogger(ConfigService.class); + + @Autowired + private PropertiesEntity properties; + + private ConfigEntity parsedConfigFile; + + /** + * Performs configuration file parsing operation. + */ + @PostConstruct + private void configure() { + String clusterContext = properties.getClusterContext(); + + if (Objects.equals(clusterContext, "null")) { + logger.fatal(new ConfigNotGivenException().getMessage()); + return; + } + + InputStream configFile = null; + + try { + configFile = IOUtils.toInputStream(clusterContext, "UTF-8"); + + ObjectMapper mapper = + new ObjectMapper(new JsonFactory()) + .configure(DeserializationFeature.FAIL_ON_NULL_CREATOR_PROPERTIES, true) + .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, true) + .configure(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY, true); + mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL); + ObjectReader reader = mapper.reader().forType(new TypeReference() { + }); + + try { + parsedConfigFile = reader.readValues(configFile).readAll().getFirst(); + } catch (IOException e) { + logger.fatal(e.getMessage()); + return; + } + + try (ValidatorFactory validatorFactory = Validation.buildDefaultValidatorFactory()) { + Validator validator = validatorFactory.getValidator(); + + Set> validationResult = + validator.validate(parsedConfigFile); + + if (!validationResult.isEmpty()) { + logger.fatal(new ConfigValidationException( + validationResult.stream() + .map(ConstraintViolation::getMessage) + .collect(Collectors.joining(", "))).getMessage()); + } + } + } finally { + try { + configFile.close(); + } catch (IOException e) { + logger.fatal(e.getMessage()); + } + } + } + + /** + * Retrieves parsed configuration file entity. + * + * @return retrieved parsed configuration file entity. + */ + public ConfigEntity getConfig() { + return parsedConfigFile; + } +} diff --git a/cluster/src/main/java/com/repoachiever/service/executor/CommandExecutorService.java b/cluster/src/main/java/com/repoachiever/service/executor/CommandExecutorService.java new file mode 100644 index 0000000..8de0220 --- /dev/null +++ b/cluster/src/main/java/com/repoachiever/service/executor/CommandExecutorService.java @@ -0,0 +1,59 @@ +package com.repoachiever.service.executor; + +import com.repoachiever.dto.CommandExecutorOutputDto; +import com.repoachiever.exception.CommandExecutorException; +import java.io.IOException; +import org.springframework.stereotype.Service; +import process.SProcess; +import process.SProcessExecutor; +import process.exceptions.NonMatchingOSException; +import process.exceptions.SProcessNotYetStartedException; + +/** CommandExecutorService provides command execution service. */ +@Service +public class CommandExecutorService { + private final SProcessExecutor processExecutor; + + CommandExecutorService() { + this.processExecutor = SProcessExecutor.getCommandExecutor(); + } + + /** + * Executes given command and gathers its output. + * + * @param command command to be executed + * @return CommandExecutorOutputEntity output, which consists of both stdout and stderr + * @throws CommandExecutorException when command execution fails or output is not gathered + */ + public CommandExecutorOutputDto executeCommand(SProcess command) throws CommandExecutorException { + try { + processExecutor.executeCommand(command); + } catch (IOException | NonMatchingOSException e) { + throw new CommandExecutorException(e.getMessage()); + } + + try { + command.waitForCompletion(); + } catch (SProcessNotYetStartedException | InterruptedException e) { + throw new CommandExecutorException(e.getMessage()); + } + + String commandErrorOutput; + + try { + commandErrorOutput = command.getErrorOutput(); + } catch (SProcessNotYetStartedException e) { + throw new CommandExecutorException(e.getMessage()); + } + + String commandNormalOutput; + + try { + commandNormalOutput = command.getNormalOutput(); + } catch (SProcessNotYetStartedException e) { + throw new CommandExecutorException(e.getMessage()); + } + + return CommandExecutorOutputDto.of(commandNormalOutput, commandErrorOutput); + } +} diff --git a/cluster/src/main/java/com/repoachiever/service/integration/communication/cluster/ClusterCommunicationConfigService.java b/cluster/src/main/java/com/repoachiever/service/integration/communication/cluster/ClusterCommunicationConfigService.java new file mode 100644 index 0000000..9a6b8d0 --- /dev/null +++ b/cluster/src/main/java/com/repoachiever/service/integration/communication/cluster/ClusterCommunicationConfigService.java @@ -0,0 +1,58 @@ +package com.repoachiever.service.integration.communication.cluster; + +import com.repoachiever.entity.PropertiesEntity; +import com.repoachiever.exception.CommunicationConfigurationFailureException; +import com.repoachiever.resource.communication.ClusterCommunicationResource; +import com.repoachiever.service.config.ConfigService; +import com.repoachiever.service.communication.common.CommunicationProviderConfigurationHelper; +import jakarta.annotation.PostConstruct; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import java.rmi.RemoteException; +import java.rmi.registry.LocateRegistry; +import java.rmi.registry.Registry; + +/** + * Service used to perform RepoAchiever Cluster communication provider configuration. + */ +@Component +public class ClusterCommunicationConfigService { + private static final Logger logger = LogManager.getLogger(ClusterCommunicationConfigService.class); + + @Autowired + private ConfigService configService; + + @Autowired + private PropertiesEntity properties; + + /** + * Performs setup of RepoAchiever Cluster communication provider. + */ + @PostConstruct + private void process() { + Registry registry; + + try { + registry = LocateRegistry.getRegistry( + configService.getConfig().getCommunication().getPort()); + } catch (RemoteException e) { + logger.fatal(new CommunicationConfigurationFailureException(e.getMessage()).getMessage()); + return; + } + + Thread.ofPlatform().start(() -> { + try { + registry.rebind( + CommunicationProviderConfigurationHelper.getBindName( + configService.getConfig().getCommunication().getPort(), + configService.getConfig().getMetadata().getName()), + new ClusterCommunicationResource(properties)); + } catch (RemoteException e) { + logger.fatal(e.getMessage()); + } + }); + } +} diff --git a/cluster/src/main/java/com/repoachiever/service/integration/logging/state/LoggingStateService.java b/cluster/src/main/java/com/repoachiever/service/integration/logging/state/LoggingStateService.java new file mode 100644 index 0000000..66e3bdf --- /dev/null +++ b/cluster/src/main/java/com/repoachiever/service/integration/logging/state/LoggingStateService.java @@ -0,0 +1,57 @@ +package com.repoachiever.service.integration.logging.state; + +import com.repoachiever.entity.PropertiesEntity; +import com.repoachiever.service.state.StateService; +import jakarta.annotation.PostConstruct; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.ApplicationContext; +import org.springframework.context.ConfigurableApplicationContext; +import org.springframework.stereotype.Component; + +import java.util.concurrent.*; + +/** + * Service used to handle incoming logging related Ä…pplication state changes. + */ +@Component +public class LoggingStateService { + @Autowired + private ApplicationContext applicationContext; + + @Autowired + private PropertiesEntity properties; + + private final ScheduledExecutorService scheduledExecutorService = + Executors.newScheduledThreadPool(2); + + /** + * Performs application exit if the required state has been changed. + */ + @PostConstruct + private void process() { + scheduledExecutorService.scheduleAtFixedRate(() -> { + if (StateService.getExit()) { + CountDownLatch finalizer = new CountDownLatch(1); + + ScheduledFuture finalizerFeature = + scheduledExecutorService.scheduleAtFixedRate(() -> { + if (StateService.getLogMessagesQueue().isEmpty()) { + finalizer.countDown(); + } + }, 0, + properties.getLoggingStateFinalizerFrequency(), + TimeUnit.MILLISECONDS); + + try { + finalizer.await(); + } catch (InterruptedException ignored) { + } + + finalizerFeature.cancel(true); + + ((ConfigurableApplicationContext) applicationContext).close(); + System.exit(1); + } + }, 0, properties.getLoggingStateFrequency(), TimeUnit.MILLISECONDS); + } +} diff --git a/cluster/src/main/java/com/repoachiever/service/integration/logging/transfer/LoggingTransferService.java b/cluster/src/main/java/com/repoachiever/service/integration/logging/transfer/LoggingTransferService.java new file mode 100644 index 0000000..a7f4cde --- /dev/null +++ b/cluster/src/main/java/com/repoachiever/service/integration/logging/transfer/LoggingTransferService.java @@ -0,0 +1,53 @@ +package com.repoachiever.service.integration.logging.transfer; + +import com.repoachiever.entity.PropertiesEntity; +import com.repoachiever.exception.ApiServerOperationFailureException; +import com.repoachiever.logging.common.LoggingConfigurationHelper; +import com.repoachiever.service.apiserver.resource.ApiServerCommunicationResource; +import com.repoachiever.service.state.StateService; +import jakarta.annotation.PostConstruct; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import java.util.concurrent.*; + +/** + * Service used to handle incoming logging messages to be transferred to RepoAchiever API Server allocation. + */ +@Component +public class LoggingTransferService { + private static final Logger logger = LogManager.getLogger(LoggingTransferService.class); + + @Autowired + private PropertiesEntity properties; + + @Autowired + private ApiServerCommunicationResource apiServerCommunicationResource; + + private final ScheduledExecutorService scheduledExecutorService = + Executors.newScheduledThreadPool(2); + + /** + * Performs application logs transfer to RepoAchiever API Server allocation. + */ + @PostConstruct + private void process() { + logger.info(LoggingConfigurationHelper.getTransferableMessage("it works")); + + scheduledExecutorService.scheduleAtFixedRate(() -> { + if (!StateService.getExit()) { + while (!StateService.getLogMessagesQueue().isEmpty()) { + try { + apiServerCommunicationResource.performLogsTransfer( + StateService.getLogMessagesQueue().poll()); + + } catch (ApiServerOperationFailureException e) { + logger.fatal(e.getMessage()); + } + } + } + }, 0, properties.getLoggingTransferFrequency(), TimeUnit.MILLISECONDS); + } +} \ No newline at end of file diff --git a/cluster/src/main/java/com/repoachiever/service/state/StateService.java b/cluster/src/main/java/com/repoachiever/service/state/StateService.java new file mode 100644 index 0000000..e41bdb6 --- /dev/null +++ b/cluster/src/main/java/com/repoachiever/service/state/StateService.java @@ -0,0 +1,42 @@ +package com.repoachiever.service.state; + +import lombok.Getter; +import lombok.Setter; + +import java.util.concurrent.ConcurrentLinkedQueue; +import java.util.concurrent.locks.ReentrantReadWriteLock; + +/** + * Service used to operate as a collection of application state properties. + */ +public class StateService { + /** + * Represents exit state used to indicate requested application shutdown. + */ + @Getter + @Setter + private static Boolean exit = false; + + /** + * Represents suspended state used to temporary halt execution of RepoAchiever Cluster allocation. By default + * RepoAchiever Cluster is considered to be suspended. + */ + @Getter + @Setter + private static Boolean suspended = true; + + /** + * Represents log message queue used to handle RepoAchiever API Server log message transfer. + */ + @Getter + private final static ConcurrentLinkedQueue logMessagesQueue = new ConcurrentLinkedQueue<>(); + + /** + * Adds new log message to log message queue. + * + * @param message given log message to be added. + */ + public static void addLogMessage(String message) { + logMessagesQueue.add(message); + } +} diff --git a/cluster/src/main/java/com/repoachiever/service/waiter/WaiterHelper.java b/cluster/src/main/java/com/repoachiever/service/waiter/WaiterHelper.java new file mode 100644 index 0000000..ef8d99f --- /dev/null +++ b/cluster/src/main/java/com/repoachiever/service/waiter/WaiterHelper.java @@ -0,0 +1,17 @@ +package com.repoachiever.service.waiter; + +import java.util.concurrent.CountDownLatch; + +/** Represents waiter helper for general usage. */ +public class WaiterHelper { + private static final CountDownLatch latch = new CountDownLatch(1); + + /** Indefinitely waits for manual program execution. */ + public static void waitForExit() { + try { + latch.await(); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + } +} diff --git a/cluster/src/main/resources/application.properties b/cluster/src/main/resources/application.properties new file mode 100644 index 0000000..be1e7f5 --- /dev/null +++ b/cluster/src/main/resources/application.properties @@ -0,0 +1,12 @@ +# Describes Spring related properties. +spring.main.banner-mode=off +spring.main.web-application-type=NONE + +# Describes frequency used to perform logs transfers to RepoAchiever API Server allocation. +logging.transfer.frequency=500 + +# Describes frequency used to perform logging state check. +logging.state.frequency=10 + +# Describes frequency used to perform logging state finalizer check. +logging.state-finalizer.frequency=10 \ No newline at end of file diff --git a/cluster/src/main/resources/log4j2.xml b/cluster/src/main/resources/log4j2.xml new file mode 100644 index 0000000..026a4f3 --- /dev/null +++ b/cluster/src/main/resources/log4j2.xml @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/config/grafana/dashboards/dashboard.yml b/config/grafana/dashboards/dashboard.yml new file mode 100644 index 0000000..60d2246 --- /dev/null +++ b/config/grafana/dashboards/dashboard.yml @@ -0,0 +1,11 @@ +apiVersion: 1 + +providers: + - name: 'Default' + orgId: 1 + folder: '' + type: file + disableDeletion: false + editable: true + options: + path: /etc/grafana/provisioning/dashboards \ No newline at end of file diff --git a/config/grafana/dashboards/diagnostics.tmpl b/config/grafana/dashboards/diagnostics.tmpl new file mode 100644 index 0000000..1214948 --- /dev/null +++ b/config/grafana/dashboards/diagnostics.tmpl @@ -0,0 +1,1831 @@ +{ + "annotations": { + "list": [ + { + "builtIn": 1, + "datasource": "-- Grafana --", + "enable": true, + "hide": true, + "iconColor": "rgba(0, 211, 255, 1)", + "name": "Annotations & Alerts", + "type": "dashboard" + } + ] + }, + "description": "RepoAchiever API Server: ${(info.version)}", + "editable": true, + "gnetId": 179, + "graphTooltip": 1, + "id": 1, + "iteration": 1571330223815, + "links": [], + "panels": [ + { + "collapsed": false, + "datasource": null, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 0 + }, + "id": 17, + "panels": [], + "title": "Host Info", + "type": "row" + }, + { + "cacheTimeout": null, + "colorBackground": false, + "colorValue": false, + "colors": [ + "#299c46", + "rgba(237, 129, 40, 0.89)", + "#d44a3a" + ], + "datasource": "Default", + "decimals": null, + "format": "s", + "gauge": { + "maxValue": 100, + "minValue": 0, + "show": false, + "thresholdLabels": false, + "thresholdMarkers": true + }, + "gridPos": { + "h": 7, + "w": 3, + "x": 0, + "y": 1 + }, + "id": 15, + "interval": null, + "links": [], + "mappingType": 1, + "mappingTypes": [ + { + "name": "value to text", + "value": 1 + }, + { + "name": "range to text", + "value": 2 + } + ], + "maxDataPoints": 100, + "nullPointMode": "connected", + "nullText": null, + "options": {}, + "postfix": "", + "postfixFontSize": "50%", + "prefix": "", + "prefixFontSize": "50%", + "rangeMaps": [ + { + "from": "null", + "text": "N/A", + "to": "null" + } + ], + "sparkline": { + "fillColor": "rgba(31, 118, 189, 0.18)", + "full": false, + "lineColor": "rgb(31, 120, 193)", + "show": false + }, + "tableColumn": "", + "targets": [ + { + "expr": "time() - process_start_time_seconds{job=\"prometheus\"}", + "format": "time_series", + "intervalFactor": 1, + "refId": "A" + } + ], + "thresholds": "", + "title": "Uptime", + "type": "singlestat", + "valueFontSize": "80%", + "valueMaps": [ + { + "op": "=", + "text": "N/A", + "value": "null" + } + ], + "valueName": "current" + }, + { + "cacheTimeout": null, + "colorBackground": false, + "colorValue": false, + "colors": [ + "#299c46", + "rgba(237, 129, 40, 0.89)", + "#d44a3a" + ], + "datasource": "Default", + "format": "short", + "gauge": { + "maxValue": 100, + "minValue": 0, + "show": false, + "thresholdLabels": false, + "thresholdMarkers": true + }, + "gridPos": { + "h": 4, + "w": 3, + "x": 3, + "y": 1 + }, + "id": 35, + "interval": null, + "links": [], + "mappingType": 1, + "mappingTypes": [ + { + "name": "value to text", + "value": 1 + }, + { + "name": "range to text", + "value": 2 + } + ], + "maxDataPoints": 100, + "nullPointMode": "connected", + "nullText": null, + "options": {}, + "postfix": "", + "postfixFontSize": "50%", + "prefix": "", + "prefixFontSize": "50%", + "rangeMaps": [ + { + "from": "null", + "text": "N/A", + "to": "null" + } + ], + "sparkline": { + "fillColor": "rgba(31, 118, 189, 0.18)", + "full": false, + "lineColor": "rgb(31, 120, 193)", + "show": false, + "ymax": null, + "ymin": null + }, + "tableColumn": "", + "targets": [ + { + "expr": "count(count(node_cpu_seconds_total{instance=~\"$node\", mode='system'}) by (cpu))", + "instant": true, + "refId": "A" + } + ], + "thresholds": "", + "timeFrom": null, + "timeShift": null, + "title": "CPU Cores", + "type": "singlestat", + "valueFontSize": "80%", + "valueMaps": [ + { + "op": "=", + "text": "N/A", + "value": "null" + } + ], + "valueName": "current" + }, + { + "cacheTimeout": null, + "colorBackground": true, + "colorValue": false, + "colors": [ + "#299c46", + "rgba(237, 129, 40, 0.89)", + "#d44a3a" + ], + "datasource": "Default", + "format": "none", + "gauge": { + "maxValue": 100, + "minValue": 0, + "show": false, + "thresholdLabels": false, + "thresholdMarkers": true + }, + "gridPos": { + "h": 7, + "w": 5, + "x": 6, + "y": 1 + }, + "id": 13, + "interval": null, + "links": [], + "mappingType": 1, + "mappingTypes": [ + { + "name": "value to text", + "value": 1 + }, + { + "name": "range to text", + "value": 2 + } + ], + "maxDataPoints": 100, + "nullPointMode": "connected", + "nullText": null, + "options": {}, + "postfix": "", + "postfixFontSize": "50%", + "prefix": "", + "prefixFontSize": "50%", + "rangeMaps": [ + { + "from": "null", + "text": "N/A", + "to": "null" + } + ], + "sparkline": { + "fillColor": "rgba(31, 118, 189, 0.18)", + "full": false, + "lineColor": "rgb(31, 120, 193)", + "show": false + }, + "tableColumn": "", + "targets": [ + { + "expr": "sum(ALERTS)", + "format": "time_series", + "intervalFactor": 1, + "refId": "A" + } + ], + "thresholds": "0,1", + "title": "Alerts", + "type": "singlestat", + "valueFontSize": "80%", + "valueMaps": [ + { + "op": "=", + "text": "N/A", + "value": "0" + } + ], + "valueName": "avg" + }, + { + "cacheTimeout": null, + "colorBackground": true, + "colorValue": false, + "colors": [ + "#d44a3a", + "rgba(237, 129, 40, 0.89)", + "#299c46" + ], + "datasource": "Default", + "format": "none", + "gauge": { + "maxValue": 100, + "minValue": 0, + "show": false, + "thresholdLabels": false, + "thresholdMarkers": true + }, + "gridPos": { + "h": 7, + "w": 4, + "x": 11, + "y": 1 + }, + "id": 11, + "interval": null, + "links": [], + "mappingType": 1, + "mappingTypes": [ + { + "name": "value to text", + "value": 1 + }, + { + "name": "range to text", + "value": 2 + } + ], + "maxDataPoints": 100, + "nullPointMode": "connected", + "nullText": null, + "options": {}, + "postfix": "", + "postfixFontSize": "50%", + "prefix": "", + "prefixFontSize": "50%", + "rangeMaps": [ + { + "from": "null", + "text": "N/A", + "to": "null" + } + ], + "sparkline": { + "fillColor": "rgba(31, 118, 189, 0.18)", + "full": false, + "lineColor": "rgb(31, 120, 193)", + "show": false + }, + "tableColumn": "", + "targets": [ + { + "expr": "sum(up)", + "format": "time_series", + "intervalFactor": 1, + "refId": "A" + } + ], + "thresholds": "0,1", + "title": "Targets Online", + "type": "singlestat", + "valueFontSize": "80%", + "valueMaps": [ + { + "op": "=", + "text": "N/A", + "value": "null" + } + ], + "valueName": "current" + }, + { + "cacheTimeout": null, + "colorBackground": false, + "colorValue": false, + "colors": [ + "#d44a3a", + "rgba(237, 129, 40, 0.89)", + "#299c46" + ], + "datasource": "Default", + "format": "none", + "gauge": { + "maxValue": 100, + "minValue": 0, + "show": false, + "thresholdLabels": false, + "thresholdMarkers": true + }, + "gridPos": { + "h": 7, + "w": 4, + "x": 15, + "y": 1 + }, + "id": 31, + "interval": null, + "links": [], + "mappingType": 1, + "mappingTypes": [ + { + "name": "value to text", + "value": 1 + }, + { + "name": "range to text", + "value": 2 + } + ], + "maxDataPoints": 100, + "nullPointMode": "connected", + "nullText": null, + "options": {}, + "postfix": "", + "postfixFontSize": "50%", + "prefix": "", + "prefixFontSize": "50%", + "rangeMaps": [ + { + "from": "null", + "text": "N/A", + "to": "null" + } + ], + "sparkline": { + "fillColor": "rgba(31, 118, 189, 0.18)", + "full": false, + "lineColor": "rgb(31, 120, 193)", + "show": true + }, + "tableColumn": "", + "targets": [ + { + "expr": "count(rate(container_last_seen{job=\"cadvisor\", name!=\"\"}[5m]))", + "format": "time_series", + "intervalFactor": 1, + "refId": "A" + } + ], + "thresholds": "0,1", + "title": "Running Containers", + "type": "singlestat", + "valueFontSize": "80%", + "valueMaps": [ + { + "op": "=", + "text": "N/A", + "value": "null" + } + ], + "valueName": "current" + }, + { + "cacheTimeout": null, + "colorBackground": false, + "colorValue": false, + "colors": [ + "#299c46", + "rgba(237, 129, 40, 0.89)", + "#d44a3a" + ], + "datasource": null, + "decimals": null, + "format": "decbytes", + "gauge": { + "maxValue": 100, + "minValue": 0, + "show": false, + "thresholdLabels": false, + "thresholdMarkers": true + }, + "gridPos": { + "h": 3, + "w": 3, + "x": 3, + "y": 5 + }, + "id": 37, + "interval": null, + "links": [], + "mappingType": 1, + "mappingTypes": [ + { + "name": "value to text", + "value": 1 + }, + { + "name": "range to text", + "value": 2 + } + ], + "maxDataPoints": 100, + "nullPointMode": "connected", + "nullText": null, + "options": {}, + "postfix": "", + "postfixFontSize": "50%", + "prefix": "", + "prefixFontSize": "50%", + "rangeMaps": [ + { + "from": "null", + "text": "N/A", + "to": "null" + } + ], + "sparkline": { + "fillColor": "rgba(31, 118, 189, 0.18)", + "full": false, + "lineColor": "rgb(31, 120, 193)", + "show": false, + "ymax": null, + "ymin": null + }, + "tableColumn": "", + "targets": [ + { + "expr": "node_memory_MemTotal_bytes{instance=~\"$node\"}", + "refId": "A" + } + ], + "thresholds": "", + "timeFrom": null, + "timeShift": null, + "title": "Host Memory", + "type": "singlestat", + "valueFontSize": "80%", + "valueMaps": [ + { + "op": "=", + "text": "N/A", + "value": "null" + } + ], + "valueName": "current" + }, + { + "cacheTimeout": null, + "colorBackground": false, + "colorValue": false, + "colors": [ + "rgba(50, 172, 45, 0.97)", + "rgba(237, 129, 40, 0.89)", + "rgba(245, 54, 54, 0.9)" + ], + "datasource": "Default", + "editable": true, + "error": false, + "format": "percent", + "gauge": { + "maxValue": 100, + "minValue": 0, + "show": true, + "thresholdLabels": false, + "thresholdMarkers": true + }, + "gridPos": { + "h": 6, + "w": 6, + "x": 0, + "y": 8 + }, + "id": 4, + "interval": null, + "isNew": true, + "links": [], + "mappingType": 1, + "mappingTypes": [ + { + "name": "value to text", + "value": 1 + }, + { + "name": "range to text", + "value": 2 + } + ], + "maxDataPoints": 100, + "nullPointMode": "connected", + "nullText": null, + "options": {}, + "postfix": "", + "postfixFontSize": "50%", + "prefix": "", + "prefixFontSize": "50%", + "rangeMaps": [ + { + "from": "null", + "text": "N/A", + "to": "null" + } + ], + "sparkline": { + "fillColor": "rgba(31, 118, 189, 0.18)", + "full": false, + "lineColor": "rgb(31, 120, 193)", + "show": false + }, + "tableColumn": "", + "targets": [ + { + "expr": "(sum(node_memory_MemTotal_bytes) - sum(node_memory_MemFree_bytes +node_memory_Buffers_bytes + node_memory_Cached_bytes) ) / sum(node_memory_MemTotal_bytes) * 100", + "format": "time_series", + "interval": "10s", + "intervalFactor": 1, + "refId": "A", + "step": 10 + } + ], + "thresholds": "65, 90", + "title": "Memory usage", + "type": "singlestat", + "valueFontSize": "80%", + "valueMaps": [ + { + "op": "=", + "text": "N/A", + "value": "null" + } + ], + "valueName": "current" + }, + { + "cacheTimeout": null, + "colorBackground": false, + "colorValue": false, + "colors": [ + "rgba(50, 172, 45, 0.97)", + "rgba(237, 129, 40, 0.89)", + "rgba(245, 54, 54, 0.9)" + ], + "datasource": "Default", + "decimals": 2, + "editable": true, + "error": false, + "format": "percent", + "gauge": { + "maxValue": 100, + "minValue": 0, + "show": true, + "thresholdLabels": false, + "thresholdMarkers": true + }, + "gridPos": { + "h": 6, + "w": 6, + "x": 6, + "y": 8 + }, + "id": 6, + "interval": null, + "isNew": true, + "links": [], + "mappingType": 1, + "mappingTypes": [ + { + "name": "value to text", + "value": 1 + }, + { + "name": "range to text", + "value": 2 + } + ], + "maxDataPoints": 100, + "nullPointMode": "connected", + "nullText": null, + "options": {}, + "postfix": "", + "postfixFontSize": "50%", + "prefix": "", + "prefixFontSize": "50%", + "rangeMaps": [ + { + "from": "null", + "text": "N/A", + "to": "null" + } + ], + "sparkline": { + "fillColor": "rgba(31, 118, 189, 0.18)", + "full": false, + "lineColor": "rgb(31, 120, 193)", + "show": false + }, + "tableColumn": "", + "targets": [ + { + "expr": "100 - (avg(irate(node_cpu_seconds_total{instance=~\"$node\",mode=\"idle\"}[5m])) * 100)", + "format": "time_series", + "interval": "1m", + "intervalFactor": 1, + "legendFormat": "", + "refId": "A", + "step": 10 + } + ], + "thresholds": "65, 90", + "title": "CPU usage", + "type": "singlestat", + "valueFontSize": "80%", + "valueMaps": [ + { + "op": "=", + "text": "N/A", + "value": "null" + } + ], + "valueName": "current" + }, + { + "cacheTimeout": null, + "colorBackground": false, + "colorValue": false, + "colors": [ + "rgba(50, 172, 45, 0.97)", + "rgba(237, 129, 40, 0.89)", + "rgba(245, 54, 54, 0.9)" + ], + "datasource": "Default", + "decimals": 2, + "editable": true, + "error": false, + "format": "percent", + "gauge": { + "maxValue": 100, + "minValue": 0, + "show": true, + "thresholdLabels": false, + "thresholdMarkers": true + }, + "gridPos": { + "h": 6, + "w": 7, + "x": 12, + "y": 8 + }, + "id": 7, + "interval": null, + "isNew": true, + "links": [], + "mappingType": 1, + "mappingTypes": [ + { + "name": "value to text", + "value": 1 + }, + { + "name": "range to text", + "value": 2 + } + ], + "maxDataPoints": 100, + "nullPointMode": "connected", + "nullText": null, + "options": {}, + "postfix": "", + "postfixFontSize": "50%", + "prefix": "", + "prefixFontSize": "50%", + "rangeMaps": [ + { + "from": "null", + "text": "N/A", + "to": "null" + } + ], + "sparkline": { + "fillColor": "rgba(31, 118, 189, 0.18)", + "full": false, + "lineColor": "rgb(31, 120, 193)", + "show": false + }, + "tableColumn": "", + "targets": [ + { + "expr": "avg( node_filesystem_avail_bytes {mountpoint=\"/\"} / node_filesystem_size_bytes{mountpoint=\"/\"})", + "interval": "10s", + "intervalFactor": 1, + "metric": "", + "refId": "A", + "step": 10 + } + ], + "thresholds": "65, 90", + "title": "Filesystem usage", + "type": "singlestat", + "valueFontSize": "80%", + "valueMaps": [ + { + "op": "=", + "text": "N/A", + "value": "null" + } + ], + "valueName": "current" + }, + { + "aliasColors": { + "RECEIVE": "#ea6460", + "SENT": "#1f78c1", + "TRANSMIT": "#1f78c1" + }, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "Default", + "fill": 4, + "fillGradient": 0, + "gridPos": { + "h": 9, + "w": 6, + "x": 0, + "y": 14 + }, + "id": 25, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "options": { + "dataLinks": [] + }, + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(container_network_receive_bytes_total{id=\"/\"}[$interval])) by (id)", + "format": "time_series", + "interval": "2m", + "intervalFactor": 2, + "legendFormat": "RECEIVE", + "refId": "A" + }, + { + "expr": "- sum(rate(container_network_transmit_bytes_total{id=\"/\"}[$interval])) by (id)", + "format": "time_series", + "interval": "2m", + "intervalFactor": 2, + "legendFormat": "TRANSMIT", + "refId": "B" + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "Node Network Traffic", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "cumulative" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "Bps", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "s", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "aliasColors": { + "Available Memory": "#508642", + "Used Memory": "#bf1b00" + }, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "Default", + "fill": 3, + "fillGradient": 0, + "gridPos": { + "h": 9, + "w": 6, + "x": 6, + "y": 14 + }, + "id": 27, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "options": { + "dataLinks": [] + }, + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": true, + "steppedLine": false, + "targets": [ + { + "expr": "sum(node_memory_MemTotal_bytes) - sum(node_memory_MemAvailable_bytes)", + "format": "time_series", + "interval": "2m", + "intervalFactor": 2, + "legendFormat": "Used Memory", + "refId": "B" + }, + { + "expr": "sum(node_memory_MemAvailable_bytes)", + "format": "time_series", + "interval": "2m", + "intervalFactor": 2, + "legendFormat": "Available Memory", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "Node Mermory", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "decbytes", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "s", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "aliasColors": { + "Available Memory": "#508642", + "Free Storage": "#447ebc", + "Total Storage Available": "#508642", + "Used Memory": "#bf1b00", + "Used Storage": "#bf1b00" + }, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "Default", + "fill": 3, + "fillGradient": 0, + "gridPos": { + "h": 9, + "w": 7, + "x": 12, + "y": 14 + }, + "id": 28, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "options": { + "dataLinks": [] + }, + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": true, + "steppedLine": false, + "targets": [ + { + "expr": "sum(node_filesystem_free_bytes {job=\"node-exporter\", instance=~\".*${(nodeexporter.port)}\", device=~\"/dev/.*\", mountpoint!=\"/var/lib/docker/aufs\"}) ", + "format": "time_series", + "interval": "2m", + "intervalFactor": 2, + "legendFormat": "Free Storage", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "Filesystem Available", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "decimals": null, + "format": "decbytes", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "s", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "collapsed": false, + "datasource": null, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 23 + }, + "id": 19, + "panels": [], + "repeat": null, + "title": "Container Performance", + "type": "row" + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "Default", + "decimals": 3, + "editable": true, + "error": false, + "fill": 0, + "fillGradient": 0, + "grid": {}, + "gridPos": { + "h": 10, + "w": 6, + "x": 0, + "y": 24 + }, + "id": 3, + "isNew": true, + "legend": { + "alignAsTable": true, + "avg": true, + "current": true, + "max": false, + "min": false, + "rightSide": false, + "show": true, + "sort": "current", + "sortDesc": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 2, + "links": [], + "nullPointMode": "connected", + "options": { + "dataLinks": [] + }, + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(container_cpu_usage_seconds_total{image!=\"\"}[1m])) by (id,name)", + "format": "time_series", + "interval": "10s", + "intervalFactor": 1, + "legendFormat": "{{ name }}", + "metric": "container_cpu_user_seconds_total", + "refId": "A", + "step": 10 + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "Container CPU usage", + "tooltip": { + "msResolution": true, + "shared": true, + "sort": 0, + "value_type": "cumulative" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "percentunit", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "Default", + "decimals": 2, + "editable": true, + "error": false, + "fill": 0, + "fillGradient": 0, + "grid": {}, + "gridPos": { + "h": 10, + "w": 6, + "x": 6, + "y": 24 + }, + "id": 2, + "isNew": true, + "legend": { + "alignAsTable": true, + "avg": true, + "current": true, + "max": false, + "min": false, + "rightSide": false, + "show": true, + "sort": "current", + "sortDesc": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 2, + "links": [], + "nullPointMode": "connected", + "options": { + "dataLinks": [] + }, + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "container_memory_max_usage_bytes{image!=\"\"}", + "format": "time_series", + "interval": "10s", + "intervalFactor": 1, + "legendFormat": "{{ name }}", + "metric": "container_memory_usage:sort_desc", + "refId": "A", + "step": 10 + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "Container Memory Usage", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "cumulative" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "bytes", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "columns": [], + "datasource": "Default", + "fontSize": "100%", + "gridPos": { + "h": 13, + "w": 10, + "x": 12, + "y": 24 + }, + "id": 23, + "links": [], + "options": {}, + "pageSize": null, + "scroll": true, + "showHeader": true, + "sort": { + "col": 0, + "desc": true + }, + "styles": [ + { + "alias": "Time", + "dateFormat": "YYYY-MM-DD HH:mm:ss", + "pattern": "Time", + "type": "date" + }, + { + "alias": "", + "colorMode": null, + "colors": [ + "rgba(245, 54, 54, 0.9)", + "rgba(237, 129, 40, 0.89)", + "rgba(50, 172, 45, 0.97)" + ], + "decimals": 2, + "pattern": "/.*/", + "thresholds": [], + "type": "number", + "unit": "short" + } + ], + "targets": [ + { + "expr": "ALERTS", + "format": "table", + "intervalFactor": 1, + "refId": "A" + } + ], + "title": "Alerts", + "transform": "table", + "type": "table" + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "Default", + "decimals": 2, + "editable": true, + "error": false, + "fill": 0, + "fillGradient": 0, + "grid": {}, + "gridPos": { + "h": 14, + "w": 6, + "x": 0, + "y": 34 + }, + "id": 8, + "isNew": true, + "legend": { + "alignAsTable": true, + "avg": true, + "current": true, + "max": false, + "min": false, + "rightSide": false, + "show": true, + "sort": "current", + "sortDesc": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 2, + "links": [], + "nullPointMode": "connected", + "options": { + "dataLinks": [] + }, + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sort_desc(sum by (name) (rate(container_network_receive_bytes_total{image!=\"\"}[1m] ) ))", + "interval": "10s", + "intervalFactor": 1, + "legendFormat": "{{ name }}", + "metric": "container_network_receive_bytes_total", + "refId": "A", + "step": 10 + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "Container Network Input", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "cumulative" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "bytes", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "Default", + "decimals": 2, + "editable": true, + "error": false, + "fill": 0, + "fillGradient": 0, + "grid": {}, + "gridPos": { + "h": 14, + "w": 6, + "x": 6, + "y": 34 + }, + "id": 9, + "isNew": true, + "legend": { + "alignAsTable": true, + "avg": true, + "current": true, + "max": false, + "min": false, + "rightSide": false, + "show": true, + "sort": "current", + "sortDesc": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 2, + "links": [], + "nullPointMode": "connected", + "options": { + "dataLinks": [] + }, + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sort_desc(sum by (name) (rate(container_network_transmit_bytes_total{image!=\"\"}[1m] ) ))", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{ name }}", + "metric": "container_network_transmit_bytes_total", + "refId": "B", + "step": 4 + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "Container Network Output", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "cumulative" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "bytes", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": false + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "columns": [], + "datasource": "Default", + "fontSize": "100%", + "gridPos": { + "h": 10, + "w": 10, + "x": 12, + "y": 37 + }, + "id": 30, + "links": [], + "options": {}, + "pageSize": 10, + "scroll": true, + "showHeader": true, + "sort": { + "col": 0, + "desc": true + }, + "styles": [ + { + "alias": "Time", + "dateFormat": "YYYY-MM-DD HH:mm:ss", + "link": false, + "linkUrl": "", + "pattern": "Time", + "type": "date" + }, + { + "alias": "", + "colorMode": null, + "colors": [ + "rgba(245, 54, 54, 0.9)", + "rgba(237, 129, 40, 0.89)", + "rgba(50, 172, 45, 0.97)" + ], + "decimals": 2, + "pattern": "/.*/", + "thresholds": [], + "type": "number", + "unit": "short" + } + ], + "targets": [ + { + "expr": "cadvisor_version_info", + "format": "table", + "instant": false, + "interval": "15m", + "intervalFactor": 2, + "legendFormat": "cAdvisor Version: {{cadvisorVersion}}", + "refId": "A" + }, + { + "expr": "prometheus_build_info", + "format": "table", + "interval": "15m", + "intervalFactor": 2, + "legendFormat": "Prometheus Version: {{version}}", + "refId": "B" + }, + { + "expr": "node_exporter_build_info", + "format": "table", + "interval": "15m", + "intervalFactor": 2, + "legendFormat": "Node-Exporter Version: {{version}}", + "refId": "C" + } + ], + "title": "Running Versions", + "transform": "table", + "type": "table" + } + ], + "refresh": "10s", + "schemaVersion": 20, + "style": "dark", + "tags": [ + "docker", + "prometheus, ", + "node-exporter", + "cadvisor" + ], + "templating": { + "list": [ + { + "auto": false, + "auto_count": 30, + "auto_min": "10s", + "current": { + "text": "1m", + "value": "1m" + }, + "hide": 0, + "label": "interval", + "name": "interval", + "options": [ + { + "selected": true, + "text": "1m", + "value": "1m" + }, + { + "selected": false, + "text": "10m", + "value": "10m" + }, + { + "selected": false, + "text": "30m", + "value": "30m" + }, + { + "selected": false, + "text": "1h", + "value": "1h" + }, + { + "selected": false, + "text": "6h", + "value": "6h" + }, + { + "selected": false, + "text": "12h", + "value": "12h" + }, + { + "selected": false, + "text": "1d", + "value": "1d" + }, + { + "selected": false, + "text": "7d", + "value": "7d" + }, + { + "selected": false, + "text": "14d", + "value": "14d" + }, + { + "selected": false, + "text": "30d", + "value": "30d" + } + ], + "query": "1m,10m,30m,1h,6h,12h,1d,7d,14d,30d", + "refresh": 2, + "skipUrlSync": false, + "type": "interval" + }, + { + "allValue": null, + "current": { + "text": "All", + "value": "$__all" + }, + "datasource": "Default", + "definition": "label_values(node_exporter_build_info{name=~'$name'},instance)", + "hide": 0, + "includeAll": true, + "label": "IP", + "multi": true, + "name": "node", + "options": [], + "query": "label_values(node_exporter_build_info{name=~'$name'},instance)", + "refresh": 2, + "regex": "", + "skipUrlSync": false, + "sort": 1, + "tagValuesQuery": "", + "tags": [], + "tagsQuery": "", + "type": "query", + "useTags": false + }, + { + "allValue": null, + "current": { + "text": "All", + "value": "$__all" + }, + "datasource": "Default", + "definition": "label_values(node_exporter_build_info,env)", + "hide": 0, + "includeAll": true, + "label": "Env", + "multi": true, + "name": "env", + "options": [], + "query": "label_values(node_exporter_build_info,env)", + "refresh": 2, + "regex": "", + "skipUrlSync": false, + "sort": 0, + "tagValuesQuery": "", + "tags": [], + "tagsQuery": "", + "type": "query", + "useTags": false + }, + { + "allValue": null, + "current": { + "text": "All", + "value": "$__all" + }, + "datasource": "Default", + "definition": "label_values(node_exporter_build_info{env=~'$env'},name)", + "hide": 0, + "includeAll": true, + "label": "CPU Name", + "multi": true, + "name": "name", + "options": [], + "query": "label_values(node_exporter_build_info{env=~'$env'},name)", + "refresh": 2, + "regex": "", + "skipUrlSync": false, + "sort": 0, + "tagValuesQuery": "", + "tags": [], + "tagsQuery": "", + "type": "query", + "useTags": false + } + ] + }, + "time": { + "from": "now-5m", + "to": "now" + }, + "timepicker": { + "refresh_intervals": [ + "5s", + "10s", + "30s", + "1m", + "5m", + "15m", + "30m", + "1h", + "2h", + "1d" + ], + "time_options": [ + "5m", + "15m", + "1h", + "6h", + "12h", + "24h", + "2d", + "7d", + "30d" + ] + }, + "timezone": "browser", + "title": "RepoAchiever Diagnostics", + "uid": "64nrElFmk", + "version": 2 +} \ No newline at end of file diff --git a/config/grafana/datasources/datasource.tmpl b/config/grafana/datasources/datasource.tmpl new file mode 100644 index 0000000..064ca10 --- /dev/null +++ b/config/grafana/datasources/datasource.tmpl @@ -0,0 +1,19 @@ +apiVersion: 1 + +deleteDatasources: + - name: Default + orgId: 1 + +datasources: + - name: Default + type: prometheus + access: proxy + orgId: 1 + url: http://${(prometheus.host)}:${(prometheus.port)} + isDefault: true + jsonData: + graphiteVersion: "1.1" + tlsAuth: false + tlsAuthWithCACert: false + version: 1 + editable: false diff --git a/config/prometheus/prometheus.tmpl b/config/prometheus/prometheus.tmpl new file mode 100644 index 0000000..f2ded30 --- /dev/null +++ b/config/prometheus/prometheus.tmpl @@ -0,0 +1,18 @@ +global: + scrape_interval: 5s + evaluation_interval: 5s + +scrape_configs: + - job_name: 'prometheus' + + scrape_interval: 15s + + static_configs: + - targets: ['${(metrics.host)}:${(metrics.port)}'] + + - job_name: 'node-exporter' + + scrape_interval: 15s + + static_configs: + - targets: ['${(nodeexporter.host)}:${(nodeexporter.port)}'] diff --git a/docs/detailed-design-raw.md b/docs/detailed-design-raw.md new file mode 100644 index 0000000..046c305 --- /dev/null +++ b/docs/detailed-design-raw.md @@ -0,0 +1,76 @@ +```plantuml +!pragma teoz true + +title + Detailed design of "ResourceTracker" +end title + +actor "Client" as client + +box "Control plain" #MOTIVATION +participant "API Server" as apiserver + +box "Cloud environment" #Lavender +queue "Kafka" as kafka +participant "Kafka starter" as kafkastarter +participant "Agent" as agent +entity "Cloud provider" as cloudprovider +end box + +end box + +note over [[[[[[kafka]]]]]]: Kafka is considered to be used in persisted mode + +opt "endpoints" +opt "/v1/secrets/acquire [POST]" +apiserver -> cloudprovider: validate provided credentials +cloudprovider -> apiserver: validation result +end +opt "/v1/topic/logs [GET]" +apiserver -> kafka: retrieve state for the given "logs" topic +kafka -> apiserver: transform data stream according to the specified filters +end +opt "/v1/terraform/apply [POST]" +apiserver -> cloudprovider: deploy resource tracking infrastructure +apiserver -> kafkastarter: request kafka cluster startup +kafkastarter -> kafka: start kafka cluster +end +opt "/v1/terraform/destroy [POST]" +apiserver -> cloudprovider: destroy resource tracking infrastructure +end +end + +opt "agent execution flow" +agent -> cloudprovider: execute remote operations +agent <-- cloudprovider: remote operation result +agent --> kafka: push latest resource state to "logs" topic +end + +opt "requests" +note over client: Uses properties specified in a client\nconfiguration file located in\n a common directory +opt "credentials validation" +client -> apiserver: /v1/secrets/acquire [POST] +end +opt "script validation" +client -> apiserver: /v1/script/acquire [POST] +end +opt "health check" +client -> apiserver: /v1/health [GET] +end +opt "readiness check" +client -> apiserver: /v1/readiness [GET] +end +opt "version validation" +client -> apiserver: /v1/info/version [GET] +end +opt "infrustructure deployment" +client -> apiserver: /v1/terraform/apply [POST] +end +opt "infrustructure clean up" +client -> apiserver: /v1/terraform/destroy [POST] +end +opt "state retrieval" +client -> apiserver: /v1/topic/logs [GET] +end +end +``` \ No newline at end of file diff --git a/docs/detailed-design.png b/docs/detailed-design.png new file mode 100644 index 0000000..2c30540 Binary files /dev/null and b/docs/detailed-design.png differ diff --git a/docs/high-level-design-raw.md b/docs/high-level-design-raw.md new file mode 100644 index 0000000..0f4c954 --- /dev/null +++ b/docs/high-level-design-raw.md @@ -0,0 +1,23 @@ +```plantuml +title + +High-level design of "RepoArchiver" + +end title + +actor "Client" + +component "Control plane" { +node "API Server" +node "Prometheus" + +cloud " Decentralized environment" { +node "Cluster" +entity "Worker" +} + +[Client] <--> [API Server]: " Send requests" +[API Server] <-> [Cluster]: " Schedule content processing" +[Cluster] <--> [Worker]: " Propagate individual requests " +[API Server] --> [Prometheus]: " Export diagnostics" +``` \ No newline at end of file diff --git a/docs/high-level-design.png b/docs/high-level-design.png new file mode 100644 index 0000000..d589fff Binary files /dev/null and b/docs/high-level-design.png differ diff --git a/docs/workspace-design.md b/docs/workspace-design.md new file mode 100644 index 0000000..1c72563 --- /dev/null +++ b/docs/workspace-design.md @@ -0,0 +1,18 @@ +```plantuml +@startwbs +title + +Workspace design of "RepoArchiver API Server" + +end title + +* Workspace unit +** Content +*** Certain repository +**** Repository version +** Metadata +*** PRs +*** Issues +*** Releases +@endwbs +``` \ No newline at end of file diff --git a/docs/workspace-design.png b/docs/workspace-design.png new file mode 100644 index 0000000..a53a228 Binary files /dev/null and b/docs/workspace-design.png differ diff --git a/exporter/pom.xml b/exporter/pom.xml new file mode 100644 index 0000000..77859cb --- /dev/null +++ b/exporter/pom.xml @@ -0,0 +1,21 @@ + + + 4.0.0 + + com.repoachiever + base + 1.0-SNAPSHOT + + + org.example + exporter + + + 21 + 21 + UTF-8 + + + \ No newline at end of file diff --git a/exporter/src/main/java/org/example/Main.java b/exporter/src/main/java/org/example/Main.java new file mode 100644 index 0000000..407f157 --- /dev/null +++ b/exporter/src/main/java/org/example/Main.java @@ -0,0 +1,7 @@ +package org.example; + +public class Main { + public static void main(String[] args) { + System.out.println("Hello world!"); + } +} \ No newline at end of file diff --git a/gui/.DS_Store b/gui/.DS_Store new file mode 100644 index 0000000..1cc341e Binary files /dev/null and b/gui/.DS_Store differ diff --git a/gui/gui.iml b/gui/gui.iml new file mode 100644 index 0000000..056f882 --- /dev/null +++ b/gui/gui.iml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/gui/pom.xml b/gui/pom.xml new file mode 100644 index 0000000..025ae62 --- /dev/null +++ b/gui/pom.xml @@ -0,0 +1,206 @@ + + + 4.0.0 + gui + 1.0-SNAPSHOT + gui + GUI for ResourceTracker + + + com.repoachiever + base + 1.0-SNAPSHOT + + + + + com.repoachiever.GUI + + + + + + + Shell-Command-Executor-Lib + Shell-Command-Executor-Lib + system + ${basedir}/../lib/Shell-Command-Executor-Lib-0.5.0-SNAPSHOT.jar + + + ink.bluecloud + elementfx + system + ${basedir}/../lib/ElementFX-1.3-SNAPSHOT.jar + + + + + org.openjfx + javafx-controls + + + com.brunomnsilva + smartgraph + + + + + org.jetbrains.kotlin + kotlin-stdlib + + + + + org.springframework.boot + spring-boot-starter-validation + + + org.springframework.boot + spring-boot-starter-webflux + + + org.springframework + spring-context + + + + + io.netty + netty-resolver-dns-native-macos + osx-aarch_64 + + + + + org.openapitools + jackson-databind-nullable + + + + + jakarta.annotation + jakarta.annotation-api + + + jakarta.validation + jakarta.validation-api + + + jakarta.servlet + jakarta.servlet-api + + + jakarta.ws.rs + jakarta.ws.rs-api + + + org.projectlombok + lombok + + + org.yaml + snakeyaml + + + com.fasterxml.jackson.dataformat + jackson-dataformat-yaml + + + com.opencsv + opencsv + + + commons-io + commons-io + + + io.github.kostaskougios + cloning + + + + + org.apache.logging.log4j + log4j-api + + + org.apache.logging.log4j + log4j-core + + + + gui + + + org.springframework.boot + spring-boot-maven-plugin + + true + + + + org.apache.maven.plugins + maven-compiler-plugin + + + org.openjfx + javafx-maven-plugin + + + org.openapitools + openapi-generator-maven-plugin + + + + generate + + + ${project.basedir}/../api-server/src/main/openapi/openapi.yml + java + webclient + ${default.package}.api + ${default.package}.model + true + false + false + false + false + false + true + false + + @lombok.Data @lombok.AllArgsConstructor(staticName = "of") + src/main/java + true + false + true + true + false + true + java8 + true + true + + + + + + + pl.project13.maven + git-commit-id-plugin + + + com.coderplus.maven.plugins + copy-rename-maven-plugin + + + + ${basedir}/target/gui.jar + ${main.basedir}/../bin/gui/gui.jar + + + + + + + diff --git a/gui/src/.DS_Store b/gui/src/.DS_Store new file mode 100644 index 0000000..e77e13b Binary files /dev/null and b/gui/src/.DS_Store differ diff --git a/gui/src/main/.DS_Store b/gui/src/main/.DS_Store new file mode 100644 index 0000000..879ce8e Binary files /dev/null and b/gui/src/main/.DS_Store differ diff --git a/gui/src/main/java/com/repoachiever/App.java b/gui/src/main/java/com/repoachiever/App.java new file mode 100644 index 0000000..668ba82 --- /dev/null +++ b/gui/src/main/java/com/repoachiever/App.java @@ -0,0 +1,81 @@ +package com.repoachiever; + +import com.repoachiever.service.client.observer.ResourceObserver; +import com.repoachiever.service.element.font.FontLoader; +import com.repoachiever.service.element.observer.ElementObserver; +import com.repoachiever.service.element.stage.MainStage; +import com.repoachiever.service.event.state.LocalState; +import com.repoachiever.service.scheduler.SchedulerHelper; +import javafx.application.Application; +import javafx.application.HostServices; +import javafx.application.Platform; +import javafx.stage.Stage; +import lombok.SneakyThrows; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.builder.SpringApplicationBuilder; +import org.springframework.context.ApplicationContextInitializer; +import org.springframework.context.ConfigurableApplicationContext; +import org.springframework.context.support.GenericApplicationContext; + +/** Represents entrypoint for the application. */ +public class App extends Application { + private ConfigurableApplicationContext applicationContext; + + @Autowired private LocalState localState; + + @Autowired private ElementObserver elementObserver; + + @Autowired private ResourceObserver resourceObserver; + + @Autowired private FontLoader fontLoader; + + @Autowired private MainStage mainStage; + + /** + * @see Application + */ + public void launch() { + Application.launch(); + } + + /** + * @see Application + */ + @Override + public void init() { + System.setProperty("apple.laf.useScreenMenuBar", "true"); + System.setProperty("apple.awt.UIElement", "true"); + + ApplicationContextInitializer initializer = + applicationContext -> { + applicationContext.registerBean(Application.class, () -> App.this); + applicationContext.registerBean(Parameters.class, this::getParameters); + applicationContext.registerBean(HostServices.class, this::getHostServices); + }; + + applicationContext = + new SpringApplicationBuilder() + .sources(GUI.class) + .initializers(initializer) + .run(getParameters().getRaw().toArray(new String[0])); + } + + /** + * @see Application + */ + @Override + public void stop() { + applicationContext.close(); + SchedulerHelper.close(); + Platform.exit(); + } + + /** + * @see Application + */ + @Override + @SneakyThrows + public void start(Stage stage) { + mainStage.getContent().show(); + } +} diff --git a/gui/src/main/java/com/repoachiever/GUI.java b/gui/src/main/java/com/repoachiever/GUI.java new file mode 100644 index 0000000..1bd752d --- /dev/null +++ b/gui/src/main/java/com/repoachiever/GUI.java @@ -0,0 +1,11 @@ +package com.repoachiever; + +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +public class GUI { + public static void main(String[] args) { + App app = new App(); + app.launch(); + } +} diff --git a/gui/src/main/java/com/repoachiever/converter/CredentialsConverter.java b/gui/src/main/java/com/repoachiever/converter/CredentialsConverter.java new file mode 100644 index 0000000..e6abb88 --- /dev/null +++ b/gui/src/main/java/com/repoachiever/converter/CredentialsConverter.java @@ -0,0 +1,11 @@ +package com.repoachiever.converter; + +import com.fasterxml.jackson.databind.ObjectMapper; + +public class CredentialsConverter { + @SuppressWarnings("unchecked") + public static T convert(Object input, Class stub) { + ObjectMapper mapper = new ObjectMapper(); + return mapper.convertValue(input, stub); + } +} diff --git a/gui/src/main/java/com/repoachiever/dto/CommandExecutorOutputDto.java b/gui/src/main/java/com/repoachiever/dto/CommandExecutorOutputDto.java new file mode 100644 index 0000000..0051755 --- /dev/null +++ b/gui/src/main/java/com/repoachiever/dto/CommandExecutorOutputDto.java @@ -0,0 +1,13 @@ +package com.repoachiever.dto; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +/** Represents gathered output of the executed command. */ +@Getter +@AllArgsConstructor(staticName = "of") +public class CommandExecutorOutputDto { + private String normalOutput; + + private String errorOutput; +} diff --git a/gui/src/main/java/com/repoachiever/dto/HealthCheckInternalCommandResultDto.java b/gui/src/main/java/com/repoachiever/dto/HealthCheckInternalCommandResultDto.java new file mode 100644 index 0000000..122fda8 --- /dev/null +++ b/gui/src/main/java/com/repoachiever/dto/HealthCheckInternalCommandResultDto.java @@ -0,0 +1,13 @@ +package com.repoachiever.dto; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +/** Represents readiness check command result. */ +@Getter +@AllArgsConstructor(staticName = "of") +public class HealthCheckInternalCommandResultDto { + private Boolean status; + + private String error; +} diff --git a/gui/src/main/java/com/repoachiever/dto/ReadinessCheckInternalCommandResultDto.java b/gui/src/main/java/com/repoachiever/dto/ReadinessCheckInternalCommandResultDto.java new file mode 100644 index 0000000..159ed89 --- /dev/null +++ b/gui/src/main/java/com/repoachiever/dto/ReadinessCheckInternalCommandResultDto.java @@ -0,0 +1,13 @@ +package com.repoachiever.dto; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +/** Represents readiness check command result. */ +@Getter +@AllArgsConstructor(staticName = "of") +public class ReadinessCheckInternalCommandResultDto { + private Boolean status; + + private String error; +} diff --git a/gui/src/main/java/com/repoachiever/dto/StartExternalCommandResultDto.java b/gui/src/main/java/com/repoachiever/dto/StartExternalCommandResultDto.java new file mode 100644 index 0000000..8030ae6 --- /dev/null +++ b/gui/src/main/java/com/repoachiever/dto/StartExternalCommandResultDto.java @@ -0,0 +1,13 @@ +package com.repoachiever.dto; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +/** Represents start deployment command result. */ +@Getter +@AllArgsConstructor(staticName = "of") +public class StartExternalCommandResultDto { + private Boolean status; + + private String error; +} diff --git a/gui/src/main/java/com/repoachiever/dto/StateExternalCommandResultDto.java b/gui/src/main/java/com/repoachiever/dto/StateExternalCommandResultDto.java new file mode 100644 index 0000000..927de4d --- /dev/null +++ b/gui/src/main/java/com/repoachiever/dto/StateExternalCommandResultDto.java @@ -0,0 +1,16 @@ +package com.repoachiever.dto; + +import com.repoachiever.model.TopicLogsResult; +import lombok.AllArgsConstructor; +import lombok.Getter; + +/** Represents state command result. */ +@Getter +@AllArgsConstructor(staticName = "of") +public class StateExternalCommandResultDto { + private TopicLogsResult topicLogsResult; + + private Boolean status; + + private String error; +} diff --git a/gui/src/main/java/com/repoachiever/dto/StopExternalCommandResultDto.java b/gui/src/main/java/com/repoachiever/dto/StopExternalCommandResultDto.java new file mode 100644 index 0000000..881b7fe --- /dev/null +++ b/gui/src/main/java/com/repoachiever/dto/StopExternalCommandResultDto.java @@ -0,0 +1,13 @@ +package com.repoachiever.dto; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +/** Represents stop deployment command result. */ +@Getter +@AllArgsConstructor(staticName = "of") +public class StopExternalCommandResultDto { + private Boolean status; + + private String error; +} diff --git a/gui/src/main/java/com/repoachiever/dto/ValidationScriptApplicationDto.java b/gui/src/main/java/com/repoachiever/dto/ValidationScriptApplicationDto.java new file mode 100644 index 0000000..1749e85 --- /dev/null +++ b/gui/src/main/java/com/repoachiever/dto/ValidationScriptApplicationDto.java @@ -0,0 +1,12 @@ +package com.repoachiever.dto; + +import java.util.List; +import lombok.AllArgsConstructor; +import lombok.Getter; + +/** Represents script validation application used for script acquiring process. */ +@Getter +@AllArgsConstructor(staticName = "of") +public class ValidationScriptApplicationDto { + private List fileContent; +} diff --git a/gui/src/main/java/com/repoachiever/dto/ValidationSecretsApplicationDto.java b/gui/src/main/java/com/repoachiever/dto/ValidationSecretsApplicationDto.java new file mode 100644 index 0000000..288b23c --- /dev/null +++ b/gui/src/main/java/com/repoachiever/dto/ValidationSecretsApplicationDto.java @@ -0,0 +1,14 @@ +package com.repoachiever.dto; + +import com.repoachiever.model.Provider; +import lombok.AllArgsConstructor; +import lombok.Getter; + +/** Represents secrets validation application used for secrets acquiring process. */ +@Getter +@AllArgsConstructor(staticName = "of") +public class ValidationSecretsApplicationDto { + private Provider provider; + + private String filePath; +} diff --git a/gui/src/main/java/com/repoachiever/dto/VersionExternalCommandResultDto.java b/gui/src/main/java/com/repoachiever/dto/VersionExternalCommandResultDto.java new file mode 100644 index 0000000..622e8db --- /dev/null +++ b/gui/src/main/java/com/repoachiever/dto/VersionExternalCommandResultDto.java @@ -0,0 +1,15 @@ +package com.repoachiever.dto; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +/** Represents version retrieval command result. */ +@Getter +@AllArgsConstructor(staticName = "of") +public class VersionExternalCommandResultDto { + private String data; + + private Boolean status; + + private String error; +} diff --git a/gui/src/main/java/com/repoachiever/entity/ConfigEntity.java b/gui/src/main/java/com/repoachiever/entity/ConfigEntity.java new file mode 100644 index 0000000..c88e21d --- /dev/null +++ b/gui/src/main/java/com/repoachiever/entity/ConfigEntity.java @@ -0,0 +1,66 @@ +package com.repoachiever.entity; + +import com.fasterxml.jackson.annotation.JsonFormat; +import com.fasterxml.jackson.annotation.JsonProperty; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.Pattern; +import java.util.List; +import lombok.Getter; + +/** Represents configuration model used for ResourceTracker deployment operation. */ +@Getter +public class ConfigEntity { + /** Represents request to be executed in remote environment. */ + @Getter + public static class Request { + @NotBlank String name; + + @Pattern(regexp = "^(((./)?)|((~/.)?)|((/?))?)([a-zA-Z/]*)((\\.([a-z]+))?)$") + String file; + + @Pattern( + regexp = + "(((([0-9]|[0-5][0-9])(-([0-9]|[0-5][0-9]))?,)*([0-9]|[0-5][0-9])(-([0-9]|[0-5][0-9]))?)|(([\\*]|[0-9]|[0-5][0-9])/([0-9]|[0-5][0-9]))|([\\?])|([\\*]))[\\s](((([0-9]|[0-5][0-9])(-([0-9]|[0-5][0-9]))?,)*([0-9]|[0-5][0-9])(-([0-9]|[0-5][0-9]))?)|(([\\*]|[0-9]|[0-5][0-9])/([0-9]|[0-5][0-9]))|([\\?])|([\\*]))[\\s](((([0-9]|[0-1][0-9]|[2][0-3])(-([0-9]|[0-1][0-9]|[2][0-3]))?,)*([0-9]|[0-1][0-9]|[2][0-3])(-([0-9]|[0-1][0-9]|[2][0-3]))?)|(([\\*]|[0-9]|[0-1][0-9]|[2][0-3])/([0-9]|[0-1][0-9]|[2][0-3]))|([\\?])|([\\*]))[\\s](((([1-9]|[0][1-9]|[1-2][0-9]|[3][0-1])(-([1-9]|[0][1-9]|[1-2][0-9]|[3][0-1]))?,)*([1-9]|[0][1-9]|[1-2][0-9]|[3][0-1])(-([1-9]|[0][1-9]|[1-2][0-9]|[3][0-1]))?(C)?)|(([1-9]|[0][1-9]|[1-2][0-9]|[3][0-1])/([1-9]|[0][1-9]|[1-2][0-9]|[3][0-1])(C)?)|(L(-[0-9])?)|(L(-[1-2][0-9])?)|(L(-[3][0-1])?)|(LW)|([1-9]W)|([1-3][0-9]W)|([\\?])|([\\*]))[\\s](((([1-9]|0[1-9]|1[0-2])(-([1-9]|0[1-9]|1[0-2]))?,)*([1-9]|0[1-9]|1[0-2])(-([1-9]|0[1-9]|1[0-2]))?)|(([1-9]|0[1-9]|1[0-2])/([1-9]|0[1-9]|1[0-2]))|(((JAN|FEB|MAR|APR|MAY|JUN|JUL|AUG|SEP|OCT|NOV|DEC)(-(JAN|FEB|MAR|APR|MAY|JUN|JUL|AUG|SEP|OCT|NOV|DEC))?,)*(JAN|FEB|MAR|APR|MAY|JUN|JUL|AUG|SEP|OCT|NOV|DEC)(-(JAN|FEB|MAR|APR|MAY|JUN|JUL|AUG|SEP|OCT|NOV|DEC))?)|((JAN|FEB|MAR|APR|MAY|JUN|JUL|AUG|SEP|OCT|NOV|DEC)/(JAN|FEB|MAR|APR|MAY|JUN|JUL|AUG|SEP|OCT|NOV|DEC))|([\\?])|([\\*]))[\\s]((([1-7](-([1-7]))?,)*([1-7])(-([1-7]))?)|([1-7]/([1-7]))|(((MON|TUE|WED|THU|FRI|SAT|SUN)(-(MON|TUE|WED|THU|FRI|SAT|SUN))?,)*(MON|TUE|WED|THU|FRI|SAT|SUN)(-(MON|TUE|WED|THU|FRI|SAT|SUN))?(C)?)|((MON|TUE|WED|THU|FRI|SAT|SUN)/(MON|TUE|WED|THU|FRI|SAT|SUN)(C)?)|(([1-7]|(MON|TUE|WED|THU|FRI|SAT|SUN))?(L|LW)?)|(([1-7]|MON|TUE|WED|THU|FRI|SAT|SUN)#([1-7])?)|([\\?])|([\\*]))([\\s]?(([\\*])?|(19[7-9][0-9])|(20[0-9][0-9]))?|" + + " (((19[7-9][0-9])|(20[0-9][0-9]))/((19[7-9][0-9])|(20[0-9][0-9])))?|" + + " ((((19[7-9][0-9])|(20[0-9][0-9]))(-((19[7-9][0-9])|(20[0-9][0-9])))?,)*((19[7-9][0-9])|(20[0-9][0-9]))(-((19[7-9][0-9])|(20[0-9][0-9])))?)?)") + String frequency; + } + + List requests; + + /** + * Represents remove cloud infrastructure configuration properties used for further deployment + * related operations. + */ + @Getter + public static class Cloud { + @JsonFormat(shape = JsonFormat.Shape.OBJECT) + public enum Provider { + @JsonProperty("aws") + AWS, + } + + @NotBlank Provider provider; + + @Getter + public static class AWSCredentials { + @Pattern(regexp = "^(((./)?)|((~/.)?)|((/?))?)([a-zA-Z/]*)((\\.([a-z]+))?)$") + String file; + + @NotBlank String region; + } + + @NotBlank Object credentials; + } + + Cloud cloud; + + /** Represents API Server configuration used for further connection establishment. */ + @Getter + public static class APIServer { + @NotBlank String host; + } + + @JsonProperty("api-server") + APIServer apiServer; +} diff --git a/gui/src/main/java/com/repoachiever/entity/PropertiesEntity.java b/gui/src/main/java/com/repoachiever/entity/PropertiesEntity.java new file mode 100644 index 0000000..6e35d20 --- /dev/null +++ b/gui/src/main/java/com/repoachiever/entity/PropertiesEntity.java @@ -0,0 +1,211 @@ +package com.repoachiever.entity; + +import lombok.Getter; +import org.apache.commons.lang3.StringUtils; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.support.PropertySourcesPlaceholderConfigurer; +import org.springframework.core.io.ClassPathResource; + +/** Represents application properties used for application configuration. */ +@Getter +@Configuration +public class PropertiesEntity { + private static final String GIT_CONFIG_PROPERTIES_FILE = "git.properties"; + + @Value(value = "${window.main.name}") + private String windowMainName; + + @Value(value = "${window.main.scale.min.width}") + private Double windowMainScaleMinWidth; + + @Value(value = "${window.main.scale.min.height}") + private Double windowMainScaleMinHeight; + + @Value(value = "${window.main.scale.max.width}") + private Double windowMainScaleMaxWidth; + + @Value(value = "${window.main.scale.max.height}") + private Double windowMainScaleMaxHeight; + + @Value(value = "${window.settings.name}") + private String windowSettingsName; + + @Value(value = "${window.settings.scale.width}") + private Double windowSettingsScaleWidth; + + @Value(value = "${window.settings.scale.height}") + private Double windowSettingsScaleHeight; + + @Value(value = "${process.background.period}") + private Integer processBackgroundPeriod; + + @Value(value = "${process.healthcheck.period}") + private Integer processHealthcheckPeriod; + + @Value(value = "${process.readiness.period}") + private Integer processReadinessPeriod; + + @Value(value = "${process.window.width.period}") + private Integer processWindowWidthPeriod; + + @Value(value = "${process.window.height.period}") + private Integer processWindowHeightPeriod; + + @Value(value = "${spinner.initial.delay}") + private Integer spinnerInitialDelay; + + @Value(value = "${button.basic.size.width}") + private Double basicButtonSizeWidth; + + @Value(value = "${button.basic.size.height}") + private Double basicButtonSizeHeight; + + @Value(value = "${scene.general.background.color.r}") + private Integer generalBackgroundColorR; + + @Value(value = "${scene.general.background.color.g}") + private Integer generalBackgroundColorG; + + @Value(value = "${scene.general.background.color.b}") + private Integer generalBackgroundColorB; + + @Value(value = "${scene.common.header.background.color.r}") + private Integer commonSceneHeaderBackgroundColorR; + + @Value(value = "${scene.common.header.background.color.g}") + private Integer commonSceneHeaderBackgroundColorG; + + @Value(value = "${scene.common.header.background.color.b}") + private Integer commonSceneHeaderBackgroundColorB; + + @Value(value = "${scene.common.header.connection.background.color.r}") + private Integer commonSceneHeaderConnectionStatusBackgroundColorR; + + @Value(value = "${scene.common.header.connection.background.color.g}") + private Integer commonSceneHeaderConnectionStatusBackgroundColorG; + + @Value(value = "${scene.common.header.connection.background.color.b}") + private Integer commonSceneHeaderConnectionStatusBackgroundColorB; + + @Value(value = "${scene.common.menu.background.color.r}") + private Integer commonSceneMenuBackgroundColorR; + + @Value(value = "${scene.common.menu.background.color.g}") + private Integer commonSceneMenuBackgroundColorG; + + @Value(value = "${scene.common.menu.background.color.b}") + private Integer commonSceneMenuBackgroundColorB; + + @Value(value = "${scene.common.content.background.color.r}") + private Integer commonSceneContentBackgroundColorR; + + @Value(value = "${scene.common.content.background.color.g}") + private Integer commonSceneContentBackgroundColorG; + + @Value(value = "${scene.common.content.background.color.b}") + private Integer commonSceneContentBackgroundColorB; + + @Value(value = "${scene.common.content.vertical-gap}") + private Double commonSceneContentVerticalGap; + + @Value(value = "${scene.common.content.bar.horizontal-gap}") + private Double sceneCommonContentBarHorizontalGap; + + @Value(value = "${scene.common.footer.background.color.r}") + private Integer commonSceneFooterBackgroundColorR; + + @Value(value = "${scene.common.footer.background.color.g}") + private Integer commonSceneFooterBackgroundColorG; + + @Value(value = "${scene.common.footer.background.color.b}") + private Integer commonSceneFooterBackgroundColorB; + + @Value(value = "${image.status.scale}") + private Double statusImageScale; + + @Value(value = "${font.default.name}") + private String fontDefaultName; + + @Value(value = "${image.icon.name}") + private String imageIconName; + + @Value(value = "${image.arrow.name}") + private String imageArrowName; + + @Value(value = "${image.edit.name}") + private String imageEditName; + + @Value(value = "${image.refresh.name}") + private String imageRefreshName; + + @Value(value = "${image.start.name}") + private String imageStartName; + + @Value(value = "${image.stop.name}") + private String imageStopName; + + @Value(value = "${image.bar.width}") + private Integer imageBarWidth; + + @Value(value = "${image.bar.height}") + private Integer imageBarHeight; + + @Value(value = "${list-view.stub.name}") + private String listViewStubName; + + @Value(value = "${alert.api-server-unavailable.message}") + private String alertApiServerUnavailableMessage; + + @Value(value = "${alert.deployment-finished.message}") + private String alertDeploymentFinishedMessage; + + @Value(value = "${alert.destruction-finished.message}") + private String alertDestructionFinishedMessage; + + @Value(value = "${alert.version-mismatch.message}") + private String alertVersionMismatchMessage; + + @Value(value = "${alert.editor-close-reminder.message}") + private String alertEditorCloseReminderMessage; + + @Value(value = "${graph.css.location}") + private String graphCssFileLocation; + + @Value(value = "${graph.properties.location}") + private String graphPropertiesLocation; + + @Value(value = "${config.root}") + private String configRootPath; + + @Value(value = "${swap.root}") + private String swapRootPath; + + @Value(value = "${config.user.file}") + private String configUserFilePath; + + @Value(value = "${api-server.directory}") + private String apiServerDirectory; + + @Value(value = "${git.commit.id.abbrev}") + private String gitCommitId; + + @Bean + private static PropertySourcesPlaceholderConfigurer placeholderConfigurer() { + PropertySourcesPlaceholderConfigurer propsConfig = new PropertySourcesPlaceholderConfigurer(); + propsConfig.setLocation(new ClassPathResource(GIT_CONFIG_PROPERTIES_FILE)); + propsConfig.setIgnoreResourceNotFound(true); + propsConfig.setIgnoreUnresolvablePlaceholders(true); + return propsConfig; + } + + /** + * Removes the last symbol in git commit id of the repository. + * + * @return chopped repository git commit id. + */ + public String getGitCommitId() { + return StringUtils.chop(gitCommitId); + } +} diff --git a/gui/src/main/java/com/repoachiever/exception/ApiServerException.java b/gui/src/main/java/com/repoachiever/exception/ApiServerException.java new file mode 100644 index 0000000..d89b655 --- /dev/null +++ b/gui/src/main/java/com/repoachiever/exception/ApiServerException.java @@ -0,0 +1,18 @@ +package com.repoachiever.exception; + +import java.io.IOException; +import java.util.Arrays; +import java.util.Formatter; + +public class ApiServerException extends IOException { + public ApiServerException() { + this(""); + } + + public ApiServerException(Object... message) { + super( + new Formatter() + .format("API Server exception: %s", Arrays.stream(message).toArray()) + .toString()); + } +} diff --git a/gui/src/main/java/com/repoachiever/exception/ApiServerNotAvailableException.java b/gui/src/main/java/com/repoachiever/exception/ApiServerNotAvailableException.java new file mode 100644 index 0000000..f8d332a --- /dev/null +++ b/gui/src/main/java/com/repoachiever/exception/ApiServerNotAvailableException.java @@ -0,0 +1,10 @@ +package com.repoachiever.exception; + +import java.io.IOException; +import java.util.Formatter; + +public class ApiServerNotAvailableException extends IOException { + public ApiServerNotAvailableException(Object... message) { + super(new Formatter().format("API Server is not available", message).toString()); + } +} diff --git a/gui/src/main/java/com/repoachiever/exception/ApplicationFontFileNotFoundException.java b/gui/src/main/java/com/repoachiever/exception/ApplicationFontFileNotFoundException.java new file mode 100644 index 0000000..77e759c --- /dev/null +++ b/gui/src/main/java/com/repoachiever/exception/ApplicationFontFileNotFoundException.java @@ -0,0 +1,16 @@ +package com.repoachiever.exception; + +import java.io.IOException; +import java.util.Arrays; +import java.util.Formatter; + +public class ApplicationFontFileNotFoundException extends IOException { + public ApplicationFontFileNotFoundException(Object... message) { + super( + new Formatter() + .format( + "Application font file at the given location is not available: %s", + Arrays.stream(message).toArray()) + .toString()); + } +} diff --git a/gui/src/main/java/com/repoachiever/exception/ApplicationImageFileNotFoundException.java b/gui/src/main/java/com/repoachiever/exception/ApplicationImageFileNotFoundException.java new file mode 100644 index 0000000..ec1ed6c --- /dev/null +++ b/gui/src/main/java/com/repoachiever/exception/ApplicationImageFileNotFoundException.java @@ -0,0 +1,20 @@ +package com.repoachiever.exception; + +import java.io.IOException; +import java.util.Arrays; +import java.util.Formatter; + +public class ApplicationImageFileNotFoundException extends IOException { + public ApplicationImageFileNotFoundException() { + this(""); + } + + public ApplicationImageFileNotFoundException(Object... message) { + super( + new Formatter() + .format( + "Application image file at the given location is not available: %s", + Arrays.stream(message).toArray()) + .toString()); + } +} diff --git a/gui/src/main/java/com/repoachiever/exception/CloudCredentialsFileNotFoundException.java b/gui/src/main/java/com/repoachiever/exception/CloudCredentialsFileNotFoundException.java new file mode 100644 index 0000000..43961c6 --- /dev/null +++ b/gui/src/main/java/com/repoachiever/exception/CloudCredentialsFileNotFoundException.java @@ -0,0 +1,19 @@ +package com.repoachiever.exception; + +import java.io.IOException; +import java.util.Arrays; +import java.util.Formatter; + +public class CloudCredentialsFileNotFoundException extends IOException { + public CloudCredentialsFileNotFoundException() { + this(""); + } + + public CloudCredentialsFileNotFoundException(Object... message) { + super( + new Formatter() + .format( + "Given cloud credentials file is not found: %s", Arrays.stream(message).toArray()) + .toString()); + } +} diff --git a/gui/src/main/java/com/repoachiever/exception/CloudCredentialsValidationException.java b/gui/src/main/java/com/repoachiever/exception/CloudCredentialsValidationException.java new file mode 100644 index 0000000..713b34f --- /dev/null +++ b/gui/src/main/java/com/repoachiever/exception/CloudCredentialsValidationException.java @@ -0,0 +1,18 @@ +package com.repoachiever.exception; + +import java.io.IOException; +import java.util.Arrays; +import java.util.Formatter; + +public class CloudCredentialsValidationException extends IOException { + public CloudCredentialsValidationException() { + this(""); + } + + public CloudCredentialsValidationException(Object... message) { + super( + new Formatter() + .format("Given cloud credentials are not valid!: %s", Arrays.stream(message).toArray()) + .toString()); + } +} diff --git a/gui/src/main/java/com/repoachiever/exception/CommandExecutorException.java b/gui/src/main/java/com/repoachiever/exception/CommandExecutorException.java new file mode 100644 index 0000000..600bcb3 --- /dev/null +++ b/gui/src/main/java/com/repoachiever/exception/CommandExecutorException.java @@ -0,0 +1,10 @@ +package com.repoachiever.exception; + +import java.io.IOException; +import java.util.Formatter; + +public class CommandExecutorException extends IOException { + public CommandExecutorException(Object... message) { + super(new Formatter().format("Invalid command executor behaviour", message).toString()); + } +} diff --git a/gui/src/main/java/com/repoachiever/exception/ScriptDataValidationException.java b/gui/src/main/java/com/repoachiever/exception/ScriptDataValidationException.java new file mode 100644 index 0000000..17250fb --- /dev/null +++ b/gui/src/main/java/com/repoachiever/exception/ScriptDataValidationException.java @@ -0,0 +1,19 @@ +package com.repoachiever.exception; + +import java.io.IOException; +import java.util.Arrays; +import java.util.Formatter; + +/** Represents exception, when given file is not valid. */ +public class ScriptDataValidationException extends IOException { + public ScriptDataValidationException() { + this(""); + } + + public ScriptDataValidationException(Object... message) { + super( + new Formatter() + .format("Given explicit script file is not valid: %s", Arrays.stream(message).toArray()) + .toString()); + } +} diff --git a/gui/src/main/java/com/repoachiever/exception/SmartGraphCssFileNotFoundException.java b/gui/src/main/java/com/repoachiever/exception/SmartGraphCssFileNotFoundException.java new file mode 100644 index 0000000..856d7e8 --- /dev/null +++ b/gui/src/main/java/com/repoachiever/exception/SmartGraphCssFileNotFoundException.java @@ -0,0 +1,13 @@ +package com.repoachiever.exception; + +import java.io.FileNotFoundException; +import java.util.Formatter; + +public class SmartGraphCssFileNotFoundException extends FileNotFoundException { + public SmartGraphCssFileNotFoundException(Object... message) { + super( + new Formatter() + .format("SmartGraph CSS file at the given location is not available", message) + .toString()); + } +} diff --git a/gui/src/main/java/com/repoachiever/exception/SmartGraphPropertiesFileNotFoundException.java b/gui/src/main/java/com/repoachiever/exception/SmartGraphPropertiesFileNotFoundException.java new file mode 100644 index 0000000..5701c9f --- /dev/null +++ b/gui/src/main/java/com/repoachiever/exception/SmartGraphPropertiesFileNotFoundException.java @@ -0,0 +1,13 @@ +package com.repoachiever.exception; + +import java.io.FileNotFoundException; +import java.util.Formatter; + +public class SmartGraphPropertiesFileNotFoundException extends FileNotFoundException { + public SmartGraphPropertiesFileNotFoundException(Object... message) { + super( + new Formatter() + .format("SmartGraph properties file at the given location is not available", message) + .toString()); + } +} diff --git a/gui/src/main/java/com/repoachiever/exception/SwapFileCreationFailedException.java b/gui/src/main/java/com/repoachiever/exception/SwapFileCreationFailedException.java new file mode 100644 index 0000000..c14abf4 --- /dev/null +++ b/gui/src/main/java/com/repoachiever/exception/SwapFileCreationFailedException.java @@ -0,0 +1,19 @@ +package com.repoachiever.exception; + +import java.io.IOException; +import java.util.Arrays; +import java.util.Formatter; + +/** Represents exception, when swap file creation failed. */ +public class SwapFileCreationFailedException extends IOException { + public SwapFileCreationFailedException() { + this(""); + } + + public SwapFileCreationFailedException(Object... message) { + super( + new Formatter() + .format("Swap file creation failed: %s", Arrays.stream(message).toArray()) + .toString()); + } +} diff --git a/gui/src/main/java/com/repoachiever/exception/SwapFileDeletionFailedException.java b/gui/src/main/java/com/repoachiever/exception/SwapFileDeletionFailedException.java new file mode 100644 index 0000000..5f4a5d1 --- /dev/null +++ b/gui/src/main/java/com/repoachiever/exception/SwapFileDeletionFailedException.java @@ -0,0 +1,19 @@ +package com.repoachiever.exception; + +import java.io.IOException; +import java.util.Arrays; +import java.util.Formatter; + +/** Represents exception, when swap file deletion failed. */ +public class SwapFileDeletionFailedException extends IOException { + public SwapFileDeletionFailedException() { + this(""); + } + + public SwapFileDeletionFailedException(Object... message) { + super( + new Formatter() + .format("Swap file deletion failed: %s", Arrays.stream(message).toArray()) + .toString()); + } +} diff --git a/gui/src/main/java/com/repoachiever/service/client/command/ApplyClientCommandService.java b/gui/src/main/java/com/repoachiever/service/client/command/ApplyClientCommandService.java new file mode 100644 index 0000000..274c3c6 --- /dev/null +++ b/gui/src/main/java/com/repoachiever/service/client/command/ApplyClientCommandService.java @@ -0,0 +1,52 @@ +package com.repoachiever.service.client.command; + +import com.repoachiever.ApiClient; +import com.repoachiever.api.TerraformResourceApi; +import com.repoachiever.exception.ApiServerException; +import com.repoachiever.exception.ApiServerNotAvailableException; +import com.repoachiever.model.TerraformDeploymentApplication; +import com.repoachiever.model.TerraformDeploymentApplicationResult; +import com.repoachiever.service.client.common.IClientCommand; +import com.repoachiever.service.config.ConfigService; +import jakarta.annotation.PostConstruct; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.web.reactive.function.client.WebClientRequestException; +import org.springframework.web.reactive.function.client.WebClientResponseException; + +/** Represents apply client command service. */ +@Service +public class ApplyClientCommandService + implements IClientCommand< + TerraformDeploymentApplicationResult, TerraformDeploymentApplication> { + @Autowired private ConfigService configService; + + private TerraformResourceApi terraformResourceApi; + + /** + * @see IClientCommand + */ + @Override + @PostConstruct + public void configure() { + ApiClient apiClient = + new ApiClient().setBasePath(configService.getConfig().getApiServer().getHost()); + + this.terraformResourceApi = new TerraformResourceApi(apiClient); + } + + /** + * @see IClientCommand + */ + @Override + public TerraformDeploymentApplicationResult process(TerraformDeploymentApplication input) + throws ApiServerException { + try { + return terraformResourceApi.v1TerraformApplyPost(input).block(); + } catch (WebClientResponseException e) { + throw new ApiServerException(e.getResponseBodyAsString()); + } catch (WebClientRequestException e) { + throw new ApiServerException(new ApiServerNotAvailableException(e.getMessage()).getMessage()); + } + } +} diff --git a/gui/src/main/java/com/repoachiever/service/client/command/DestroyClientCommandService.java b/gui/src/main/java/com/repoachiever/service/client/command/DestroyClientCommandService.java new file mode 100644 index 0000000..fa4ffd1 --- /dev/null +++ b/gui/src/main/java/com/repoachiever/service/client/command/DestroyClientCommandService.java @@ -0,0 +1,47 @@ +package com.repoachiever.service.client.command; + +import com.repoachiever.ApiClient; +import com.repoachiever.api.TerraformResourceApi; +import com.repoachiever.exception.ApiServerException; +import com.repoachiever.exception.ApiServerNotAvailableException; +import com.repoachiever.model.TerraformDestructionApplication; +import com.repoachiever.service.client.common.IClientCommand; +import com.repoachiever.service.config.ConfigService; +import jakarta.annotation.PostConstruct; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.web.reactive.function.client.WebClientRequestException; +import org.springframework.web.reactive.function.client.WebClientResponseException; + +/** Represents destroy client command service. */ +@Service +public class DestroyClientCommandService + implements IClientCommand { + @Autowired private ConfigService configService; + private TerraformResourceApi terraformResourceApi; + + /** + * @see IClientCommand + */ + @Override + @PostConstruct + public void configure() { + ApiClient apiClient = + new ApiClient().setBasePath(configService.getConfig().getApiServer().getHost()); + + this.terraformResourceApi = new TerraformResourceApi(apiClient); + } + + /** + * @see IClientCommand + */ + public Void process(TerraformDestructionApplication input) throws ApiServerException { + try { + return terraformResourceApi.v1TerraformDestroyPost(input).block(); + } catch (WebClientResponseException e) { + throw new ApiServerException(e.getResponseBodyAsString()); + } catch (WebClientRequestException e) { + throw new ApiServerException(new ApiServerNotAvailableException(e.getMessage()).getMessage()); + } + } +} diff --git a/gui/src/main/java/com/repoachiever/service/client/command/HealthCheckClientCommandService.java b/gui/src/main/java/com/repoachiever/service/client/command/HealthCheckClientCommandService.java new file mode 100644 index 0000000..9a7162f --- /dev/null +++ b/gui/src/main/java/com/repoachiever/service/client/command/HealthCheckClientCommandService.java @@ -0,0 +1,47 @@ +package com.repoachiever.service.client.command; + +import com.repoachiever.ApiClient; +import com.repoachiever.api.HealthResourceApi; +import com.repoachiever.exception.ApiServerException; +import com.repoachiever.exception.ApiServerNotAvailableException; +import com.repoachiever.model.HealthCheckResult; +import com.repoachiever.service.client.common.IClientCommand; +import com.repoachiever.service.config.ConfigService; +import jakarta.annotation.PostConstruct; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.web.reactive.function.client.WebClientRequestException; +import org.springframework.web.reactive.function.client.WebClientResponseException; + +/** Represents health check client command service. */ +@Service +public class HealthCheckClientCommandService implements IClientCommand { + @Autowired private ConfigService configService; + + private HealthResourceApi healthResourceApi; + + /** + * @see IClientCommand + */ + @Override + @PostConstruct + public void configure() { + ApiClient apiClient = + new ApiClient().setBasePath(configService.getConfig().getApiServer().getHost()); + + this.healthResourceApi = new HealthResourceApi(apiClient); + } + + /** + * @see IClientCommand + */ + public HealthCheckResult process(Void input) throws ApiServerException { + try { + return healthResourceApi.v1HealthGet().block(); + } catch (WebClientResponseException e) { + throw new ApiServerException(e.getResponseBodyAsString()); + } catch (WebClientRequestException e) { + throw new ApiServerException(new ApiServerNotAvailableException(e.getMessage()).getMessage()); + } + } +} diff --git a/gui/src/main/java/com/repoachiever/service/client/command/LogsClientCommandService.java b/gui/src/main/java/com/repoachiever/service/client/command/LogsClientCommandService.java new file mode 100644 index 0000000..6ff0026 --- /dev/null +++ b/gui/src/main/java/com/repoachiever/service/client/command/LogsClientCommandService.java @@ -0,0 +1,46 @@ +package com.repoachiever.service.client.command; + +import com.repoachiever.ApiClient; +import com.repoachiever.api.TopicResourceApi; +import com.repoachiever.exception.ApiServerException; +import com.repoachiever.exception.ApiServerNotAvailableException; +import com.repoachiever.model.TopicLogsApplication; +import com.repoachiever.model.TopicLogsResult; +import com.repoachiever.service.client.common.IClientCommand; +import com.repoachiever.service.config.ConfigService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.web.reactive.function.client.WebClientRequestException; +import org.springframework.web.reactive.function.client.WebClientResponseException; + +/** Represents logs topic client command service. */ +@Service +public class LogsClientCommandService + implements IClientCommand { + private final TopicResourceApi topicResourceApi; + + public LogsClientCommandService(@Autowired ConfigService configService) { + ApiClient apiClient = + new ApiClient().setBasePath(configService.getConfig().getApiServer().getHost()); + + this.topicResourceApi = new TopicResourceApi(apiClient); + } + + /** */ + @Override + public void configure() {} + + /** + * @see IClientCommand + */ + @Override + public TopicLogsResult process(TopicLogsApplication input) throws ApiServerException { + try { + return topicResourceApi.v1TopicLogsPost(input).block(); + } catch (WebClientResponseException e) { + throw new ApiServerException(e.getResponseBodyAsString()); + } catch (WebClientRequestException e) { + throw new ApiServerException(new ApiServerNotAvailableException(e.getMessage()).getMessage()); + } + } +} diff --git a/gui/src/main/java/com/repoachiever/service/client/command/ReadinessCheckClientCommandService.java b/gui/src/main/java/com/repoachiever/service/client/command/ReadinessCheckClientCommandService.java new file mode 100644 index 0000000..75a3b9f --- /dev/null +++ b/gui/src/main/java/com/repoachiever/service/client/command/ReadinessCheckClientCommandService.java @@ -0,0 +1,49 @@ +package com.repoachiever.service.client.command; + +import com.repoachiever.ApiClient; +import com.repoachiever.api.HealthResourceApi; +import com.repoachiever.exception.ApiServerException; +import com.repoachiever.exception.ApiServerNotAvailableException; +import com.repoachiever.model.ReadinessCheckApplication; +import com.repoachiever.model.ReadinessCheckResult; +import com.repoachiever.service.client.common.IClientCommand; +import com.repoachiever.service.config.ConfigService; +import jakarta.annotation.PostConstruct; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.web.reactive.function.client.WebClientRequestException; +import org.springframework.web.reactive.function.client.WebClientResponseException; + +/** Represents readiness check client command service. */ +@Service +public class ReadinessCheckClientCommandService + implements IClientCommand { + @Autowired private ConfigService configService; + + private HealthResourceApi healthResourceApi; + + /** + * @see IClientCommand + */ + @Override + @PostConstruct + public void configure() { + ApiClient apiClient = + new ApiClient().setBasePath(configService.getConfig().getApiServer().getHost()); + + this.healthResourceApi = new HealthResourceApi(apiClient); + } + + /** + * @see IClientCommand + */ + public ReadinessCheckResult process(ReadinessCheckApplication input) throws ApiServerException { + try { + return healthResourceApi.v1ReadinessPost(input).block(); + } catch (WebClientResponseException e) { + throw new ApiServerException(e.getResponseBodyAsString()); + } catch (WebClientRequestException e) { + throw new ApiServerException(new ApiServerNotAvailableException(e.getMessage()).getMessage()); + } + } +} diff --git a/gui/src/main/java/com/repoachiever/service/client/command/ScriptAcquireClientCommandService.java b/gui/src/main/java/com/repoachiever/service/client/command/ScriptAcquireClientCommandService.java new file mode 100644 index 0000000..d2c089c --- /dev/null +++ b/gui/src/main/java/com/repoachiever/service/client/command/ScriptAcquireClientCommandService.java @@ -0,0 +1,54 @@ +package com.repoachiever.service.client.command; + +import com.repoachiever.ApiClient; +import com.repoachiever.api.ValidationResourceApi; +import com.repoachiever.dto.ValidationScriptApplicationDto; +import com.repoachiever.exception.ApiServerException; +import com.repoachiever.exception.ApiServerNotAvailableException; +import com.repoachiever.model.ValidationScriptApplication; +import com.repoachiever.model.ValidationScriptApplicationResult; +import com.repoachiever.service.client.common.IClientCommand; +import com.repoachiever.service.config.ConfigService; +import jakarta.annotation.PostConstruct; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.web.reactive.function.client.WebClientRequestException; +import org.springframework.web.reactive.function.client.WebClientResponseException; + +/** Represents script validation client command service. */ +@Service +public class ScriptAcquireClientCommandService + implements IClientCommand { + @Autowired private ConfigService configService; + + private ValidationResourceApi validationResourceApi; + + /** + * @see IClientCommand + */ + @Override + @PostConstruct + public void configure() { + ApiClient apiClient = + new ApiClient().setBasePath(configService.getConfig().getApiServer().getHost()); + + this.validationResourceApi = new ValidationResourceApi(apiClient); + } + + /** + * @see IClientCommand + */ + @Override + public ValidationScriptApplicationResult process(ValidationScriptApplicationDto input) + throws ApiServerException { + try { + return validationResourceApi + .v1ScriptAcquirePost(ValidationScriptApplication.of(input.getFileContent())) + .block(); + } catch (WebClientResponseException e) { + throw new ApiServerException(e.getResponseBodyAsString()); + } catch (WebClientRequestException e) { + throw new ApiServerException(new ApiServerNotAvailableException(e.getMessage()).getMessage()); + } + } +} diff --git a/gui/src/main/java/com/repoachiever/service/client/command/SecretsAcquireClientCommandService.java b/gui/src/main/java/com/repoachiever/service/client/command/SecretsAcquireClientCommandService.java new file mode 100644 index 0000000..f3d9d2a --- /dev/null +++ b/gui/src/main/java/com/repoachiever/service/client/command/SecretsAcquireClientCommandService.java @@ -0,0 +1,74 @@ +package com.repoachiever.service.client.command; + +import com.repoachiever.ApiClient; +import com.repoachiever.api.ValidationResourceApi; +import com.repoachiever.dto.ValidationSecretsApplicationDto; +import com.repoachiever.exception.ApiServerException; +import com.repoachiever.exception.ApiServerNotAvailableException; +import com.repoachiever.exception.CloudCredentialsFileNotFoundException; +import com.repoachiever.model.Provider; +import com.repoachiever.model.ValidationSecretsApplication; +import com.repoachiever.model.ValidationSecretsApplicationResult; +import com.repoachiever.service.client.common.IClientCommand; +import com.repoachiever.service.config.ConfigService; +import jakarta.annotation.PostConstruct; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.web.reactive.function.client.WebClientRequestException; +import org.springframework.web.reactive.function.client.WebClientResponseException; + +/** Represents secrets validation client command service. */ +@Service +public class SecretsAcquireClientCommandService + implements IClientCommand { + @Autowired private ConfigService configService; + + private ValidationResourceApi validationResourceApi; + + /** + * @see IClientCommand + */ + @Override + @PostConstruct + public void configure() { + ApiClient apiClient = + new ApiClient().setBasePath(configService.getConfig().getApiServer().getHost()); + + this.validationResourceApi = new ValidationResourceApi(apiClient); + } + + /** + * @see IClientCommand + */ + @Override + public ValidationSecretsApplicationResult process(ValidationSecretsApplicationDto input) + throws ApiServerException { + Path filePath = Paths.get(input.getFilePath()); + + if (Files.notExists(filePath)) { + throw new ApiServerException(new CloudCredentialsFileNotFoundException().getMessage()); + } + + String content; + + try { + content = Files.readString(filePath); + } catch (IOException e) { + throw new ApiServerException(e.getMessage()); + } + + try { + return validationResourceApi + .v1SecretsAcquirePost(ValidationSecretsApplication.of(Provider.AWS, content)) + .block(); + } catch (WebClientResponseException e) { + throw new ApiServerException(e.getResponseBodyAsString()); + } catch (WebClientRequestException e) { + throw new ApiServerException(new ApiServerNotAvailableException(e.getHeaders()).getMessage()); + } + } +} diff --git a/gui/src/main/java/com/repoachiever/service/client/command/VersionClientCommandService.java b/gui/src/main/java/com/repoachiever/service/client/command/VersionClientCommandService.java new file mode 100644 index 0000000..b53a972 --- /dev/null +++ b/gui/src/main/java/com/repoachiever/service/client/command/VersionClientCommandService.java @@ -0,0 +1,47 @@ +package com.repoachiever.service.client.command; + +import com.repoachiever.ApiClient; +import com.repoachiever.api.InfoResourceApi; +import com.repoachiever.exception.ApiServerException; +import com.repoachiever.exception.ApiServerNotAvailableException; +import com.repoachiever.model.ApplicationInfoResult; +import com.repoachiever.service.client.common.IClientCommand; +import com.repoachiever.service.config.ConfigService; +import jakarta.annotation.PostConstruct; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.web.reactive.function.client.WebClientRequestException; +import org.springframework.web.reactive.function.client.WebClientResponseException; + +/** Represents version information client command service. */ +@Service +public class VersionClientCommandService implements IClientCommand { + @Autowired private ConfigService configService; + + private InfoResourceApi infoResourceApi; + + /** + * @see IClientCommand + */ + @Override + @PostConstruct + public void configure() { + ApiClient apiClient = + new ApiClient().setBasePath(configService.getConfig().getApiServer().getHost()); + + this.infoResourceApi = new InfoResourceApi(apiClient); + } + + /** + * @see IClientCommand + */ + public ApplicationInfoResult process(Void input) throws ApiServerException { + try { + return infoResourceApi.v1InfoVersionGet().block(); + } catch (WebClientResponseException e) { + throw new ApiServerException(e.getResponseBodyAsString()); + } catch (WebClientRequestException e) { + throw new ApiServerException(new ApiServerNotAvailableException(e.getMessage()).getMessage()); + } + } +} diff --git a/gui/src/main/java/com/repoachiever/service/client/common/IClientCommand.java b/gui/src/main/java/com/repoachiever/service/client/common/IClientCommand.java new file mode 100644 index 0000000..c6645e6 --- /dev/null +++ b/gui/src/main/java/com/repoachiever/service/client/common/IClientCommand.java @@ -0,0 +1,22 @@ +package com.repoachiever.service.client.common; + +import com.repoachiever.exception.ApiServerException; + +/** + * Represents external resource command interface. + * + * @param type of the command response. + * @param type of the command request. + */ +public interface IClientCommand { + /** Provides configuration of the API Client. */ + void configure(); + + /** + * Processes certain request for an external command. + * + * @param input input to be given as request body. + * @return command response. + */ + T process(K input) throws ApiServerException; +} diff --git a/gui/src/main/java/com/repoachiever/service/client/observer/ResourceObserver.java b/gui/src/main/java/com/repoachiever/service/client/observer/ResourceObserver.java new file mode 100644 index 0000000..91fa45f --- /dev/null +++ b/gui/src/main/java/com/repoachiever/service/client/observer/ResourceObserver.java @@ -0,0 +1,52 @@ +package com.repoachiever.service.client.observer; + +import com.repoachiever.entity.PropertiesEntity; +import com.repoachiever.exception.ApiServerException; +import com.repoachiever.model.HealthCheckResult; +import com.repoachiever.service.client.command.HealthCheckClientCommandService; +import com.repoachiever.service.client.command.ReadinessCheckClientCommandService; +import com.repoachiever.service.event.payload.ConnectionStatusEvent; +import com.repoachiever.service.hand.executor.CommandExecutorService; +import com.repoachiever.service.scheduler.SchedulerHelper; +import jakarta.annotation.PostConstruct; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.ApplicationEventPublisher; +import org.springframework.stereotype.Component; + +/** Provides resource observables to manage state of the application. */ +@Component +public class ResourceObserver { + @Autowired private ApplicationEventPublisher applicationEventPublisher; + + @Autowired private PropertiesEntity properties; + + @Autowired private CommandExecutorService commandExecutorService; + + @Autowired private HealthCheckClientCommandService healthCommandService; + + @Autowired private ReadinessCheckClientCommandService readinessCommandService; + + /** Sends healthcheck requests to API Server and updates connection status. */ + @PostConstruct + private void handleHealthCommand() { + SchedulerHelper.scheduleTask( + () -> { + ConnectionStatusEvent connectionStatusEvent; + + try { + HealthCheckResult result = healthCommandService.process(null); + + connectionStatusEvent = + switch (result.getStatus()) { + case UP -> new ConnectionStatusEvent(true); + case DOWN -> new ConnectionStatusEvent(false); + }; + } catch (ApiServerException e) { + connectionStatusEvent = new ConnectionStatusEvent(false); + } + + applicationEventPublisher.publishEvent(connectionStatusEvent); + }, + properties.getProcessHealthcheckPeriod()); + } +} diff --git a/gui/src/main/java/com/repoachiever/service/command/common/ICommand.java b/gui/src/main/java/com/repoachiever/service/command/common/ICommand.java new file mode 100644 index 0000000..1352379 --- /dev/null +++ b/gui/src/main/java/com/repoachiever/service/command/common/ICommand.java @@ -0,0 +1,9 @@ +package com.repoachiever.service.command.common; + +import com.repoachiever.exception.ApiServerException; + +/** Represents common command interface. */ +public interface ICommand { + /** Processes certain request for an external command. */ + T process() throws ApiServerException; +} diff --git a/gui/src/main/java/com/repoachiever/service/command/external/start/StartExternalCommandService.java b/gui/src/main/java/com/repoachiever/service/command/external/start/StartExternalCommandService.java new file mode 100644 index 0000000..e6096db --- /dev/null +++ b/gui/src/main/java/com/repoachiever/service/command/external/start/StartExternalCommandService.java @@ -0,0 +1,25 @@ +package com.repoachiever.service.command.external.start; + +import com.repoachiever.dto.StartExternalCommandResultDto; +import com.repoachiever.service.command.common.ICommand; +import com.repoachiever.service.command.external.start.provider.aws.AWSStartExternalCommandService; +import com.repoachiever.service.config.ConfigService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +/** Represents start external command service. */ +@Service +public class StartExternalCommandService implements ICommand { + @Autowired private ConfigService configService; + + @Autowired private AWSStartExternalCommandService awsStartExternalCommandService; + + /** + * @see ICommand + */ + public StartExternalCommandResultDto process() { + return switch (configService.getConfig().getCloud().getProvider()) { + case AWS -> awsStartExternalCommandService.process(); + }; + } +} diff --git a/gui/src/main/java/com/repoachiever/service/command/external/start/provider/aws/AWSStartExternalCommandService.java b/gui/src/main/java/com/repoachiever/service/command/external/start/provider/aws/AWSStartExternalCommandService.java new file mode 100644 index 0000000..faadc82 --- /dev/null +++ b/gui/src/main/java/com/repoachiever/service/command/external/start/provider/aws/AWSStartExternalCommandService.java @@ -0,0 +1,110 @@ +package com.repoachiever.service.command.external.start.provider.aws; + +import com.repoachiever.converter.CredentialsConverter; +import com.repoachiever.dto.StartExternalCommandResultDto; +import com.repoachiever.dto.ValidationScriptApplicationDto; +import com.repoachiever.dto.ValidationSecretsApplicationDto; +import com.repoachiever.entity.ConfigEntity; +import com.repoachiever.exception.ApiServerException; +import com.repoachiever.exception.CloudCredentialsValidationException; +import com.repoachiever.exception.ScriptDataValidationException; +import com.repoachiever.model.*; +import com.repoachiever.service.client.command.ApplyClientCommandService; +import com.repoachiever.service.client.command.ScriptAcquireClientCommandService; +import com.repoachiever.service.client.command.SecretsAcquireClientCommandService; +import com.repoachiever.service.command.common.ICommand; +import com.repoachiever.service.config.ConfigService; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Paths; +import java.util.ArrayList; +import java.util.List; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +/** Represents start external command service for AWS provider. */ +@Service +public class AWSStartExternalCommandService implements ICommand { + @Autowired private ConfigService configService; + + @Autowired private ApplyClientCommandService applyClientCommandService; + + @Autowired private SecretsAcquireClientCommandService secretsAcquireClientCommandService; + + @Autowired private ScriptAcquireClientCommandService scriptAcquireClientCommandService; + + /** + * @see ICommand + */ + @Override + public StartExternalCommandResultDto process() { + ConfigEntity.Cloud.AWSCredentials credentials = + CredentialsConverter.convert( + configService.getConfig().getCloud().getCredentials(), + ConfigEntity.Cloud.AWSCredentials.class); + + ValidationSecretsApplicationDto validationSecretsApplicationDto = + ValidationSecretsApplicationDto.of(Provider.AWS, credentials.getFile()); + + ValidationSecretsApplicationResult validationSecretsApplicationResult; + try { + validationSecretsApplicationResult = + secretsAcquireClientCommandService.process(validationSecretsApplicationDto); + } catch (ApiServerException e) { + return StartExternalCommandResultDto.of(false, e.getMessage()); + } + + if (validationSecretsApplicationResult.getValid()) { + List requests = new ArrayList<>(); + + for (ConfigEntity.Request request : configService.getConfig().getRequests()) { + try { + requests.add(DeploymentRequest.of( + request.getName(), + Files.readString(Paths.get(request.getFile())), + request.getFrequency())); + } catch (IOException e) { + return StartExternalCommandResultDto.of(false, new ScriptDataValidationException(e.getMessage()).getMessage()); + } + } + + ValidationScriptApplicationDto validationScriptApplicationDto = + ValidationScriptApplicationDto.of( + requests.stream().map(DeploymentRequest::getScript).toList()); + + ValidationScriptApplicationResult validationScriptApplicationResult; + try { + validationScriptApplicationResult = + scriptAcquireClientCommandService.process(validationScriptApplicationDto); + } catch (ApiServerException e) { + return StartExternalCommandResultDto.of(false, e.getMessage()); + } + + if (validationScriptApplicationResult.getValid()) { + CredentialsFields credentialsFields = + CredentialsFields.of( + AWSSecrets.of( + validationSecretsApplicationResult.getSecrets().getAccessKey(), + validationSecretsApplicationResult.getSecrets().getSecretKey()), + credentials.getRegion()); + + TerraformDeploymentApplication terraformDeploymentApplication = + TerraformDeploymentApplication.of(requests, Provider.AWS, credentialsFields); + + try { + applyClientCommandService.process(terraformDeploymentApplication); + } catch (ApiServerException e) { + return StartExternalCommandResultDto.of(false, e.getMessage()); + } + + return StartExternalCommandResultDto.of(true, null); + } else { + return StartExternalCommandResultDto.of( + false, new ScriptDataValidationException().getMessage()); + } + } else { + return StartExternalCommandResultDto.of( + false, new CloudCredentialsValidationException().getMessage()); + } + } +} diff --git a/gui/src/main/java/com/repoachiever/service/command/external/state/StateExternalCommandService.java b/gui/src/main/java/com/repoachiever/service/command/external/state/StateExternalCommandService.java new file mode 100644 index 0000000..a7d0d5e --- /dev/null +++ b/gui/src/main/java/com/repoachiever/service/command/external/state/StateExternalCommandService.java @@ -0,0 +1,25 @@ +package com.repoachiever.service.command.external.state; + +import com.repoachiever.dto.StateExternalCommandResultDto; +import com.repoachiever.service.command.common.ICommand; +import com.repoachiever.service.command.external.state.provider.aws.AWSStateExternalCommandService; +import com.repoachiever.service.config.ConfigService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +/** Represents state external command service. */ +@Service +public class StateExternalCommandService implements ICommand { + @Autowired private ConfigService configService; + + @Autowired private AWSStateExternalCommandService awsStateExternalCommandService; + + /** + * @see ICommand + */ + public StateExternalCommandResultDto process() { + return switch (configService.getConfig().getCloud().getProvider()) { + case AWS -> awsStateExternalCommandService.process(); + }; + } +} diff --git a/gui/src/main/java/com/repoachiever/service/command/external/state/provider/aws/AWSStateExternalCommandService.java b/gui/src/main/java/com/repoachiever/service/command/external/state/provider/aws/AWSStateExternalCommandService.java new file mode 100644 index 0000000..838a1c2 --- /dev/null +++ b/gui/src/main/java/com/repoachiever/service/command/external/state/provider/aws/AWSStateExternalCommandService.java @@ -0,0 +1,72 @@ +package com.repoachiever.service.command.external.state.provider.aws; + +import com.repoachiever.converter.CredentialsConverter; +import com.repoachiever.dto.StateExternalCommandResultDto; +import com.repoachiever.dto.ValidationSecretsApplicationDto; +import com.repoachiever.entity.ConfigEntity; +import com.repoachiever.exception.ApiServerException; +import com.repoachiever.exception.CloudCredentialsValidationException; +import com.repoachiever.model.*; +import com.repoachiever.service.client.command.LogsClientCommandService; +import com.repoachiever.service.client.command.SecretsAcquireClientCommandService; +import com.repoachiever.service.command.common.ICommand; +import com.repoachiever.service.config.ConfigService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +/** Represents start external command service for AWS provider. */ +@Service +public class AWSStateExternalCommandService implements ICommand { + @Autowired private ConfigService configService; + + @Autowired private SecretsAcquireClientCommandService secretsAcquireClientCommandService; + + @Autowired private LogsClientCommandService logsClientCommandService; + + /** + * @see ICommand + */ + @Override + public StateExternalCommandResultDto process() { + ConfigEntity.Cloud.AWSCredentials credentials = + CredentialsConverter.convert( + configService.getConfig().getCloud().getCredentials(), + ConfigEntity.Cloud.AWSCredentials.class); + + ValidationSecretsApplicationDto validationSecretsApplicationDto = + ValidationSecretsApplicationDto.of(Provider.AWS, credentials.getFile()); + + ValidationSecretsApplicationResult validationSecretsApplicationResult; + try { + validationSecretsApplicationResult = + secretsAcquireClientCommandService.process(validationSecretsApplicationDto); + } catch (ApiServerException e) { + return StateExternalCommandResultDto.of(null, false, e.getMessage()); + } + + if (validationSecretsApplicationResult.getValid()) { + CredentialsFields credentialsFields = + CredentialsFields.of( + AWSSecrets.of( + validationSecretsApplicationResult.getSecrets().getAccessKey(), + validationSecretsApplicationResult.getSecrets().getSecretKey()), + credentials.getRegion()); + + TopicLogsResult topicLogsResult; + + TopicLogsApplication topicLogsApplication = + TopicLogsApplication.of(Provider.AWS, credentialsFields); + + try { + topicLogsResult = logsClientCommandService.process(topicLogsApplication); + } catch (ApiServerException e) { + return StateExternalCommandResultDto.of(null, false, e.getMessage()); + } + + return StateExternalCommandResultDto.of(topicLogsResult, true, null); + } else { + return StateExternalCommandResultDto.of( + null, false, new CloudCredentialsValidationException().getMessage()); + } + } +} diff --git a/gui/src/main/java/com/repoachiever/service/command/external/stop/StopExternalCommandService.java b/gui/src/main/java/com/repoachiever/service/command/external/stop/StopExternalCommandService.java new file mode 100644 index 0000000..a6e0d83 --- /dev/null +++ b/gui/src/main/java/com/repoachiever/service/command/external/stop/StopExternalCommandService.java @@ -0,0 +1,25 @@ +package com.repoachiever.service.command.external.stop; + +import com.repoachiever.dto.StopExternalCommandResultDto; +import com.repoachiever.service.command.common.ICommand; +import com.repoachiever.service.command.external.stop.provider.aws.AWSStopExternalCommandService; +import com.repoachiever.service.config.ConfigService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +/** Represents stop external command service. */ +@Service +public class StopExternalCommandService implements ICommand { + @Autowired private ConfigService configService; + + @Autowired private AWSStopExternalCommandService stopExternalCommandService; + + /** + * @see ICommand + */ + public StopExternalCommandResultDto process() { + return switch (configService.getConfig().getCloud().getProvider()) { + case AWS -> stopExternalCommandService.process(); + }; + } +} diff --git a/gui/src/main/java/com/repoachiever/service/command/external/stop/provider/aws/AWSStopExternalCommandService.java b/gui/src/main/java/com/repoachiever/service/command/external/stop/provider/aws/AWSStopExternalCommandService.java new file mode 100644 index 0000000..4f58d3b --- /dev/null +++ b/gui/src/main/java/com/repoachiever/service/command/external/stop/provider/aws/AWSStopExternalCommandService.java @@ -0,0 +1,75 @@ +package com.repoachiever.service.command.external.stop.provider.aws; + +import com.repoachiever.converter.CredentialsConverter; +import com.repoachiever.dto.StopExternalCommandResultDto; +import com.repoachiever.dto.ValidationSecretsApplicationDto; +import com.repoachiever.entity.ConfigEntity; +import com.repoachiever.exception.ApiServerException; +import com.repoachiever.exception.CloudCredentialsValidationException; +import com.repoachiever.model.*; +import com.repoachiever.service.client.command.DestroyClientCommandService; +import com.repoachiever.service.client.command.SecretsAcquireClientCommandService; +import com.repoachiever.service.command.common.ICommand; +import com.repoachiever.service.config.ConfigService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +/** */ +@Service +public class AWSStopExternalCommandService implements ICommand { + @Autowired private ConfigService configService; + + @Autowired private DestroyClientCommandService destroyClientCommandService; + + @Autowired private SecretsAcquireClientCommandService secretsAcquireClientCommandService; + + /** + * @see ICommand + */ + @Override + public StopExternalCommandResultDto process() { + ConfigEntity.Cloud.AWSCredentials credentials = + CredentialsConverter.convert( + configService.getConfig().getCloud().getCredentials(), + ConfigEntity.Cloud.AWSCredentials.class); + + ValidationSecretsApplicationDto validationSecretsApplicationDto = + ValidationSecretsApplicationDto.of(Provider.AWS, credentials.getFile()); + + ValidationSecretsApplicationResult validationSecretsApplicationResult; + try { + validationSecretsApplicationResult = + secretsAcquireClientCommandService.process(validationSecretsApplicationDto); + } catch (ApiServerException e) { + return StopExternalCommandResultDto.of(false, e.getMessage()); + } + + if (validationSecretsApplicationResult.getValid()) { + CredentialsFields credentialsFields = + CredentialsFields.of( + AWSSecrets.of( + validationSecretsApplicationResult.getSecrets().getAccessKey(), + validationSecretsApplicationResult.getSecrets().getSecretKey()), + credentials.getRegion()); + + TerraformDestructionApplication terraformDestructionApplication = + TerraformDestructionApplication.of( + configService.getConfig().getRequests().stream() + .map(element -> DestructionRequest.of(element.getName())) + .toList(), + Provider.AWS, + credentialsFields); + + try { + destroyClientCommandService.process(terraformDestructionApplication); + } catch (ApiServerException e) { + return StopExternalCommandResultDto.of(false, e.getMessage()); + } + + return StopExternalCommandResultDto.of(true, null); + } else { + return StopExternalCommandResultDto.of( + false, new CloudCredentialsValidationException().getMessage()); + } + } +} diff --git a/gui/src/main/java/com/repoachiever/service/command/external/version/VersionExternalCommandService.java b/gui/src/main/java/com/repoachiever/service/command/external/version/VersionExternalCommandService.java new file mode 100644 index 0000000..7eb4406 --- /dev/null +++ b/gui/src/main/java/com/repoachiever/service/command/external/version/VersionExternalCommandService.java @@ -0,0 +1,33 @@ +package com.repoachiever.service.command.external.version; + +import com.repoachiever.dto.VersionExternalCommandResultDto; +import com.repoachiever.entity.PropertiesEntity; +import com.repoachiever.exception.ApiServerException; +import com.repoachiever.model.ApplicationInfoResult; +import com.repoachiever.service.client.command.VersionClientCommandService; +import com.repoachiever.service.command.common.ICommand; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +/** Represents version external command service. */ +@Service +public class VersionExternalCommandService implements ICommand { + @Autowired private PropertiesEntity properties; + + @Autowired private VersionClientCommandService versionClientCommandService; + + /** + * @see ICommand + */ + public VersionExternalCommandResultDto process() { + ApplicationInfoResult applicationInfoResult; + try { + applicationInfoResult = versionClientCommandService.process(null); + } catch (ApiServerException e) { + return VersionExternalCommandResultDto.of(null, false, e.getMessage()); + } + + return VersionExternalCommandResultDto.of( + applicationInfoResult.getExternalApi().getHash(), true, null); + } +} diff --git a/gui/src/main/java/com/repoachiever/service/command/internal/health/HealthCheckInternalCommandService.java b/gui/src/main/java/com/repoachiever/service/command/internal/health/HealthCheckInternalCommandService.java new file mode 100644 index 0000000..0347b14 --- /dev/null +++ b/gui/src/main/java/com/repoachiever/service/command/internal/health/HealthCheckInternalCommandService.java @@ -0,0 +1,41 @@ +package com.repoachiever.service.command.internal.health; + +import com.repoachiever.dto.HealthCheckInternalCommandResultDto; +import com.repoachiever.exception.ApiServerException; +import com.repoachiever.exception.ApiServerNotAvailableException; +import com.repoachiever.model.HealthCheckResult; +import com.repoachiever.model.HealthCheckStatus; +import com.repoachiever.service.client.command.HealthCheckClientCommandService; +import com.repoachiever.service.command.common.ICommand; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +@Service +public class HealthCheckInternalCommandService + implements ICommand { + @Autowired private HealthCheckClientCommandService healthCheckClientCommandService; + + /** + * @see ICommand + */ + @Override + public HealthCheckInternalCommandResultDto process() { + HealthCheckResult healthCheckResult; + try { + healthCheckResult = healthCheckClientCommandService.process(null); + } catch (ApiServerException e) { + return HealthCheckInternalCommandResultDto.of(false, e.getMessage()); + } + + if (healthCheckResult.getStatus() == HealthCheckStatus.DOWN) { + return HealthCheckInternalCommandResultDto.of( + false, + new ApiServerException( + new ApiServerNotAvailableException(healthCheckResult.getChecks().toString()) + .getMessage()) + .getMessage()); + } + + return HealthCheckInternalCommandResultDto.of(true, null); + } +} diff --git a/gui/src/main/java/com/repoachiever/service/command/internal/readiness/ReadinessCheckInternalCommandService.java b/gui/src/main/java/com/repoachiever/service/command/internal/readiness/ReadinessCheckInternalCommandService.java new file mode 100644 index 0000000..053e8dd --- /dev/null +++ b/gui/src/main/java/com/repoachiever/service/command/internal/readiness/ReadinessCheckInternalCommandService.java @@ -0,0 +1,28 @@ +package com.repoachiever.service.command.internal.readiness; + +import com.repoachiever.dto.ReadinessCheckInternalCommandResultDto; +import com.repoachiever.service.command.common.ICommand; +import com.repoachiever.service.command.internal.readiness.provider.aws.AWSReadinessCheckInternalCommandService; +import com.repoachiever.service.config.ConfigService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +/** Represents readiness check internal command service. */ +@Service +public class ReadinessCheckInternalCommandService + implements ICommand { + @Autowired private ConfigService configService; + + @Autowired + private AWSReadinessCheckInternalCommandService awsReadinessCheckInternalCommandService; + + /** + * @see ICommand + */ + @Override + public ReadinessCheckInternalCommandResultDto process() { + return switch (configService.getConfig().getCloud().getProvider()) { + case AWS -> awsReadinessCheckInternalCommandService.process(); + }; + } +} diff --git a/gui/src/main/java/com/repoachiever/service/command/internal/readiness/provider/aws/AWSReadinessCheckInternalCommandService.java b/gui/src/main/java/com/repoachiever/service/command/internal/readiness/provider/aws/AWSReadinessCheckInternalCommandService.java new file mode 100644 index 0000000..081da5c --- /dev/null +++ b/gui/src/main/java/com/repoachiever/service/command/internal/readiness/provider/aws/AWSReadinessCheckInternalCommandService.java @@ -0,0 +1,82 @@ +package com.repoachiever.service.command.internal.readiness.provider.aws; + +import com.repoachiever.converter.CredentialsConverter; +import com.repoachiever.dto.ReadinessCheckInternalCommandResultDto; +import com.repoachiever.dto.ValidationSecretsApplicationDto; +import com.repoachiever.entity.ConfigEntity; +import com.repoachiever.exception.ApiServerException; +import com.repoachiever.exception.ApiServerNotAvailableException; +import com.repoachiever.exception.CloudCredentialsValidationException; +import com.repoachiever.model.*; +import com.repoachiever.service.client.command.ReadinessCheckClientCommandService; +import com.repoachiever.service.client.command.SecretsAcquireClientCommandService; +import com.repoachiever.service.command.common.ICommand; +import com.repoachiever.service.config.ConfigService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +@Service +public class AWSReadinessCheckInternalCommandService + implements ICommand { + @Autowired private ConfigService configService; + + @Autowired private SecretsAcquireClientCommandService secretsAcquireClientCommandService; + + @Autowired private ReadinessCheckClientCommandService readinessCheckClientCommandService; + + /** + * @see ICommand + */ + @Override + public ReadinessCheckInternalCommandResultDto process() { + ConfigEntity.Cloud.AWSCredentials credentials = + CredentialsConverter.convert( + configService.getConfig().getCloud().getCredentials(), + ConfigEntity.Cloud.AWSCredentials.class); + + ValidationSecretsApplicationDto validationSecretsApplicationDto = + ValidationSecretsApplicationDto.of(Provider.AWS, credentials.getFile()); + + ValidationSecretsApplicationResult validationSecretsApplicationResult; + try { + validationSecretsApplicationResult = + secretsAcquireClientCommandService.process(validationSecretsApplicationDto); + } catch (ApiServerException e) { + return ReadinessCheckInternalCommandResultDto.of(false, e.getMessage()); + } + + if (validationSecretsApplicationResult.getValid()) { + CredentialsFields credentialsFields = + CredentialsFields.of( + AWSSecrets.of( + validationSecretsApplicationResult.getSecrets().getAccessKey(), + validationSecretsApplicationResult.getSecrets().getSecretKey()), + credentials.getRegion()); + + ReadinessCheckApplication readinessCheckApplication = + ReadinessCheckApplication.of(Provider.AWS, credentialsFields); + + ReadinessCheckResult readinessCheckResult; + try { + readinessCheckResult = + readinessCheckClientCommandService.process(readinessCheckApplication); + } catch (ApiServerException e) { + return ReadinessCheckInternalCommandResultDto.of(false, e.getMessage()); + } + + if (readinessCheckResult.getStatus() == ReadinessCheckStatus.DOWN) { + return ReadinessCheckInternalCommandResultDto.of( + false, + new ApiServerException( + new ApiServerNotAvailableException(readinessCheckResult.getData().toString()) + .getMessage()) + .getMessage()); + } + + return ReadinessCheckInternalCommandResultDto.of(true, null); + } else { + return ReadinessCheckInternalCommandResultDto.of( + false, new CloudCredentialsValidationException().getMessage()); + } + } +} diff --git a/gui/src/main/java/com/repoachiever/service/config/ConfigService.java b/gui/src/main/java/com/repoachiever/service/config/ConfigService.java new file mode 100644 index 0000000..cb66093 --- /dev/null +++ b/gui/src/main/java/com/repoachiever/service/config/ConfigService.java @@ -0,0 +1,78 @@ +package com.repoachiever.service.config; + +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.DeserializationFeature; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.ObjectReader; +import com.fasterxml.jackson.dataformat.yaml.YAMLFactory; +import com.repoachiever.entity.ConfigEntity; +import com.repoachiever.entity.PropertiesEntity; +import jakarta.annotation.PostConstruct; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.InputStream; +import java.nio.file.Paths; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +/** Represents service used for configuration processing. */ +@Service +public class ConfigService { + private static final Logger logger = LogManager.getLogger(ConfigService.class); + + @Autowired private PropertiesEntity properties; + + private ConfigEntity parsedConfigFile; + + /** Processes configuration file */ + @PostConstruct + public void configure() { + InputStream configFile; + + try { + configFile = + new FileInputStream( + Paths.get( + System.getProperty("user.home"), + properties.getConfigRootPath(), + properties.getConfigUserFilePath()) + .toString()); + } catch (FileNotFoundException e) { + logger.fatal(e.getMessage()); + return; + } + + ObjectMapper mapper = + new ObjectMapper(new YAMLFactory()) + .configure(DeserializationFeature.FAIL_ON_NULL_CREATOR_PROPERTIES, true) + .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, true) + .configure(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY, true); + mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL); + ObjectReader reader = mapper.reader().forType(new TypeReference() {}); + + try { + parsedConfigFile = reader.readValues(configFile).readAll().getFirst(); + } catch (IOException e) { + logger.fatal(e.getMessage()); + } finally { + try { + configFile.close(); + } catch (IOException e) { + logger.fatal(e.getMessage()); + } + } + } + + /** + * Retrieves parsed configuration file entity. + * + * @return retrieved parsed configuration file entity. + */ + public ConfigEntity getConfig() { + return parsedConfigFile; + } +} diff --git a/gui/src/main/java/com/repoachiever/service/element/alert/ErrorAlert.java b/gui/src/main/java/com/repoachiever/service/element/alert/ErrorAlert.java new file mode 100644 index 0000000..f6a4d89 --- /dev/null +++ b/gui/src/main/java/com/repoachiever/service/element/alert/ErrorAlert.java @@ -0,0 +1,43 @@ +package com.repoachiever.service.element.alert; + +import com.repoachiever.service.element.storage.ElementStorage; +import com.repoachiever.service.element.text.common.IElement; +import ink.bluecloud.css.CssResources; +import ink.bluecloud.css.ElementButton; +import ink.bluecloud.css.ElementButtonKt; +import java.util.UUID; +import javafx.application.Platform; +import javafx.scene.control.Alert; +import javafx.scene.control.Button; +import javafx.scene.control.ButtonType; +import org.springframework.stereotype.Service; + +/** Represents error related alert. */ +@Service +public class ErrorAlert implements IElement { + UUID id = UUID.randomUUID(); + + public ErrorAlert() { + Platform.runLater( + () -> { + Alert alert = new Alert(Alert.AlertType.ERROR); + alert.setTitle("Error occurred"); + + ElementButtonKt.theme( + (Button) alert.getDialogPane().lookupButton(ButtonType.OK), ElementButton.redButton); + alert.getDialogPane().getStylesheets().add(CssResources.globalCssFile); + alert.getDialogPane().getStylesheets().add(CssResources.buttonCssFile); + alert.getDialogPane().getStylesheets().add(CssResources.textFieldCssFile); + + ElementStorage.setElement(id, alert); + }); + } + + /** + * @see IElement + */ + @Override + public Alert getContent() { + return ElementStorage.getElement(id); + } +} diff --git a/gui/src/main/java/com/repoachiever/service/element/alert/InformationAlert.java b/gui/src/main/java/com/repoachiever/service/element/alert/InformationAlert.java new file mode 100644 index 0000000..31ca501 --- /dev/null +++ b/gui/src/main/java/com/repoachiever/service/element/alert/InformationAlert.java @@ -0,0 +1,43 @@ +package com.repoachiever.service.element.alert; + +import com.repoachiever.service.element.storage.ElementStorage; +import com.repoachiever.service.element.text.common.IElement; +import ink.bluecloud.css.CssResources; +import ink.bluecloud.css.ElementButton; +import ink.bluecloud.css.ElementButtonKt; +import java.util.UUID; +import javafx.application.Platform; +import javafx.scene.control.Alert; +import javafx.scene.control.Button; +import javafx.scene.control.ButtonType; +import org.springframework.stereotype.Service; + +/** Represents error related alert. */ +@Service +public class InformationAlert implements IElement { + UUID id = UUID.randomUUID(); + + public InformationAlert() { + Platform.runLater( + () -> { + Alert alert = new Alert(Alert.AlertType.INFORMATION); + alert.setTitle("Action information"); + + ElementButtonKt.theme( + (Button) alert.getDialogPane().lookupButton(ButtonType.OK), ElementButton.redButton); + alert.getDialogPane().getStylesheets().add(CssResources.globalCssFile); + alert.getDialogPane().getStylesheets().add(CssResources.buttonCssFile); + alert.getDialogPane().getStylesheets().add(CssResources.textFieldCssFile); + + ElementStorage.setElement(id, alert); + }); + } + + /** + * @see IElement + */ + @Override + public Alert getContent() { + return ElementStorage.getElement(id); + } +} diff --git a/gui/src/main/java/com/repoachiever/service/element/button/BasicButton.java b/gui/src/main/java/com/repoachiever/service/element/button/BasicButton.java new file mode 100644 index 0000000..ddebf80 --- /dev/null +++ b/gui/src/main/java/com/repoachiever/service/element/button/BasicButton.java @@ -0,0 +1,75 @@ +package com.repoachiever.service.element.button; + +import com.repoachiever.entity.PropertiesEntity; +import com.repoachiever.service.element.common.ElementHelper; +import com.repoachiever.service.element.storage.ElementStorage; +import com.repoachiever.service.element.text.common.IElement; +import com.repoachiever.service.element.text.common.IElementResizable; +import com.repoachiever.service.event.state.LocalState; +import ink.bluecloud.css.CssResources; +import ink.bluecloud.css.ElementButton; +import ink.bluecloud.css.ElementButtonKt; +import java.util.UUID; +import javafx.geometry.Rectangle2D; +import javafx.scene.control.Button; + +/** Represents basic button. */ +public class BasicButton implements IElementResizable, IElement