使用Jenkins和Java进行持续集成

本文主要内容:

  • 使用Jenkinsfile进行管道配置
  • 管理第三方凭证
  • 集成Jenkins测试报告
  • Poll 和hook构建触发器
  • 建立 pull 请求

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

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

  • Git等SCM系统
  • CI服务器(如Jenkins)
  • 自动化测试
  • 一些良好的团队CI实践–使你可以缩短构建时间,立即修复损坏的构建,频繁提交并保持较小的更改

所需工具:

运行 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 并完成存储库信息:

单击“ 保存”创建项目。

凭证管理

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

K8S中文社区微信公众号

评论 抢沙发

登录后评论

立即登录