Add daemon handling service and associated routes.

This commit is contained in:
2019-09-03 23:17:49 +02:00
parent d7556a504e
commit cf8a540540
7 changed files with 295 additions and 1 deletions

View File

@@ -14,6 +14,7 @@ import java.util.UUID;
import static org.cerberus.core.constant.RoleSecurity.ADMIN; import static org.cerberus.core.constant.RoleSecurity.ADMIN;
import static org.cerberus.core.constant.RoleSecurity.MAINTAINER; import static org.cerberus.core.constant.RoleSecurity.MAINTAINER;
import static org.cerberus.services.DaemonHandlingService.Action.*;
@RestController @RestController
@RequestMapping("/api/applications") @RequestMapping("/api/applications")
@@ -54,4 +55,31 @@ public class ApplicationController {
securityService.checkHasAnyRole(connectedUser, id, ADMIN, MAINTAINER); securityService.checkHasAnyRole(connectedUser, id, ADMIN, MAINTAINER);
service.delete(id); service.delete(id);
} }
@PostMapping("/{id}/start")
@ResponseStatus(HttpStatus.NO_CONTENT)
public void start(@PathVariable("id") UUID id, Principal connectedUser) {
securityService.checkHasAnyRole(connectedUser, id, ADMIN, MAINTAINER);
service.doServiceAction(id, START);
}
@PostMapping("/{id}/stop")
@ResponseStatus(HttpStatus.NO_CONTENT)
public void stop(@PathVariable("id") UUID id, Principal connectedUser) {
securityService.checkHasAnyRole(connectedUser, id, ADMIN, MAINTAINER);
service.doServiceAction(id, STOP);
}
@PostMapping("/{id}/restart")
@ResponseStatus(HttpStatus.NO_CONTENT)
public void restart(@PathVariable("id") UUID id, Principal connectedUser) {
securityService.checkHasAnyRole(connectedUser, id, ADMIN, MAINTAINER);
service.doServiceAction(id, RESTART);
}
@GetMapping("/{id}/status")
public int status(@PathVariable("id") UUID id, Principal connectedUser) {
securityService.checkHasAnyRole(connectedUser, id, ADMIN, MAINTAINER);
return service.getStatus(id);
}
} }

View File

@@ -0,0 +1,18 @@
package org.cerberus.core.constant;
public enum ResultCode {
SUCCESS(0),
FAILED(1),
STATE_UNCHANGED(2),
ILLEGAL_ARGUMENT(3);
private int val;
private ResultCode(final int pVal) {
val = pVal;
}
public final int val() {
return val;
}
}

View File

@@ -0,0 +1,15 @@
package org.cerberus.core.exceptions;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.ResponseStatus;
@ResponseStatus(value = HttpStatus.SERVICE_UNAVAILABLE)
public class ServiceUnavailableException extends BusinessException {
public ServiceUnavailableException(String message) {
super(message);
}
public ServiceUnavailableException(String message, Throwable cause) {
super(message, cause);
}
}

View File

@@ -0,0 +1,99 @@
package org.cerberus.entities.business;
/**
* Object that contains the results of a system command.
*
* @author takiguchi
*
*/
public class SystemResult {
/** The result code of the command. */
private int resultCode;
/** The standard output of the command. */
private StringBuilder stdOut;
/** The standard errors output of the command. */
private StringBuilder stdErr;
public SystemResult() {
super();
stdOut = new StringBuilder();
stdErr = new StringBuilder();
}
/**
* Gets the result code.
*
* @return the result code
*/
public int getResultCode() {
return resultCode;
}
/**
* Sets the result code.
*
* @param resultCode
* the new result code
*/
public void setResultCode(int resultCode) {
this.resultCode = resultCode;
}
/**
* Gets the std out.
*
* @return the std out
*/
public String getStdOut() {
return stdOut.toString();
}
/**
* Add an output line to the {@code stdOut}.
*
* @param pLine
* The output line to append.
*/
public void addOutputLine(final String pLine) {
stdOut.append(pLine);
}
/**
* Sets the std out.
*
* @param stdOut
* the new std out
*/
public void setStdOut(String stdOut) {
this.stdOut = new StringBuilder(stdOut);
}
/**
* Gets the std err.
*
* @return the std err
*/
public String getStdErr() {
return stdErr.toString();
}
/**
* Add an error line to the {@code stdErr}.
*
* @param pLine
* The error line to append.
*/
public void addErrorLine(final String pLine) {
stdErr.append(pLine);
}
/**
* Sets the std err.
*
* @param stdErr
* the new std err
*/
public void setStdErr(String stdErr) {
this.stdErr = new StringBuilder(stdErr);
}
}

View File

@@ -8,6 +8,8 @@ import org.cerberus.validators.ApplicationValidator;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
import java.util.UUID;
import static org.cerberus.core.constant.Role.MAINTAINER; import static org.cerberus.core.constant.Role.MAINTAINER;
import static org.cerberus.core.utils.StringUtils.concat; import static org.cerberus.core.utils.StringUtils.concat;
@@ -16,14 +18,17 @@ public class ApplicationService extends AbstractService<Application> {
private ApplicationRepository repository; private ApplicationRepository repository;
private ApplicationRoleService applicationRoleService; private ApplicationRoleService applicationRoleService;
private ApplicationValidator validator; private ApplicationValidator validator;
private DaemonHandlingService daemonHandlingService;
ApplicationService(ApplicationRepository repository, ApplicationService(ApplicationRepository repository,
ApplicationRoleService applicationRoleService, ApplicationRoleService applicationRoleService,
ApplicationValidator validator) { ApplicationValidator validator,
DaemonHandlingService daemonHandlingService) {
super(repository); super(repository);
this.repository = repository; this.repository = repository;
this.applicationRoleService = applicationRoleService; this.applicationRoleService = applicationRoleService;
this.validator = validator; this.validator = validator;
this.daemonHandlingService = daemonHandlingService;
} }
@Transactional @Transactional
@@ -63,4 +68,12 @@ public class ApplicationService extends AbstractService<Application> {
return repository.save(application); return repository.save(application);
} }
public void doServiceAction(UUID applicationId, DaemonHandlingService.Action action) {
daemonHandlingService.doServiceAction(findByIdOrElseThrow(applicationId), action);
}
public int getStatus(UUID applicationId) {
return daemonHandlingService.getStatus(findByIdOrElseThrow(applicationId));
}
} }

View File

@@ -0,0 +1,47 @@
package org.cerberus.services;
import org.cerberus.core.exceptions.BadRequestException;
import org.cerberus.core.exceptions.ServiceUnavailableException;
import org.cerberus.entities.business.SystemResult;
import org.cerberus.entities.persistence.Application;
import org.springframework.stereotype.Service;
import static org.cerberus.core.constant.ResultCode.SUCCESS;
import static org.cerberus.core.utils.StringUtils.concat;
@Service
public class DaemonHandlingService {
public enum Action {
START,
STOP,
RESTART
}
private SystemService systemService;
DaemonHandlingService(SystemService systemService) {
this.systemService = systemService;
}
public int getStatus(Application application) {
return systemService.executeCommand("sudo service", application.getServiceName(), "status")
.getResultCode();
}
public void doServiceAction(Application application, Action action) {
int applicationStatus = getStatus(application);
if((Action.START == action && applicationStatus == 0)
|| (Action.STOP == action && applicationStatus != 0)) {
throw new BadRequestException(concat("Service is already ", action.name().toLowerCase(), "ed."));
}
SystemResult commandResult = systemService.executeCommand("sudo service",
application.getServiceName(),
action.name().toLowerCase());
if(commandResult.getResultCode() != SUCCESS.val()) {
throw new ServiceUnavailableException(commandResult.getStdErr());
}
}
}

View File

@@ -0,0 +1,74 @@
package org.cerberus.services;
import org.cerberus.entities.business.SystemResult;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import static org.cerberus.core.utils.StringUtils.concat;
/**
* Business service to execute unix commands.
*/
@Service
public class SystemService {
private static final Logger LOG = LoggerFactory.getLogger(SystemService.class);
public SystemResult executeCommand(final String pCommand, final String... arguments) {
final String commandWithArgs = buildCommand(pCommand, arguments);
final SystemResult commandResults = new SystemResult();
try {
// Process creation and execution of the command.
final Process process = Runtime.getRuntime().exec(commandWithArgs);
// Waiting the end of the command execution.
process.waitFor();
// Getting of the stantard command output, and the standard error output.
BufferedReader outputReader = new BufferedReader(new InputStreamReader(process.getInputStream()));
BufferedReader errorReader = new BufferedReader(new InputStreamReader(process.getErrorStream()));
String tempLine;
while((tempLine = outputReader.readLine()) != null) {
commandResults.addOutputLine(tempLine);
}
while((tempLine = errorReader.readLine()) != null) {
commandResults.addErrorLine(tempLine);
}
commandResults.setResultCode(process.exitValue());
} catch(IOException | InterruptedException ex) {
LOG.error(concat("Error during command execution of: \"", commandWithArgs, "\"."), ex);
}
return commandResults;
}
/**
* Build the command in form of one string from the parameters.
*
* @param command
* The command to execute.
* @param arguments
* The command arguments, could be {@code null}.
* @return The command built.
*/
private String buildCommand(final String command, final Object... arguments) {
final StringBuilder commandBuilder = new StringBuilder(command);
if (arguments != null) {
for (final Object arg : arguments) {
commandBuilder.append(" ").append(arg);
}
}
return commandBuilder.toString();
}
}