UrlsController/src/main/java/eu/openaire/urls_controller/services/ShutdownServiceImpl.java

91 lines
5.1 KiB
Java

package eu.openaire.urls_controller.services;
import eu.openaire.urls_controller.controllers.UrlsController;
import eu.openaire.urls_controller.models.WorkerInfo;
import eu.openaire.urls_controller.util.UriBuilder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import org.springframework.web.client.HttpServerErrorException;
import org.springframework.web.client.RestTemplate;
import java.net.ConnectException;
import java.util.regex.Pattern;
@Service
public class ShutdownServiceImpl implements ShutdownService {
private static final Logger logger = LoggerFactory.getLogger(ShutdownServiceImpl.class);
// Private Addresses, according to RFC 1918: https://www.rfc-editor.org/rfc/rfc1918
private static final Pattern PRIVATE_IP_ADDRESSES_RFC_1918 = Pattern.compile("(?:10.|172.(?:1[6-9]|2[0-9]|3[0-1])|192.168.)[0-9.]+");
@Value("${services.pdfaggregation.worker.port}")
private String workerPort;
public ResponseEntity<?> passSecurityChecks(String remoteAddr, String initMsg)
{
// In case the Controller is running inside a docker container, and we want to send the "shutdownServiceRequest" from the terminal (with curl), without entering inside the container,
// then the request will appear coming from a local (private) IP, instead of localhost.
if ( ! (remoteAddr.equals("127.0.0.1") || remoteAddr.equals(UriBuilder.ip) || PRIVATE_IP_ADDRESSES_RFC_1918.matcher(remoteAddr).matches()) ) {
logger.error(initMsg + "The request came from another IP: " + remoteAddr + " | while the Controller has the IP: " + UriBuilder.ip);
return ResponseEntity.status(HttpStatus.FORBIDDEN).build();
}
return null; // The checks are passing.
}
public void postShutdownOrCancelRequestsToAllWorkers(boolean shouldCancel)
{
// Send "shutdownWorker" requests to all active Workers.
for ( String workerId : UrlsController.workersInfoMap.keySet() ) {
WorkerInfo workerInfo = UrlsController.workersInfoMap.get(workerId);
if ( ! workerInfo.getHasShutdown() ) // A worker may have shutdown on its own (by sending it a shutDown request manually), so it will have told the Controller when it shut down. In case of a Worker-crash, the Controller will not know about it.
postShutdownOrCancelRequestToWorker(workerId, workerInfo.getWorkerIP(), shouldCancel);
else
logger.warn("Will not post " + (shouldCancel ? "Cancel-" : "") + "ShutdownRequest to Worker \"" + workerId + "\", since is it has already shutdown.");
}
}
private static final RestTemplate restTemplate = new RestTemplate();
public boolean postShutdownOrCancelRequestToWorker(String workerId, String workerIp, boolean shouldCancel)
{
String url = "http://" + workerIp + ":" + workerPort + "/api/" + (shouldCancel ? "cancelShutdownWorker" : "shutdownWorker");
try {
ResponseEntity<?> responseEntity = restTemplate.postForEntity(url, null, String.class);
int responseCode = responseEntity.getStatusCodeValue();
if ( responseCode != HttpStatus.OK.value() ) {
logger.error("HTTP-Connection problem with the submission of the \"postShutdownOrCancelRequestToWorker\" of worker \"" + workerId + "\"! Error-code was: " + responseCode);
return false;
} else
return true;
} catch (HttpServerErrorException hsee) {
logger.error("The Worker \"" + workerId + "\" failed to handle the \"postShutdownOrCancelRequestToWorker\": " + hsee.getMessage());
return false;
} catch (Exception e) {
// The Spring RestTemplate may return a "ResourceAccessException", but the actual cause will has to be identified, in order to set the worker as shutdown.
Throwable cause = e.getCause(); // No need to check explicitly for null.
if ( cause instanceof ConnectException ) { // This includes the "ConnectException".
logger.error("Got a \"ConnectException\" when doing a \"postShutdownOrCancelRequestToWorker\", to the Worker: \"" + workerId + "\". | Will register this worker as \"shutdown\".\n" + cause.getMessage());
UrlsController.workersInfoMap.get(workerId).setHasShutdown(true);
} else {
logger.error("Error for \"postShutdownOrCancelRequestToWorker\", to the Worker: " + workerId, e);
// TODO - What should we do? If there was some error from the Controller, side, it does not mean that the worker has shutdown..
// For now, let's handle that case manually, by check with that specific worker and sending it a shutdownRequest from inside its VM.
// Then the Worker will automatically send a "shutdownReport" to the Controller, causing it to shutdown (when all other workers have shutdown as well).
}
return false;
}
}
}