Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.inject.Inject;
import org.eclipse.microprofile.config.inject.ConfigProperty;
import org.qubership.colly.achka.AchKubernetesAgentService;
import org.qubership.colly.cloudpassport.CloudPassportEnvironment;
import org.qubership.colly.cloudpassport.CloudPassportNamespace;
import org.qubership.colly.cloudpassport.ClusterInfo;
Expand All @@ -23,7 +24,10 @@

import java.io.IOException;
import java.time.Instant;
import java.util.*;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collectors;

Expand All @@ -35,6 +39,7 @@ public class ClusterResourcesLoader {
private final ClusterRepository clusterRepository;
private final EnvironmentRepository environmentRepository;
private final MonitoringService monitoringService;
private final AchKubernetesAgentService achKubernetesAgentService;

@ConfigProperty(name = "colly.environment-operational-service.config-map.versions.name")
String versionsConfigMapName;
Expand All @@ -46,11 +51,12 @@ public class ClusterResourcesLoader {
public ClusterResourcesLoader(NamespaceRepository namespaceRepository,
ClusterRepository clusterRepository,
EnvironmentRepository environmentRepository,
MonitoringService monitoringService) {
MonitoringService monitoringService, AchKubernetesAgentService achKubernetesAgentService) {
this.namespaceRepository = namespaceRepository;
this.clusterRepository = clusterRepository;
this.environmentRepository = environmentRepository;
this.monitoringService = monitoringService;
this.achKubernetesAgentService = achKubernetesAgentService;
}


Expand Down Expand Up @@ -80,8 +86,8 @@ void loadClusterResources(CoreV1Api coreV1Api, ClusterInfo clusterInfo) {
clusterRepository.save(cluster);
}

//it is requirzed to set links to cluster only if it was saved to db. so need to invoke persist two
List<Environment> environments = loadEnvironments(coreV1Api, cluster, clusterInfo.environments(), clusterInfo.monitoringUrl());
//it is required to set links to cluster only if it was saved to db. so need to invoke persist two
List<Environment> environments = loadEnvironments(coreV1Api, cluster, clusterInfo);
try {
V1NodeList execute = coreV1Api.listNode().execute();
int numberOfNodes = execute.getItems().size();
Expand All @@ -95,7 +101,7 @@ void loadClusterResources(CoreV1Api coreV1Api, ClusterInfo clusterInfo) {
Log.info("Cluster " + clusterInfo.name() + " loaded successfully.");
}

private List<Environment> loadEnvironments(CoreV1Api coreV1Api, Cluster cluster, Collection<CloudPassportEnvironment> environments, String monitoringUri) {
private List<Environment> loadEnvironments(CoreV1Api coreV1Api, Cluster cluster, ClusterInfo clusterInfo) {
Log.info("Start loading environments for cluster " + cluster.getName());
CoreV1Api.APIlistNamespaceRequest apilistNamespaceRequest = coreV1Api.listNamespace();
Map<String, V1Namespace> k8sNamespaces;
Expand All @@ -109,8 +115,8 @@ private List<Environment> loadEnvironments(CoreV1Api coreV1Api, Cluster cluster,
}

List<Environment> envs = new ArrayList<>();
Log.info("Namespaces are loaded for " + cluster.getName() + ". Count is " + k8sNamespaces.size() + ". Environments count = " + environments.size());
for (CloudPassportEnvironment cloudPassportEnvironment : environments) {
Log.info("Namespaces are loaded for " + cluster.getName() + ". Count is " + k8sNamespaces.size() + ". Environments count = " + clusterInfo.environments().size());
for (CloudPassportEnvironment cloudPassportEnvironment : clusterInfo.environments()) {
List<Environment> envList = environmentRepository.findByName(cloudPassportEnvironment.name());
Environment environment = envList.stream()
.filter(env -> cluster.getId().equals(env.getClusterId()))
Expand Down Expand Up @@ -167,8 +173,9 @@ private List<Environment> loadEnvironments(CoreV1Api coreV1Api, Cluster cluster,
namespaceRepository.findByUid(nsId).ifPresent(ns -> namespaceNames.add(ns.getName()));
}
}
environment.setMonitoringData(monitoringService.loadMonitoringData(monitoringUri, environment.getName(), cluster.getName(), namespaceNames));
environment.setMonitoringData(monitoringService.loadMonitoringData(clusterInfo.monitoringUrl(), environment.getName(), cluster.getName(), namespaceNames));
environment.setDeploymentVersion(deploymentVersions.toString());
//todo environment.setDeploymentOperations(achKubernetesAgentService.getDeploymentOperations(clusterInfo.cloudApiHost(), namespaceNames));
environmentRepository.save(environment);

envs.add(environment);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package org.qubership.colly.achka;

import com.fasterxml.jackson.annotation.JsonProperty;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.Produces;
import jakarta.ws.rs.QueryParam;
import jakarta.ws.rs.core.MediaType;
import org.eclipse.microprofile.rest.client.inject.RegisterRestClient;

import java.util.List;
import java.util.Map;

@Path("/v2/public")
@RegisterRestClient(configKey = "achka-api")
public interface AchKubernetesAgentClient {

@GET
@Path("/versions")
@Produces(MediaType.APPLICATION_JSON)
AchkaResponse versions(@QueryParam("namespace") List<String> namespaces,
@QueryParam("group_by") String groupBy);

record AchkaResponse(
@JsonProperty("versions")
Map<String, List<ApplicationsVersion>> deploymentSessionIdToApplicationVersions) {
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package org.qubership.colly.achka;

import jakarta.enterprise.context.ApplicationScoped;
import org.eclipse.microprofile.rest.client.RestClientBuilder;

import java.net.URI;

@ApplicationScoped
public class AchKubernetesAgentClientFactory {

public AchKubernetesAgentClient create(String cloudPublicHost) {
String url = "https://ach-kubernetes-agent-devops-toolkit." + cloudPublicHost;
return RestClientBuilder.newBuilder()
.baseUri(URI.create(url))
.build(AchKubernetesAgentClient.class);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
package org.qubership.colly.achka;

import io.quarkus.logging.Log;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.inject.Inject;
import org.qubership.colly.db.data.*;

import java.time.Instant;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

import static org.apache.commons.lang3.compare.ComparableUtils.max;

@ApplicationScoped
public class AchKubernetesAgentService {

@Inject
AchKubernetesAgentClientFactory clientFactory;

public List<DeploymentOperation> getDeploymentOperations(String cloudPublicHost, List<String> namespaceNames) {
AchKubernetesAgentClient achkaClient = clientFactory.create(cloudPublicHost);
List<DeploymentOperation> deploymentOperations = new ArrayList<>();
AchKubernetesAgentClient.AchkaResponse achkaResponse = achkaClient.versions(namespaceNames, "deployment_session_id");
for (String deploymentSessionId : achkaResponse.deploymentSessionIdToApplicationVersions().keySet()) {
if (deploymentSessionId.equals("None")) {
Log.error("Invalid deployment session id: " + deploymentSessionId);
continue;
}
List<ApplicationsVersion> applicationsVersions = achkaResponse.deploymentSessionIdToApplicationVersions().get(deploymentSessionId);

Instant completedAt = Instant.MIN;
List<DeploymentItem> deploymentItems = new ArrayList<>();
Map<String, List<ApplicationsVersion>> sdToApplications = applicationsVersions.stream().collect(Collectors.groupingBy(ApplicationsVersion::source));
for (String sdName : sdToApplications.keySet()) {
List<ApplicationsVersion> sdApplicationsVersions = sdToApplications.get(sdName);
if (sdApplicationsVersions.isEmpty()) {
Log.warn("No applications versions found for SD: " + sdName);
continue;
}

ApplicationsVersion latest = sdApplicationsVersions.stream()
.max(Comparator.comparing(appVer -> Instant.ofEpochMilli(Long.parseLong(appVer.deployDate()))))
.orElseThrow();
completedAt = max(completedAt, Instant.ofEpochMilli(Long.parseLong(latest.deployDate())));
long failedApps = sdApplicationsVersions.stream().filter(appVer -> appVer.deployStatus().equals("FAILED")).count();
DeploymentStatus status = failedApps > 0 ? DeploymentStatus.FAILED : DeploymentStatus.SUCCESS;
deploymentItems.add(new DeploymentItem(sdName, status, DeploymentItemType.PRODUCT, DeploymentMode.ROLLING_UPDATE));
}
deploymentOperations.add(new DeploymentOperation(completedAt, deploymentItems));
}
return deploymentOperations;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
package org.qubership.colly.achka;

public record AchkaResponse() {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package org.qubership.colly.achka;

import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;

@JsonIgnoreProperties(ignoreUnknown = true)
public record ApplicationsVersion(
@JsonProperty("source")
String source,
@JsonProperty("deploy_status")
String deployStatus,
@JsonProperty("deploy_date")
String deployDate,
@JsonProperty("ticket_id")
String ticketId
) {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package org.qubership.colly.db.data;

public record DeploymentItem(String name, DeploymentStatus status, DeploymentItemType deploymentItemType,
DeploymentMode deploymentMode) {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package org.qubership.colly.db.data;

public enum DeploymentItemType {
PRODUCT,
PROJECT
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package org.qubership.colly.db.data;

public enum DeploymentMode {
CLEAN_INSTALL,
ROLLING_UPDATE
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package org.qubership.colly.db.data;

import java.time.Instant;
import java.util.List;

public record DeploymentOperation(Instant createdAt, List<DeploymentItem> deploymentItems) {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package org.qubership.colly.db.data;

public enum DeploymentStatus {
SUCCESS,
FAILED
}
Original file line number Diff line number Diff line change
@@ -1,11 +1,18 @@
package org.qubership.colly.db.data;

import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;

import java.time.Instant;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;

@Setter
@Getter
@NoArgsConstructor
public class Environment {

private String id;
Expand All @@ -14,6 +21,7 @@ public class Environment {
private String clusterId;
private Map<String, String> monitoringData;
private String deploymentVersion;
private List<DeploymentOperation> deploymentOperations;
private List<String> namespaceIds;

public Environment(String id, String name) {
Expand All @@ -22,71 +30,22 @@ public Environment(String id, String name) {
this.namespaceIds = new ArrayList<>();
}

public Environment() {
}

public String getId() {
return id;
}

public void setId(String id) {
this.id = id;
}


public List<String> getNamespaceIds() {
return namespaceIds != null ? Collections.unmodifiableList(namespaceIds) : Collections.emptyList();
}

public void setNamespaceIds(List<String> namespaceIds) {
this.namespaceIds = namespaceIds;
}

public void addNamespaceId(String namespaceId) {
if (this.namespaceIds == null) {
this.namespaceIds = new ArrayList<>();
}
this.namespaceIds.add(namespaceId);
}

public String getDeploymentVersion() {
return deploymentVersion;
}

public void setDeploymentVersion(String deploymentVersion) {
this.deploymentVersion = deploymentVersion;
}

public String getName() {
return name;
public List<DeploymentOperation> getDeploymentOperations() {
return deploymentOperations != null ? Collections.unmodifiableList(deploymentOperations) : Collections.emptyList();
}

public void setName(String name) {
this.name = name;
public void setDeploymentOperations(List<DeploymentOperation> deploymentOperations) {
this.deploymentOperations = deploymentOperations != null ? new ArrayList<>(deploymentOperations) : null;
}

public String getClusterId() {
return clusterId;
}

public void setClusterId(String clusterId) {
this.clusterId = clusterId;
}

public Map<String, String> getMonitoringData() {
return monitoringData;
}

public void setMonitoringData(Map<String, String> monitoringData) {
this.monitoringData = monitoringData;
}

public Instant getCleanInstallationDate() {
return cleanInstallationDate;
}

public void setCleanInstallationDate(Instant cleanInstallationDate) {
this.cleanInstallationDate = cleanInstallationDate;
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package org.qubership.colly.dto;

import org.qubership.colly.db.data.DeploymentItemType;
import org.qubership.colly.db.data.DeploymentMode;
import org.qubership.colly.db.data.DeploymentStatus;

public record DeploymentItemDto(String name, DeploymentStatus status, DeploymentItemType deploymentItemType,
DeploymentMode deploymentMode) {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package org.qubership.colly.dto;

import java.time.Instant;
import java.util.List;

public record DeploymentOperationDto(Instant completedAt, List<DeploymentItemDto> deploymentItems) {
}
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,13 @@ public record EnvironmentDTO(
)
Instant cleanInstallationDate,

@Schema(
description = "List of deployment operations performed in this environment",
examples = "",
required = true
)
List<DeploymentOperationDto> deploymentOperations,

@Schema(
description = "Additional monitoring metrics and data collected from the environment",
examples = "{\"cpu_usage\": \"75%\", \"memory_usage\": \"60%\", \"pod_count\": \"42\"}",
Expand Down
Loading