DevOps-7:Jenkins API介绍

news2024/10/5 21:25:50

前言:

为什么要使用Jenkins的API?
我在使用Jenkins的过程中,觉得Jenkins的UI还是有不少问题的:

  • UI性能差,尤其是有一些任务在构建中时,UI非常卡顿,等个十来秒都正常,极端时甚至会崩溃;
  • 权限管理功能薄弱,虽然有Role-based Authorization Strategy类似的插件提供了角色管理,但是还是不太好用;
  • 一些需要人工介入的能力扩展不太方便,比如发布前需要比对数据库表结构并支持人工忽略时,就不好扩展。

如上,因此我还是比较推荐自行开发一个发布系统,自行实现权限能力和一些扩展,通过调用Jenkins的构建API和状态API完成项目的发布和管理操作,包括历史和审计相关能力。


API认证方式说明

Jenkins的API认证支持两种方式,

  • 第一种是使用账号密码进行认证,这种请求会报错:Error 403 No valid crumb was included in the request,需要在所有请求之前先获取crumb,再把crumb添加在后续请求的Header里,参考:https://stackoverflow.com/questions/44711696/jenkins-403-no-valid-crumb-was-included-in-the-request
  • 第二种是在Jenkins生成一个Token,在API请求里使用这个Token代替密码,这种不会存在crumb问题,生成用户Token的方法,在Jenkins首页左侧->People->对应用户->Configure->Add new Token
    在这里插入图片描述

官方API文档介绍

  • 安装完成Jenkins之后,直接访问 http://你的Jenkins域名/api 就可以看到API的介绍了,这里会有创建Job和复制Job等API介绍。
  • 进入具体的某个job,在url后面直接添加 /api,就可以看到这个job的API介绍,如 http://你的Jenkins域名/job/具体job名/api 这里会有删除Job、启动Job构建、禁用/启用Job等API介绍。

Java调用API说明

网上也有一些封装好的Maven库可以使用,我都是直接用feign去调用,下面就以feign的代码进行举例说明:

feign类基础定义

  • 定义一个配置类,返回Jenkins的认证信息(建议使用Jenkins用户token方案)
class MultipartSupportConfig {
    @Value("${jenkins.username:}") // yml里配置的jenkins用户名
    private String jenkinsUser;
    @Value("${jenkins.password:}") // yml里配置的jenkins用户token
    private String jenkinsPassword;
    
    @Bean
    public Contract feignContract() {
        return new Contract.Default();
    }

    @Bean
    public BasicAuthRequestInterceptor basicAuthorizationInterceptor() {
        return new BasicAuthRequestInterceptor(jenkinsUser, jenkinsPassword);
    }
}
  • 定义feignClient类,指向上面的配置类:
@FeignClient(value = "jenkins", url = "${jenkins.worker.url}", configuration = JenkinsFeign.MultipartSupportConfig.class)
public interface JenkinsFeign {
    // 具体的jenkins API方法定义
}

常用Jenkins API方法声明

注:通过feign调用Jenkins,不能用RequestMapping注解,不太记得为啥了,当时是出了很多问题,包括JobName的拼接之类,所以最后采用了RequestLine注解实现,你也可以再使用RequestMapping验证看看。
以下是API方法声明:

  • crumb头信息获取接口
    如果你直接使用Jenkins用户名和密码作为API请求认证方式,那么在所有API调用的Header里,都要有crumb值,这个接口就是用于获取crumb值的(注:不推荐使用)
// 直接请求后续接口会报错: Error 403 No valid crumb was included in the request
// 这个方法就是获取crumb数据的,再在后续请求头里添加 Jenkins-Crumb: xxx
@RequestLine("GET /crumbIssuer/api/json")
String getCrumb();
  • 启动Job构建接口,加入队列
    此接口用于启动某个job的构建,启动成功后,会在响应header里,返回加入到Jenkins的队列序号信息。
    后续需要根据这个队列序号,去查找真正的构建任务编号数据。
    注1:如果该Job配置了This project is parameterized,则必须在方法参数列表里增加这个Job的Parameter值,
    如果调用时未提供对应参数,则会用空值传递给Jenkins(旧版本里会报错,说未提供某某参数,我现在的版本2.361.2不会报错,直接用空值启动)
    注2:如果该Job无任何参数,请使用API: /job/{jenkinsName}/build
/**
 * 启动job构建
 * @param jenkinsName Jenkins的job名,用于url拼接
 * @param pack_env job的参数1:构建环境:test/prev/prod
 * @param publish_branch job的参数2:使用的git分支名
 * @param is_publish job的参数3:是否直接发布
 * @param version_desc job的参数4:发布说明
 * @return 没有响应body,只有header,如:
 * Headers:
 *   content-length: 0
 *   date: Fri, 23 Dec 2022 06:05:01 GMT
 *   location: http://10.100.72.165:8080/queue/item/79/
 *   server: Jetty(10.0.11)
 *   x-content-type-options: nosniff
 */
@RequestLine("POST /job/{jenkinsName}/buildWithParameters")
@Headers("Content-Type: application/x-www-form-urlencoded")
ResponseEntity<String> startDeploy(
        @Param("jenkinsName") String jenkinsName,
        @Param("pack_env") String pack_env,
        @Param("publish_branch") String publish_branch,
        @Param("is_publish") String is_publish,
        @Param("version_desc") String version_desc
);
  • 根据startDeploy方法返回的队列ID,查询真实任务构建ID(buildNum)接口
    队列ID表示还在排队,并没有启动,要轮询此接口,得到buildNum再访问后续接口。
/**
 * buildWithParameters方法启动构建,得到的只是QueueId,不是Job任务号BuildNum。
 * Jenkins会在队列中等待一个可用的线程,再真的启动Job。
 * 此方法就是根据QueueId去查询启动的Job任务号。
 * 注:需要不断轮询,直到结果出现如下结果为止
 * "executable":{"_class":"hudson.model.FreeStyleBuild","number":142,
 * @param queueId 队列id
 * @return json字符串
 */
@RequestLine("GET /queue/item/{queueId}/api/json")
HashMap<String, Object> queueItem(@Param("queueId") String queueId);
  • 根据任务构建ID(buildNum) 查询构建进度文本结果的接口
    这个文本结果,用于展示Jenkins工作的中间过程数据。
/**
 * 获取某一次构建的进度文本结果信息.
 * 注:也可以用/logText/progressiveText接口,或 /consoleText接口,但是缺少时间数据
 *
 * @param jenkinsName JOB名
 * @param buildNum    具体的构建序列号,可以是字符串"lastBuild",表示最后一次构建。
 * @param start       从第几个字节开始获取,默认0
 * @return 构建过程html数据,用于展示时,建议套上标签<pre> </pre>
 */
@RequestLine("GET /job/{jenkinsName}/{buildNum}/logText/progressiveHtml?start={start}")
String getBuildProgressive(@Param("jenkinsName") String jenkinsName,
                           @Param("buildNum") int buildNum,
                           @Param("start") int start);
  • 根据任务构建ID(buildNum) 查询构建是否完成的接口
    如果返回值的isBuilding为false,表示构建结束了,去看看成功还是失败吧。
/**
 * 获取某一次构建的进展信息。
 * 需要轮询,直到结果出现:"building":false,表示构建结束
 *
 * @param jenkinsName JOB名
 * @param number      具体的构建序列号,可以是字符串"lastBuild",表示最后一次构建。
 * @return json字符串
 */
@RequestLine("GET /job/{jenkinsName}/{number}/api/json?tree=*,executor[*],actions[parameters[*]]")
JenkinsBuildInfo getBuildInfo(@Param("jenkinsName") String jenkinsName,
                              @Param("number") int number);

一个Jenkins JOB的完整构建代码过程

private final JenkinsFeign jenkinsFeign;

private void startProjectBuild(String jenkinsName) {
    ResponseEntity response = jenkinsFeign.startDeploy(jenkinsName, "test", "origin/master", "true", "aaa");
    String queueId = getQueueId(response);
    // 轮询,直到构建启动
    int taskNum = getTaskNumber(queueId);
    // 轮询,直到构建结束
    waitTaskFinish(jenkinsName, taskNum);
}

// 获取Jenkins响应里的队列ID
private String getQueueId(ResponseEntity response) {
    String location = response.getHeaders().getFirst("location");
    // location参考 http://10.100.72.165:8080/queue/item/37/ 最后一个数字是队列ID
    String[] split = location.split("/");
    return split[split.length - 1];
}

// 根据Jenkins的QueueId,循环查找执行中的TaskId,如果任务处于Pending状态时,会返回0
private int getTaskNumber(String queueId) {
    int times = 0;
    while (times < 10) {
        ThreadHelper.sleep(3000);
        HashMap<String, Object> map = jenkinsFeign.queueItem(queueId);
        Object objExe = map.get("executable");
        if (objExe != null) {
            Object ret = ((Map<String, Object>) objExe).get("number");
            if (ret != null)
                return (int) ret;
        }
        times++;
    }
    return 0;
}

// 循环等待Jenkins任务构建结束
private String waitTaskFinish(String jenkinsName, int taskNum) {
    while (true) {
        ThreadHelper.sleep(10000);
        JenkinsBuildInfo info = jenkinsFeign.getBuildInfo(jenkinsName, taskNum);
        if (!info.isBuilding()) {
            return info.getResult();
        }
    }
}

附:我在用的完整FeignClient类定义

import com.zixun.zixunops.builds.jenkins.dtos.JenkinsBuildInfo;
import feign.Contract;
import feign.Headers;
import feign.Param;
import feign.RequestLine;
import feign.auth.BasicAuthRequestInterceptor;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.context.annotation.Bean;
import org.springframework.http.ResponseEntity;

import java.util.HashMap;

@FeignClient(value = "jenkins", url = "${jenkins.worker.url}", configuration = JenkinsFeign.MultipartSupportConfig.class)
public interface JenkinsFeign {

    // 直接请求后续接口会报错: Error 403 No valid crumb was included in the request
    // 这个方法就是获取crumb数据的,再在后续请求头里添加 Jenkins-Crumb: xxx
    @RequestLine("GET /crumbIssuer/api/json")
    String getCrumb();

    /**
     * 启动job构建
     *
     * @param jenkinsName    Jenkins的job名,用于url拼接
     * @param pack_env       job的参数1:构建环境:test/prev/prod
     * @param publish_branch job的参数2:使用的git分支名
     * @param is_publish     job的参数3:是否直接发布
     * @param version_desc   job的参数4:发布说明
     * @return 没有响应body,只有header,如:
     * Headers:
     * content-length: 0
     * date: Fri, 23 Dec 2022 06:05:01 GMT
     * location: http://10.100.72.165:8080/queue/item/79/
     * server: Jetty(10.0.11)
     * x-content-type-options: nosniff
     */
    @RequestLine("POST /job/{jenkinsName}/buildWithParameters")
    @Headers("Content-Type: application/x-www-form-urlencoded")
    ResponseEntity<String> startDeploy(
            @Param("jenkinsName") String jenkinsName,
            @Param("pack_env") String pack_env,
            @Param("publish_branch") String publish_branch,
            @Param("is_publish") String is_publish,
            @Param("version_desc") String version_desc
    );

    /**
     * buildWithParameters方法启动构建,得到的只是QueueId,不是Job任务号BuildNum。
     * Jenkins会在队列中等待一个可用的线程,再真的启动Job。
     * 此方法就是根据QueueId去查询启动的Job任务号。
     * 注:需要不断轮询,直到结果出现如下结果为止
     * "executable":{"_class":"hudson.model.FreeStyleBuild","number":142,
     *
     * @param queueId 队列id
     * @return json字符串
     */
    @RequestLine("GET /queue/item/{queueId}/api/json")
    HashMap<String, Object> queueItem(@Param("queueId") String queueId);


    /**
     * 获取某一次构建的进度文本结果信息.
     * 注:也可以用/logText/progressiveText接口,或 /consoleText接口,但是缺少时间数据
     *
     * @param jenkinsName JOB名
     * @param buildNum    具体的构建序列号,可以是字符串"lastBuild",表示最后一次构建。
     * @param start       从第几个字节开始获取,默认0
     * @return 构建过程html数据,用于展示时,建议套上标签<pre> </pre>
     */
    @RequestLine("GET /job/{jenkinsName}/{buildNum}/logText/progressiveHtml?start={start}")
    String getBuildProgressive(@Param("jenkinsName") String jenkinsName,
                               @Param("buildNum") int buildNum,
                               @Param("start") int start);

    /**
     * 获取某一次构建的进展信息。
     * 需要轮询,直到结果出现:"building":false,表示构建结束
     *
     * @param jenkinsName JOB名
     * @param number      具体的构建序列号,可以是字符串"lastBuild",表示最后一次构建。
     * @return json字符串
     */
    @RequestLine("GET /job/{jenkinsName}/{number}/api/json?tree=*,executor[*],actions[parameters[*]]")
    JenkinsBuildInfo getBuildInfo(@Param("jenkinsName") String jenkinsName,
                                  @Param("number") int number);

    // 获取job的配置,可以用于备份
    @RequestLine("GET /job/{jenkinsName}/config.xml")
    String getJobConfig(@Param("jenkinsName") String jenkinsName);
    
    // 创建job
    @RequestLine("POST /createItem?name={jenkinsName}")
    @Headers("Content-Type: application/xml")
    void createJob(@Param("jenkinsName") String jenkinsName, String config);

    // 删除job
    @RequestLine("POST /job/{jenkinsName}/doDelete")
    void deleteJob(@Param("jenkinsName") String jenkinsName);

    // 更新job的配置
    @RequestLine("POST /job/{jenkinsName}/config.xml")
    @Headers("Content-Type: application/x-www-form-urlencoded")
    void updateJobConfig(@Param("jenkinsName") String jenkinsName, String config);


    class MultipartSupportConfig {
        @Value("${jenkins.worker.username:}")
        private String jenkinsUser;
        @Value("${jenkins.worker.password:}")
        private String jenkinsPassword;

        @Bean
        public Contract feignContract() {
            return new Contract.Default();
        }

        @Bean
        public BasicAuthRequestInterceptor basicAuthorizationInterceptor() {
            return new BasicAuthRequestInterceptor(jenkinsUser, jenkinsPassword);
        }
    }
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/115759.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

LeetCode 221. 最大正方形

LeetCode 221. 最大正方形 在一个由 0 和 1 组成的二维矩阵内&#xff0c;找到只包含 1 的最大正方形&#xff0c;并返回其面积。 示例 1&#xff1a; 输入&#xff1a;matrix [["1","0","1","0","0"],["1",&quo…

【OpenCV-Python】教程:8-1 图像去噪 Image Denoising

OpenCV Python 图像去噪 Image Denoising 【目标】 非局部均值去噪算法去除图像中的噪声。 cv2.fastNlMeansDenoising() , cv2.fastNlMeansDenoisingColored() etc. 【理论】 在前面的章节中&#xff0c;我们已经看到了许多图像平滑技术&#xff0c;如高斯模糊&#xff0c…

1、MYSQL基础(DDL DML DCL)

&#xff08;1&#xff09;关于字段修改 change和modify的异同&#xff1a; 同&#xff1a;可以修改表的定义 异&#xff1a;change需要写两次列名&#xff0c;不方便&#xff0c;但是change可以修改表名&#xff0c;modify不可 &#xff08;2&#xff09;多表更新&#xff…

店铺如何快速实现数字化管理?不妨参考一下管理系统

百数店铺管理系统主要是以门店管理为核心&#xff0c;该应用管理涵盖商品、订单、库存、客户、采购、财务、营销等功能体系&#xff0c;维度数据分析&#xff0c;智能指导门店经营&#xff0c;账目清晰一目了然&#xff0c;店铺经营更高效。 1、销售看板 该分析报表里通过销售…

手把手教你玩转 Excel 数据透视表

1. 什么是数据透视表 数据透视表是一种可以快速汇总、分析大量数据表格的交互式分析工具。使用数据透视表可以按照数据表格的不同字段从多个角度进行透视&#xff0c;并建立交叉表格&#xff0c;用以查看数据表格不同层面的汇总信息、分析结果以及摘要数据。 使用数据透视表…

【Vue + Koa 前后端分离项目实战6】使用开源框架==>快速搭建后台管理系统 -- part6 前端实现期刊列表管理【增删查改】

要把努力当成一种习惯&#xff0c;而不是一时热血。 对应后端部分章节回顾&#xff1a; 【Vue Koa 前后端分离项目实战4】使用开源框架&#xff1e;快速搭建后台管理系统 -- part4 后端实现【增删改查】功能_小白Rachel的博客-CSDN博客 目录 一、前端项目准备 1.运行项目 …

支持百问网T113 D1-H D1s V853 V851s 等开发板 使用 Tina Linux NOR Flash文件系统 开发指南

此文章内容适用于 百问网T113 D1-H D1s V853 V851s 等开发板&#xff0c;开发板详情请访问 www.100ask.net 。 1 简介 编写目的 此文档描述Sunxi NOR 模块的使用方法&#xff0c;为相关人员调试提供指导 适用范围 boot0: 适用于brandy-2.0u-boot: 适用于u-boot-2018kernel: …

记录内值排序

记录内值排序 【问题】 could anyone please suggest a way to accomplish this. i have a table which consists of six columns : Table name : orders num1 number, num2 number , num3 number , num4 number , num5 number , num6 number there is a routine which fi…

修改后的代码只进行了git add操作不小心给他恢复了怎么找回来

一份干净的代码在main.js里加了一行console.log(666)&#xff0c;并且只进行了git add 然后不小心给他reset了&#xff01; git reset --hard哦豁&#xff0c;没了&#xff1f; 别急一样可以恢复&#xff0c;我们先执行 git fsck --lost-found然后我们去项目的.git下找到这个…

初级软件测试面试会问什么 除了常见问题,技术题也是重点

众所周知&#xff0c;面试是我们进入一个公司的门槛&#xff0c;面试者只有通过了面试才能进入公司&#xff0c;因此&#xff0c;很多新手测试人就想要知道&#xff0c;自己去面试初级软件测试的岗位&#xff0c;HR们都会问些什么样的问题&#xff1f;自己可以从哪些方面做准备…

多线程顺序运行的 4 种方法

1、在子线程中通过join()方法指定顺序 通过join()方法使当前线程“阻塞”&#xff0c;等待指定线程执行完毕后继续执行。 举例&#xff1a;在线程thread2中&#xff0c;加上一句thread1.join()&#xff0c;其意义在于&#xff0c;当前线程2运行到此行代码时会进入阻塞状态&…

FineReport填报报表常用属性方法

1、去除填报页面选中单元格时的黑色边框 // 加载结束事件 _g().curLGP.hideSelectFrame(); 2、 获取单元格焦点 预览填报报表时&#xff0c;希望页面加载完成后&#xff0c;自动将光标定位在某个控件中&#xff0c;可以直接编辑。 // 获取A2单元格 var cell _g().curLGP.getT…

风控模型算法

目录1 蚂蚁金服2 陆金所3 京东金融4 苏宁金融5 百度金融6 腾讯理财通7 宜信8 钱大掌柜9 万达金融10 网易理财11 美团金融主要是整理目前市面上的风控模型以及风控算法。1 蚂蚁金服 &#xff08;1&#xff09;对接第三方征信公司芝麻信用分&#xff0c;通过用户信用历史、行为偏…

echarts的xAxis和yAxis——x轴y轴以及网格线的详细配置

先看一下xAxis和yAxis配置的图表效果 下图详细的标注了图表中x轴y轴可见的内容 说明一下&#xff1a; x轴y轴在echarts配置项里&#xff0c;从内容上来说大体上没有太大区别&#xff0c;x轴能用的配置项y轴基本也可以用。 通过配置xAxis和yAxis可实现内容 坐标轴箭头的样式&…

Python量化交易01——构建基础策略

参考书目:深入浅出Python量化交易实战 量化交易是很早就想开的栏目了&#xff0c;之前没时间。现在正好放寒假&#xff0c;然后也找到了一本合适的书可以进行学习。 本次第一章就介绍一下简单的量化流程和一个简单的策略。 量化交易顾名思义就是用代码去验证交易策略是否赚钱…

【正点原子I.MX6U-MINI移植篇】Ubuntu-base根文件系统移植构建过程详解(四)

摘要&#xff1a;能不能在ARM板上运行Ubuntu呢&#xff1f;答案肯定是可以的&#xff0c;Ubuntu是Linux系统的一种&#xff0c;可以简单的将Ubuntu理解为一个根文件系统&#xff0c;和我们用busybox、buildroot制作的根文件系统一样。因此移植Ubuntu也就是将Ubuntu根文件系统移…

边缘计算概述

引用李开复在《ai 未来》书中说的&#xff0c;未来ai将会是中美双雄争霸的天下&#xff0c;美国虽然拥有创新的ai技术&#xff0c;重于开发&#xff0c;但现在ai已经到了可以实现落地到社会各个方面了&#xff0c;既然要落地就需要大量的测试和数据&#xff0c;只有中国的市场&…

我要是在学习 C 语言之前知道这些就好了

学习 C 语言好难啊。这门语言本身的基础知识并不是很难&#xff0c;但是“用 C 语言编程”需要用到各种知识&#xff0c;这些知识可没有那么容易掌握&#xff1a; C 语言在各个平台和操作系统上的行为有所差异&#xff0c;因此你需要了解平台&#xff1b; C 语言有许多编译器选…

SpringCloud+SpringCloud Alibaba(尚硅谷2020版)

文章目录1.传统单体架构和微服务架构的对比1.1传统单体架构1.1.1传统单体架构概述1.1.2单体架构的特点1.1.3单体架构的优点1.1.4单体架构的缺点1.2微服务架构1.2.1微服务架构概述1.2.2微服务架构的特点1.2.3微服务架构的优点1.2.4微服务架构的缺点2.SpringCloud简介3.SpringClo…

Service入门

Service入门 1.什么是Service 1.1手机中的Service案例 1.2为什么会有service&#xff1f; 由于手机的原因一个时刻只允许一个激活状态的Activity&#xff0c;其余的处于未激活 后台服务机制应运而生 1.3Service简介 四大组件之一没有UI界面&#xff0c;后台服务&#xff0c;长时…