Skip to content
20 changes: 20 additions & 0 deletions src/main/java/com/autotune/analyzer/serviceObjects/BulkInput.java
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
*******************************************************************************/
package com.autotune.analyzer.serviceObjects;

import com.fasterxml.jackson.annotation.JsonIgnore;
import java.util.List;
import java.util.Map;

Expand Down Expand Up @@ -43,6 +44,12 @@ public void setRequestId(String requestId) {
public BulkInput() {
}

@JsonIgnore
public boolean isEmpty() {
return (filter == null && time_range == null && measurement_duration == null && metadata_profile == null
&& datasource == null);
}

public TimeRange getTime_range() {
return time_range;
}
Expand Down Expand Up @@ -166,6 +173,19 @@ public String getEnd() {
public void setEnd(String end) {
this.end = end;
}

@JsonIgnore
public boolean isEmpty() {
return (start == null && end == null);
}

@Override
public String toString() {
return "TimeRange{" +
"start='" + start + '\'' +
", end='" + end + '\'' +
'}';
}
}

public static class Webhook {
Expand Down
17 changes: 16 additions & 1 deletion src/main/java/com/autotune/analyzer/services/BulkService.java
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@
import com.autotune.analyzer.serviceObjects.BulkInput;
import com.autotune.analyzer.serviceObjects.BulkJobStatus;
import com.autotune.analyzer.workerimpl.BulkJobManager;
import com.autotune.common.bulk.BulkServiceValidation;
import com.autotune.common.data.ValidationOutputData;
import com.autotune.database.dao.ExperimentDAO;
import com.autotune.database.dao.ExperimentDAOImpl;
import com.autotune.database.table.lm.KruizeBulkJobEntry;
Expand Down Expand Up @@ -283,6 +285,13 @@ protected void doPost(HttpServletRequest request, HttpServletResponse response)

// Generate a unique jobID
String jobID = UUID.randomUUID().toString();
// validate the input params
if (payload != null && !payload.isEmpty()) {
ValidationOutputData validationOutputData = BulkServiceValidation.validate(payload, jobID);
if (!validationOutputData.isSuccess()) {
throw new Exception(validationOutputData.getMessage());
}
}
BulkJobStatus jobStatus = new BulkJobStatus(jobID, IN_PROGRESS, Instant.now(), payload);

if (KruizeDeploymentInfo.TEST_USE_ONLY_CACHE_JOB_IN_MEM)
Expand All @@ -303,6 +312,13 @@ protected void doPost(HttpServletRequest request, HttpServletResponse response)
jsonObject.put(JOB_ID, jobID);
response.getWriter().write(jsonObject.toString());
statusValue = "success";
} catch (Exception e) {
sendErrorResponse(
response,
null,
HttpServletResponse.SC_BAD_REQUEST,
e.getMessage()
);
} finally {
if (null != timerCreateBulkJob) {
MetricsConfig.timerCreateBulkJob = MetricsConfig.timerBCreateBulkJob.tag("status", statusValue).register(MetricsConfig.meterRegistry());
Expand All @@ -320,7 +336,6 @@ public void sendErrorResponse(HttpServletResponse response, Exception e, int htt
IOException {
if (null != e) {
LOGGER.error(e.toString());
e.printStackTrace();
if (null == errorMsg) errorMsg = e.getMessage();
}
response.sendError(httpStatusCode, errorMsg);
Expand Down
95 changes: 95 additions & 0 deletions src/main/java/com/autotune/common/bulk/BulkServiceValidation.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
package com.autotune.common.bulk;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@khansaad Thanks for raising the validations PR. Please kindly add the Copyrights header, as it's a new file.


import com.autotune.analyzer.serviceObjects.BulkInput;
import com.autotune.analyzer.serviceObjects.BulkJobStatus;
import com.autotune.common.data.ValidationOutputData;
import com.autotune.common.datasource.DataSourceInfo;
import com.autotune.common.datasource.DataSourceOperatorImpl;
import com.autotune.common.utils.CommonUtils;
import com.autotune.database.dao.ExperimentDAOImpl;
import com.autotune.database.service.ExperimentDBService;
import com.autotune.database.table.lm.KruizeBulkJobEntry;
import com.autotune.utils.KruizeConstants;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.time.Duration;
import java.time.OffsetDateTime;
import java.time.format.DateTimeParseException;

public class BulkServiceValidation {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Adding a Javadoc could be a bit helpful for the devs to understand about this class.


private static final Logger LOGGER = LoggerFactory.getLogger(BulkServiceValidation.class);

public static ValidationOutputData validate(BulkInput payload, String jobID) throws Exception {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Java doc for this and other methods would be helpful as well.


ValidationOutputData validationOutputData;

validationOutputData = buildErrorOutput(validateTimeRange(payload.getTime_range()), jobID);
if (validationOutputData != null) return validationOutputData;

if (payload.getDatasource() != null) {
validationOutputData = buildErrorOutput(validateDatasourceConnection(payload.getDatasource()), jobID);
}

if (validationOutputData == null) {
validationOutputData = new ValidationOutputData(true, "", 200);
}
return validationOutputData;
}

private static ValidationOutputData buildErrorOutput(String errorMsg, String jobID) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Having a java doc would be helpful

if (errorMsg != null && !errorMsg.isEmpty()) {
return new ValidationOutputData(false, errorMsg + " for the jobId: " + jobID, 400);
}
return null;
}


public static String validateDatasourceConnection(String datasourceName) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Having a java doc would be helpful

String errorMessage = "";
try {
DataSourceInfo dataSourceInfo = null;
try {
dataSourceInfo = new ExperimentDBService().loadDataSourceFromDBByName(datasourceName);
} catch (Exception e) {
errorMessage = String.format(KruizeConstants.DataSourceConstants.DataSourceMetadataErrorMsgs.LOAD_DATASOURCE_FROM_DB_ERROR, datasourceName, e.getMessage());
LOGGER.error(errorMessage);
return errorMessage;
}
LOGGER.info(KruizeConstants.DataSourceConstants.DataSourceInfoMsgs.VERIFYING_DATASOURCE_REACHABILITY, datasourceName);
DataSourceOperatorImpl op = DataSourceOperatorImpl.getInstance().getOperator(KruizeConstants.SupportedDatasources.PROMETHEUS);
if (dataSourceInfo == null || op.isServiceable(dataSourceInfo) == CommonUtils.DatasourceReachabilityStatus.NOT_REACHABLE) {
errorMessage = KruizeConstants.DataSourceConstants.DataSourceErrorMsgs.DATASOURCE_NOT_SERVICEABLE;
LOGGER.error(errorMessage);
}
} catch (Exception ex) {
errorMessage = ex.getMessage();
LOGGER.error(errorMessage);
}
return errorMessage;
}

public static String validateTimeRange(BulkInput.TimeRange timeRange) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Having a java doc would be helpful

String errorMessage = "";
if (timeRange == null || timeRange.isEmpty()) {
LOGGER.debug("No time range specified");
return errorMessage;
}
try {
OffsetDateTime startTime = OffsetDateTime.parse(timeRange.getStart());
OffsetDateTime endTime = OffsetDateTime.parse(timeRange.getEnd());

if (startTime.isAfter(endTime)) {
errorMessage = KruizeConstants.KRUIZE_BULK_API.INVALID_START_TIME;
return errorMessage;
}

} catch (DateTimeParseException ex) {
errorMessage = KruizeConstants.KRUIZE_BULK_API.INVALID_DATE_FORMAT;
} catch (Exception e) {
errorMessage = KruizeConstants.KRUIZE_BULK_API.TIME_RANGE_EXCEPTION;
}
return errorMessage;
}
}
10 changes: 10 additions & 0 deletions src/main/java/com/autotune/utils/KruizeConstants.java
Original file line number Diff line number Diff line change
Expand Up @@ -900,6 +900,16 @@ public static final class KRUIZE_BULK_API {
public static final String BULK_JOB_SAVE_ERROR = "Not able to save experiment due to {}";
public static final String BULK_JOB_LOAD_ERROR = "Not able to load bulk JOB {} due to {}";

// Validation error messages
public static final String DUPLICATE_REQ_ID_WITH_SAME_PAYLOAD = "Duplicate requestId found with different payload: %s";
public static final String MISSING_REQUEST_ID = "RequestId parameter is missing";
public static final String INVALID_REQUEST_ID = "Invalid requestId format. Must be 36-character alphanumeric";
public static final String INVALID_START_TIME = "Start time should be before end time";
public static final String INVALID_TIME_RANGE = "Time range must be between 24 hours and 15 days";
public static final String INVALID_DATE_FORMAT = "Invalid date format. Must follow ISO 8601 format (YYYY-MM-DDTHH:mm:ss.sssZ)";
public static final String TIME_RANGE_EXCEPTION = "Exception occurred while validating the time range";



// TODO : Bulk API Create Experiments defaults
public static final CreateExperimentConfigBean CREATE_EXPERIMENT_CONFIG_BEAN;
Expand Down