Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Multiple Import Metadata actions #1178

Merged
merged 18 commits into from
Jun 26, 2024
Merged
Show file tree
Hide file tree
Changes from 12 commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
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
252 changes: 240 additions & 12 deletions src/main/java/com/autotune/analyzer/services/DSMetadataService.java
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,17 @@

package com.autotune.analyzer.services;

import com.autotune.analyzer.exceptions.KruizeResponse;
import com.autotune.analyzer.serviceObjects.DSMetadataAPIObject;
import com.autotune.analyzer.utils.AnalyzerConstants;
import com.autotune.analyzer.utils.AnalyzerErrorConstants;
import com.autotune.analyzer.utils.GsonUTCDateAdapter;
import com.autotune.common.data.dataSourceMetadata.DataSourceMetadataInfo;
import com.autotune.common.datasource.DataSourceInfo;
import com.autotune.common.datasource.DataSourceManager;
import com.autotune.common.datasource.DataSourceMetadataValidation;
import com.autotune.database.service.ExperimentDBService;
import com.autotune.utils.KruizeConstants;
import com.autotune.utils.KruizeSupportedTypes;
import com.autotune.utils.MetricsConfig;
import com.google.gson.Gson;
Expand All @@ -38,10 +41,8 @@
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Set;
import java.io.PrintWriter;
import java.util.*;
import java.util.stream.Collectors;

import static com.autotune.analyzer.utils.AnalyzerConstants.ServiceConstants.CHARACTER_ENCODING;
Expand All @@ -55,13 +56,44 @@ public void init(ServletConfig config) throws ServletException {
super.init(config);
}

/**
* Handles the POST request for importing metadata - POST /dsmetadata
* @param request the HttpServletRequest containing the client request
* @param response the HttpServletResponse containing the response to be sent to the client
*
* The input request body should be a JSON object with the following structure:
* Example:
* {
* "version": "v1.0",
* "datasource_name": "exampleDataSourceName"
* }
* where:
* The `version` field is the version of the API, and the `datasource_name` field is the datasource name
*
* Example API response:
* {
* "datasources": {
* "exampleDataSourceName": {
* "datasource_name": "exampleDataSourceName",
* "clusters": {
* "exampleClusterName": {
* "cluster_name": "exampleClusterName"
* }
* }
* }
* }
* }
* API response displays cluster-level metadata
* NOTE - POST /dsmetadata API also supports multiple import metadata actions
*/
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String statusValue = "failure";
Timer.Sample timerImportDSMetadata = Timer.start(MetricsConfig.meterRegistry());
//Key = dataSourceName
HashMap<String, DataSourceMetadataInfo> dataSourceMetadataMap = new HashMap<>();
String inputData = "";
DataSourceManager dataSourceManager = new DataSourceManager();

try {
// Set the character encoding of the request to UTF-8
Expand All @@ -70,7 +102,7 @@ protected void doPost(HttpServletRequest request, HttpServletResponse response)
inputData = request.getReader().lines().collect(Collectors.joining());

if (null == inputData || inputData.isEmpty()) {
throw new Exception("Request input data cannot be null or empty");
throw new Exception(AnalyzerErrorConstants.APIErrors.DSMetadataAPI.DATASOURCE_METADATA_MISSING_REQUEST_INPUT_EXCPTN);
}

DSMetadataAPIObject metadataAPIObject = new Gson().fromJson(inputData, DSMetadataAPIObject.class);
Expand All @@ -86,15 +118,77 @@ protected void doPost(HttpServletRequest request, HttpServletResponse response)
null,
HttpServletResponse.SC_BAD_REQUEST,
AnalyzerErrorConstants.APIErrors.DSMetadataAPI.DATASOURCE_NAME_MANDATORY);
return;
}

DataSourceInfo datasource = new ExperimentDBService().loadDataSourceFromDBByName(dataSourceName);
if(null != datasource) {
new DataSourceManager().importMetadataFromDataSource(datasource);
DataSourceMetadataInfo dataSourceMetadata = new ExperimentDBService().loadMetadataFromDBByName(dataSourceName, "false");
dataSourceMetadataMap.put(dataSourceName,dataSourceMetadata);
DataSourceInfo datasource;
try {
datasource = dataSourceManager.fetchDataSourceFromDBByName(dataSourceName);
} catch (Exception e) {
sendErrorResponse(
inputData,
response,
e,
HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
String.format(KruizeConstants.DataSourceConstants.DataSourceMetadataErrorMsgs.LOAD_DATASOURCE_FROM_DB_ERROR, dataSourceName, e.getMessage())
);
return;
}

if (datasource == null) {
sendErrorResponse(
inputData,
response,
new Exception(AnalyzerErrorConstants.APIErrors.DSMetadataAPI.INVALID_DATASOURCE_NAME_METADATA_EXCPTN),
HttpServletResponse.SC_BAD_REQUEST,
String.format(AnalyzerErrorConstants.APIErrors.DSMetadataAPI.DATASOURCE_METADATA_IMPORT_ERROR_MSG, dataSourceName)
);
return;
}

DataSourceMetadataInfo metadataInfo;
try {
dataSourceManager.importMetadataFromDataSource(datasource);
metadataInfo = dataSourceManager.getMetadataFromDataSource(datasource);
} catch (Exception e) {
sendErrorResponse(inputData, response, e, HttpServletResponse.SC_INTERNAL_SERVER_ERROR, e.getMessage());
return;
}

// Validate imported metadataInfo object
DataSourceMetadataValidation validationObject = new DataSourceMetadataValidation();
validationObject.validate(metadataInfo);

if (!validationObject.isSuccess()) {
sendErrorResponse(
response,
new Exception(AnalyzerErrorConstants.APIErrors.DSMetadataAPI.DATASOURCE_METADATA_VALIDATION_FAILURE_EXCPTN),
HttpServletResponse.SC_BAD_REQUEST,
validationObject.getErrorMessage()
);
return;
}

try {
// fetch and delete metadata from database
dataSourceManager.deleteMetadataFromDBByDataSource(datasource);
// fetch metadata from cluster of specified datasource and add to database
dataSourceManager.saveMetadataFromDataSourceToDB(datasource);
} catch (Exception e) {
sendErrorResponse(inputData, response, e, HttpServletResponse.SC_INTERNAL_SERVER_ERROR, e.getMessage());
return;
}

DataSourceMetadataInfo dataSourceMetadata;
try {
dataSourceMetadata = dataSourceManager.fetchDataSourceMetadataFromDBByName(dataSourceName, "false");
} catch (Exception e) {
sendErrorResponse(inputData, response, e, HttpServletResponse.SC_INTERNAL_SERVER_ERROR, e.getMessage());
return;
}

dataSourceMetadataMap.put(dataSourceName, dataSourceMetadata);

if (dataSourceMetadataMap.isEmpty() || !dataSourceMetadataMap.containsKey(dataSourceName)) {
sendErrorResponse(
inputData,
Expand All @@ -103,9 +197,11 @@ protected void doPost(HttpServletRequest request, HttpServletResponse response)
HttpServletResponse.SC_BAD_REQUEST,
String.format(AnalyzerErrorConstants.APIErrors.DSMetadataAPI.DATASOURCE_METADATA_IMPORT_ERROR_MSG, dataSourceName)
);
} else {
sendSuccessResponse(response, dataSourceMetadataMap.get(dataSourceName));
return;
}

sendSuccessResponse(response, dataSourceMetadataMap.get(dataSourceName));

} catch (Exception e) {
e.printStackTrace();
LOGGER.error("Unknown exception caught: " + e.getMessage());
Expand Down Expand Up @@ -150,6 +246,24 @@ public void sendErrorResponse(String inputRequestPayload, HttpServletResponse re
response.sendError(httpStatusCode, errorMsg);
}

/**
* Handles the GET request for listing metadata - GET /dsmetadata
* @param request
* @param response
* @throws ServletException
* @throws IOException
*
* Supported Query Parameters -
* datasource name of the datasource(required)
* cluster_name name of the cluster(optional)
* namespace The namespace(optional)
* verbose Flag to retrieve container-level metadata(optional)
*
* When the verbose parameter is set to true, the API response includes granular container-level details in the metadata,
* offering a more comprehensive view of the clusters, namespaces, workloads and containers associated with the
* specified datasource. When the verbose parameter is not provided or set to false, the API response provides basic
* information like list of clusters, namespaces associated with the specified datasource
*/
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException{
String statusValue = "failure";
Expand Down Expand Up @@ -283,4 +397,118 @@ private Gson createGsonObject() {
private boolean isValidBooleanValue(String value) {
return value != null && (value.equals("true") || value.equals("false"));
}

/**
* TODO temp solution to delete metadata, Need to evaluate use cases
* Handles the DELETE request for deleting metadata - DELETE /dsmetadata
*
* The input request body should be a JSON object with the following structure:
* {
* "version": "exampleVersion",
* "datasource_name": "exampleDataSourceName"
* }
*
* The `version` field is the version of the API, and the `datasource_name` field is the data source name.
*
* The expected output is a response indicating the success or failure of the datasource metadata deletion.
*
* @param request the HttpServletRequest containing the client request
* @param response the HttpServletResponse containing the response to be sent to the client
* @throws ServletException
* @throws IOException
*/
@Override
protected void doDelete(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
dinogun marked this conversation as resolved.
Show resolved Hide resolved
HashMap<String, DataSourceMetadataInfo> dataSourceMetadataMap = new HashMap<>();
String inputData = "";
DataSourceManager dataSourceManager = new DataSourceManager();
try {
// Set the character encoding of the request to UTF-8
request.setCharacterEncoding(CHARACTER_ENCODING);

inputData = request.getReader().lines().collect(Collectors.joining());
if (null == inputData || inputData.isEmpty()) {
throw new Exception(AnalyzerErrorConstants.APIErrors.DSMetadataAPI.DATASOURCE_METADATA_MISSING_REQUEST_INPUT_EXCPTN);
}
DSMetadataAPIObject metadataAPIObject = new Gson().fromJson(inputData, DSMetadataAPIObject.class);
metadataAPIObject.validateInputFields();
String dataSourceName = metadataAPIObject.getDataSourceName();

if (null == dataSourceName || dataSourceName.isEmpty()) {
sendErrorResponse(
inputData,
response,
null,
HttpServletResponse.SC_BAD_REQUEST,
AnalyzerErrorConstants.APIErrors.DSMetadataAPI.DATASOURCE_NAME_MANDATORY);
}

DataSourceInfo datasource = dataSourceManager.fetchDataSourceFromDBByName(dataSourceName);

if (null == datasource) {
sendErrorResponse(
inputData,
response,
new Exception(AnalyzerErrorConstants.APIErrors.DSMetadataAPI.INVALID_DATASOURCE_NAME_METADATA_EXCPTN),
HttpServletResponse.SC_BAD_REQUEST,
String.format(AnalyzerErrorConstants.APIErrors.DSMetadataAPI.DATASOURCE_METADATA_DELETE_ERROR_MSG, dataSourceName)
);
}

DataSourceMetadataInfo dataSourceMetadata = dataSourceManager.fetchDataSourceMetadataFromDBByName(dataSourceName, "false");
if (null == dataSourceMetadata) {
sendErrorResponse(
inputData,
response,
new Exception(AnalyzerErrorConstants.APIErrors.DSMetadataAPI.MISSING_DATASOURCE_METADATA_EXCPTN),
HttpServletResponse.SC_BAD_REQUEST,
String.format(AnalyzerErrorConstants.APIErrors.DSMetadataAPI.DATASOURCE_METADATA_DELETE_ERROR_MSG, dataSourceName)
);
}
dataSourceMetadataMap.put(dataSourceName, dataSourceMetadata);

if (!dataSourceMetadataMap.isEmpty() && dataSourceMetadataMap.containsKey(dataSourceName)) {
try {
// fetch and delete metadata from database
dataSourceManager.deleteMetadataFromDBByDataSource(datasource);

//deletes in-memory metadata object fetched from the cluster of the specified datasource
dataSourceManager.deleteMetadataFromDataSource(datasource);
dataSourceMetadataMap.remove(dataSourceName);
} catch (Exception e) {
sendErrorResponse(
inputData,
response,
e,
HttpServletResponse.SC_BAD_REQUEST,
e.getMessage());
}
} else {
sendErrorResponse(
inputData,
response,
new Exception(AnalyzerErrorConstants.APIErrors.DSMetadataAPI.DATASOURCE_METADATA_DELETE_EXCPTN),
HttpServletResponse.SC_BAD_REQUEST,
String.format(AnalyzerErrorConstants.APIErrors.DSMetadataAPI.DATASOURCE_METADATA_DELETE_ERROR_MSG, dataSourceName)
);
}
sendSuccessResponse(response, KruizeConstants.DataSourceConstants.DataSourceMetadataInfoSuccessMsgs.DATASOURCE_METADATA_DELETED);

} catch (Exception e) {
sendErrorResponse(inputData, response, e, HttpServletResponse.SC_BAD_REQUEST, e.getMessage());
}
}

private void sendSuccessResponse(HttpServletResponse response, String message) throws IOException {
response.setContentType(JSON_CONTENT_TYPE);
response.setCharacterEncoding(CHARACTER_ENCODING);
response.setStatus(HttpServletResponse.SC_CREATED);
PrintWriter out = response.getWriter();
out.append(
new Gson().toJson(
new KruizeResponse(message + " View imported metadata at GET /dsmetadata", HttpServletResponse.SC_CREATED, "", "SUCCESS")
)
);
out.flush();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -317,7 +317,6 @@ public static final class ServiceConstants {
public static final String VERBOSE = "verbose";
public static final String FALSE = "false";


private ServiceConstants() {
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -186,8 +186,12 @@ private DSMetadataAPI(){
public static final String MISSING_DATASOURCE_METADATA_MSG = "Metadata for a given datasource - \" %s \", cluster name - \" %s \", namespace - \"%s \" " +
"either does not exist or is not valid";
public static final String DATASOURCE_METADATA_IMPORT_ERROR_MSG = "Metadata cannot be imported for datasource - \" %s \" , either does not exist or is not valid";
public static final String DATASOURCE_METADATA_DELETE_EXCPTN = "Datasource metadata not found";
public static final String DATASOURCE_METADATA_DELETE_ERROR_MSG = "Metadata cannot be deleted for datasource - \" %s \" , either does not exist or is not valid";
public static final String INVALID_QUERY_PARAM = "The query param(s) - \" %s \" is/are invalid";
public static final String INVALID_QUERY_PARAM_VALUE = "The query param value(s) is/are invalid";
public static final String DATASOURCE_METADATA_VALIDATION_FAILURE_EXCPTN = "Invalid DataSourceMetadata object";
public static final String DATASOURCE_METADATA_MISSING_REQUEST_INPUT_EXCPTN = "Request input data cannot be null or empty";
}
}

Expand Down
Loading
Loading