最近开始客串运维做CI/CD的规划设计, 主要是基于’Pipeline as Code in Jenkins’. 整理了下思路和技术点, 慢慢的写.

这一篇是关于基于角色的授权策略, 用的是Role-Based Authorization Strategy Plugin.

授权在CI/CD流程中比较常见, 比如我们只让某些特定用户才可以构建Pre-Release的Job. 而更高级的Release发布, 又会需要某些用户的审批才可以进行. 需要授权时, 可能还需要发邮件提醒用户.

UI上如何使用就不提了, 这里只说Pipeline as Code. 后面的几篇也会是这个背景.

参考的这篇文章, 文章里的代码运行失败, 做了修复.

配置

安装完插件, 需要开始基于角色的授权策略. 同时添加角色和为用户分配角色.

使用Role-Based Strategy作为验证方式

Manage Jenkins / Configure Global Security / Configure Global Security

添加角色

Manage Jenkins / Manage and Assign Roles / Manage Roles / Global roles

输入要添加的角色名

为新加的角色配置权限

为用户指定角色

`Manage Jenkins / Manage and Assign Roles / Assign Roles / Global roles’

输入已有的用户名

分配角色

编码

授权会在很多job里使用, 所以我们使用shared library来定义.

1
2
3
4
timeout(time: 5, unit: 'MINUTES') {
    notifyAwaitApproval approvers: getApprovers('pre-release'),
        emailPrompt: 'Build is ready to prepare release'
}

给用户发送待审批邮件, 使用input等待用户的交互, 同时使用milestone阻塞后续的构建请求. notifyAwaitApproval.groovy

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
def call(options) {

    def jobName = env.JOB_NAME

    if(options.message == null) {
        options.message = "Action Required For Build $jobName"
    }

    def csvApproverUsernames = {
        switch (options.approvers) {
            case String:
                // already csv
                return options.approvers
            case Map:
                // keys are usernames and values are names
                return options.approvers.keySet().join(',')
            case ArrayList:
            case HashSet:
                return options.approvers.join(',')
            default:
                throw new Exception("Unexpeced approver type ${options.approvers.class}!")
        }
    }()

    echo "Notify approvers: $csvApproverUsernames for approval"
    // emailext needs to be inside a node block but don't want to take up a node while waiting for approval
    emailext body: "Action Required For Build \${jobName} (#\${env.BUILD_NUMBER})",
            to: csvApproverUsernames,
            subject: "Action Required For Build \${jobName} (#\${env.BUILD_NUMBER})"

    milestone()
    input id: 'Approval',
            message: options.message,
            submitter: csvApproverUsernames,
            submitterParameter: 'submitter'
    milestone()
}

检查并获取Jenkins当前配置的授权策略, 如果是Role-Based Authorization, 返回拥有指定角色的用户列表. getApprovers.groovy

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
import com.cloudbees.groovy.cps.NonCPS
import com.michelin.cio.hudson.plugins.rolestrategy.RoleBasedAuthorizationStrategy

@NonCPS
def call(role) {
    echo "Retrieving users for $role"

    def strategy = RoleBasedAuthorizationStrategy.instance;
    if (strategy != null) {
        return strategy.getGrantedRoles(RoleBasedAuthorizationStrategy.GLOBAL)
                .entrySet()
                .find { entry -> entry.key.getName().equals(role) }.getValue()
    } else {
        throw new Exception("Role Strategy Plugin not in use.  Please enable to retrieve users for a role")
    }
}