diff --git a/.gitignore b/.gitignore
index 91a800a..7d285c7 100644
--- a/.gitignore
+++ b/.gitignore
@@ -43,3 +43,4 @@ nb-configuration.xml
/.quarkus/cli/plugins/
# TLS Certificates
.certs/
+/src/main/resources/dev/
diff --git a/Dockerfile b/Dockerfile
index e64b609..114cd8a 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -105,6 +105,8 @@ COPY target/quarkus-app/quarkus/ /deployments/quarkus/
EXPOSE 8080
+RUN sudo chmod -R 777 /var/lib/kubooboo
+
USER quarkus
ENV JAVA_OPTS_APPEND="-Dquarkus.http.host=0.0.0.0"
diff --git a/pg-docker-compose.yaml b/pg-docker-compose.yaml
new file mode 100644
index 0000000..83997da
--- /dev/null
+++ b/pg-docker-compose.yaml
@@ -0,0 +1,9 @@
+services:
+ db:
+ image: postgres
+ restart: always
+ shm_size: 128mb
+ environment:
+ POSTGRES_PASSWORD: postgres
+ ports:
+ - "6666:5432"
\ No newline at end of file
diff --git a/pom.xml b/pom.xml
index 0624fc5..4013765 100644
--- a/pom.xml
+++ b/pom.xml
@@ -65,6 +65,22 @@
kubernetes-httpclient-vertx
7.3.1
+
+ io.quarkus
+ quarkus-scheduler
+
+
+
+
+ io.quarkus
+ quarkus-hibernate-orm-panache
+
+
+
+
+ io.quarkus
+ quarkus-jdbc-postgresql
+
diff --git a/src/main/java/dev/dinauer/NodeResource.java b/src/main/java/dev/dinauer/NodeResource.java
index d711722..2fb0175 100644
--- a/src/main/java/dev/dinauer/NodeResource.java
+++ b/src/main/java/dev/dinauer/NodeResource.java
@@ -1,5 +1,6 @@
package dev.dinauer;
+import dev.dinauer.monitoring.nodes.NodeStats;
import dev.dinauer.utils.ClientProvider;
import io.fabric8.kubernetes.api.model.Node;
import io.fabric8.kubernetes.client.KubernetesClient;
@@ -83,10 +84,6 @@ public class NodeResource
throw new RuntimeException("Failed to retrieve top nodes.");
}
- public record NodeStats(Node node, Integer absoluteCpuUsage, Integer relativeCpuUsage, Integer totalCpu, Integer absoluteMemory, Integer relativeMemory, Integer totalMemory)
- {
- }
-
private Integer extractInteger(String input)
{
return Integer.valueOf(input.replace("m", "").replace("%", ""));
diff --git a/src/main/java/dev/dinauer/ProcessRunner.java b/src/main/java/dev/dinauer/ProcessRunner.java
new file mode 100644
index 0000000..232c2d8
--- /dev/null
+++ b/src/main/java/dev/dinauer/ProcessRunner.java
@@ -0,0 +1,44 @@
+package dev.dinauer;
+
+import jakarta.enterprise.context.ApplicationScoped;
+import jakarta.inject.Inject;
+import org.jboss.logging.Logger;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.stream.Collectors;
+
+@ApplicationScoped
+public class ProcessRunner
+{
+ @Inject
+ Logger LOG;
+
+ public String run(String command) throws IOException, InterruptedException
+ {
+ LOG.infof("Running command: %s", command);
+ ProcessBuilder pb = new ProcessBuilder(command.split("\\s+"));
+ pb.redirectErrorStream(true);
+
+ Process p = pb.start();
+
+ List text = new ArrayList<>();
+ try(BufferedReader br = new BufferedReader(new InputStreamReader(p.getInputStream())))
+ {
+ String line;
+ while((line = br.readLine()) != null)
+ {
+ text.add(line);
+ }
+ }
+ int exitCode = p.waitFor();
+ if(exitCode == 0)
+ {
+ return String.join("\n", text);
+ }
+ throw new RuntimeException("Error executing command: " + command);
+ }
+}
diff --git a/src/main/java/dev/dinauer/login/LoginResource.java b/src/main/java/dev/dinauer/login/LoginResource.java
index e87ade9..932c182 100644
--- a/src/main/java/dev/dinauer/login/LoginResource.java
+++ b/src/main/java/dev/dinauer/login/LoginResource.java
@@ -6,14 +6,19 @@ import jakarta.inject.Inject;
import jakarta.ws.rs.*;
import jakarta.ws.rs.core.MediaType;
import io.quarkus.elytron.security.common.BcryptUtil;
+import org.jboss.logging.Logger;
import java.io.IOException;
import java.time.ZonedDateTime;
+import java.util.Optional;
@Path("/login")
@ApplicationScoped
public class LoginResource
{
+ @Inject
+ Logger LOG;
+
@Inject
UserRepo userRepo;
@@ -22,11 +27,18 @@ public class LoginResource
@Produces(MediaType.TEXT_PLAIN)
public String login(Login login) throws IOException
{
- User user = userRepo.findByUsername(login.username());
- if(BcryptUtil.matches(login.password(), user.password()))
+ Optional userOptional = userRepo.findOptionalByUsername(login.username());
+ if(userOptional.isPresent())
{
- return Jwt.upn(user.username()).expiresAt(ZonedDateTime.now().plusDays(15).toInstant()).groups(user.roles()).sign();
+ User user = userOptional.get();
+ if(BcryptUtil.matches(login.password(), user.password()))
+ {
+ return Jwt.upn(user.username()).expiresAt(ZonedDateTime.now().plusDays(15).toInstant()).groups(user.roles()).sign();
+ }
+ LOG.info("Cannot access user. Forbidden");
+ throw new ForbiddenException();
}
- throw new ForbiddenException();
+ LOG.info("User not found");
+ throw new NotFoundException();
}
}
diff --git a/src/main/java/dev/dinauer/monitoring/MonitoredNode.java b/src/main/java/dev/dinauer/monitoring/MonitoredNode.java
new file mode 100644
index 0000000..80cffee
--- /dev/null
+++ b/src/main/java/dev/dinauer/monitoring/MonitoredNode.java
@@ -0,0 +1,10 @@
+package dev.dinauer.monitoring;
+
+import dev.dinauer.monitoring.indexing.IndexCollection;
+import io.fabric8.kubernetes.api.model.Node;
+
+import java.util.List;
+
+public record MonitoredNode(Node node, List jobs)
+{
+}
diff --git a/src/main/java/dev/dinauer/monitoring/MonitoredResource.java b/src/main/java/dev/dinauer/monitoring/MonitoredResource.java
new file mode 100644
index 0000000..4462f7a
--- /dev/null
+++ b/src/main/java/dev/dinauer/monitoring/MonitoredResource.java
@@ -0,0 +1,9 @@
+package dev.dinauer.monitoring;
+
+import io.fabric8.kubernetes.api.model.Pod;
+
+import java.util.List;
+
+public record MonitoredResource(E resource, List jobs)
+{
+}
diff --git a/src/main/java/dev/dinauer/monitoring/MonitoringJobResource.java b/src/main/java/dev/dinauer/monitoring/MonitoringJobResource.java
new file mode 100644
index 0000000..b3c6afb
--- /dev/null
+++ b/src/main/java/dev/dinauer/monitoring/MonitoringJobResource.java
@@ -0,0 +1,42 @@
+package dev.dinauer.monitoring;
+
+import dev.dinauer.monitoring.entity.MonitoringConfig;
+import dev.dinauer.monitoring.entity.repo.MonitoringRepo;
+import dev.dinauer.monitoring.indexing.IndexCollection;
+import dev.dinauer.monitoring.indexing.IndexMetricsRepo;
+import dev.dinauer.monitoring.indexing.TimeUnit;
+import io.fabric8.kubernetes.api.model.Pod;
+import jakarta.inject.Inject;
+import jakarta.ws.rs.GET;
+import jakarta.ws.rs.Path;
+import jakarta.ws.rs.PathParam;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+@Path("/monitorings/{monitoring-id}/jobs")
+public class MonitoringJobResource
+{
+ @Inject
+ MonitoringRepo monitoringRepo;
+
+ @Inject
+ MonitoringService monitoringService;
+
+ @Inject
+ IndexMetricsRepo indexMetricsRepo;
+
+ @GET
+ public List> get(@PathParam("monitoring-id") String monitoringId) throws IOException
+ {
+ List> result = new ArrayList<>();
+ MonitoringConfig config = monitoringRepo.findById(monitoringId);
+ List pods = monitoringService.findRunningPodsByMonitoringConfig(config);
+ for (Pod pod : pods)
+ {
+ result.add(new MonitoredResource<>(pod, indexMetricsRepo.findByResourceAndMetricAndTimeUnit(String.format("POD-%s", pod.getMetadata().getUid()), config.getType().toString(), TimeUnit.RAW)));
+ }
+ return result;
+ }
+}
diff --git a/src/main/java/dev/dinauer/monitoring/MonitoringJobRunner.java b/src/main/java/dev/dinauer/monitoring/MonitoringJobRunner.java
new file mode 100644
index 0000000..9d153a6
--- /dev/null
+++ b/src/main/java/dev/dinauer/monitoring/MonitoringJobRunner.java
@@ -0,0 +1,102 @@
+package dev.dinauer.monitoring;
+
+import dev.dinauer.monitoring.entity.MonitoringConfig;
+import dev.dinauer.monitoring.entity.MonitoringType;
+import dev.dinauer.monitoring.entity.repo.MonitoringRepo;
+import dev.dinauer.monitoring.log.Log;
+import dev.dinauer.monitoring.log.LogRepo;
+import dev.dinauer.monitoring.memory.MemoryMonitoringJobRunner;
+import dev.dinauer.monitoring.nodes.NodeMonitoringService;
+import dev.dinauer.monitoring.volume.VolumeMonitoringJobRunner;
+import dev.dinauer.monitoring.volume.utils.Duration;
+import io.quarkus.runtime.Startup;
+import jakarta.annotation.PostConstruct;
+import jakarta.enterprise.context.ApplicationScoped;
+import jakarta.inject.Inject;
+import org.jboss.logging.Logger;
+
+import java.io.IOException;
+import java.util.List;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.TimeUnit;
+
+@Startup
+@ApplicationScoped
+public class MonitoringJobRunner
+{
+ @Inject
+ Logger LOG;
+
+ @Inject
+ VolumeMonitoringJobRunner volumeMonitoringJobRunner;
+
+ @Inject
+ MemoryMonitoringJobRunner memoryMonitoringJobRunner;
+
+ @Inject
+ NodeMonitoringService nodeMonitoringService;
+
+ @Inject
+ MonitoringRepo monitoringRepo;
+
+ @Inject
+ LogRepo logRepo;
+
+ @PostConstruct
+ public void run()
+ {
+ List configs = monitoringRepo.listAll();
+ for (MonitoringConfig config : configs)
+ {
+ schedule(config);
+ }
+ ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);
+ Runnable task = () -> {
+ try
+ {
+ nodeMonitoringService.run();
+ }
+ catch (Exception e)
+ {
+ logRepo.persist(Log.init("Failed to node monitoring job"));
+ LOG.error("Failed to node monitoring job");
+ }
+ };
+ scheduler.scheduleAtFixedRate(task, 0, Duration.parse("5m"), TimeUnit.SECONDS);
+ }
+
+ private void schedule(MonitoringConfig config)
+ {
+ ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);
+ Runnable task = () -> {
+ try
+ {
+ run(config);
+ }
+ catch (Exception e)
+ {
+ logRepo.persist(Log.init(String.format("Monitoring %s failed.", config.getConfigName())));
+ LOG.errorf("Monitoring %s failed.", config.getConfigName());
+ }
+ };
+ scheduler.scheduleAtFixedRate(task, 0, Duration.parse(config.getInterval()), TimeUnit.SECONDS);
+ }
+
+ private void run(MonitoringConfig config) throws IOException, InterruptedException
+ {
+ LOG.infof("Running %s %s monitoring.", config.getConfigName(), config.getType().toString().toLowerCase());
+ switch (config.getType())
+ {
+ case VOLUME ->
+ {
+ volumeMonitoringJobRunner.run(config);
+ }
+ case MEMORY ->
+ {
+ memoryMonitoringJobRunner.run(config);
+ }
+ }
+ }
+}
diff --git a/src/main/java/dev/dinauer/monitoring/MonitoringResource.java b/src/main/java/dev/dinauer/monitoring/MonitoringResource.java
new file mode 100644
index 0000000..8820a34
--- /dev/null
+++ b/src/main/java/dev/dinauer/monitoring/MonitoringResource.java
@@ -0,0 +1,25 @@
+package dev.dinauer.monitoring;
+
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import dev.dinauer.monitoring.entity.MonitoringConfig;
+import dev.dinauer.monitoring.entity.MonitoringType;
+import dev.dinauer.monitoring.entity.repo.MonitoringRepo;
+import jakarta.inject.Inject;
+import jakarta.ws.rs.GET;
+import jakarta.ws.rs.Path;
+
+import java.util.List;
+
+@Path("/monitorings")
+public class MonitoringResource
+{
+ @Inject
+ MonitoringRepo monitoringRepo;
+
+ @GET
+ public List get() throws JsonProcessingException
+ {
+ return monitoringRepo.listAll();
+ }
+}
diff --git a/src/main/java/dev/dinauer/monitoring/MonitoringService.java b/src/main/java/dev/dinauer/monitoring/MonitoringService.java
new file mode 100644
index 0000000..08ed1d3
--- /dev/null
+++ b/src/main/java/dev/dinauer/monitoring/MonitoringService.java
@@ -0,0 +1,35 @@
+package dev.dinauer.monitoring;
+
+import dev.dinauer.monitoring.entity.MonitoringConfig;
+import dev.dinauer.monitoring.entity.TargetConfig;
+import dev.dinauer.service.PodService;
+import io.fabric8.kubernetes.api.model.Pod;
+import jakarta.enterprise.context.ApplicationScoped;
+import jakarta.inject.Inject;
+import org.jboss.resteasy.reactive.common.NotImplementedYet;
+
+import java.util.List;
+
+@ApplicationScoped
+public class MonitoringService
+{
+ @Inject
+ PodService podService;
+
+ public List findRunningPodsByMonitoringConfig(MonitoringConfig config)
+ {
+ TargetConfig targetConfig = config.getTargetConfig();
+ switch (targetConfig.getType())
+ {
+ case LABELS ->
+ {
+ return podService.findByLabels(targetConfig.getNamespace(), targetConfig.getLabels()).stream().filter(pod -> pod.getStatus().getPhase().equals("Running")).toList();
+ }
+ case DEPLOYMENT, STATEFUL_SET ->
+ {
+ throw new NotImplementedYet();
+ }
+ }
+ throw new RuntimeException("Invalid monitoring config type");
+ }
+}
diff --git a/src/main/java/dev/dinauer/monitoring/NodeMonitoringJobResource.java b/src/main/java/dev/dinauer/monitoring/NodeMonitoringJobResource.java
new file mode 100644
index 0000000..240a9ae
--- /dev/null
+++ b/src/main/java/dev/dinauer/monitoring/NodeMonitoringJobResource.java
@@ -0,0 +1,59 @@
+package dev.dinauer.monitoring;
+
+import dev.dinauer.monitoring.indexing.IndexCollection;
+import dev.dinauer.monitoring.indexing.IndexMetric;
+import dev.dinauer.monitoring.indexing.IndexMetricsRepo;
+import dev.dinauer.monitoring.indexing.TimeUnit;
+import dev.dinauer.utils.ClientProvider;
+import io.fabric8.kubernetes.api.model.Node;
+import jakarta.inject.Inject;
+import jakarta.ws.rs.GET;
+import jakarta.ws.rs.Path;
+import jakarta.ws.rs.PathParam;
+import jakarta.ws.rs.QueryParam;
+
+import java.io.IOException;
+import java.time.ZonedDateTime;
+import java.util.ArrayList;
+import java.util.List;
+
+@Path("/monitorings/nodes/jobs")
+public class NodeMonitoringJobResource
+{
+ @Inject
+ IndexMetricsRepo indexMetricsRepo;
+
+ @Inject
+ ClientProvider clientProvider;
+
+ @GET
+ public List> get(@QueryParam("from") ZonedDateTime from, @QueryParam("to") ZonedDateTime to) throws IOException
+ {
+ List> result = new ArrayList<>();
+ List nodes = clientProvider.getClient().nodes().list().getItems();
+ for (Node node : nodes)
+ {
+ String resource = String.format("NODE-%s", node.getMetadata().getUid());
+ result.add(new MonitoredResource<>(node, indexMetricsRepo.findByResourceAndMetricAndTimeUnit(resource, "NODE_METRICS", determineTimeUnit(from, to))));
+ }
+ return result;
+ }
+
+ private TimeUnit determineTimeUnit(ZonedDateTime from, ZonedDateTime to)
+ {
+ long day = 60 * 60 * 24;
+ long twoDays = day * 2;
+ long fifteenDays = day * 15;
+
+ long dif = to.toEpochSecond() - from.toEpochSecond();
+ if (dif < twoDays)
+ {
+ return TimeUnit.RAW;
+ }
+ if (dif < fifteenDays)
+ {
+ return TimeUnit.HOUR;
+ }
+ return TimeUnit.DAY;
+ }
+}
diff --git a/src/main/java/dev/dinauer/monitoring/entity/CpuConfig.java b/src/main/java/dev/dinauer/monitoring/entity/CpuConfig.java
new file mode 100644
index 0000000..462789b
--- /dev/null
+++ b/src/main/java/dev/dinauer/monitoring/entity/CpuConfig.java
@@ -0,0 +1,53 @@
+package dev.dinauer.monitoring.entity;
+
+import com.fasterxml.jackson.annotation.JsonIgnore;
+import jakarta.persistence.*;
+
+@Entity
+@Table(name = "cpu_config")
+public class CpuConfig
+{
+ @Id
+ private String id;
+
+ @Column(name = "container_name")
+ private String containerName;
+
+ @OneToOne
+ @JoinColumn(name = "config_id")
+ @JsonIgnore
+ private MonitoringConfig config;
+
+ public String getId()
+ {
+ return id;
+ }
+
+ public CpuConfig setId(String id)
+ {
+ this.id = id;
+ return this;
+ }
+
+ public String getContainerName()
+ {
+ return containerName;
+ }
+
+ public CpuConfig setContainerName(String containerName)
+ {
+ this.containerName = containerName;
+ return this;
+ }
+
+ public MonitoringConfig getConfig()
+ {
+ return config;
+ }
+
+ public CpuConfig setConfig(MonitoringConfig config)
+ {
+ this.config = config;
+ return this;
+ }
+}
diff --git a/src/main/java/dev/dinauer/monitoring/entity/HealthcheckConfig.java b/src/main/java/dev/dinauer/monitoring/entity/HealthcheckConfig.java
new file mode 100644
index 0000000..08fa452
--- /dev/null
+++ b/src/main/java/dev/dinauer/monitoring/entity/HealthcheckConfig.java
@@ -0,0 +1,39 @@
+package dev.dinauer.monitoring.entity;
+
+import com.fasterxml.jackson.annotation.JsonIgnore;
+import jakarta.persistence.*;
+
+@Entity
+@Table(name = "healthcheck_config")
+public class HealthcheckConfig
+{
+ @Id
+ private String id;
+
+ @OneToOne
+ @JoinColumn(name = "config_id")
+ @JsonIgnore
+ private MonitoringConfig config;
+
+ public String getId()
+ {
+ return id;
+ }
+
+ public HealthcheckConfig setId(String id)
+ {
+ this.id = id;
+ return this;
+ }
+
+ public MonitoringConfig getConfig()
+ {
+ return config;
+ }
+
+ public HealthcheckConfig setConfig(MonitoringConfig config)
+ {
+ this.config = config;
+ return this;
+ }
+}
diff --git a/src/main/java/dev/dinauer/monitoring/entity/MonitoringConfig.java b/src/main/java/dev/dinauer/monitoring/entity/MonitoringConfig.java
new file mode 100644
index 0000000..e20e204
--- /dev/null
+++ b/src/main/java/dev/dinauer/monitoring/entity/MonitoringConfig.java
@@ -0,0 +1,119 @@
+package dev.dinauer.monitoring.entity;
+
+import jakarta.persistence.*;
+
+@Entity
+@Table(name = "monitoring_config")
+public class MonitoringConfig
+{
+ @Id
+ private String id;
+
+ @Column(name = "config_name")
+ private String configName;
+
+ @Enumerated(EnumType.STRING)
+ private MonitoringType type;
+
+ private String interval;
+
+ @OneToOne(mappedBy = "config")
+ private TargetConfig targetConfig;
+
+ @OneToOne(mappedBy = "config")
+ private CpuConfig cpuConfig;
+
+ @OneToOne(mappedBy = "config")
+ private HealthcheckConfig healthcheckConfig;
+
+ @OneToOne(mappedBy = "config")
+ private VolumeConfig volumeConfig;
+
+ public String getId()
+ {
+ return id;
+ }
+
+ public MonitoringConfig setId(String id)
+ {
+ this.id = id;
+ return this;
+ }
+
+ public String getConfigName()
+ {
+ return configName;
+ }
+
+ public MonitoringConfig setConfigName(String configName)
+ {
+ this.configName = configName;
+ return this;
+ }
+
+ public MonitoringType getType()
+ {
+ return type;
+ }
+
+ public MonitoringConfig setType(MonitoringType type)
+ {
+ this.type = type;
+ return this;
+ }
+
+ public String getInterval()
+ {
+ return interval;
+ }
+
+ public MonitoringConfig setInterval(String interval)
+ {
+ this.interval = interval;
+ return this;
+ }
+
+ public TargetConfig getTargetConfig()
+ {
+ return targetConfig;
+ }
+
+ public MonitoringConfig setTargetConfig(TargetConfig targetConfig)
+ {
+ this.targetConfig = targetConfig;
+ return this;
+ }
+
+ public CpuConfig getCpuConfig()
+ {
+ return cpuConfig;
+ }
+
+ public MonitoringConfig setCpuConfig(CpuConfig cpuConfig)
+ {
+ this.cpuConfig = cpuConfig;
+ return this;
+ }
+
+ public HealthcheckConfig getHealthcheckConfig()
+ {
+ return healthcheckConfig;
+ }
+
+ public MonitoringConfig setHealthcheckConfig(HealthcheckConfig healthcheckConfig)
+ {
+ this.healthcheckConfig = healthcheckConfig;
+ return this;
+ }
+
+ public VolumeConfig getVolumeConfig()
+ {
+ return volumeConfig;
+ }
+
+ public MonitoringConfig setVolumeConfig(VolumeConfig volumeConfig)
+ {
+ this.volumeConfig = volumeConfig;
+ return this;
+ }
+}
diff --git a/src/main/java/dev/dinauer/monitoring/entity/MonitoringTargetType.java b/src/main/java/dev/dinauer/monitoring/entity/MonitoringTargetType.java
new file mode 100644
index 0000000..6b89430
--- /dev/null
+++ b/src/main/java/dev/dinauer/monitoring/entity/MonitoringTargetType.java
@@ -0,0 +1,8 @@
+package dev.dinauer.monitoring.entity;
+
+public enum MonitoringTargetType
+{
+ DEPLOYMENT,
+ STATEFUL_SET,
+ LABELS
+}
diff --git a/src/main/java/dev/dinauer/monitoring/entity/MonitoringType.java b/src/main/java/dev/dinauer/monitoring/entity/MonitoringType.java
new file mode 100644
index 0000000..e320146
--- /dev/null
+++ b/src/main/java/dev/dinauer/monitoring/entity/MonitoringType.java
@@ -0,0 +1,6 @@
+package dev.dinauer.monitoring.entity;
+
+public enum MonitoringType
+{
+ VOLUME, CPU, MEMORY, HEALTHCHECK
+}
diff --git a/src/main/java/dev/dinauer/monitoring/entity/TargetConfig.java b/src/main/java/dev/dinauer/monitoring/entity/TargetConfig.java
new file mode 100644
index 0000000..273b55b
--- /dev/null
+++ b/src/main/java/dev/dinauer/monitoring/entity/TargetConfig.java
@@ -0,0 +1,135 @@
+package dev.dinauer.monitoring.entity;
+
+import com.fasterxml.jackson.annotation.JsonIgnore;
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.core.type.TypeReference;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import jakarta.persistence.*;
+
+import java.util.Map;
+
+@Entity
+@Table(name = "target_config")
+public class TargetConfig
+{
+ private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper();
+
+ @Id
+ private String id;
+
+ @Enumerated(EnumType.STRING)
+ private MonitoringTargetType type;
+
+ private String namespace;
+
+ @Column(name = "deployment_name")
+ private String deploymentName;
+
+ @Column(name = "stateful_set_name")
+ private String statefulSetName;
+
+ private String labels;
+
+ @OneToOne
+ @JoinColumn(name = "config_id")
+ @JsonIgnore
+ private MonitoringConfig config;
+
+ public String getId()
+ {
+ return id;
+ }
+
+ public TargetConfig setId(String id)
+ {
+ this.id = id;
+ return this;
+ }
+
+ public MonitoringTargetType getType()
+ {
+ return type;
+ }
+
+ public TargetConfig setType(MonitoringTargetType type)
+ {
+ this.type = type;
+ return this;
+ }
+
+ public String getNamespace()
+ {
+ return namespace;
+ }
+
+ public TargetConfig setNamespace(String namespace)
+ {
+ this.namespace = namespace;
+ return this;
+ }
+
+ public String getDeploymentName()
+ {
+ return deploymentName;
+ }
+
+ public TargetConfig setDeploymentName(String deploymentName)
+ {
+ this.deploymentName = deploymentName;
+ return this;
+ }
+
+ public String getStatefulSetName()
+ {
+ return statefulSetName;
+ }
+
+ public TargetConfig setStatefulSetName(String statefulSetName)
+ {
+ this.statefulSetName = statefulSetName;
+ return this;
+ }
+
+ public Map getLabels()
+ {
+ if (labels != null)
+ {
+ try
+ {
+ return OBJECT_MAPPER.readValue(labels, new TypeReference