diff --git a/.idea/.gitignore b/.idea/.gitignore index 13566b8..8d55843 100644 --- a/.idea/.gitignore +++ b/.idea/.gitignore @@ -1,5 +1,6 @@ # Default ignored files /shelf/ +../.idea /workspace.xml # Editor-based HTTP Client requests /httpRequests/ diff --git a/.idea/compiler.xml b/.idea/compiler.xml index 19c1013..11647b5 100644 --- a/.idea/compiler.xml +++ b/.idea/compiler.xml @@ -3,15 +3,20 @@ + + + + + - - - - + + + + @@ -46,15 +51,9 @@ - - - - - - - + @@ -69,6 +68,7 @@ + diff --git a/.idea/encodings.xml b/.idea/encodings.xml index d7e1aa7..3f5727c 100644 --- a/.idea/encodings.xml +++ b/.idea/encodings.xml @@ -59,6 +59,8 @@ + + diff --git a/.idea/gradle.xml b/.idea/gradle.xml index 8948be4..73f84f6 100644 --- a/.idea/gradle.xml +++ b/.idea/gradle.xml @@ -1,5 +1,6 @@ + - + diff --git a/dependencies/.flattened-pom.xml b/dependencies/.flattened-pom.xml index bb565c9..3356936 100644 --- a/dependencies/.flattened-pom.xml +++ b/dependencies/.flattened-pom.xml @@ -18,6 +18,7 @@ 3.47.1.0 3.5.16 0.33.0 + 2.9.3 8.6.0 1.27.1 2.3.1 @@ -26,19 +27,27 @@ 3.36.0 0.9.0 2.14.5 + 1.28 3.3.4 1.80 2.3.0 + 1.12 2.2.7 2.17.0 + 1.2.11 + 2.6.10 6.0.0-M16 + 2.3.0 1.18.1 3.5.8 4.5.0 1.18.34 9.0.0 5.2.0 + 1.2.11 + 1.4.0 2.4 + 0.10.2 3.0.6 2.0.8.3 3.12.1 @@ -47,6 +56,7 @@ 5.8.32 2.0.0-jdk17 0.1.55 + 1.28 42.7.4 3.3.3 23.5.0.24.07 @@ -184,18 +194,48 @@ ${revision} - cd.casic.pipeline - pipeline-ci-commons + cd.casic.ci + ci-commons ${revision} - cd.casic.pipeline - pipeline-ci-event + cd.casic.ci + ci-event ${revision} - cd.casic.pipeline - pipeline-ci-log + cd.casic.ci + ci-log + ${revision} + + + cd.casic.ci + ci-market + ${revision} + + + cd.casic.ci + ci-process + ${revision} + + + cd.casic.ci + ci-project + ${revision} + + + cd.casic.ci + ci-quality + ${revision} + + + cd.casic.ci + ci-ticket + ${revision} + + + cd.casic.ci + ci-scm-repository ${revision} @@ -204,8 +244,8 @@ ${bizlog-sdk.version} - org.springframework.boot spring-boot-starter + org.springframework.boot @@ -260,12 +300,12 @@ ${easy-trans.version} - org.springframework spring-context + org.springframework - org.springframework.cloud spring-cloud-commons + org.springframework.cloud @@ -285,8 +325,8 @@ ${redisson.version} - org.springframework.boot spring-boot-starter-actuator + org.springframework.boot @@ -296,8 +336,8 @@ ${lock4j.version} - org.redisson redisson-spring-boot-starter + org.redisson @@ -337,8 +377,8 @@ ${spring-boot-admin.version} - de.codecentric spring-boot-admin-server-cloud + de.codecentric @@ -358,12 +398,12 @@ ${spring.boot.version} - org.ow2.asm asm + org.ow2.asm - org.mockito mockito-core + org.mockito @@ -483,8 +523,8 @@ ${pf4j.version} - org.slf4j slf4j-log4j12 + org.slf4j @@ -533,6 +573,56 @@ bcprov-jdk18on ${bcprov-jdk18on.version} + + org.jvnet.winp + winp + ${winp.version} + + + org.jenkins-ci + version-number + ${version-number.version}} + + + org.reflections + reflections + ${reflections.version} + + + org.apache.commons + commons-exec + ${commons-exec.version} + + + com.googlecode.plist + dd-plist + ${dd-plist.version} + + + net.dongliu + apk-parser + ${apk-parser.version} + + + ch.qos.logback + logback-core + ${logback-core.version} + + + ch.qos.logback + logback-classic + ${logback-classic.version} + + + com.github.ben-manes.caffeine + caffeine + ${caffeine.version} + + + io.github.resilience4j + resilience4j-circuitbreaker + ${resilience4j-circuitbreaker.version} + diff --git a/modules/ci-commons/pom.xml b/modules/ci-commons/pom.xml index 027857a..2b1d51f 100644 --- a/modules/ci-commons/pom.xml +++ b/modules/ci-commons/pom.xml @@ -21,14 +21,190 @@ - - cd.casic.boot - commons - + cd.casic.boot spring-boot-starter-mybatis + + org.projectlombok + lombok + + + + javax.ws.rs + javax.ws.rs-api + 2.0 + + + + io.swagger.core.v3 + swagger-annotations + 2.2.16 + + + + org.hashids + hashids + 1.0.3 + + + + com.fasterxml.jackson.module + jackson-module-kotlin + 2.13.4 + + + + com.fasterxml.jackson.core + jackson-databind + 2.13.4.2 + + + + com.fasterxml.jackson.core + jackson-core + 2.13.4 + + + + com.fasterxml.jackson.core + jackson-annotations + 2.13.4 + + + + com.fasterxml.jackson.jaxrs + jackson-jaxrs-json-provider + 2.13.4 + + + + com.fasterxml.jackson.dataformat + jackson-dataformat-yaml + 2.13.4 + + + + com.fasterxml.jackson.jaxrs + jackson-jaxrs-base + 2.13.4 + + + + org.bouncycastle + bcprov-jdk15on + 1.70 + + + + com.github.fge + json-schema-validator + 2.2.6 + + + + com.google.guava + guava + 18.0 + + + + com.squareup.okhttp3 + okhttp + 4.9.0 + + + + commons-codec + commons-codec + 1.15 + + + + org.springframework.boot + spring-boot-starter-data-redis + + + + org.apache.commons + commons-compress + + + + org.apache.commons + commons-exec + + + + org.apache.commons + commons-collections4 + 4.4 + + + + javax.servlet + javax.servlet-api + 4.0.1 + provided + + + + + + + + + + com.vdurmont + emoji-java + 5.1.1 + + + + org.apache.lucene + lucene-core + 8.11.1 + + + + org.apache.commons + commons-csv + 1.8 + + + + com.github.ben-manes.caffeine + caffeine + + + + com.fasterxml.jackson.datatype + jackson-datatype-jsr310 + 2.13.4 + + + + com.jakewharton + disklrucache + 2.0.2 + + + + + + org.apache.maven.plugins + maven-compiler-plugin + + 9 + 9 + + + + \ No newline at end of file diff --git a/modules/ci-commons/src/main/java/cd/casic/ci/commons/constant/CommonConstants.java b/modules/ci-commons/src/main/java/cd/casic/ci/commons/commonapi/constant/CommonConstants.java similarity index 99% rename from modules/ci-commons/src/main/java/cd/casic/ci/commons/constant/CommonConstants.java rename to modules/ci-commons/src/main/java/cd/casic/ci/commons/commonapi/constant/CommonConstants.java index 636ecd4..27e2ed4 100644 --- a/modules/ci-commons/src/main/java/cd/casic/ci/commons/constant/CommonConstants.java +++ b/modules/ci-commons/src/main/java/cd/casic/ci/commons/commonapi/constant/CommonConstants.java @@ -1,4 +1,4 @@ -package cd.casic.ci.commons.constant; +package cd.casic.ci.commons.commonapi.constant; /** * @Classname CommonConstants diff --git a/modules/ci-commons/src/main/java/cd/casic/ci/commons/constant/CommonMessageCode.java b/modules/ci-commons/src/main/java/cd/casic/ci/commons/commonapi/constant/CommonMessageCode.java similarity index 99% rename from modules/ci-commons/src/main/java/cd/casic/ci/commons/constant/CommonMessageCode.java rename to modules/ci-commons/src/main/java/cd/casic/ci/commons/commonapi/constant/CommonMessageCode.java index b2abb14..726ad39 100644 --- a/modules/ci-commons/src/main/java/cd/casic/ci/commons/constant/CommonMessageCode.java +++ b/modules/ci-commons/src/main/java/cd/casic/ci/commons/commonapi/constant/CommonMessageCode.java @@ -1,4 +1,4 @@ -package cd.casic.ci.commons.constant; +package cd.casic.ci.commons.commonapi.constant; /** * @Classname CommonMessageCode diff --git a/modules/ci-commons/src/main/java/cd/casic/ci/commons/commonapi/enums/AgentStatus.java b/modules/ci-commons/src/main/java/cd/casic/ci/commons/commonapi/enums/AgentStatus.java new file mode 100644 index 0000000..0b517ce --- /dev/null +++ b/modules/ci-commons/src/main/java/cd/casic/ci/commons/commonapi/enums/AgentStatus.java @@ -0,0 +1,57 @@ +package cd.casic.ci.commons.commonapi.enums; + + +import lombok.SneakyThrows; + +public enum AgentStatus { + UN_IMPORT(0), // 未导入,用户刚刚在界面上面生成链接 + UN_IMPORT_OK(1), // 未导入但是agent状态正常(这个时候还是不能用来当构建机) + IMPORT_OK(2), // 用户已经在界面导入并且agent工作正常(构建机只有在这个状态才能正常工作) + IMPORT_EXCEPTION(3), // agent异常 + DELETE(4); + + private final int status; + + AgentStatus(int status) { + this.status = status; + } + + @Override + public String toString() { + return String.valueOf(status); + } + + @SneakyThrows + public static AgentStatus fromStatus(int status) { + for (AgentStatus value : values()) { + if (status == value.status) { + return value; + } + } + throw new Exception("Unknown agent status(" + status + ")"); +// throw new InvalidParamException("Unknown agent status(" + status + ")"); + } + + public static boolean isDelete(AgentStatus status) { + return status == DELETE; + } + + public static boolean isUnImport(AgentStatus status) { + return status == UN_IMPORT; + } + + public static boolean isImportException(AgentStatus status) { + return status == IMPORT_EXCEPTION; + } + + @SneakyThrows + public static AgentStatus fromString(String status) { + for (AgentStatus value : values()) { + if (status.equals(value.name())) { + return value; + } + } + throw new Exception("Unknown agent status(" + status + ")"); +// throw new InvalidParamException("Unknown agent status(" + status + ")"); + } +} \ No newline at end of file diff --git a/modules/ci-commons/src/main/java/cd/casic/ci/commons/commonapi/enums/BuildReviewType.java b/modules/ci-commons/src/main/java/cd/casic/ci/commons/commonapi/enums/BuildReviewType.java new file mode 100644 index 0000000..09e209f --- /dev/null +++ b/modules/ci-commons/src/main/java/cd/casic/ci/commons/commonapi/enums/BuildReviewType.java @@ -0,0 +1,11 @@ +package cd.casic.ci.commons.commonapi.enums; + +public enum BuildReviewType { + TASK_REVIEW, + STAGE_REVIEW, + QUALITY_TASK_REVIEW_PASS, + QUALITY_TASK_REVIEW_ABORT, + QUALITY_CHECK_IN, + QUALITY_CHECK_OUT, + TRIGGER_REVIEW +} \ No newline at end of file diff --git a/modules/ci-commons/src/main/java/cd/casic/ci/commons/commonapi/enums/BusTypeEnum.java b/modules/ci-commons/src/main/java/cd/casic/ci/commons/commonapi/enums/BusTypeEnum.java new file mode 100644 index 0000000..f0161b5 --- /dev/null +++ b/modules/ci-commons/src/main/java/cd/casic/ci/commons/commonapi/enums/BusTypeEnum.java @@ -0,0 +1,6 @@ +package cd.casic.ci.commons.commonapi.enums; + +public enum BusTypeEnum { + PIPELINE, + TEMPLATE +} \ No newline at end of file diff --git a/modules/ci-commons/src/main/java/cd/casic/ci/commons/commonapi/enums/CheckoutRepositoryType.java b/modules/ci-commons/src/main/java/cd/casic/ci/commons/commonapi/enums/CheckoutRepositoryType.java new file mode 100644 index 0000000..e03088b --- /dev/null +++ b/modules/ci-commons/src/main/java/cd/casic/ci/commons/commonapi/enums/CheckoutRepositoryType.java @@ -0,0 +1,24 @@ +package cd.casic.ci.commons.commonapi.enums; + +import org.apache.commons.lang3.StringUtils; + +public enum CheckoutRepositoryType { + ID, + NAME, + URL, + SELF; + + public static CheckoutRepositoryType parseType(String type) { + if (type == null || StringUtils.isBlank(type)) { + return ID; + } + return valueOf(type); + } + + /** + * 拉取类型跳过定时触发源代码变更检查 + */ + public static boolean skipTimerTriggerChange(String type) { + return type != null && (type.equals(URL.name()) || type.equals(SELF.name())); + } +} \ No newline at end of file diff --git a/modules/ci-commons/src/main/java/cd/casic/ci/commons/commonapi/enums/CrudEnum.java b/modules/ci-commons/src/main/java/cd/casic/ci/commons/commonapi/enums/CrudEnum.java new file mode 100644 index 0000000..d322058 --- /dev/null +++ b/modules/ci-commons/src/main/java/cd/casic/ci/commons/commonapi/enums/CrudEnum.java @@ -0,0 +1,8 @@ +package cd.casic.ci.commons.commonapi.enums; + +public enum CrudEnum { + CREATE, // 增加 + DELETE, // 删除 + UPDATE, // 更新 + READ // 查询 +} \ No newline at end of file diff --git a/modules/ci-commons/src/main/java/cd/casic/ci/commons/commonapi/enums/EnumLoader.java b/modules/ci-commons/src/main/java/cd/casic/ci/commons/commonapi/enums/EnumLoader.java new file mode 100644 index 0000000..ad57700 --- /dev/null +++ b/modules/ci-commons/src/main/java/cd/casic/ci/commons/commonapi/enums/EnumLoader.java @@ -0,0 +1,41 @@ +package cd.casic.ci.commons.commonapi.enums; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.ServiceLoader; +import java.util.concurrent.atomic.AtomicBoolean; + +/** + * 仅在初始化时调用一次,不可重复使用 + */ +public class EnumLoader { + private static final AtomicBoolean modify = new AtomicBoolean(false); + private static final Logger logger = LoggerFactory.getLogger(EnumLoader.class); + + private EnumLoader() { + // Private constructor to prevent instantiation + } + + public static void enumModified() { + // 同一JVM中防止多次重复加载,造成Enum实例不一致 + if (!modify.compareAndSet(false, true)) { + return; + } + + Class clazz = EnumModifier.class; + ServiceLoader fetcheries = ServiceLoader.load(clazz); + if (!fetcheries.iterator().hasNext()) { + fetcheries = ServiceLoader.load(clazz, ServiceLoader.class.getClassLoader()); + } + + fetcheries.forEach(modifier -> { + logger.info("[ENUM MODIFIER]: {}", modifier); + try { + modifier.modified(); + } catch (Exception e) { + logger.error("[ENUM MODIFIER]| load fail| {}", e.getMessage(), e); + } + }); + } +} \ No newline at end of file diff --git a/modules/ci-commons/src/main/java/cd/casic/ci/commons/commonapi/enums/EnumModifier.java b/modules/ci-commons/src/main/java/cd/casic/ci/commons/commonapi/enums/EnumModifier.java new file mode 100644 index 0000000..d927ecc --- /dev/null +++ b/modules/ci-commons/src/main/java/cd/casic/ci/commons/commonapi/enums/EnumModifier.java @@ -0,0 +1,8 @@ +package cd.casic.ci.commons.commonapi.enums; + +public interface EnumModifier { + /** + * 实现对现有枚举进行注册扩展修改 + */ + void modified(); +} \ No newline at end of file diff --git a/modules/ci-commons/src/main/java/cd/casic/ci/commons/commonapi/enums/FrontendTypeEnum.java b/modules/ci-commons/src/main/java/cd/casic/ci/commons/commonapi/enums/FrontendTypeEnum.java new file mode 100644 index 0000000..cc2fe77 --- /dev/null +++ b/modules/ci-commons/src/main/java/cd/casic/ci/commons/commonapi/enums/FrontendTypeEnum.java @@ -0,0 +1,26 @@ +package cd.casic.ci.commons.commonapi.enums; + +public enum FrontendTypeEnum { + HISTORY("1.0"), // 历史老插件UI + NORMAL("1.1"), // 官方提供典型的插件UI配置方式 + SPECIAL("1.2"); // 定制插件UI方式 + + private final String typeVersion; + + FrontendTypeEnum(String typeVersion) { + this.typeVersion = typeVersion; + } + + public static FrontendTypeEnum getFrontendTypeObj(String typeVersion) { + for (FrontendTypeEnum enumObj : values()) { + if (enumObj.typeVersion.equals(typeVersion)) { + return enumObj; + } + } + return null; + } + + public String getTypeVersion() { + return typeVersion; + } +} \ No newline at end of file diff --git a/modules/ci-commons/src/main/java/cd/casic/ci/commons/commonapi/enums/I18nSourceEnum.java b/modules/ci-commons/src/main/java/cd/casic/ci/commons/commonapi/enums/I18nSourceEnum.java new file mode 100644 index 0000000..962b1cd --- /dev/null +++ b/modules/ci-commons/src/main/java/cd/casic/ci/commons/commonapi/enums/I18nSourceEnum.java @@ -0,0 +1,6 @@ +package cd.casic.ci.commons.commonapi.enums; + +public enum I18nSourceEnum { + DB, // 来源于数据库 + PROPERTIES // 来源于国际化资源文件 +} \ No newline at end of file diff --git a/modules/ci-commons/src/main/java/cd/casic/ci/commons/commonapi/enums/I18nTranslateTypeEnum.java b/modules/ci-commons/src/main/java/cd/casic/ci/commons/commonapi/enums/I18nTranslateTypeEnum.java new file mode 100644 index 0000000..4f7adf3 --- /dev/null +++ b/modules/ci-commons/src/main/java/cd/casic/ci/commons/commonapi/enums/I18nTranslateTypeEnum.java @@ -0,0 +1,6 @@ +package cd.casic.ci.commons.commonapi.enums; + +public enum I18nTranslateTypeEnum { + NAME, // 基于字段名称进行翻译 + VALUE // 基于字段值进行翻译 +} \ No newline at end of file diff --git a/modules/ci-commons/src/main/java/cd/casic/ci/commons/commonapi/enums/OSType.java b/modules/ci-commons/src/main/java/cd/casic/ci/commons/commonapi/enums/OSType.java new file mode 100644 index 0000000..6d14822 --- /dev/null +++ b/modules/ci-commons/src/main/java/cd/casic/ci/commons/commonapi/enums/OSType.java @@ -0,0 +1,11 @@ +package cd.casic.ci.commons.commonapi.enums; + +/** + * Powered By Tencent + */ +public enum OSType { + WINDOWS, + LINUX, + MAC_OS, + OTHER +} \ No newline at end of file diff --git a/modules/ci-commons/src/main/java/cd/casic/ci/commons/commonapi/enums/PlatformEnum.java b/modules/ci-commons/src/main/java/cd/casic/ci/commons/commonapi/enums/PlatformEnum.java new file mode 100644 index 0000000..864c23c --- /dev/null +++ b/modules/ci-commons/src/main/java/cd/casic/ci/commons/commonapi/enums/PlatformEnum.java @@ -0,0 +1,36 @@ +package cd.casic.ci.commons.commonapi.enums; + +public enum PlatformEnum { + ANDROID(1, "安卓"), + IOS(2, "IOS"); + + private final int id; + private final String mean; + + PlatformEnum(int id, String mean) { + this.id = id; + this.mean = mean; + } + + public static PlatformEnum of(Integer id) { + if (id == null) { + return null; + } + + for (PlatformEnum value : values()) { + if (value.id == id) { + return value; + } + } + + return null; + } + + public int getId() { + return id; + } + + public String getMean() { + return mean; + } +} \ No newline at end of file diff --git a/modules/ci-commons/src/main/java/cd/casic/ci/commons/commonapi/enums/RepositoryType.java b/modules/ci-commons/src/main/java/cd/casic/ci/commons/commonapi/enums/RepositoryType.java new file mode 100644 index 0000000..4fa10b7 --- /dev/null +++ b/modules/ci-commons/src/main/java/cd/casic/ci/commons/commonapi/enums/RepositoryType.java @@ -0,0 +1,15 @@ +package cd.casic.ci.commons.commonapi.enums; + +import org.apache.commons.lang3.StringUtils; + +public enum RepositoryType { + ID, + NAME; + + public static RepositoryType parseType(String type) { + if (type == null || StringUtils.isBlank(type)) { + return ID; + } + return valueOf(type); + } +} \ No newline at end of file diff --git a/modules/ci-commons/src/main/java/cd/casic/ci/commons/commonapi/enums/RequestChannelTypeEnum.java b/modules/ci-commons/src/main/java/cd/casic/ci/commons/commonapi/enums/RequestChannelTypeEnum.java new file mode 100644 index 0000000..34612cb --- /dev/null +++ b/modules/ci-commons/src/main/java/cd/casic/ci/commons/commonapi/enums/RequestChannelTypeEnum.java @@ -0,0 +1,10 @@ +package cd.casic.ci.commons.commonapi.enums; + +public enum RequestChannelTypeEnum { + USER, + SERVICE, + BUILD, + OP, + OPEN, + API +} \ No newline at end of file diff --git a/modules/ci-commons/src/main/java/cd/casic/ci/commons/commonapi/enums/ScmType.java b/modules/ci-commons/src/main/java/cd/casic/ci/commons/commonapi/enums/ScmType.java new file mode 100644 index 0000000..42d191f --- /dev/null +++ b/modules/ci-commons/src/main/java/cd/casic/ci/commons/commonapi/enums/ScmType.java @@ -0,0 +1 @@ +package cd.casic.ci.commons.commonapi.enums; \ No newline at end of file diff --git a/modules/ci-commons/src/main/java/cd/casic/ci/commons/commonapi/enums/SystemModuleEnum.java b/modules/ci-commons/src/main/java/cd/casic/ci/commons/commonapi/enums/SystemModuleEnum.java new file mode 100644 index 0000000..f1e833f --- /dev/null +++ b/modules/ci-commons/src/main/java/cd/casic/ci/commons/commonapi/enums/SystemModuleEnum.java @@ -0,0 +1,46 @@ +package cd.casic.ci.commons.commonapi.enums; + +@SuppressWarnings("unused") +public enum SystemModuleEnum { + COMMON("00"), // 公共模块 + PROCESS("01"), // 流水线 + ARTIFACTORY("02"), // 版本仓库 + OPENAPI("12"), // 开放平台API + PLUGIN("13"), // 插件 + QUALITY("14"), // 质量红线 + REPOSITORY("15"), // 代码库 + SCM("16"), // 软件配置管理 + SUPPORT("17"), // 支撑服务 + TICKET("18"), // 证书凭据 + PROJECT("19"), // 项目管理 + STORE("20"), // 商店 + AUTH("21"), // 权限 + SIGN("22"), // 签名服务 + METRICS("23"), // 度量服务 + EXTERNAL("24"), // 外部扩展 + PREBUILD("25"), // 预构建 + BUILDLESS("27"), // buildless服务 + LAMBDA("28"), // lambda服务 + STREAM("29"), // stream服务 + WORKER("30"), // 度量服务 + REMOTEDEV("32"); // 远程开发服务 + + private final String code; + + SystemModuleEnum(String code) { + this.code = code; + } + + public static String getSystemModule(String code) { + for (SystemModuleEnum value : values()) { + if (value.code.equals(code)) { + return value.name(); + } + } + return COMMON.name(); + } + + public String getCode() { + return code; + } +} \ No newline at end of file diff --git a/modules/ci-commons/src/main/java/cd/casic/ci/commons/commonapi/enums/TaskStatusEnum.java b/modules/ci-commons/src/main/java/cd/casic/ci/commons/commonapi/enums/TaskStatusEnum.java new file mode 100644 index 0000000..f4d8c0e --- /dev/null +++ b/modules/ci-commons/src/main/java/cd/casic/ci/commons/commonapi/enums/TaskStatusEnum.java @@ -0,0 +1,12 @@ +package cd.casic.ci.commons.commonapi.enums; + +public enum TaskStatusEnum { + INIT, + HANDING, + FAIL, + SUCCESS, + PENDING_ROLLBACK, + ROLLBACKING, + ROLLBACK_SUCCESS, + ROLLBACK_FAIL +} \ No newline at end of file diff --git a/modules/ci-commons/src/main/java/cd/casic/ci/commons/commonapi/enums/TriggerRepositoryType.java b/modules/ci-commons/src/main/java/cd/casic/ci/commons/commonapi/enums/TriggerRepositoryType.java new file mode 100644 index 0000000..50120e8 --- /dev/null +++ b/modules/ci-commons/src/main/java/cd/casic/ci/commons/commonapi/enums/TriggerRepositoryType.java @@ -0,0 +1,25 @@ +package cd.casic.ci.commons.commonapi.enums; + +import cd.casic.ci.commons.commonapi.enums.RepositoryType; + +public enum TriggerRepositoryType { + ID, + NAME, + SELF, + NONE; // 触发器不需要绑定代码库,如定时触发默认时,不需要绑定 + + public static RepositoryType toRepositoryType(TriggerRepositoryType type) { + if (type == null) { + return null; + } + switch (type) { + case ID: + case SELF: + return RepositoryType.ID; + case NAME: + return RepositoryType.NAME; + default: + return null; + } + } +} \ No newline at end of file diff --git a/modules/ci-commons/src/main/java/cd/casic/ci/commons/commonapi/pojo/AgentResult.java b/modules/ci-commons/src/main/java/cd/casic/ci/commons/commonapi/pojo/AgentResult.java new file mode 100644 index 0000000..f74f88e --- /dev/null +++ b/modules/ci-commons/src/main/java/cd/casic/ci/commons/commonapi/pojo/AgentResult.java @@ -0,0 +1,47 @@ +package cd.casic.ci.commons.commonapi.pojo; + +import cd.casic.ci.commons.commonapi.enums.AgentStatus; +import com.fasterxml.jackson.annotation.JsonIgnore; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.AllArgsConstructor; +import lombok.Data; + +@Data +@AllArgsConstructor +@Schema(title = "第三方Agent数据返回包装模型") +public class AgentResult { + @Schema(title = "状态码", required = true) + private final Integer status; + + @Schema(title = "错误信息", required = false) + private final String message; + + @Schema(title = "Agent状态", required = false) + private final AgentStatus agentStatus; + + @Schema(title = "数据", required = false) + private final T data; + + public AgentResult(AgentStatus status, T data) { + this(0, null, status, data); + } + + public AgentResult(Integer status, String message) { + this(status, message, null, null); + } + + @JsonIgnore + public boolean isNotOk() { + return status != 0; + } + + @JsonIgnore + public boolean isAgentNotOK() { + return agentStatus == null || isAgentDelete(); + } + + @JsonIgnore + public boolean isAgentDelete() { + return agentStatus != null && agentStatus == AgentStatus.DELETE; + } +} \ No newline at end of file diff --git a/modules/ci-commons/src/main/java/cd/casic/ci/commons/commonapi/pojo/AtomMonitorData.java b/modules/ci-commons/src/main/java/cd/casic/ci/commons/commonapi/pojo/AtomMonitorData.java new file mode 100644 index 0000000..43b8ae1 --- /dev/null +++ b/modules/ci-commons/src/main/java/cd/casic/ci/commons/commonapi/pojo/AtomMonitorData.java @@ -0,0 +1,62 @@ +package cd.casic.ci.commons.commonapi.pojo; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.util.Map; + +@Data +@Schema(title = "插件监控数据") +public class AtomMonitorData { + @Schema(title = "插件执行错误码", required = true) + //@InfluxTag + private Integer errorCode; + + @Schema(title = "插件执行错误信息", required = false) + private String errorMsg; + + @Schema(title = "插件执行错误类型", required = false) + //@InfluxTag + private String errorType; + + @Schema(title = "插件代码", required = true) + //@InfluxTag + private String atomCode; + + @Schema(title = "插件版本", required = true) + private String version; + + @Schema(title = "项目ID", required = true) + private String projectId; + + @Schema(title = "流水线ID", required = true) + private String pipelineId; + + @Schema(title = "构建ID", required = true) + private String buildId; + + @Schema(title = "构建环境ID", required = true) + private String vmSeqId; + + @Schema(title = "执行开始时间", required = false) + //@InfluxTag + private Long startTime; + + @Schema(title = "执行结束时间", required = false) + //@InfluxTag + private Long endTime; + + @Schema(title = "执行耗时时间", required = false) + private Long elapseTime; + + @Schema(title = "来源渠道", required = false) + private String channel; + + @Schema(title = "执行人", required = true) + private String starter; + + @Schema(title = "组织架构详细信息", required = true) + private OrganizationDetailInfo organizationDetailInfo; + + @Schema(title = "扩展数据", required = false) + private Map extData; +} \ No newline at end of file diff --git a/modules/ci-commons/src/main/java/cd/casic/ci/commons/commonapi/pojo/BuildHistoryPage.java b/modules/ci-commons/src/main/java/cd/casic/ci/commons/commonapi/pojo/BuildHistoryPage.java new file mode 100644 index 0000000..b455ec0 --- /dev/null +++ b/modules/ci-commons/src/main/java/cd/casic/ci/commons/commonapi/pojo/BuildHistoryPage.java @@ -0,0 +1,42 @@ +package cd.casic.ci.commons.commonapi.pojo; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.util.List; + +@Data +@Schema(title = "构建历史-分页数据包装模型") +public class BuildHistoryPage { + @Schema(title = "总记录行数", required = true) + private final Long count; + + @Schema(title = "第几页", required = true) + private final Integer page; + + @Schema(title = "每页多少条", required = true) + private final Integer pageSize; + + @Schema(title = "总共多少页", required = true) + private final Integer totalPages; + + @Schema(title = "数据", required = true) + private final List records; + + @Schema(title = "是否拥有下载构建的权限", required = true) + private final Boolean hasDownloadPermission; + + @Schema(title = "最新的编排版本号", required = true) + private final Integer pipelineVersion; + + public BuildHistoryPage(Integer page, Integer pageSize, Long count, List records, + Boolean hasDownloadPermission, Integer pipelineVersion) { + this.count = count; + this.page = page; + this.pageSize = pageSize; + this.totalPages = (int) Math.ceil(count.doubleValue() / pageSize); + this.records = records; + this.hasDownloadPermission = hasDownloadPermission; + this.pipelineVersion = pipelineVersion; + } +} \ No newline at end of file diff --git a/modules/ci-commons/src/main/java/cd/casic/ci/commons/commonapi/pojo/CommonEnv.java b/modules/ci-commons/src/main/java/cd/casic/ci/commons/commonapi/pojo/CommonEnv.java new file mode 100644 index 0000000..d74c5b5 --- /dev/null +++ b/modules/ci-commons/src/main/java/cd/casic/ci/commons/commonapi/pojo/CommonEnv.java @@ -0,0 +1,26 @@ +package cd.casic.ci.commons.commonapi.pojo; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +public class CommonEnv { + private static final Map envMap = new ConcurrentHashMap<>(); + private static final Logger logger = LoggerFactory.getLogger(CommonEnv.class); + + private CommonEnv() { + // Private constructor to prevent instantiation + } + + public static void addCommonEnv(Map env) { + logger.info("Add the env({}) to common environment", env); + envMap.putAll(env); + } + + public static Map getCommonEnv() { + return new HashMap<>(envMap); + } +} \ No newline at end of file diff --git a/modules/ci-commons/src/main/java/cd/casic/ci/commons/commonapi/pojo/ErrorCode.java b/modules/ci-commons/src/main/java/cd/casic/ci/commons/commonapi/pojo/ErrorCode.java new file mode 100644 index 0000000..e21618d --- /dev/null +++ b/modules/ci-commons/src/main/java/cd/casic/ci/commons/commonapi/pojo/ErrorCode.java @@ -0,0 +1,33 @@ +package cd.casic.ci.commons.commonapi.pojo; + +public class ErrorCode { + // 蓝盾系统错误 + public static final int SYSTEM_DAEMON_INTERRUPTED = 2189500; + public static final int SYSTEM_SERVICE_ERROR = 2189501; + public static final int SYSTEM_OUTTIME_ERROR = 2189502; + public static final int SYSTEM_WORKER_LOADING_ERROR = 2189503; + public static final int SYSTEM_WORKER_INITIALIZATION_ERROR = 2189504; + public static final int SYSTEM_INNER_TASK_ERROR = 2189505; + + // 第三方接入平台错误 + public static final int THIRD_PARTY_INTERFACE_ERROR = 2199501; + public static final int THIRD_PARTY_BUILD_ENV_ERROR = 2199502; + + // 插件执行错误 + public static final int PLUGIN_DEFAULT_ERROR = 2199001; + + // 用户使用错误 + public static final int USER_INPUT_INVAILD = 2199002; + public static final int USER_RESOURCE_NOT_FOUND = 2199003; + public static final int USER_TASK_OPERATE_FAIL = 2199004; + public static final int USER_JOB_OUTTIME_LIMIT = 2199005; + public static final int USER_TASK_OUTTIME_LIMIT = 2199006; + public static final int USER_QUALITY_CHECK_FAIL = 2199007; + public static final int USER_SCRIPT_COMMAND_INVAILD = 2199009; + public static final int USER_STAGE_FASTKILL_TERMINATE = 2199010; + public static final int USER_SCRIPT_TASK_FAIL = 2199011; + + private ErrorCode() { + // Private constructor to prevent instantiation + } +} \ No newline at end of file diff --git a/modules/ci-commons/src/main/java/cd/casic/ci/commons/commonapi/pojo/ErrorInfo.java b/modules/ci-commons/src/main/java/cd/casic/ci/commons/commonapi/pojo/ErrorInfo.java new file mode 100644 index 0000000..b0e7d13 --- /dev/null +++ b/modules/ci-commons/src/main/java/cd/casic/ci/commons/commonapi/pojo/ErrorInfo.java @@ -0,0 +1,35 @@ +package cd.casic.ci.commons.commonapi.pojo; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +@Data +@Schema(title = "插件错误信息") +public class ErrorInfo { + @Schema(title = "阶段ID", required = false) + private String stageId; + + @Schema(title = "作业ID", required = false) + private String containerId; + + @Schema(title = "构建矩阵标识", required = false) + private Boolean matrixFlag; + + @Schema(title = "插件ID", required = false) + private String taskId; + + @Schema(title = "插件名称", required = false) + private String taskName; + + @Schema(title = "插件编号", required = false) + private String atomCode; + + @Schema(title = "错误类型", required = false) + private Integer errorType; + + @Schema(title = "错误码", required = true) + private Integer errorCode; + + @Schema(title = "错误信息", required = false) + private String errorMsg; +} \ No newline at end of file diff --git a/modules/ci-commons/src/main/java/cd/casic/ci/commons/commonapi/pojo/ErrorType.java b/modules/ci-commons/src/main/java/cd/casic/ci/commons/commonapi/pojo/ErrorType.java new file mode 100644 index 0000000..559d25e --- /dev/null +++ b/modules/ci-commons/src/main/java/cd/casic/ci/commons/commonapi/pojo/ErrorType.java @@ -0,0 +1,75 @@ +package cd.casic.ci.commons.commonapi.pojo; + +public enum ErrorType { + SYSTEM("system", 0), + USER("user", 1), + THIRD_PARTY("thirdParty", 2), + PLUGIN("plugin", 3); + + private final String typeName; + private final int num; + + ErrorType(String typeName, int num) { + this.typeName = typeName; + this.num = num; + } + + public static ErrorType getErrorType(String name) { + if (name == null) { + return null; + } + for (ErrorType type : values()) { + if (type.name().equals(name)) { + return type; + } + } + return null; + } + public static ErrorType getErrorType(Integer ordinal) { + ErrorType result; + if (ordinal == null) { + result = ErrorType.PLUGIN; + } else { + switch (ordinal) { + case 0: + result = ErrorType.SYSTEM; + break; + case 1: + result = ErrorType.USER; + break; + case 2: + result = ErrorType.THIRD_PARTY; + break; + default: + result = ErrorType.PLUGIN; + } + } + return result; + } + /* public static ErrorType getErrorType(Integer ordinal) { + if (ordinal == null) { + return PLUGIN; + } + return switch (ordinal) { + case 0 -> SYSTEM; + case 1 -> USER; + case 2 -> THIRD_PARTY; + default -> PLUGIN; + }; + }*/ + + /* public String getI18n(String language) { + return MessageUtil.getMessageByLocale( + "errorType." + this.typeName, + language + ); + }*/ + + public String getTypeName() { + return typeName; + } + + public int getNum() { + return num; + } +} \ No newline at end of file diff --git a/modules/ci-commons/src/main/java/cd/casic/ci/commons/commonapi/pojo/FieldLocaleInfo.java b/modules/ci-commons/src/main/java/cd/casic/ci/commons/commonapi/pojo/FieldLocaleInfo.java new file mode 100644 index 0000000..2c927b0 --- /dev/null +++ b/modules/ci-commons/src/main/java/cd/casic/ci/commons/commonapi/pojo/FieldLocaleInfo.java @@ -0,0 +1,17 @@ +package cd.casic.ci.commons.commonapi.pojo; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +@Data +@Schema(title = "字段国际化信息") +public class FieldLocaleInfo { + @Schema(title = "字段名称") + private String fieldName; + + @Schema(title = "字段值") + private String fieldValue; + + @Schema(title = "语言信息") + private String language; +} \ No newline at end of file diff --git a/modules/ci-commons/src/main/java/cd/casic/ci/commons/commonapi/pojo/I18Variable.java b/modules/ci-commons/src/main/java/cd/casic/ci/commons/commonapi/pojo/I18Variable.java new file mode 100644 index 0000000..97d8dca --- /dev/null +++ b/modules/ci-commons/src/main/java/cd/casic/ci/commons/commonapi/pojo/I18Variable.java @@ -0,0 +1,23 @@ +package cd.casic.ci.commons.commonapi.pojo; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.util.List; + +@Data +@Schema(title = "国际化变量") +public class I18Variable { + @Schema(title = "国际化变量名") + private String code; + + @Schema(title = "国际化参数") + private List params; + + @Schema(title = "默认信息") + private String defaultMessage; + +// public String toJsonStr() { +// return JsonUtil.toJson(this, false); +// } +} \ No newline at end of file diff --git a/modules/ci-commons/src/main/java/cd/casic/ci/commons/commonapi/pojo/I18nFieldInfo.java b/modules/ci-commons/src/main/java/cd/casic/ci/commons/commonapi/pojo/I18nFieldInfo.java new file mode 100644 index 0000000..8da57df --- /dev/null +++ b/modules/ci-commons/src/main/java/cd/casic/ci/commons/commonapi/pojo/I18nFieldInfo.java @@ -0,0 +1,33 @@ +package cd.casic.ci.commons.commonapi.pojo; + +import cd.casic.ci.commons.commonapi.enums.I18nSourceEnum; +import cd.casic.ci.commons.commonapi.enums.I18nTranslateTypeEnum; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.lang.reflect.Field; + +@Data +@Schema(title = "国际化字段信息") +public class I18nFieldInfo { + @Schema(title = "字段") + private Field field; + + @Schema(title = "字段所属对象") + private Object entity; + + @Schema(title = "翻译信息来源") + private I18nSourceEnum source = I18nSourceEnum.PROPERTIES; + + @Schema(title = "翻译类型") + private I18nTranslateTypeEnum translateType = I18nTranslateTypeEnum.NAME; + + @Schema(title = "字段前缀名称") + private String keyPrefixName = ""; + + @Schema(title = "是否复用接口定义的公共前缀") + private boolean reusePrefixFlag = true; + + @Schema(title = "字段转换名称") + private String convertName = ""; +} \ No newline at end of file diff --git a/modules/ci-commons/src/main/java/cd/casic/ci/commons/commonapi/pojo/I18nMessage.java b/modules/ci-commons/src/main/java/cd/casic/ci/commons/commonapi/pojo/I18nMessage.java new file mode 100644 index 0000000..355fc5d --- /dev/null +++ b/modules/ci-commons/src/main/java/cd/casic/ci/commons/commonapi/pojo/I18nMessage.java @@ -0,0 +1,20 @@ +package cd.casic.ci.commons.commonapi.pojo; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +@Data +@Schema(title = "国际化信息") +public class I18nMessage { + @Schema(title = "模块标识") + private String moduleCode; + + @Schema(title = "国际化语言信息") + private String language; + + @Schema(title = "国际化变量名") + private String key; + + @Schema(title = "国际化变量值") + private String value; +} \ No newline at end of file diff --git a/modules/ci-commons/src/main/java/cd/casic/ci/commons/commonapi/pojo/IdValue.java b/modules/ci-commons/src/main/java/cd/casic/ci/commons/commonapi/pojo/IdValue.java new file mode 100644 index 0000000..1b4e088 --- /dev/null +++ b/modules/ci-commons/src/main/java/cd/casic/ci/commons/commonapi/pojo/IdValue.java @@ -0,0 +1,11 @@ +package cd.casic.ci.commons.commonapi.pojo; + +import lombok.AllArgsConstructor; +import lombok.Data; + +@Data +@AllArgsConstructor +public class IdValue { + private String id; + private String value; +} \ No newline at end of file diff --git a/modules/ci-commons/src/main/java/cd/casic/ci/commons/commonapi/pojo/LocaleInfo.java b/modules/ci-commons/src/main/java/cd/casic/ci/commons/commonapi/pojo/LocaleInfo.java new file mode 100644 index 0000000..b9b7fce --- /dev/null +++ b/modules/ci-commons/src/main/java/cd/casic/ci/commons/commonapi/pojo/LocaleInfo.java @@ -0,0 +1,11 @@ +package cd.casic.ci.commons.commonapi.pojo; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +@Data +@Schema(title = "语言环境信息") +public class LocaleInfo { + @Schema(title = "语言") + private String language; +} \ No newline at end of file diff --git a/modules/ci-commons/src/main/java/cd/casic/ci/commons/commonapi/pojo/MessageCodeDetail.java b/modules/ci-commons/src/main/java/cd/casic/ci/commons/commonapi/pojo/MessageCodeDetail.java new file mode 100644 index 0000000..aa756eb --- /dev/null +++ b/modules/ci-commons/src/main/java/cd/casic/ci/commons/commonapi/pojo/MessageCodeDetail.java @@ -0,0 +1,26 @@ +package cd.casic.ci.commons.commonapi.pojo; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +@Data +@Schema(title = "返回码详情") +public class MessageCodeDetail { + @Schema(title = "主键ID", required = true) + private String id; + + @Schema(title = "信息码", required = true) + private String messageCode; + + @Schema(title = "模块代码", required = true) + private String moduleCode; + + @Schema(title = "中文简体描述信息", required = true) + private String messageDetailZhCn; + + @Schema(title = "中文繁体描述信息", required = false) + private String messageDetailZhTw; + + @Schema(title = "英文描述信息", required = false) + private String messageDetailEn; +} \ No newline at end of file diff --git a/modules/ci-commons/src/main/java/cd/casic/ci/commons/commonapi/pojo/OS.java b/modules/ci-commons/src/main/java/cd/casic/ci/commons/commonapi/pojo/OS.java new file mode 100644 index 0000000..8045ad7 --- /dev/null +++ b/modules/ci-commons/src/main/java/cd/casic/ci/commons/commonapi/pojo/OS.java @@ -0,0 +1,31 @@ +package cd.casic.ci.commons.commonapi.pojo; + +public enum OS { + MACOS, + WINDOWS, + LINUX; + + public static OS parse(String os) { + // 处理传入的字符串为 null 的情况 + if (os == null) { + return null; + } + // 将传入的字符串转换为大写,以避免大小写问题 + String upperCaseOs = os.toUpperCase(); + OS result = null; + switch (upperCaseOs) { + case "MACOS": + result = MACOS; + break; + case "WINDOWS": + result = WINDOWS; + break; + case "LINUX": + result = LINUX; + break; + default: + result = null; + } + return result; + } +} \ No newline at end of file diff --git a/modules/ci-commons/src/main/java/cd/casic/ci/commons/commonapi/pojo/OrganizationDetailInfo.java b/modules/ci-commons/src/main/java/cd/casic/ci/commons/commonapi/pojo/OrganizationDetailInfo.java new file mode 100644 index 0000000..da815e7 --- /dev/null +++ b/modules/ci-commons/src/main/java/cd/casic/ci/commons/commonapi/pojo/OrganizationDetailInfo.java @@ -0,0 +1,26 @@ +package cd.casic.ci.commons.commonapi.pojo; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +@Data +@Schema(title = "组织架构详细信息") +public class OrganizationDetailInfo { + @Schema(title = "事业群ID") + private Integer bgId; + + @Schema(title = "事业群名字") + private String bgName; + + @Schema(title = "部门ID") + private Integer deptId; + + @Schema(title = "部门名字") + private String deptName; + + @Schema(title = "中心ID") + private Integer centerId; + + @Schema(title = "中心名字") + private String centerName; +} \ No newline at end of file diff --git a/modules/ci-commons/src/main/java/cd/casic/ci/commons/commonapi/pojo/Page.java b/modules/ci-commons/src/main/java/cd/casic/ci/commons/commonapi/pojo/Page.java new file mode 100644 index 0000000..bd1c0f2 --- /dev/null +++ b/modules/ci-commons/src/main/java/cd/casic/ci/commons/commonapi/pojo/Page.java @@ -0,0 +1,34 @@ +package cd.casic.ci.commons.commonapi.pojo; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.util.List; + +@Data +@Schema(title = "分页数据包装模型") +public class Page { + @Schema(title = "总记录行数", required = true) + private final Long count; + + @Schema(title = "第几页", required = true) + private final Integer page; + + @Schema(title = "每页多少条", required = true) + private final Integer pageSize; + + @Schema(title = "总共多少页", required = true) + private final Integer totalPages; + + @Schema(title = "数据", required = true) +// @BkFieldI18n + private final List records; + + public Page(Integer page, Integer pageSize, Long count, List records) { + this.count = count; + this.page = page; + this.pageSize = pageSize; + this.totalPages = (int) Math.ceil(count.doubleValue() / pageSize); + this.records = records; + } +} \ No newline at end of file diff --git a/modules/ci-commons/src/main/java/cd/casic/ci/commons/commonapi/pojo/Pagination.java b/modules/ci-commons/src/main/java/cd/casic/ci/commons/commonapi/pojo/Pagination.java new file mode 100644 index 0000000..a7a691d --- /dev/null +++ b/modules/ci-commons/src/main/java/cd/casic/ci/commons/commonapi/pojo/Pagination.java @@ -0,0 +1,19 @@ +package cd.casic.ci.commons.commonapi.pojo; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.util.List; + +@Data +@Schema(title = "分页数据包装模型") +public class Pagination { + @Schema(title = "是否有下一页", required = true) + private Boolean hasNext; + + @Schema(title = "数据", required = true) + private List records; + + @Schema(title = "总记录行数", required = false) + private Long count; +} \ No newline at end of file diff --git a/modules/ci-commons/src/main/java/cd/casic/ci/commons/commonapi/pojo/PipelineAsCodeSettings.java b/modules/ci-commons/src/main/java/cd/casic/ci/commons/commonapi/pojo/PipelineAsCodeSettings.java new file mode 100644 index 0000000..cb3cfb9 --- /dev/null +++ b/modules/ci-commons/src/main/java/cd/casic/ci/commons/commonapi/pojo/PipelineAsCodeSettings.java @@ -0,0 +1,11 @@ +package cd.casic.ci.commons.commonapi.pojo; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +@Data +@Schema(title = "设置-YAML流水线功能设置") +public class PipelineAsCodeSettings { + @Schema(title = "是否支持YAML流水线功能", required = true) + private Boolean enable = false; +} \ No newline at end of file diff --git a/modules/ci-commons/src/main/java/cd/casic/ci/commons/commonapi/pojo/Result.java b/modules/ci-commons/src/main/java/cd/casic/ci/commons/commonapi/pojo/Result.java new file mode 100644 index 0000000..93b4535 --- /dev/null +++ b/modules/ci-commons/src/main/java/cd/casic/ci/commons/commonapi/pojo/Result.java @@ -0,0 +1,43 @@ +package cd.casic.ci.commons.commonapi.pojo; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.AllArgsConstructor; +import lombok.Data; + +@Data +@AllArgsConstructor +@Schema(title = "数据返回包装模型") +public class Result { + @Schema(title = "状态码", required = true) + private final Integer status; + + @Schema(title = "错误信息", required = false) + private final String message; + + @Schema(title = "数据", required = false) +// @BkFieldI18n + private final T data; + + public Result(T data) { + this(0, null, data); + } + + public Result(String message, T data) { + this(0, message, data); + } + + public Result(Integer status, String message) { + this(status, message, null); + } + + @JsonIgnore + public boolean isOk() { + return status == 0; + } + + @JsonIgnore + public boolean isNotOk() { + return !isOk(); + } +} \ No newline at end of file diff --git a/modules/ci-commons/src/main/java/cd/casic/ci/commons/commonapi/pojo/ShardingRoutingRule.java b/modules/ci-commons/src/main/java/cd/casic/ci/commons/commonapi/pojo/ShardingRoutingRule.java new file mode 100644 index 0000000..dca88f2 --- /dev/null +++ b/modules/ci-commons/src/main/java/cd/casic/ci/commons/commonapi/pojo/ShardingRoutingRule.java @@ -0,0 +1,30 @@ +package cd.casic.ci.commons.commonapi.pojo; + +import cd.casic.ci.commons.commonapi.enums.SystemModuleEnum; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +@Data +@Schema(title = "DB分片路由规则") +public class ShardingRoutingRule { + @Schema(title = "集群名称") + private String clusterName; + + @Schema(title = "模块标识") + private SystemModuleEnum moduleCode; + + @Schema(title = "数据源名称") + private String dataSourceName; + + @Schema(title = "数据库表名称") + private String tableName; + + @Schema(title = "规则类型") + private ShardingRuleTypeEnum type; + + @Schema(title = "路由规则名称") + private String routingName; + + @Schema(title = "路由规则值") + private String routingRule; +} \ No newline at end of file diff --git a/modules/ci-commons/src/main/java/cd/casic/ci/commons/commonapi/pojo/ShardingRuleTypeEnum.java b/modules/ci-commons/src/main/java/cd/casic/ci/commons/commonapi/pojo/ShardingRuleTypeEnum.java new file mode 100644 index 0000000..cd8980b --- /dev/null +++ b/modules/ci-commons/src/main/java/cd/casic/ci/commons/commonapi/pojo/ShardingRuleTypeEnum.java @@ -0,0 +1,8 @@ +package cd.casic.ci.commons.commonapi.pojo; + +public enum ShardingRuleTypeEnum { + DB, + TABLE, + ARCHIVE_DB, + ARCHIVE_TABLE +} \ No newline at end of file diff --git a/modules/ci-commons/src/main/java/cd/casic/ci/commons/commonapi/pojo/SimpleResult.java b/modules/ci-commons/src/main/java/cd/casic/ci/commons/commonapi/pojo/SimpleResult.java new file mode 100644 index 0000000..69d7a9b --- /dev/null +++ b/modules/ci-commons/src/main/java/cd/casic/ci/commons/commonapi/pojo/SimpleResult.java @@ -0,0 +1,30 @@ +package cd.casic.ci.commons.commonapi.pojo; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.util.Set; + +@Data +@Schema(title = "状态") +public class SimpleResult { + @Schema(title = "是否成功", required = true) + private Boolean success; + + @Schema(title = "错误信息", required = false) + private String message; + + @Schema(title = "错误码信息", required = false) + private Error error; + + @Schema(title = "ENV中需要被忽略的调度机器") + private Set ignoreAgentIds; +} + +@Data +@Schema(title = "第三方构建信息模型-错误信息") +class Error { + private String errorType; + private String errorMessage; + private Integer errorCode; +} \ No newline at end of file diff --git a/modules/ci-commons/src/main/java/cd/casic/ci/commons/commonapi/pojo/Zone.java b/modules/ci-commons/src/main/java/cd/casic/ci/commons/commonapi/pojo/Zone.java new file mode 100644 index 0000000..275b468 --- /dev/null +++ b/modules/ci-commons/src/main/java/cd/casic/ci/commons/commonapi/pojo/Zone.java @@ -0,0 +1,25 @@ +package cd.casic.ci.commons.commonapi.pojo; + + +public enum Zone { + DEFAULT("默认"), + SHENZHEN("深圳"), + SHANGHAI("上海"), + CHENGDU("成都"), + TIANJIN("天津"), + GITHUB("GitHub"), + EXTERNAL("外网"); + + private final String name; + + Zone(String name) { + this.name = name; + } + + /* public String getI18n(String language) { + return MessageUtil.getMessageByLocale( + "ZONE." + this.name(), + language + ); + }*/ +} \ No newline at end of file diff --git a/modules/ci-commons/src/main/java/cd/casic/ci/commons/commonapi/pojo/agent/AgentErrorExitData.java b/modules/ci-commons/src/main/java/cd/casic/ci/commons/commonapi/pojo/agent/AgentErrorExitData.java new file mode 100644 index 0000000..6672095 --- /dev/null +++ b/modules/ci-commons/src/main/java/cd/casic/ci/commons/commonapi/pojo/agent/AgentErrorExitData.java @@ -0,0 +1,39 @@ +/* + * Tencent is pleased to support the open source community by making BK-CI 蓝鲸持续集成平台 available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * BK-CI 蓝鲸持续集成平台 is licensed under the MIT license. + * + * A copy of the MIT License is included in this file. + * + * ... + */ + +package cd.casic.ci.commons.commonapi.pojo.agent; + +public class AgentErrorExitData { + private String errorEnum; + private String message; + + public AgentErrorExitData(String errorEnum, String message) { + this.errorEnum = errorEnum; + this.message = message; + } + + public String getErrorEnum() { + return errorEnum; + } + + public void setErrorEnum(String errorEnum) { + this.errorEnum = errorEnum; + } + + public String getMessage() { + return message; + } + + public void setMessage(String message) { + this.message = message; + } +} \ No newline at end of file diff --git a/modules/ci-commons/src/main/java/cd/casic/ci/commons/commonapi/pojo/agent/AgentPropsInfo.java b/modules/ci-commons/src/main/java/cd/casic/ci/commons/commonapi/pojo/agent/AgentPropsInfo.java new file mode 100644 index 0000000..a7bf280 --- /dev/null +++ b/modules/ci-commons/src/main/java/cd/casic/ci/commons/commonapi/pojo/agent/AgentPropsInfo.java @@ -0,0 +1,59 @@ +/* + * Tencent is pleased to support the open source community by making BK-CI 蓝鲸持续集成平台 available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * BK-CI 蓝鲸持续集成平台 is licensed under the MIT license. + * + * A copy of the MIT License is included in this file. + * + * ... + */ + +package cd.casic.ci.commons.commonapi.pojo.agent; + +import io.swagger.v3.oas.annotations.media.Schema; + +import java.util.List; + +@Schema(title = "Agent属性信息") +public class AgentPropsInfo { + @Schema(title = "agent运行系统的架构信息") + private String arch; + + @Schema(title = "jdk版本信息") + private List jdkVersion; + + @Schema(title = "docker init 文件升级信息") + private DockerInitFileInfo dockerInitFileInfo; + + public AgentPropsInfo(String arch, List jdkVersion, DockerInitFileInfo dockerInitFileInfo) { + this.arch = arch; + this.jdkVersion = jdkVersion; + this.dockerInitFileInfo = dockerInitFileInfo; + } + + public String getArch() { + return arch; + } + + public void setArch(String arch) { + this.arch = arch; + } + + public List getJdkVersion() { + return jdkVersion; + } + + public void setJdkVersion(List jdkVersion) { + this.jdkVersion = jdkVersion; + } + + public DockerInitFileInfo getDockerInitFileInfo() { + return dockerInitFileInfo; + } + + public void setDockerInitFileInfo(DockerInitFileInfo dockerInitFileInfo) { + this.dockerInitFileInfo = dockerInitFileInfo; + } +} \ No newline at end of file diff --git a/modules/ci-commons/src/main/java/cd/casic/ci/commons/commonapi/pojo/agent/DockerInitFileInfo.java b/modules/ci-commons/src/main/java/cd/casic/ci/commons/commonapi/pojo/agent/DockerInitFileInfo.java new file mode 100644 index 0000000..6024da3 --- /dev/null +++ b/modules/ci-commons/src/main/java/cd/casic/ci/commons/commonapi/pojo/agent/DockerInitFileInfo.java @@ -0,0 +1,45 @@ +/* + * Tencent is pleased to support the open source community by making BK-CI 蓝鲸持续集成平台 available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * BK-CI 蓝鲸持续集成平台 is licensed under the MIT license. + * + * A copy of the MIT License is included in this file. + * + * ... + */ + +package cd.casic.ci.commons.commonapi.pojo.agent; + +import io.swagger.v3.oas.annotations.media.Schema; + +@Schema(title = "docker init 文件升级信息") +public class DockerInitFileInfo { + @Schema(title = "文件md5值") + private String fileMd5; + + @Schema(title = "目前只支持linux机器,所以其他系统不需要检查") + private boolean needUpgrade; + + public DockerInitFileInfo(String fileMd5, boolean needUpgrade) { + this.fileMd5 = fileMd5; + this.needUpgrade = needUpgrade; + } + + public String getFileMd5() { + return fileMd5; + } + + public void setFileMd5(String fileMd5) { + this.fileMd5 = fileMd5; + } + + public boolean isNeedUpgrade() { + return needUpgrade; + } + + public void setNeedUpgrade(boolean needUpgrade) { + this.needUpgrade = needUpgrade; + } +} \ No newline at end of file diff --git a/modules/ci-commons/src/main/java/cd/casic/ci/commons/commonapi/pojo/agent/NewHeartbeatInfo.java b/modules/ci-commons/src/main/java/cd/casic/ci/commons/commonapi/pojo/agent/NewHeartbeatInfo.java new file mode 100644 index 0000000..3f2162b --- /dev/null +++ b/modules/ci-commons/src/main/java/cd/casic/ci/commons/commonapi/pojo/agent/NewHeartbeatInfo.java @@ -0,0 +1,120 @@ +/* + * Tencent is pleased to support the open source community by making BK-CI 蓝鲸持续集成平台 available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * BK-CI 蓝鲸持续集成平台 is licensed under the MIT license. + * + * A copy of the MIT License is included in this file. + * + * ... + */ + +package cd.casic.ci.commons.commonapi.pojo.agent; + +import io.swagger.v3.oas.annotations.media.Schema; + +import java.util.List; + +@Schema(title = "心跳信息模型") +public class NewHeartbeatInfo { + @Schema(title = "主版本") + private String masterVersion; + + @Schema(title = "从属版本") + private String slaveVersion; + + @Schema(title = "主机名") + private String hostName; + + @Schema(title = "构建机模型") + private String agentIp; + + @Schema(title = "并行任务计数") + private int parallelTaskCount; + + @Schema(title = "构建机安装路径") + private String agentInstallPath; + + @Schema(title = "启动者") + private String startedUser; + + @Schema(title = "第三方构建信息列表") + private List taskList; + + @Schema(title = "Agent属性信息") + private AgentPropsInfo props; + + @Schema(title = "构建机id") + private Long agentId; + + @Schema(title = "项目id") + private String projectId; + + @Schema(title = "心跳时间戳") + private Long heartbeatTime; + + @Schema(title = "忙碌运行中任务数量") + private int busyTaskSize = 0; + + @Schema(title = "docker并行任务计数") + private Integer dockerParallelTaskCount; + + @Schema(title = "docker构建信息列表") + private List dockerTaskList; + + @Schema(title = "忙碌运行docker中任务数量") + private int dockerBusyTaskSize = 0; + + @Schema(title = "Agent退出的错误信息") + private AgentErrorExitData errorExitData; + + public NewHeartbeatInfo(String masterVersion, String slaveVersion, String hostName, String agentIp, + int parallelTaskCount, String agentInstallPath, String startedUser, + List taskList, AgentPropsInfo props, Long agentId, + String projectId, Long heartbeatTime, int busyTaskSize, + Integer dockerParallelTaskCount, List dockerTaskList, + int dockerBusyTaskSize, AgentErrorExitData errorExitData) { + this.masterVersion = masterVersion; + this.slaveVersion = slaveVersion; + this.hostName = hostName; + this.agentIp = agentIp; + this.parallelTaskCount = parallelTaskCount; + this.agentInstallPath = agentInstallPath; + this.startedUser = startedUser; + this.taskList = taskList; + this.props = props; + this.agentId = agentId; + this.projectId = projectId; + this.heartbeatTime = heartbeatTime; + this.busyTaskSize = busyTaskSize; + this.dockerParallelTaskCount = dockerParallelTaskCount; + this.dockerTaskList = dockerTaskList; + this.dockerBusyTaskSize = dockerBusyTaskSize; + this.errorExitData = errorExitData; + } + + public static NewHeartbeatInfo dummyHeartbeat(String projectId, Long agentId) { + return new NewHeartbeatInfo( + "", + "", + "", + "", + 0, + "", + "", + List.of(), + new AgentPropsInfo("", null, null), + agentId, + projectId, + System.currentTimeMillis(), + 0, + 0, + List.of(), + 0, + null + ); + } + + // Getters and Setters +} \ No newline at end of file diff --git a/modules/ci-commons/src/main/java/cd/casic/ci/commons/commonapi/pojo/agent/ThirdPartyBuildInfo.java b/modules/ci-commons/src/main/java/cd/casic/ci/commons/commonapi/pojo/agent/ThirdPartyBuildInfo.java new file mode 100644 index 0000000..3bb93ef --- /dev/null +++ b/modules/ci-commons/src/main/java/cd/casic/ci/commons/commonapi/pojo/agent/ThirdPartyBuildInfo.java @@ -0,0 +1,27 @@ +package cd.casic.ci.commons.commonapi.pojo.agent; + +import io.swagger.v3.oas.annotations.media.Schema; + +@Schema(title = "第三方构建信息") +public class ThirdPartyBuildInfo { + @Schema(title = "项目id") + private String projectId; + + @Schema(title = "构建id") + private String buildId; + + @Schema(title = "构建机编排序号") + private String vmSeqId; + + @Schema(title = "工作空间") + private String workspace; + + public ThirdPartyBuildInfo(String projectId, String buildId, String vmSeqId, String workspace) { + this.projectId = projectId; + this.buildId = buildId; + this.vmSeqId = vmSeqId; + this.workspace = workspace; + } + + // Getters and Setters +} diff --git a/modules/ci-commons/src/main/java/cd/casic/ci/commons/commonapi/pojo/agent/ThirdPartyDockerBuildInfo.java b/modules/ci-commons/src/main/java/cd/casic/ci/commons/commonapi/pojo/agent/ThirdPartyDockerBuildInfo.java new file mode 100644 index 0000000..d3fd033 --- /dev/null +++ b/modules/ci-commons/src/main/java/cd/casic/ci/commons/commonapi/pojo/agent/ThirdPartyDockerBuildInfo.java @@ -0,0 +1,35 @@ +/* + * Tencent is pleased to support the open source community by making BK-CI 蓝鲸持续集成平台 available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * BK-CI 蓝鲸持续集成平台 is licensed under the MIT license. + * + * A copy of the MIT License is included in this file. + * + * ... + */ + +package cd.casic.ci.commons.commonapi.pojo.agent; + +import io.swagger.v3.oas.annotations.media.Schema; + +@Schema(title = "第三方构建Docker信息") +public class ThirdPartyDockerBuildInfo { + @Schema(title = "项目id") + private String projectId; + + @Schema(title = "构建id") + private String buildId; + + @Schema(title = "构建机编排序号") + private String vmSeqId; + + public ThirdPartyDockerBuildInfo(String projectId, String buildId, String vmSeqId) { + this.projectId = projectId; + this.buildId = buildId; + this.vmSeqId = vmSeqId; + } + + // Getters and Setters +} \ No newline at end of file diff --git a/modules/ci-commons/src/main/java/cd/casic/ci/commons/commonapi/pojo/agent/UpgradeItem.java b/modules/ci-commons/src/main/java/cd/casic/ci/commons/commonapi/pojo/agent/UpgradeItem.java new file mode 100644 index 0000000..7354849 --- /dev/null +++ b/modules/ci-commons/src/main/java/cd/casic/ci/commons/commonapi/pojo/agent/UpgradeItem.java @@ -0,0 +1,39 @@ +/* + * Tencent is pleased to support the open source community by making BK-CI 蓝鲸持续集成平台 available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * BK-CI 蓝鲸持续集成平台 is licensed under the MIT license. + * + * A copy of the MIT License is included in this file. + * + * ... + */ + +package cd.casic.ci.commons.commonapi.pojo.agent; + +import io.swagger.v3.oas.annotations.media.Schema; + +@Schema(title = "需要升级的项") +public class UpgradeItem { + @Schema(title = "升级go agent") + private boolean agent; + + @Schema(title = "升级worker") + private boolean worker; + + @Schema(title = "升级jdk") + private boolean jdk; + + @Schema(title = "升级docker init 脚本") + private boolean dockerInitFile; + + public UpgradeItem(boolean agent, boolean worker, boolean jdk, boolean dockerInitFile) { + this.agent = agent; + this.worker = worker; + this.jdk = jdk; + this.dockerInitFile = dockerInitFile; + } + + // Getters and Setters +} \ No newline at end of file diff --git a/modules/ci-commons/src/main/java/cd/casic/ci/commons/constant/OpsHeader.java b/modules/ci-commons/src/main/java/cd/casic/ci/commons/constant/OpsHeader.java index d339145..ce0ba6a 100644 --- a/modules/ci-commons/src/main/java/cd/casic/ci/commons/constant/OpsHeader.java +++ b/modules/ci-commons/src/main/java/cd/casic/ci/commons/constant/OpsHeader.java @@ -1,7 +1,5 @@ package cd.casic.ci.commons.constant; -import lombok.NoArgsConstructor; -import lombok.val; /** * @author by mianbin @@ -9,7 +7,7 @@ import lombok.val; * @Description 重要,莫动 * @Date 2025/3/19 14:49 */ -@NoArgsConstructor + public final class OpsHeader { public static final String AUTH_HEADER_USER_ID = "X-DEVOPS-UID"; diff --git a/modules/ci-environment/pom.xml b/modules/ci-environment/pom.xml new file mode 100644 index 0000000..b9aa22c --- /dev/null +++ b/modules/ci-environment/pom.xml @@ -0,0 +1,39 @@ + + + 4.0.0 + + cd.casic.boot + modules + ${revision} + + + cd.casic.pipeline + ci-environment + + + 17 + 17 + UTF-8 + + + + + cd.casic.pipeline + ci-commons + + + + cd.casic.pipeline + ci-market + 2.0.0-jdk17 + + + + cd.casic.boot + spring-boot-starter-mybatis + + + + \ No newline at end of file diff --git a/modules/ci-environment/src/main/java/cd/casic/pipeline/Main.java b/modules/ci-environment/src/main/java/cd/casic/pipeline/Main.java new file mode 100644 index 0000000..86c364d --- /dev/null +++ b/modules/ci-environment/src/main/java/cd/casic/pipeline/Main.java @@ -0,0 +1,7 @@ +package cd.casic.devops; + +public class Main { + public static void main(String[] args) { + System.out.println("Hello world!"); + } +} \ No newline at end of file diff --git a/modules/ci-scm-repository/src/main/java/cd/casic/ci/log/scm/service/github/GithubService.java b/modules/ci-scm-repository/src/main/java/cd/casic/ci/log/scm/service/github/GithubService.java index 4f7d21d..8d3e5f8 100644 --- a/modules/ci-scm-repository/src/main/java/cd/casic/ci/log/scm/service/github/GithubService.java +++ b/modules/ci-scm-repository/src/main/java/cd/casic/ci/log/scm/service/github/GithubService.java @@ -1,11 +1,11 @@ package cd.casic.ci.log.scm.service.github; +import cd.casic.ci.commons.commonapi.constant.CommonMessageCode; import cd.casic.ci.log.scm.dal.pojo.github.GithubBranch; import cd.casic.ci.log.scm.dal.pojo.github.GithubTag; import cd.casic.framework.commons.exception.ServiceException; import cd.casic.framework.commons.util.http.OkhttpUtils; import cd.casic.framework.commons.util.retry.RetryUtils; -import cd.casic.ci.commons.constant.CommonMessageCode; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.ObjectMapper; diff --git a/modules/ci-worker/pom.xml b/modules/ci-worker/pom.xml new file mode 100644 index 0000000..7ff2ada --- /dev/null +++ b/modules/ci-worker/pom.xml @@ -0,0 +1,223 @@ + + + 4.0.0 + + cd.casic.boot + modules + ${revision} + + + + cd.casic.devops + ci-worker + + + + + + + + + + + + + + + cd.casic.ci + ci-commons + + + + cd.casic.boot + spring-boot-starter-mybatis + + + + org.apache.commons + commons-compress + + + com.github.oshi + oshi-core + + + + + org.jvnet.winp + winp + + + + + org.jenkins-ci + version-number + 1.12 + + + + + org.reflections + reflections + + + + + org.apache.commons + commons-exec + + + + + org.apache.commons + commons-compress + + + + + + com.googlecode.plist + dd-plist + + + + + net.dongliu + apk-parser + + + + + org.xerial + sqlite-jdbc + 3.36.0.3 + + + + ch.qos.logback + logback-core + + + + + ch.qos.logback + logback-classic + + + + + com.github.ben-manes.caffeine + caffeine + + + + io.github.resilience4j + resilience4j-circuitbreaker + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + net.java.dev.jna + jna + 5.13.0 + + + + + + + + + + \ No newline at end of file diff --git a/modules/ci-worker/src/main/java/cd/casic/devops/Main.java b/modules/ci-worker/src/main/java/cd/casic/devops/Main.java new file mode 100644 index 0000000..86c364d --- /dev/null +++ b/modules/ci-worker/src/main/java/cd/casic/devops/Main.java @@ -0,0 +1,7 @@ +package cd.casic.devops; + +public class Main { + public static void main(String[] args) { + System.out.println("Hello world!"); + } +} \ No newline at end of file diff --git a/modules/ci-worker/src/main/java/cd/casic/devops/common/process/BkProcessTree.java b/modules/ci-worker/src/main/java/cd/casic/devops/common/process/BkProcessTree.java new file mode 100644 index 0000000..7d0033d --- /dev/null +++ b/modules/ci-worker/src/main/java/cd/casic/devops/common/process/BkProcessTree.java @@ -0,0 +1,937 @@ +/* + * Tencent is pleased to support the open source community by making BK-CI 蓝鲸持续集成平台 available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * BK-CI 蓝鲸持续集成平台 is licensed under the MIT license. + * + * A copy of the MIT License is included in this file. + * + * + * Terms of the MIT License: + * --------------------------------------------------- + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated + * documentation files (the "Software"), to deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or substantial portions of + * the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT + * LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN + * NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +package cd.casic.devops.common.process; + +import com.sun.jna.Memory; +import com.sun.jna.Native; +import com.sun.jna.NativeLong; +import com.sun.jna.ptr.IntByReference; +import com.sun.jna.ptr.NativeLongByReference; +import org.apache.commons.io.FileUtils; +import org.apache.commons.io.IOUtils; +import org.jvnet.winp.WinProcess; +import org.jvnet.winp.WinpException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.BufferedReader; +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileReader; +import java.io.IOException; +import java.io.RandomAccessFile; +import java.io.Serializable; +import java.io.UnsupportedEncodingException; +import java.lang.reflect.Field; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.nio.charset.StandardCharsets; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Set; +import java.util.SortedMap; + +import static com.sun.jna.Pointer.NULL; +import static cd.casic.devops.common.process.jna.GNUCLibrary.LIBC; + +@SuppressWarnings("all") +public abstract class BkProcessTree implements Iterable, ProcessTreeRemoting.IProcessTree, Serializable { + protected final Map processes; + private transient volatile List killers; + private static final boolean IS_LITTLE_ENDIAN = "little".equals(System.getProperty("sun.cpu.endian")); + private static final Logger logger = LoggerFactory.getLogger(BkProcessTree.class); + + private BkProcessTree() { + this.processes = new HashMap<>(); + } + + public final BkProcessTree.OSProcess get(int pid) { + return this.processes.get(pid); + } + + public final Iterator iterator() { + return this.processes.values().iterator(); + } + + public abstract BkProcessTree.OSProcess get(Process var1); + + public abstract void killAll(Map var1, boolean forceFlag) throws InterruptedException; + + public static void log(String msg) { + logger.info(msg); + } + + public void killAll(Process proc, Map modelEnvVars, boolean forceFlag) throws InterruptedException { + log("killAll: process=" + proc + " and envs=" + modelEnvVars); + BkProcessTree.OSProcess p = this.get(proc); + if (p != null) { + p.killRecursively(forceFlag); + } + + if (modelEnvVars != null) { + this.killAll(modelEnvVars, forceFlag); + } + + } + + final List getKillers() throws InterruptedException { + if (this.killers == null) { + this.killers = Collections.emptyList(); + } + + return this.killers; + } + + public static void log(String msg, Throwable e) { + logger.error(msg, e); + } + + public static BkProcessTree get() { + try { + if (File.pathSeparatorChar == ';') { + return new BkProcessTree.Windows(); + } + + String os = Util.fixNull(System.getProperty("os.name")); + switch (os) { + case "Linux": + return new Linux(); + case "SunOS": + return new Solaris(); + case "Mac OS X": + return new Darwin(); + default: + return new Default(); + } + + } catch (Exception var1) { + log("Failed to load winp. Reverting to the default", var1); + } + + return new BkProcessTree.Default(); + } + + public abstract static class Local extends BkProcessTree { + Local() { + super(); + } + } + + private static class Default extends BkProcessTree.Local { + public BkProcessTree.OSProcess get(final Process proc) { + return new BkProcessTree.OSProcess(-1) { + public BkProcessTree.OSProcess getParent() { + return null; + } + + public void killRecursively(boolean forceFlag) { + proc.destroy(); + } + + public void kill0(boolean forceFlag) throws InterruptedException { + proc.destroy(); + } + + public List getArguments() { + return Collections.emptyList(); + } + + public EnvVars getEnvironmentVariables() { + return new EnvVars(); + } + }; + } + + public void killAll(Map modelEnvVars, boolean forceFlag) { + } + } + + private static class Darwin extends Unix { + Darwin() { + String arch = System.getProperty("sun.arch.data.model"); + // local constants + int sizeOf_kinfo_proc; + int kinfo_proc_pid_offset; + int kinfo_proc_ppid_offset; + if ("64".equals(arch)) { + sizeOf_kinfo_proc = sizeOf_kinfo_proc_64; + kinfo_proc_pid_offset = kinfo_proc_pid_offset_64; + kinfo_proc_ppid_offset = kinfo_proc_ppid_offset_64; + } else { + sizeOf_kinfo_proc = sizeOf_kinfo_proc_32; + kinfo_proc_pid_offset = kinfo_proc_pid_offset_32; + kinfo_proc_ppid_offset = kinfo_proc_ppid_offset_32; + } + try { + NativeLongByReference size = new NativeLongByReference(new NativeLong(0)); + Memory m; + int nRetry = 0; + while (true) { + // find out how much memory we need to do this + if (LIBC.sysctl(MIB_PROC_ALL, 3, NULL, size, NULL, new NativeLong(0)) != 0) + throw new IOException("Failed to obtain memory requirement: " + LIBC.strerror(Native.getLastError())); + + // now try the real call + m = new Memory(size.getValue().longValue()); + if (LIBC.sysctl(MIB_PROC_ALL, 3, m, size, NULL, new NativeLong(0)) != 0) { + if (Native.getLastError() == ENOMEM && nRetry++ < 16) + continue; // retry + throw new IOException("Failed to call kern.proc.all: " + LIBC.strerror(Native.getLastError())); + } + break; + } + + int count = size.getValue().intValue() / sizeOf_kinfo_proc; + logger.info("Found {} processes", count); + + for (int base = 0; base < size.getValue().intValue(); base += sizeOf_kinfo_proc) { + int pid = m.getInt(base + kinfo_proc_pid_offset); + int ppid = m.getInt(base + kinfo_proc_ppid_offset); + + super.processes.put(pid, new DarwinProcess(pid, ppid)); + } + } catch (IOException e) { + logger.warn("Failed to obtain process list", e); + } + } + + private class DarwinProcess extends UnixProcess { + private final int ppid; + private EnvVars envVars; + private List arguments; + + DarwinProcess(int pid, int ppid) { + super(pid); + this.ppid = ppid; + } + + @Override + public OSProcess getParent() { + return get(ppid); + } + + @Override + public synchronized EnvVars getEnvironmentVariables() { + if (envVars != null) + return envVars; + parse(); + return envVars; + } + + @Override + public synchronized List getArguments() { + if (arguments != null) + return arguments; + parse(); + return arguments; + } + + private void parse() { + try { +// allocate them first, so that the parse error wil result in empty data + // and avoid retry. + arguments = new ArrayList<>(); + envVars = new EnvVars(); + + IntByReference argmaxRef = new IntByReference(0); + NativeLongByReference size = new NativeLongByReference(new NativeLong(sizeOfInt)); + + // for some reason, I was never able to get sysctlbyname work. +// if (LIBC.sysctlbyname("kern.argmax", argmaxRef.getPointer(), size, NULL, _)!=0) + if (LIBC.sysctl(new int[]{CTL_KERN, KERN_ARGMAX}, 2, argmaxRef.getPointer(), size, NULL, new NativeLong(0)) != 0) + throw new IOException("Failed to get kern.argmax: " + LIBC.strerror(Native.getLastError())); + + int argmax = argmaxRef.getValue(); + + class StringArrayMemory extends Memory { + private long offset = 0; + private long length; + + StringArrayMemory(long l) { + super(l); + length = l; + } + + void setLength(long l) { + length = Math.min(l, size()); + } + + int readInt() { + if (offset > length - sizeOfInt) + return 0; + int r = getInt(offset); + offset += sizeOfInt; + return r; + } + + byte peek() { + if (offset >= length) + return 0; + return getByte(offset); + } + + String readString() throws UnsupportedEncodingException { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + byte ch; + while (offset < length && (ch = getByte(offset++)) != '\0') + baos.write(ch); + return baos.toString(StandardCharsets.UTF_8.name()); + } + + void skip0() { + // skip padding '\0's + while (offset < length && getByte(offset) == '\0') + offset++; + } + } + + StringArrayMemory m = new StringArrayMemory(argmax); + m.clear(); + size.setValue(new NativeLong(argmax)); + if (LIBC.sysctl(new int[]{CTL_KERN, KERN_PROCARGS2, pid}, 3, m, size, NULL, new NativeLong(0)) != 0) + throw new IOException("Failed to obtain ken.procargs2: " + LIBC.strerror(Native.getLastError())); + m.setLength(size.getValue().longValue()); + + + /* + * Make a sysctl() call to get the raw argument space of the + * process. The layout is documented in start.s, which is part + * of the Csu project. In summary, it looks like: + * + * /---------------\ 0x00000000 + * : : + * : : + * |---------------| + * | argc | + * |---------------| + * | arg[0] | + * |---------------| + * : : + * : : + * |---------------| + * | arg[argc - 1] | + * |---------------| + * | 0 | + * |---------------| + * | env[0] | + * |---------------| + * : : + * : : + * |---------------| + * | env[n] | + * |---------------| + * | 0 | + * |---------------| <-- Beginning of data returned by sysctl() is here. + * | argc | + * |---------------| + * | exec_path | + * |:::::::::::::::| + * | | + * | String area. | + * | | + * |---------------| <-- Top of stack. + * : : + * : : + * \---------------/ 0xffffffff + */ + + // I find the Darwin source code of the 'ps' command helpful in understanding how it does this: + // see https://opensource.apple.com/source/adv_cmds/adv_cmds-176/ps/print.c + int argc = m.readInt(); + String args0 = m.readString(); // exec path + m.skip0(); + try { + for (int i = 0; i < argc; i++) { + arguments.add(m.readString()); + } + } catch (IndexOutOfBoundsException e) { + throw new IllegalStateException("Failed to parse arguments: pid=" + pid + ", arg0=" + args0 + ", arguments=" + arguments + ", nargs=" + argc + ". Please see https://www.jenkins.io/redirect/troubleshooting/darwin-failed-to-parse-arguments", e); + } + + // read env vars that follow + while (m.peek() != 0) + envVars.addLine(m.readString()); + } catch (IOException e) { + // this happens with insufficient permissions, so just ignore the problem. + } + } + } + + private static final int sizeOf_kinfo_proc_32 = 492; // on 32bit Mac OS X. + private static final int sizeOf_kinfo_proc_64 = 648; // on 64bit Mac OS X. + private static final int kinfo_proc_pid_offset_32 = 24; + private static final int kinfo_proc_pid_offset_64 = 40; + private static final int kinfo_proc_ppid_offset_32 = 416; + private static final int kinfo_proc_ppid_offset_64 = 560; + private static final int sizeOfInt = Native.getNativeSize(int.class); + private static final int CTL_KERN = 1; + private static final int KERN_PROC = 14; + private static final int KERN_PROC_ALL = 0; + private static final int ENOMEM = 12; + private static final int[] MIB_PROC_ALL = {CTL_KERN, KERN_PROC, KERN_PROC_ALL}; + private static final int KERN_ARGMAX = 8; + private static final int KERN_PROCARGS2 = 49; + } + + static class Solaris extends BkProcessTree.ProcfsUnix { + Solaris() { + } + + protected BkProcessTree.OSProcess createProcess(int pid) throws IOException { + return new BkProcessTree.Solaris.SolarisProcess(pid); + } + + private static long to64(int i) { + return (long) i & 4294967295L; + } + + private static int adjust(int i) { + return BkProcessTree.IS_LITTLE_ENDIAN ? i << 24 | i << 8 & 16711680 | i >> 8 & '\uff00' | i >>> 24 : i; + } + + private class SolarisProcess extends BkProcessTree.UnixProcess { + private final int ppid; + private final int envp; + private final int argp; + private final int argc; + private EnvVars envVars; + private List arguments; + + private SolarisProcess(int pid) throws IOException { + super(pid); + + try (RandomAccessFile psinfo = new RandomAccessFile(this.getFile("psinfo"), "r")) { + psinfo.seek(8L); + if (Solaris.adjust(psinfo.readInt()) != pid) { + throw new IOException("psinfo PID mismatch"); + } + + this.ppid = Solaris.adjust(psinfo.readInt()); + psinfo.seek(188L); + this.argc = Solaris.adjust(psinfo.readInt()); + this.argp = Solaris.adjust(psinfo.readInt()); + this.envp = Solaris.adjust(psinfo.readInt()); + } + + if (this.ppid == -1) { + throw new IOException("Failed to parse PPID from /proc/" + pid + "/status"); + } + } + + public BkProcessTree.OSProcess getParent() { + return Solaris.this.get(this.ppid); + } + + public synchronized List getArguments() { + if (this.arguments == null) { + this.arguments = new ArrayList<>(this.argc); + + try (RandomAccessFile as = new RandomAccessFile(this.getFile("as"), "r")) { + BkProcessTree.log("Reading " + this.getFile("as")); + for (int n = 0; n < this.argc; ++n) { + as.seek(Solaris.to64(this.argp + n * 4)); + int p = Solaris.adjust(as.readInt()); + this.arguments.add(this.readLine(as, p, "argv[" + n + "]")); + } + } catch (IOException var8) { + log(var8.getMessage()); + } + + this.arguments = Collections.unmodifiableList(this.arguments); + } + return this.arguments; + } + + public synchronized EnvVars getEnvironmentVariables() { + if (this.envVars == null) { + this.envVars = new EnvVars(); + + try (RandomAccessFile as = new RandomAccessFile(this.getFile("as"), "r")) { + BkProcessTree.log("Reading " + this.getFile("as")); + int n = 0; + + while (true) { + as.seek(Solaris.to64(this.envp + n * 4)); + int p = Solaris.adjust(as.readInt()); + if (p == 0) { + break; + } + + this.envVars.addLine(this.readLine(as, p, "env[" + n + "]")); + ++n; + } + } catch (IOException var8) { + log(var8.getMessage()); + } + + } + return this.envVars; + } + + private String readLine(RandomAccessFile as, int p, String prefix) throws IOException { + BkProcessTree.log("Reading " + prefix + " at " + p); + as.seek(BkProcessTree.Solaris.to64(p)); + ByteArrayOutputStream buf = new ByteArrayOutputStream(); + + int ch; + for (int i = 0; (ch = as.read()) > 0; buf.write(ch)) { + ++i; + if (i % 100 == 0) { + BkProcessTree.log(prefix + " is so far " + buf); + } + } + + String line = buf.toString(); + BkProcessTree.log(prefix + " was " + line); + return line; + } + } + } + + static class Linux extends BkProcessTree.ProcfsUnix { + Linux() { + } + + protected BkProcessTree.Linux.LinuxProcess createProcess(int pid) throws IOException { + return new BkProcessTree.Linux.LinuxProcess(pid); + } + + public byte[] readFileToByteArray(File file) throws IOException { + FileInputStream in = FileUtils.openInputStream(file); + + byte[] var3; + try { + var3 = IOUtils.toByteArray(in); + } finally { + in.close(); + } + + return var3; + } + + class LinuxProcess extends BkProcessTree.UnixProcess { + private int ppid = -1; + private EnvVars envVars; + private List arguments; + + LinuxProcess(int pid) throws IOException { + super(pid); + + try (BufferedReader r = new BufferedReader(new FileReader(this.getFile("status")))) { + String line; + while ((line = r.readLine()) != null) { + line = line.toLowerCase(Locale.ENGLISH); + if (line.startsWith("ppid:")) { + this.ppid = Integer.parseInt(line.substring(5).trim()); + break; + } + } + } + + if (this.ppid == -1) { + throw new IOException("Failed to parse PPID from /proc/" + pid + "/status"); + } + } + + public BkProcessTree.OSProcess getParent() { + return Linux.this.get(this.ppid); + } + + public synchronized List getArguments() { + if (this.arguments == null) { + this.arguments = new ArrayList<>(); + + try { + byte[] cmdline = Linux.this.readFileToByteArray(this.getFile("cmdline")); + int pos = 0; + + for (int i = 0; i < cmdline.length; ++i) { + byte b = cmdline[i]; + if (b == 0) { + this.arguments.add(new String(cmdline, pos, i - pos)); + pos = i + 1; + } + } + } catch (IOException var5) { + log(var5.getMessage()); + } + + this.arguments = Collections.unmodifiableList(this.arguments); + } + return this.arguments; + } + + public synchronized EnvVars getEnvironmentVariables() { + if (this.envVars == null) { + this.envVars = new EnvVars(); + + try { + byte[] environ = Linux.this.readFileToByteArray(this.getFile("environ")); + int pos = 0; + + for (int i = 0; i < environ.length; ++i) { + byte b = environ[i]; + if (b == 0) { + this.envVars.addLine(new String(environ, pos, i - pos)); + pos = i + 1; + } + } + } catch (IOException var5) { + log(var5.getMessage()); + } + + } + return this.envVars; + } + } + } + + private static final class UnixReflection { + private static final Field PID_FIELD; + private static final Method DESTROY_PROCESS; + + private UnixReflection() { + } + + public static void destroy(int pid, boolean forceFlag) throws IllegalAccessException, InvocationTargetException { + if (isPreJava8()) { + DESTROY_PROCESS.invoke((Object) null, pid); + } else { + DESTROY_PROCESS.invoke((Object) null, pid, forceFlag); + } + + } + + private static boolean isPreJava8() { + int javaVersionAsAnInteger = Integer.parseInt(System.getProperty("java.version").replaceAll("\\.", "").replaceAll("_", "").substring(0, 2)); + return javaVersionAsAnInteger < 18; + } + + static { + LinkageError x; + try { + Class clazz = Class.forName("java.lang.UNIXProcess"); + PID_FIELD = clazz.getDeclaredField("pid"); + PID_FIELD.setAccessible(true); + if (isPreJava8()) { + DESTROY_PROCESS = clazz.getDeclaredMethod("destroyProcess", Integer.TYPE); + } else { + DESTROY_PROCESS = clazz.getDeclaredMethod("destroyProcess", Integer.TYPE, Boolean.TYPE); + } + + DESTROY_PROCESS.setAccessible(true); + } catch (ClassNotFoundException | NoSuchFieldException | NoSuchMethodException e) { + x = new LinkageError(); + x.initCause(e); + throw x; + } + } + } + + public abstract class UnixProcess extends BkProcessTree.OSProcess { + protected UnixProcess(int pid) { + super(pid); + } + + protected final File getFile(String relativePath) { + return new File(new File("/proc/" + this.getPid()), relativePath); + } + + public void kill0(boolean forceFlag) throws InterruptedException { + try { + int pid = this.getPid(); + BkProcessTree.log("Killing pid=" + pid); + BkProcessTree.UnixReflection.destroy(pid, forceFlag); + } catch (IllegalAccessException var3) { + IllegalAccessError x = new IllegalAccessError(); + x.initCause(var3); + throw x; + } catch (InvocationTargetException var4) { + if (var4.getTargetException() instanceof Error) { + throw (Error) var4.getTargetException(); + } + + BkProcessTree.log("Failed to terminate pid=" + this.getPid(), var4); + } + } + + public void killRecursively(boolean forceFlag) throws InterruptedException { + BkProcessTree.log("Recursively killing pid=" + this.getPid()); + + for (OSProcess p : this.getChildren()) { + p.killRecursively(forceFlag); + } + + this.kill(forceFlag); + } + + public abstract List getArguments(); + } + + abstract static class ProcfsUnix extends BkProcessTree.Unix { + ProcfsUnix() { + File[] processes = (new File("/proc")).listFiles(File::isDirectory); + if (processes == null) { + log("No /proc"); + } else { + for (File p : processes) { + int pid; + try { + pid = Integer.parseInt(p.getName()); + } catch (NumberFormatException var9) { + continue; + } + + try { + this.processes.put(pid, this.createProcess(pid)); + } catch (IOException var8) { + log(var8.getMessage()); + } + } + + } + } + + protected abstract BkProcessTree.OSProcess createProcess(int var1) throws IOException; + } + + abstract static class Unix extends BkProcessTree.Local { + Unix() { + } + + public BkProcessTree.OSProcess get(Process proc) { + try { + return this.get((Integer) BkProcessTree.UnixReflection.PID_FIELD.get(proc)); + } catch (IllegalAccessException var4) { + IllegalAccessError x = new IllegalAccessError(); + x.initCause(var4); + throw x; + } + } + + public void killAll(Map modelEnvVars, boolean forceFlag) throws InterruptedException { + + for (OSProcess p : this) { + if (p.hasMatchingEnvVars(modelEnvVars)) { + p.killRecursively(forceFlag); + } + } + + } + } + + private static final class Windows extends BkProcessTree.Local { + Windows() { + + for (WinProcess p : WinProcess.all()) { + int pid = p.getPid(); + if (pid != 0 && pid != 4) { + super.processes.put(pid, new OSProcess(pid) { + private EnvVars env; + private List args; + + public OSProcess getParent() { + return null; + } + + public void killRecursively(boolean forceFlag) throws InterruptedException { + BkProcessTree.log("Killing recursively " + this.getPid()); + p.killRecursively(); + } + + public void kill0(boolean forceFlag) throws InterruptedException { + BkProcessTree.log("Killing " + this.getPid()); + p.kill(); + } + + public synchronized List getArguments() { + if (this.args == null) { + this.args = Arrays.asList(QuotedStringTokenizer.tokenize(p.getCommandLine())); + } + + return this.args; + } + + public synchronized EnvVars getEnvironmentVariables() { + if (this.env == null) { + this.env = new EnvVars(); + + try { + this.env.putAll(p.getEnvironmentVariables()); + } catch (WinpException var2) { + log(var2.getMessage()); + } + + } + return this.env; + } + }); + } + } + + } + + public BkProcessTree.OSProcess get(Process proc) { + return this.get((new WinProcess(proc)).getPid()); + } + + public void killAll(Map modelEnvVars, boolean forceFlag) throws InterruptedException { + Iterator var2 = this.iterator(); + + while (true) { + BkProcessTree.OSProcess p; + do { + if (!var2.hasNext()) { + return; + } + + p = var2.next(); + } while (p.getPid() < 10); + + log("Considering to kill " + p.getPid()); + + boolean matched; + try { + matched = p.hasMatchingEnvVars(modelEnvVars); + } catch (WinpException var6) { + log(" Failed to check environment variable match", var6); + continue; + } + + if (matched) { + p.killRecursively(forceFlag); + } else { + log("Environment variable didn't match"); + } + } + } + + static { + WinProcess.enableDebugPrivilege(); + } + } + + private final class SerializedProcess implements Serializable { + private final int pid; + private static final long serialVersionUID = 1L; + + private SerializedProcess(int pid) { + this.pid = pid; + } + + Object readResolve() { + return BkProcessTree.this.get(this.pid); + } + } + + public abstract class OSProcess implements ProcessTreeRemoting.IOSProcess, Serializable { + private final Set keepAlivePids = new HashSet<>(64); + + final int pid; + + private OSProcess(int pid) { + this.pid = pid; + } + + public final int getPid() { + return this.pid; + } + + public final void addKeepAlivePids(Collection pids) { + keepAlivePids.addAll(pids); + } + + public abstract BkProcessTree.OSProcess getParent(); + + public final List getChildren() { + List r = new ArrayList<>(); + + for (OSProcess p : BkProcessTree.this) { + if (p.getParent() == this) { + if (keepAlivePids.contains(p.pid)) { + this.keepAlivePids.add(this.pid); + } else { + p.addKeepAlivePids(keepAlivePids); + r.add(p); + } + } + } + + return r; + } + + public void kill(boolean forceFlag) throws InterruptedException { + BkProcessTree.log("pid=" + pid + ", iskeepAlive=" + keepAlivePids.contains(pid)); + if (!keepAlivePids.contains(pid)) { + kill0(forceFlag); + } + } + + public abstract void kill0(boolean forceFlag) throws InterruptedException; + + public abstract void killRecursively(boolean forceFlag) throws InterruptedException; + + public abstract List getArguments(); + + public abstract EnvVars getEnvironmentVariables(); + + public final boolean hasMatchingEnvVars(Map modelEnvVar) { + if (!modelEnvVar.isEmpty()) { + SortedMap envs = this.getEnvironmentVariables(); + Iterator> var3 = modelEnvVar.entrySet().iterator(); + + Entry e; + String v; + do { + if (!var3.hasNext()) { + return true; + } + + e = var3.next(); + v = envs.get(e.getKey()); + } while (v != null && v.equals(e.getValue())); + + } + return false; + } + + Object writeReplace() { + return BkProcessTree.this.new SerializedProcess(this.pid); + } + } +} diff --git a/modules/ci-worker/src/main/java/cd/casic/devops/common/process/CyclicGraphDetector.java b/modules/ci-worker/src/main/java/cd/casic/devops/common/process/CyclicGraphDetector.java new file mode 100644 index 0000000..594d933 --- /dev/null +++ b/modules/ci-worker/src/main/java/cd/casic/devops/common/process/CyclicGraphDetector.java @@ -0,0 +1,83 @@ +package cd.casic.devops.common.process; + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import java.util.Stack; +import java.util.stream.Collectors; + +// copy from jenkins 96a66619b55b3b78b86817798fe36e58b2798cd4 + +/** + * Traverses a directed graph and if it contains any cycle, throw an exception. + * + * @author Kohsuke Kawaguchi + */ +public abstract class CyclicGraphDetector { + private final Set visited = new HashSet<>(); + private final Set visiting = new HashSet<>(); + private final Stack path = new Stack<>(); + + private final List topologicalOrder = new ArrayList<>(); + + public void run(Iterable allNodes) throws CycleDetectedException { + for (N n : allNodes) { + visit(n); + } + } + + /** + * Returns all the nodes in the topologically sorted order. + * That is, if there's an edge a → b, b always come earlier than a. + */ + public List getSorted() { + return topologicalOrder; + } + + /** + * List up edges from the given node (by listing nodes that those edges point to.) + * + * @return + * Never null. + */ + protected abstract Iterable getEdges(N n); + + private void visit(N p) throws CycleDetectedException { + if (!visited.add(p)) return; + + visiting.add(p); + path.push(p); + for (N q : getEdges(p)) { + if (q == null) continue; // ignore unresolved references + if (visiting.contains(q)) + detectedCycle(q); + visit(q); + } + visiting.remove(p); + path.pop(); + topologicalOrder.add(p); + } + + private void detectedCycle(N q) throws CycleDetectedException { + int i = path.indexOf(q); + path.push(q); + reactOnCycle(q, path.subList(i, path.size())); + } + + /** + * React on detected cycles - default implementation throws an exception. + */ + protected void reactOnCycle(N q, List cycle) throws CycleDetectedException { + throw new CycleDetectedException(cycle); + } + + public static final class CycleDetectedException extends Exception { + public final List cycle; + + public CycleDetectedException(List cycle) { + super("Cycle detected: " + cycle.stream().map(Object::toString).collect(Collectors.joining(" -> "))); + this.cycle = cycle; + } + } +} diff --git a/modules/ci-worker/src/main/java/cd/casic/devops/common/process/EnvVars.java b/modules/ci-worker/src/main/java/cd/casic/devops/common/process/EnvVars.java new file mode 100644 index 0000000..20564be --- /dev/null +++ b/modules/ci-worker/src/main/java/cd/casic/devops/common/process/EnvVars.java @@ -0,0 +1,468 @@ +/* + * The MIT License + * + * Copyright (c) 2004-2009, Sun Microsystems, Inc., Kohsuke Kawaguchi, Red Hat, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +package cd.casic.devops.common.process; + +import java.io.File; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.Comparator; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.TreeMap; +import java.util.TreeSet; +import java.util.UUID; +import java.util.logging.Logger; + +// copy from jenkins 96a66619b55b3b78b86817798fe36e58b2798cd4 + +/** + * Environment variables. + * + *

+ * While all the platforms I tested (Linux 2.6, Solaris, and Windows XP) have the case sensitive + * environment variable table, Windows batch script handles environment variable in the case preserving + * but case insensitive way (that is, cmd.exe can get both FOO and foo as environment variables + * when it's launched, and the "set" command will display it accordingly, but "echo %foo%" results in + * echoing the value of "FOO", not "foo" — this is presumably caused by the behavior of the underlying + * Win32 API {@code GetEnvironmentVariable} acting in case insensitive way.) Windows users are also + * used to write environment variable case-insensitively (like %Path% vs %PATH%), and you can see many + * documents on the web that claims Windows environment variables are case insensitive. + * + *

+ * So for a consistent cross platform behavior, it creates the least confusion to make the table + * case insensitive but case preserving. + * + *

+ * In Jenkins, often we need to build up "environment variable overrides" + * on the controller, then to execute the process on agents. This causes a problem + * when working with variables like {@code PATH}. So to make this work, + * we introduce a special convention {@code PATH+FOO} — all entries + * that starts with {@code PATH+} are merged and prepended to the inherited + * {@code PATH} variable, on the process where a new process is executed. + * + * @author Kohsuke Kawaguchi + */ +public class EnvVars extends TreeMap { + private static final long serialVersionUID = 4320331661987259022L; + private static Logger LOGGER = Logger.getLogger(EnvVars.class.getName()); + /** + * If this {@link EnvVars} object represents the whole environment variable set, + * not just a partial list used for overriding later, then we need to know + * the platform for which this env vars are targeted for, or else we won't know + * how to merge variables properly. + * + *

+ * So this property remembers that information. + */ + private Platform platform; + + /** + * Gets the platform for which these env vars targeted. + * @since 2.144 + * @return The platform. + */ + public Platform getPlatform() { + return platform; + } + + /** + * Sets the platform for which these env vars target. + * @since 2.144 + * @param platform the platform to set. + */ + public void setPlatform(Platform platform) { + this.platform = platform; + } + + public EnvVars() { + super(String.CASE_INSENSITIVE_ORDER); + } + + public EnvVars(Map m) { + this(); + putAll(m); + + // because of the backward compatibility, some parts of Jenkins passes + // EnvVars as Map so downcasting is safer. + if (m instanceof EnvVars) { + EnvVars lhs = (EnvVars) m; + this.platform = lhs.platform; + } + } + + @SuppressWarnings("CopyConstructorMissesField") // does not set #platform, see its Javadoc + public EnvVars(EnvVars m) { + // this constructor is so that in future we can get rid of the downcasting. + this((Map) m); + } + + /** + * Builds an environment variables from an array of the form {@code "key","value","key","value"...} + */ + public EnvVars(String... keyValuePairs) { + this(); + if (keyValuePairs.length % 2 != 0) + throw new IllegalArgumentException(Arrays.asList(keyValuePairs).toString()); + for (int i = 0; i < keyValuePairs.length; i += 2) + put(keyValuePairs[i], keyValuePairs[i + 1]); + } + + /** + * Overrides the current entry by the given entry. + * + *

+ * Handles {@code PATH+XYZ} notation. + */ + public void override(String key, String value) { + if (value == null || value.isEmpty()) { + remove(key); + return; + } + + int idx = key.indexOf('+'); + if (idx > 0) { + String realKey = key.substring(0, idx); + String v = get(realKey); + if (v == null) v = value; + else { + // we might be handling environment variables for a agent that can have different path separator + // than the controller, so the following is an attempt to get it right. + // it's still more error prone that I'd like. + char ch = platform == null ? File.pathSeparatorChar : platform.pathSeparator; + v = value + ch + v; + } + put(realKey, v); + return; + } + + put(key, value); + } + + /** + * Overrides all values in the map by the given map. + * See {@link #override(String, String)}. + * @return this + */ + public EnvVars overrideAll(Map all) { + for (Map.Entry e : all.entrySet()) { + override(e.getKey(), e.getValue()); + } + return this; + } + + /** + * Calculates the order to override variables. + * + * Sort variables with topological sort with their reference graph. + * + * This is package accessible for testing purpose. + */ + static class OverrideOrderCalculator { + /** + * Extract variables referred directly from a variable. + */ + private static class TraceResolver implements VariableResolver { + private final Comparator comparator; + public Set referredVariables; + + TraceResolver(Comparator comparator) { + this.comparator = comparator; + clear(); + } + + public void clear() { + referredVariables = new TreeSet<>(comparator); + } + + @Override + public String resolve(String name) { + referredVariables.add(name); + return ""; + } + } + + private static class VariableReferenceSorter extends CyclicGraphDetector { + // map from a variable to a set of variables that variable refers. + private final Map> refereeSetMap; + + VariableReferenceSorter(Map> refereeSetMap) { + this.refereeSetMap = refereeSetMap; + } + + @Override + protected Iterable getEdges(String n) { + // return variables referred from the variable. + if (!refereeSetMap.containsKey(n)) { + // there is a case a non-existing variable is referred... + return Collections.emptySet(); + } + return refereeSetMap.get(n); + } + } + + private final Comparator comparator; + + + private final EnvVars target; + + private final Map overrides; + + private Map> refereeSetMap; + private List orderedVariableNames; + + OverrideOrderCalculator(EnvVars target, Map overrides) { + comparator = target.comparator(); + this.target = target; + this.overrides = overrides; + scan(); + } + + public List getOrderedVariableNames() { + return orderedVariableNames; + } + + // Cut the reference to the variable in a cycle. + private void cutCycleAt(String referee, List cycle) { + // cycle contains variables in referrer-to-referee order. + // This should not be negative, for the first and last one is same. + int refererIndex = cycle.lastIndexOf(referee) - 1; + + assert refererIndex >= 0; + String referrer = cycle.get(refererIndex); + boolean removed = refereeSetMap.get(referrer).remove(referee); + assert removed; + LOGGER.warning(String.format("Cyclic reference detected: %s", String.join(" -> ", cycle))); + LOGGER.warning(String.format("Cut the reference %s -> %s", referrer, referee)); + } + + // Cut the variable reference in a cycle. + private void cutCycle(List cycle) { + // if an existing variable is contained in that cycle, + // cut the cycle with that variable: + // existing: + // PATH=/usr/bin + // overriding: + // PATH1=/usr/local/bin:${PATH} + // PATH=/opt/something/bin:${PATH1} + // then consider reference PATH1 -> PATH can be ignored. + for (String referee : cycle) { + if (target.containsKey(referee)) { + cutCycleAt(referee, cycle); + return; + } + } + + // if not, cut the reference to the first one. + cutCycleAt(cycle.get(0), cycle); + } + + /** + * Scan all variables and list all referring variables. + */ + public void scan() { + refereeSetMap = new TreeMap<>(comparator); + List extendingVariableNames = new ArrayList<>(); + + TraceResolver resolver = new TraceResolver(comparator); + + for (Map.Entry entry : overrides.entrySet()) { + if (entry.getKey().indexOf('+') > 0) { + // XYZ+AAA variables should be always processed in last. + extendingVariableNames.add(entry.getKey()); + continue; + } + resolver.clear(); + Util.replaceMacro(entry.getValue(), resolver); + + // Variables directly referred from the current scanning variable. + Set refereeSet = resolver.referredVariables; + // Ignore self reference. + refereeSet.remove(entry.getKey()); + refereeSetMap.put(entry.getKey(), refereeSet); + } + + VariableReferenceSorter sorter; + while (true) { + sorter = new VariableReferenceSorter(refereeSetMap); + try { + sorter.run(refereeSetMap.keySet()); + } catch (CyclicGraphDetector.CycleDetectedException e) { + // cyclic reference found. + // cut the cycle and retry. + @SuppressWarnings("unchecked") + List cycle = e.cycle; + cutCycle(cycle); + continue; + } + break; + } + + // When A refers B, the last appearance of B always comes after + // the last appearance of A. + List reversedDuplicatedOrder = new ArrayList<>(sorter.getSorted()); + Collections.reverse(reversedDuplicatedOrder); + + orderedVariableNames = new ArrayList<>(overrides.size()); + for (String key : reversedDuplicatedOrder) { + if (overrides.containsKey(key) && !orderedVariableNames.contains(key)) { + orderedVariableNames.add(key); + } + } + Collections.reverse(orderedVariableNames); + orderedVariableNames.addAll(extendingVariableNames); + } + } + + + /** + * Overrides all values in the map by the given map. Expressions in values will be expanded. + * See {@link #override(String, String)}. + * @return {@code this} + */ + public EnvVars overrideExpandingAll(Map all) { + for (String key : new OverrideOrderCalculator(this, all).getOrderedVariableNames()) { + override(key, expand(all.get(key))); + } + return this; + } + + /** + * Resolves environment variables against each other. + */ + public static void resolve(Map env) { + for (Map.Entry entry : env.entrySet()) { + entry.setValue(Util.replaceMacro(entry.getValue(), env)); + } + } + + /** + * Convenience message + * @since 1.485 + **/ + public String get(String key, String defaultValue) { + String v = get(key); + if (v == null) v = defaultValue; + return v; + } + + @Override + public String put(String key, String value) { + if (value == null) throw new IllegalArgumentException("Null value not allowed as an environment variable: " + key); + return super.put(key, value); + } + + /** + * Add a key/value but only if the value is not-null. Otherwise no-op. + * @since 1.556 + */ + public void putIfNotNull(String key, String value) { + if (value != null) + put(key, value); + } + + /** + * Add entire map but filter null values out. + * @since 2.214 + */ + public void putAllNonNull(Map map) { + map.forEach(this::putIfNotNull); + } + + + /** + * Takes a string that looks like "a=b" and adds that to this map. + */ + public void addLine(String line) { + int sep = line.indexOf('='); + if (sep > 0) { + put(line.substring(0, sep), line.substring(sep + 1)); + } + } + + /** + * Expands the variables in the given string by using environment variables represented in 'this'. + */ + public String expand(String s) { + return Util.replaceMacro(s, this); + } + + /** + * Creates a magic cookie that can be used as the model environment variable + * when we later kill the processes. + */ + public static EnvVars createCookie() { + return new EnvVars("HUDSON_COOKIE", UUID.randomUUID().toString()); + } + +// /** +// * Obtains the environment variables of a remote peer. +// * +// * @param channel +// * Can be null, in which case the map indicating "N/A" will be returned. +// * @return +// * A fresh copy that can be owned and modified by the caller. +// */ +// public static EnvVars getRemote(VirtualChannel channel) throws IOException, InterruptedException { +// if (channel == null) +// return new EnvVars("N/A", "N/A"); +// return channel.call(new GetEnvVars()); +// } +// +// private static final class GetEnvVars extends MasterToSlaveCallable { +// @Override +// public EnvVars call() { +// return new EnvVars(EnvVars.masterEnvVars); +// } +// +// private static final long serialVersionUID = 1L; +// } +// +// /** +// * Environmental variables that we've inherited. +// * +// *

+// * Despite what the name might imply, this is the environment variable +// * of the current JVM process. And therefore, it is the Jenkins controller's +// * environment variables only when you access this from the controller. +// * +// *

+// * If you access this field from agents, then this is the environment +// * variable of the agent. +// */ +// public static final Map masterEnvVars = initMaster(); +// +// private static EnvVars initMaster() { +// EnvVars vars = new EnvVars(System.getenv()); +// vars.platform = Platform.current(); +// if (Main.isUnitTest || Main.isDevelopmentMode) +// // if unit test is launched with maven debug switch, +// // we need to prevent forked Maven processes from seeing it, or else +// // they'll hang +// vars.remove("MAVEN_OPTS"); +// return vars; +// } +} diff --git a/modules/ci-worker/src/main/java/cd/casic/devops/common/process/Platform.java b/modules/ci-worker/src/main/java/cd/casic/devops/common/process/Platform.java new file mode 100644 index 0000000..59021d2 --- /dev/null +++ b/modules/ci-worker/src/main/java/cd/casic/devops/common/process/Platform.java @@ -0,0 +1,79 @@ +/* + * The MIT License + * + * Copyright (c) 2004-2009, Sun Microsystems, Inc., Kohsuke Kawaguchi + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +package cd.casic.devops.common.process; + + +import hudson.util.VersionNumber; + +import java.io.File; +import java.util.Locale; + +// copy from jenkins 96a66619b55b3b78b86817798fe36e58b2798cd4 + +/** + * Strategy object that absorbs the platform differences. + * + *

+ * Do not switch/case on this enum, or do a comparison, as we may add new constants. + * + * @author Kohsuke Kawaguchi + */ +public enum Platform { + WINDOWS(';'), UNIX(':'); + + /** + * The character that separates paths in environment variables like PATH and CLASSPATH. + * On Windows ';' and on Unix ':'. + * + * @see File#pathSeparator + */ + public final char pathSeparator; + + Platform(char pathSeparator) { + this.pathSeparator = pathSeparator; + } + + public static Platform current() { + if (File.pathSeparatorChar == ':') return UNIX; + return WINDOWS; + } + + public static boolean isDarwin() { + // according to http://developer.apple.com/technotes/tn2002/tn2110.html + return System.getProperty("os.name").toLowerCase(Locale.ENGLISH).startsWith("mac"); + } + + /** + * Returns true if we run on Mac OS X ≥ 10.6 + */ + public static boolean isSnowLeopardOrLater() { + try { + return isDarwin() && new VersionNumber(System.getProperty("os.version")).compareTo(new VersionNumber("10.6")) >= 0; + } catch (IllegalArgumentException e) { + // failed to parse the version + return false; + } + } +} diff --git a/modules/ci-worker/src/main/java/cd/casic/devops/common/process/ProcessKiller.java b/modules/ci-worker/src/main/java/cd/casic/devops/common/process/ProcessKiller.java new file mode 100644 index 0000000..25546ff --- /dev/null +++ b/modules/ci-worker/src/main/java/cd/casic/devops/common/process/ProcessKiller.java @@ -0,0 +1,48 @@ +/* + * Tencent is pleased to support the open source community by making BK-CI 蓝鲸持续集成平台 available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * BK-CI 蓝鲸持续集成平台 is licensed under the MIT license. + * + * A copy of the MIT License is included in this file. + * + * + * Terms of the MIT License: + * --------------------------------------------------- + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated + * documentation files (the "Software"), to deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or substantial portions of + * the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT + * LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN + * NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +package cd.casic.devops.common.process; + +import cd.casic.devops.common.process.BkProcessTree.OSProcess; + +import java.io.IOException; +import java.io.Serializable; +import java.util.ArrayList; +import java.util.List; + +public abstract class ProcessKiller implements Serializable { + private static final long serialVersionUID = 1L; + + public ProcessKiller() { + } + + public static List all() { + return new ArrayList<>(); + } + + public abstract boolean kill(OSProcess var1) throws IOException, InterruptedException; +} diff --git a/modules/ci-worker/src/main/java/cd/casic/devops/common/process/ProcessTreeRemoting.java b/modules/ci-worker/src/main/java/cd/casic/devops/common/process/ProcessTreeRemoting.java new file mode 100644 index 0000000..ca87988 --- /dev/null +++ b/modules/ci-worker/src/main/java/cd/casic/devops/common/process/ProcessTreeRemoting.java @@ -0,0 +1,54 @@ +/* + * Tencent is pleased to support the open source community by making BK-CI 蓝鲸持续集成平台 available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * BK-CI 蓝鲸持续集成平台 is licensed under the MIT license. + * + * A copy of the MIT License is included in this file. + * + * + * Terms of the MIT License: + * --------------------------------------------------- + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated + * documentation files (the "Software"), to deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or substantial portions of + * the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT + * LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN + * NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +package cd.casic.devops.common.process; + +import java.util.List; +import java.util.Map; + +public class ProcessTreeRemoting { + public ProcessTreeRemoting() { + } + + public interface IOSProcess { + int getPid(); + + ProcessTreeRemoting.IOSProcess getParent(); + + void kill(boolean forceFlag) throws InterruptedException; + + void killRecursively(boolean forceFlag) throws InterruptedException; + + List getArguments(); + + EnvVars getEnvironmentVariables(); + } + + public interface IProcessTree { + void killAll(Map var1, boolean forceFlag) throws InterruptedException; + } +} diff --git a/modules/ci-worker/src/main/java/cd/casic/devops/common/process/QuotedStringTokenizer.java b/modules/ci-worker/src/main/java/cd/casic/devops/common/process/QuotedStringTokenizer.java new file mode 100644 index 0000000..281fc83 --- /dev/null +++ b/modules/ci-worker/src/main/java/cd/casic/devops/common/process/QuotedStringTokenizer.java @@ -0,0 +1,557 @@ +/* + * (C) Copyright 2004-2005 Mort Bay Consulting Pty. Ltd. + * + * Parts of this code was taken from the Jetty project, which can be + * found at http://www.mortbay.org/jetty + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// ======================================================================== +// Copyright 2004-2005 Mort Bay Consulting Pty. Ltd. +// ------------------------------------------------------------------------ +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// ======================================================================== + +package cd.casic.devops.common.process; + +import java.util.ArrayList; +import java.util.List; +import java.util.NoSuchElementException; +import java.util.StringTokenizer; + +// copy from jenkins 96a66619b55b3b78b86817798fe36e58b2798cd4 + +/* ------------------------------------------------------------ */ + +/** StringTokenizer with Quoting support. + * + * This class is a copy of the java.util.StringTokenizer API and + * the behaviour is the same, except that single and double quoted + * string values are recognized. + * Delimiters within quotes are not considered delimiters. + * Quotes can be escaped with '\'. + * + * @see StringTokenizer + * @author Greg Wilkins (gregw) + */ +@SuppressWarnings("all") +public class QuotedStringTokenizer + extends StringTokenizer +{ + private static final String __delim = " \t\n\r"; + private String _string; + private String _delim = __delim; + private boolean _returnQuotes = false; + private boolean _returnDelimiters = false; + private StringBuilder _token; + private boolean _hasToken = false; + private int _i = 0; + private int _lastStart = 0; + private boolean _double = true; + private boolean _single = true; + + public static String[] tokenize(String str) { + return new QuotedStringTokenizer(str).toArray(); + } + + public static String[] tokenize(String str, String delimiters) { + return new QuotedStringTokenizer(str, delimiters).toArray(); + } + + /* ------------------------------------------------------------ */ + /** + * + * @param str + * String to tokenize. + * @param delim + * List of delimiter characters as string. Can be null, to default to ' \t\n\r' + * @param returnDelimiters + * If true, {@link #nextToken()} will include the delimiters, not just tokenized + * tokens. + * @param returnQuotes + * If true, {@link #nextToken()} will include the quotation characters when they are present. + */ + public QuotedStringTokenizer(String str, + String delim, + boolean returnDelimiters, + boolean returnQuotes) + { + super(""); + _string = str; + if (delim != null) + _delim = delim; + _returnDelimiters = returnDelimiters; + _returnQuotes = returnQuotes; + + if (_delim.indexOf('\'') >= 0 || + _delim.indexOf('"') >= 0) + throw new Error("Can't use quotes as delimiters: " + _delim); + + _token = new StringBuilder(_string.length() > 1024 ? 512 : _string.length() / 2); + } + + /* ------------------------------------------------------------ */ + public QuotedStringTokenizer(String str, + String delim, + boolean returnDelimiters) + { + this(str, delim, returnDelimiters, false); + } + + /* ------------------------------------------------------------ */ + public QuotedStringTokenizer(String str, + String delim) + { + this(str, delim, false, false); + } + + /* ------------------------------------------------------------ */ + public QuotedStringTokenizer(String str) + { + this(str, null, false, false); + } + + public String[] toArray() { + List r = new ArrayList<>(); + while (hasMoreTokens()) + r.add(nextToken()); + return r.toArray(new String[r.size()]); + } + + + /* ------------------------------------------------------------ */ + @Override + public boolean hasMoreTokens() + { + // Already found a token + if (_hasToken) + return true; + + _lastStart = _i; + + int state = 0; + boolean escape = false; + while (_i < _string.length()) + { + char c = _string.charAt(_i++); + + switch (state) + { + case 0: // Start + if (_delim.indexOf(c) >= 0) + { + if (_returnDelimiters) + { + _token.append(c); + return _hasToken = true; + } + } + else if (c == '\'' && _single) + { + if (_returnQuotes) + _token.append(c); + state = 2; + } + else if (c == '\"' && _double) + { + if (_returnQuotes) + _token.append(c); + state = 3; + } + else + { + _token.append(c); + _hasToken = true; + state = 1; + } + continue; + + case 1: // Token + _hasToken = true; + if (escape) + { + escape = false; + if (ESCAPABLE_CHARS.indexOf(c) < 0) + _token.append('\\'); + _token.append(c); + } + else if (_delim.indexOf(c) >= 0) + { + if (_returnDelimiters) + _i--; + return _hasToken; + } + else if (c == '\'' && _single) + { + if (_returnQuotes) + _token.append(c); + state = 2; + } + else if (c == '\"' && _double) + { + if (_returnQuotes) + _token.append(c); + state = 3; + } + else if (c == '\\') + { + escape = true; + } + else + _token.append(c); + continue; + + + case 2: // Single Quote + _hasToken = true; + if (escape) + { + escape = false; + if (ESCAPABLE_CHARS.indexOf(c) < 0) + _token.append('\\'); + _token.append(c); + } + else if (c == '\'') + { + if (_returnQuotes) + _token.append(c); + state = 1; + } + else if (c == '\\') + { + if (_returnQuotes) + _token.append(c); + escape = true; + } + else + _token.append(c); + continue; + + + case 3: // Double Quote + _hasToken = true; + if (escape) + { + escape = false; + if (ESCAPABLE_CHARS.indexOf(c) < 0) + _token.append('\\'); + _token.append(c); + } + else if (c == '\"') + { + if (_returnQuotes) + _token.append(c); + state = 1; + } + else if (c == '\\') + { + if (_returnQuotes) + _token.append(c); + escape = true; + } + else + _token.append(c); + continue; + + default: + break; + } + } + + return _hasToken; + } + + /* ------------------------------------------------------------ */ + @Override + public String nextToken() + throws NoSuchElementException + { + if (!hasMoreTokens() || _token == null) + throw new NoSuchElementException(); + String t = _token.toString(); + _token.setLength(0); + _hasToken = false; + return t; + } + + /* ------------------------------------------------------------ */ + @Override + public String nextToken(String delim) + throws NoSuchElementException + { + _delim = delim; + _i = _lastStart; + _token.setLength(0); + _hasToken = false; + return nextToken(); + } + + /* ------------------------------------------------------------ */ + @Override + public boolean hasMoreElements() + { + return hasMoreTokens(); + } + + /* ------------------------------------------------------------ */ + @Override + public Object nextElement() + throws NoSuchElementException + { + return nextToken(); + } + + /* ------------------------------------------------------------ */ + /** Not implemented. + */ + @Override + public int countTokens() + { + return -1; + } + + + /* ------------------------------------------------------------ */ + /** Quote a string. + * The string is quoted only if quoting is required due to + * embedded delimiters, quote characters or the + * empty string. + * @param s The string to quote. + * @return quoted string + */ + public static String quote(String s, String delim) + { + if (s == null) + return null; + if (s.isEmpty()) + return "\"\""; + + + for (int i = 0; i < s.length(); i++) + { + char c = s.charAt(i); + if (c == '\\' || c == '"' || c == '\'' || Character.isWhitespace(c) || delim.indexOf(c) >= 0) + { + StringBuffer b = new StringBuffer(s.length() + 8); + quote(b, s); + return b.toString(); + } + } + + return s; + } + + /* ------------------------------------------------------------ */ + /** Quote a string. + * The string is quoted only if quoting is required due to + * embedded delimiters, quote characters or the + * empty string. + * @param s The string to quote. + * @return quoted string + */ + public static String quote(String s) + { + if (s == null) + return null; + if (s.isEmpty()) + return "\"\""; + + StringBuffer b = new StringBuffer(s.length() + 8); + quote(b, s); + return b.toString(); + + } + + + /* ------------------------------------------------------------ */ + /** Quote a string into a StringBuffer. + * The characters ", \, \n, \r, \t, \f and \b are escaped + * @param buf The StringBuffer + * @param s The String to quote. + */ + public static void quote(StringBuffer buf, String s) + { + synchronized (buf) + { + buf.append('"'); + for (int i = 0; i < s.length(); i++) + { + char c = s.charAt(i); + switch (c) + { + case '"': + buf.append("\\\""); + continue; + case '\\': + buf.append("\\\\"); + continue; + case '\n': + buf.append("\\n"); + continue; + case '\r': + buf.append("\\r"); + continue; + case '\t': + buf.append("\\t"); + continue; + case '\f': + buf.append("\\f"); + continue; + case '\b': + buf.append("\\b"); + continue; + + default: + buf.append(c); + } + } + buf.append('"'); + } + } + + /* ------------------------------------------------------------ */ + /** Unquote a string. + * @param s The string to unquote. + * @return quoted string + */ + public static String unquote(String s) + { + if (s == null) + return null; + if (s.length() < 2) + return s; + + char first = s.charAt(0); + char last = s.charAt(s.length() - 1); + if (first != last || (first != '"' && first != '\'')) + return s; + + StringBuilder b = new StringBuilder(s.length() - 2); + boolean escape = false; + for (int i = 1; i < s.length() - 1; i++) + { + char c = s.charAt(i); + + if (escape) + { + escape = false; + switch (c) + { + case 'n': + b.append('\n'); + break; + case 'r': + b.append('\r'); + break; + case 't': + b.append('\t'); + break; + case 'f': + b.append('\f'); + break; + case 'b': + b.append('\b'); + break; + case 'u': + b.append((char) ( + (convertHexDigit((byte) s.charAt(i++)) << 24) + + (convertHexDigit((byte) s.charAt(i++)) << 16) + + (convertHexDigit((byte) s.charAt(i++)) << 8) + + convertHexDigit((byte) s.charAt(i++)) + ) + ); + break; + default: + b.append(c); + } + } + else if (c == '\\') + { + escape = true; + } + else + b.append(c); + } + + return b.toString(); + } + + /* ------------------------------------------------------------ */ + /** + * @return handle double quotes if true + */ + public boolean getDouble() + { + return _double; + } + + /* ------------------------------------------------------------ */ + /** + * @param d handle double quotes if true + */ + public void setDouble(boolean d) + { + _double = d; + } + + /* ------------------------------------------------------------ */ + /** + * @return handle single quotes if true + */ + public boolean getSingle() + { + return _single; + } + + /* ------------------------------------------------------------ */ + /** + * @param single handle single quotes if true + */ + public void setSingle(boolean single) + { + _single = single; + } + + /** + * @param b An ASCII encoded character 0-9 a-f A-F + * @return The byte value of the character 0-16. + */ + public static byte convertHexDigit(byte b) + { + if (b >= '0' && b <= '9') return (byte) (b - '0'); + if (b >= 'a' && b <= 'f') return (byte) (b - 'a' + 10); + if (b >= 'A' && b <= 'F') return (byte) (b - 'A' + 10); + return 0; + } + + /** + * Characters that can be escaped with \. + * + * Others, like, say, \W will be left alone instead of becoming just W. + * This is important to keep Hudson behave on Windows, which uses '\' as + * the directory separator. + */ + private static final String ESCAPABLE_CHARS = "\\\"' "; +} diff --git a/modules/ci-worker/src/main/java/cd/casic/devops/common/process/Util.java b/modules/ci-worker/src/main/java/cd/casic/devops/common/process/Util.java new file mode 100644 index 0000000..626cf8f --- /dev/null +++ b/modules/ci-worker/src/main/java/cd/casic/devops/common/process/Util.java @@ -0,0 +1,101 @@ +/* + * The MIT License + * + * Copyright (c) 2004-2009, Sun Microsystems, Inc., Kohsuke Kawaguchi + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +package cd.casic.devops.common.process; + +import java.util.Map; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +// copy from jenkins 96a66619b55b3b78b86817798fe36e58b2798cd4 + +public class Util { + /** + * Convert null to "". + */ + public static String fixNull(String s) { + return fixNull(s, ""); + } + + /** + * Convert {@code null} to a default value. + * + * @param defaultValue Default value. It may be immutable or not, depending on the implementation. + * @since 2.144 + */ + public static T fixNull(T s, T defaultValue) { + return s != null ? s : defaultValue; + } + + /** + * Pattern for capturing variables. Either $xyz, ${xyz} or ${a.b} but not $a.b, while ignoring "$$" + */ + private static final Pattern VARIABLE = Pattern.compile("\\$([A-Za-z0-9_]+|\\{[A-Za-z0-9_.]+\\}|\\$)"); + + /** + * Replaces the occurrence of '$key' by {@code properties.get('key')}. + * + *

+ * Unlike shell, undefined variables are left as-is (this behavior is the same as Ant.) + */ + public static String replaceMacro(String s, Map properties) { + return replaceMacro(s, new VariableResolver.ByMap<>(properties)); + } + + /** + * Replaces the occurrence of '$key' by {@code resolver.get('key')}. + * + *

+ * Unlike shell, undefined variables are left as-is (this behavior is the same as Ant.) + */ + public static String replaceMacro(String s, VariableResolver resolver) { + if (s == null) { + return null; + } + + int idx = 0; + while (true) { + Matcher m = VARIABLE.matcher(s); + if (!m.find(idx)) return s; + + String key = m.group().substring(1); + + // escape the dollar sign or get the key to resolve + String value; + if (key.charAt(0) == '$') { + value = "$"; + } else { + if (key.charAt(0) == '{') key = key.substring(1, key.length() - 1); + value = resolver.resolve(key); + } + + if (value == null) + idx = m.end(); // skip this + else { + s = s.substring(0, m.start()) + value + s.substring(m.end()); + idx = m.start() + value.length(); + } + } + } +} diff --git a/modules/ci-worker/src/main/java/cd/casic/devops/common/process/VariableResolver.java b/modules/ci-worker/src/main/java/cd/casic/devops/common/process/VariableResolver.java new file mode 100644 index 0000000..3cfee78 --- /dev/null +++ b/modules/ci-worker/src/main/java/cd/casic/devops/common/process/VariableResolver.java @@ -0,0 +1,102 @@ +/* + * The MIT License + * + * Copyright (c) 2004-2009, Sun Microsystems, Inc., Kohsuke Kawaguchi + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +package cd.casic.devops.common.process; + +import java.util.Collection; +import java.util.Map; + +// copy from jenkins 96a66619b55b3b78b86817798fe36e58b2798cd4 + +/** + * Resolves variables to its value, while encapsulating + * how that resolution happens. + * + * @author Kohsuke Kawaguchi + */ +public interface VariableResolver { + /** + * Receives a variable name and obtains the value associated with the name. + * + *

+ * This can be implemented simply on top of a {@link Map} (see {@link ByMap}), or + * this can be used like an expression evaluator. + * + * @param name + * Name of the variable to be resolved. + * Never null, never empty. The name shouldn't include the syntactic + * marker of an expression. IOW, it should be "foo" but not "${foo}". + * A part of the goal of this design is to abstract away the expression + * marker syntax. + * @return + * Object referenced by the name. + * Null if not found. + */ + V resolve(String name); + + /** + * Empty resolver that always returns null. + */ + VariableResolver NONE = name -> null; + + /** + * {@link VariableResolver} backed by a {@link Map}. + */ + final class ByMap implements VariableResolver { + private final Map data; + + public ByMap(Map data) { + this.data = data; + } + + @Override + public V resolve(String name) { + return data.get(name); + } + } + + /** + * Union of multiple {@link VariableResolver}. + */ + final class Union implements VariableResolver { + private final VariableResolver[] resolvers; + + public Union(VariableResolver... resolvers) { + this.resolvers = resolvers.clone(); + } + + public Union(Collection> resolvers) { + this.resolvers = resolvers.toArray(new VariableResolver[0]); + } + + @Override + public V resolve(String name) { + for (VariableResolver r : resolvers) { + V v = r.resolve(name); + if (v != null) return v; + } + return null; + } + } +} diff --git a/modules/ci-worker/src/main/java/cd/casic/devops/common/process/jna/GNUCLibrary.java b/modules/ci-worker/src/main/java/cd/casic/devops/common/process/jna/GNUCLibrary.java new file mode 100644 index 0000000..bfb155a --- /dev/null +++ b/modules/ci-worker/src/main/java/cd/casic/devops/common/process/jna/GNUCLibrary.java @@ -0,0 +1,148 @@ +/* + * The MIT License + * + * Copyright (c) 2004-2009, Sun Microsystems, Inc., Kohsuke Kawaguchi + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +package cd.casic.devops.common.process.jna; + +import com.sun.jna.LastErrorException; +import com.sun.jna.Library; +import com.sun.jna.Memory; +import com.sun.jna.Native; +import com.sun.jna.NativeLong; +import com.sun.jna.Pointer; +import com.sun.jna.StringArray; +import com.sun.jna.ptr.IntByReference; +import com.sun.jna.ptr.NativeLongByReference; + +// copy from jenkins 96a66619b55b3b78b86817798fe36e58b2798cd4 + +/** + * GNU C library. + * + *

+ * Not available on all platforms (such as Linux/PPC, IBM mainframe, etc.), so the caller should recover gracefully + * in case of {@link LinkageError}. See JENKINS-4820. + * @author Kohsuke Kawaguchi + */ +public interface GNUCLibrary extends Library { + int fork(); + + int kill(int pid, int signum); + + int setsid(); + + int umask(int mask); + + int getpid(); + + int geteuid(); + + int getegid(); + + int getppid(); + + int chdir(String dir); + + int getdtablesize(); + + int execv(String path, StringArray args); + + int execvp(String file, StringArray args); + + int setenv(String name, String value, int replace); + + int unsetenv(String name); + + void perror(String msg); + + String strerror(int errno); + + int fcntl(int fd, int command); + + int fcntl(int fd, int command, Object... flags); + + // obtained from Linux. Needs to be checked if these values are portable. + int F_GETFD = 1; + int F_SETFD = 2; + int FD_CLOEXEC = 1; + + int chown(String fileName, int uid, int gid); + + int chmod(String fileName, int i); + + int open(String pathname, int flags) throws LastErrorException; + + int dup(int old); + + int dup2(int old, int _new); + + long pread(int fd, Memory buffer, NativeLong size, NativeLong offset) throws LastErrorException; + + int close(int fd); + + // see http://www.gnu.org/s/libc/manual/html_node/Renaming-Files.html + int rename(String oldname, String newname); + + // The following three functions are Darwin-specific. The native "long" and "size_t" types always have + // the same size on Darwin, we use NativeLong and NativeLongByReference where the native functions use + // "size_t" and "size_t *" respectively. By updating JNA to 5.9.0 and adding a dependency on "jna-platform", + // the "com.sun.jna.platform.unix.LibCAPI.size_t" and "com.sun.jna.platform.unix.LIBCAPI.size_t.ByReference" + // types could be used instead. + + // this is listed in http://developer.apple.com/DOCUMENTATION/Darwin/Reference/ManPages/man3/sysctlbyname.3.html + // but not in http://www.gnu.org/software/libc/manual/html_node/System-Parameters.html#index-sysctl-3493 + // perhaps it is only supported on BSD? + @Deprecated + int sysctlbyname(String name, Pointer oldp, IntByReference oldlenp, Pointer newp, IntByReference newlen); + + int sysctlbyname(String name, Pointer oldp, NativeLongByReference oldlenp, Pointer newp, NativeLong newlen); + + @Deprecated + int sysctl(int[] mib, int nameLen, Pointer oldp, IntByReference oldlenp, Pointer newp, IntByReference newlen); + + int sysctl(int[] name, int namelen, Pointer oldp, NativeLongByReference oldlenp, Pointer newp, NativeLong newlen); + + @Deprecated + int sysctlnametomib(String name, Pointer mibp, IntByReference size); + + int sysctlnametomib(String name, Pointer mibp, NativeLongByReference sizep); + + /** + * Creates a symlink. + * + * See symlink(3) + */ + int symlink(String oldname, String newname); + + /** + * Read a symlink. The name will be copied into the specified memory, and returns the number of + * bytes copied. The string is not null-terminated. + * + * @return + * if the return value equals size, the caller needs to retry with a bigger buffer. + * If -1, error. + */ + int readlink(String filename, Memory buffer, NativeLong size); + + GNUCLibrary LIBC = Native.load("c", GNUCLibrary.class); +} diff --git a/modules/ci-worker/src/main/java/cd/casic/devops/common/worker/Constants.java b/modules/ci-worker/src/main/java/cd/casic/devops/common/worker/Constants.java new file mode 100644 index 0000000..1b8fdff --- /dev/null +++ b/modules/ci-worker/src/main/java/cd/casic/devops/common/worker/Constants.java @@ -0,0 +1,34 @@ +package cd.casic.devops.common.worker; + +import java.util.Arrays; +import java.util.List; + +public class Constants { + public static final String BUILD_ID = "devops.build.id"; + public static final String BUILD_TYPE = "build.type"; + public static final String WORKSPACE_ENV = "WORKSPACE"; + public static final String COMMON_ENV_CONTEXT = "common_env"; + public static final String WORKSPACE_CONTEXT = "ci.workspace"; + public static final String CI_TOKEN_CONTEXT = "ci.token"; + public static final String JOB_OS_CONTEXT = "job.os"; + public static final String SLAVE_AGENT_START_FILE = "devops.slave.agent.start.file"; + public static final String SLAVE_AGENT_PREPARE_START_FILE = "devops.slave.agent.prepare.start.file"; + public static final String AGENT_ERROR_MSG_FILE = "devops.agent.error.file"; + public static final String CLEAN_WORKSPACE = "DEVOPS_CLEAN_WORKSPACE"; + public static final String JAVA_PATH_ENV = "bk_java_path"; + public static final String NODEJS_PATH_ENV = "bk_nodejs_path"; + public static final String LOG_DEBUG_FLAG = "##[debug]"; + public static final String LOG_ERROR_FLAG = "##[error]"; + public static final String LOG_WARN_FLAG = "##[warning]"; + public static final String LOG_SUBTAG_FLAG = "##subTag##"; + public static final String LOG_SUBTAG_FINISH_FLAG = "##subTagFinish##"; + public static final int LOG_MESSAGE_LENGTH_LIMIT = 16 * 1024; // 16KB + public static final int LOG_TASK_LINE_LIMIT = 1000000; + public static final long LOG_FILE_LENGTH_LIMIT = 1073741824L; // 1 GB = 1073741824 Byte + public static final List PIPELINE_SCRIPT_ATOM_CODE = Arrays.asList( + "PipelineScriptDev", + "PipelineScriptTest", + "PipelineScript" + ); + public static final String BK_CI_ATOM_EXECUTE_ENV_PATH = "BK_CI_ATOM_EXECUTE_ENV_PATH"; +} \ No newline at end of file diff --git a/modules/ci-worker/src/main/java/cd/casic/devops/common/worker/constant/WorkerMessageCode.java b/modules/ci-worker/src/main/java/cd/casic/devops/common/worker/constant/WorkerMessageCode.java new file mode 100644 index 0000000..003e300 --- /dev/null +++ b/modules/ci-worker/src/main/java/cd/casic/devops/common/worker/constant/WorkerMessageCode.java @@ -0,0 +1,230 @@ +/* + * Tencent is pleased to support the open source community by making BK-CI 蓝鲸持续集成平台 available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * BK-CI 蓝鲸持续集成平台 is licensed under the MIT license. + * + * A copy of the MIT License is included in this file. + * + * ... + */ + +package cd.casic.devops.common.worker.constant; + +public class WorkerMessageCode { + public static final String MSG_CODE_ROLE_PREFIX = "MSG_CODE_ROLE_PREFIX_";// 角色国际化前缀 + public static final String MSG_CODE_PERMISSION_PREFIX = "MSG_CODE_PERMISSION_PREFIX_";// 操作权限国际化前缀 + public static final String SUCCESS = "0";// 成功 + public static final int OAUTH_DENERD = 418;// 自定义状态码, 未进行oauth认证 + public static final String SYSTEM_ERROR = "2100001";// 系统内部繁忙,请稍后再试 + public static final String PARAMETER_IS_NULL = "2100002";// 参数{0}不能为空 + public static final String PARAMETER_IS_EXIST = "2100003";// 参数值{0}已经存在系统,请换一个再试 + public static final String PARAMETER_IS_INVALID = "2100004";// 参数值{0}为非法数据 + public static final String OAUTH_TOKEN_IS_INVALID = "2100005";// 无效的token,请先oauth认证 + public static final String PERMISSION_DENIED = "2100006";// 无权限{0} + public static final String ERROR_SERVICE_NO_FOUND = "2100007";// "找不到任何有效的{0}服务提供者" + public static final String ERROR_SERVICE_INVOKE_FAILURE = "2100008";// "服务调用失败:{0},uniqueId={1}" + public static final String ERROR_INVALID_CONFIG = "2100009";// "配置不可用:{0},uniqueId={1}" + public static final String ERROR_REST_EXCEPTION_COMMON_TIP = "2100010";// 接口访问出现异常,请联系助手或稍后再重试 + public static final String ERROR_CLIENT_REST_ERROR = "2100011";// 用户请求不合法,参数或方法错误,请咨询助手 + public static final String ERROR_PROJECT_FEATURE_NOT_ACTIVED = "2100012";// 项目[{0}]未开通该功能 + public static final String ERROR_INVALID_PARAM_ = "2100013";// 无效参数: {0} + public static final String ERROR_NEED_PARAM_ = "2100014";// 缺少参数: {0} + public static final String PARAMETER_VALIDATE_ERROR = "2100015";// {0}参数校验错误: {1} + public static final String ERROR_SERVICE_NO_AUTH = "2100016";// 无访问服务的权限 + public static final String ERROR_QUERY_NUM_TOO_BIG = "2100017";// 查询的数量超过系统规定的值:{0},请调整查询条件或咨询助手 + public static final String ERROR_QUERY_TIME_RANGE_TOO_LARGE = "2100018";// 查询的时间范围跨度最大,最长时间范围跨度不能超过{0}天 + public static final String ERROR_HTTP_RESPONSE_BODY_TOO_LARGE = "2100019";// http请求返回体太大 + public static final String PERMISSION_DENIED_FOR_APP = "2100020";// APP的无权限{0} + public static final String ERROR_SENSITIVE_API_NO_AUTH = "2100021";// 无敏感API访问权限 + public static final String PARAMETER_LENGTH_TOO_LONG = "2100022";// 参数长度不能超过{0}个字符 + public static final String PARAMETER_LENGTH_TOO_SHORT = "2100023";// 参数长度不能小于{0}个字符 + public static final String PARAMETER_ILLEGAL_ERROR = "2100024";// {0}参数非法错误: {1} + public static final String PARAMETER_EXPIRED_ERROR = "2100025";// {0}token过期错误: {1} + public static final String PARAMETER_SECRET_ERROR = "2100026";// {0}密钥配置错误: {1} + public static final String PARAMETER_IS_EMPTY = "2100027";// 参数不能为空 + public static final String ERROR_QUERY_TIME_RANGE_ERROR = "2100028";// 查询的时间范围跨度错误 + public static final String SERVICE_NOT_EXIST = "2100029";// 父服务不存在异常 + + public static final String ILLEGAL_GITCI_SERVICE_IMAGE_FORMAT = "2100030";// GITCI Service镜像格式非法 + public static final String THIRD_PARTY_SERVICE_DEVCLOUD_EXCEPTION = "2100031";// 第三方服务-DEVCLOUD 异常,请联系8006排查,异常信息 - + public static final String CREATE_CONTAINER_INTERFACE_EXCEPTION = "2100032";// 创建容器接口异常 + public static final String CREATE_CONTAINER_RETURNS_FAILED = "2100033";// 创建容器接口返回失败 + public static final String CREATE_CONTAINER_TIMED_OUT = "2100034";// 创建容器接口超时 + public static final String OPERATION_CONTAINER_INTERFACE_EXCEPTION = "2100035";// 操作容器接口异常 + public static final String OPERATION_CONTAINER_RETURNED_FAILURE = "2100036";// 操作容器接口返回失败 + public static final String OPERATION_CONTAINER_TIMED_OUT = "2100037";// 操作容器接口超时 + public static final String GET_STATUS_INTERFACE_EXCEPTION = "2100038";// 获取容器状态接口异常 + public static final String GET_STATUS_TIMED_OUT = "2100039";// 获取容器状态接口超时 + public static final String CREATE_MIRROR_INTERFACE_EXCEPTION = "2100040";// 创建镜像接口异常 + public static final String CREATE_MIRROR_INTERFACE_RETURNED_FAILURE = "2100041";// 创建镜像接口返回失败 + public static final String CREATE_MIRROR_INTERFACE_EXCEPTION_NEW = "2100042";// 创建镜像新版本接口异常 + public static final String NEW_MIRROR_INTERFACE_RETURNED_FAILURE = "2100043";// 创建镜像新版本接口返回失败 + public static final String TASK_STATUS_INTERFACE_EXCEPTION = "2100044";// 获取TASK状态接口异常 + public static final String TASK_STATUS_TIMED_OUT = "2100045";// 获取TASK状态接口超时 + public static final String GET_WEBSOCKET_INTERFACE_EXCEPTION = "2100046";// 获取websocket接口异常 + public static final String PARAMETER_CANNOT_EMPTY_ALL = "2100047";// 参数不能全部为空 + public static final String USERS_EXCEEDS_THE_LIMIT = "2100048";// 授权用户数越界:{0} + public static final String FAILED_TO_QUERY_GSE_AGENT_STATUS = "2100049";// 查询 Gse Agent 状态失败 + public static final String FAILED_TO_GET_AGENT_STATUS = "2100050";// 获取agent状态失败 + public static final String STAGES_AND_STEPS_CANNOT_EXIST_BY_SIDE = "2100053";// stages和steps不能并列存在! + + public static final String USER_NOT_PERMISSIONS_OPERATE_PIPELINE = "2100054";// 用户({0})无权限在工程({1})下{2}流水线{3} + public static final String USER_NOT_HAVE_PROJECT_PERMISSIONS = "2100055";// 用户 {0}无项目{1}权限 + public static final String UNABLE_GET_PIPELINE_JOB_STATUS = "2100056";// 无法获取流水线JOB状态,构建停止 + public static final String JOB_BUILD_STOPS = "2100057";// 流水线JOB已经不再运行,构建停止 + public static final String PIPELINE_NAME_OCCUPIED = "2100058";// 流水线名称已被他人使用 + public static final String INTERNAL_DEPENDENCY_SERVICE_EXCEPTION = "2100059";// 内部依赖服务异常 + public static final String PUBLIC_BUILD_RESOURCE_POOL_NOT_EXIST = "2100060";// 公共构建资源池不存在,请检查yml配置. + public static final String ERROR_LANGUAGE_IS_NOT_SUPPORT = "2100061";// 该语言蓝盾目前不支持,蓝盾目前支持的语言标识为:{0} + public static final String INIT_SERVICE_LIST_ERROR = "2100062";// 初始化服务列表异常问题 + public static final String FILE_NOT_EXIST = "2100063";// 文件{0}不存在 + public static final String USER_ACCESS_CHECK_FAIL = "2100064";// Gitlab access token 不正确 + + public static final String GITLAB_TOKEN_EMPTY = "2100065";// GitLab Token为空 + public static final String GITLAB_HOOK_URL_EMPTY = "2100066";// GitLab hook url为空 + public static final String GITLAB_TOKEN_FAIL = "2100067";// GitLab Token不正确 + public static final String GIT_TOKEN_FAIL = "2100068";// Git Token不正确 + public static final String SERCRT_EMPTY = "2100069";// GIT 私钥为空 + public static final String GIT_SERCRT_WRONG = "2100070";// Git 私钥不对 + public static final String PWD_EMPTY = "2100071";// 用户密码为空 + public static final String USER_NAME_EMPTY = "2100072";// 用户名为空 + public static final String GITLAB_INVALID = "2100073";// 无效的GITLAB仓库 + public static final String GIT_TOKEN_WRONG = "2100074";// Git Token 不正确 + public static final String GIT_LOGIN_FAIL = "2100075";// Git 用户名或者密码不对 + public static final String GIT_TOKEN_EMPTY = "2100076";// Git Token为空 + public static final String GIT_HOOK_URL_EMPTY = "2100077";// Git hook url为空 + public static final String TGIT_LOGIN_FAIL = "2100078";// TGit 用户名或者密码不对 + public static final String TGIT_TOKEN_FAIL = "2100079";// TGit Token 不正确 + public static final String TGIT_SECRET_WRONG = "2100080";// TGit 私钥不对 + public static final String SVN_SECRET_OR_PATH_ERROR = "2100081";// SVN 私钥不正确 或者 SVN 路径没有权限 + public static final String SVN_CREATE_HOOK_FAIL = "2100082";// 添加SVN WEB hook 失败 + public static final String LOCK_FAIL = "2100083";// lock失败 + public static final String UNLOCK_FAIL = "2100084";// unlock失败 + public static final String GIT_REPO_PEM_FAIL = "2100085";// 代码仓库访问未授权 + public static final String CALL_REPO_ERROR = "2100086";// 代码仓库访问异常 + public static final String P4_USERNAME_PASSWORD_FAIL = "2100087";// p4用户名密码错误 + public static final String PARAM_ERROR = "2100088";// 参数错误 + public static final String AUTH_FAIL = "2100089";// {0}认证失败 + public static final String ACCOUNT_NO_OPERATION_PERMISSIONS = "2100090";// 账户没有{0}的权限 + public static final String REPO_NOT_EXIST_OR_NO_OPERATION_PERMISSION = "2100091";// {0}仓库不存在或者是账户没有该项目{1}的权限 + public static final String GIT_INTERFACE_NOT_EXIST = "2100092";// {0}平台没有{1}的接口 + public static final String GIT_CANNOT_OPERATION = "2100093";// {0}平台{1}操作不能进行 + public static final String WEBHOOK_LOCK_UNLOCK_FAIL = "2100094";// unlock webhooklock失败,请确认token是否已经配置 + public static final String COMMIT_CHECK_ADD_FAIL = "2100095";// Commit Check添加失败,请确保该代码库的凭据关联的用户对代码库有Developer权限 + public static final String ADD_MR_COMMENTS_FAIL = "2100096";// 添加MR的评论失败,请确保该代码库的凭据关联的用户对代码库有Developer权限 + public static final String WEBHOOK_ADD_FAIL = "2100097";// Webhook添加失败,请确保该代码库的凭据关联的用户对代码库有{0}权限 + public static final String WEBHOOK_UPDATE_FAIL = "2100098";// Webhook更新失败,请确保该代码库的凭据关联的用户对代码库有Developer权限 + public static final String ENGINEERING_REPO_UNAUTHORIZED = "2100099";// 工程仓库访问未授权 + public static final String ENGINEERING_REPO_NOT_EXIST = "2100100";// 工程仓库不存在 + public static final String ENGINEERING_REPO_CALL_ERROR = "2100101";// 工程仓库访问异常 + public static final String NOT_MEMBER_AND_NOT_OPEN_SOURCE = "2100102";// 非项目成员且项目为非开源项目 + public static final String USER_NO_PIPELINE_PERMISSION = "2100108";// 流水线: 用户无{0}权限 + public static final String SERVICE_COULD_NOT_BE_ANALYZED = "2100109";// 无法根据接口"{0}"分析所属的服务 + public static final String RETURNED_RESULT_COULD_NOT_BE_PARSED = "2100110";// 内部服务返回结果无法解析 status:{0} body:{1} + public static final String SERVICE_PROVIDER_NOT_FOUND = "2100111";// 找不到任何有效的{0}【{1}】服务提供者 + public static final String ILLEGAL_JOB_TYPE = "2100112";// 非法的job类型! + public static final String ERROR_YAML_FORMAT_EXCEPTION = "2100113";// {0} 中 {1} 格式有误,应为 {2}, error message:{3} + public static final String ERROR_YAML_FORMAT_EXCEPTION_CHECK_STAGE_LABEL = "2100114";// 请核对Stage标签是否正确 + public static final String ERROR_YAML_FORMAT_EXCEPTION_LENGTH_LIMIT_EXCEEDED = "2100115";// "{0} job.id 超过长度限制64 {1}}" + public static final String ERROR_YAML_FORMAT_EXCEPTION_NEED_PARAM = "2100116";// {0} 中的step必须包含uses或run或checkout! + public static final String ERROR_YAML_FORMAT_EXCEPTION_SERVICE_IMAGE_FORMAT_ILLEGAL = "2100117";// STREAM Service镜像格式非法 + public static final String ERROR_YAML_FORMAT_EXCEPTION_STEP_ID_UNIQUENESS = "2100118";// 请确保step.id唯一性!({0}) + public static final String BUILD_RESOURCE_NOT_EXIST = "2100119";// {0}构建资源不存在,请检查yml配置. + public static final String ERROR_YAML_FORMAT_EXCEPTION_ENV_QUANTITY_LIMIT_EXCEEDED = "2100120";// {0}配置Env数量超过100限制! + ;// {0}Env单变量{1}长度超过{2}字符!({3}) + public static final String ERROR_YAML_FORMAT_EXCEPTION_ENV_VARIABLE_LENGTH_LIMIT_EXCEEDED = "2100121"; + public static final String ERROR_PROJECT_API_ACCESS_NO_PERMISSION = "2100122";// 项目[{0}]没有接口[{1}]的访问权限 + public static final String ERROR_INTERFACE_RETRY_NUM_EXCEEDED = "2100123";// 接口连续重试次数超过{0}次,请稍后再试 + public static final String ERROR_PIPELINE_API_ACCESS_NO_PERMISSION = "2100124";// 流水线[{0}]没有接口[{1}]的访问权限 + public static final String TEMPLATE_PLUGIN_NOT_ALLOWED_USE = "2100125";// 模板中插件【{0}】的【{1}】版本的状态是【{2}】,不允许使用 + public static final String ADD_MR_FAIL = "2100126";// 添加MR失败 + + ;// 互转使用 + public static final String ELEMENT_UPDATE_WRONG_PATH = "2100127";// 更新插件的标注位置有误 + public static final String ELEMENT_NOT_SUPPORT_TRANSFER = "2100128";// 如下插件在 Code 方式下已不支持,请修改后再切换: \n[{0}] + public static final String DISPATCH_NOT_SUPPORT_TRANSFER = "2100129";// 如下构建环境在 Code 方式下不支持转换,请修改后再切换: \n[{0}] + public static final String YAML_NOT_VALID = "2100130";// yaml不合法 {0} + public static final String GIT_INVALID_PRIVATE_KEY = "2100131";// 不支持的SSH私钥格式,仅支持rsa格式私钥 + public static final String GIT_INVALID_PRIVATE_KEY_OR_PASSWORD = "2100132";// 第三方服务[{0}]操作失败,失败详情:{1} + public static final String MR_ACCEPT_EVENT_NOT_SUPPORT_TRANSFER = "2100133";// mr accept事件类型不支持code转换 + + public static final String SVN_TOKEN_FAIL = "2100135";// SVN Token 不正确 + public static final String SVN_TOKEN_EMPTY = "2100136";// SVN Token 为空, 请检查代码库的凭证类型 + public static final String BK_CONTAINER_TIMED_OUT = "bkContainerTimedOut";// 创建容器超时 + public static final String BK_CREATION_FAILED_EXCEPTION_INFORMATION = "bkCreationFailedExceptionInformation";// 创建失败,异常信息 + + public static final String BK_FILE_NAME = "bkFileName";// 文件名 + public static final String BK_BELONG_TO_THE_PROJECT = "bkBelongToTheProject";// 所属项目 + public static final String BK_OPERATING = "bkOperating";// 操作 + public static final String BK_PUSH_FROM_BLUE_SHIELD_DEVOPS_PLATFORM = "bkPushFromBlueShieldDevopsPlatform";// 来自蓝盾DevOps平台的推送 + public static final String BK_TABLE_CONTENTS = "bkTableContents";// 表格内容 + public static final String BK_PLEASE_FEEL_TO_CONTACT_BLUE_SHIELD_ASSISTANT = "bkPleaseFeelToContactBlueShieldAssistant";// 如有任何问题,可随时联系蓝盾助手 + public static final String BK_ETH1_NETWORK_CARD_IP_EMPTY = "bkEth1NetworkCardIpEmpty";// eth1 网卡Ip为空,因此,获取eth0的网卡ip + public static final String BK_LOOPBACK_ADDRESS_OR_NIC_EMPTY = "bkLoopbackAddressOrNicEmpty";// loopback地址或网卡名称为空 + public static final String BK_FAILED_GET_NETWORK_CARD = "bkFailedGetNetworkCard";// 获取网卡失败 + public static final String BK_MANUAL_TRIGGER = "bkManualTrigger";// 手动触发 + public static final String BK_BUILD_TRIGGER = "bkBuildTrigger";// 构建触发 + public static final String BK_VIEW_DETAILS = "bkSeeDetails";// 查看详情 + public static final String BK_PROJECT_ID = "bkProjectId";// # 项目ID: + public static final String BK_PIPELINE_NAME = "bkPipelineName";// # 流水线名称: + public static final String BK_CREATE_SERVICE = "bkCreateService";// 创建{0}服务 + public static final String BK_SESSION_ID = "bkSessionId";// 会话ID + public static final String BK_GROUP_ID = "bkGroupId";// 群ID + public static final String BK_THIS_GROUP_ID = "bkThisGroupId";// 本群ID={0}。PS:群ID可用于蓝盾平台上任意企业微信群通知。 + public static final String BK_MISSING_RESOURCE_DEPENDENCY = "bkMissingResourceDependency";// 依赖的资源不存在 + + public static final String BK_REQUEST_TIMED_OUT = "bkRequestTimedOut";// 请求超时 + public static final String BK_QUERY_PARAM_REQUEST_ERROR = "bkQueryParamRequestError";// 查询参数请求错误 + public static final String BK_JSON_BAD_PARAMETERS = "bkJsonBadParameters";// JSON参数错误/Bad Parameters in json + ;// 请求体内容参数错误。温馨提示:请确认{0}是否符合要求 + public static final String BK_REQUEST_BODY_CONTENT_PARAMETER_INCORRECT = "bkRequestBodyContentParameterIncorrect"; + public static final String BK_REQUESTED_RESOURCE_DOES_NOT_EXIST = "bkRequestedResourceDoesNotExist";// 请求的资源不存在 + public static final String BK_NOT_OAUTH_CERTIFICATION = "bkNotOauthCertification";// 你没有Oauth认证 + public static final String BK_QUERY_PARAM_REQUEST_EMPTY = "bkQueryParamRequestEmpty";// 请求的参数内容为空 + public static final String BK_QUERY_PARAM_TYPE_ERROR = "bkQueryParamTypeError";// 查询参数类型错误 + ;// 你没有权限进行该操作 + public static final String BK_NOT_HAVE_PERMISSION_PERFORM_THIS_OPERATION = "bkNotHavePermissionPerformThisOperation";// 访问后台数据失败,已通知产品、开发,请稍后重试 + public static final String BK_FAILED_ACCESS_BACKGROUND_DATA = "bkFailedAccessBackgroundData";// 未授权访问的资源 + public static final String BK_RESOURCES_THAT_NOT_AUTHORIZED_ACCESS = "bkResourcesThatNotAuthorizedAccess"; + public static final String BK_CODE_BASE_TRIGGERING = "bkCodeBaseTriggering";// 代码库触发 + public static final String BK_FAILED_START_BUILD_MACHINE = "bkFailedStartBuildMachine";// 启动构建机失败 + + public static final String CREATE_BRANCH = "bkCreateBranch";// 创建分支 + public static final String DELETE_BRANCH = "bkDeleteBranch";// 删除分支 + + public static final String GET_PROJECT_INFO = "bkGetProjectInfo";// 获取项目详情 + public static final String GET_COMMIT_REVIEW_INFO = "bkGetCommitReviewInfo";// 获取Commit Review详情 + public static final String GET_SESSION_INFO = "bkGetSessionInfo";// 获取会话详情 + + public static final String OPERATION_BRANCH = "bkOperationBranch";// 拉分支 + public static final String OPERATION_TAG = "bkOperationTag";// 拉标签 + public static final String OPERATION_ADD_WEBHOOK = "bkOperationAddWebhook";// 添加WEBHOOK + public static final String OPERATION_UPDATE_WEBHOOK = "bkOperationUpdateWebhook";// 修改WEBHOOK + public static final String OPERATION_LIST_WEBHOOK = "bkOperationListWebhook";// 查询WEBHOOK + public static final String OPERATION_ADD_COMMIT_CHECK = "bkOperationAddCommitCheck";// 添加COMMIT CHECK + public static final String OPERATION_ADD_MR_COMMENT = "bkOperationAddMrComment";// 添加MR COMMENT + public static final String OPERATION_LIST_MR = "bkOperationListMr";// 添加MR + public static final String OPERATION_ADD_MR = "bkOperationAddMr";// 添加MR + public static final String OPERATION_COMMIT = "bkOperationCommit";// 拉提交记录 + public static final String OPERATION_COMMIT_DIFF = "bkOperationCommitDiff";// 查询commit变化 + public static final String OPERATION_UNLOCK_HOOK_LOCK = "bkOperationUnlockHookLock";// 解锁hook锁 + public static final String OPERATION_MR_CHANGE = "bkOperationMrChange";// 查询合并请求的代码变更 + public static final String OPERATION_MR_INFO = "bkOperationMrInfo";// 查询项目合并请求 + public static final String OPERATION_GET_CHANGE_FILE_LIST = "bkOperationGetChangeFileList";// 查询变更文件列表 + public static final String OPERATION_GET_MR_COMMIT_LIST = "bkOperationGetMrCommitList";// 获取合并请求中的提交 + public static final String OPERATION_PROJECT_USER_INFO = "bkOperationProjectUserInfo";// 获取项目中成员信息 + public static final String OPERATION_TAPD_WORKITEMS = "bkOperationTapdWorkItems";// 查看绑定的TAPD单 + public static final String BK_USER_GROUP_CRATE_TIME = "bkUserGroupCrateTime";// {0} 用户组:{1},由{2} 创建于 + public static final String BK_USER_RATING_ADMIN_CRATE_TIME = "bkUserRatingAdminCrateTime";// {0} 分级管理员,由{1} 创建于 + public static final String BK_SECOND_LEVEL_ADMIN_CREATE = "bkSecondLevelAdminCreate";// {0} 二级管理员, 由{1} 创建于 + public static final String BK_SECOND_LEVEL_ADMIN_REVISE = "bkSecondLevelAdminRevise";// {0} 二级管理员, 由{1} 修改于 + ;// 用户 {0} 申请{1}蓝盾项目 {2} ,请审批! + public static final String BK_USER_REQUESTS_THE_PROJECT = "bkUserRequestsTheProject"; + public static final String BK_ENV_NOT_YET_SUPPORTED = "bkEnvNotYetSupported";// 尚未支持 {0} {1},请联系 管理员 添加对应版本 + + public static final String BK_BUILD_ENV_TYPE = "BUILD_ENV_TYPE_";// 构建环境- + public static final String BK_BUILD_ENV_TYPE_BUILDLESS = "BUILD_ENV_TYPE_BUILDLESS";// 无编译环境 + public static final String BK_BUILD_ENV_TYPE_BUILD_TRIGGERS = "BUILD_ENV_TYPE_BUILD_TRIGGER";// 构建触发 +} \ No newline at end of file diff --git a/modules/ci-worker/src/main/java/cd/casic/devops/common/worker/env/AgentEnv.java b/modules/ci-worker/src/main/java/cd/casic/devops/common/worker/env/AgentEnv.java new file mode 100644 index 0000000..728e06b --- /dev/null +++ b/modules/ci-worker/src/main/java/cd/casic/devops/common/worker/env/AgentEnv.java @@ -0,0 +1,186 @@ +/* + * Tencent is pleased to support the open source community by making BK-CI 蓝鲸持续集成平台 available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * BK-CI 蓝鲸持续集成平台 is licensed under the MIT license. + * + * A copy of the MIT License is included in this file. + * + * ... + */ + +package cd.casic.devops.common.worker.env; + +import lombok.SneakyThrows; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import sun.awt.OSInfo; + +import java.io.File; +import java.io.FileInputStream; +import java.util.Locale; +import java.util.Properties; + +public class AgentEnv { + private static final Logger logger = LoggerFactory.getLogger(AgentEnv.class); + + public static final String PROJECT_ID = "devops.project.id"; + public static final String DOCKER_PROJECT_ID = "devops_project_id"; + public static final String AGENT_ID = "devops.agent.id"; + public static final String DOCKER_AGENT_ID = "devops_agent_id"; + public static final String AGENT_SECRET_KEY = "devops.agent.secret.key"; + public static final String DOCKER_AGENT_SECRET_KEY = "devops_agent_secret_key"; + public static final String AGENT_GATEWAY = "landun.gateway"; + public static final String DOCKER_GATEWAY = "devops_gateway"; + public static final String AGENT_FILE_GATEWAY = "DEVOPS_FILE_GATEWAY"; + public static final String AGENT_ENV = "landun.env"; + public static final String AGENT_LOG_SAVE_MODE = "devops_log_save_mode"; + public static final String AGENT_PROPERTIES_FILE_NAME = ".agent.properties"; + public static final String BK_TAG = "devops_bk_tag"; + public static final String PUBLIC_HOST_MAX_FILE_CACHE_SIZE = "devops.public.host.maxFileCacheSize"; + public static final String THIRD_HOST_MAX_FILE_CACHE_SIZE = "devops.public.third.maxFileCacheSize"; + + private static String projectId; + private static String agentId; + private static String secretKey; + private static String gateway; + private static String fileGateway; + private static OSInfo.OSType os; +// private static Env env; +// private static LogStorageMode logStorageMode; + private static String bkTag; + private static Properties property; + + private static final File propertyFile = new File(System.getProperty("user.home"), AGENT_PROPERTIES_FILE_NAME); + + public static String getProjectId() { + if (projectId == null) { + synchronized (AgentEnv.class) { + if (projectId == null) { + projectId = getProperty(PROJECT_ID); + logger.info("Get the project ID({})", projectId); + } + } + } + return projectId; + } + + public static String getAgentId() { + if (agentId == null) { + synchronized (AgentEnv.class) { + if (agentId == null) { + agentId = getProperty(AGENT_ID); + logger.info("Get the agent ID({})", agentId); + } + } + } + return agentId; + } + + public static String getSecretKey() { + if (secretKey == null) { + synchronized (AgentEnv.class) { + if (secretKey == null) { + secretKey = getProperty(AGENT_SECRET_KEY); + logger.info("Get the secret key({})", secretKey); + } + } + } + return secretKey; + } + + public static String getGateway() { + if (gateway == null) { + synchronized (AgentEnv.class) { + if (gateway == null) { + gateway = getProperty(AGENT_GATEWAY); + logger.info("Get the gateway({})", gateway); + } + } + } + return gateway; + } + + public static String getFileGateway() { + if (fileGateway == null) { + synchronized (AgentEnv.class) { + if (fileGateway == null) { + fileGateway = getProperty(AGENT_FILE_GATEWAY); + logger.info("Get the file gateway({})", fileGateway); + } + } + } + return fileGateway; + } + + public static OSInfo.OSType getOs() { + if (os == null) { + synchronized (AgentEnv.class) { + if (os == null) { + os = OSInfo.OSType.valueOf(System.getProperty("os.name").toUpperCase(Locale.ROOT)); + logger.info("Get the OS type({})", os); + } + } + } + return os; + } + + /* public static LogStorageMode getLogMode() { + if (logStorageMode == null) { + synchronized (AgentEnv.class) { + if (logStorageMode == null) { + logStorageMode = LogStorageMode.valueOf(getProperty(AGENT_LOG_SAVE_MODE)); + logger.info("Get the log mode({})", logStorageMode); + } + } + } + return logStorageMode; + }*/ + + public static String getBkTag() { + if (bkTag == null) { + synchronized (AgentEnv.class) { + if (bkTag == null) { + bkTag = getProperty(BK_TAG); + logger.info("Get the bkTag({})", bkTag); + } + } + } + return bkTag; + } + + @SneakyThrows + public static String getProperty(String prop) { + if (property == null) { + if (!propertyFile.exists()) { + throw new Exception("The property file(" + propertyFile.getAbsolutePath() + ") does not exist"); +// throw new ParamBlankException("The property file(" + propertyFile.getAbsolutePath() + ") does not exist"); + } + property = new Properties(); + try (FileInputStream fis = new FileInputStream(propertyFile)) { + property.load(fis); + } catch (Exception e) { + logger.error("Failed to load properties from file", e); + } + } + return property.getProperty(prop); + } + + public static String getEnvProp(String prop) { + String value = System.getenv(prop); + if (value == null || value.isEmpty()) { + value = System.getProperty(prop); + } + return value; + } + + public static boolean isDockerEnv() { + return BuildEnv.getBuildType() == BuildType.DOCKER; + } + + public static String getLocaleLanguage() { + return null; +// return System.getProperty(LOCALE_LANGUAGE, System.getenv(LOCALE_LANGUAGE) != null ? System.getenv(LOCALE_LANGUAGE) : DEFAULT_LOCALE_LANGUAGE); + } +} \ No newline at end of file diff --git a/modules/ci-worker/src/main/java/cd/casic/devops/common/worker/env/BuildEnv.java b/modules/ci-worker/src/main/java/cd/casic/devops/common/worker/env/BuildEnv.java new file mode 100644 index 0000000..f59788a --- /dev/null +++ b/modules/ci-worker/src/main/java/cd/casic/devops/common/worker/env/BuildEnv.java @@ -0,0 +1,51 @@ +/* + * Tencent is pleased to support the open source community by making BK-CI 蓝鲸持续集成平台 available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * BK-CI 蓝鲸持续集成平台 is licensed under the MIT license. + * + * A copy of the MIT License is included in this file. + * + * ... + */ + +package cd.casic.devops.common.worker.env; + + +import static cd.casic.devops.common.worker.Constants.BUILD_ID; +import static cd.casic.devops.common.worker.Constants.BUILD_TYPE; + +public class BuildEnv { + private static String buildType; + + public static BuildType getBuildType() { + if (buildType == null) { + synchronized (BuildEnv.class) { + if (buildType == null) { + buildType = System.getProperty(BUILD_TYPE); + } + } + } + if (buildType == null || !BuildType.contains(buildType)) { + return BuildType.AGENT; + } + return BuildType.valueOf(buildType); + } + + public static void setBuildType(BuildType value) { + buildType = value.name(); + } + + public static boolean isThirdParty() { + return getBuildType() == BuildType.AGENT; + } + + public static String getBuildId() { + return System.getProperty(BUILD_ID); + } + + public static void setBuildId(String buildId) { + System.setProperty(BUILD_ID, buildId); + } +} \ No newline at end of file diff --git a/modules/ci-worker/src/main/java/cd/casic/devops/common/worker/env/BuildType.java b/modules/ci-worker/src/main/java/cd/casic/devops/common/worker/env/BuildType.java new file mode 100644 index 0000000..2ec1f7c --- /dev/null +++ b/modules/ci-worker/src/main/java/cd/casic/devops/common/worker/env/BuildType.java @@ -0,0 +1,30 @@ +/* + * Tencent is pleased to support the open source community by making BK-CI 蓝鲸持续集成平台 available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * BK-CI 蓝鲸持续集成平台 is licensed under the MIT license. + * + * A copy of the MIT License is included in this file. + * + * ... + */ + +package cd.casic.devops.common.worker.env; + +public enum BuildType { + WORKER, + AGENT, + MACOS, + MACOS_NEW, + DOCKER; + + public static boolean contains(String env) { + for (BuildType type : values()) { + if (type.name().equals(env)) { + return true; + } + } + return false; + } +} \ No newline at end of file diff --git a/modules/ci-worker/src/main/java/cd/casic/devops/common/worker/env/DockerEnv.java b/modules/ci-worker/src/main/java/cd/casic/devops/common/worker/env/DockerEnv.java new file mode 100644 index 0000000..1291609 --- /dev/null +++ b/modules/ci-worker/src/main/java/cd/casic/devops/common/worker/env/DockerEnv.java @@ -0,0 +1,198 @@ +/* + * Tencent is pleased to support the open source community by making BK-CI 蓝鲸持续集成平台 available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * BK-CI 蓝鲸持续集成平台 is licensed under the MIT license. + * + * A copy of the MIT License is included in this file. + * + * ... + */ + +package cd.casic.devops.common.worker.env; + +import com.tencent.devops.worker.common.exception.PropertyNotExistException; +import lombok.SneakyThrows; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class DockerEnv { + private static final Logger logger = LoggerFactory.getLogger(DockerEnv.class); + + private static final String PROJECT_ID = "devops_project_id"; + private static final String BUILD_ID = "devops_build_id"; + private static final String AGENT_ID = "devops_agent_id"; + private static final String AGENT_SECRET_KEY = "devops_agent_secret_key"; + private static final String AGENT_GATEWAY = "devops_gateway"; + private static final String JOB_POOL = "JOB_POOL"; + private static final String DOCKER_HOST_IP = "docker_host_ip"; + private static final String DOCKER_HOST_PORT = "docker_host_port"; + private static final String HOSTNAME = "HOSTNAME"; + + private static String projectId; + private static String buildId; + private static String agentId; + private static String secretKey; + private static String gateway; + private static String jobPool; + private static String dockerHostIp; + private static String dockerHostPort; + private static String hostname; + + public static void setProjectId(String projectId) { + System.setProperty(PROJECT_ID, projectId); + } + + @SneakyThrows + public static String getProjectId() { + if (projectId == null) { + synchronized (DockerEnv.class) { + if (projectId == null) { + projectId = getProperty(PROJECT_ID); + if (projectId == null || projectId.isEmpty()) { + throw new Exception(PROJECT_ID+ "Empty project Id"); +// throw new PropertyNotExistException(PROJECT_ID, "Empty project Id"); + } + logger.info("Get the project ID({})", projectId); + } + } + } + return projectId; + } + + public static void setBuildId(String buildId) { + System.setProperty(BUILD_ID, buildId); + } + + @SneakyThrows + public static String getBuildId() { + if (buildId == null) { + synchronized (DockerEnv.class) { + if (buildId == null) { + buildId = getProperty(BUILD_ID); + if (buildId == null || buildId.isEmpty()) { + throw new Exception(BUILD_ID+ "Empty build Id"); +// throw new PropertyNotExistException(BUILD_ID, "Empty build Id"); + } + logger.info("Get the build ID({})", buildId); + } + } + } + return buildId; + } + + public static String getAgentId() { + if (agentId == null) { + synchronized (DockerEnv.class) { + if (agentId == null) { + agentId = getProperty(AGENT_ID); + if (agentId == null || agentId.isEmpty()) { + throw new PropertyNotExistException(AGENT_ID, "Empty agent Id"); + } + logger.info("Get the agent ID({})", agentId); + } + } + } + return agentId; + } + + public static void setAgentSecretKey(String secretKey) { + System.setProperty(AGENT_SECRET_KEY, secretKey); + } + + public static String getAgentSecretKey() { + if (secretKey == null) { + synchronized (DockerEnv.class) { + if (secretKey == null) { + secretKey = getProperty(AGENT_SECRET_KEY); + if (secretKey == null || secretKey.isEmpty()) { + throw new PropertyNotExistException(AGENT_SECRET_KEY, "Empty agent secret key"); + } + logger.info("Get the agent secret key({})", secretKey); + } + } + } + return secretKey; + } + + public static String getGateway() { + if (gateway == null) { + synchronized (DockerEnv.class) { + if (gateway == null) { + gateway = getProperty(AGENT_GATEWAY); + if (gateway == null || gateway.isEmpty()) { + throw new PropertyNotExistException(AGENT_GATEWAY, "Empty agent gateway"); + } + logger.info("Get the gateway({})", gateway); + } + } + } + return gateway; + } + + public static String getJobPool() { + if (jobPool == null) { + synchronized (DockerEnv.class) { + if (jobPool == null) { + jobPool = getProperty(JOB_POOL); + logger.info("Get the jobPool({})", jobPool); + } + } + } + return jobPool; + } + + public static String getDockerHostIp() { + if (dockerHostIp == null) { + synchronized (DockerEnv.class) { + if (dockerHostIp == null) { + dockerHostIp = getProperty(DOCKER_HOST_IP); + if (dockerHostIp == null || dockerHostIp.isEmpty()) { + throw new PropertyNotExistException(DOCKER_HOST_IP, "Empty dockerHostIp"); + } + logger.info("Get the dockerHostIp({})", dockerHostIp); + } + } + } + return dockerHostIp; + } + + public static String getDockerHostPort() { + if (dockerHostPort == null) { + synchronized (DockerEnv.class) { + if (dockerHostPort == null) { + dockerHostPort = getProperty(DOCKER_HOST_PORT); + if (dockerHostPort == null || dockerHostPort.isEmpty()) { + throw new PropertyNotExistException(DOCKER_HOST_PORT, "Empty dockerHostPort"); + } + logger.info("Get the dockerHostPort({})", dockerHostPort); + } + } + } + return dockerHostPort; + } + + public static String getHostname() { + if (hostname == null) { + synchronized (DockerEnv.class) { + if (hostname == null) { + hostname = getProperty(HOSTNAME); + if (hostname == null || hostname.isEmpty()) { + throw new PropertyNotExistException(HOSTNAME, "Empty hostname"); + } + logger.info("Get the hostname({})", hostname); + } + } + } + return hostname; + } + + private static String getProperty(String prop) { + String value = System.getenv(prop); + if (value == null || value.isEmpty()) { + value = System.getProperty(prop); + } + return value; + } +} \ No newline at end of file diff --git a/modules/ci-worker/src/main/java/cd/casic/devops/common/worker/exception/TaskExecuteExceptionDecorator.kt b/modules/ci-worker/src/main/java/cd/casic/devops/common/worker/exception/TaskExecuteExceptionDecorator.kt new file mode 100644 index 0000000..172de70 --- /dev/null +++ b/modules/ci-worker/src/main/java/cd/casic/devops/common/worker/exception/TaskExecuteExceptionDecorator.kt @@ -0,0 +1,154 @@ +//package com.tencent.devops.worker.common.exception +// +//import com.fasterxml.jackson.databind.exc.InvalidFormatException +//import com.fasterxml.jackson.databind.exc.MismatchedInputException +//import com.tencent.devops.common.api.exception.RemoteServiceException +//import com.tencent.devops.common.api.exception.TaskExecuteException +//import com.tencent.devops.common.api.pojo.ErrorCode +//import com.tencent.devops.common.api.pojo.ErrorType +//import com.tencent.devops.worker.common.exception.TaskExecuteExceptionDecorator.LOGGER +//import org.slf4j.Logger +//import org.slf4j.LoggerFactory +//import java.io.FileNotFoundException +//import java.io.IOException +// +///** +// * 错误处理 +// */ +//object TaskExecuteExceptionDecorator { +// +// val LOGGER: Logger = LoggerFactory.getLogger(TaskExecuteExceptionDecorator::class.java) +// +// private val defaultExceptionBase = DefaultExceptionBase() +// +// private val factory = mutableMapOf( +// IllegalStateException::class to IllegalStateExceptionD(), +// InvalidFormatException::class to InvalidFormatExceptionD(), +// FileNotFoundException::class to FileNotFoundExceptionD(), +// RemoteServiceException::class to RemoteServiceExceptionD(), +// MismatchedInputException::class to MismatchedInputExceptionD(), +// IOException::class to IOExceptionD(), +// FileSystemException::class to FileSystemExceptionD() +// ) +// +// @Suppress("UNCHECKED_CAST") +// fun decorate(throwable: Throwable): TaskExecuteException { +// var exception = throwable +// return (factory[exception::class] as ExceptionDecorator? ?: exception.cause?.let { cause -> +// (factory[cause::class] as ExceptionDecorator?)?.let { +// exception = cause +// it +// } +// } ?: defaultExceptionBase).decorate(exception) +// } +//} +// +//class DefaultExceptionBase : ExceptionDecorator { +// override fun decorate(exception: Throwable): TaskExecuteException { +// return when { +// exception is TaskExecuteException -> exception +// // TEE只有一层,所以不遍历cause,防止InputMismatchException 无限循环。 +// exception.cause is TaskExecuteException -> exception.cause as TaskExecuteException +// else -> { +// LOGGER.warn("[Worker Error]: ", exception) +// val defaultMessage = StringBuilder("Unknown system error has occurred with StackTrace:\n") +// defaultMessage.append(exception.toString()) +// exception.stackTrace.forEach { +// with(it) { +// defaultMessage.append( +// "\n at $className.$methodName($fileName:$lineNumber)" +// ) +// } +// } +// TaskExecuteException( +// errorMsg = exception.message ?: defaultMessage.toString(), +// errorType = ErrorType.SYSTEM, +// errorCode = ErrorCode.SYSTEM_WORKER_LOADING_ERROR, +// cause = exception +// ) +// } +// } +// } +//} +// +//interface ExceptionDecorator { +// fun decorate(exception: T): TaskExecuteException +//} +// +//class IllegalStateExceptionD : ExceptionDecorator { +// override fun decorate(exception: IllegalStateException): TaskExecuteException { +// return TaskExecuteException( +// errorMsg = "Machine process error: ${exception.message}", +// errorType = ErrorType.USER, +// errorCode = ErrorCode.THIRD_PARTY_BUILD_ENV_ERROR, +// cause = exception +// ) +// } +//} +// +//class FileNotFoundExceptionD : ExceptionDecorator { +// override fun decorate(exception: FileNotFoundException): TaskExecuteException { +// return TaskExecuteException( +// errorMsg = "Machine file not found error: ${exception.message}", +// errorType = ErrorType.USER, +// errorCode = ErrorCode.USER_RESOURCE_NOT_FOUND, +// cause = exception +// ) +// } +//} +// +//class RemoteServiceExceptionD : ExceptionDecorator { +// +// override fun decorate(exception: RemoteServiceException): TaskExecuteException { +// return TaskExecuteException( +// errorMsg = "THIRD PARTY response error: ${exception.message}", +// errorType = ErrorType.THIRD_PARTY, +// errorCode = ErrorCode.THIRD_PARTY_INTERFACE_ERROR, +// cause = exception +// ) +// } +//} +// +//class MismatchedInputExceptionD : ExceptionDecorator { +// override fun decorate(exception: MismatchedInputException): TaskExecuteException { +// return TaskExecuteException( +// errorMsg = "Plugin data is illegal: ${exception.message}", +// errorType = ErrorType.PLUGIN, +// errorCode = ErrorCode.USER_TASK_OPERATE_FAIL, +// cause = exception +// ) +// } +//} +// +//class InvalidFormatExceptionD : ExceptionDecorator { +// override fun decorate(exception: InvalidFormatException): TaskExecuteException { +// return TaskExecuteException( +// errorMsg = "Plugin data is illegal: ${exception.message}", +// errorType = ErrorType.PLUGIN, +// errorCode = ErrorCode.USER_TASK_OPERATE_FAIL, +// cause = exception +// ) +// } +//} +// +//class IOExceptionD : ExceptionDecorator { +// override fun decorate(exception: IOException): TaskExecuteException { +// return TaskExecuteException( +// errorMsg = "IO Exception: ${exception.message}", +// errorType = ErrorType.USER, +// errorCode = ErrorCode.USER_RESOURCE_NOT_FOUND, +// cause = exception +// ) +// } +//} +// +//class FileSystemExceptionD : ExceptionDecorator { +// override fun decorate(exception: FileSystemException): TaskExecuteException { +// return TaskExecuteException( +// errorMsg = "FileSystem Exception: ${exception.message}", +// errorType = ErrorType.USER, +// errorCode = ErrorCode.USER_RESOURCE_NOT_FOUND, +// cause = exception +// ) +// } +//} diff --git a/modules/ci-worker/src/main/kotlin/cd.casic.devops.common.worker.exception/ApiNotExistException.kt b/modules/ci-worker/src/main/kotlin/cd.casic.devops.common.worker.exception/ApiNotExistException.kt new file mode 100644 index 0000000..4f0f2c2 --- /dev/null +++ b/modules/ci-worker/src/main/kotlin/cd.casic.devops.common.worker.exception/ApiNotExistException.kt @@ -0,0 +1,30 @@ +/* + * Tencent is pleased to support the open source community by making BK-CI 蓝鲸持续集成平台 available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * BK-CI 蓝鲸持续集成平台 is licensed under the MIT license. + * + * A copy of the MIT License is included in this file. + * + * + * Terms of the MIT License: + * --------------------------------------------------- + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated + * documentation files (the "Software"), to deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or substantial portions of + * the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT + * LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN + * NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +package com.tencent.devops.worker.common.exception + +class ApiNotExistException(message: String) : RuntimeException(message) diff --git a/modules/ci-worker/src/main/kotlin/cd.casic.devops.common.worker.exception/PropertyNotExistException.kt b/modules/ci-worker/src/main/kotlin/cd.casic.devops.common.worker.exception/PropertyNotExistException.kt new file mode 100644 index 0000000..0683b4f --- /dev/null +++ b/modules/ci-worker/src/main/kotlin/cd.casic.devops.common.worker.exception/PropertyNotExistException.kt @@ -0,0 +1,30 @@ +/* + * Tencent is pleased to support the open source community by making BK-CI 蓝鲸持续集成平台 available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * BK-CI 蓝鲸持续集成平台 is licensed under the MIT license. + * + * A copy of the MIT License is included in this file. + * + * + * Terms of the MIT License: + * --------------------------------------------------- + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated + * documentation files (the "Software"), to deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or substantial portions of + * the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT + * LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN + * NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +package com.tencent.devops.worker.common.exception + +class PropertyNotExistException(val key: String, message: String) : RuntimeException(message) diff --git a/modules/ci-worker/src/main/kotlin/cd.casic.devops.common.worker.exception/TaskExecuteExceptionDecorator.kt b/modules/ci-worker/src/main/kotlin/cd.casic.devops.common.worker.exception/TaskExecuteExceptionDecorator.kt new file mode 100644 index 0000000..172de70 --- /dev/null +++ b/modules/ci-worker/src/main/kotlin/cd.casic.devops.common.worker.exception/TaskExecuteExceptionDecorator.kt @@ -0,0 +1,154 @@ +//package com.tencent.devops.worker.common.exception +// +//import com.fasterxml.jackson.databind.exc.InvalidFormatException +//import com.fasterxml.jackson.databind.exc.MismatchedInputException +//import com.tencent.devops.common.api.exception.RemoteServiceException +//import com.tencent.devops.common.api.exception.TaskExecuteException +//import com.tencent.devops.common.api.pojo.ErrorCode +//import com.tencent.devops.common.api.pojo.ErrorType +//import com.tencent.devops.worker.common.exception.TaskExecuteExceptionDecorator.LOGGER +//import org.slf4j.Logger +//import org.slf4j.LoggerFactory +//import java.io.FileNotFoundException +//import java.io.IOException +// +///** +// * 错误处理 +// */ +//object TaskExecuteExceptionDecorator { +// +// val LOGGER: Logger = LoggerFactory.getLogger(TaskExecuteExceptionDecorator::class.java) +// +// private val defaultExceptionBase = DefaultExceptionBase() +// +// private val factory = mutableMapOf( +// IllegalStateException::class to IllegalStateExceptionD(), +// InvalidFormatException::class to InvalidFormatExceptionD(), +// FileNotFoundException::class to FileNotFoundExceptionD(), +// RemoteServiceException::class to RemoteServiceExceptionD(), +// MismatchedInputException::class to MismatchedInputExceptionD(), +// IOException::class to IOExceptionD(), +// FileSystemException::class to FileSystemExceptionD() +// ) +// +// @Suppress("UNCHECKED_CAST") +// fun decorate(throwable: Throwable): TaskExecuteException { +// var exception = throwable +// return (factory[exception::class] as ExceptionDecorator? ?: exception.cause?.let { cause -> +// (factory[cause::class] as ExceptionDecorator?)?.let { +// exception = cause +// it +// } +// } ?: defaultExceptionBase).decorate(exception) +// } +//} +// +//class DefaultExceptionBase : ExceptionDecorator { +// override fun decorate(exception: Throwable): TaskExecuteException { +// return when { +// exception is TaskExecuteException -> exception +// // TEE只有一层,所以不遍历cause,防止InputMismatchException 无限循环。 +// exception.cause is TaskExecuteException -> exception.cause as TaskExecuteException +// else -> { +// LOGGER.warn("[Worker Error]: ", exception) +// val defaultMessage = StringBuilder("Unknown system error has occurred with StackTrace:\n") +// defaultMessage.append(exception.toString()) +// exception.stackTrace.forEach { +// with(it) { +// defaultMessage.append( +// "\n at $className.$methodName($fileName:$lineNumber)" +// ) +// } +// } +// TaskExecuteException( +// errorMsg = exception.message ?: defaultMessage.toString(), +// errorType = ErrorType.SYSTEM, +// errorCode = ErrorCode.SYSTEM_WORKER_LOADING_ERROR, +// cause = exception +// ) +// } +// } +// } +//} +// +//interface ExceptionDecorator { +// fun decorate(exception: T): TaskExecuteException +//} +// +//class IllegalStateExceptionD : ExceptionDecorator { +// override fun decorate(exception: IllegalStateException): TaskExecuteException { +// return TaskExecuteException( +// errorMsg = "Machine process error: ${exception.message}", +// errorType = ErrorType.USER, +// errorCode = ErrorCode.THIRD_PARTY_BUILD_ENV_ERROR, +// cause = exception +// ) +// } +//} +// +//class FileNotFoundExceptionD : ExceptionDecorator { +// override fun decorate(exception: FileNotFoundException): TaskExecuteException { +// return TaskExecuteException( +// errorMsg = "Machine file not found error: ${exception.message}", +// errorType = ErrorType.USER, +// errorCode = ErrorCode.USER_RESOURCE_NOT_FOUND, +// cause = exception +// ) +// } +//} +// +//class RemoteServiceExceptionD : ExceptionDecorator { +// +// override fun decorate(exception: RemoteServiceException): TaskExecuteException { +// return TaskExecuteException( +// errorMsg = "THIRD PARTY response error: ${exception.message}", +// errorType = ErrorType.THIRD_PARTY, +// errorCode = ErrorCode.THIRD_PARTY_INTERFACE_ERROR, +// cause = exception +// ) +// } +//} +// +//class MismatchedInputExceptionD : ExceptionDecorator { +// override fun decorate(exception: MismatchedInputException): TaskExecuteException { +// return TaskExecuteException( +// errorMsg = "Plugin data is illegal: ${exception.message}", +// errorType = ErrorType.PLUGIN, +// errorCode = ErrorCode.USER_TASK_OPERATE_FAIL, +// cause = exception +// ) +// } +//} +// +//class InvalidFormatExceptionD : ExceptionDecorator { +// override fun decorate(exception: InvalidFormatException): TaskExecuteException { +// return TaskExecuteException( +// errorMsg = "Plugin data is illegal: ${exception.message}", +// errorType = ErrorType.PLUGIN, +// errorCode = ErrorCode.USER_TASK_OPERATE_FAIL, +// cause = exception +// ) +// } +//} +// +//class IOExceptionD : ExceptionDecorator { +// override fun decorate(exception: IOException): TaskExecuteException { +// return TaskExecuteException( +// errorMsg = "IO Exception: ${exception.message}", +// errorType = ErrorType.USER, +// errorCode = ErrorCode.USER_RESOURCE_NOT_FOUND, +// cause = exception +// ) +// } +//} +// +//class FileSystemExceptionD : ExceptionDecorator { +// override fun decorate(exception: FileSystemException): TaskExecuteException { +// return TaskExecuteException( +// errorMsg = "FileSystem Exception: ${exception.message}", +// errorType = ErrorType.USER, +// errorCode = ErrorCode.USER_RESOURCE_NOT_FOUND, +// cause = exception +// ) +// } +//} diff --git a/modules/ci-worker/src/main/resources/logback.xml b/modules/ci-worker/src/main/resources/logback.xml new file mode 100644 index 0000000..3a4b747 --- /dev/null +++ b/modules/ci-worker/src/main/resources/logback.xml @@ -0,0 +1,71 @@ + + + + + + logback + + + + ${LOG_PATH:-.}/logs/${AGENT_LOG_PREFIX:-agent}.log + + ${LOG_PATH:-.}/logs/${AGENT_LOG_PREFIX:-agent}-%d{yyyy-MM-dd}.%i.log + 100MB + 14 + 10GB + + + ${pattern} + UTF-8 + + + + + + ${LOG_PATH:-.}/logs/runtime.log + + ${LOG_PATH:-.}/logs/runtime-%d{yyyy-MM-dd}.%i.log + 100MB + 30 + 10GB + + + ${pattern} + UTF-8 + + + + + + + + + + + diff --git a/modules/module-system-biz/src/main/java/cd/casic/module/system/controller/admin/auth/http-client.env.json b/modules/module-system-biz/src/main/java/cd/casic/module/system/controller/admin/auth/http-client.env.json new file mode 100644 index 0000000..f24fcb5 --- /dev/null +++ b/modules/module-system-biz/src/main/java/cd/casic/module/system/controller/admin/auth/http-client.env.json @@ -0,0 +1,6 @@ +{ + "dev": { + "baseUrl": "127.0.0.1:48080", + "adminTenentId": "1" + } +} \ No newline at end of file