ssh迁移
This commit is contained in:
parent
247e65761e
commit
5e893baefa
13
dependencies/pom.xml
vendored
13
dependencies/pom.xml
vendored
@ -89,6 +89,7 @@
|
|||||||
<logback-classic.version>1.5.8</logback-classic.version>
|
<logback-classic.version>1.5.8</logback-classic.version>
|
||||||
<caffeine.version>2.9.3</caffeine.version>
|
<caffeine.version>2.9.3</caffeine.version>
|
||||||
<resilience4j-circuitbreaker.version>2.3.0</resilience4j-circuitbreaker.version>
|
<resilience4j-circuitbreaker.version>2.3.0</resilience4j-circuitbreaker.version>
|
||||||
|
<!-- <winrm4j.version>0.12.3</winrm4j.version>-->
|
||||||
|
|
||||||
</properties>
|
</properties>
|
||||||
|
|
||||||
@ -650,9 +651,9 @@
|
|||||||
</exclusions>
|
</exclusions>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.pf4j</groupId>
|
<groupId>io.cloudsoft.windows</groupId>
|
||||||
<artifactId>pf4j-spring</artifactId>
|
<artifactId>winrm4j</artifactId>
|
||||||
<version>${pf4j-spring.version}</version>
|
<version>0.12.0</version> <!-- 最新稳定版 -->
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
<!--数据库驱动-->
|
<!--数据库驱动-->
|
||||||
@ -761,6 +762,12 @@
|
|||||||
<version>${resilience4j-circuitbreaker.version}</version>
|
<version>${resilience4j-circuitbreaker.version}</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
<!-- devops ci- worker end-->
|
<!-- devops ci- worker end-->
|
||||||
|
<!-- ssh相关依赖-->
|
||||||
|
<dependency>
|
||||||
|
<groupId>io.cloudsoft.windows</groupId>
|
||||||
|
<artifactId>winrm4j</artifactId>
|
||||||
|
<version>0.12.3</version>
|
||||||
|
</dependency>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
</dependencyManagement>
|
</dependencyManagement>
|
||||||
|
|
||||||
|
@ -29,6 +29,36 @@
|
|||||||
<groupId>cd.casic.boot</groupId>
|
<groupId>cd.casic.boot</groupId>
|
||||||
<artifactId>spring-boot-starter-biz-tenant</artifactId>
|
<artifactId>spring-boot-starter-biz-tenant</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<!-- ssh 相关依赖,暂时先放在这-->
|
||||||
|
<dependency>
|
||||||
|
<groupId>io.cloudsoft.windows</groupId>
|
||||||
|
<artifactId>winrm4j</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.jcraft</groupId>
|
||||||
|
<artifactId>jsch</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.antherd</groupId>
|
||||||
|
<artifactId>sm-crypto</artifactId>
|
||||||
|
<version>0.3.2</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.hierynomus</groupId>
|
||||||
|
<artifactId>sshj</artifactId>
|
||||||
|
<version>0.32.0</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>net.sf.expectit</groupId>
|
||||||
|
<artifactId>expectit-core</artifactId>
|
||||||
|
<version>0.9.0</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>io.cloudsoft.windows</groupId>
|
||||||
|
<artifactId>winrm4j</artifactId>
|
||||||
|
<version>0.12.0</version> <!-- 最新稳定版 -->
|
||||||
|
</dependency>
|
||||||
|
|
||||||
</dependencies>
|
</dependencies>
|
||||||
|
|
||||||
</project>
|
</project>
|
@ -0,0 +1,13 @@
|
|||||||
|
package cd.casic.ci.process.constant;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 命令常量
|
||||||
|
* @author cww
|
||||||
|
* @date 2022/6/17
|
||||||
|
*/
|
||||||
|
public class CommandConstant {
|
||||||
|
public static final String ENTER = "\r\n";
|
||||||
|
public static final String EXIT = "exit" + "\r\n";
|
||||||
|
public static final String SOURCE = "source-";
|
||||||
|
public static final String TARGET = "target-";
|
||||||
|
}
|
@ -1,220 +0,0 @@
|
|||||||
package cd.casic.ci.process.constant;
|
|
||||||
|
|
||||||
public class PipelineFinalConstant {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 项目名称
|
|
||||||
*/
|
|
||||||
public static final String appName = "arbess";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* DEFAULT
|
|
||||||
*/
|
|
||||||
public static final String DEFAULT = "default";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 流水线文件系统
|
|
||||||
*/
|
|
||||||
public static final String MATFLOW_WORKSPACE = "/source";
|
|
||||||
|
|
||||||
public static final String MATFLOW_LOGS = "/artifact";
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 流水线运行状态
|
|
||||||
*/
|
|
||||||
//流水线运行状态
|
|
||||||
public static final String RUN_SUCCESS = "success";
|
|
||||||
|
|
||||||
public static final String RUN_ERROR = "error";
|
|
||||||
|
|
||||||
public static final String RUN_WAIT = "wait";
|
|
||||||
|
|
||||||
public static final String RUN_HALT = "halt";
|
|
||||||
|
|
||||||
public static final String RUN_RUN = "run";
|
|
||||||
|
|
||||||
public static final String RUN_SUSPEND = "suspend";
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 系统编码
|
|
||||||
*/
|
|
||||||
//字节编码
|
|
||||||
public static final String UTF_8 = "UTF-8";
|
|
||||||
|
|
||||||
public static final String GBK = "GBK";
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 消息
|
|
||||||
*/
|
|
||||||
//消息发送类型
|
|
||||||
public static final String MES_PIPELINE_RUN = "PIPELINE_RUN";
|
|
||||||
|
|
||||||
//消息发送方式
|
|
||||||
public static final String MES_SEND_SITE = "site";
|
|
||||||
public static final String MES_SEND_EMAIL = "email";
|
|
||||||
public static final String MES_SEND_DINGDING = "dingding";
|
|
||||||
public static final String MES_SEND_WECHAT = "qywechat";
|
|
||||||
public static final String MES_SEND_SMS = "sms";
|
|
||||||
|
|
||||||
//消息通知方案
|
|
||||||
public static final String MES_UPDATE = "MF_MES_TYPE_UPDATE";
|
|
||||||
public static final String MES_DELETE = "MF_MES_TYPE_DELETE";
|
|
||||||
public static final String MES_CREATE = "MF_MES_TYPE_CREATE";
|
|
||||||
public static final String MES_RUN = "MF_MES_TYPE_RUN";
|
|
||||||
|
|
||||||
|
|
||||||
// 日志类型
|
|
||||||
public static final String LOG_TYPE_CREATE = "MF_LOG_TYPE_CREATE";
|
|
||||||
|
|
||||||
public static final String LOG_TYPE_DELETE = "MF_LOG_TYPE_DELETE";
|
|
||||||
|
|
||||||
public static final String LOG_TYPE_UPDATE = "MF_LOG_TYPE_UPDATE";
|
|
||||||
|
|
||||||
public static final String LOG_TYPE_RUN = "MF_LOG_TYPE_RUN";
|
|
||||||
|
|
||||||
|
|
||||||
public static final String CREATE_LINK = "/pipeline/${pipelineId}/config";
|
|
||||||
|
|
||||||
public static final String DELETE_LINK = "/pipeline/${pipelineId}/delete";
|
|
||||||
|
|
||||||
public static final String UPDATE_LINK = "/pipeline/${pipelineId}/set/info";
|
|
||||||
|
|
||||||
public static final String RUN_LINK = "/pipeline/${pipelineId}/history/${instanceId}";
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 构建产物信息
|
|
||||||
*/
|
|
||||||
// 默认制品地址
|
|
||||||
public static final String PROJECT_DEFAULT_ADDRESS = "${PROJECT_DEFAULT_ADDRESS}";
|
|
||||||
|
|
||||||
public static final String DEFAULT_ARTIFACT_ADDRESS = "DEFAULT_ARTIFACT_ADDRESS";
|
|
||||||
|
|
||||||
// 默认制品
|
|
||||||
public static final String DEFAULT_ARTIFACT_NAME = "DEFAULT_ARTIFACT_NAME";
|
|
||||||
|
|
||||||
// Docker制品
|
|
||||||
public static final String DEFAULT_ARTIFACT_DOCKER = "DEFAULT_ARTIFACT_DOCKER";
|
|
||||||
|
|
||||||
// Docker名称
|
|
||||||
public static final String DEFAULT_ARTIFACT_DOCKER_NAME = "DEFAULT_ARTIFACT_DOCKER_NAME";
|
|
||||||
|
|
||||||
|
|
||||||
// 默认源码位置
|
|
||||||
public static final String DEFAULT_CODE_ADDRESS = "${DEFAULT_CODE_ADDRESS}";
|
|
||||||
|
|
||||||
public static final String DEFAULT_TYPE = "string";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 默认命令
|
|
||||||
*/
|
|
||||||
public static final String TEST_DEFAULT_ORDER = "mvn test";
|
|
||||||
public static final String MAVEN_DEFAULT_ORDER = "mvn clean package";
|
|
||||||
public static final String NODE_DEFAULT_ORDER = "npm install";
|
|
||||||
public static final String DOCKER_DEFAULT_ORDER = "docker image build -t default .";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 文件信息
|
|
||||||
*/
|
|
||||||
public static final String FILE_TEMP_PREFIX = "temp";
|
|
||||||
public static final String FILE_TYPE_TXT = ".txt";
|
|
||||||
public static final String FILE_TYPE_SH = ".sh";
|
|
||||||
public static final String FILE_TYPE_BAT = ".bat";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 系统任务类型
|
|
||||||
*/
|
|
||||||
|
|
||||||
// 源码应用类型
|
|
||||||
public static final String TASK_TYPE_CODE = "code";
|
|
||||||
public static final String TASK_CODE_GIT = "git";
|
|
||||||
public static final String TASK_CODE_GITLAB = "gitlab";
|
|
||||||
public static final String TASK_CODE_GITHUB = "github";
|
|
||||||
public static final String TASK_CODE_GITEE = "gitee";
|
|
||||||
public static final String TASK_CODE_SVN = "svn";
|
|
||||||
public static final String TASK_CODE_XCODE = "gitpuk";
|
|
||||||
public static final String TASK_CODE_DEFAULT_BRANCH = "master";
|
|
||||||
|
|
||||||
|
|
||||||
// 构建应用类型
|
|
||||||
public static final String TASK_TYPE_BUILD = "build";
|
|
||||||
public static final String TASK_BUILD_MAVEN = "maven";
|
|
||||||
public static final String TASK_BUILD_NODEJS = "nodejs";
|
|
||||||
|
|
||||||
public static final String TASK_BUILD_DOCKER = "build_docker";
|
|
||||||
|
|
||||||
|
|
||||||
// 测试应用类型
|
|
||||||
public static final String TASK_TYPE_TEST = "test";
|
|
||||||
public static final String TASK_TEST_MAVENTEST = "maventest";
|
|
||||||
public static final String TASK_TEST_TESTON = "testhubo";
|
|
||||||
|
|
||||||
|
|
||||||
// 部署应用类型
|
|
||||||
public static final String TASK_TYPE_DEPLOY = "deploy";
|
|
||||||
public static final String TASK_DEPLOY_LINUX = "liunx";
|
|
||||||
public static final String TASK_DEPLOY_DOCKER = "docker";
|
|
||||||
public static final String TASK_DEPLOY_K8S = "k8s";
|
|
||||||
|
|
||||||
|
|
||||||
// 推送制品应用类型
|
|
||||||
public static final String TASK_TYPE_ARTIFACT = "artifact";
|
|
||||||
public static final String TASK_ARTIFACT_MAVEN = "artifact_maven";
|
|
||||||
public static final String TASK_ARTIFACT_NODEJS = "artifact_nodejs";
|
|
||||||
public static final String TASK_ARTIFACT_DOCKER = "artifact_docker";
|
|
||||||
|
|
||||||
|
|
||||||
// 制品拉取应用类型
|
|
||||||
public static final String TASK_TYPE_PULL = "pull";
|
|
||||||
public static final String TASK_PULL_MAVEN = "pull_maven";
|
|
||||||
public static final String TASK_PULL_NODEJS = "pull_nodejs";
|
|
||||||
public static final String TASK_PULL_DOCKER = "pull_docker";
|
|
||||||
|
|
||||||
// 制品推送应用方式
|
|
||||||
public static final String TASK_ARTIFACT_XPACK = "hadess";
|
|
||||||
public static final String TASK_ARTIFACT_SSH = "ssh";
|
|
||||||
public static final String TASK_ARTIFACT_NEXUS = "nexus";
|
|
||||||
|
|
||||||
// 代码扫描应用类型
|
|
||||||
public static final String TASK_TYPE_CODESCAN = "codescan";
|
|
||||||
public static final String TASK_CODESCAN_SONAR = "sonar";
|
|
||||||
public static final String TASK_CODESCAN_SPOTBUGS = "spotbugs";
|
|
||||||
|
|
||||||
// 消息应用类型
|
|
||||||
public static final String TASK_TYPE_MESSAGE = "message";
|
|
||||||
public static final String TASK_MESSAGE_MSG = "message";
|
|
||||||
|
|
||||||
// 脚本应用类型
|
|
||||||
public static final String TASK_TYPE_SCRIPT = "script";
|
|
||||||
public static final String TASK_SCRIPT_SHELL = "shell";
|
|
||||||
public static final String TASK_SCRIPT_BAT = "bat";
|
|
||||||
|
|
||||||
|
|
||||||
//触发器
|
|
||||||
public static final String TRIGGER_SCHEDULED = "scheduled";
|
|
||||||
|
|
||||||
|
|
||||||
public static final String SIZE_TYPE_MB = "MB";
|
|
||||||
|
|
||||||
public static final int DEFAULT_SIZE = 2;
|
|
||||||
|
|
||||||
|
|
||||||
public static final String SIZE_TYPE_GB = "GB";
|
|
||||||
|
|
||||||
|
|
||||||
public static final Integer DEFAULT_CLEAN_CACHE_DAY = 7;
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
@ -0,0 +1,13 @@
|
|||||||
|
package cd.casic.ci.process.engine.constant;
|
||||||
|
|
||||||
|
public class DIYImageExecuteCommandConstant {
|
||||||
|
/**
|
||||||
|
* 机器ID
|
||||||
|
*/
|
||||||
|
public static final String MACHINE_ID ="machineId";
|
||||||
|
/**
|
||||||
|
* 人工卡点命令脚本
|
||||||
|
*/
|
||||||
|
public static final String COMMAND_SCRIPT ="commandScript";
|
||||||
|
public static final String STATUS_CODE = "statusCode";
|
||||||
|
}
|
@ -1,6 +1,11 @@
|
|||||||
package cd.casic.ci.process.engine.manager;
|
package cd.casic.ci.process.engine.manager;
|
||||||
|
|
||||||
|
import cd.casic.ci.process.engine.message.TaskRunMessage;
|
||||||
|
import cd.casic.framework.mq.redis.core.stream.AbstractRedisStreamMessageListener;
|
||||||
|
import org.springframework.context.ApplicationContextAware;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 负责监听队列,找到ContextManager获取runContext,然后实际执行
|
* 负责监听队列,找到ContextManager获取runContext,然后实际执行
|
||||||
* */
|
* */
|
||||||
public interface WorkerManager {
|
public abstract class WorkerManager extends AbstractRedisStreamMessageListener<TaskRunMessage> implements ApplicationContextAware {
|
||||||
}
|
}
|
||||||
|
@ -1,9 +1,15 @@
|
|||||||
package cd.casic.ci.process.engine.manager.impl;
|
package cd.casic.ci.process.engine.manager.impl;
|
||||||
|
|
||||||
import cd.casic.ci.common.pipeline.annotation.Plugin;
|
import cd.casic.ci.common.pipeline.annotation.Plugin;
|
||||||
|
import cd.casic.ci.process.engine.enums.ContextStateEnum;
|
||||||
|
import cd.casic.ci.process.engine.manager.RunContextManager;
|
||||||
|
import cd.casic.ci.process.engine.manager.WorkerManager;
|
||||||
import cd.casic.ci.process.engine.message.TaskRunMessage;
|
import cd.casic.ci.process.engine.message.TaskRunMessage;
|
||||||
|
import cd.casic.ci.process.engine.runContext.BaseRunContext;
|
||||||
import cd.casic.ci.process.engine.worker.BaseWorker;
|
import cd.casic.ci.process.engine.worker.BaseWorker;
|
||||||
import cd.casic.ci.process.process.dataObject.task.PipTask;
|
import cd.casic.ci.process.process.dataObject.task.PipTask;
|
||||||
|
import cd.casic.framework.commons.exception.ServiceException;
|
||||||
|
import cd.casic.framework.commons.exception.enums.GlobalErrorCodeConstants;
|
||||||
import cd.casic.framework.mq.redis.core.stream.AbstractRedisStreamMessageListener;
|
import cd.casic.framework.mq.redis.core.stream.AbstractRedisStreamMessageListener;
|
||||||
import jakarta.annotation.PostConstruct;
|
import jakarta.annotation.PostConstruct;
|
||||||
import jakarta.annotation.Resource;
|
import jakarta.annotation.Resource;
|
||||||
@ -27,13 +33,15 @@ import java.util.Set;
|
|||||||
|
|
||||||
@Component
|
@Component
|
||||||
@Slf4j
|
@Slf4j
|
||||||
public class DefaultWorkerManager extends AbstractRedisStreamMessageListener<TaskRunMessage> implements ApplicationContextAware {
|
public class DefaultWorkerManager extends WorkerManager {
|
||||||
private static final String basePackage = "cd.casic.ci.process.engine.worker";
|
private static final String basePackage = "cd.casic.ci.process.engine.worker";
|
||||||
private Set<BeanDefinition> candidates;
|
private Set<BeanDefinition> candidates;
|
||||||
private ApplicationContext applicationContext;
|
private ApplicationContext applicationContext;
|
||||||
private Map<String,BaseWorker> taskTypeWorkerMap = null;
|
private Map<String,BaseWorker> taskTypeWorkerMap = null;
|
||||||
@Resource
|
@Resource
|
||||||
private ThreadPoolTaskExecutor workerExecutor;
|
private ThreadPoolTaskExecutor workerExecutor;
|
||||||
|
@Resource
|
||||||
|
private RunContextManager contextManager;
|
||||||
|
|
||||||
private void setTaskTypeWorker(Map<String, BaseWorker> taskTypeWorker) {
|
private void setTaskTypeWorker(Map<String, BaseWorker> taskTypeWorker) {
|
||||||
this.taskTypeWorkerMap = taskTypeWorker;
|
this.taskTypeWorkerMap = taskTypeWorker;
|
||||||
@ -70,13 +78,22 @@ public class DefaultWorkerManager extends AbstractRedisStreamMessageListener<Tas
|
|||||||
log.info("===============接收到消息================");
|
log.info("===============接收到消息================");
|
||||||
try {
|
try {
|
||||||
PipTask task = message.getTask();
|
PipTask task = message.getTask();
|
||||||
|
BaseRunContext context = contextManager.getContext(task.getId());
|
||||||
|
if (context==null) {
|
||||||
|
throw new ServiceException(GlobalErrorCodeConstants.PIPELINE_ERROR.getCode(),"未找到对应上下文");
|
||||||
|
}
|
||||||
|
ContextStateEnum byCode = ContextStateEnum.getByCode(context.getState().get());
|
||||||
|
if (ContextStateEnum.SUSPEND.equals(byCode)) {
|
||||||
|
throw new ServiceException(GlobalErrorCodeConstants.PIPELINE_ERROR.getCode(),"当前task为挂起状态,进入队列重新执行");
|
||||||
|
}
|
||||||
String taskType = task.getTaskType();
|
String taskType = task.getTaskType();
|
||||||
BaseWorker baseWorker = taskTypeWorkerMap.get(taskType);
|
BaseWorker baseWorker = taskTypeWorkerMap.get(taskType);
|
||||||
baseWorker.setContextKey(task.getId());
|
baseWorker.setContextKey(task.getId());
|
||||||
workerExecutor.execute(baseWorker);
|
workerExecutor.execute(baseWorker);
|
||||||
}catch (Exception e){
|
}catch (Exception e){
|
||||||
// TODO 后期可以考虑专门整一个队列
|
|
||||||
log.error("=======================workerManager:",e);
|
log.error("=======================workerManager:",e);
|
||||||
|
// 这一块报错应该只可能是因为,找不到对应worker 线程池的拒绝策略 抛出错误等待redis重新消费
|
||||||
|
throw new ServiceException(GlobalErrorCodeConstants.PIPELINE_ERROR.getCode(),"");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,32 +1,44 @@
|
|||||||
package cd.casic.ci.process.engine.worker;
|
package cd.casic.ci.process.engine.worker;
|
||||||
|
|
||||||
import cd.casic.ci.common.pipeline.annotation.Plugin;
|
|
||||||
|
import cd.casic.ci.process.constant.CommandConstant;
|
||||||
import cd.casic.ci.process.engine.enums.ContextStateEnum;
|
import cd.casic.ci.process.engine.enums.ContextStateEnum;
|
||||||
import cd.casic.ci.process.engine.manager.RunContextManager;
|
import cd.casic.ci.process.engine.manager.RunContextManager;
|
||||||
import cd.casic.ci.process.engine.runContext.BaseRunContext;
|
import cd.casic.ci.process.engine.runContext.BaseRunContext;
|
||||||
import cd.casic.ci.process.engine.runContext.PipelineRunContext;
|
|
||||||
import cd.casic.ci.process.engine.runContext.TaskRunContext;
|
import cd.casic.ci.process.engine.runContext.TaskRunContext;
|
||||||
import cd.casic.framework.commons.exception.ServiceException;
|
import cd.casic.ci.process.enums.MachineSystemEnum;
|
||||||
import cd.casic.framework.commons.exception.enums.GlobalErrorCodeConstants;
|
import cd.casic.ci.process.process.dataObject.machine.MachineInfo;
|
||||||
import jakarta.annotation.PostConstruct;
|
import cd.casic.ci.process.process.service.machine.MachineInfoService;
|
||||||
|
|
||||||
|
import cd.casic.ci.process.ssh.SshClient;
|
||||||
|
import cd.casic.ci.process.ssh.SshClientFactory;
|
||||||
|
import cd.casic.ci.process.ssh.WinRMHelper;
|
||||||
import jakarta.annotation.Resource;
|
import jakarta.annotation.Resource;
|
||||||
import jodd.util.StringUtil;
|
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
|
||||||
@Data
|
@Data
|
||||||
|
@Slf4j
|
||||||
public abstract class BaseWorker implements Runnable{
|
public abstract class BaseWorker implements Runnable{
|
||||||
// 一些属性
|
// 一些属性
|
||||||
@Resource
|
@Resource
|
||||||
private RunContextManager contextManager;
|
private RunContextManager contextManager;
|
||||||
private String contextKey;
|
private String contextKey;
|
||||||
|
@Resource
|
||||||
|
public MachineInfoService machineInfoService;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
if (StringUtils.isEmpty(contextKey)) {
|
if (StringUtils.isEmpty(contextKey)) {
|
||||||
throw new ServiceException(GlobalErrorCodeConstants.PIPELINE_ERROR.getCode(),"请设置当前worker的contextKey");
|
log.error("请设置当前worker的contextKey");
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
doWorker(contextKey);
|
doWorker(contextKey);
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,75 @@
|
|||||||
|
package cd.casic.ci.process.engine.worker;
|
||||||
|
|
||||||
|
import cd.casic.ci.common.pipeline.annotation.Plugin;
|
||||||
|
import cd.casic.ci.process.engine.constant.DIYImageExecuteCommandConstant;
|
||||||
|
import cd.casic.ci.process.engine.runContext.TaskRunContext;
|
||||||
|
import cd.casic.ci.process.process.dataObject.base.PipBaseElement;
|
||||||
|
import cd.casic.ci.process.process.dataObject.machine.MachineInfo;
|
||||||
|
import cd.casic.ci.process.process.dataObject.task.PipTask;
|
||||||
|
import cd.casic.framework.commons.exception.ServiceException;
|
||||||
|
import cd.casic.framework.commons.exception.enums.GlobalErrorCodeConstants;
|
||||||
|
import com.alibaba.fastjson.JSON;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 自定义镜像执行命令
|
||||||
|
*
|
||||||
|
* @author herenbin
|
||||||
|
* @date 2022-11-08 9:59
|
||||||
|
*/
|
||||||
|
@Slf4j
|
||||||
|
@Plugin(taskType = "CUSTOM_IMAGE_EXECUTION_COMMAND")
|
||||||
|
public class DIYImageExecuteCommandWorker extends SshWorker {
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void execute(TaskRunContext context) {
|
||||||
|
int statusCode = -1;
|
||||||
|
AtomicInteger state = context.getState();
|
||||||
|
if (context.getContextDef() instanceof PipTask taskDef) {
|
||||||
|
log.info(taskDef.getTaskName());
|
||||||
|
Map<String, Object> taskProperties = taskDef.getTaskProperties();
|
||||||
|
Object commandScriptObj = taskProperties.get(DIYImageExecuteCommandConstant.COMMAND_SCRIPT);
|
||||||
|
Object machineIdObj = taskProperties.get(DIYImageExecuteCommandConstant.MACHINE_ID);
|
||||||
|
String commandScript = commandScriptObj instanceof String ? ((String) commandScriptObj) : null;
|
||||||
|
Long machineId = null;
|
||||||
|
try {
|
||||||
|
machineId=Long.valueOf(String.valueOf(machineIdObj));
|
||||||
|
} catch (NumberFormatException e) {
|
||||||
|
log.error("缺少参数:{}",DIYImageExecuteCommandConstant.MACHINE_ID);
|
||||||
|
}
|
||||||
|
if (StringUtils.isEmpty(commandScript) ||machineIdObj == null) {
|
||||||
|
throw new ServiceException(GlobalErrorCodeConstants.PIPELINE_ERROR.getCode(),"缺少参数");
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
//将节点的配置信息反编译成对象
|
||||||
|
log.info("构建脚本" + commandScript);
|
||||||
|
|
||||||
|
//如果machineId为0,则说明该节点没有配置机器,则使用开始节点的机器
|
||||||
|
|
||||||
|
//获取机器
|
||||||
|
MachineInfo machineInfoDO = this.getMachineInfoService().getById(machineId);
|
||||||
|
statusCode = shell(machineInfoDO,
|
||||||
|
"echo \"自定义镜像执行命令\"",
|
||||||
|
commandScript
|
||||||
|
);
|
||||||
|
} catch (Exception e) {
|
||||||
|
String errorMessage = "该节点配置信息为空,请先配置该节点信息" + "\r\n";
|
||||||
|
// errorHandle(e, errorMessage);
|
||||||
|
}
|
||||||
|
if (statusCode == 0) {
|
||||||
|
// log.info("{}节点执行完成", getName());
|
||||||
|
} else {
|
||||||
|
// log.error("{}节点执行失败", getName());
|
||||||
|
}
|
||||||
|
context.getLocalVariables().put(DIYImageExecuteCommandConstant.STATUS_CODE,statusCode);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,81 @@
|
|||||||
|
package cd.casic.ci.process.engine.worker;
|
||||||
|
|
||||||
|
import cd.casic.ci.process.constant.CommandConstant;
|
||||||
|
import cd.casic.ci.process.enums.MachineSystemEnum;
|
||||||
|
import cd.casic.ci.process.process.dataObject.machine.MachineInfo;
|
||||||
|
import cd.casic.ci.process.ssh.SshClient;
|
||||||
|
import cd.casic.ci.process.ssh.SshClientFactory;
|
||||||
|
import cd.casic.ci.process.ssh.WinRMHelper;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
|
@Slf4j
|
||||||
|
public abstract class SshWorker extends BaseWorker{
|
||||||
|
/**
|
||||||
|
* 执行shell命令
|
||||||
|
* @param machineInfo 机器
|
||||||
|
* @param commands 命令
|
||||||
|
* @return 0 成功;其他值 失败
|
||||||
|
*/
|
||||||
|
public int shell(MachineInfo machineInfo, String... commands) {
|
||||||
|
List<String> commandList = Arrays.asList(commands);
|
||||||
|
if(MachineSystemEnum.WINDOWS.getSystem().equals(machineInfo.getOsSystem())){
|
||||||
|
return powerShell(machineInfo, commandList);
|
||||||
|
}
|
||||||
|
// NodeLogger nodeLogger = nodeLoggerThreadLocal.get();
|
||||||
|
int statusCode = -1;
|
||||||
|
// String loggerUuid = nodeLogger.getLoggerUuid();
|
||||||
|
// String nodeUuid = nodeLogger.getNodeUuid();
|
||||||
|
//loggerUuid得转换成String类型,才能生成key,然后才能通过websocket实时推送节点执行日志
|
||||||
|
SshClient ssh = null;
|
||||||
|
try {
|
||||||
|
ssh = SshClientFactory.createSsh(machineInfo);
|
||||||
|
//执行命令,并且把命令的执行回传到前端
|
||||||
|
statusCode = ssh.execNew(commandList, var -> {
|
||||||
|
// TODO 记录日志
|
||||||
|
// loggerService.sendMessage(key, var);
|
||||||
|
// nodeLogger.append(var);
|
||||||
|
});
|
||||||
|
log.info("exit-status: " + statusCode);
|
||||||
|
//主动释放当前socket连接
|
||||||
|
// loggerService.close(key);
|
||||||
|
} catch (Exception e) {
|
||||||
|
String errorMessage = "与机器建立SSH连接出错" + CommandConstant.ENTER;
|
||||||
|
// errorHandle(e, errorMessage);
|
||||||
|
} finally {
|
||||||
|
if(ssh!=null) {
|
||||||
|
ssh.disconnect();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return statusCode;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* 执行shell命令
|
||||||
|
* @param machineInfo 机器
|
||||||
|
* @param commandList 命令
|
||||||
|
* @return 0 成功;其他值 失败
|
||||||
|
*/
|
||||||
|
public int powerShell(MachineInfo machineInfo, List<String> commandList) {
|
||||||
|
int statusCode = -1;
|
||||||
|
//loggerUuid得转换成String类型,才能生成key,然后才能通过websocket实时推送节点执行日志
|
||||||
|
|
||||||
|
try {
|
||||||
|
WinRMHelper winRmHelper = new WinRMHelper(machineInfo);
|
||||||
|
statusCode = winRmHelper.executePs(commandList, var ->{
|
||||||
|
log.info("================================================================" + var);
|
||||||
|
// TODO 记录日志
|
||||||
|
// loggerService.sendMessage(key, var);
|
||||||
|
// nodeLogger.append(var);
|
||||||
|
});
|
||||||
|
|
||||||
|
log.info("exit-status: " + statusCode);
|
||||||
|
//主动释放当前socket连接
|
||||||
|
|
||||||
|
} catch (Exception e) {
|
||||||
|
String errorMessage = "与Window机器建立winrm连接出错" + CommandConstant.ENTER;
|
||||||
|
// errorHandle(e, errorMessage);
|
||||||
|
}
|
||||||
|
return statusCode;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,27 @@
|
|||||||
|
package cd.casic.ci.process.enums;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 枚举
|
||||||
|
* @author herenbin
|
||||||
|
* @date 2023/6/25
|
||||||
|
*/
|
||||||
|
public enum MachineSystemEnum {
|
||||||
|
/**
|
||||||
|
* Linux
|
||||||
|
*/
|
||||||
|
LINUX("Linux"),
|
||||||
|
/**
|
||||||
|
* Windows
|
||||||
|
*/
|
||||||
|
WINDOWS("Windows");
|
||||||
|
|
||||||
|
private final String system;
|
||||||
|
|
||||||
|
MachineSystemEnum(String system) {
|
||||||
|
this.system = system;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getSystem() {
|
||||||
|
return system;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,15 @@
|
|||||||
|
package cd.casic.ci.process.process.dal.machine;
|
||||||
|
|
||||||
|
|
||||||
|
import cd.casic.ci.process.process.dataObject.machine.MachineInfo;
|
||||||
|
import cd.casic.framework.mybatis.core.mapper.BaseMapperX;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 机器信息表
|
||||||
|
*
|
||||||
|
* @author herenbin
|
||||||
|
* @date 2022-09-27 10:10:37
|
||||||
|
*/
|
||||||
|
public interface MachineInfoDao extends BaseMapperX<MachineInfo> {
|
||||||
|
}
|
@ -4,7 +4,9 @@ import cd.casic.framework.commons.dataobject.BaseDO;
|
|||||||
import com.baomidou.mybatisplus.annotation.IdType;
|
import com.baomidou.mybatisplus.annotation.IdType;
|
||||||
import com.baomidou.mybatisplus.annotation.TableId;
|
import com.baomidou.mybatisplus.annotation.TableId;
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
|
import lombok.EqualsAndHashCode;
|
||||||
|
|
||||||
|
@EqualsAndHashCode(callSuper = true)
|
||||||
@Data
|
@Data
|
||||||
public class PipBaseElement extends BaseDO {
|
public class PipBaseElement extends BaseDO {
|
||||||
/**
|
/**
|
||||||
|
@ -0,0 +1,84 @@
|
|||||||
|
package cd.casic.ci.process.process.dataObject.machine;
|
||||||
|
|
||||||
|
|
||||||
|
import cd.casic.framework.commons.dataobject.BaseDO;
|
||||||
|
import com.baomidou.mybatisplus.annotation.IdType;
|
||||||
|
import com.baomidou.mybatisplus.annotation.TableField;
|
||||||
|
import com.baomidou.mybatisplus.annotation.TableId;
|
||||||
|
import com.baomidou.mybatisplus.annotation.TableName;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.EqualsAndHashCode;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 机器信息表
|
||||||
|
*
|
||||||
|
* @author herenbin
|
||||||
|
* @date 2022-09-27 10:10:37
|
||||||
|
*/
|
||||||
|
@EqualsAndHashCode(callSuper = true)
|
||||||
|
@Data
|
||||||
|
@TableName("machine_info")
|
||||||
|
public class MachineInfo extends BaseDO {
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 机器认证方式 1: 账号认证 2: key认证
|
||||||
|
*/
|
||||||
|
private String authType;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 机器描述
|
||||||
|
*/
|
||||||
|
private String description;
|
||||||
|
/**
|
||||||
|
* id
|
||||||
|
*/
|
||||||
|
@TableId(type = IdType.ASSIGN_ID)
|
||||||
|
private Long id;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 主机ip
|
||||||
|
*/
|
||||||
|
private String machineHost;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 机器名称
|
||||||
|
*/
|
||||||
|
private String machineName;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 机器状态 1有效 2无效
|
||||||
|
*/
|
||||||
|
private String machineStatus;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 机器唯一标识
|
||||||
|
*/
|
||||||
|
private String machineTag;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 机器密码
|
||||||
|
*/
|
||||||
|
private String password;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 代理id
|
||||||
|
*/
|
||||||
|
private Long proxyId;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ssh端口
|
||||||
|
*/
|
||||||
|
private Integer sshPort;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 机器账号
|
||||||
|
*/
|
||||||
|
private String username;
|
||||||
|
/**
|
||||||
|
* 机器账号
|
||||||
|
*/
|
||||||
|
@TableField("os_system")
|
||||||
|
private String osSystem;
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,17 @@
|
|||||||
|
package cd.casic.ci.process.process.service.machine;
|
||||||
|
|
||||||
|
|
||||||
|
import cd.casic.ci.process.process.dataObject.machine.MachineInfo;
|
||||||
|
import com.baomidou.mybatisplus.extension.service.IService;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 机器信息表service接口
|
||||||
|
*
|
||||||
|
* @author herenbin
|
||||||
|
* @date 2022-09-27 10:25:29
|
||||||
|
*/
|
||||||
|
public interface MachineInfoService extends IService<MachineInfo> {
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,25 @@
|
|||||||
|
package cd.casic.ci.process.process.service.machine.impl;
|
||||||
|
|
||||||
|
|
||||||
|
import cd.casic.ci.process.process.dal.machine.MachineInfoDao;
|
||||||
|
import cd.casic.ci.process.process.dataObject.machine.MachineInfo;
|
||||||
|
import cd.casic.ci.process.process.service.machine.MachineInfoService;
|
||||||
|
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 机器信息表service接口实现类
|
||||||
|
* @author herenbin
|
||||||
|
* @date 2022-09-27 10:25:29
|
||||||
|
*/
|
||||||
|
@Slf4j
|
||||||
|
@Service
|
||||||
|
public class MachineInfoServiceImpl extends ServiceImpl<MachineInfoDao, MachineInfo> implements MachineInfoService {
|
||||||
|
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,19 @@
|
|||||||
|
package cd.casic.ci.process.ssh;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @ClassName ops
|
||||||
|
* @Descriptions ssh 命令回调
|
||||||
|
* @Author mianbin
|
||||||
|
* @Date 2022/6/22 15:26
|
||||||
|
* @Version 1.0
|
||||||
|
**/
|
||||||
|
@FunctionalInterface
|
||||||
|
public interface ExecCallback {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 回调执行方法
|
||||||
|
* @param out 命令执行的输出
|
||||||
|
*/
|
||||||
|
void callback(String out);
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,56 @@
|
|||||||
|
package cd.casic.ci.process.ssh;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @ClassName ops
|
||||||
|
* @Description ssh 命令 , 不记录
|
||||||
|
* @Author mianbin
|
||||||
|
* @Date 2022/6/22 15:27
|
||||||
|
* @Version 1.0
|
||||||
|
**/
|
||||||
|
public interface SshClient {
|
||||||
|
/**
|
||||||
|
* 执行ssh 命令
|
||||||
|
*
|
||||||
|
* @param command 要执行的命令,多个命令通过";"号隔开
|
||||||
|
* @param execCallback 命令执行过程中的回调器
|
||||||
|
* @return 执行成功返回0,失败返回1
|
||||||
|
* @throws IOException
|
||||||
|
*/
|
||||||
|
int exec(String command, ExecCallback execCallback) throws IOException;
|
||||||
|
/**
|
||||||
|
* 执行ssh 命令
|
||||||
|
*
|
||||||
|
* @param commands 要执行的命令,多个命令通过";"号隔开
|
||||||
|
* @param execCallback 命令执行过程中的回调器
|
||||||
|
* @return 执行成功返回0,失败返回1
|
||||||
|
* @throws IOException
|
||||||
|
*/
|
||||||
|
int execNew(List<String> commands, ExecCallback execCallback) throws IOException;
|
||||||
|
/**
|
||||||
|
* 执行ssh 命令
|
||||||
|
*
|
||||||
|
* @param commands 要执行的命令,多个命令通过";"号隔开
|
||||||
|
* @param execCallback 命令执行过程中的回调器
|
||||||
|
* @return 执行成功返回0,失败返回1
|
||||||
|
* @throws IOException
|
||||||
|
*/
|
||||||
|
int parallelDebugExec(List<String> commands, ExecCallback execCallback) throws IOException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 执行多行命令
|
||||||
|
*
|
||||||
|
* @param command
|
||||||
|
* @param execCallback
|
||||||
|
* @return
|
||||||
|
* @throws IOException
|
||||||
|
*/
|
||||||
|
int exec(List<String> command, ExecCallback execCallback) throws IOException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 断开客户端连接
|
||||||
|
*/
|
||||||
|
void disconnect();
|
||||||
|
}
|
@ -0,0 +1,45 @@
|
|||||||
|
package cd.casic.ci.process.ssh;
|
||||||
|
|
||||||
|
|
||||||
|
import cd.casic.ci.process.process.dataObject.machine.MachineInfo;
|
||||||
|
import com.jcraft.jsch.JSchException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @ClassName ops
|
||||||
|
* @Description ssh 工厂 ,这里主要使用的是这个,做个封装
|
||||||
|
* @Author mianbin
|
||||||
|
* @Date 2022/6/22 15:40
|
||||||
|
* @Version 1.0
|
||||||
|
**/
|
||||||
|
public class SshClientFactory {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 基于用户名密码认证创建客户端
|
||||||
|
* @param username 用户名
|
||||||
|
* @param password 密码
|
||||||
|
* @param host 主机ip
|
||||||
|
* @param port ssh端口号
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public static SshClient createSsh(String username, String password, String host, int port) throws Exception {
|
||||||
|
try {
|
||||||
|
return new SshCommand(username, password, host, port);
|
||||||
|
} catch (JSchException e) {
|
||||||
|
throw new Exception("创建ssh客户端出错", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* 用户名密码或私钥连接客户端
|
||||||
|
* @param properties 配置参数
|
||||||
|
* 主要包含 用户名 密码 私钥 主机ip 端口 认证类型
|
||||||
|
*
|
||||||
|
* */
|
||||||
|
public static SshClient createSsh(MachineInfo properties) throws Exception {
|
||||||
|
try {
|
||||||
|
return new SshCommand(properties);
|
||||||
|
} catch (JSchException e) {
|
||||||
|
throw new Exception("创建ssh客户端出错", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,253 @@
|
|||||||
|
package cd.casic.ci.process.ssh;
|
||||||
|
|
||||||
|
|
||||||
|
import cd.casic.ci.process.constant.CommandConstant;
|
||||||
|
import cd.casic.ci.process.process.dataObject.machine.MachineInfo;
|
||||||
|
import cd.casic.ci.process.util.ChannelShellUtil;
|
||||||
|
import cd.casic.ci.process.util.CryptogramUtil;
|
||||||
|
import cn.hutool.extra.ssh.JschUtil;
|
||||||
|
import com.jcraft.jsch.ChannelShell;
|
||||||
|
import com.jcraft.jsch.JSchException;
|
||||||
|
import com.jcraft.jsch.Session;
|
||||||
|
|
||||||
|
import java.io.*;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @ClassName ops
|
||||||
|
* @Description ssh 的命令操作的集合 ,这里是做个统一,其他pipleline 和web ternminal 的实现方式不同
|
||||||
|
* @Author mianbin
|
||||||
|
* @Date 2022/6/22 15:43
|
||||||
|
* @Version 1.0
|
||||||
|
**/
|
||||||
|
public class SshCommand implements SshClient {
|
||||||
|
|
||||||
|
private static final int BUFF_SIZE = 1024 * 8; // 8KB
|
||||||
|
|
||||||
|
/**
|
||||||
|
* jsch session
|
||||||
|
*/
|
||||||
|
Session session;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 基于用户名密码认证的构造器
|
||||||
|
*
|
||||||
|
* @param username 用户名
|
||||||
|
* @param password 密码
|
||||||
|
* @param host 主机ip
|
||||||
|
* @param port ssh端口号
|
||||||
|
* @throws JSchException
|
||||||
|
*/
|
||||||
|
|
||||||
|
public SshCommand(String username, String password, String host, int port) throws JSchException {
|
||||||
|
this.session = JschUtil.createSession(host, port, username, password);
|
||||||
|
this.session.setConfig("PreferredAuthentications", "password");
|
||||||
|
this.session.setConfig("StrictHostKeyChecking", "no");
|
||||||
|
this.session.setPassword(password);
|
||||||
|
this.session.connect();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 基于用户名密码认证的构造器
|
||||||
|
* Machine实体类
|
||||||
|
*
|
||||||
|
* @throws JSchException
|
||||||
|
*/
|
||||||
|
public SshCommand(MachineInfo machine) throws JSchException {
|
||||||
|
// 根据用户名,主机ip,端口获取一个Session对象
|
||||||
|
String decrypt = CryptogramUtil.doDecrypt(machine.getPassword());
|
||||||
|
this.session = JschUtil.createSession(machine.getMachineHost(), machine.getSshPort(), machine.getUsername(), decrypt);
|
||||||
|
this.session.setConfig("PreferredAuthentications", "password");
|
||||||
|
this.session.setConfig("StrictHostKeyChecking", "no");
|
||||||
|
// 通过Session建立链接
|
||||||
|
this.session.connect();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void disconnect() {
|
||||||
|
if(session!=null) {
|
||||||
|
session.disconnect();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int exec(String command, ExecCallback execCallback) throws IOException {
|
||||||
|
|
||||||
|
ChannelShell channel = null;
|
||||||
|
try {
|
||||||
|
channel = (ChannelShell) session.openChannel("shell");
|
||||||
|
channel.connect();
|
||||||
|
} catch (JSchException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
|
||||||
|
InputStream inputStream = channel.getInputStream();
|
||||||
|
OutputStream outputStream = channel.getOutputStream();
|
||||||
|
PrintWriter printWriter = new PrintWriter(outputStream);
|
||||||
|
|
||||||
|
printWriter.println(command);
|
||||||
|
|
||||||
|
printWriter.println("exit");
|
||||||
|
printWriter.flush();
|
||||||
|
BufferedReader in = new BufferedReader(new InputStreamReader(inputStream));
|
||||||
|
|
||||||
|
String msg = null;
|
||||||
|
while ((msg = in.readLine()) != null) {
|
||||||
|
execCallback.callback(msg);
|
||||||
|
}
|
||||||
|
int result = channel.getExitStatus();
|
||||||
|
|
||||||
|
in.close();
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int exec(List<String> commands, ExecCallback execCallback) throws IOException {
|
||||||
|
|
||||||
|
ChannelShell channel = null;
|
||||||
|
try {
|
||||||
|
channel = (ChannelShell) session.openChannel("shell");
|
||||||
|
channel.connect();
|
||||||
|
} catch (JSchException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
|
||||||
|
assert channel != null;
|
||||||
|
ChannelShellUtil.setDefault(channel);
|
||||||
|
InputStream inputStream = channel.getInputStream();
|
||||||
|
OutputStream outputStream = channel.getOutputStream();
|
||||||
|
InputStreamReader isr = new InputStreamReader(inputStream);
|
||||||
|
BufferedReader br = new BufferedReader(isr, BUFF_SIZE);
|
||||||
|
PrintStream commander = new PrintStream(outputStream, true);
|
||||||
|
|
||||||
|
for (String command : commands) {
|
||||||
|
commander.append(command).append(CommandConstant.ENTER);
|
||||||
|
}
|
||||||
|
commander.append("exit" + CommandConstant.ENTER);
|
||||||
|
|
||||||
|
try {
|
||||||
|
char[] buff = new char[BUFF_SIZE];
|
||||||
|
int read;
|
||||||
|
while ((read = br.read(buff)) != -1) {
|
||||||
|
String output = String.valueOf(buff, 0, read);
|
||||||
|
execCallback.callback( output + CommandConstant.ENTER);
|
||||||
|
TimeUnit.MILLISECONDS.sleep(10L);
|
||||||
|
// 检测输出,如果满足跳出条件则退出循环
|
||||||
|
if (output.contains("# exit")||output.contains("$ exit")) {
|
||||||
|
commander.append("exit" + CommandConstant.ENTER);
|
||||||
|
TimeUnit.MILLISECONDS.sleep(1000L);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (Exception ex) {
|
||||||
|
ex.printStackTrace();
|
||||||
|
}
|
||||||
|
inputStream.close();
|
||||||
|
outputStream.close();
|
||||||
|
channel.disconnect();
|
||||||
|
int result = channel.getExitStatus();
|
||||||
|
commander.close();
|
||||||
|
isr.close();
|
||||||
|
br.close();
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int execNew(List<String> commands, ExecCallback execCallback) throws IOException {
|
||||||
|
|
||||||
|
ChannelShell channel = null;
|
||||||
|
try {
|
||||||
|
channel = (ChannelShell) session.openChannel("shell");
|
||||||
|
channel.connect();
|
||||||
|
} catch (JSchException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
|
||||||
|
assert channel != null;
|
||||||
|
ChannelShellUtil.setDefault(channel);
|
||||||
|
InputStream inputStream = channel.getInputStream();
|
||||||
|
OutputStream outputStream = channel.getOutputStream();
|
||||||
|
InputStreamReader isr = new InputStreamReader(inputStream);
|
||||||
|
BufferedReader br = new BufferedReader(isr, BUFF_SIZE);
|
||||||
|
PrintStream commander = new PrintStream(outputStream, true);
|
||||||
|
|
||||||
|
for (String command : commands) {
|
||||||
|
commander.append(command).append(CommandConstant.ENTER);
|
||||||
|
}
|
||||||
|
commander.append("exit" + CommandConstant.ENTER);
|
||||||
|
|
||||||
|
try {
|
||||||
|
char[] buff = new char[BUFF_SIZE];
|
||||||
|
int read;
|
||||||
|
while ((read = br.read(buff)) != -1) {
|
||||||
|
execCallback.callback(String.valueOf(buff, 0, read) + CommandConstant.ENTER);
|
||||||
|
TimeUnit.MILLISECONDS.sleep(10L);
|
||||||
|
}
|
||||||
|
} catch (Exception ex) {
|
||||||
|
ex.printStackTrace();
|
||||||
|
}
|
||||||
|
|
||||||
|
inputStream.close();
|
||||||
|
outputStream.close();
|
||||||
|
channel.disconnect();
|
||||||
|
int result = channel.getExitStatus();
|
||||||
|
commander.close();
|
||||||
|
isr.close();
|
||||||
|
br.close();
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
@Override
|
||||||
|
public int parallelDebugExec(List<String> commands, ExecCallback execCallback) throws IOException {
|
||||||
|
|
||||||
|
ChannelShell channel = null;
|
||||||
|
try {
|
||||||
|
channel = (ChannelShell) session.openChannel("shell");
|
||||||
|
channel.connect();
|
||||||
|
} catch (JSchException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
|
||||||
|
assert channel != null;
|
||||||
|
ChannelShellUtil.setDefault(channel);
|
||||||
|
InputStream inputStream = channel.getInputStream();
|
||||||
|
OutputStream outputStream = channel.getOutputStream();
|
||||||
|
InputStreamReader isr = new InputStreamReader(inputStream);
|
||||||
|
BufferedReader br = new BufferedReader(isr, BUFF_SIZE);
|
||||||
|
PrintStream commander = new PrintStream(outputStream, true);
|
||||||
|
|
||||||
|
for (String command : commands) {
|
||||||
|
commander.append(command).append(CommandConstant.ENTER);
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
char[] buff = new char[BUFF_SIZE];
|
||||||
|
int read;
|
||||||
|
while ((read = br.read(buff)) != -1) {
|
||||||
|
String msg = String.valueOf(buff, 0, read);
|
||||||
|
execCallback.callback(msg + CommandConstant.ENTER);
|
||||||
|
TimeUnit.MILLISECONDS.sleep(20L);
|
||||||
|
if(msg.endsWith("$ ") || msg.endsWith("# ")) {
|
||||||
|
commander.append("exit").append(CommandConstant.ENTER);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (Exception ex) {
|
||||||
|
ex.printStackTrace();
|
||||||
|
}
|
||||||
|
int result = channel.getExitStatus();
|
||||||
|
|
||||||
|
inputStream.close();
|
||||||
|
outputStream.close();
|
||||||
|
channel.disconnect();
|
||||||
|
commander.close();
|
||||||
|
isr.close();
|
||||||
|
br.close();
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,168 @@
|
|||||||
|
package cd.casic.ci.process.ssh;
|
||||||
|
|
||||||
|
import cd.casic.ci.process.process.dataObject.machine.MachineInfo;
|
||||||
|
import cd.casic.ci.process.util.CryptogramUtil;
|
||||||
|
import io.cloudsoft.winrm4j.client.WinRmClientContext;
|
||||||
|
import io.cloudsoft.winrm4j.winrm.WinRmTool;
|
||||||
|
import io.cloudsoft.winrm4j.winrm.WinRmToolResponse;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
|
||||||
|
import java.io.*;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author herenbin
|
||||||
|
* @date 2023/6/26
|
||||||
|
*/
|
||||||
|
@Slf4j
|
||||||
|
public class WinRMHelper {
|
||||||
|
|
||||||
|
private final String ip;
|
||||||
|
|
||||||
|
private final String username;
|
||||||
|
|
||||||
|
private final String password;
|
||||||
|
|
||||||
|
public static final int DEFAULT_PORT = WinRmTool.DEFAULT_WINRM_PORT;
|
||||||
|
|
||||||
|
public WinRMHelper(final String ip, final String username, final String password) {
|
||||||
|
this.ip = ip;
|
||||||
|
this.username = username;
|
||||||
|
this.password = password;
|
||||||
|
}
|
||||||
|
|
||||||
|
public WinRMHelper(MachineInfo machineInfo) {
|
||||||
|
this.ip = machineInfo.getMachineHost();
|
||||||
|
this.username = machineInfo.getUsername();
|
||||||
|
this.password = CryptogramUtil.doDecrypt(machineInfo.getPassword());
|
||||||
|
}
|
||||||
|
|
||||||
|
public int execute(final String command, ExecCallback execCallback) {
|
||||||
|
WinRmClientContext context = WinRmClientContext.newInstance();
|
||||||
|
try {
|
||||||
|
WinRmTool tool = WinRmTool.Builder.builder(ip, username, password)
|
||||||
|
.port(DEFAULT_PORT).useHttps(false).context(context).build();
|
||||||
|
tool.setOperationTimeout(5000L);
|
||||||
|
WinRmToolResponse resp = tool.executeCommand(command);
|
||||||
|
int statusCode = resp.getStatusCode();
|
||||||
|
execCallback.callback(resp.getStdOut());
|
||||||
|
String stdErr = resp.getStdErr();
|
||||||
|
if (StringUtils.isNotEmpty(stdErr)) {
|
||||||
|
execCallback.callback(stdErr);
|
||||||
|
}
|
||||||
|
return statusCode;
|
||||||
|
} finally {
|
||||||
|
context.shutdown();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public int execute(final List<String> commandList, ExecCallback execCallback) {
|
||||||
|
WinRmClientContext context = WinRmClientContext.newInstance();
|
||||||
|
WinRmTool tool = WinRmTool.Builder.builder(ip, username, password)
|
||||||
|
.port(DEFAULT_PORT).useHttps(false).context(context).build();
|
||||||
|
tool.setOperationTimeout(5000L);
|
||||||
|
|
||||||
|
WinRmToolResponse resp = tool.executeCommand(commandList);
|
||||||
|
|
||||||
|
StringReader reader = new StringReader(resp.getStdOut() + resp.getStdErr());
|
||||||
|
try {
|
||||||
|
char[] buffer = new char[1024];
|
||||||
|
int output;
|
||||||
|
while ((output = reader.read(buffer)) != -1) {
|
||||||
|
execCallback.callback(new String(buffer, 0, output));
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
int statusCode = resp.getStatusCode();
|
||||||
|
context.shutdown();
|
||||||
|
return statusCode;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int executePs(final List<String> commandList, ExecCallback execCallback) {
|
||||||
|
WinRmClientContext context = WinRmClientContext.newInstance();
|
||||||
|
WinRmTool tool = WinRmTool.Builder.builder(ip, username, password)
|
||||||
|
.port(DEFAULT_PORT).useHttps(false).context(context).build();
|
||||||
|
tool.setOperationTimeout(5000L);
|
||||||
|
|
||||||
|
WinRmToolResponse resp = tool.executePs(commandList);
|
||||||
|
|
||||||
|
StringReader reader = new StringReader(resp.getStdOut() + resp.getStdErr());
|
||||||
|
try {
|
||||||
|
char[] buffer = new char[1024];
|
||||||
|
int output;
|
||||||
|
while ((output = reader.read(buffer)) != -1) {
|
||||||
|
execCallback.callback(new String(buffer, 0, output));
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
int statusCode = resp.getStatusCode();
|
||||||
|
context.shutdown();
|
||||||
|
return statusCode;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String execute(final List<String> commandList, ExecCallback execCallback, String pass) {
|
||||||
|
WinRmClientContext context = WinRmClientContext.newInstance();
|
||||||
|
WinRmTool tool = WinRmTool.Builder.builder(ip, username, password)
|
||||||
|
.port(DEFAULT_PORT).useHttps(false).context(context).build();
|
||||||
|
tool.setOperationTimeout(5000L);
|
||||||
|
WinRmToolResponse resp = tool.executeCommand(commandList);
|
||||||
|
context.shutdown();
|
||||||
|
return resp.getStdOut();
|
||||||
|
}
|
||||||
|
|
||||||
|
private String join(List<String> commands) {
|
||||||
|
StringBuilder builder = new StringBuilder();
|
||||||
|
boolean first = true;
|
||||||
|
for (String command : commands) {
|
||||||
|
if (first) {
|
||||||
|
first = false;
|
||||||
|
} else {
|
||||||
|
builder.append(" & ");
|
||||||
|
}
|
||||||
|
builder.append(command);
|
||||||
|
}
|
||||||
|
return builder.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void main(String[] args) throws IOException {
|
||||||
|
// WinRMHelper exec = new WinRMHelper("192.168.0.88", "hrb", "1qaz!QAZ");
|
||||||
|
// List<String> commands = new ArrayList<>();
|
||||||
|
//// commands.add("powershell");
|
||||||
|
// commands.add("dir");
|
||||||
|
// commands.add("cd /d D:\\Users\\hrb\\Desktop");
|
||||||
|
// commands.add("dir");
|
||||||
|
// commands.add("git clone -b master http://192.168.0.12:3000/liuyuchao/testrepo.git");
|
||||||
|
//// commands.add("rmdir /s /q 456123");
|
||||||
|
//
|
||||||
|
// int resp = exec.execute(commands, System.out::println);
|
||||||
|
// System.out.println(resp);
|
||||||
|
PipedWriter writer = new PipedWriter();
|
||||||
|
PipedReader reader = new PipedReader(writer);
|
||||||
|
|
||||||
|
Thread t = new Thread(() -> {
|
||||||
|
try {
|
||||||
|
for (int i = 0; i < 10; i++) {
|
||||||
|
writer.write("Line " + i + "\n");
|
||||||
|
writer.flush();
|
||||||
|
Thread.sleep(500);
|
||||||
|
}
|
||||||
|
writer.close();
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
t.start();
|
||||||
|
|
||||||
|
try (BufferedReader br = new BufferedReader(reader)) {
|
||||||
|
String line;
|
||||||
|
while ((line = br.readLine()) != null) {
|
||||||
|
System.out.println(line);
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,14 @@
|
|||||||
|
package cd.casic.ci.process.util;
|
||||||
|
|
||||||
|
import com.jcraft.jsch.ChannelShell;
|
||||||
|
|
||||||
|
public class ChannelShellUtil {
|
||||||
|
|
||||||
|
public static void setDefault(ChannelShell channel) {
|
||||||
|
// channel.setEnv("LANG", "zh_CN.UTF-8");
|
||||||
|
channel.setEnv("LANG", "en_US.UTF-8");
|
||||||
|
// SSH 代理转发
|
||||||
|
channel.setAgentForwarding(false);
|
||||||
|
channel.setPtyType("xterm");
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,135 @@
|
|||||||
|
package cd.casic.ci.process.util;
|
||||||
|
|
||||||
|
|
||||||
|
import cn.hutool.log.Log;
|
||||||
|
import com.antherd.smcrypto.sm2.Sm2;
|
||||||
|
import com.antherd.smcrypto.sm3.Sm3;
|
||||||
|
import com.antherd.smcrypto.sm4.Sm4;
|
||||||
|
import com.antherd.smcrypto.sm4.Sm4Options;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 加密工具类,本框架目前使用 https://github.com/antherd/sm-crypto 项目中一些加解密方式
|
||||||
|
* 使用小伙伴需要过等保密评相关,请在此处更改为自己的加密方法,或加密机,使用加密机同时需要替换公钥,私钥在内部无法导出,提供加密的方法
|
||||||
|
*
|
||||||
|
* @author yubaoshan
|
||||||
|
*/
|
||||||
|
public class CryptogramUtil {
|
||||||
|
|
||||||
|
private static final Log log = Log.get();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 加密方法(Sm2 的专门针对前后端分离,非对称秘钥对的方式,暴露出去的公钥,对传输过程中的密码加个密)
|
||||||
|
*
|
||||||
|
* @param str 待加密数据
|
||||||
|
* @return 加密后的密文
|
||||||
|
* @author yubaoshan
|
||||||
|
*/
|
||||||
|
public static String doSm2Encrypt(String str) {
|
||||||
|
return Sm2.doEncrypt(str, Keypair.PUBLIC_KEY);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 解密方法
|
||||||
|
* 如果采用加密机的方法,用try catch 捕捉异常,返回原文值即可
|
||||||
|
*
|
||||||
|
* @param str 密文
|
||||||
|
* @return 解密后的明文
|
||||||
|
* @author yubaoshan
|
||||||
|
*/
|
||||||
|
public static String doSm2Decrypt(String str) {
|
||||||
|
// 解密
|
||||||
|
return Sm2.doDecrypt(str, Keypair.PRIVATE_KEY);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 加密方法
|
||||||
|
*
|
||||||
|
* @param str 待加密数据
|
||||||
|
* @return 加密后的密文
|
||||||
|
* @author yubaoshan
|
||||||
|
*/
|
||||||
|
public static String doEncrypt(String str) {
|
||||||
|
// SM4 加密 cbc模式
|
||||||
|
Sm4Options sm4Options4 = new Sm4Options();
|
||||||
|
sm4Options4.setMode("cbc");
|
||||||
|
sm4Options4.setIv("fedcba98765432100123456789abcdef");
|
||||||
|
return Sm4.encrypt(str, Keypair.KEY, sm4Options4);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 解密方法
|
||||||
|
* 如果采用加密机的方法,用try catch 捕捉异常,返回原文值即可
|
||||||
|
*
|
||||||
|
* @param str 密文
|
||||||
|
* @return 解密后的明文
|
||||||
|
* @author yubaoshan
|
||||||
|
*/
|
||||||
|
public static String doDecrypt(String str) {
|
||||||
|
// 解密,cbc 模式,输出 utf8 字符串
|
||||||
|
Sm4Options sm4Options8 = new Sm4Options();
|
||||||
|
sm4Options8.setMode("cbc");
|
||||||
|
sm4Options8.setIv("fedcba98765432100123456789abcdef");
|
||||||
|
String docString = Sm4.decrypt(str, Keypair.KEY, sm4Options8);
|
||||||
|
if (docString.equals("")) {
|
||||||
|
log.warn(">>> 字段解密失败,返回原文值:{}", str);
|
||||||
|
return str;
|
||||||
|
} else {
|
||||||
|
return docString;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 纯签名
|
||||||
|
*
|
||||||
|
* @param str 待签名数据
|
||||||
|
* @return 签名结果
|
||||||
|
* @author yubaoshan
|
||||||
|
*/
|
||||||
|
public static String doSignature(String str) {
|
||||||
|
return Sm2.doSignature(str, Keypair.PRIVATE_KEY);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 验证签名结果
|
||||||
|
*
|
||||||
|
* @param originalStr 签名原文数据
|
||||||
|
* @param str 签名结果
|
||||||
|
* @return 是否通过
|
||||||
|
* @author yubaoshan
|
||||||
|
*/
|
||||||
|
public static boolean doVerifySignature(String originalStr, String str) {
|
||||||
|
return Sm2.doVerifySignature(originalStr, str, Keypair.PUBLIC_KEY);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 通过杂凑算法取得hash值,用于做数据完整性保护
|
||||||
|
*
|
||||||
|
* @param str 字符串
|
||||||
|
* @return hash 值
|
||||||
|
* @author yubaoshan
|
||||||
|
*/
|
||||||
|
public static String doHashValue(String str) {
|
||||||
|
return Sm3.sm3(str);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class Keypair{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 公钥
|
||||||
|
*/
|
||||||
|
public static String PUBLIC_KEY = "04298364ec840088475eae92a591e01284d1abefcda348b47eb324bb521bb03b0b2a5bc393f6b71dabb8f15c99a0050818b56b23f31743b93df9cf8948f15ddb54";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 私钥
|
||||||
|
*/
|
||||||
|
public static String PRIVATE_KEY = "3037723d47292171677ec8bd7dc9af696c7472bc5f251b2cec07e65fdef22e25";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* SM4的对称秘钥(生产环境需要改成自己使用的)
|
||||||
|
* 16 进制字符串,要求为 128 比特
|
||||||
|
*/
|
||||||
|
public static String KEY = "0123456789abcdeffedcba9876543210";
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -14,7 +14,7 @@ public interface DictDataConvert {
|
|||||||
|
|
||||||
DictDataConvert INSTANCE = Mappers.getMapper(DictDataConvert.class);
|
DictDataConvert INSTANCE = Mappers.getMapper(DictDataConvert.class);
|
||||||
|
|
||||||
List<DictDataSimpleRespVO> convertList(List<DictDataDO> list);
|
// List<DictDataSimpleRespVO> convertList(List<DictDataDO> list);
|
||||||
|
|
||||||
DictDataRespVO convert(DictDataDO bean);
|
DictDataRespVO convert(DictDataDO bean);
|
||||||
|
|
||||||
|
@ -27,5 +27,6 @@ public interface DictTypeConvert {
|
|||||||
// List<DictTypeExcelVO> convertList02(List<DictTypeDO> list);
|
// List<DictTypeExcelVO> convertList02(List<DictTypeDO> list);
|
||||||
|
|
||||||
List<DictDataTreeVO> convertList03(List<DictTypeDO> list);
|
List<DictDataTreeVO> convertList03(List<DictTypeDO> list);
|
||||||
|
DictDataTreeVO converter03(DictTypeDO typeDO);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user