diff options
4 files changed, 352 insertions, 46 deletions
diff --git a/main/src/main/java/org/onap/policy/pap/main/rest/PolicyComponentsHealthCheckControllerV1.java b/main/src/main/java/org/onap/policy/pap/main/rest/PolicyComponentsHealthCheckControllerV1.java index d608b41e..8dcc07ec 100644 --- a/main/src/main/java/org/onap/policy/pap/main/rest/PolicyComponentsHealthCheckControllerV1.java +++ b/main/src/main/java/org/onap/policy/pap/main/rest/PolicyComponentsHealthCheckControllerV1.java @@ -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(); } diff --git a/main/src/main/java/org/onap/policy/pap/main/rest/PolicyComponentsHealthCheckProvider.java b/main/src/main/java/org/onap/policy/pap/main/rest/PolicyComponentsHealthCheckProvider.java index ddcab778..2da354da 100644 --- a/main/src/main/java/org/onap/policy/pap/main/rest/PolicyComponentsHealthCheckProvider.java +++ b/main/src/main/java/org/onap/policy/pap/main/rest/PolicyComponentsHealthCheckProvider.java @@ -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) { diff --git a/main/src/test/java/org/onap/policy/pap/main/rest/TestPolicyComponentsHealthCheckControllerV1.java b/main/src/test/java/org/onap/policy/pap/main/rest/TestPolicyComponentsHealthCheckControllerV1.java index f73178b7..cf0da937 100644 --- a/main/src/test/java/org/onap/policy/pap/main/rest/TestPolicyComponentsHealthCheckControllerV1.java +++ b/main/src/test/java/org/onap/policy/pap/main/rest/TestPolicyComponentsHealthCheckControllerV1.java @@ -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. @@ -20,7 +20,23 @@ 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 index 00000000..2fdb2e6e --- /dev/null +++ b/main/src/test/java/org/onap/policy/pap/main/rest/TestPolicyComponentsHealthCheckProvider.java @@ -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 |