aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorFiete Ostkamp <Fiete.Ostkamp@telekom.de>2022-10-12 09:23:14 +0200
committerFiete Ostkamp <Fiete.Ostkamp@telekom.de>2022-10-12 09:15:00 +0000
commit9adde5462ff9fd281eb5057dbb4c58e4676a9d90 (patch)
tree527e86981639b7d2599838a34f72e858479a1408
parent538ab0a28c329916c4c76c4574b4f7051d5093a8 (diff)
Enhancement of AAI-traversal healthcheck
- enhance the EchoResponse endpoint to check for db connectivity when the myAction parameter is provided Issue-ID: AAI-3547 Signed-off-by: Fiete Ostkamp <Fiete.Ostkamp@telekom.de> Change-Id: I4d5686b63efd139b942cee0c222305a17d2a2497
-rw-r--r--.gitignore3
-rw-r--r--aai-traversal/.classpath59
-rw-r--r--aai-traversal/src/main/java/org/onap/aai/rest/util/AaiGraphChecker.java103
-rw-r--r--aai-traversal/src/main/java/org/onap/aai/rest/util/EchoResponse.java161
-rw-r--r--aai-traversal/src/test/java/org/onap/aai/rest/util/AaiGraphCheckerTest.java43
-rw-r--r--aai-traversal/src/test/java/org/onap/aai/rest/util/EchoResponseTest.java50
6 files changed, 328 insertions, 91 deletions
diff --git a/.gitignore b/.gitignore
index f586926..5b775f4 100644
--- a/.gitignore
+++ b/.gitignore
@@ -13,3 +13,6 @@ bundleconfig-local/etc/logback.xml
*.iml
**/oxm/**
**/dbedgerules/**
+
+.devcontainer
+.vscode \ No newline at end of file
diff --git a/aai-traversal/.classpath b/aai-traversal/.classpath
index 31f5ed6..d9feb1e 100644
--- a/aai-traversal/.classpath
+++ b/aai-traversal/.classpath
@@ -3,7 +3,64 @@
<classpathentry kind="src" path="target/generated-sources/annotations">
<attributes>
<attribute name="optional" value="true"/>
+ <attribute name="maven.pomderived" value="true"/>
+ <attribute name="ignore_optional_problems" value="true"/>
+ <attribute name="m2e-apt" value="true"/>
</attributes>
</classpathentry>
- <classpathentry kind="output" path="bin"/>
+ <classpathentry kind="src" output="target/classes" path="src/main/java">
+ <attributes>
+ <attribute name="optional" value="true"/>
+ <attribute name="maven.pomderived" value="true"/>
+ </attributes>
+ </classpathentry>
+ <classpathentry kind="src" output="target/classes" path="target/generated-sources/antlr4">
+ <attributes>
+ <attribute name="optional" value="true"/>
+ <attribute name="maven.pomderived" value="true"/>
+ </attributes>
+ </classpathentry>
+ <classpathentry excluding="**" kind="src" output="target/classes" path="src/main/resources">
+ <attributes>
+ <attribute name="maven.pomderived" value="true"/>
+ </attributes>
+ </classpathentry>
+ <classpathentry excluding="**" kind="src" output="target/classes" path="src/main/docker">
+ <attributes>
+ <attribute name="maven.pomderived" value="true"/>
+ </attributes>
+ </classpathentry>
+ <classpathentry kind="src" output="target/test-classes" path="src/test/java">
+ <attributes>
+ <attribute name="optional" value="true"/>
+ <attribute name="maven.pomderived" value="true"/>
+ <attribute name="test" value="true"/>
+ </attributes>
+ </classpathentry>
+ <classpathentry excluding="**" kind="src" output="target/test-classes" path="src/test/resources">
+ <attributes>
+ <attribute name="maven.pomderived" value="true"/>
+ <attribute name="test" value="true"/>
+ </attributes>
+ </classpathentry>
+ <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8">
+ <attributes>
+ <attribute name="maven.pomderived" value="true"/>
+ </attributes>
+ </classpathentry>
+ <classpathentry kind="con" path="org.eclipse.m2e.MAVEN2_CLASSPATH_CONTAINER">
+ <attributes>
+ <attribute name="maven.pomderived" value="true"/>
+ </attributes>
+ </classpathentry>
+ <classpathentry kind="src" output="target/test-classes" path="target/generated-test-sources/test-annotations">
+ <attributes>
+ <attribute name="optional" value="true"/>
+ <attribute name="maven.pomderived" value="true"/>
+ <attribute name="ignore_optional_problems" value="true"/>
+ <attribute name="m2e-apt" value="true"/>
+ <attribute name="test" value="true"/>
+ </attributes>
+ </classpathentry>
+ <classpathentry kind="output" path="target/classes"/>
</classpath>
diff --git a/aai-traversal/src/main/java/org/onap/aai/rest/util/AaiGraphChecker.java b/aai-traversal/src/main/java/org/onap/aai/rest/util/AaiGraphChecker.java
new file mode 100644
index 0000000..11b412d
--- /dev/null
+++ b/aai-traversal/src/main/java/org/onap/aai/rest/util/AaiGraphChecker.java
@@ -0,0 +1,103 @@
+/*
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2022 Bell Canada
+ * Modification Copyright (C) 2022 Deutsche Telekom SA
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+package org.onap.aai.rest.util;
+
+import java.util.Iterator;
+
+import org.janusgraph.core.JanusGraphException;
+import org.janusgraph.core.JanusGraphTransaction;
+import org.onap.aai.dbmap.AAIGraph;
+import org.onap.aai.logging.ErrorLogHelper;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.config.ConfigurableBeanFactory;
+import org.springframework.context.annotation.Scope;
+import org.springframework.stereotype.Component;
+
+import com.google.common.collect.Iterators;
+
+/**
+ * Singleton class responsible to check that AAI service is able to connect to its back-end
+ * database.
+ */
+@Component
+@Scope(ConfigurableBeanFactory.SCOPE_SINGLETON)
+public class AaiGraphChecker {
+ private static final Logger LOGGER = LoggerFactory.getLogger(AaiGraphChecker.class);
+
+ private AaiGraphChecker() {
+ }
+
+ /**
+ * Checks whether a connection to the graph database can be made.
+ *
+ * @return
+ * <li>true, if database is available</li>
+ * <li>false, if database is NOT available</li>
+ */
+ public Boolean isAaiGraphDbAvailable() {
+ Boolean dbAvailable;
+ JanusGraphTransaction transaction = null;
+ try {
+ transaction = AAIGraph.getInstance().getGraph().newTransaction();
+ final Iterator<?> vertexIterator = transaction.query().limit(1).vertices().iterator();
+ if (LOGGER.isDebugEnabled()) {
+ LOGGER.debug("Number of vertices retrieved while checking db: {}",
+ Iterators.size(vertexIterator));
+ }
+ vertexIterator.hasNext();
+ LOGGER.debug("Database is available");
+ dbAvailable = Boolean.TRUE;
+ } catch (JanusGraphException e) {
+ String message = "Database is not available (after JanusGraph exception)";
+ ErrorLogHelper.logError("500", message + ": " + e.getMessage());
+ LOGGER.error(message, e);
+ dbAvailable = Boolean.FALSE;
+ } catch (Error e) {
+ // Following error occurs when aai resources is starting:
+ // - UnsatisfiedLinkError (for org.onap.aai.dbmap.AAIGraph$Helper instantiation)
+ // Following errors are raised when aai resources is starting and cassandra is not
+ // running:
+ // - ExceptionInInitializerError
+ // - NoClassDefFoundError (definition for org.onap.aai.dbmap.AAIGraph$Helper is not
+ // found)
+ String message = "Database is not available (after error)";
+ ErrorLogHelper.logError("500", message + ": " + e.getMessage());
+ LOGGER.error(message, e);
+ dbAvailable = Boolean.FALSE;
+ } catch (Exception e) {
+ String message = "Database availability can not be determined";
+ ErrorLogHelper.logError("500", message + ": " + e.getMessage());
+ LOGGER.error(message, e);
+ dbAvailable = null;
+ } finally {
+ if (transaction != null && !transaction.isClosed()) {
+ // check if transaction is open then closed instead of flag
+ try {
+ transaction.rollback();
+ } catch (Exception e) {
+ String message = "Exception occurred while closing transaction";
+ LOGGER.error(message, e);
+ ErrorLogHelper.logError("500", message + ": " + e.getMessage());
+ }
+ }
+ }
+ return dbAvailable;
+ }
+}
diff --git a/aai-traversal/src/main/java/org/onap/aai/rest/util/EchoResponse.java b/aai-traversal/src/main/java/org/onap/aai/rest/util/EchoResponse.java
index d0ba708..2e42e4c 100644
--- a/aai-traversal/src/main/java/org/onap/aai/rest/util/EchoResponse.java
+++ b/aai-traversal/src/main/java/org/onap/aai/rest/util/EchoResponse.java
@@ -8,7 +8,7 @@
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
- * http://www.apache.org/licenses/LICENSE-2.0
+ * http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
@@ -36,85 +36,96 @@ import javax.ws.rs.core.Response.Status;
import org.onap.aai.exceptions.AAIException;
import org.onap.aai.logging.ErrorLogHelper;
import org.onap.aai.restcore.RESTAPI;
+import org.springframework.stereotype.Component;
/**
* The Class EchoResponse.
*/
@Path("/util")
+@Component
public class EchoResponse extends RESTAPI {
- protected static String authPolicyFunctionName = "util";
-
- public static final String ECHO_PATH = "/echo";
-
- /**
- * Simple health-check API that echos back the X-FromAppId and X-TransactionId to clients.
- * If there is a query string, a transaction gets logged into hbase, proving the application is
- * connected to the data store.
- * If there is no query string, no transacction logging is done to hbase.
- *
- * @param headers the headers
- * @param req the req
- * @param myAction if exists will cause transaction to be logged to hbase
- * @return the response
- */
- @GET
- @Produces({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON})
- @Path(ECHO_PATH)
- public Response echoResult(@Context HttpHeaders headers, @Context HttpServletRequest req,
- @QueryParam("action") String myAction) {
- AAIException ex = null;
- Response response;
- String fromAppId;
- String transId;
-
- try {
- fromAppId = getFromAppId(headers);
- transId = getTransId(headers);
- } catch (AAIException e) {
- ArrayList<String> templateVars = new ArrayList<>();
- templateVars.add("PUT uebProvider");
- templateVars.add("addTopic");
- return Response
- .status(e.getErrorObject().getHTTPResponseCode()).entity(ErrorLogHelper
- .getRESTAPIErrorResponse(headers.getAcceptableMediaTypes(), e, templateVars))
- .build();
- }
-
- try {
-
- HashMap<AAIException, ArrayList<String>> exceptionList = new HashMap<>();
-
- ArrayList<String> templateVars = new ArrayList<>();
- templateVars.add(fromAppId);
- templateVars.add(transId);
-
- exceptionList.put(new AAIException("AAI_0002", "OK"), templateVars);
-
- response = Response
- .status(Status.OK).entity(ErrorLogHelper
- .getRESTAPIInfoResponse(headers.getAcceptableMediaTypes(), exceptionList))
- .build();
-
- } catch (Exception e) {
- ex = new AAIException("AAI_4000", e);
- ArrayList<String> templateVars = new ArrayList<>();
- templateVars.add(Action.GET.name());
- templateVars.add(fromAppId + " " + transId);
-
- response = Response
- .status(Status.INTERNAL_SERVER_ERROR).entity(ErrorLogHelper
- .getRESTAPIErrorResponse(headers.getAcceptableMediaTypes(), ex, templateVars))
- .build();
-
- } finally {
- if (ex != null) {
- ErrorLogHelper.logException(ex);
- }
-
- }
-
- return response;
- }
-
+ protected static String authPolicyFunctionName = "util";
+
+ public static final String ECHO_PATH = "/echo";
+
+ private AaiGraphChecker aaiGraphChecker;
+
+ public EchoResponse(AaiGraphChecker aaiGraphChecker) {
+ this.aaiGraphChecker = aaiGraphChecker;
+ }
+
+ /**
+ * Simple health-check API that echos back the X-FromAppId and X-TransactionId
+ * to clients.
+ * If there is a query string, the healthcheck will also check for database connectivity.
+ *
+ * @param headers the headers
+ * @param req the request
+ * @param myAction if exists will cause database connectivity check
+ * @return the response
+ */
+ @GET
+ @Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
+ @Path(ECHO_PATH)
+ public Response echoResult(@Context HttpHeaders headers, @Context HttpServletRequest req,
+ @QueryParam("action") String myAction) {
+
+ String fromAppId;
+ String transId;
+ ArrayList<String> templateVars = new ArrayList<>();
+
+ try {
+ fromAppId = getFromAppId(headers);
+ transId = getTransId(headers);
+ } catch (AAIException aaiException) {
+ templateVars.add("PUT uebProvider");
+ templateVars.add("addTopic");
+ ErrorLogHelper.logException(aaiException);
+ return generateFailureResponse(headers, templateVars, aaiException);
+ }
+
+ templateVars.add(fromAppId);
+ templateVars.add(transId);
+ if (myAction != null) {
+ try {
+ if (!aaiGraphChecker.isAaiGraphDbAvailable()) {
+ throw new AAIException("AAI_5105", "Error establishing a database connection");
+ }
+ return generateSuccessResponse(headers, templateVars);
+ } catch (AAIException aaiException) {
+ ErrorLogHelper.logException(aaiException);
+ return generateFailureResponse(headers, templateVars, aaiException);
+ } catch (Exception e) {
+ AAIException aaiException = new AAIException("AAI_4000", e);
+ ErrorLogHelper.logException(aaiException);
+ return generateFailureResponse(headers, templateVars, aaiException);
+ }
+ }
+ return generateSuccessResponse(headers, templateVars);
+ }
+
+ private Response generateSuccessResponse(HttpHeaders headers, ArrayList<String> templateVariables) {
+ HashMap<AAIException, ArrayList<String>> exceptionList = new HashMap<>();
+ exceptionList.put(new AAIException("AAI_0002", "OK"), templateVariables);
+ try {
+ return Response.status(Status.OK)
+ .entity(
+ ErrorLogHelper.getRESTAPIInfoResponse(headers.getAcceptableMediaTypes(), exceptionList))
+ .build();
+ } catch (Exception e) {
+ AAIException aaiException = new AAIException("AAI_4000", e);
+ ErrorLogHelper.logException(aaiException);
+ return generateFailureResponse(headers, templateVariables, aaiException);
+ }
+ }
+
+ private Response generateFailureResponse(HttpHeaders headers, ArrayList<String> templateVariables,
+ AAIException aaiException) {
+ return Response.status(aaiException.getErrorObject().getHTTPResponseCode())
+ .entity(
+ ErrorLogHelper.getRESTAPIErrorResponseWithLogging(
+ headers.getAcceptableMediaTypes(), aaiException, templateVariables))
+ .build();
+ }
}
diff --git a/aai-traversal/src/test/java/org/onap/aai/rest/util/AaiGraphCheckerTest.java b/aai-traversal/src/test/java/org/onap/aai/rest/util/AaiGraphCheckerTest.java
new file mode 100644
index 0000000..409c021
--- /dev/null
+++ b/aai-traversal/src/test/java/org/onap/aai/rest/util/AaiGraphCheckerTest.java
@@ -0,0 +1,43 @@
+/*
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2022 Bell Canada
+ * Modification Copyright (C) 2022 Deutsche Telekom SA
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.aai.rest.util;
+
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+import org.junit.Test;
+import org.onap.aai.AAISetup;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.test.context.ContextConfiguration;
+
+@ContextConfiguration(classes = {AaiGraphChecker.class})
+public class AaiGraphCheckerTest extends AAISetup {
+
+ @Autowired
+ private AaiGraphChecker graphChecker;
+
+ @Test
+ public void testIsAaiGraphDbAvailable() {
+ Boolean result = graphChecker.isAaiGraphDbAvailable();
+
+ assertNotNull(result);
+ assertTrue(result);
+ }
+}
diff --git a/aai-traversal/src/test/java/org/onap/aai/rest/util/EchoResponseTest.java b/aai-traversal/src/test/java/org/onap/aai/rest/util/EchoResponseTest.java
index 97702fa..eb67799 100644
--- a/aai-traversal/src/test/java/org/onap/aai/rest/util/EchoResponseTest.java
+++ b/aai-traversal/src/test/java/org/onap/aai/rest/util/EchoResponseTest.java
@@ -8,7 +8,7 @@
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
- * http://www.apache.org/licenses/LICENSE-2.0
+ * http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
@@ -21,7 +21,7 @@ package org.onap.aai.rest.util;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
-import static org.mockito.ArgumentMatchers.anyObject;
+import static org.mockito.ArgumentMatchers.anyObject;import static org.mockito.ArgumentMatchers.anyObject;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
@@ -59,6 +59,8 @@ public class EchoResponseTest {
private EchoResponse echoResponse;
+ private final AaiGraphChecker aaiGraphCheckerMock = mock(AaiGraphChecker.class);
+
private HttpHeaders httpHeaders;
private UriInfo uriInfo;
@@ -73,15 +75,15 @@ public class EchoResponseTest {
private static final Logger logger = LoggerFactory.getLogger(EchoResponseTest.class.getName());
@Before
- public void setup() {
+ public void setup(){
logger.info("Starting the setup for the integration tests of Rest Endpoints");
- echoResponse = new EchoResponse();
- httpHeaders = mock(HttpHeaders.class);
- uriInfo = mock(UriInfo.class);
+ echoResponse = new EchoResponse(aaiGraphCheckerMock);
+ httpHeaders = mock(HttpHeaders.class);
+ uriInfo = mock(UriInfo.class);
- headersMultiMap = new MultivaluedHashMap<>();
- queryParameters = Mockito.spy(new MultivaluedHashMap<>());
+ headersMultiMap = new MultivaluedHashMap<>();
+ queryParameters = Mockito.spy(new MultivaluedHashMap<>());
headersMultiMap.add("X-FromAppId", "JUNIT");
headersMultiMap.add("X-TransactionId", UUID.randomUUID().toString());
@@ -102,11 +104,11 @@ public class EchoResponseTest {
when(httpHeaders.getRequestHeader("aai-request-context")).thenReturn(aaiRequestContextList);
+
when(uriInfo.getQueryParameters()).thenReturn(queryParameters);
when(uriInfo.getQueryParameters(false)).thenReturn(queryParameters);
- // TODO - Check if this is valid since RemoveDME2QueryParameters seems to be very
- // unreasonable
+ // TODO - Check if this is valid since RemoveDME2QueryParameters seems to be very unreasonable
Mockito.doReturn(null).when(queryParameters).remove(anyObject());
when(httpHeaders.getMediaType()).thenReturn(APPLICATION_JSON);
@@ -115,17 +117,35 @@ public class EchoResponseTest {
@Test
public void testEchoResultWhenValidHeaders() throws Exception {
- Response response = echoResponse.echoResult(httpHeaders, null, "");
+ Response response = echoResponse.echoResult(httpHeaders, null, null);
assertNotNull(response);
assertEquals(Response.Status.OK.getStatusCode(), response.getStatus());
}
@Test
+ public void testEchoResultWhenActionIsProvidedDbAvailable() throws Exception {
+ when(aaiGraphCheckerMock.isAaiGraphDbAvailable()).thenReturn(true);
+ Response response = echoResponse.echoResult(httpHeaders, null, "myAction");
+
+ assertNotNull(response);
+ assertEquals(Response.Status.OK.getStatusCode(), response.getStatus());
+ }
+
+ @Test
+ public void testEchoResultWhenActionIsProvidedDbNotAvailable() throws Exception {
+ when(aaiGraphCheckerMock.isAaiGraphDbAvailable()).thenReturn(false);
+ Response response = echoResponse.echoResult(httpHeaders, null, "myAction");
+
+ assertNotNull(response);
+ assertEquals(Response.Status.INTERNAL_SERVER_ERROR.getStatusCode(), response.getStatus());
+ }
+
+ @Test
public void testEchoResultWhenInValidHeadersThrowsBadRequest() throws Exception {
httpHeaders = mock(HttpHeaders.class);
- Response response = echoResponse.echoResult(httpHeaders, null, "");
+ Response response = echoResponse.echoResult(httpHeaders, null, null);
assertNotNull(response);
assertEquals(Response.Status.BAD_REQUEST.getStatusCode(), response.getStatus());
@@ -135,11 +155,11 @@ public class EchoResponseTest {
public void testEchoResultWhenValidHeadersButMediaTypeWrong() throws Exception {
when(httpHeaders.getAcceptableMediaTypes()).thenThrow(new IllegalStateException())
- .thenReturn(outputMediaTypes);
+ .thenReturn(outputMediaTypes);
- Response response = echoResponse.echoResult(httpHeaders, null, "");
+ Response response = echoResponse.echoResult(httpHeaders, null, null);
assertNotNull(response);
assertEquals(Response.Status.INTERNAL_SERVER_ERROR.getStatusCode(), response.getStatus());
}
-}
+} \ No newline at end of file