JenkinsShareLibrary实践之自定义通知器

目录

  • 1、需求引入
  • 2、钉钉机器人
  • 3、jenkins消息推送插件
  • 4、自定义通知器的实现
    • 4.3.1、消息来源
    • 4.3.2、消息模板定义
    • 4.3.3、消息发送方法
    • 4.3.4、最终方法
    • 4.1、内容定义
    • 4.2、共享库创建
    • 4.3、方法的具体实现
    • 4.4、方法调用
    • 4.5、最终效果
  • 5、总结


与任何编程环境一样,在Jenkins流水线中,集中化功能,共享公共代码和代码重用都是快速、有效地进行开发的基本技术,这些实践鼓励使用标准方法来调用功能,为更复杂的操作创建构建块并隐藏复杂性。他们还可以用于提供一致性以及鼓励约定优于配置以简化任务。

Jenkins允许用户完成所有这些操作的一个关键方法就是使用流水线共享库(pipeline share library)。共享流水线库是由存储在代码仓库中的代码组成的,该代码仓库由Jenkins自动下载并可供流水线使用。

以上中文描述来自《Jenkins 2权威指南》。
关于jenkins pipeline share library的更多介绍,可以参考官方文档

1、需求引入

随着devops理念在公司越来越多的实践,jenkins等工具的应用场景越来越多,当我们在执行完成某个流水线任务后,常常需要关注的是这个任务为什么执行,执行成功与否等等。于是就需要在执行完流水线后进行一定程度的消息推送,在现今的工作流中消息推送无外乎分为两大类:邮件和企业沟通协作软件,相比之下,我们可能更多的会去关注和使用沟通软件来发送消息而不是通过邮件的方式。而常用的企业沟通协作软件有以下几类:腾讯系的企业微信、阿里系的钉钉、字节跳动的飞书等等,当然有能力的企业也会自己研发这类软件。

本文示例以钉钉为例,通过流水线共享库实现自定义消息通知器。

2、钉钉机器人

钉钉的群机器人是钉钉群的高级扩展功能。群机器人可以将第三方服务的信息聚合到群聊中,实现自动化的信息同步。例如:通过聚合GitHub,GitLab等源码管理服务,实现源码更新同步;通过聚合Trello,JIRA等项目协调服务,实现项目信息同步。不仅如此,群机器人支持Webhook协议的自定义接入,支持更多可能性。

自定义钉钉机器人支持以下类型消息类型数据格式的推送,更多定义方法可参考官方的接口文档:

  • text类型
  • markdown类型
  • 整体跳转ActionCard类型
  • 独立跳转ActionCard类型
  • FeedCard类型

钉钉机器人在2019年的下半年进行过升级,在新增机器人时,需要选择一种安全条件(自定义关键词、加签、ip地址或ip地址段)来保障自定义机器人的安全。可以理解为即使机器人的token泄漏,如果不知道设置的安全条件是什么,还是无法盗用的。

3、jenkins消息推送插件

这里要提到的是在jenkins插件列表中有一个钉钉插件。

简单对此插件做了下分析:截止目前此插件在2020年1月份有相应代码提交,并且发布了2.0版本,从jenkins的插件官网中可以看到此版本的插件在在消息中支持了更多内容,效果如下,但是此插件目前还暂不支持流水线中使用

在此之前的上一版本提交记录已经是2018年了,此插件使用方法类似,推送的消息效果如下

此版本支持在流水线中使用,相应内容如下

dingTalk accessToken: "xxx", 
imageUrl: "xxx", 
jenkinsUrl: "https://127.0.0.1:8080", 
message: "项目构建成功", 
notifyPeople: "155xxxx5533"

如上所示,在流水线脚本中配置钉钉机器人token、图片路径、jenkins地址、消息内容、要提醒的人手机号码即可,可以发现,此消息还是有局限性,不够友好。

因此在没有编写插件能力的情况下,我们可以通过更为灵活的自定义流水线共享库的形式,并且按照钉钉机器人的官方接口文档,自定义一个消息推送通知器。

4、自定义通知器的实现

4.1、内容定义

无论jenkins任务的构建触发原因是使用者手动构建或通过代码推送的自动触发,往往关注此消息的人群是开发者们。因此通过一段时间的需求调研以及综合各方的建议,最终将消息推送的内容中包含了以下信息:

  • 应用名称
  • 构建结果
  • 当前版本
  • 构建发起
  • 持续时间
  • 构建日志
  • 更新记录(包含用户提交的短日志,用户名称,提交时间)

每次构建结果通知中包含了以上就基本完备。

4.2、共享库创建

本文不过多介绍共享库具体的创建与在pipeline流水线中的引用方法,整体来说,共享库的代码目录结构如下

(root)
+- src                     # Groovy source files
|   +- org
|       +- foo
|           +- Bar.groovy  # for org.foo.Bar class
+- vars
|   +- foo.groovy          # for global 'foo' variable
|   +- foo.txt             # help for 'foo' variable
+- resources               # resource files (external libraries only)
|   +- org
|       +- foo
|           +- bar.json    # static helper data for org.foo.Bar

官方描述:
src目录应该看起来像标准的Java源目录结构。当执行流水线时,该目录被添加到类路径下。

vars目录定义可从流水线访问的全局变量的脚本。 每个 *.groovy文件的基名应该是一个Groovy (~ Java) 标识符, 通常是camelCased。 匹配*.txt, 如果存在, 可以包含文档, 通过系统的配置标记格式化从处理 (所以可能是HTML, Markdown等,虽然txt扩展是必需的)。

这些目录中的Groovy源文件 在脚本化流水线中的CPS transformation一样。

resources目录允许从外部库中使用 libraryResource步骤来加载有关的非Groovy文件。 目前,内部库不支持该特性。

根目录下的其他目录被保留下来以便于将来的增强。

4.3、方法的具体实现

定义共享库中src/org/devops目录为共享库方法的主目录,在这个目录下创建一个名为dingmes.groovy的文件作为钉钉消息推送方法的代码文件。

构建一个消息通知器的主要思路:

  • 消息指标内容从哪来
  • 消息模板如何定义
  • 消息怎么发送,发到哪里

4.3.1、消息来源

首先,消息内容从哪来,上面提到的需要在消息中体现的每个指标的可取的获取方式

指标名称指标来源定义
应用名称定义为jenkins的任务名称,通过全局变量env.JOB_NAME获取或者在pipeline中自定义一个变量给出
构建结果在pipeline中post字段指标判断并给出
当前版本定义为jenkins的构建编号,通过全局变量env.BUILD_NUMBER或者在pipeline中自定义版本号
构建发起通过全局变量env.BUILD_USER获取
持续时间通过全局变量currentBuild.durationString获取,这个值更为友好
构建日志日志太多,给个链接即可,通过全局变量env.BUILD_URL/console获取
更新记录这个指标是指代码提交到版本库中的更新信息,而且包含提交时间,提交者名称,获取思路可以通过在检出代码后通过类似git log的命令过滤出或者根据全局变量currentBuild.changeSet获取

分析:
本文中的共享库用于jenkins+k8s自动化ci测试环境,因此某些指标的定义方法为:
应用名称自定义,用变量给出,在pipeline前文定义全局变量,在这里传入变量即可
当前版本自定义,以代码分支+commitid作为docker镜像的tag,在pipeline前文中实现或亦通过共享库实现,在这里传入变量即可
更新记录根据全局变量获取,在这里通过代码实现

较为复杂的是如何解读currentBuild.changeSet这个全局变量,通过jenkins上的全局变量列表文档查看如下


点击其中的链接查看官方文档


通过进一步查看官方文档得知,currentBuild.changeSet返回的是一个集合,这个集合中包含了提交日志,commitid,作者id,作者全称,时间戳等信息,具体对象相关属性如下

currentBuild.changeSets{
    items[{
        msg //提交注释
        commitId //提交hash值
        author{ //提交用户相关信息
            id
            fullName
        }
        timestamp
        affectedFiles[{ //受影响的文件列表
            editType{
                name
            } 
            path: "path"
        }]
        affectedPaths[// 受影响的目录,是个Collection<String>
            "path-a","path-b"
        ]
    }]
}

因此,可以通过循环遍历得出我们需要的相关属性值,通过groovy脚本定义方法并返回相应字符串,其中为了更优化,需要对提交日志做一下长度限制,对时间戳进行格式化,这两个功能需要不断调试。其中changeString变量的赋值格式定义为markdown的无序列表,最终方法如下

def getChangeString() {
    def changeString = ""
    def MAX_MSG_LEN = 20
    def changeLogSets = currentBuild.changeSets
    for (int i = 0; i < changeLogSets.size(); i++) {
        def entries = changeLogSets[i].items
        for (int j = 0; j < entries.length; j++) {
            def entry = entries[j]
            truncatedMsg = entry.msg.take(MAX_MSG_LEN)
            commitTime = new Date(entry.timestamp).format("yyyy-MM-dd HH:mm:ss")
            changeString += " - ${truncatedMsg} [${entry.author} ${commitTime}]\n"
        }
    }
    if (!changeString) {
        changeString = " - No new changes"
    }
    return (changeString)
}

4.3.2、消息模板定义

消息中的相关字段都获取到了,下一步需要做的就是定义一个消息模板,如果使用邮件发送通知,同样的也需要定义一个模板。

这里使用更为友好的markdown格式来发送通知,钉钉机器人接口接收的消息是json格式,具体内容可以通过查看官方文档,为了避免换行出错,手动指定换行符,最终的json格式数据和markdown格式模板如下

{
    "msgtype":"markdown",
    "markdown":{
        "title":"项目构建信息",
        "text":"### 构建信息\n>- 应用名称: **${AppName}**\n- 构建结果: **${Status} ${CatchInfo}**\n- 当前版本: **${ImageTag}**\n- 构建发起: **${env.BUILD_USER}**\n- 持续时间: **${currentBuild.durationString}**\n- 构建日志: [点击查看详情](${env.BUILD_URL}console)\n#### 更新记录: \n${ChangeLog}"
    },
    "at":{
        "atMobiles":[
            "155xxxx5533"
        ],
        "isAtAll":false
    }
}

4.3.3、消息发送方法

在流水线中按照消息模板渲染好的消息发送给钉钉的接口地址,可以实现的方法包括但不限于以下几种:

  • 通过执行shell命令发送,例如curl命令指定参数即可,最为简单,但不够友好
  • 通过pipeline语法和插件实现,例如使用HTTP Request插件,在Jenkins pipeline中发送HTTP请求给钉钉接口。
  • 通过调用其他脚本发送,例如python脚本,较复杂,不推荐。

综上比较,选择一种友好且不复杂的方案,即通过pipeline语法和插件实现

首先在插件安装中安装好HTTP Request插件,打开语法片段生成器查看对应语法

相应参数对应如下:
httpRequest步骤返回的response对象包含两个字段。

  • content:响应内容。
  • status:响应码。 以下是httpRequest步骤支持的参数。
  • url:字符串类型,请求URL。
  • acceptType:枚举类型,HTTP请求Header的“Accept”的值类型为NOT_SET、 TEXT_HTML、TEXT_PLAIN、APPLICATION_FORM、APPLICATION_JSON、 APPLICATION_JSON_UTF8、APPLICATION_TAR、APPLICATION_ZIP、 APPLICATION_OCTETSTREAM。
  • authentication:字符串类型,Username with password凭证的ID,采用的是HTTP Basic认证方式。
  • consoleLogResponseBody:布尔类型,是否将请求的响应body打印出来。
  • contentType:枚举类型,HTTP请求Header的“Content-type”的值类型,与acceptType 支持的枚举一样。
  • customHeaders:HttpRequestNameValuePair对象数组,HTTP请求Header部分的内 容,该对象有3个参数。 ◦ name:字符串类型,Header名称。 ◦ value:字符串类型,Header值。 ◦ maskValue:布尔类型,是否隐藏Header值。如果设置为true,则在打印时使用“*”代 替。 • httpMode:枚举类型,HTTP方法,有GET(默认)、HEAD、POST、PUT、 DELETE、OPTIONS、PATCH。
  • httpProxy:字符串类型,HTTP代理地址
  • ignoreSslErrors:布尔类型,是否忽略SSL错误。
  • requestBody:字符串类型,请求的body内容。
  • timeout:整型,超时时间,单位为秒。默认值为0,代表不设置超时时间。
  • validResponseCodes:字符串类型,代表HTTP请求成功的状态码。它支持3种格式的 值。 ◦ 单状态值:比如200,当收到200响应状态码时,表示HTTP请求成功。 ◦ 多状态值:当响应状态码符合多个状态码中的一个时,代表请求成功。多个状态码 之间使用逗号(,)分隔。比如200,404,500。 ◦ 范围状态值:格式为“From:To”。比如200:302,代表收到200到302的响应状态码 都代表请求成功。
  • validResponseContent:字符串类型,比如设置它的值为“showme.codes”,那么只有 当HTTP返回的内容中包含了“showme.codes”时,才代表请求成功。
  • quiet:布尔类型,是否关闭所有的日志打印,默认值为false。
  • responseHandle:枚举类型,获取HTTP响应内容的方式。其值可以为 ◦ NONE:不读取响应内容。 ◦ LEAVE_OPEN:当执行完请求后,并不会返回响应的内容,而是返回一个打开了的 inputStream,由你自己决定该如何读取响应内容。但是在使用完之后,记得调用inputStream的close()方法关闭。 ◦ STRING(默认值):将响应内容转换成一个字符串。
  • outputFile:字符串类型,请求响应内容的输出路径。

虽然参数有些多,但是只有url是必需的,其他参数都是可选的。这里我们传入请求内容以及url,并省去其他不必要的参数,如下

httpRequest acceptType: 'APPLICATION_JSON_UTF8', 
        consoleLogResponseBody: false, 
        contentType: 'APPLICATION_JSON_UTF8', 
        httpMode: 'POST', 
        ignoreSslErrors: true, 
        requestBody: ReqBody, 
        responseHandle: 'NONE', 
        url: "${DingTalkHook}",
        quiet: true

4.3.4、最终方法

综上所述,在调用此共享库方法时传入应用名称变量AppName、应用版本(镜像tag)变量ImageTag、构建状态变量Status、以及在pipeline前文中实现的异常信息捕捉变量CatchInfo,并结合前面实现的方法内容,最终方法dingmes.groovy内容如下

/* dingmes.groovy
   ##################################################
   # Created by SSgeek                              #
   #                                                #
   # A Part of the Project jenkins-library          #
   ##################################################
*/

package org.devops

def getChangeString() {
    def changeString = ""
    def MAX_MSG_LEN = 20
    def changeLogSets = currentBuild.changeSets
    for (int i = 0; i < changeLogSets.size(); i++) {
        def entries = changeLogSets[i].items
        for (int j = 0; j < entries.length; j++) {
            def entry = entries[j]
            truncatedMsg = entry.msg.take(MAX_MSG_LEN)
            commitTime = new Date(entry.timestamp).format("yyyy-MM-dd HH:mm:ss")
            changeString += " - ${truncatedMsg} [${entry.author} ${commitTime}]\n"
        }
    }
    if (!changeString) {
        changeString = " - No new changes"
    }
    return (changeString)
}

def HttpReq(AppName,ImageTag=' ',Status,CatchInfo=' '){
    wrap([$class: 'BuildUser']){
        def DingTalkHook = "https://oapi.dingtalk.com/robot/send?access_token=67449753547bfcb8e2ee6088fdebaf2cdc7228787201fca83406fc449ffaf92"
        def ChangeLog = getChangeString()
        def ReqBody = """{
            "msgtype": "markdown",
            "markdown": {
                "title": "项目构建信息",
                "text": "### 构建信息\n>- 应用名称: **${AppName}**\n- 构建结果: **${Status} ${CatchInfo}**\n- 当前版本: **${ImageTag}**\n- 构建发起: **${env.BUILD_USER}**\n- 持续时间: **${currentBuild.durationString}**\n- 构建日志: [点击查看详情](${env.BUILD_URL}console)\n#### 更新记录: \n${ChangeLog}"
            },
            "at": {
                "atMobiles": [
                    "155xxxx5533"
                ], 
                "isAtAll": false
                }
            }"""
        // println(currentBuild.description)
        // println(currentBuild.changeSets)
        httpRequest acceptType: 'APPLICATION_JSON_UTF8', 
                consoleLogResponseBody: false, 
                contentType: 'APPLICATION_JSON_UTF8', 
                httpMode: 'POST', 
                ignoreSslErrors: true, 
                requestBody: ReqBody, 
                responseHandle: 'NONE', 
                url: "${DingTalkHook}",
                quiet: true
    }
}

4.4、方法调用

此消息通知的方法通常在pipeline的post部分调用,如下所示

post{
    success{
        script{
            tools.PrintMes("========pipeline executed successfully========",'green')
            dingmes.HttpReq(AppName,ImageTag,"构建成功 ✅")
        }
    }
    failure{
        script{
            tools.PrintMes("========pipeline execution failed========",'red')
            dingmes.HttpReq(AppName,ImageTag,"构建失败 ❌",CatchInfo)
        }
    }
    unstable{
        script{
            tools.PrintMes("========pipeline execution unstable========",'red')
            dingmes.HttpReq(AppName,ImageTag,"构建失败 ❌","不稳定异常")
        }
    }
    aborted{
        script{
            tools.PrintMes("========pipeline execution aborted========",'blue')
            dingmes.HttpReq(AppName,ImageTag,"构建失败 ❌","暂停或中断")
        }
    }
}

4.5、最终效果

测试代码提交,执行流水线,最终的消息通知效果如下图

5、总结

至此,本文记录通过自定义jenkins pipeline流水线共享库方法,实现了较为灵活的自定义钉钉机器人消息通知。如果是使用企信等其他软件,与此实现思路相近。


原文地址:https://blog.51cto.com/u_13397153/2713497

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


基础环境、流程图1)流程图jenkinspipeline发布应用2)、jenkins上安装kubectl#主节点上查看kubectl位置[root@k8s01~]#whichkubectl/usr/bin/kubectl#将k8s节点的kubectl文件cp至jenkinsscp10.0.0.101:/usr/bin/kubectl.cp./kubectl/usr/local/bin/kubectl
本节是建立在流水线入门内容的基础上,而且,应当被当作一个参考。对于在实际示例中如何使用流水线语法的更多信息,请参阅本章在流水线插件的2.5版本中的使用Jenkinsfile部分,流水线支持两种离散的语法,具体如下对于每种的优缺点,参见语法比较。正如本章开始讨论的,流水线最
Linuxcentos7安装Jenkins(jenkins-2.227-1.1.noarch.rpm)时遇到问题:1.出现问题1:安装jenkins一直失败,提示Nosuchfileordirectory[helen@linuxprobeJenkins]$sudorpm-ivhjenkins-2.227-1.1.noarch.rpmPreparing...####################
作为一个拥有很多账号的当代美少女,忘记用户名密码什么的是再正常不过的事了。“忘记密码”这一功能给我真的带来了很大的福音啊。但是最近重新使用Jenkins时,看到需要输入用户名密码,顿时脑子一片空白。经过一系列的查找,终于解决了问题,害,只有把方法记下来才是王道,以后妈妈再
Jenkins、GitLab和Maven(Nexus)完成自动构建迈向持续集成持续集成,毋庸置疑的是这是两部分要求,一个是持续一个是集成,那么持续和集成到底表示了什么要求和含义呢?其中的集成比较容易理解就是指开发人员将自己开发的代码不断地交付给全部系统的代码,在Git仓的概念之下也就是不断地
 下载文档https://www.jenkins.io/zh/download/  Debian下载1.下载安装keyhttps://pkg.jenkins.io/debian-stable/jenkins.io.key  
流水线建设从Jenkins部署开始Jenkins是一个独立的开源自动化服务器,可以用来自动化,例如构建、测试和部署软件等各种任务,是一个实现CI(持续集成)的很好的工具。Jenkins可以通过本地系统包、Docker安装,甚至可以在安装Java运行时环境的机器上独立运行。JenkCI/CD的概念谈到持
原因:这种情况是SSHServer中的RemoteDirectory路径不存在。解决方法:重新输入目标服务器中存在的路径。      
什么是持续集成持续集成(Continuousintegration,简称CI)指的是,频繁地(一天多次)将代码集成到主干。持续集成的目的,就是让产品可以快速迭代,同时还能保持高质量。它的核心措施是,代码集成到主干之前,必须通过自动化测试。只要有一个测试用例失败,就不能集成。通过持续集成,团
Tomcat安装:1.下载Tomcat包:http:/omcat.apache.org/并解压2.启动:双击bin\startup.bat文件浏览器访问:http://localhost:8080/3.Tomcat配置登录:打开confomcat-users.xml文件添加如下代码:<rolerolename="manager-gui"/><userusername="admin"password=&q
文章目录jenkinscron笔记简介H符号(hash的简写)别名例子jenkinscron笔记简介jenkinscron遵循普通cron的语法,略有不同,每行包含五个字段可以使用Tab和空行分割分钟小时日月周0-590-231-311-120-7(0和7都表示周日)可以使用以下操作符为一个字段指定多个值(按照
1.nginx的配置location/jenkinsno{indexindex.htmlindex.htm;proxy_passhttp://127.0.0.1:8081;proxy_connect_timeout3000s;proxy_send_timeout3000s;proxy_read_timeout3000s;prox
1、下载安装插件下载地址:http://files.jetbrains.org.cn/aliyun-oss.hpi在Jenkins中安装插件,请到ManageJenkins->Advanced->Upload,上传插件(.hpi文件)安装完毕后请重新启动Jenkins2、搜索安装jenkins插件安装3、安装完成以后配置阿里云oss账号oss这边设置:(账号的权限也要
首先将构建历史清除Jenkins构建历史删除与重置然后进入Jenkins服务器工作目录oot/.jenkinstar-czvfjobs.tarjobs 将jobs打包将job.tar文件复制到新Jenkins服务器相同目录下解压tar包 tar-zxvfjob.tar登录新JenkinsJenkins-系统管理-读取配置,只有点击了读取配置,原
一、动态生成Slave1.1、简介之前我们都是在物理机或者虚拟机上部署jenkins,但是这种部署方式会有一些难点,如下:主Master发生单点故障时,整个流程都不可用了每个Slave的配置环境不一样,来完成不同语言的编译打包等操作,但是这些差异化的配置导致管理起来非常不方便,维护起来也是
from jenkins import Jenkins# 判断job是否运行结束def build():    job = 'branch-deploy-wap'    params = {        'server_ip': '10.160.30.XXX',        'server_username': 'root',        'server_userpass&#
背景client端执行webUI自动化测试时依赖操作系统上安装了对应的浏览器,并且浏览器与驱动要进行版本的对应——而selenium是可以部署集群,通过远程的方式执行对应的自动化用例,方便高效。原理1、通过代码可知,自动化运行的主类接收了任务id,获取到任务信息后根据任务类型判断进行接口
背景工具选择架构设计及技术实现参数设计断言持续集成测试集编写总结  一、背景1.目前公司发展比较迅速,还处于不停堆业务阶段,所以迭代比较频繁,导致人工回归的成本越来越大2.在有限的测试资源情况下,开发自测的需求占比不低,后端频繁发布容易心里没底
本地pycharm打开项目,运行自动化项目下载项目代码,并用pycharm打开项目gitclonehttps://gitee.com/iread9527/iInterface_python.git进入iInterface_python目录下,cdiInterface_python使用pip3install-rrequirements.txt命令,安装python项目依赖的第三方库使
Jenkins邮件设置一、简介最近有朋友问Jenkins邮件设置的问题,想起来当时也是碰到不少坑,网上看了很多博客说的解决办法根本没有用。最后我解决了这个问题,在此记录一下,希望能够帮助到在网上搜了半天仍然徒劳无功的朋友们,我懂那种失败了一次又一次的感觉,希望能帮到大家。二、配置步