使用Jenkins和Java进行持续集成

本文主要内容:

  • 使用Jenkinsfile进行管道配置

  • 管理第三方凭证

  • 集成Jenkins测试报告

  • Poll 和hook构建触发器

  • 建立 pull 请求

首先,让我们谈谈持续集成(CI)。CI是一种流行的开发实践,尽可能确保软件高质量且可部署。

要采用CI,需要做好一些关键工作:

  • Git等SCM系统

  • CI服务器(如Jenkins)

  • 自动化测试

  • 一些良好的团队CI实践–使你可以缩短构建时间,立即修复损坏的构建,频繁提交并保持较小的更改

所需工具:

  • Docker

  • Java 8

运行 Jenkins

Jenkins是开源的,开发人员可以将其用于持续集成,持续交付和持续部署。它来自Hudson,这是2004年Sun Microsystems用Java编写的CI服务器。

Jenkins Pipeline 是插件,你可以用它来自动构建,测试和部署的套件。你可以在Jenkinsfile中使用特定语法定义管道,你可以在 Pipeline-as-code 模型中将其提交到项目的存储库中。

为了快速入门,请从Docker HYub中提取 Jenkins 镜像:

docker pull jenkins/jenkins:lts

然后启动一个Jenkins容器:

docker run \

-p 8081:8080 \

-p 50000:50000 \

--name my-jenkins \

-v jenkins_data:/var/jenkins_home

jenkins/jenkins:lts

在上面的命令中,我们将Jenkins端口8080映射到主机端口8081,并将Jenkins端口50000映射到主机端口50000。我们还在host文件夹中定义了Jenkins home的存储卷jenkins_data。

当容器启动时,将运行初始安装,Jenkins将记录管理员密码:

Jenkins initial setup is required. An admin user has been created and a password generated.

Please use the following password to proceed to installation:

b518968d266d41d3beb0abef50834fa7

This may also be found at: /var/jenkins_home/secrets/initialAdminPassword

复制密码,然后登陆http://localhost:8081进行初始设置。

粘贴管理员密码,然后继续。

设置过程将使你可以选择自定义要添加的插件。

选择Install Suggested Plugins并继续。

等待安装完成。

设置管理员用户数据并继续。

由于这是一项测试,请保留默认的Jenkins URL(http://localhost:8081/)并完成它。

我们准备创建第一个Jenkins管道。

Okta OIDC身份验证的简单应用

我们将使用Jenkins通过Okta OIDC身份验证自动化构建简单的Java应用程序,因此让我们首先使用 Spring Intializr  创建该应用程序:

curl https://start.spring.io/starter.zip -d dependencies=web,okta \

-d language=java \

-d type=maven-project \

-d groupId=com.okta.developer \

-d artifactId=simpleapp \

-d name="Simple Application" \

-d description="Demo project for Jenkins CI test" \

-d packageName=com.okta.developer.simpleapp \

-o simple-app.zip

解压缩文件:

unzip simple-app.zip -d simple-app

cd simple-app

如果你还没有Okta开发者帐户,请执行 Okta Maven插件 创建一个(免费!)并在应用程序中配置身份验证:

./mvnw com.okta:okta-maven-plugin:setup

你应该看到以下输出:

First name: Jimena

Last name: Garbarino

Email address: ***

Company: ***

Creating new Okta Organization, this may take a minute:

OrgUrl: ***

Check your email address to verify your account.

Writing Okta SDK config to: /home/indiepopart/.okta/okta.yaml

Configuring a new OIDC, almost done:

Created OIDC application, client-id: ***

检查你的电子邮件,然后按照说明激活你的Okta帐户。

Maven插件将在src/main/resources/application.properties中生成OIDC客户端ID,密钥和发行者URL 。因为我们将在公共代码库(如-GitHub)用于CI测试时,所以将凭据复制到其他位置,然后从属性文件中删除它们。

如果你已经拥有Okta Developer帐户,请登录并创建一个新应用程序:在“应用程序”页面上,选择 Add Application 。在“创建新应用程序”页面上,选择“ Web”。给你的应用起一个令人难忘的名称,然后将其添加http://localhost:8080/login/oauth2/code/okta做为“ 登录”重定向URI。

复制发行者(你可以在 API > Authorization Servers 下找到它),客户端ID和客户端密钥,以备后用。

添加一个REST Controller

创建一个GreetingController类以在登录时向用户打招呼。

package com.okta.developer.simpleapp;


import org.springframework.security.core.annotation.AuthenticationPrincipal;

import org.springframework.security.oauth2.core.oidc.user.OidcUser;

import org.springframework.web.bind.annotation.GetMapping;

import org.springframework.web.bind.annotation.RestController;


@RestController

public class GreetingController {


@GetMapping("/greeting")

public String greet(@AuthenticationPrincipal OidcUser user){

return "Hello " + user.getEmail();

}

}

使用Maven Spring Boot插件测试应用程序:

OKTA_OAUTH2_CLIENT_ID={youtOktaClientId} \

OKTA_OAUTH2_CLIENT_SECRET={yourOktaClientSecret} \

OKTA_OAUTH2_ISSUER={yourOktaDomain}/oauth2/default \

mvn spring-boot:run

转到http://localhost:8080/greeting。该应用程序应重定向到Okta进行登录:

登录后,应用程序应显示问候响应:

Hello jimena@***.com

为创建一个 GitHub  公共存储库,simple-app并按照说明推送现有代码。

git init

git add .

git commit -m "initial commit"

git remote add origin https://github.com/<your-username>/simple-app.git

git push -u origin master

Jenkins Pipeline和Jenkinsfile

在Jenkins仪表板中,选择Create New Jobs,设置simple-app为项目名称,然后选择Pipeline作为项目类型。

在下一个屏幕中,选择选项卡 Advanced Project Options 。从右侧的下拉菜单中,选择GitHub + Maven以获取我们将要自定义的Jenkinsfile模板。

将管道脚本复制到Jenkinsfile文件中。更新GitHub地址并为构建设置Okta凭证。还要更改Maven命令。

pipeline {

agent any

environment {

// use your actual issuer URL here and NOT the placeholder {yourOktaDomain}

OKTA_OAUTH2_ISSUER = '{yourOktaDomain}/oauth2/default'

OKTA_OAUTH2_CLIENT_ID = credentials('OKTA_OAUTH2_CLIENT_ID')

OKTA_OAUTH2_CLIENT_SECRET = credentials('OKTA_OAUTH2_CLIENT_SECRET')

}

stages {

stage('Build') {

steps {

// Get some code from a GitHub repository

git 'https://github.com/<your-username>/simple-app.git'

// Run Maven on a Unix agent.

sh "./mvnw -Dmaven.test.failure.ignore=true clean package"

// To run Maven on a Windows agent, use

// bat "mvn -Dmaven.test.failure.ignore=true clean package"

}


post {

// If Maven was able to run the tests, even if some of the test

// failed, record the test results and archive the jar file.

success {

junit '**/target/surefire-reports/TEST-*.xml'

archiveArtifacts 'target/*.jar'

}

}

}

}

}

我们正在使用environment管道语法的指令来定义OKTA_*构建所需的变量。该指令支持credentials()帮助程序从Jenkins环境中检索值。

然后,在请求项目构建之前,我们需要在Jenkins中设置Okta托管凭据。

将推Jenkinsfile送到公共存储库。

在 Advanced Project Options 中,对于“管道定义”,选择 Pipeline script from SCM 并完成存储库信息:

  • SCM:GitHub

  • 资料库URL:https://github.com/%3Cyour-username%3E/simple-app.git

  • 凭证:无

  • 分支说明符:\ * / master

  • 脚本路径:Jenkinsfile

单击“ 保存”创建项目。

凭证管理

Jenkins允许你安全地存储第三方应用程序的凭据,从而使Pipeline项目方便地与第三方服务的交互。让我们添加Okta身份验证的凭据。

在Jenkins控制台中,转到左侧菜单上的 Credentials ,然后选择 global 。

为创建一个“ Secret text ”凭证OKTA_OAUTH2_CLIENT_ID,单击 Add Credentials ,然后选择以下选项:

  • Kind: Secret text

  • Scope: global

  • Secret: {yourOktaClientID}

  • ID: OKTA_OAUTH2_CLIENT_ID

注意:替换{yourOktaClientID}为你的实际 Client ID 。

对OKTA_OAUTH2_CLIENT_SECRET执行相同的操作。

注:存储Jenkins的密钥和拉取代码分支请求不应该一起使用。

现在我们准备构建该项目。转到simple-app并选择Build Now。转到 Build History 并选择构建#1。然后选择 Console Output 选项以监视任务。

添加 controller 测试

GitHub和Maven的Jenkinsfile模板已经集成了测试报告,并可以从构建摘要中对其进行访问。

让我们在应用程序中添加一个控制器测试以验证此功能。

将spring-security-test依赖项添加到pom.xml:

<dependency>

<groupId>org.springframework.security</groupId>

<artifactId>spring-security-test</artifactId>

<scope>test</scope>

</dependency>

创建一个新类src/test/java/com/okta/developer/simpleapp/GreetingControllerTest.java:

package com.okta.developer.simpleapp;


import org.junit.jupiter.api.Test;

import org.springframework.beans.factory.annotation.Autowired;

import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;

import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;

import org.springframework.security.core.GrantedAuthority;

import org.springframework.security.core.authority.SimpleGrantedAuthority;

import org.springframework.security.oauth2.client.authentication.OAuth2AuthenticationToken;

import org.springframework.security.oauth2.core.oidc.OidcIdToken;

import org.springframework.security.oauth2.core.oidc.user.DefaultOidcUser;

import org.springframework.security.oauth2.core.oidc.user.OidcUser;

import org.springframework.test.context.ContextConfiguration;

import org.springframework.test.web.servlet.MockMvc;

import org.springframework.test.web.servlet.result.MockMvcResultMatchers;

import java.time.Instant;

import java.util.ArrayList;

import java.util.Collection;

import java.util.HashMap;

import java.util.Map;

import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.authentication;

import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;

import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;


@AutoConfigureMockMvc

@WebMvcTest

@ContextConfiguration(classes={GreetingController.class})


public class GreetingControllerTest {


private final static String ID_TOKEN = "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9" +

".eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWUsIm" +

"p0aSI6ImQzNWRmMTRkLTA5ZjYtNDhmZi04YTkzLTdjNmYwMzM5MzE1OSIsImlhdCI6MTU0M" +

"Tk3MTU4MywiZXhwIjoxNTQxOTc1MTgzfQ.QaQOarmV8xEUYV7yvWzX3cUE_4W1luMcWCwpr" +

"oqqUrg";

@Autowired

private MockMvc mvc;

@Test

void testGreet() throws Exception {

OidcIdToken idToken = createOidcToken();

this.mvc.perform(get("/greeting") .with(authentication(createMockOAuth2AuthenticationToken(idToken))))

.andExpect(MockMvcResultMatchers.status().isOk())

.andExpect(content().string("Hello user@email.com"));

}

private OAuth2AuthenticationToken createMockOAuth2AuthenticationToken(OidcIdToken idToken) {

Collection<GrantedAuthority> authorities = new ArrayList<>();

authorities.add(new SimpleGrantedAuthority("ROLE_USER"));

OidcUser user = new DefaultOidcUser(authorities, idToken);

return new OAuth2AuthenticationToken(user, authorities, "oidc");

}

private OidcIdToken createOidcToken(){

Map<String, Object> claims = new HashMap<>();

claims.put("groups", "ROLE_USER");

claims.put("email", "user@email.com");

claims.put("sub", 123);

OidcIdToken idToken = new OidcIdToken(ID_TOKEN, Instant.now(),

Instant.now().plusSeconds(60), claims);

return idToken;

}

}

将更改推送到存储库并安排新的构建。完成后,你可以在全局或包级别查看测试结果:

轮询变更

管道支持多种类型的触发器来安排构建。其中之一会定期轮询SCM系统(GitHub)进行更改。如果存在新的更改,它将重新触发管道。Jenkinsfile中的triggers指令配置了构建触发器:

pipeline {

agent any

// poll every 15 minutes

triggers { pollSCM('H/15 * * * *') }

environment {

...

触发器类型pollSCM具有cron表达式,该表达式将管道配置为每15分钟轮询GitHub。

注意:要在Jenkins中安装触发器,必须在推送更新的Jenkinsfile之后首先计划从Jenkins进行手动构建。

多分支管道

多分支管道( Multibranch Pipeline)项目会自动发现分支的管道,并可用于验证拉取请求。 GitHub插件 提供了验证功能, CloudBees 提供文档。你已安装了建议的插件,因此让我们逐步进行配置。

在Jenkins仪表板中,转到 New Item ,键入项目名称,然后选择 Multibranch Pipeline 。然后,在配置表单中,转到Branch Sources并选择GitHub。选择选项 Repository Scan 。在 Owner 字段中,设置你的GitHub用户,然后选择要扫描的存储库。为了简化此测试,我们已经创建了一个公共存储库,因此我们可以跳过GitHub凭证设置。

选择 Scan Multibranch Pipeline Triggers ,选中 Periodically if not otherwise run ,然后将5分钟设置为间隔。

单击 Save 以添加新项目。

触发构建

README.md在simple-app项目的根文件夹中创建一个文件:

# Simple Application with Okta OIDC Authentication

Clone the project and run the application with Maven:

```shell

git clone https://github.com/<your-username>/simple-app.git

cd simple-api

OKTA_OAUTH2_CLIENT_ID={youtOktaClientId} \

OKTA_OAUTH2_CLIENT_SECRET={yourOktaClientSecret} \

OKTA_OAUTH2_ISSUER={yourOktaDomain}/oauth2/default \

./mvnw spring-boot:run

```

为更改和拉取请求创建一个分支。在下一次定期扫描中,Jenkins将为拉取请求创建作业

git checkout -b add-readme

git add README.md

git commit -m "added readme"

git push origin add-readme

GitHub Branch Source插件,还允许你使用项目类型为“ GitHub Organization”来基于GitHub组织的存储库结构创建一个项目。对于此类项目,该插件将根据配置的标准将所有或部分存储库扫描并导入为作业(job)。

GitHub Hook 触发器

Jenkins有一个GitHub插件,可在收到有关推送更改和拉取请求的通知后触发构建。通过GitHub Webhooks,当事件触发时,GitHub会通过HTTP POST发送到Jenkins webhook的配置URL。收到POST后,Jenkins将简单地对SCM进行内部轮询。

你可以在GitHub上手动配置Jenkins Hook的URL,或者Jenkins本身可以根据配置管理项目的Hook。对于托管模式,你还必须配置对GitHub的身份验证,如果你在GitHub中启用了双重身份验证,Jenkins将无法进行身份验证。

GitHub Webhooks的使用要求Jenkins必须可从互联网访问。该 插件的文档 还提到了hook网址是所有仓库独一无二的,但没有提及对发送方所需要的任何一种认证。使用此功能之前,应先评估文档中列出的其他安全隐患。

译者:王延飞

原文链接:  https://dzone.com/articles/using-jenkins-and-java-for-continuous-integration

END

我来评几句
登录后评论

已发表评论数()

相关站点

+订阅
热门文章