| | |
| | | import com.alibaba.fastjson.JSONArray; |
| | | import com.alibaba.fastjson.JSONObject; |
| | | import com.alibaba.nacos.shaded.com.google.protobuf.ServiceException; |
| | | import com.baomidou.mybatisplus.core.toolkit.Wrappers; |
| | | import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; |
| | | import com.baomidou.mybatisplus.extension.toolkit.SqlHelper; |
| | | import com.vci.ubcs.deploy.entity.CmdConfig; |
| | | import com.vci.ubcs.deploy.entity.DeployApps; |
| | | import com.vci.ubcs.deploy.entity.Instance; |
| | | import com.vci.ubcs.deploy.enumpack.CmdConfigEnum; |
| | | import com.vci.ubcs.deploy.mapper.DeployAppsMapper; |
| | | import com.vci.ubcs.deploy.service.IDeployAppsService; |
| | | import com.vci.ubcs.deploy.vo.DeployAppsVO; |
| | | import com.vci.ubcs.starter.util.HttpUtils; |
| | | import lombok.RequiredArgsConstructor; |
| | | import lombok.extern.slf4j.Slf4j; |
| | | import org.springblade.core.tool.api.R; |
| | | import org.springblade.core.tool.utils.Func; |
| | | import org.springblade.core.tool.utils.UrlUtil; |
| | | import org.springframework.beans.factory.annotation.Autowired; |
| | | import org.springframework.beans.factory.annotation.Value; |
| | | import org.springframework.cloud.client.ServiceInstance; |
| | | import org.springframework.cloud.client.discovery.DiscoveryClient; |
| | | import org.springframework.context.EnvironmentAware; |
| | | import org.springframework.core.env.Environment; |
| | | import org.springframework.http.HttpHeaders; |
| | | import org.springframework.http.HttpStatus; |
| | | import org.springframework.http.MediaType; |
| | | import org.springframework.http.ResponseEntity; |
| | | import org.springframework.stereotype.Service; |
| | | import org.springframework.web.multipart.MultipartFile; |
| | | |
| | | import javax.servlet.ServletRequest; |
| | | import javax.servlet.http.HttpServletRequest; |
| | | import java.io.*; |
| | | import java.net.URL; |
| | | import java.util.ArrayList; |
| | | import java.util.List; |
| | | import java.nio.file.Files; |
| | | import java.nio.file.Path; |
| | | import java.nio.file.Paths; |
| | | import java.nio.file.StandardCopyOption; |
| | | import java.util.*; |
| | | import java.util.stream.Collectors; |
| | | import java.util.stream.Stream; |
| | | import java.util.zip.ZipEntry; |
| | | import java.util.zip.ZipFile; |
| | | import java.util.zip.ZipInputStream; |
| | | |
| | | /** |
| | | * @author ludc |
| | | * @date 2024/1/7 19:54 |
| | | */ |
| | | @Service |
| | | public class DeployAppsServiceImpl extends ServiceImpl<DeployAppsMapper, DeployApps> implements IDeployAppsService { |
| | | @RequiredArgsConstructor |
| | | @Slf4j |
| | | public class DeployAppsServiceImpl extends ServiceImpl<DeployAppsMapper, DeployApps> implements IDeployAppsService, EnvironmentAware { |
| | | |
| | | @Autowired |
| | | private DeployAppsMapper deployAppsMapper; |
| | | private final DeployAppsMapper deployAppsMapper; |
| | | |
| | | @Value("${password-free.pwd-free-addr:localhost}") |
| | | private String pwdFreeAddr; |
| | | |
| | | // 通过服务注册中心获取网关的端口号 |
| | | /** |
| | | * 通过服务注册中心获取网关的端口号 |
| | | */ |
| | | @Autowired |
| | | private DiscoveryClient discoveryClient; |
| | | |
| | | /** |
| | | * 各个服务存放的的父路径 |
| | | */ |
| | | @Value("${local-log.parent-path:/data1/ubcs/ubcs-server}") |
| | | private String PARENTPATH; |
| | | |
| | | /** |
| | | * 日志文件的具体位置 |
| | | */ |
| | | @Value("${local-log.log-path:/target/log}") |
| | | private String LOGPATH; |
| | | |
| | | /** |
| | | * 根据操作系统生成分隔符 |
| | | */ |
| | | private String SEPARATOR = "/"; |
| | | |
| | | /** |
| | | * 根据当前运行的环境,对配置的日志路径格式进行调整 |
| | | * @param environment |
| | | */ |
| | | @Override |
| | | public void setEnvironment(Environment environment) { |
| | | String os = environment.getProperty("os.name").toLowerCase(); |
| | | if (!os.contains("win")) { |
| | | this.SEPARATOR = "/"; |
| | | }else{ |
| | | this.SEPARATOR = "\\"; |
| | | } |
| | | } |
| | | |
| | | @Override |
| | | public List<DeployAppsVO> getApplications(ServletRequest servletRequest) throws ServiceException { |
| | |
| | | deployAppsVO.setStatusTimestamp(jsonObject.get("statusTimestamp").toString()); |
| | | deployAppsVOList.add(deployAppsVO); |
| | | } |
| | | |
| | | } |
| | | }catch (Exception e){ |
| | | throw new ServiceException("调用ubcs-admin获取服务信息失败,原因:"+e.getMessage()); |
| | | } |
| | | // 再查询库中已经存在的服务配置信息,进行是否已经启动的判断 |
| | | List<DeployApps> deployApps = deployAppsMapper.selectList(null); |
| | | // 库中未配置,直接返回正在运行的服务信息 |
| | | if(deployApps.isEmpty()){ |
| | | return deployAppsVOList; |
| | | } |
| | | // 筛选出不在运行的并生成默认的服务信息 |
| | | List<DeployAppsVO> deployAppsVOS1 = deployApps.stream() |
| | | .filter(deployApp -> deployAppsVOList.stream() |
| | | .noneMatch(deployAppVO -> deployApp.getServerName().equals(deployAppVO.getName()) && |
| | | deployApp.getServerName().equals(deployAppVO.getName()))) |
| | | .map(deployApp -> { |
| | | DeployAppsVO deployAppsVO2 = new DeployAppsVO(deployApp.getServerName(), "DOWN", "", "", 0); |
| | | if(deployApp.getServerName().equals("web")){ |
| | | deployAppsVO2.setStatus("UP"); |
| | | deployAppsVO2.setPort("8080"); |
| | | deployAppsVO2.setServiceNum(1); |
| | | } |
| | | return deployAppsVO2; |
| | | }) |
| | | .collect(Collectors.toList()); |
| | | deployAppsVOList.addAll(deployAppsVOS1); |
| | | return deployAppsVOList; |
| | | } |
| | | |
| | | @Override |
| | | public DeployApps saveOrGetServiceConfInfo(DeployAppsVO deployAppsVO) throws ServiceException { |
| | | if(deployAppsVO.getName().isEmpty()){ |
| | | throw new ServiceException("缺少必传参数name"); |
| | | } |
| | | DeployApps deployApps = deployAppsMapper.selectOne(Wrappers.<DeployApps>query().lambda().eq(DeployApps::getServerName, deployAppsVO.getName())); |
| | | if(Func.isNotEmpty(deployApps)){ |
| | | return deployApps; |
| | | } |
| | | // 未从库中查询到,需要生成服务信息保存默认信息到库中 |
| | | DeployApps defaultDeployApps = new DeployApps(); |
| | | defaultDeployApps.setLogPath(PARENTPATH + "\\" + deployAppsVO.getName().replace("-","_") + LOGPATH); |
| | | defaultDeployApps.setServerName(deployAppsVO.getName()); |
| | | defaultDeployApps.setStartCmd(CmdConfigEnum.START_CMD.getValue() + deployAppsVO.getName()); |
| | | defaultDeployApps.setStopCmd(CmdConfigEnum.STOP_CMD.getValue() + deployAppsVO.getName()); |
| | | defaultDeployApps.setRestartCmd(CmdConfigEnum.RESTART_CMD.getValue() + deployAppsVO.getName()); |
| | | defaultDeployApps.setServerPath(PARENTPATH + "\\" + deployAppsVO.getName().replace("-","_")); |
| | | int eft = deployAppsMapper.insert(defaultDeployApps); |
| | | if (!SqlHelper.retBool(eft)) { |
| | | throw new ServiceException("生成默认服务信息到库中时失败!"); |
| | | } |
| | | return defaultDeployApps; |
| | | } |
| | | |
| | | /** |
| | | * 修改或保存 |
| | | * @param deployAppsVO |
| | | * @return |
| | | * @throws ServiceException |
| | | */ |
| | | @Override |
| | | public boolean saveOrUpdateServiceInfo(DeployAppsVO deployAppsVO) throws ServiceException { |
| | | boolean b = this.saveOrUpdate(deployAppsVO); |
| | | return b; |
| | | } |
| | | |
| | | /** |
| | | * 新增服务信息 |
| | | * @param deployApps |
| | | * @return |
| | | * @throws ServiceException |
| | | */ |
| | | @Override |
| | | public boolean addSave(DeployApps deployApps) throws ServiceException { |
| | | if (Func.isBlank(deployApps.getServerName()) || Func.isBlank(deployApps.getServerPath())) { |
| | | throw new ServiceException("必传参数[服务名称,服务存放路径]不能为空"); |
| | | } |
| | | return SqlHelper.retBool(deployAppsMapper.insert(deployApps)); |
| | | } |
| | | |
| | | /** |
| | | * 更新文件上传 |
| | | * @param files |
| | | * @param serverName |
| | | * @return |
| | | */ |
| | | @Override |
| | | public R importClassify(MultipartFile[] files, String serverName) throws ServiceException { |
| | | // 根据服务名查看到服务相关信息 |
| | | List<DeployApps> deployAppsDB = deployAppsMapper.selectList(Wrappers.<DeployApps>query().lambda().eq(DeployApps::getServerName, serverName)); |
| | | if(deployAppsDB.isEmpty()){ |
| | | return R.fail("No configuration information related to "+ serverName +" service found"); |
| | | } |
| | | // 遍历MultipartFile数组,逐个处理文件 |
| | | try { |
| | | for (MultipartFile file : files) { |
| | | // 配置了备份文件路径,先备份再替换 |
| | | if(Func.isNotEmpty(deployAppsDB.get(0).getFileBack())){ |
| | | File backFile = new File(deployAppsDB.get(0).getFileBack()); |
| | | // 路径不存在就创建 |
| | | if (!backFile.exists()) { |
| | | backFile.mkdirs(); |
| | | } |
| | | String backName = ""; |
| | | String fileType = "file"; |
| | | // 是压缩文件,因为只会存在两种情况,文件名是压缩文件,或者文件(.jar类型的文件) |
| | | if (file.getContentType().equals("application/zip") || file.getContentType().equals("application/x-zip-compressed")) { |
| | | backName = file.getOriginalFilename().replace(".zip","_"+Func.formatDate(new Date())); |
| | | fileType = "zip"; |
| | | }else{ |
| | | backName = file.getOriginalFilename().replace(".","_"+Func.formatDate(new Date())+"."); |
| | | fileType = "file"; |
| | | } |
| | | File source = new File(deployAppsDB.get(0).getServerPath() + this.SEPARATOR + file.getOriginalFilename().replace(".zip", "")); |
| | | File destination = new File(deployAppsDB.get(0).getFileBack() + this.SEPARATOR + backName); |
| | | copyFolder(source, destination); |
| | | } |
| | | Path filePath = Paths.get(deployAppsDB.get(0).getServerPath(), file.getOriginalFilename()); |
| | | Files.copy(file.getInputStream(), filePath, StandardCopyOption.REPLACE_EXISTING); |
| | | // 检查文件类型,如果是压缩文件则解压缩 |
| | | if (file.getContentType().equals("application/zip") || file.getContentType().equals("application/x-zip-compressed")) { |
| | | //sourcePath压缩包文件路径 |
| | | try (ZipFile zipFile = new ZipFile(new File(deployAppsDB.get(0).getServerPath()+ this.SEPARATOR +file.getOriginalFilename()))) { |
| | | Enumeration enumeration = zipFile.entries(); |
| | | while (enumeration.hasMoreElements()) { |
| | | //依次获取压缩包内的文件实体对象 |
| | | ZipEntry entry = (ZipEntry) enumeration.nextElement(); |
| | | String name = entry.getName(); |
| | | if (entry.isDirectory()) { |
| | | continue; |
| | | } |
| | | try (BufferedInputStream inputStream = new BufferedInputStream(zipFile.getInputStream(entry))) { |
| | | // 需要判断文件所在的目录是否存在,处理压缩包里面有文件夹的情况 |
| | | String outName = deployAppsDB.get(0).getServerPath() + this.SEPARATOR + name; |
| | | File outFile = new File(outName); |
| | | File tempFile = new File(outName.substring(0, outName.lastIndexOf("/"))); |
| | | if (!tempFile.exists()) { |
| | | tempFile.mkdirs(); |
| | | } |
| | | try (BufferedOutputStream outputStream = new BufferedOutputStream(new FileOutputStream(outFile))) { |
| | | int len; |
| | | byte[] buffer = new byte[1024]; |
| | | while ((len = inputStream.read(buffer)) > 0) { |
| | | outputStream.write(buffer, 0, len); |
| | | } |
| | | } |
| | | } |
| | | } |
| | | } catch (Exception e) { |
| | | e.printStackTrace(); |
| | | } |
| | | File file1 = new File(deployAppsDB.get(0).getServerPath() + SEPARATOR + file.getOriginalFilename()); |
| | | // 压缩文件上传成功之后,删除解压文件 |
| | | file1.delete(); |
| | | } |
| | | } |
| | | String output = excute(deployAppsDB.get(0),"UP"); |
| | | return R.success(output.toString()); |
| | | } catch (IOException e) { |
| | | e.printStackTrace(); |
| | | log.error(e.getMessage()); |
| | | return R.fail("Failed to upload files"); |
| | | } |
| | | |
| | | } |
| | | |
| | | /** |
| | | * 执行命令 |
| | | * @param deployAppsVO |
| | | * @return |
| | | */ |
| | | @Override |
| | | public R cmdExecute(DeployAppsVO deployAppsVO) throws ServiceException { |
| | | String excuteRes = ""; |
| | | try { |
| | | List<DeployApps> deployAppsDB = deployAppsMapper.selectList(Wrappers.<DeployApps>query().lambda().eq(DeployApps::getServerName, deployAppsVO.getName())); |
| | | if(deployAppsDB.isEmpty()){ |
| | | return R.fail("命令执行出错,库中未找到"+ deployAppsVO.getName() +"服务相关配置:" ); |
| | | } |
| | | excuteRes = excute(deployAppsDB.get(0),deployAppsVO.getStatus()); |
| | | return R.success("命令执行结束:"+excuteRes); |
| | | }catch (Exception e){ |
| | | throw new ServiceException(e.getMessage()); |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * 执行命令 |
| | | * @param deployApps |
| | | * @return |
| | | * @throws ServiceException |
| | | */ |
| | | private String excute(DeployApps deployApps,String type) throws ServiceException { |
| | | // 处理上传文件的逻辑 |
| | | StringBuilder output = new StringBuilder(); |
| | | try { |
| | | String cmd = ""; |
| | | if(type.equalsIgnoreCase("UP")){ |
| | | cmd = deployApps.getRestartCmd(); |
| | | }else { |
| | | cmd = deployApps.getStartCmd(); |
| | | } |
| | | if(Func.isEmpty(cmd)){ |
| | | return "The executed command is empty"; |
| | | } |
| | | // 执行Linux命令 |
| | | Process process = Runtime.getRuntime().exec(cmd); |
| | | // 读取命令执行结果 |
| | | BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream())); |
| | | String line; |
| | | while ((line = reader.readLine()) != null) { |
| | | output.append(line).append("\n"); |
| | | } |
| | | // 等待命令执行完成 |
| | | int exitCode = process.waitFor(); |
| | | log.info("命令执行结果:" + output.toString()); |
| | | return output.toString(); |
| | | }catch (IOException | InterruptedException e){ |
| | | e.printStackTrace(); |
| | | log.error("命令执行出错,原因:" + e.getMessage()); |
| | | throw new ServiceException("Command execution failed"+e.getMessage()); |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * 文件备份操作 |
| | | * @param source 源文件 |
| | | * @param destination 文件备份路径 |
| | | * @throws IOException |
| | | */ |
| | | private void copyFolder(File source, File destination) throws IOException { |
| | | // 文件存在才需要备份 |
| | | if(source.exists()){ |
| | | if (source.isDirectory()) { |
| | | if (!destination.exists()) { |
| | | destination.mkdir(); |
| | | } |
| | | String[] files = source.list(); |
| | | for (String file : files) { |
| | | File srcFile = new File(source, file); |
| | | File destFile = new File(destination, file); |
| | | copyFolder(srcFile, destFile); |
| | | } |
| | | } else { |
| | | Files.copy(source.toPath(), destination.toPath()); |
| | | } |
| | | } |
| | | } |
| | | |
| | | /** |
| | |
| | | * @param serviceId |
| | | * @return |
| | | */ |
| | | public String getGatewayPort(String serviceId) { |
| | | private String getGatewayPort(String serviceId) { |
| | | List<ServiceInstance> instances = discoveryClient.getInstances(serviceId); |
| | | if (!instances.isEmpty()) { |
| | | ServiceInstance gatewayInstance = instances.get(0); |
| | |
| | | } |
| | | return "8080"; |
| | | } |
| | | |
| | | |
| | | } |