基于Python的Jenkins的二次开发

背景

最近我们在整一个云执行的平台,底层用的是Jenkins来做执行引擎,方便的把我们的脚本做一个统一的调度。

Jenkins确实是一个非常方便的框架,它提供了一整套的RESTful的API,可以非常方便的做二次开发,而且提供了一个python的库,操作起来就更加方便了。

常用的Jenkins概念

我们在使用Jenkins的时候,一般看到的都是Jenkins的View

也就是说我们看到的基本上都是一些视图。

每一个构建的内容,无论是执行用例,跑脚本,还是打包编译发布,都是一个job

每一个job都有一个对应的name,如果这个job被放在某个文件夹了,那么name就是文件夹名+job名。在查看job信息的时候,会返回一个full_name字段,指的就是这个了。

PythonSDK

pip install python-jenkins执行这条命令就可以安装SDK了。

官网:https://pypi.org/project/python-jenkins/

说明文档:http://python-jenkins.readthedocs.io/en/latest/

说明文档都是英文的,如果有兴趣,看这块当然最好,不过这个SDK实际上非常非常的简单,如果直接去看源代码,也是没问题的,我在开发的过程中,基本上都是去看源码来写的,SDK里面函数命名比较直白,不用深刻理解也能够正常的去使用。

这里列一些比较常用的方法,也是我这次开发的时候用到的一些,我按照我自己写的过程中,一步一步的写出来。

开发过程

  • job信息查询

Jenkins中,每一个构建都是一个job,所以,我们做开发的时候,要在页面上展示历史的构建信息,因此,要能够拿到历史执行job的信息。因此我们要使用get_job_info这个方法。

    def get_job_info(self, name, depth=0, fetch_all_builds=False):
        '''Get job information dictionary.

        :param name: Job name, ``str``
        :param depth: JSON depth, ``int``
        :param fetch_all_builds: If true, all builds will be retrieved
                                 from Jenkins. Otherwise, Jenkins will
                                 only return the most recent 100
                                 builds. This comes at the expense of
                                 an additional API call which may
                                 return significant amounts of
                                 data. ``bool``
        :returns: dictionary of job information
        '''

这里的name参数指的是job的名字,比如我新建了一个叫自动化测试这么个job,那么这个name传的就是自动化测试,如果我把这个job放到了某个文件夹中,那么name这个参数需要传文件夹名/自动化测试
这里有一个坑,在Python2.7版本中传递这个name,如果以变量的形式传递过去,会有一个报错,规避的方法是name.encode('utf-8')做一下编码的处理。返回的参数是这些:

image

builds字段里面就存有所有的历史构建数据。在property这个字段中放有Jenkins构建时的各种参数。

  • 构建日志

拿到了构建数据,我们需要在页面上展示出来,如果能在页面上直接看日志就更方便了,因此要把每次的构建日志拿出来,需要调用get_build_console_output这个方法。

    def get_build_console_output(self, name, number):
        '''Get build console text.

        :param name: Job name, ``str``
        :param number: Build number, ``int``
        :returns: Build console output,  ``str``
        '''

这里可以拿到Jenkins的构建日志,返回的就是一个字符串了。需要注意的是,如果你想要构建的时候也展示构建日志,只需要在构建的时候调用这个方法就行,会返回调用时候构建的日志,用socket或者轮询的方式都可以实现实时日志的展示。

  • 执行构建

执行构建当然是非常关键的一步,我们可以使用build_job这个方法来执行构建。

    def build_job(self, name, parameters=None, token=None):
        '''Trigger build job.

        This method returns a queue item number that you can pass to
        :meth:`Jenkins.get_queue_item`. Note that this queue number is only
        valid for about five minutes after the job completes, so you should
        get/poll the queue information as soon as possible to determine the
        job's URL.

        :param name: name of job
        :param parameters: parameters for job, or ``None``, ``dict``
        :param token: Jenkins API token
        :returns: ``int`` queue item
        '''

这里的name参数跟job信息查询的参数一样,也会有编码的问题。parameters字段传入的是一个字典类型,也就是在页面上点击构建的时候,需要填入的参数。token参数在Jenkins这个类初始化的时候就已经有了,所以一般来说无需传入。这个方法会返回一个queue_item的编号,这个就是Jenkins的构建编号,这个编号可以做一些其他数据的查询,比如构建状态,取消构建等操作,在SDK里面都有对应的方法。

注意:绝对不行用这个来查询是否处于构建中,这个编号在一定时间内会出现失效的情况,如果构建时间过长,会导致查询不到结果而报错,实际上job依然处于构建中

这些方法使用起来比较方便,但是有一些局限性,我们如果在构建的时候用了第三方插件,这些方法是没办法拿到第三方插件的结果的,比如我们这里用RobotFramework的插件来处理了结果,用这些方法是没办法拿到结果的。

从原理入手

Jenkins对外提供的是一个RESTful的接口,那么Python的SDK做的动作实际上就是去请求这个接口,只不过做了一些包装,保证了易用性,很多方法的核心,都会用到jenkins_request,比如:

response = self.jenkins_request(requests.Request(
            'POST', self.build_job_url(name, parameters, token)))

有兴趣的可以自己去读一读源码,整个SDK实际上就是基于requests这个库做了一下包装,然后最终都是通过jenkins_request去请求RESTful的API。我们进入jenkins的页面,在右下角能看到一个这样的东西。

image

有这个标记的页面,就是有接口的,那么这个接口是怎么拿的呢?

从源码里面可以找到答案,很多方法里面都有这么样一个东西。

self.jenkins_open(requests.Request('GET', self._build_url(INFO),auth=auth),
                        add_crumb=False, resolve_auth=False)
                        
response = self.jenkins_open(requests.Request('GET', self._build_url(BUILD_INFO,locals())))

在最上方的配置中可以找到:INFO = 'api/json'

也就是说,在我们看到页面的后方加上/api/json就可以拿到数据了。

image

所有的操作都在这个数据的背后了,也就是说只要写好这个url的拼接规则,就能很简单的去做二次开发了。

当然,在开发的过程中,也是有很多坑的,比如拿历史数据的时候,接口竟然不返回历史构建的状态。。。。

总结

整个开发过程下来还算比较顺利,由于源码比较简单的原因,基本上遇到问题都能通过看源码来解决,但是比较大的问题就是返回结果的枚举没有一个地方列出来,所以导致开发完成后经常有地方需要修修补补。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 202,056评论 5 474
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 84,842评论 2 378
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 148,938评论 0 335
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,296评论 1 272
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,292评论 5 363
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,413评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,824评论 3 393
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,493评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,686评论 1 295
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,502评论 2 318
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,553评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,281评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,820评论 3 305
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,873评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,109评论 1 258
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,699评论 2 348
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,257评论 2 341

推荐阅读更多精彩内容