Expose CNF health check api 88/122488/19
authorGrzegorz Wielgosinski <g.wielgosins@samsung.com>
Wed, 7 Jul 2021 11:29:34 +0000 (13:29 +0200)
committerGrzegorz Wielgosinski <g.wielgosins@samsung.com>
Mon, 23 Aug 2021 11:00:26 +0000 (13:00 +0200)
Issue-ID: SO-3690

Signed-off-by: Grzegorz Wielgosinski <g.wielgosins@samsung.com>
Change-Id: If4f60efb27bd057f562e8f8c01dd01c9e769b393

so-cnf-adapter-application/src/main/java/org/onap/so/adapters/cnf/model/healthcheck/HealthCheckInstance.java [new file with mode: 0644]
so-cnf-adapter-application/src/main/java/org/onap/so/adapters/cnf/model/healthcheck/HealthCheckResponse.java
so-cnf-adapter-application/src/main/java/org/onap/so/adapters/cnf/model/statuscheck/StatusCheckResponse.java
so-cnf-adapter-application/src/main/java/org/onap/so/adapters/cnf/rest/CnfAdapterRest.java
so-cnf-adapter-application/src/main/java/org/onap/so/adapters/cnf/service/CnfAdapterService.java
so-cnf-adapter-application/src/main/java/org/onap/so/adapters/cnf/service/healthcheck/HealthCheckService.java [new file with mode: 0644]
so-cnf-adapter-application/src/test/java/org/onap/so/adapters/cnf/rest/CnfAdapterRestTest.java
so-cnf-adapter-application/src/test/java/org/onap/so/adapters/cnf/service/CnfAdapterServiceTest.java

diff --git a/so-cnf-adapter-application/src/main/java/org/onap/so/adapters/cnf/model/healthcheck/HealthCheckInstance.java b/so-cnf-adapter-application/src/main/java/org/onap/so/adapters/cnf/model/healthcheck/HealthCheckInstance.java
new file mode 100644 (file)
index 0000000..df87768
--- /dev/null
@@ -0,0 +1,27 @@
+package org.onap.so.adapters.cnf.model.healthcheck;
+
+public class HealthCheckInstance {
+    private final String instanceId;
+    private final String healthCheckInstance;
+
+    public HealthCheckInstance(String instanceId, String healthCheckInstance) {
+        this.instanceId = instanceId;
+        this.healthCheckInstance = healthCheckInstance;
+    }
+
+    public String getInstanceId() {
+        return instanceId;
+    }
+
+    public String getHealthCheckInstance() {
+        return healthCheckInstance;
+    }
+
+    @Override
+    public String toString() {
+        return "HealthCheckInstance{" +
+                "instanceId='" + instanceId + '\'' +
+                ", healthCheckInstance='" + healthCheckInstance + '\'' +
+                '}';
+    }
+}
index aabd490..2d0bb88 100644 (file)
@@ -13,6 +13,9 @@ public class HealthCheckResponse {
     @JsonProperty("result")
     private List<HealthCheckInstanceResponse> instanceResponse;
 
+    @JsonProperty("error")
+    private String errorMessage;
+
     public List<HealthCheckInstanceResponse> getInstanceResponse() {
         return instanceResponse;
     }
@@ -21,11 +24,19 @@ public class HealthCheckResponse {
         this.instanceResponse = instanceResponse;
     }
 
+    public String getErrorMessage() {
+        return errorMessage;
+    }
+
+    public void setErrorMessage(String errorMessage) {
+        this.errorMessage = errorMessage;
+    }
+
     @Override
     public String toString() {
-        return "StatusCheckResponse{" +
+        return "HealthCheckResponse{" +
                 "instanceResponse=" + instanceResponse +
+                ", errorMessage='" + errorMessage + '\'' +
                 '}';
     }
-
 }
index 36c3068..86f4812 100644 (file)
@@ -13,6 +13,9 @@ public class StatusCheckResponse {
     @JsonProperty("result")
     private List<StatusCheckInstanceResponse> instanceResponse;
 
+    @JsonProperty("errorMessage")
+    private String errorMessage;
+
     public List<StatusCheckInstanceResponse> getInstanceResponse() {
         return instanceResponse;
     }
@@ -21,11 +24,19 @@ public class StatusCheckResponse {
         this.instanceResponse = instanceResponse;
     }
 
+    public String getErrorMessage() {
+        return errorMessage;
+    }
+
+    public void setErrorMessage(String errorMessage) {
+        this.errorMessage = errorMessage;
+    }
+
     @Override
     public String toString() {
         return "StatusCheckResponse{" +
                 "instanceResponse=" + instanceResponse +
+                ", errorMessage='" + errorMessage + '\'' +
                 '}';
     }
-
 }
index 2036f46..aab138e 100644 (file)
 
 package org.onap.so.adapters.cnf.rest;
 
-import java.io.File;
-import java.io.IOException;
+import com.fasterxml.jackson.core.JsonParseException;
+import com.fasterxml.jackson.databind.JsonMappingException;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.SerializationFeature;
 import org.apache.http.HttpEntity;
 import org.apache.http.client.methods.CloseableHttpResponse;
 import org.apache.http.client.methods.HttpDelete;
@@ -37,7 +39,9 @@ import org.apache.http.impl.client.CloseableHttpClient;
 import org.apache.http.impl.client.HttpClients;
 import org.apache.http.util.EntityUtils;
 import org.onap.so.adapters.cnf.MulticloudConfiguration;
+import org.onap.so.adapters.cnf.client.SoCallbackClient;
 import org.onap.so.adapters.cnf.model.BpmnInstanceRequest;
+import org.onap.so.adapters.cnf.model.CheckInstanceRequest;
 import org.onap.so.adapters.cnf.model.ConfigTemplateEntity;
 import org.onap.so.adapters.cnf.model.ConfigurationEntity;
 import org.onap.so.adapters.cnf.model.ConfigurationRollbackEntity;
@@ -45,10 +49,13 @@ import org.onap.so.adapters.cnf.model.ConnectivityInfo;
 import org.onap.so.adapters.cnf.model.ProfileEntity;
 import org.onap.so.adapters.cnf.model.ResourceBundleEntity;
 import org.onap.so.adapters.cnf.model.Tag;
+import org.onap.so.adapters.cnf.model.healthcheck.HealthCheckResponse;
+import org.onap.so.adapters.cnf.model.statuscheck.StatusCheckResponse;
 import org.onap.so.adapters.cnf.service.CnfAdapterService;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.http.ResponseEntity;
 import org.springframework.web.bind.annotation.PathVariable;
 import org.springframework.web.bind.annotation.RequestBody;
 import org.springframework.web.bind.annotation.RequestMapping;
@@ -56,11 +63,12 @@ import org.springframework.web.bind.annotation.RequestMethod;
 import org.springframework.web.bind.annotation.RequestParam;
 import org.springframework.web.bind.annotation.ResponseBody;
 import org.springframework.web.bind.annotation.RestController;
+import org.springframework.web.context.request.async.DeferredResult;
 import org.springframework.web.multipart.MultipartFile;
-import com.fasterxml.jackson.core.JsonParseException;
-import com.fasterxml.jackson.databind.JsonMappingException;
-import com.fasterxml.jackson.databind.ObjectMapper;
-import com.fasterxml.jackson.databind.SerializationFeature;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.concurrent.ForkJoinPool;
 
 @RestController
 public class CnfAdapterRest {
@@ -68,22 +76,41 @@ public class CnfAdapterRest {
     private static final Logger logger = LoggerFactory.getLogger(CnfAdapterRest.class);
     private final CloseableHttpClient httpClient = HttpClients.createDefault();
     private final CnfAdapterService cnfAdapterService;
+    private final SoCallbackClient callbackClient;
     private final String uri;
 
     @Autowired
-    public CnfAdapterRest(CnfAdapterService cnfAdapterService, MulticloudConfiguration multicloudConfiguration) {
+    public CnfAdapterRest(CnfAdapterService cnfAdapterService,
+                          SoCallbackClient callbackClient,
+                          MulticloudConfiguration multicloudConfiguration) {
         this.cnfAdapterService = cnfAdapterService;
+        this.callbackClient = callbackClient;
         this.uri = multicloudConfiguration.getMulticloudUrl();
     }
 
     @ResponseBody
-    @RequestMapping(value = {"/api/cnf-adapter/v1/healthcheck"}, method = RequestMethod.GET,
+    @RequestMapping(value = {"/api/cnf-adapter/v1/healthcheck"}, method = RequestMethod.POST,
             produces = "application/json")
-    public String healthCheck() throws Exception {
-
+    public DeferredResult<ResponseEntity> healthCheck(@RequestBody CheckInstanceRequest healthCheckRequest) {
         logger.info("healthCheck called.");
-        return cnfAdapterService.healthCheck();
-
+        DeferredResult<ResponseEntity> response = new DeferredResult<>();
+
+        ForkJoinPool.commonPool().submit(() -> {
+            logger.info("Processing healthCheck service");
+            HealthCheckResponse healthCheckResponse = null;
+            try {
+                healthCheckResponse = cnfAdapterService.healthCheck(healthCheckRequest);
+            } catch (Exception e) {
+                HealthCheckResponse errorHealthCheck = new HealthCheckResponse();
+                errorHealthCheck.setErrorMessage(e.getMessage());
+                callbackClient.sendPostCallback(healthCheckRequest.getCallbackUrl(), errorHealthCheck);
+                return;
+            }
+            callbackClient.sendPostCallback(healthCheckRequest.getCallbackUrl(), healthCheckResponse);
+        });
+
+        response.setResult(ResponseEntity.accepted().build());
+        return response;
     }
 
     @ResponseBody
index faef1d0..2008280 100644 (file)
@@ -28,7 +28,10 @@ import javax.ws.rs.core.UriBuilder;
 import org.apache.http.HttpStatus;
 import org.onap.so.adapters.cnf.MulticloudConfiguration;
 import org.onap.so.adapters.cnf.model.BpmnInstanceRequest;
+import org.onap.so.adapters.cnf.model.CheckInstanceRequest;
 import org.onap.so.adapters.cnf.model.MulticloudInstanceRequest;
+import org.onap.so.adapters.cnf.model.healthcheck.HealthCheckResponse;
+import org.onap.so.adapters.cnf.service.healthcheck.HealthCheckService;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.annotation.Autowired;
@@ -48,43 +51,23 @@ import com.fasterxml.jackson.databind.JsonMappingException;
 public class CnfAdapterService {
     private static final Logger logger = LoggerFactory.getLogger(CnfAdapterService.class);
     private static final String INSTANCE_CREATE_PATH = "/v1/instance";
-    private static final String HEALTH_CHECK = "/v1/healthcheck";
 
     private final RestTemplate restTemplate;
+    private HealthCheckService healthCheckService;
     private final String uri;
 
     @Autowired
     public CnfAdapterService(RestTemplate restTemplate,
+                             HealthCheckService healthCheckService,
                              MulticloudConfiguration multicloudConfiguration) {
         this.restTemplate = restTemplate;
+        this.healthCheckService = healthCheckService;
         this.uri = multicloudConfiguration.getMulticloudUrl();
     }
 
-    public String healthCheck() {
-
+    public HealthCheckResponse healthCheck(CheckInstanceRequest healthCheckRequest) throws Exception {
         logger.info("CnfAdapterService healthCheck called");
-        ResponseEntity<String> result = null;
-        try {
-
-            // String uri = env.getRequiredProperty("multicloud.endpoint"); //TODO:
-            // This needs to be added as well
-            // for configuration
-            String endpoint = UriBuilder.fromUri(uri).path(HEALTH_CHECK).build().toString();
-            HttpEntity<?> requestEntity = new HttpEntity<>(getHttpHeaders());
-            logger.info("request: " + requestEntity);
-            result = restTemplate.exchange(endpoint, HttpMethod.GET, requestEntity, String.class);
-            logger.info("response: " + result);
-            return result.getBody();
-        } catch (HttpClientErrorException e) {
-            logger.error("Error Calling Multicloud, e");
-            if (HttpStatus.SC_NOT_FOUND == e.getStatusCode().value()) {
-                throw new EntityNotFoundException(e.getResponseBodyAsString());
-            }
-            throw e;
-        } catch (HttpStatusCodeException e) {
-            logger.error("Error in Multicloud", e);
-            throw e;
-        }
+        return healthCheckService.healthCheck(healthCheckRequest);
     }
 
     public String createInstance(BpmnInstanceRequest bpmnInstanceRequest)
diff --git a/so-cnf-adapter-application/src/main/java/org/onap/so/adapters/cnf/service/healthcheck/HealthCheckService.java b/so-cnf-adapter-application/src/main/java/org/onap/so/adapters/cnf/service/healthcheck/HealthCheckService.java
new file mode 100644 (file)
index 0000000..2f91be8
--- /dev/null
@@ -0,0 +1,147 @@
+package org.onap.so.adapters.cnf.service.healthcheck;
+
+import com.google.common.util.concurrent.ThreadFactoryBuilder;
+import org.onap.so.adapters.cnf.client.MulticloudClient;
+import org.onap.so.adapters.cnf.model.CheckInstanceRequest;
+import org.onap.so.adapters.cnf.model.InstanceRequest;
+import org.onap.so.adapters.cnf.model.healthcheck.HealthCheckInstance;
+import org.onap.so.adapters.cnf.model.healthcheck.HealthCheckInstanceResponse;
+import org.onap.so.adapters.cnf.model.healthcheck.HealthCheckResponse;
+import org.onap.so.adapters.cnf.model.healthcheck.K8sRbInstanceHealthCheck;
+import org.onap.so.adapters.cnf.model.healthcheck.K8sRbInstanceHealthCheckSimple;
+import org.onap.so.adapters.cnf.service.CnfAdapterService;
+import org.onap.so.client.exception.BadResponseException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.Callable;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.ThreadFactory;
+import java.util.stream.Collectors;
+
+import static java.lang.Thread.sleep;
+import static java.util.concurrent.Executors.newFixedThreadPool;
+
+@Service
+public class HealthCheckService {
+
+    private static final Logger log = LoggerFactory.getLogger(CnfAdapterService.class);
+
+    private final MulticloudClient instanceApi;
+
+    @Autowired
+    public HealthCheckService(MulticloudClient multicloudClient) {
+        this.instanceApi = multicloudClient;
+    }
+
+    public HealthCheckResponse healthCheck(CheckInstanceRequest healthCheckRequest) throws Exception {
+        log.info("Health check - START");
+        List<HealthCheckInstance> instanceHealthCheckList = startInstanceHealthCheck(healthCheckRequest);
+        HealthCheckResponse statuses = getStatuses(instanceHealthCheckList);
+        log.info("Health check - END");
+
+        return statuses;
+    }
+
+    private List<HealthCheckInstance> startInstanceHealthCheck(CheckInstanceRequest healthCheckRequest) throws Exception {
+        log.debug("startInstanceHealthCheck - START");
+        List<HealthCheckInstance> healthCheckInstanceList = new ArrayList<>();
+
+        for (InstanceRequest instance : healthCheckRequest.getInstances()) {
+            String instanceId = instance.getInstanceId();
+            K8sRbInstanceHealthCheckSimple response = instanceApi.startInstanceHealthCheck(instanceId);
+            log.info("K8sRbInstanceHealthCheckSimple: {}", response);
+            healthCheckInstanceList.add(new HealthCheckInstance(instanceId, response.getId()));
+        }
+
+        log.info("healthCheckInstanceList: {}", healthCheckInstanceList);
+        log.debug("startInstanceHealthCheck - END");
+        return healthCheckInstanceList;
+    }
+
+    private HealthCheckResponse getStatuses(List<HealthCheckInstance> instanceHealthCheckList) throws Exception {
+        log.debug("getStatuses - START");
+        List<HealthCheckThread> threads = instanceHealthCheckList.stream()
+                .map(HealthCheckThread::new)
+                .collect(Collectors.toList());
+
+        int processors = Runtime.getRuntime().availableProcessors();
+        ThreadFactory threadFactory = new ThreadFactoryBuilder().setNameFormat("Health-check-thread-%d").build();
+        ExecutorService executorService = newFixedThreadPool(processors, threadFactory);
+        HealthCheckResponse response = new HealthCheckResponse();
+        List<HealthCheckInstanceResponse> healthCheckInstance = null;
+        healthCheckInstance = executorService.invokeAll(threads).stream()
+            .map(future -> {
+                try {
+                    InstanceStatusTuple instanceStatusTuple = future.get();
+                    String instanceId = instanceStatusTuple.getInstanceId();
+                    String status = instanceStatusTuple.getStatus();
+                    String reason = null;
+                    return new HealthCheckInstanceResponse(instanceId, reason, status);
+                } catch (Exception e) {
+                    throw new RuntimeException(e);
+                }
+            })
+            .collect(Collectors.toList());
+        response.setInstanceResponse(healthCheckInstance);
+        log.info("Get statuses response: \n {}", response);
+        log.debug("getStatuses - END");
+        return response;
+    }
+
+    private class HealthCheckThread implements Callable<InstanceStatusTuple> {
+
+        private final HealthCheckInstance healthCheckInstance;
+
+        HealthCheckThread(HealthCheckInstance healthCheckInstance) {
+            this.healthCheckInstance = healthCheckInstance;
+        }
+
+        /**
+         * Approx 5 minutes thread
+         * If timeout method returns tuple with HeatStackId => Timeout
+         * @return InstanceStatusTuple
+         * @throws InterruptedException
+         * @throws BadResponseException
+         */
+        @Override
+        public InstanceStatusTuple call() throws InterruptedException, BadResponseException {
+            log.info("{} started for: {}", Thread.currentThread().getName(), healthCheckInstance);
+            for (int retry = 0; retry < 30; retry++) {
+                K8sRbInstanceHealthCheck response = instanceApi.getInstanceHealthCheck(healthCheckInstance.getInstanceId(), healthCheckInstance.getHealthCheckInstance());
+                log.debug("Response for instanceId={}: {}", healthCheckInstance, response);
+                String status = response.getStatus();
+                if (!"RUNNING".equals(status.toUpperCase())) {
+                    log.info("Poll status: {} for {}", status, healthCheckInstance);
+                    instanceApi.deleteInstanceHealthCheck(healthCheckInstance.getInstanceId(), healthCheckInstance.getHealthCheckInstance());
+                    return new InstanceStatusTuple(healthCheckInstance.getInstanceId(), status);
+                }
+                sleep(10_000L);
+            }
+            return new InstanceStatusTuple(healthCheckInstance.getInstanceId(), "Timeout");
+        }
+    }
+
+    private class InstanceStatusTuple {
+        private final String instanceId;
+        private final String status;
+
+        InstanceStatusTuple(String instanceId, String status) {
+            this.instanceId = instanceId;
+            this.status = status;
+        }
+
+        String getInstanceId() {
+            return instanceId;
+        }
+
+        String getStatus() {
+            return status;
+        }
+    }
+
+}
index 979f13b..c85031a 100644 (file)
@@ -28,10 +28,12 @@ import org.mockito.Mock;
 import org.mockito.Mockito;
 import org.onap.so.adapters.cnf.MulticloudConfiguration;
 import org.onap.so.adapters.cnf.model.*;
+import org.onap.so.adapters.cnf.model.healthcheck.HealthCheckResponse;
 import org.onap.so.adapters.cnf.service.CnfAdapterService;
 import org.springframework.http.HttpStatus;
 import org.springframework.http.ResponseEntity;
 import org.springframework.test.context.junit4.SpringRunner;
+import org.springframework.web.context.request.async.DeferredResult;
 import org.springframework.web.multipart.MultipartFile;
 import java.util.ArrayList;
 import java.util.HashMap;
@@ -61,13 +63,16 @@ public class CnfAdapterRestTest {
 
     @Test
     public void healthCheckTest() throws Exception {
-
-        ResponseEntity<String> response = new ResponseEntity<String>(HttpStatus.OK);
+        HealthCheckResponse response = new HealthCheckResponse();
+        DeferredResult<HealthCheckResponse> deferredResponse = new DeferredResult<>();
+        deferredResponse.setResult(response);
         CnfAdapterService cnfAdapterService = Mockito.mock(CnfAdapterService.class);
-        Mockito.when(cnfAdapterService.healthCheck()).thenReturn(String.valueOf(response));
-        cnfAdapterRest.healthCheck();
+        CheckInstanceRequest healthCheckRequest = Mockito.mock(CheckInstanceRequest.class);
+        Mockito.when(cnfAdapterService.healthCheck(healthCheckRequest)).thenReturn(response);
+
+        cnfAdapterRest.healthCheck(healthCheckRequest);
+
         Assert.assertNotNull(response);
-        assertEquals(HttpStatus.OK, response.getStatusCode());
     }
 
     @Test
@@ -573,6 +578,3 @@ public class CnfAdapterRestTest {
         }
     }
 }
-
-
-
index bdad347..8e8cd5c 100644 (file)
@@ -48,16 +48,7 @@ public class CnfAdapterServiceTest {
     @Mock
     ResponseEntity<String> instanceResponse;
 
-    @Test
-    public void healthCheckTest() throws Exception {
-        try {
-            cnfAdapterService.healthCheck();
-        }
-        catch (Exception exp) {
-          assert(true);
-        }
-
-    }
+    
     @Test
     public void createInstanceTest() throws Exception {
         Map<String, String> labels = new HashMap<String, String>();