Add test cases for consolidated policy health check 03/102303/8
authorhuaxing <huaxing.jin@est.tech>
Tue, 25 Feb 2020 07:53:55 +0000 (15:53 +0800)
committerhuaxing <huaxing.jin@est.tech>
Fri, 28 Feb 2020 08:53:59 +0000 (16:53 +0800)
Issue-ID: POLICY-1689
Signed-off-by: huaxing <huaxing.jin@est.tech>
Change-Id: Ifda17a4486e1bce1ce0f523cc1984519b7fbc1aa

main/src/main/java/org/onap/policy/pap/main/rest/PolicyComponentsHealthCheckControllerV1.java
main/src/main/java/org/onap/policy/pap/main/rest/PolicyComponentsHealthCheckProvider.java
main/src/test/java/org/onap/policy/pap/main/rest/TestPolicyComponentsHealthCheckControllerV1.java
main/src/test/java/org/onap/policy/pap/main/rest/TestPolicyComponentsHealthCheckProvider.java [new file with mode: 0644]

index d608b41..8dcc07e 100644 (file)
@@ -37,6 +37,7 @@ import javax.ws.rs.Path;
 import javax.ws.rs.core.Response;
 import javax.ws.rs.core.Response.Status;
 import org.apache.commons.lang3.tuple.Pair;
+import org.onap.policy.common.endpoints.http.client.HttpClientConfigException;
 
 /**
  * Class to provide REST end point for PAP component to fetch all policy components, including PAP,
@@ -46,10 +47,19 @@ import org.apache.commons.lang3.tuple.Pair;
  */
 public class PolicyComponentsHealthCheckControllerV1 extends PapRestControllerV1 {
 
+    private PolicyComponentsHealthCheckProvider provider;
+
+    /**
+     * Constructs the object.
+     *
+     * @throws HttpClientConfigException if creating http client failed
+     */
+    public PolicyComponentsHealthCheckControllerV1() throws HttpClientConfigException {
+        provider = new PolicyComponentsHealthCheckProvider();
+    }
+
     /**
      * Returns health status of all Policy components, including PAP, API, Distribution, and PDPs.
-     * Note: a new provider {@link PolicyComponentsHealthCheckProvider} must be created for each
-     * request.
      *
      * @param requestId request ID used in ONAP logging
      * @return a response
@@ -82,7 +92,7 @@ public class PolicyComponentsHealthCheckControllerV1 extends PapRestControllerV1
     public Response policyComponentsHealthCheck(
             @HeaderParam(REQUEST_ID_NAME) @ApiParam(REQUEST_ID_PARAM_DESCRIPTION) final UUID requestId) {
         final Pair<Status, Map<String, Object>> pair =
-                new PolicyComponentsHealthCheckProvider().fetchPolicyComponentsHealthStatus();
+                provider.fetchPolicyComponentsHealthStatus();
         return addLoggingHeaders(addVersionControlHeaders(Response.status(pair.getLeft())), requestId)
                 .entity(pair.getRight()).build();
     }
index ddcab77..2da354d 100644 (file)
@@ -1,6 +1,6 @@
 /*-
  * ============LICENSE_START=======================================================
- *  Copyright (C) 2019 Nordix Foundation.
+ *  Copyright (C) 2019-2020 Nordix Foundation.
  * ================================================================================
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -52,9 +52,7 @@ import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 /**
- * Provider for PAP to fetch health status of all Policy components, including PAP, API,
- * Distribution, and PDPs. Note: a new provider {@link PolicyComponentsHealthCheckProvider} must be
- * created for each request.
+ * Provider for PAP to fetch health status of all Policy components, including PAP, API, Distribution, and PDPs.
  *
  * @author Yehui Wang (yehui.wang@est.tech)
  */
@@ -66,27 +64,65 @@ public class PolicyComponentsHealthCheckProvider {
     private static final Pattern IP_REPLACEMENT_PATTERN = Pattern.compile("//(\\S+):");
     private static final String POLICY_PAP_HEALTHCHECK_URI = "/policy/pap/v1/healthcheck";
     private PapParameterGroup papParameterGroup = ParameterService.get(PAP_GROUP_PARAMS_NAME);
-    private boolean isHealthy = true;
-    private Map<String, Object> result = new HashMap<>();
+    private List<HttpClient> clients = new ArrayList<>();
+
+    /**
+     * Constructs the object.
+     *
+     * @throws HttpClientConfigException if creating http client failed
+     */
+    public PolicyComponentsHealthCheckProvider() throws HttpClientConfigException {
+        this(HttpClientFactoryInstance.getClientFactory());
+    }
+
+    /**
+     * Constructs the object with provided http client factory.
+     *
+     * <p>This constructor is for unit test to use a mock {@link HttpClientFactory}.
+     *
+     * @param clientFactory factory used to construct http client
+     * @throws HttpClientConfigException if creating http client failed
+     */
+    PolicyComponentsHealthCheckProvider(HttpClientFactory clientFactory) throws HttpClientConfigException {
+        for (BusTopicParams params : papParameterGroup.getHealthCheckRestClientParameters()) {
+            params.setManaged(false);
+            clients.add(clientFactory.build(params));
+        }
+    }
 
     /**
      * Returns health status of all Policy components.
      *
      * @return a pair containing the status and the response
-     * @throws PfModelException in case of errors
      */
     public Pair<Status, Map<String, Object>> fetchPolicyComponentsHealthStatus() {
-        getHttpClients(papParameterGroup.getHealthCheckRestClientParameters()).parallelStream()
-                .forEach(this::fetchPolicyComponentHealthStatus);
+        boolean isHealthy = true;
+        Map<String, Object> result = new HashMap<>();
+
+        // Check remote components
+        for (HttpClient client : clients) {
+            HealthCheckReport report = fetchPolicyComponentHealthStatus(client);
+            if (!report.isHealthy()) {
+                isHealthy = false;
+            }
+            result.put(client.getName(), report);
+        }
+
+        // Check PAP itself
         HealthCheckReport papReport = new HealthCheckProvider().performHealthCheck();
         RestServerParameters restServerParameters = papParameterGroup.getRestServerParameters();
         papReport.setUrl((restServerParameters.isHttps() ? "https://" : "http://") + papReport.getUrl() + ":"
-                + restServerParameters.getPort() + POLICY_PAP_HEALTHCHECK_URI);
+            + restServerParameters.getPort() + POLICY_PAP_HEALTHCHECK_URI);
+        if (!papReport.isHealthy()) {
+            isHealthy = false;
+        }
         result.put(PapConstants.POLICY_PAP, papReport);
+
+        // Check PDPs, read status from DB
         try {
             Map<String, List<Pdp>> pdpListWithType = fetchPdpsHealthStatus();
             if (isHealthy && (pdpListWithType.isEmpty() || pdpListWithType.values().stream().flatMap(List::stream)
-                    .anyMatch(pdp -> !PdpHealthStatus.HEALTHY.equals(pdp.getHealthy())))) {
+                .anyMatch(pdp -> !PdpHealthStatus.HEALTHY.equals(pdp.getHealthy())))) {
                 isHealthy = false;
             }
             result.put(PapConstants.POLICY_PDPS, pdpListWithType);
@@ -94,6 +130,7 @@ public class PolicyComponentsHealthCheckProvider {
             result.put(PapConstants.POLICY_PDPS, exp.getErrorResponse());
             isHealthy = false;
         }
+
         result.put(HEALTH_STATUS, isHealthy);
         LOGGER.debug("Policy Components HealthCheck Response - {}", result);
         return Pair.of(Response.Status.OK, result);
@@ -102,7 +139,7 @@ public class PolicyComponentsHealthCheckProvider {
     private Map<String, List<Pdp>> fetchPdpsHealthStatus() throws PfModelException {
         Map<String, List<Pdp>> pdpListWithType = new HashMap<>();
         final PolicyModelsProviderFactoryWrapper modelProviderWrapper =
-                Registry.get(PapConstants.REG_PAP_DAO_FACTORY, PolicyModelsProviderFactoryWrapper.class);
+            Registry.get(PapConstants.REG_PAP_DAO_FACTORY, PolicyModelsProviderFactoryWrapper.class);
         try (PolicyModelsProvider databaseProvider = modelProviderWrapper.create()) {
             final List<PdpGroup> groups = databaseProvider.getPdpGroups(null);
             for (final PdpGroup group : groups) {
@@ -115,51 +152,34 @@ public class PolicyComponentsHealthCheckProvider {
         return pdpListWithType;
     }
 
-    private List<HttpClient> getHttpClients(List<BusTopicParams> restClientParameters) {
-        HttpClientFactory clientFactory = HttpClientFactoryInstance.getClientFactory();
-        for (BusTopicParams params : restClientParameters) {
-            try {
-                params.setManaged(true);
-                clientFactory.build(params);
-            } catch (HttpClientConfigException e) {
-                LOGGER.warn("{} httpClient creation error", params.getClientName());
-                String url = (params.isUseHttps() ? "https://" : "http://") + params.getHostname() + ":"
-                        + params.getPort() + "/" + params.getBasePath();
-                storeUnHealthCheckReport(params.getClientName(), url, HttpURLConnection.HTTP_BAD_REQUEST,
-                        e.getMessage());
-            }
-        }
-        return clientFactory.inventory();
-    }
-
-    private void fetchPolicyComponentHealthStatus(HttpClient httpClient) {
+    private HealthCheckReport fetchPolicyComponentHealthStatus(HttpClient httpClient) {
+        HealthCheckReport clientReport;
         try {
             Response resp = httpClient.get();
+            clientReport = replaceIpWithHostname(
+                resp.readEntity(HealthCheckReport.class), httpClient.getBaseUrl());
+
+            // A health report is read successfully when HTTP status is not OK, it is also not healthy
+            // even in the report it says healthy.
             if (resp.getStatus() != HttpURLConnection.HTTP_OK) {
-                isHealthy = false;
-                result.put(httpClient.getName(),
-                        replaceIpWithHostname(resp.readEntity(HealthCheckReport.class), httpClient.getBaseUrl()));
-            } else {
-                result.put(httpClient.getName(),
-                        replaceIpWithHostname(resp.readEntity(HealthCheckReport.class), httpClient.getBaseUrl()));
+                clientReport.setHealthy(false);
             }
         } catch (RuntimeException e) {
             LOGGER.warn("{} connection error", httpClient.getName());
-            storeUnHealthCheckReport(httpClient.getName(), httpClient.getBaseUrl(),
-                    HttpURLConnection.HTTP_INTERNAL_ERROR, e.getMessage());
+            clientReport = createUnHealthCheckReport(httpClient.getName(), httpClient.getBaseUrl(),
+                HttpURLConnection.HTTP_INTERNAL_ERROR, e.getMessage());
         }
+        return clientReport;
     }
 
-
-    private void storeUnHealthCheckReport(String name, String url, int code, String message) {
+    private HealthCheckReport createUnHealthCheckReport(String name, String url, int code, String message) {
         HealthCheckReport report = new HealthCheckReport();
         report.setName(name);
         report.setUrl(url);
         report.setHealthy(false);
-        isHealthy = false;
         report.setCode(code);
         report.setMessage(message);
-        result.put(name, report);
+        return report;
     }
 
     private HealthCheckReport replaceIpWithHostname(HealthCheckReport report, String baseUrl) {
index f73178b..cf0da93 100644 (file)
@@ -1,6 +1,6 @@
 /*-
  * ============LICENSE_START=======================================================
- *  Copyright (C) 2019 Nordix Foundation.
+ *  Copyright (C) 2019-2020 Nordix Foundation.
  * ================================================================================
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
 
 package org.onap.policy.pap.main.rest;
 
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import javax.ws.rs.client.Invocation;
+import javax.ws.rs.core.GenericType;
+import javax.ws.rs.core.Response;
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
 import org.junit.Test;
+import org.onap.policy.common.endpoints.event.comm.bus.internal.BusTopicParams;
+import org.onap.policy.common.parameters.ParameterService;
+import org.onap.policy.pap.main.parameters.PapParameterGroup;
+import org.powermock.reflect.Whitebox;
 
 /**
  * Class to perform unit test of {@link PolicyComponentsHealthCheckControllerV1}.
@@ -30,9 +46,45 @@ import org.junit.Test;
 public class TestPolicyComponentsHealthCheckControllerV1 extends CommonPapRestServer {
 
     private static final String ENDPOINT = "components/healthcheck";
+    private static List<BusTopicParams> savedBusTopicParams;
+
+    /**
+     * Set up for the test class.
+     */
+    @BeforeClass
+    public static void setUpClass() {
+        // To skip calling to the remote components
+        PapParameterGroup papParameterGroup = ParameterService.get("PapGroup");
+        List<BusTopicParams> lo = Whitebox.getInternalState(papParameterGroup,
+            "healthCheckRestClientParameters");
+        savedBusTopicParams = new ArrayList<>(lo);
+        lo.clear();
+    }
+
+    /**
+     * Tear down for the test class.
+     */
+    @AfterClass
+    public static void tearDownClass() {
+        PapParameterGroup papParameterGroup = ParameterService.get("PapGroup");
+        List<BusTopicParams> lo = Whitebox.getInternalState(papParameterGroup,
+            "healthCheckRestClientParameters");
+        lo.addAll(savedBusTopicParams);
+    }
 
     @Test
     public void testSwagger() throws Exception {
         super.testSwagger(ENDPOINT);
     }
+
+    @Test
+    public void testPolicyComponentsHealthCheck() throws Exception {
+        Invocation.Builder invocationBuilder = sendRequest(ENDPOINT);
+        Response response = invocationBuilder.get();
+        assertEquals(Response.Status.OK.getStatusCode(), response.getStatus());
+        Map<String, Object> result = new HashMap<>();
+        result = (Map<String, Object>) response.readEntity(GenericType.forInstance(result));
+        // No PDP configured, healthy is false
+        assertFalse((Boolean) result.get("healthy"));
+    }
 }
diff --git a/main/src/test/java/org/onap/policy/pap/main/rest/TestPolicyComponentsHealthCheckProvider.java b/main/src/test/java/org/onap/policy/pap/main/rest/TestPolicyComponentsHealthCheckProvider.java
new file mode 100644 (file)
index 0000000..2fdb2e6
--- /dev/null
@@ -0,0 +1,224 @@
+/*-
+ * ============LICENSE_START=======================================================
+ *  Copyright (C) 2020 Nordix Foundation.
+ * ================================================================================
+ * 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.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.policy.pap.main.rest;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.when;
+
+import java.io.File;
+import java.net.HttpURLConnection;
+import java.util.List;
+import java.util.Map;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.Response.Status;
+import org.apache.commons.lang3.tuple.Pair;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.onap.policy.common.endpoints.event.comm.bus.internal.BusTopicParams;
+import org.onap.policy.common.endpoints.http.client.HttpClient;
+import org.onap.policy.common.endpoints.http.client.HttpClientFactory;
+import org.onap.policy.common.endpoints.report.HealthCheckReport;
+import org.onap.policy.common.parameters.ParameterService;
+import org.onap.policy.common.utils.coder.Coder;
+import org.onap.policy.common.utils.coder.CoderException;
+import org.onap.policy.common.utils.coder.StandardCoder;
+import org.onap.policy.common.utils.resources.ResourceUtils;
+import org.onap.policy.common.utils.services.Registry;
+import org.onap.policy.models.pdp.concepts.Pdp;
+import org.onap.policy.models.pdp.concepts.PdpGroup;
+import org.onap.policy.models.pdp.concepts.PdpGroups;
+import org.onap.policy.models.pdp.enums.PdpHealthStatus;
+import org.onap.policy.models.provider.PolicyModelsProvider;
+import org.onap.policy.pap.main.PapConstants;
+import org.onap.policy.pap.main.PolicyModelsProviderFactoryWrapper;
+import org.onap.policy.pap.main.parameters.CommonTestData;
+import org.onap.policy.pap.main.parameters.PapParameterGroup;
+import org.onap.policy.pap.main.startstop.PapActivator;
+
+public class TestPolicyComponentsHealthCheckProvider {
+
+    private static final String CLIENT_1 = "client1";
+    private static final String PDP_GROUP_DATA_FILE = "rest/pdpGroup.json";
+    private static final String PAP_GROUP_PARAMS_NAME = "PapGroup";
+
+    @Mock
+    private PolicyModelsProvider dao;
+
+    @Mock
+    private PolicyModelsProviderFactoryWrapper daofact;
+
+    @Mock
+    private HttpClientFactory clientFactory;
+
+    @Mock
+    PapActivator papActivator;
+
+    @Mock
+    private HttpClient client1;
+
+    @Mock
+    private HttpClient client2;
+
+    @Mock
+    private Response response1;
+
+    @Mock
+    private Response response2;
+
+    private List<PdpGroup> groups;
+
+    private PapParameterGroup savedPapParameterGroup;
+
+    /**
+     * Configures mocks and objects.
+     *
+     * @throws Exception if an error occurs
+     */
+    @Before
+    public void setUp() throws Exception {
+        MockitoAnnotations.initMocks(this);
+
+        groups = loadPdpGroupsFromFile().getGroups();
+        when(dao.getPdpGroups(any())).thenReturn(groups);
+
+        when(daofact.create()).thenReturn(dao);
+        Registry.newRegistry();
+        Registry.register(PapConstants.REG_PAP_DAO_FACTORY, daofact);
+
+        when(papActivator.isAlive()).thenReturn(true);
+        Registry.register(PapConstants.REG_PAP_ACTIVATOR, papActivator);
+
+        if (ParameterService.contains(PAP_GROUP_PARAMS_NAME)) {
+            savedPapParameterGroup = ParameterService.get(PAP_GROUP_PARAMS_NAME);
+        }
+        CommonTestData testData = new CommonTestData();
+        ParameterService.register(testData.getPapParameterGroup(0), true);
+
+        when(client1.getName()).thenReturn(CLIENT_1);
+        when(client1.getBaseUrl()).thenReturn("url1");
+        when(response1.getStatus()).thenReturn(HttpURLConnection.HTTP_OK);
+        when(response1.readEntity(HealthCheckReport.class))
+            .thenReturn(createReport(HttpURLConnection.HTTP_OK, true));
+        when(client1.get()).thenReturn(response1);
+
+        when(client2.getName()).thenReturn("client2");
+        when(client2.getBaseUrl()).thenReturn("url2");
+        when(response2.getStatus()).thenReturn(HttpURLConnection.HTTP_OK);
+        when(response2.readEntity(HealthCheckReport.class))
+            .thenReturn(createReport(HttpURLConnection.HTTP_OK, true));
+        when(client2.get()).thenReturn(response2);
+
+        PapParameterGroup papParameterGroup =  ParameterService.get(PAP_GROUP_PARAMS_NAME);
+        List<BusTopicParams> params = papParameterGroup.getHealthCheckRestClientParameters();
+        when(clientFactory.build(params.get(0))).thenReturn(client1);
+        when(clientFactory.build(params.get(1))).thenReturn(client2);
+    }
+
+    /**
+     * Tear down.
+     */
+    @After
+    public void tearDown() {
+        if (savedPapParameterGroup != null) {
+            ParameterService.register(savedPapParameterGroup, true);
+        } else {
+            ParameterService.deregister(PAP_GROUP_PARAMS_NAME);
+        }
+    }
+
+    @Test
+    public void testFetchPolicyComponentsHealthStatus_allHealthy() throws Exception {
+        PolicyComponentsHealthCheckProvider provider = new PolicyComponentsHealthCheckProvider(clientFactory);
+        Pair<Status, Map<String, Object>> ret = provider.fetchPolicyComponentsHealthStatus();
+        assertEquals(ret.getLeft(), Response.Status.OK);
+        assertTrue((Boolean) ret.getRight().get("healthy"));
+    }
+
+    @Test
+    public void testFetchPolicyComponentsHealthStatus_unhealthyClient() throws Exception {
+        when(response1.getStatus()).thenReturn(HttpURLConnection.HTTP_INTERNAL_ERROR);
+        when(response1.readEntity(HealthCheckReport.class))
+            .thenReturn(createReport(HttpURLConnection.HTTP_INTERNAL_ERROR, false));
+        Map<String, Object> result = callFetchPolicyComponentsHealthStatus();
+        assertFalse((Boolean) result.get("healthy"));
+        HealthCheckReport report = (HealthCheckReport) result.get(CLIENT_1);
+        assertFalse(report.isHealthy());
+
+        when(response1.getStatus()).thenReturn(HttpURLConnection.HTTP_OK);
+        when(response1.readEntity(HealthCheckReport.class))
+            .thenReturn(createReport(HttpURLConnection.HTTP_OK, false));
+        Map<String, Object> result2 = callFetchPolicyComponentsHealthStatus();
+        assertFalse((Boolean) result2.get("healthy"));
+        HealthCheckReport report2 = (HealthCheckReport) result.get(CLIENT_1);
+        assertFalse(report.isHealthy());
+    }
+
+    @Test
+    public void testFetchPolicyComponentsHealthStatus_unhealthyPdps() throws Exception {
+        //Get a PDP and set it unhealthy
+        groups.get(0).getPdpSubgroups().get(0)
+            .getPdpInstances().get(0).setHealthy(PdpHealthStatus.NOT_HEALTHY);
+        Map<String, Object> result = callFetchPolicyComponentsHealthStatus();
+        Map<String, List<Pdp>> pdpListWithType = (Map<String, List<Pdp>>) result.get(PapConstants.POLICY_PDPS);
+        assertEquals(2, pdpListWithType.size());
+        assertFalse((Boolean) result.get("healthy"));
+    }
+
+    @Test
+    public void testFetchPolicyComponentsHealthStatus_unhealthyPap() throws Exception {
+        when(papActivator.isAlive()).thenReturn(false);
+        Map<String, Object> result = callFetchPolicyComponentsHealthStatus();
+        assertFalse((Boolean) result.get("healthy"));
+        HealthCheckReport report = (HealthCheckReport) result.get(PapConstants.POLICY_PAP);
+        assertFalse(report.isHealthy());
+    }
+
+    private Map<String, Object> callFetchPolicyComponentsHealthStatus() throws Exception {
+        PolicyComponentsHealthCheckProvider provider = new PolicyComponentsHealthCheckProvider(clientFactory);
+        return provider.fetchPolicyComponentsHealthStatus().getRight();
+    }
+
+    private HealthCheckReport createReport(int code, boolean healthy) {
+        HealthCheckReport report = new HealthCheckReport();
+        report.setName("name");
+        report.setUrl("url");
+        report.setCode(code);
+        report.setHealthy(healthy);
+        report.setMessage("message");
+        return report;
+    }
+
+    private static PdpGroups loadPdpGroupsFromFile() {
+        final File propFile = new File(ResourceUtils.getFilePath4Resource(PDP_GROUP_DATA_FILE));
+        try {
+            Coder coder = new StandardCoder();
+            return coder.decode(propFile, PdpGroups.class);
+        } catch (final CoderException e) {
+            throw new RuntimeException(e);
+        }
+    }
+}
\ No newline at end of file