1,原因:私人账号被禁用

现象:审批应用后,没有在jenkins上创建项目,查询日志得知:java代码通过Jenkins的api创建的Jenkins项目接口报错了,403,account forbidden

2,大致背景和流程:

公司自动化构建项目流程:

  1. 创建gitlab代码库
  2. 申请应用,审批应用
  3. 系统创建Jenkins在gitlab上的webhooks,保证gitlab有变动事件时,提醒Jenkins触发构建任务
  4. 系统自动创建Jenkins项目

3,初步解决:

代码流程:

DocumentBuilderFactory dbFactory = DocumentBuilderFactory.newInstance();
dbFactory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);
dbFactory.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
DocumentBuilder dBuilder = dbFactory.newDocumentBuilder();
Document doc = dBuilder.parse(CommonService.class.getResourceAsStream("/freejenkinstemplate.xml"));
DOMSource domSource = new DOMSource(doc);
StringWriter writer = new StringWriter();
StreamResult result = new StreamResult(writer);
TransformerFactory tf = TransformerFactory.newInstance();
tf.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);
Transformer transformer = tf.newTransformer();
transformer.setOutputProperty(OutputKeys.INDENT, "yes");
transformer.transform(domSource, result);
String xmlTemplate = writer.toString();
xmlTemplate = xmlTemplate.replace("{jdk}", "java-11");//"java-11"
xmlTemplate = xmlTemplate.replace("{recipients}", "邮箱地址");
xmlTemplate = xmlTemplate.replace("{description}", "test-new-project");
xmlTemplate = xmlTemplate.replace("{uuid}", UUID.randomUUID().toString());
xmlTemplate = xmlTemplate.replace("{gitlabUrl}", "biz/profile/test-new-pro.git");
xmlTemplate = xmlTemplate.replace("{sourceBranchRegex}", "(develop|master|hotfix-.*)");//"(develop|master|hotfix-.*)"
xmlTemplate = xmlTemplate.replace("{command}", "-jobName ${JOB_NAME}");
xmlTemplate = xmlTemplate.replace("{credentialsId}", $credentialsId);
xmlTemplate = xmlTemplate.replace("{secretToken}", $token);

JenkinsServer jenkinsServer = JenkinsClient.getInstance().getJenkins();
jenkinsServer.createJob("test-new-project", xmlTemplate);

上述代码中只有在以下三个地方涉及到了账户和token的问题

String credentialsId = getCredentialsId(instance.getDomain());
String token = getScretToken(instance.getDomain());
JenkinsServer jenkinsServer = getJenkinsServer(instance);

4,开始踩坑

首先,在gitlab上创建对应的账号A,并且在Jenkins中添加为用户

此时有到gitlab上A的accessToken,绑定到Jenkins上的凭证:(图1)


jenkins 指定gitlab分支触发构建 jenkins gitlab token_Jenkins

标题

图1 

 

点击进去之后,会发现有

id和token有一一对应关系(图2)


jenkins 指定gitlab分支触发构建 jenkins gitlab token_xml_02

标题

图2 

 

然后兴匆匆把这个凭证id和api token放到了代码中,拭目以待

其次,JenkinsServer登陆时的账号,直接使用了A账号在gitlab上的accessToken,尝试发现可以连接成功

开始第一次调试,成功了!!你以为这就结束了?错!

结果:Jenkins项目创建成功了,但是有信息不对:(图3图4)

这个项目对应的gitlab仓库地址不对,secretToken为空,这是两个很重要的问题,导致每次创建项目后,运维都要手动去设置这两个东西


jenkins 指定gitlab分支触发构建 jenkins gitlab token_xml_03

标题

图3 


jenkins 指定gitlab分支触发构建 jenkins gitlab token_jenkins_04

标题

图4 

 

 

5,继续踩坑

本地调试过程中发现,给定的凭证和token访问依旧时403,谷歌得知,Jenkins升级后,禁止跨域,于是采用了匿名的方式,即

JenkinsServer jenkinsServer = JenkinsClient.getInstance().getJenkins03();
jenkinsServer.createJob("test-new-project", xmlTemplate,true); //true表示匿名,默认为false

然后继续调试发现可以删除项目,创建项目,但是创建完项目,依然有4中的问题

6,走投无路,询问大佬

结果:

{credentialsId}:是随机的,且全局的,它来源于首个Jenkins项目创建后,Jenkins服务器中对应项目的xml文件中的值,

{secretToken}:图4中“Generate”按钮生成后,同样在Jenkins服务器对应项目的xml文件中取得,直接放到代码中即可。

passwordOrToken:唯一变化的是,JenkinsServer连接时使用的passwordOrToken,该值来源是A账号在Jenkins服务器中通过APITOKEN自动生成的:(图5)


jenkins 指定gitlab分支触发构建 jenkins gitlab token_git_05

标题

图5 

 

将该token设置到对应的passwordOrToken中即可

public JenkinsServer getJenkinsServer() {
    JenkinsServer jenkinsServer;
    try {
        if (jenkinsServer == null) {
            jenkinsServer = new JenkinsServer(new URI("http://ip:port"), "A",
                    "11******************************15");
            if (jenkinsServer != null && jenkinsServer.isRunning()) {
                LOG.info("jenkins连接成功");
            } else {
                LOG.info("jenkins连接异常");
            }
        }
    } catch (Exception e) {
        LOG.error("jenkins连接异常", e);
    }
    return jenkinsServer;
}

至此,问题解决

7,回顾总结

1,原始的Jenkins账号有两个凭证,其中老的就是可以用的,但是当时心急,并没有看到信息:(图6)

这里记录了,该账号被应用到的项目列表


jenkins 指定gitlab分支触发构建 jenkins gitlab token_xml_06

标题

图6 

 

2,本次涉及三个类似配置一样的信息:

{credentialsId}:与机器有关,每个Jenkins服务器有一个,且全局

{secretToken}:gitlab调用Jenkins时的token,仅限gitlab。该token是gitlab有变动事件时,通知Jenkins构建时,与Jenkins通信使用的token,由Jenkins签发,即项目中的SecretToken中的“Generate”,可以每个项目设置一个, 也可以所有项目设置成一样的。我们使用的是所有项目设置一个,且在创建Jenkins项目前,设置webHooks时,就已经将该secret Token和对应的Jenkins的url告诉了gitlab,此后gitlab以此通知Jenkins

passwordOrToken:其他方式调用Jenkins的token,如api等。该token是通过其他方式访问Jenkins的api时使用的token,Jenkins以此判断是否接受该请求,也是由Jenkins签发,即在A账号点进去之后的Configure下的APITOKEN中,Add new Token创建的,

3,之前之所以出现图3图4的问题,是因为,passwordOrToken可以连接到Jenkins,但是无法操作Jenkins的api,因为不是Jenkins签发的token,是gitlab中的accessToken,所有能创建项目,但是无法设置项目相关参数(图3图4)

4,第一次接触陌生项目时,先弄清楚脉络流向,再去深入细节