4.3_流水线任务扩展参考

一、数据库脚本定义实现

    注意:不要和系统提供的主键冲突。

DPS_ENGINE_STAGE_TEMPLATE表: 流水线任务模板定义表,可以定义任务名称,任务类型,任务图标等。注意:任务名称要保证唯一,不能冲突。

create table DPS_ENGINE_STAGE_TEMPLATE
(
   STAGE_TP_ID varchar(64) not null comment '唯一标识',
   STAGE_TP_NAME varchar(64) comment '任务名称',
   STAGE_TP_LABEL varchar(64) comment '任务标签名称',
   STAGE_TP_TYPE varchar(64) comment '任务类型',
   STAGE_TP_TYPE_LABEL varchar(64) comment '任务类型标签名称',
   STAGE_TAGS varchar(512) comment '任务标签',
   STAGE_TP_ICON varchar(2048) comment '任务ICON路径',
   STAGE_HANDLER text comment '任务拦截器类名,实现接口com.primeton.devops.specs.api.engine.service.IStageHandler',
   ESTIMATED_DURATION integer comment '预估持续时间',
   DESCRIPTION text comment '任务描述',
   COMMON_STAGE_TPS varchar(1024) comment '公共任务模板列表,逗号分隔',
   primary key (STAGE_TP_ID)
) comment='任务模板表';

DPS_ENGINE_STAGE_ATTRIBUTE_DEFINITION表: 流水线任务属性定义表,定义流水线任务有哪些属性。其中disable(禁用)和failedContinue(失败后继续运行)是常规高级属性,必须要有。

create table DPS_ENGINE_STAGE_ATTRIBUTE_DEFINITION
(
  ATTR_DEF_ID          varchar(64) not null comment '唯一标识',
  ATTR_DEF_NAME        varchar(64) comment '任务属性定义名称',
  ATTR_DEF_LABEL       varchar(64) comment '任务属性定义标签名称',
  STAGE_TP_ID          varchar(64) comment '任务模板ID',
  SOURCE               char default '0' comment '任务属性来源类型,是否是用户自定义, 默认是0,目前没用',
  SORT                 int comment '任务属性顺序',
  CATEGORY             varchar(64) comment '任务属性类别',
  IS_REQUIRED          boolean comment '是否必须',
  IS_INHERITABLE       boolean comment '是否继承,目前没用',
  IS_ENCRYPTED         boolean comment '是否加密,目前没用',
  IS_IMMUTABLE         boolean comment '是否不可改变,目前没用',
  DISPLAY_FORMAT       text comment '显示格式,目前没用',
  TIP                  text comment '提示信息',
  DEFAULT_VALUE        text comment '默认值',
  FIELD_TYPE           varchar(64) comment '字段类型,均设置为string',
  CONTROL_TYPE         varchar(64) comment '控件类型',
  VALUE_PROVIDER       text comment '值提供者',
  OPTIONS              text comment '控件其它相关信息',
  CHECK_POLICY         varchar(64) comment '校验策略,目前没用',
  primary key (ATTR_DEF_ID)
) comment='任务属性定义表';

示例如下:

/* 流水线任务定义 : 脚本执行 */
NSERT INTO DPS_ENGINE_STAGE_TEMPLATE (STAGE_TP_ID, STAGE_TP_NAME, STAGE_TP_LABEL, STAGE_TP_TYPE, STAGE_TP_TYPE_LABEL, STAGE_HANDLER, ESTIMATED_DURATION, DESCRIPTION, STAGE_TAGS, STAGE_TP_ICON, COMMON_STAGE_TPS)
   VALUES ('1000', 'script-exec', '脚本执行', 'tool', '工具', NULL, 1000, '运行脚本', 'build,deploy', '/static/images/component_logo/script-exec.svg', 'all-common');

/* 流水线任务属性定义: 脚本执行 */
INSERT INTO DPS_ENGINE_STAGE_ATTRIBUTE_DEFINITION (ATTR_DEF_ID, ATTR_DEF_NAME, ATTR_DEF_LABEL, STAGE_TP_ID, SORT, CATEGORY, IS_REQUIRED, TIP, DEFAULT_VALUE, CONTROL_TYPE, VALUE_PROVIDER, OPTIONS, CHECK_POLICY)
   VALUES ('10000000', 'scriptType', '脚本类型', '1000', 1, '1:基本信息', 1, NULL, 'shell', 'dict', '{"type":"dictcombobox","dictTypeId":"DPS_CD_PIPELINE_SCRIPT_TYPE","eventName":"scriptTypeChange"}', NULL, NULL),
          ('10000001', 'scriptUrl', '脚本URL', '1000', 2, '1:基本信息', NULL, '远程或者本地的脚本文件', NULL, 'large-select', '{"url":"api/ci/buildartifacts?projectId=:projectId","textField":"artifactUrl","valueField":"artifactUrl","value":"","multiSelect":false,"filters":[{"label":"名称","field":"artifactName"}],"tableColumnFields":[{"label":"名称","field":"artifactName"},{"label":"URL","field":"artifactUrl"}],"valueDetailSearchField":"artifactUrl"}', NULL, NULL),
         ('10000002', 'scriptContent', '脚本内容', '1000', 3, '1:基本信息', NULL, NULL, 'echo "hello"', 'editor', '{"type":"bat"}', NULL, NULL),
          ('10000010', 'scriptParams', '脚本参数', '1000', 0, '2:高级', NULL, 'Map格式的Json串,替换脚本中同名的变量,示例:{"name1": "value1"}', '{ }', 'editor', '{"type":"json"}', NULL, NULL),
          ('10000100', 'resources', '目标选择', '1000', 1, '2:高级', NULL, '脚本执行所在的机器,如果为空,默认是Jenkins本地', NULL, 'large-select', '{"url":"api/cd/project-resources?projectId=:projectId&envType=:envType&resourceType=host","textField":"resourceName,hostAddr","valueField":"resourceId","value":"","multiSelect":true,"filters":[{"label":"名称","field":"resourceName"}],"tableColumnFields":[{"label":"名称","field":"resourceName"},{"label":"IP地址","field":"hostAddr"}],"valueDetailSearchField":"resourceId"}', NULL, NULL),
          ('10000101', 'userDir', '目标用户目录', '1000', 2, '2:高级', NULL, '用户的权限目录', '/opt/idc/apps', 'textbox', NULL, NULL, NULL),
          ('10000104', 'evalAttrs', '计算属性值', '1000', 5, '2:高级', NULL, '是否使用变量计算属性值', 'true', 'checkbox', NULL, NULL, NULL);

VALUE_PROVIDER字段规则如下:

1、JSON字符串
2、dict控件类型
 示例:{"type":"dictcombobox","dictTypeId":"DPS_CD_PIPELINE_DATABASE_TYPE"}  
3、combox控件类型
 1)示例:url中可以使用:varName进行变量引用拼串,现在默认的变量有projectId,envType,当前控件名字,以及valueFiled对应的名字等
   {"url":"api/spm/components?projectId=:projectId&componentCategory=data","textField":"componentName","valueField":"componentName","value":"", "multiSelect":false, "dataField":"data"}
 2)联动示例:childName是联动的控件的名字
   {"url":"api/spm/components?projectId=:projectId&componentCategory=data","textField":"componentName","valueField":"componentName","value":"", "multiSelect":false, "dataField":"data", "childName":"artifactVersion", "childUrl":"api/ci/buildartifacts/:componentName?projectId=:projectId"}
4、editor控件类型
5、larget-select控件类型
示例:{"url":"api/cd/project-resources?projectId=:projectId&envType=:envType&resourceType=host","textField":"resourceName,hostAddr","valueField":"resourceId","value":"","multiSelect":true,"filters":[{"label":"名称","field":"resourceName"}],"tableColumnFields":[{"label":"名称","field":"resourceName"},{"label":"IP地址","field":"hostAddr"}],"valueDetailSearchField":"resourceId"}

二、groovy执行脚本实现

    groovy执行脚本存放于构件包的META-INF/pipeline/stages目录下

    脚本要放在任务类型名称命名的目录下,脚本名称要和任务模板名称相同,脚本中的方法名要和脚本名称类似,要把中杠变成下划线。注意:方法名不能冲突。 

    如果脚本名称和系统的重名,则会覆盖系统实现。

    可以直接使用jenkins提供的groovy方法,也可以直接使用系统提供的common.groovy脚本中的方法。

    可以使用//import_files("stages/tool/methods/upload.groovy")引用其他groovy脚本中的方法,要从stages目录开始。

示例如下:tool/script-exec.groovy脚本

def script_exec(pipelineContext, pipelineResult, stageAttrs, stageResult)> {
   if (isNullOrBlank(stageAttrs.scriptContent) 
       && isNullOrBlank(stageAttrs.scriptUrl)) {
       return
   }
   if (isNullOrBlank(stageAttrs.scriptType)) {
       error "Script Type is null!"
   }
   try {
       this."script_exec_${stageAttrs.scriptType}"(pipelineContext, pipelineResult, stageAttrs, stageResult)
   } catch (e) {
       if (e instanceof MissingMethodException) {
           echo "$e"
           error "Not support script ${stageAttrs.scriptType}!"
       } else {
           throw e
       }
   }
}

def getScriptParams(stageAttrs) {
    if (isNullOrBlank(stageAttrs.scriptParams)) {
        return [:]
    }
    return parseJsonString(stageAttrs.scriptParams)
}

def getScriptContent(stageAttrs) {
   def scriptContent = ""
   if (!isNullOrBlank(stageAttrs.scriptContent)) {
       scriptContent = "${stageAttrs.scriptContent}"
   } else if (!isNullOrBlank(stageAttrs.scriptUrl)) {
       scriptContent = readUrl(stageAttrs.scriptUrl, "UTF-8")
   }
   if (!scriptContent.contains('${')) {
       return scriptContent
   }
   if (isNullOrBlank(stageAttrs.scriptParams)) {
        return scriptContent
   }
   def scriptParams = parseJsonString(stageAttrs.scriptParams)
   return replaceVars(scriptContent, scriptParams, true)
}

def script_exec_groovy(pipelineContext, pipelineResult, stageAttrs, stageResult) {
   def scriptContent = getScriptContent(stageAttrs)
   if (isNullOrBlank(scriptContent)) {
       return
   }
   def varMap = [:]
   varMap.pipelineContext = pipelineContext
   varMap.pipelineResult = pipelineResult
   varMap.stageAttrs = stageAttrs
   varMap.stageResult = stageResult
   evaluate(varMap, scriptContent)
}

def script_exec_ansible(pipelineContext, pipelineResult, stageAttrs, stageResult) {
   def scriptContent = getScriptContent(stageAttrs)
   if (isNullOrBlank(scriptContent)) {
       return
   }
   def tempAnsible = "__tempAnsible.${stageAttrs.stageIndex}.yml"
   try {
       writeFile file: tempAnsible, text: "${scriptContent}", encoding: 'UTF-8'
       if (isNullOrBlank(stageAttrs.targetIps)) {
           executeAnsibleScript pipelineContext: pipelineContext, stageAttrs: stageAttrs, playbook: tempAnsible
       } else {
           if (!stageAttrs.userDir.endsWith("/")) {
               stageAttrs.userDir = "${stageAttrs.userDir}/"
           }
           executeAnsibleScript pipelineContext: pipelineContext, stageAttrs: stageAttrs, playbook: tempAnsible, 
           extras: """\
               --extra-vars \"install_host=$stageAttrs.targetIps user_dir=$stageAttrs.userDir \"\
               """
       }
   } finally {
       deletePath(tempAnsible)
   }
}

def script_exec_shell(pipelineContext, pipelineResult, stageAttrs, stageResult) {
   def scriptContent = getScriptContent(stageAttrs)
   if (isNullOrBlank(scriptContent)) {
       return
   }
   if (isNullOrBlank(stageAttrs.targetIps)) {
       sh scriptContent
       return
   }
   execRemoteShell(pipelineContext, stageAttrs, scriptContent, stageAttrs.userDir)
}

//import_files("ansible/tool/exec-shell.yml")
def execRemoteShell(pipelineContext, stageAttrs, scriptContent, userDir) {
   try {
       if (!userDir.endsWith("/")) {
           userDir = "${userDir}/"
       }
       writeFile file: "tool/files/__tempScript.${stageAttrs.stageIndex}.sh", text: "${scriptContent}", encoding: 'UTF-8'
       executeAnsibleScript pipelineContext: pipelineContext, stageAttrs: stageAttrs, playbook: 'tool/exec-shell.yml',  
       extras: """\
           --extra-vars \"install_host=$stageAttrs.targetIps shell_user_dir=$userDir stage_index=${stageAttrs.stageIndex} \"\
           """
   } finally {
       deletePath("tool/files/__tempScript.${stageAttrs.stageIndex}.sh")
   }
}

def script_exec_bat(pipelineContext, pipelineResult, stageAttrs, stageResult) {
   def scriptContent = getScriptContent(stageAttrs)
   if (isNullOrBlank(scriptContent)) {
       return
   }
   bat scriptContent
}

def script_exec_exe(pipelineContext, pipelineResult, stageAttrs, stageResult) {
   if (isNullOrBlank(stageAttrs.scriptUrl)) {
       return
   }
   def workspaceDir = getContext hudson.FilePath
   def scriptFileObj = null
   if (stageAttrs.scriptUrl.contains(":")) {
       def scriptUrlArray = stageAttrs.scriptUrl.split("/")
       def scriptFile = scriptUrlArray[scriptUrlArray.length - 1]
       executeScript("curl -O ${stageAttrs.scriptUrl}")
       scriptFileObj = workspaceDir.child("${scriptFile}")
       if (!scriptFileObj.exists()) {
           error "can not download ${stageAttrs.scriptUrl} !"
       }
   } else {
       scriptFileObj = workspaceDir.child(stageAttrs.scriptUrl)
       if (!scriptFileObj.exists()) {
           error "${stageAttrs.scriptUrl} not existed!"
       }
       if (scriptFileObj.isDirectory()) {
           error "${stageAttrs.scriptUrl} is Directory!"
       }
   }
   bat "${scriptFileObj}"
}

results matching ""

    No results matching ""