diff --git a/deployment_manifest.json b/deployment_manifest.json index a367cefa..55213550 100644 --- a/deployment_manifest.json +++ b/deployment_manifest.json @@ -125,10 +125,6 @@ { "name": "SERVICE_DUMP_DAG_ID", "value": "$SERVICE_DUMP_DAG_ID" - }, - { - "name": "SERVICE_DUMP_BUCKET_NAME", - "value": "$SERVICE_DUMP_BUCKET_NAME" } ], "namespace": "$NAMESPACE", diff --git a/src/main/java/com/navi/infra/portal/v2/client/airflow/AirflowApiResponse.java b/src/main/java/com/navi/infra/portal/v2/client/airflow/AirflowApiResponse.java index e7216b39..dcebc08e 100644 --- a/src/main/java/com/navi/infra/portal/v2/client/airflow/AirflowApiResponse.java +++ b/src/main/java/com/navi/infra/portal/v2/client/airflow/AirflowApiResponse.java @@ -40,6 +40,8 @@ public class AirflowApiResponse { private String dumpName; + private String dumpType; + @JsonAnyGetter public Map getAdditionalProperties() { diff --git a/src/main/java/com/navi/infra/portal/v2/client/airflow/AirflowClient.java b/src/main/java/com/navi/infra/portal/v2/client/airflow/AirflowClient.java index 862529a3..702a639a 100644 --- a/src/main/java/com/navi/infra/portal/v2/client/airflow/AirflowClient.java +++ b/src/main/java/com/navi/infra/portal/v2/client/airflow/AirflowClient.java @@ -54,7 +54,8 @@ public class AirflowClient { "target_container_name", dumpTriggerRequest.getContainerName(), "image", dumpTriggerRequest.getRunnerImage(), "pre_signed_url", dumpTriggerRequest.getPreSignedUrl(), - "dump_name", dumpTriggerRequest.getDumpName() + "dump_name", dumpTriggerRequest.getDumpName(), + "dump_type", dumpTriggerRequest.getDumpType() )); var payload = convertMapToString(payloadMap); @@ -77,12 +78,12 @@ public class AirflowClient { return getAirflowApiResponse(dagId, runId, response, airflowApiResponse, dumpTriggerRequest.getCluster(), dumpTriggerRequest.getNamespace(), - dumpTriggerRequest.getPodName(), dumpTriggerRequest.getDumpName() + dumpTriggerRequest.getPodName(), dumpTriggerRequest.getDumpName(), + dumpTriggerRequest.getDumpType() ); } catch (Exception e) { - e.printStackTrace(); + throw new RuntimeException("Failed to connect to airflow", e.getCause()); } - return null; } private String convertMapToString(Map payloadMap) { @@ -103,7 +104,8 @@ public class AirflowClient { String cluster, String namespace, String podName, - String dumpName + String dumpName, + String dumpType ) { if (response.statusCode() == HttpStatus.OK.value()) { var dashboardUri = dashboardUri(dagId, runId); @@ -113,6 +115,7 @@ public class AirflowClient { airflowApiResponse.setNamespace(namespace); airflowApiResponse.setPodName(podName); airflowApiResponse.setDumpName(dumpName); + airflowApiResponse.setDumpType(dumpType); log.info("Link to Airflow DAG: " + dashboardUri); return airflowApiResponse; diff --git a/src/main/java/com/navi/infra/portal/v2/dump/Dump.java b/src/main/java/com/navi/infra/portal/v2/dump/Dump.java index f6c87dff..0a5f8a9c 100644 --- a/src/main/java/com/navi/infra/portal/v2/dump/Dump.java +++ b/src/main/java/com/navi/infra/portal/v2/dump/Dump.java @@ -1,6 +1,7 @@ package com.navi.infra.portal.v2.dump; import com.navi.infra.portal.domain.BaseEntity; +import javax.persistence.Convert; import javax.persistence.Entity; import javax.persistence.EnumType; import javax.persistence.Enumerated; @@ -31,4 +32,7 @@ public class Dump extends BaseEntity { private DumpStatus status; private String remarks; private String dumpName; + + @Convert(converter = DumpTypeConverter.class) + private DumpType dumpType; } diff --git a/src/main/java/com/navi/infra/portal/v2/dump/DumpController.java b/src/main/java/com/navi/infra/portal/v2/dump/DumpController.java index 700cb89b..03fbf253 100644 --- a/src/main/java/com/navi/infra/portal/v2/dump/DumpController.java +++ b/src/main/java/com/navi/infra/portal/v2/dump/DumpController.java @@ -44,15 +44,17 @@ public class DumpController { } @PostMapping - public ResponseEntity> triggerDump(@RequestBody DumpRequest dumpRequest) { + public ResponseEntity triggerDump(@RequestBody TriggerDumpRequest triggerDumpRequest) { try { log.info("Triggering dump for manifest id: {}, dump type: {}, pod names: {}", - dumpRequest.getManifestId(), dumpRequest.getDumpType(), dumpRequest.getPodNames()); - final var dtoList = dumpService.createDump(dumpRequest); - return new ResponseEntity<>(dtoList, CREATED); + triggerDumpRequest.getManifestId(), triggerDumpRequest.getDumpType(), + triggerDumpRequest.getPodNames()); + final var dtoList = dumpService.createDump(triggerDumpRequest); + return ResponseEntity.status(CREATED).body(TriggerDumpResponse.of(dtoList)); } catch (Exception e) { log.error("Failed to trigger dump", e); - return ResponseEntity.status(INTERNAL_SERVER_ERROR).build(); + return ResponseEntity.status(INTERNAL_SERVER_ERROR) + .body(TriggerDumpResponse.of(e.getMessage())); } } diff --git a/src/main/java/com/navi/infra/portal/v2/dump/DumpService.java b/src/main/java/com/navi/infra/portal/v2/dump/DumpService.java index 8dd5d5cb..5d7f48a6 100644 --- a/src/main/java/com/navi/infra/portal/v2/dump/DumpService.java +++ b/src/main/java/com/navi/infra/portal/v2/dump/DumpService.java @@ -4,7 +4,7 @@ import java.util.List; public interface DumpService { - List createDump(DumpRequest dumpRequest); + List createDump(TriggerDumpRequest triggerDumpRequest); DumpDto updateStatus(DumpStatusUpdateRequest dumpStatusUpdateRequest); diff --git a/src/main/java/com/navi/infra/portal/v2/dump/DumpServiceImpl.java b/src/main/java/com/navi/infra/portal/v2/dump/DumpServiceImpl.java index 2f5b032b..c0b65dd0 100644 --- a/src/main/java/com/navi/infra/portal/v2/dump/DumpServiceImpl.java +++ b/src/main/java/com/navi/infra/portal/v2/dump/DumpServiceImpl.java @@ -2,6 +2,7 @@ package com.navi.infra.portal.v2.dump; import static com.navi.infra.portal.v2.dump.DumpServiceUtilities.ASIA_KOLKATA; import static com.navi.infra.portal.v2.dump.DumpServiceUtilities.createRunId; +import static com.navi.infra.portal.v2.dump.DumpServiceUtilities.getBucketName; import static com.navi.infra.portal.v2.dump.DumpServiceUtilities.getCluster; import static com.navi.infra.portal.v2.dump.DumpServiceUtilities.getContainerFromPod; import static com.navi.infra.portal.v2.dump.DumpServiceUtilities.getDumpName; @@ -35,7 +36,7 @@ class DumpServiceImpl implements DumpService { private final DumpRepository dumpRepository; private final AwsS3Client awsS3Client; private final String dagId; - private final String bucketName; + private final String vertical; private final String runnerImage; public DumpServiceImpl( @@ -44,7 +45,7 @@ class DumpServiceImpl implements DumpService { DumpRepository dumpRepository, AwsS3Client awsS3Client, @Value("${service-dump.dag.id}") String dagId, - @Value("${service-dump.bucket.name}") String bucketName, + @Value("${portal.vertical}") String vertical, @Value("${service-dump.image.name}") String runnerImage ) { this.airflowClient = airflowClient; @@ -52,25 +53,25 @@ class DumpServiceImpl implements DumpService { this.dumpRepository = dumpRepository; this.awsS3Client = awsS3Client; this.dagId = dagId; - this.bucketName = bucketName; + this.vertical = vertical; this.runnerImage = runnerImage; } @Override - public List createDump(DumpRequest dumpRequest) { - final var manifest = manifestService.fetchById(dumpRequest.getManifestId()); + public List createDump(TriggerDumpRequest triggerDumpRequest) { + final var manifest = manifestService.fetchById(triggerDumpRequest.getManifestId()); final var cluster = getCluster(manifest); final var namespace = getNamespace(manifest); final var environment = getEnvironment(manifest); final var manifestName = manifest.getName(); - return createDump(dumpRequest, cluster, namespace, environment, manifestName) + return createDump(triggerDumpRequest, cluster, namespace, environment, manifestName) .map(DumpDto::new) .collect(toList()); } private Stream createDump( - DumpRequest dumpRequest, + TriggerDumpRequest triggerDumpRequest, String cluster, String namespace, String env, @@ -80,26 +81,28 @@ class DumpServiceImpl implements DumpService { var dump = new Dump(); dump.setDagId(response.getDagId()); dump.setRunId(response.getDagRunId()); - dump.setManifestId(dumpRequest.getManifestId()); + dump.setManifestId(triggerDumpRequest.getManifestId()); dump.setCluster(response.getCluster()); dump.setNamespace(response.getNamespace()); dump.setPodName(response.getPodName()); dump.setStatus(DumpStatus.STARTED); dump.setDumpName(response.getDumpName()); + dump.setDumpType(DumpType.of(response.getDumpType())); return dump; }; - return dumpRequest.getPodNames() + return triggerDumpRequest.getPodNames() .parallelStream() .distinct() - .map(createDumpTriggerRequest(dumpRequest, cluster, namespace, env, manifestName)) + .map( + createDumpTriggerRequest(triggerDumpRequest, cluster, namespace, env, manifestName)) .map(airflowClient::triggerDag) .map(toDumpEntity) .map(dumpRepository::save); } private Function createDumpTriggerRequest( - DumpRequest dumpRequest, + TriggerDumpRequest triggerDumpRequest, String cluster, String namespace, String environment, @@ -108,10 +111,11 @@ class DumpServiceImpl implements DumpService { return podName -> { final var instant = LocalDateTime.now(ZoneId.of(ASIA_KOLKATA)); final var dumpName = getDumpName(podName, instant, - dumpRequest.getDumpType().getName(), - dumpRequest.getDumpType().getExtension()); + triggerDumpRequest.getDumpType().getName(), + triggerDumpRequest.getDumpType().getExtension()); final var dumpPath = getDumpPath(environment, manifestName); - final var preSignedUrl = getPreSignedUrl(bucketName, dumpPath, dumpName); + final var preSignedUrl = getPreSignedUrl(getBucketName(vertical, environment), dumpPath, + dumpName); return new DumpTriggerRequestBuilder() .cluster(cluster) .namespace(namespace) @@ -122,6 +126,7 @@ class DumpServiceImpl implements DumpService { .runnerImage(runnerImage) .preSignedUrl(preSignedUrl) .dumpName(dumpName) + .dumpType(triggerDumpRequest.getDumpType().getName()) .build(); }; } @@ -176,7 +181,8 @@ class DumpServiceImpl implements DumpService { final var dumpPath = getDumpPath(manifest.getEnvironment(), manifest.getName()); final var objectKey = dumpPath + "/" + dumpName; - final var inputStream = awsS3Client.downloadFile(bucketName, objectKey); + final var inputStream = awsS3Client.downloadFile(getBucketName(vertical, + manifest.getEnvironment()), objectKey); return new DumpDownloadDto(dumpName, inputStream); } } diff --git a/src/main/java/com/navi/infra/portal/v2/dump/DumpServiceUtilities.java b/src/main/java/com/navi/infra/portal/v2/dump/DumpServiceUtilities.java index e1ecc3ac..d81556fd 100644 --- a/src/main/java/com/navi/infra/portal/v2/dump/DumpServiceUtilities.java +++ b/src/main/java/com/navi/infra/portal/v2/dump/DumpServiceUtilities.java @@ -36,6 +36,10 @@ public class DumpServiceUtilities { dumpExtension); } + static String getBucketName(String vertical, String env) { + return format("java-heap-dumps-%s-%s", vertical, env); + } + static String getContainerFromPod(String podName) { var split = podName.split("-"); var containerName = Arrays.copyOfRange(split, 0, split.length - 2); diff --git a/src/main/java/com/navi/infra/portal/v2/dump/DumpTriggerRequest.java b/src/main/java/com/navi/infra/portal/v2/dump/DumpTriggerRequest.java index 0c2fdbcd..7a4afbc9 100644 --- a/src/main/java/com/navi/infra/portal/v2/dump/DumpTriggerRequest.java +++ b/src/main/java/com/navi/infra/portal/v2/dump/DumpTriggerRequest.java @@ -15,4 +15,5 @@ public class DumpTriggerRequest { private String runnerImage; private String preSignedUrl; private String dumpName; + private String dumpType; } diff --git a/src/main/java/com/navi/infra/portal/v2/dump/DumpType.java b/src/main/java/com/navi/infra/portal/v2/dump/DumpType.java new file mode 100644 index 00000000..867dd7d4 --- /dev/null +++ b/src/main/java/com/navi/infra/portal/v2/dump/DumpType.java @@ -0,0 +1,45 @@ +package com.navi.infra.portal.v2.dump; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonValue; + +enum DumpType { + HEAP_DUMP("heap_dump", "bin"), + THREAD_DUMP("thread_dump", "bin"), + CPU_PROFILE("cpu_profile", "pprof"); + + private final String name; + private final String extension; + + DumpType(String name, String extension) { + this.name = name; + this.extension = extension; + } + + @JsonCreator + public static DumpType fromString(String name) { + return of(name); + } + + public static DumpType of(String code) { + switch (code) { + case "heap_dump": + return HEAP_DUMP; + case "thread_dump": + return THREAD_DUMP; + case "cpu_profile": + return CPU_PROFILE; + default: + throw new IllegalArgumentException("Invalid code: " + code); + } + } + + @JsonValue + public String getName() { + return name; + } + + public String getExtension() { + return extension; + } +} diff --git a/src/main/java/com/navi/infra/portal/v2/dump/DumpTypeConverter.java b/src/main/java/com/navi/infra/portal/v2/dump/DumpTypeConverter.java new file mode 100644 index 00000000..e088c9cd --- /dev/null +++ b/src/main/java/com/navi/infra/portal/v2/dump/DumpTypeConverter.java @@ -0,0 +1,16 @@ +package com.navi.infra.portal.v2.dump; + +import javax.persistence.AttributeConverter; + +public class DumpTypeConverter implements AttributeConverter { + + @Override + public String convertToDatabaseColumn(DumpType dumpType) { + return dumpType.getName(); + } + + @Override + public DumpType convertToEntityAttribute(String code) { + return DumpType.of(code); + } +} diff --git a/src/main/java/com/navi/infra/portal/v2/dump/TriggerDumpRequest.java b/src/main/java/com/navi/infra/portal/v2/dump/TriggerDumpRequest.java new file mode 100644 index 00000000..00384630 --- /dev/null +++ b/src/main/java/com/navi/infra/portal/v2/dump/TriggerDumpRequest.java @@ -0,0 +1,14 @@ +package com.navi.infra.portal.v2.dump; + +import java.util.List; +import lombok.Getter; +import lombok.Setter; + +@Setter +@Getter +public class TriggerDumpRequest { + + private Long manifestId; + private DumpType dumpType; + private List podNames; +} diff --git a/src/main/java/com/navi/infra/portal/v2/dump/TriggerDumpResponse.java b/src/main/java/com/navi/infra/portal/v2/dump/TriggerDumpResponse.java new file mode 100644 index 00000000..fa52a184 --- /dev/null +++ b/src/main/java/com/navi/infra/portal/v2/dump/TriggerDumpResponse.java @@ -0,0 +1,30 @@ +package com.navi.infra.portal.v2.dump; + +import static java.util.Collections.emptyList; + +import java.util.List; +import lombok.Data; + +@Data +public class TriggerDumpResponse { + + private List dumps; + private String status; + + private TriggerDumpResponse(List dumps) { + this.dumps = dumps; + } + + private TriggerDumpResponse(String status) { + this.dumps = emptyList(); + this.status = status; + } + + public static TriggerDumpResponse of(List dumps) { + return new TriggerDumpResponse(dumps); + } + + public static TriggerDumpResponse of(String status) { + return new TriggerDumpResponse(status); + } +} diff --git a/src/main/resources/application-dev.properties b/src/main/resources/application-dev.properties index b2bdab80..aace5521 100644 --- a/src/main/resources/application-dev.properties +++ b/src/main/resources/application-dev.properties @@ -31,5 +31,4 @@ aws.region=ap-south-1 airflow.url=${AIRFLOW_URL} airflow.token=${AIRFLOW_AUTH_TOKEN} service-dump.dag.id=${SERVICE_DUMP_DAG_ID:kubectl_get_pod} -service-dump.bucket.name=${SERVICE_DUMP_BUCKET_NAME} service-dump.image.name=${SERVICE_DUMP_IMAGE_NAME:193044292705.dkr.ecr.ap-south-1.amazonaws.com/common/openjdk:11.0.16-user4k} diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index 1f9e045a..acbf13d1 100644 --- a/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -67,6 +67,5 @@ kubernetes.security-group.id.fetch.fixed-backoff.max-attempts=${SECURITY_GROUP_I extraResource.list=database,docdb,elasticCache,aws_access,dynamodb,s3_buckets,deployment airflow.url=${AIRFLOW_URL} airflow.token=${AIRFLOW_AUTH_TOKEN} -service-dump.dag.id=${SERVICE_DUMP_DAG_ID:kubectl_get_pod} -service-dump.bucket.name=${SERVICE_DUMP_BUCKET_NAME} +service-dump.dag.id=${SERVICE_DUMP_DAG_ID} service-dump.image.name=${SERVICE_DUMP_IMAGE_NAME:193044292705.dkr.ecr.ap-south-1.amazonaws.com/common/openjdk:11.0.16-user4k} diff --git a/src/main/resources/db/migration/V1.59__Add_dump_table.sql b/src/main/resources/db/migration/V1.59__Add_dump_table.sql index da1db271..c14de9b9 100644 --- a/src/main/resources/db/migration/V1.59__Add_dump_table.sql +++ b/src/main/resources/db/migration/V1.59__Add_dump_table.sql @@ -8,6 +8,7 @@ CREATE TABLE dump namespace VARCHAR(255) NOT NULL, pod_name VARCHAR(255) NOT NULL, dump_name VARCHAR(255) NOT NULL, + dump_type VARCHAR(255) NOT NULL, status VARCHAR(255) NOT NULL, remarks VARCHAR(255), created_at TIMESTAMP NOT NULL, diff --git a/src/test/resources/application.properties b/src/test/resources/application.properties index 26b1f0cd..30f9071e 100644 --- a/src/test/resources/application.properties +++ b/src/test/resources/application.properties @@ -61,5 +61,4 @@ extraResource.list=database,docdb,elasticCache,aws_access,dynamodb,s3_buckets,de airflow.url=${AIRFLOW_URL:http://localhost:9090} airflow.token=${AIRFLOW_AUTH_TOKEN:something} service-dump.dag.id=${SERVICE_DUMP_DAG_ID:kubectl_get_pod} -service-dump.bucket.name=${SERVICE_DUMP_BUCKET_NAME:bucket_name} service-dump.image.name=${SERVICE_DUMP_IMAGE_NAME:193044292705.dkr.ecr.ap-south-1.amazonaws.com/common/openjdk:11.0.16-user4k}