Java tutorial
/* * LICENCE : CloudUnit is available under the GNU Affero General Public License : https://gnu.org/licenses/agpl.html * but CloudUnit is licensed too under a standard commercial license. * Please contact our sales team if you would like to discuss the specifics of our Enterprise license. * If you are not sure whether the AGPL is right for you, * you can always test our software under the AGPL and inspect the source code before you contact us * about purchasing a commercial license. * * LEGAL TERMS : "CloudUnit" is a registered trademark of Treeptik and can't be used to endorse * or promote products derived from this project without prior written permission from Treeptik. * Products or services derived from this software may not be called "CloudUnit" * nor may "Treeptik" or similar confusing terms appear in their names without prior written permission. * For any questions, contact us : contact@treeptik.fr */ package cn.org.once.cstack.service.impl; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Optional; import java.util.stream.Collectors; import javax.inject.Inject; import javax.persistence.PersistenceException; import cn.org.once.cstack.config.events.HookEvent; import cn.org.once.cstack.config.events.ModuleStopEvent; import cn.org.once.cstack.dao.ModuleDAO; import cn.org.once.cstack.dao.PortDAO; import cn.org.once.cstack.enums.RemoteExecAction; import cn.org.once.cstack.exception.CheckException; import cn.org.once.cstack.model.EnvironmentVariable; import cn.org.once.cstack.model.Image; import cn.org.once.cstack.model.Port; import cn.org.once.cstack.model.User; import cn.org.once.cstack.service.EnvironmentService; import cn.org.once.cstack.service.FileService; import cn.org.once.cstack.service.ImageService; import cn.org.once.cstack.utils.NamingUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Value; import org.springframework.cache.annotation.CacheEvict; import org.springframework.context.ApplicationEventPublisher; import org.springframework.dao.DataAccessException; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import org.springframework.web.multipart.MultipartFile; import cn.org.once.cstack.config.events.ModuleStartEvent; import cn.org.once.cstack.dto.Hook; import cn.org.once.cstack.enums.ModuleEnvironmentRole; import cn.org.once.cstack.exception.DockerJSONException; import cn.org.once.cstack.exception.ServiceException; import cn.org.once.cstack.model.Application; import cn.org.once.cstack.model.Module; import cn.org.once.cstack.model.Status; import cn.org.once.cstack.service.DockerService; import cn.org.once.cstack.service.ModuleService; import cn.org.once.cstack.utils.ModuleUtils; @Service public class ModuleServiceImpl implements ModuleService { private Logger logger = LoggerFactory.getLogger(ModuleServiceImpl.class); @Inject private ModuleDAO moduleDAO; @Inject private PortDAO portDAO; @Inject private EnvironmentService environmentService; @Inject private ImageService imageService; @Inject private DockerService dockerService; @Value("${cloudunit.instance.name}") private String cuInstanceName; @Inject private ApplicationEventPublisher applicationEventPublisher; @Inject private FileService fileService; @Override @Transactional @CacheEvict("env") public Module create(String imageName, Application application, User user) throws ServiceException, CheckException { // General informations checkImageExist(imageName); Image image = imageService.findByName(imageName); checkModuleAlreadyPresent(image.getPrefixEnv(), application.getId()); Module module = application.addModule(image); // Build a custom container String containerName = NamingUtils.getContainerName(module.getApplication().getName(), module.getImage().getPrefixEnv(), user.getLogin()); String imagePath = module.getImage().getPath(); logger.debug("imagePath:" + imagePath); try { Map<ModuleEnvironmentRole, ModuleEnvironmentVariable> moduleEnvs = getModuleEnvironmentVariables(image, application.getName()); List<String> internalEnvironment = getInternalEnvironment(moduleEnvs); List<EnvironmentVariable> exportedEnvironment = getExportedEnvironment(module, image, moduleEnvs); environmentService.save(user, exportedEnvironment, application.getName(), application.getServer().getName()); dockerService.createModule(containerName, module, imagePath, user, internalEnvironment, true, new ArrayList<>()); module = dockerService.startModule(containerName, module); module = moduleDAO.save(module); environmentService.createInDatabase(getInternalEnvironment(module, image, moduleEnvs), containerName, application); applicationEventPublisher.publishEvent(new ModuleStartEvent(module)); applicationEventPublisher .publishEvent(new HookEvent(new Hook(containerName, RemoteExecAction.MODULE_POST_CREATION))); } catch (PersistenceException e) { logger.error("ServerService Error : Create Server " + e); throw new ServiceException(e.getLocalizedMessage(), e); } catch (DockerJSONException e) { logger.error("module = {}", module); logger.error("Error detail", e); throw new ServiceException("Error while creating a module", e); } return module; } private List<String> getInternalEnvironment(Map<ModuleEnvironmentRole, ModuleEnvironmentVariable> moduleEnvs) { List<String> internalEnvironment = moduleEnvs.values().stream() .map(v -> String.format("%s=%s", v.getName(), v.getValue())).collect(Collectors.toList()); return internalEnvironment; } private List<EnvironmentVariable> getExportedEnvironment(Module module, Image image, Map<ModuleEnvironmentRole, ModuleEnvironmentVariable> moduleEnvs) { List<EnvironmentVariable> environmentVariables = moduleEnvs.entrySet().stream().map(kv -> { EnvironmentVariable environmentVariable = new EnvironmentVariable(); environmentVariable.setKeyEnv(String.format("CU_%s_%s_%s", image.getImageSubType(), kv.getKey().toString(), image.getPrefixEnv().toUpperCase())); environmentVariable.setValueEnv(kv.getValue().getValue()); return environmentVariable; }).collect(Collectors.toList()); EnvironmentVariable environmentVariable = new EnvironmentVariable(); environmentVariable.setKeyEnv( String.format("CU_%s_DNS_%s", image.getImageSubType(), image.getPrefixEnv().toUpperCase())); environmentVariable.setValueEnv(module.getInternalDNSName()); environmentVariables.add(environmentVariable); return environmentVariables; } private List<EnvironmentVariable> getInternalEnvironment(Module module, Image image, Map<ModuleEnvironmentRole, ModuleEnvironmentVariable> moduleEnvs) { List<EnvironmentVariable> environmentVariables = moduleEnvs.entrySet().stream().map(kv -> { EnvironmentVariable environmentVariable = new EnvironmentVariable(); environmentVariable.setKeyEnv(kv.getValue().getName()); environmentVariable.setValueEnv(kv.getValue().getValue()); return environmentVariable; }).collect(Collectors.toList()); return environmentVariables; } /* * * Save app in just in DB, not create container use principally to charge* * status.PENDING of entity until it's really functionnal */ @Override @Transactional(rollbackFor = ServiceException.class) @CacheEvict("env") public Module publishPort(Integer id, Boolean publishPort, String port, User user) throws ServiceException, CheckException { Module module = findById(id); Optional<Port> optionalPort = module.getPorts().stream().filter(p -> p.getContainerValue().equals(port)) .findAny(); if (optionalPort.isPresent()) { Port portToBind = optionalPort.get(); portToBind.setOpened(publishPort); portDAO.save(portToBind); } module = findById(id); if (module == null) { throw new CheckException("Module not found"); } List<String> envs = environmentService.loadEnvironnmentsByContainer(module.getName()).stream() .map(e -> e.getKeyEnv() + "=" + e.getValueEnv()).collect(Collectors.toList()); dockerService.removeContainer(module.getName(), false); dockerService.createModule(module.getName(), module, module.getImage().getPath(), user, envs, false, new ArrayList<>()); module = dockerService.startModule(module.getName(), module); module = moduleDAO.save(module); applicationEventPublisher.publishEvent(new ModuleStartEvent(module)); return module; } public void checkImageExist(String moduleName) throws ServiceException { Image image = imageService.findByName(moduleName); if (image == null) { throw new ServiceException("Error : the module " + moduleName + " is not available"); } } @Override @Transactional public Module update(Module module) throws ServiceException { logger.debug("update : Methods parameters : " + module.toString()); logger.info("ModuleService : Starting updating Module " + module.getName()); try { module = moduleDAO.save(module); } catch (PersistenceException e) { module.setStatus(Status.FAIL); module = moduleDAO.save(module); logger.error("ModuleService Error : update Module" + e); throw new ServiceException(e.getLocalizedMessage(), e); } logger.info("ModuleService : Module " + module.getName() + " successfully updated."); return module; } @Override @Transactional public void remove(User user, String moduleName, Boolean isModuleRemoving, Status previousApplicationStatus) throws ServiceException, CheckException { Module module = findByName(moduleName); remove(user, module, isModuleRemoving, previousApplicationStatus); } @Override @Transactional public void remove(User user, Module module, Boolean isModuleRemoving, Status previousApplicationStatus) throws ServiceException, CheckException { try { dockerService.removeContainer(module.getName(), true); Application application = module.getApplication(); application.removeModule(module); moduleDAO.delete(module); if (isModuleRemoving) { List<EnvironmentVariable> envs = environmentService .loadEnvironnmentsByContainer(application.getServer().getName()); environmentService.delete(user, envs.stream().filter( e -> e.getKeyEnv().toLowerCase().contains(module.getImage().getPrefixEnv().toLowerCase())) .collect(Collectors.toList()), application.getName(), application.getServer().getName()); } logger.info("Module successfully removed "); } catch (Exception e) { StringBuilder msgError = new StringBuilder(); msgError.append(user.toString()); msgError.append(module.toString()); msgError.append(", isModuleRemoving:").append(isModuleRemoving); msgError.append(", previousApplicationStatus:").append(previousApplicationStatus); throw new ServiceException(msgError.toString(), e); } } @Override @Transactional public Module startModule(String moduleName) throws ServiceException { logger.info("Module : Starting module " + moduleName); Module module = null; try { module = findByName(moduleName); module = dockerService.startModule(moduleName, module); applicationEventPublisher.publishEvent(new ModuleStartEvent(module)); if (!module.isInitialized()) { module.setInitialized(true); module = moduleDAO.save(module); applicationEventPublisher .publishEvent(new HookEvent(new Hook(moduleName, RemoteExecAction.MODULE_POST_START_ONCE))); } applicationEventPublisher .publishEvent(new HookEvent(new Hook(moduleName, RemoteExecAction.MODULE_POST_START))); } catch (PersistenceException e) { logger.error("ModuleService Error : fail to start Module" + moduleName); throw new ServiceException(e.getLocalizedMessage(), e); } return module; } @Override @Transactional public Module stopModule(String moduleName) throws ServiceException { Module module = null; try { module = findByName(moduleName); dockerService.stopContainer(moduleName); applicationEventPublisher.publishEvent(new ModuleStopEvent(module)); } catch (DataAccessException e) { logger.error("[" + moduleName + "] Fail to stop Module : " + moduleName); throw new ServiceException(e.getLocalizedMessage(), e); } return module; } @Override public Module findById(Integer id) throws ServiceException { try { logger.debug("findById : Methods parameters : " + id); Module module = moduleDAO.findOne(id); logger.info("Module with id " + id + " found!"); return module; } catch (PersistenceException e) { logger.error("Error ModuleService : error findById Method : " + e); throw new ServiceException(e.getLocalizedMessage(), e); } } @Override public List<Module> findAll() throws ServiceException { try { logger.debug("start findAll"); List<Module> modules = moduleDAO.findAll(); logger.info("ModuleService : All Modules found "); return modules; } catch (PersistenceException e) { logger.error("Error ModuleService : error findAll Method : " + e); throw new ServiceException(e.getLocalizedMessage(), e); } } @Override public Module findByContainerID(String id) throws ServiceException { try { return moduleDAO.findByContainerID(id); } catch (PersistenceException e) { logger.error("Error ModuleService : error findCloudId Method : " + e); throw new ServiceException(e.getLocalizedMessage(), e); } } @Override public Module findByName(String moduleName) throws ServiceException { try { logger.debug("findByName : " + moduleName); Module module = moduleDAO.findByName(moduleName); logger.debug("findByName : " + module); return module; } catch (PersistenceException e) { logger.error("Error ModuleService : error findName Method : " + e); throw new ServiceException(e.getLocalizedMessage(), e); } } @Override public List<Module> findByApp(Application application) throws ServiceException { try { return moduleDAO.findByApp(application.getName(), cuInstanceName); } catch (PersistenceException e) { logger.error("Error ModuleService : error findByApp Method : " + e); throw new ServiceException(e.getLocalizedMessage(), e); } } @Override public List<Module> findByAppAndUser(User user, String applicationName) throws ServiceException { try { List<Module> modules = moduleDAO.findByAppAndUser(user.getId(), applicationName, cuInstanceName); return modules; } catch (PersistenceException e) { logger.error("Error ModuleService : error findByAppAndUser Method : " + e); throw new ServiceException(e.getLocalizedMessage(), e); } } @Override public String runScript(String moduleName, MultipartFile file) throws ServiceException { try { Module module = findByName(moduleName); String filename = file.getOriginalFilename(); String containerId = module.getContainerID(); String tempDirectory = dockerService.getEnv(containerId, "CU_TMP"); fileService.sendFileToContainer(containerId, tempDirectory, file, null, null); @SuppressWarnings("serial") Map<String, String> kvStore = new HashMap<String, String>() { { put("CU_FILE", filename); } }; return dockerService.execCommand(containerId, RemoteExecAction.RUN_SCRIPT.getCommand(kvStore)); } catch (Exception e) { throw new ServiceException(e.getLocalizedMessage(), e); } } public Map<ModuleEnvironmentRole, ModuleEnvironmentVariable> getModuleEnvironmentVariables(Image image, String applicationName) { return image.getModuleEnvironmentVariables().entrySet().stream() .collect(Collectors.toMap(kv -> kv.getKey(), kv -> { String value = null; switch (kv.getKey()) { case USER: value = ModuleUtils.generateRamdomUser(); break; case PASSWORD: value = ModuleUtils.generateRamdomPassword(); break; case NAME: value = applicationName; break; } return new ModuleEnvironmentVariable(kv.getValue(), value); })); } private static class ModuleEnvironmentVariable { private final String name; private final String value; public ModuleEnvironmentVariable(String name, String value) { super(); this.name = name; this.value = value; } public String getName() { return name; } public String getValue() { return value; } } public void checkModuleAlreadyPresent(String imagePrefixEnv, Integer applicationId) throws CheckException { Long moduleCount = moduleDAO.countModuleNameByApplication(imagePrefixEnv, applicationId); logger.info("count module : " + moduleCount); if (moduleCount != 0) { logger.info("This module already exists"); throw new CheckException("This module already exists"); } } }