--- /dev/null
+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 + '\'' +
+ '}';
+ }
+}
@JsonProperty("result")
private List<HealthCheckInstanceResponse> instanceResponse;
+ @JsonProperty("error")
+ private String errorMessage;
+
public List<HealthCheckInstanceResponse> getInstanceResponse() {
return instanceResponse;
}
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 + '\'' +
'}';
}
-
}
@JsonProperty("result")
private List<StatusCheckInstanceResponse> instanceResponse;
+ @JsonProperty("errorMessage")
+ private String errorMessage;
+
public List<StatusCheckInstanceResponse> getInstanceResponse() {
return instanceResponse;
}
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 + '\'' +
'}';
}
-
}
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;
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;
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;
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 {
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
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;
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)
--- /dev/null
+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;
+ }
+ }
+
+}
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;
@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
}
}
}
-
-
-
@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>();